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