1 ///	
2 // Written in the D programming language.
3 /**
4 This is a build tool,compile *.d to exe or lib,and help to build dfl2 gui (or other you like).
5 now default DC is dmd ,default platform is windows.
6 
7 If your DC is dmd, dco can start only 'dco.ini' config file. 
8 
9 Compiler dco.d :dmd dco.d -release,and dco ↓,
10 
11 Usage:
12   Config some info to 'dco.ini' file,';' or '#' means you can do as it.Then copy dco.ini to your PATH: such as dmd's config file:sc.ini.
13   And dco.exe can auto copy itsself to EnvPath,that also is  dmd.exe 's path: dmd2\window\bin.
14   After that,you can run the 'dco.exe'  anywhere.
15   If not found 'dco.ini',run: dco -ini,please.
16   
17 For example:
18     to get the debug version( -release to get another)
19 
20 	build some *.d to lib or exe 			 : dco ↓
21 	build  one app.d in many *.d    		 : dco app or  dco app.d
22 	build for libs 	such as dfl,dgui         : dco  -lib
23 	build for app.d use dfl2				 : dco  -gui
24 	build app.d use dfl2 for console		 : dco  -con
25 	build lib and copy to libs				 : dco -lib -c
26 	build by custom	and copy to libs         : dco -arg -addlib -lib -c
27 
28     if your exe's file works on console,you should add '-con' or '-console'. 
29     
30 Copyright: Copyright FrankLIKE 2014-.
31 
32 License:   $(LGPL-3.0).
33 
34 Authors:   FrankLIKE
35 
36 Source: $(dco.d)
37  
38 Created Time:2014-10-27
39 Modify Time:2014-10-31~2014-11-13
40 */
41 module dco;
42 /// dco 
43 import	std.stdio;
44 import	std.datetime;
45 import	std.process; 
46 import	std.string;
47 import	std.file;
48 import	std.path;
49 import	std.exception;
50 import  std.json;
51 import std.exception;
52 
53 string strVersion ="v0.0.6";
54 string	strAddArgs,strAddArgsdfl = " -de -w -property -X ";
55 string	strDebug,strDebugDefault=" -debug";
56 string	strTargetLflags,strConsole=" -L/su:console:4 ",strWindows = " -L/SUBSYSTEM:WINDOWS ";
57 string	strTargetLib,SpecialLib = "dfl",strWinLibs=" ole32.lib oleAut32.lib gdi32.lib Comctl32.lib Comdlg32.lib advapi32.lib uuid.lib ws2_32.lib "; 
58 string	strDFile;
59 string	strAddLib;
60 string	strOtherArgs;
61 string	strImportDefault = " -I$(DMDInstallDir)windows/import ";
62 string	strTargetPath,strTargetFileName,strTargetTypeSwitch,targetTypeDefault = "lib";
63 string	strDCEnv,strDCEnvFile;
64 SysTime sourceLastUpdateTime,targetTime;
65 
66 bool	bUseSpecialLib =false,bDebug =true,bBuildSpecialLib =false;
67 bool	bCopy =false ,bDisplayBuildStr=false,bDisplayCopyInfo =true;
68 bool	bForce = false;
69 bool 	bAssignTarget =false;
70 
71 //ini
72 string configFile ="dco.ini";
73 string[string] configKeyValue;
74 
75 //ini args
76 string strPackageName,strArgs,strTargetName,strTargetType ="exe",strDC,strDCStandardEnvBin ="dmd2\\windows\\bin",strLibs ,strImport,strLflags;
77  
78 void main(string[] args)
79 {
80 	if(!findDCEnv()) return;
81 	// readInJson();
82 	if(!checkArgs(args))
83 	{
84 		if(!findFiles())
85 		{
86 			ShowUsage();
87 			return;
88 		}
89 	}
90 	if(args.length ==1)
91 	{
92 		if(!CheckBinFolderAndCopy()) return;
93     }
94 	if(args.length == 2 && (toLower(args[1]) == "-h" || toLower(args[1]) == "-help"))
95 	{
96 		ShowUsage();
97 		return;
98 	}
99 	if(strPackageName =="")
100 	{
101 		strPackageName = strTargetName;
102 	}
103  
104 	buildExe(args);
105 }
106 
107 bool findDCEnv()
108 {
109 	 if(!readConfig(configFile)) return false;
110 	string strNoDC = "Not found '"~strDC~"' in your computer,please setup it.",strTemp,strTempFile;
111 	string strDCExe = "\\" ~ strDC.stripRight() ~ ".exe";
112 	string strFireWall = " Maybe FirWall stop checking the " ~ strDCExe ~ ",please stop it.";
113 	
114 	int len = strDCStandardEnvBin.length;
115 
116 	auto path = environment["PATH"];
117 	string[] strDCs = path.split(";");
118 	foreach(s;strDCs)
119 	{
120 		ptrdiff_t i = path.indexOf(strDCStandardEnvBin);
121 		if(i != -1)
122 		{
123 			if(exists(s ~ strDCExe))
124 			{ 
125 				strTempFile = s ~ "\\dco.exe";
126 				if(exists(strTempFile))
127 				{
128 					strDCEnv = s;
129 					strDCEnvFile =  s ~ strDCExe;
130 				   break;
131 			    }
132 			}
133 		}
134 	}
135 
136 	if(strDCEnvFile =="")
137 	{
138 		writeln(strNoDC);
139 		return false;
140 	}
141 	else
142 	{
143 		//writeln(strDC ~ " is " ~ strDCEnvFile);
144 		return true;
145 	}
146 }
147 
148 bool readConfig(string configFile)
149 { 
150 	try
151 	{ 
152 		string strConfigPath = thisExePath();
153 		strConfigPath = strConfigPath[0..strConfigPath.lastIndexOf("\\")].idup;
154 		strConfigPath ~= "\\" ~ configFile;
155 
156 		if(!enforce(exists(strConfigPath),"'FireWall' stop to access the 'dco.ini',please stop it."))
157 		{
158 			writeln("dco not found dco.ini, it will help you to  create a init dco.ini file ,but you should input something in it.");
159 			initNewConfigFile();
160 			return false;
161 		}  
162 	   
163 		auto file = File(strConfigPath); 
164 		scope(failure) file.close();
165 		auto range = file.byLine();
166 		foreach (line; range)
167 		{
168 			if (!line.init && line[0] != '#' && line[0] != ';' && line.indexOf("=") != -1)
169 			{ 
170 				ptrdiff_t i =line.indexOf("=");
171 				configKeyValue[line.strip()[0..i].idup] = line.strip()[i+1..$].idup;
172 			}
173 		}
174 	 
175 		 file.close();
176   
177 		strDC = configKeyValue.get("DC","dmd"); 
178 		 
179 		strDCStandardEnvBin = configKeyValue.get("DCStandardEnvBin","dmd2\\windows\\bin"); 
180 		SpecialLib = configKeyValue.get("SpecialLib","dfl");  
181 		strImport = configKeyValue.get("importPath","");
182 		strLflags = configKeyValue.get("lflags","/su:console:4"); 
183 		return true;
184   }
185   catch(Exception e) 
186   {
187 		writeln(" Read ini file err,you should input something in ini file.",e.msg);
188 		return false;
189   }
190 }
191 
192 bool checkArgs(string[] args)
193 {
194 	string c;
195 	int p;
196 	bool bDFile =false;
197 	foreach(int i,arg;args)
198 	{
199 		if(i == 0) continue;
200 		c = toLower(arg);
201 		p = c.indexOf('-');
202 		if(p == -1 || c.indexOf(".d") != -1)
203 		{
204 
205 			strTargetName = c;
206  			strDFile ~= " ";
207 			strDFile ~= c;
208 			bDFile = true;
209 		}
210 		else
211 		{
212 			c = c[p+1 .. $];
213 		}
214 
215 		if(i ==0) continue;
216  
217 		if(c == "force")
218 		{
219 			bForce = true;
220 		}
221 		else if(c.indexOf("of") != -1)
222 		{
223 			bAssignTarget = true;
224 			strTargetName = c[(c.indexOf("of")+1)..$];
225 		}
226 		else if(c == strPackageName || c == strPackageName ~ "lib")
227 		{
228 			bAssignTarget = true;
229 			bBuildSpecialLib = true;
230 			strTargetTypeSwitch = " -" ~ targetTypeDefault;
231 			strTargetName = c ~ ".lib";
232 		}
233 		else if (c == "ini")
234 		{
235 			initNewConfigFile();
236 			return false;
237 		}
238 	}
239 	return bDFile;
240 }
241 
242 bool CheckBinFolderAndCopy() 
243 {
244 	if(checkIsUpToDate())
245 	{
246 		writeln(strTargetName ~" file is up to date.");
247 		return false;
248 	}
249 	return true;
250 }
251 
252 bool checkIsUpToDate()
253 {
254 	 getTargetInfo();
255      if(exists(strTargetFileName))
256      {
257 		targetTime = getTargetTime(strTargetFileName);
258  
259         if(strTargetFileName.indexOf("dco.exe") != -1)
260         {
261 			if(!checkIsUpToDate(strDCEnvFile ,targetTime))
262 			{
263 				auto files = dirEntries(".","dco.{exe,ini}",SpanMode.shallow);
264 				foreach(d;files)
265 				{
266 					string strcopy ="copy " ~ d ~" " ~ strDCEnv;
267 					writeln(strcopy);
268 					auto pid = enforce(spawnShell(strcopy.dup()),"spawnShell(strcopy.dup()) is err!");
269 					if (wait(pid) != 0)
270 					{
271 						writeln("copy failed.");
272 					}
273 				}
274 			 //copy(strTargetFileName,strDCEnvFile);
275 			}
276  	    }
277  		 
278 		bool bUpToDate = (targetTime >= sourceLastUpdateTime);
279 		 
280 		if(!bUpToDate || bForce)
281 		{
282 			removeExe(strTargetFileName);
283 		}
284  		return bUpToDate;
285     }
286  
287     return false;
288 }
289 
290 SysTime getTargetTime(string strPathFile)
291 {
292 	 return DirEntry(strPathFile).timeLastModified;
293 }
294 
295 void removeExe(string strPathExe)
296 {
297     if(exists(strPathExe))
298 	{
299 		auto pid = enforce(spawnShell("del " ~ strPathExe.dup()),"del " ~ strPathExe.dup() ~ " Err");
300 		if (wait(pid) != 0)
301         {
302 			writeln(strPathExe ~ ", remove  failed!");
303 			return;
304 		}
305 	}
306 }
307 
308 bool checkIsUpToDate(string strPathFile,SysTime targettime)
309 {
310 	 if(!exists(strPathFile)) return false;
311     auto testFile = DirEntry(strPathFile);
312     auto createTime = testFile.timeLastModified;
313    
314     return (targettime <= createTime);
315 }
316 
317 void buildExe(string[] args)
318 {
319 	string c;
320 	int p;
321 	foreach(int i,arg;args)
322 	{
323 		if(i ==0) continue;
324 		c = toLower(arg);
325 		p = c.indexOf('-');
326 		if(p != -1)
327 		{
328 			c = c[p+1 .. c.length];
329 
330 		switch(c)
331     	{
332 			case "h","help":
333 				ShowUsage();
334 				break;
335 			case "gui":
336 				strTargetLflags = strWindows;
337 				bUseSpecialLib = true;
338     			strAddArgs = strAddArgsdfl;
339 				break;
340 			case "win","windows","winexe":
341 				strTargetLflags = strWindows;
342 				break;
343     		case "debug":
344 				bDebug = true;
345 				break;
346 			case "release":
347 				bDebug = false;
348 				strDebug = " -" ~ c.idup;
349 				break;
350 
351     		case "console","con","exe":
352     			strTargetLflags = strConsole;
353     			break;
354 			case "all":
355 				bUseSpecialLib = false;
356 				strAddLib = strLibs;
357     			strAddArgs = strAddArgsdfl;
358     			strImport = strImportDefault;
359     			strTargetLflags = strConsole;
360     			break;
361 			case "addlib":
362     			strAddLib = strLibs~" ";
363     			strImport = strImportDefault;
364     			strTargetLflags = strConsole;
365     			break;
366 			case "arg":
367     			strAddArgs = strAddArgsdfl;
368     			break;
369 			case "lib":
370 				strTargetTypeSwitch = " -" ~ targetTypeDefault;
371 				break;
372 			case "dfl","dfllib":
373 				 bBuildSpecialLib = true;
374 				strTargetTypeSwitch = " -" ~ targetTypeDefault;
375 				break;
376 			case "c","copy":
377 				bCopy = true;
378 				break;
379 			case "force":
380 				bForce = true;
381 				break;
382 			case "init":
383 				
384 				break;
385     		default:
386 				strOtherArgs ~= " ";
387 				strOtherArgs ~= arg;
388 				break;
389     		}
390     	}
391 	}
392 
393    strTargetLib = bDebug ? SpecialLib ~ "_debug.lib" : SpecialLib ~ ".lib";
394  
395    if(bBuildSpecialLib)
396    {
397 	   strOtherArgs = " -of" ~ strTargetLib;
398 	   strAddLib = strLibs;
399 	  strTargetFileName = getcwd() ~ "\\" ~ strTargetLib;
400    }
401    else
402    {
403 		strTargetFileName = getcwd() ~ "\\" ~ strTargetName;
404    }
405  
406 	if(bUseSpecialLib)
407 	{
408 			if(SpecialLib == "dfl")
409 	{
410 		strLibs =strWinLibs;
411 	}
412 		strAddLib = strTargetLib ~" " ~ strLibs;
413 	}
414   
415 	buildExe();
416 }
417 
418 void buildExe()
419 {
420 	if(bForce)
421 	{
422 		removeExe(strTargetFileName);
423 	}
424 	strDC ~= " ";
425 	strDC ~= strTargetTypeSwitch;
426 	string strCommon = strOtherArgs ~" " ~ strImportDefault ~ strImport ~ " " ~ strAddLib ~ strTargetLflags ~ strDFile ~ strDebug;
427     string buildstr = strDC ~ strAddArgsdfl ~ strCommon ~ "\r\n";
428 	buildstr = bUseSpecialLib ? buildstr : strDC ~ strCommon;
429 	if(bDisplayBuildStr)
430 	{
431 		writeln(buildstr);
432 	}
433  
434 	StopWatch sw;
435 	sw.start();
436 	auto pid =  enforce(spawnShell(buildstr.dup()),"build function is error! ");
437 
438 	if (wait(pid) != 0)
439 	{
440 		writeln("Compilation failed:\n", pid);
441 	}
442 	else
443 	{
444 			sw.stop();
445    
446 		writeln("\nCompile time :" , sw.peek().msecs/1000.0,"secs");
447 
448 		if(bCopy)
449 		{
450 			copyFile();
451 		}
452 	}
453 	writeln("End.");
454 }
455 
456 void copyFile()
457 {
458 	string strcopy;
459 	 if(!exists(strTargetFileName)) 
460 	 {
461 	 	writeln(strTargetFileName," is not exists,stop copy.");
462 	   return;
463 	}
464  
465 	if(strTargetFileName.indexOf("exe") != -1)
466 	{
467 		//copy(strTargetFileName,strDCEnv); //
468 		strcopy = "copy " ~ strTargetFileName~" " ~ strDCEnv;
469 	}
470 	else
471 	{ 
472 		string strDCLibPath = strDCEnv[0..(strDCEnv.length - "bin".length)].idup ~ "lib"; 
473 		//copy(strDCEnv,strDCLibPath);
474 		strcopy = "copy " ~ strTargetFileName ~ " " ~ strDCLibPath;
475 	}
476 	if(bDisplayCopyInfo)
477 	{
478 		writeln(strcopy);
479 	}
480 		 
481 	auto pid =  enforce(spawnShell(strcopy.dup()),"copyFile() error");
482 	if (wait(pid) != 0)
483 	{
484 		writeln("Copy failed.");
485 	}
486 }
487 
488 bool findFiles()
489 { 
490 	int i=0;
491 	bool bPackage = false; 
492 	auto packages = dirEntries(".","{package.d,all.d}",SpanMode.depth);
493 	foreach(p; packages){i++;}
494 	bPackage = (i > 0);
495 	auto dFiles = dirEntries(".","*.{d,di}",SpanMode.depth);
496 	int icount =0;
497     SysTime fileTime;
498     DirEntry rootDE ;
499   
500 	foreach(d; dFiles)
501 	{	 
502 	    if(!bAssignTarget)
503 	    {
504 			if(icount == 0)
505 			{
506 				strTargetName = d.name[(d.name.lastIndexOf("\\")+1) .. d.name.lastIndexOf(".")];
507 				strTargetName ~= "." ~ strTargetType; 
508 			}
509 		}
510 		if(icount ==0 )
511 		{
512 			ReadDFile(d,bPackage);
513 		}
514 		
515 		strDFile ~= " ";
516 		strDFile ~= d.name[2 ..$].idup;
517 		 
518 		//sourceLastUpdateTime 
519 		rootDE = DirEntry(d);
520         if(rootDE.timeLastModified > fileTime)
521         {
522         	fileTime = rootDE.timeLastModified;
523         } 
524         icount++;
525 	}
526     sourceLastUpdateTime = fileTime;
527     
528 	strDFile = strDFile.stripRight().idup;
529 	
530 	if(icount <= 0)  
531 	{
532 		writeln("Not found any *.d files in current folder.If there is a 'source' or 'src' folder,dco will find the '*.d' from there.");
533 		 return false;
534 	}
535 	bCopy = (strDFile.indexOf("dco.d") != -1) ? true : false;
536 	return true;
537 }
538 
539 void getTargetInfo()
540 { 
541 	string root_path = getcwd();
542     string strPath;
543 	auto dFiles = dirEntries(root_path,strTargetName ~ ".{lib,exe}",SpanMode.shallow);
544 	int i =0;
545 	foreach(d;dFiles)
546 	{
547 		i++;
548 		strTargetFileName =d;
549 	   strTargetType = d.name[d.name.lastIndexOf(".")+1..$];
550 	   break;
551 
552 	}
553 	if(i ==0)
554 	{
555 		if(strTargetName.indexOf("." ~ strTargetType) == -1)
556 		{
557 			strTargetName = strTargetName ~ "." ~ strTargetType;
558 		}
559 		strTargetFileName = root_path ~ "\\" ~ strTargetName;
560 	}
561 		 
562 	return;
563 }
564  
565 void ShowUsage()
566 {
567 	writeln("
568 dco build tool " ~ strVersion ~ "
569 written by FrankLIKE.
570 Usage:
571 	dco [<switches...>] <files...>
572 		  
573 	for example: dco  
574 	         or: dco app.d 
575 		 
576 build for dfl2:	dco  
577 			or: dco -gui
578 			or:	dco *.d -gui
579 build for other: dco -lib
580 		     or: dco *.d -lib
581 			 or: dco *.d -release
582 	         or: dco *.d -arg -addlib
583 
584 Switches:
585     -h	       Print help(usage: -h,or -help).
586     -c	       Copy new exe or lib to 'windows/bin' or 'lib' Folder
587 		(-copy also is ok). 
588     -release   Build files's Release version(Default version is 'debug').
589     -gui       Make a Windows GUI exe without a console(For DFL).
590     -win       Make a Windows GUI exe without a console
591 		(For any other: the same to -winexe,-windows).
592     -lib       Build lib files.
593     -ini	   Create the ini file for config. 
594     -all       Build files by args,libs(Default no dfl_debug.lib) in Console.
595     -arg       Build files by args(-de -w -property -X).
596     -addlib    Build files by add libs(ole32.lib oleAut32.lib gdi32.lib 
597 		Comctl32.lib Comdlg32.lib advapi32.lib uuid.lib ws2_32.lib).
598     ");
599 } 
600 
601 void ReadDFile(string dFile,bool bPackage)
602 { 
603 	 auto file = File(dFile); 
604 	 scope(exit)  file.close();
605 	 auto range = file.byLine();
606 	 int icount = 0;
607     foreach (line; range)
608     {
609         if (!line.init && line.indexOf("import") != -1)
610         { 
611           bUseSpecialLib = !bPackage;
612           bBuildSpecialLib = bPackage;
613           if(bUseSpecialLib) strTargetLflags = strWindows;
614           
615         	if(line.indexOf("dfl") != -1)
616         	{
617         		SpecialLib = "dfl";
618 				break;
619 			}
620 			else if(line.indexOf("dgui") != -1)
621 			{
622 				strArgs = strAddArgsdfl = " -g -de -w -property -X ";
623 				SpecialLib = "dgui";
624 				break;
625 			}
626         }
627         icount++;
628         if(icount >100) break;
629     }
630 }
631 
632 void initNewConfigFile()
633 {
634 	auto ini = File("dco.ini","w"); 
635 	scope(failure) ini.close();
636 	ini.writeln(";DC=dmd");
637 	ini.writeln("DC=");
638 	ini.writeln(";DCStandardEnvBin=dmd2\\windows\\bin");
639 	ini.writeln("DCStandardEnvBin=");
640 	ini.writeln(";SpecialLib=dfl");
641 	ini.writeln("SpecialLib=");
642 	ini.writeln(";importPath=-I$(DMDInstallDir)windows/import");
643 	ini.writeln("importPath=");
644 	ini.writeln(";lflags=-L/su:console:4");
645 	ini.writeln(";lflags=-L/SUBSYSTEM:WINDOWS");
646 	ini.writeln("lflags=");
647 	ini.close();
648  
649 	auto pid = spawnProcess(["notepad.exe","dco.ini"]);
650     auto dmd = tryWait(pid);
651 	if (dmd.terminated)
652 	{
653 		if (dmd.status == 0) writeln("open dco.ini succeeded!");
654 		else writeln("open dco.ini failed");
655 	}
656 	else writeln("Still opening...");
657 	
658 }
659 
660 void readInJson()
661 {
662 	if(!exists("dub.json") & !exists("package.json") ) return;
663 	/*
664 	strPackageName = configKeyValue.get("name","");
665 	strArgs = configKeyValue.get("args",strAddArgs);
666 	strLibs = configKeyValue.get("libs","");
667 	strTargetName = configKeyValue.get("targetName","");
668 	strTargetType = configKeyValue.get("targetType",strTargetType); 
669 	*/
670 }
671