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.3
38 Created Time:2014-10-27
39 Modify Time:2014-10-31~2014-11-3
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 				string strcopy ="copy dco.exe " ~ strDCEnvFile;
250 				writeln(strcopy);
251 				auto status = system(strcopy);
252 				if(status !=0)
253 				{
254 					writeln("copy failed.");
255 				}
256 			 //copy(strTargetFileName,strDCEnvFile);
257 			}
258  	    }
259  		 
260 		bool bUpToDate = (targetTime >= sourceLastUpdateTime);
261 		 
262 		if(!bUpToDate || bForce)
263 		{
264 			removeExe(strTargetFileName);
265 		}
266  		return bUpToDate;
267     }
268  
269     return false;
270 }
271 SysTime getTargetTime(string strPathFile)
272 {
273 	 return DirEntry(strPathFile).timeLastModified;
274 }
275 void removeExe(string strPathExe)
276 {
277     if(exists(strPathExe))
278 	{
279 		if(system("del " ~ strPathExe) == 0) 
280         {
281 			writeln(strPathExe ~ ", remove  ok!");
282 			return;
283 		}
284 	}
285 }
286 
287 bool checkIsUpToDate(string strPathFile,SysTime targettime)
288 {
289 	 if(!exists(strPathFile)) return false;
290     auto testFile = DirEntry(strPathFile);
291     auto createTime = testFile.timeLastModified;
292    
293     return (targettime <= createTime);
294 }
295 
296 void buildExe(string[] args)
297 {
298 	string c;
299 	int p;
300 	foreach(int i,arg;args)
301 	{
302 		c = toLower(arg);
303 		p = c.indexOf('-');
304 		if(p != -1)
305 		{
306 			c = c[p+1 .. c.length];
307 		}
308 		switch(c)
309     	{
310 			case "h","help":
311 				ShowUsage();
312 				break;
313 			case "gui":
314 				strTargetLflags = strWindows;
315 				bUseSpecialLib = true;
316     			strAddArgs = strAddArgsdfl;
317 				break;
318 			case "win","windows","winexe":
319 				strTargetLflags = strWindows;
320 				break;
321     		case "debug":
322 				bDebug = true;
323 				break;
324 			case "release":
325 				bDebug = false;
326 				strDebug = " -" ~ c.idup;
327 				break;
328 
329     		case "console","con","exe":
330     			strTargetLflags = strConsole;
331     			break;
332 			case "all":
333 				bUseSpecialLib = false;
334 				strAddLib = strLibs;
335     			strAddArgs = strAddArgsdfl;
336     			strImport = strImportDefault;
337     			strTargetLflags = strConsole;
338     			break;
339 			case "addlib":
340     			strAddLib = strLibs~" ";
341     			strImport = strImportDefault;
342     			strTargetLflags = strConsole;
343     			break;
344 			case "arg":
345     			strAddArgs = strAddArgsdfl;
346     			break;
347 			case "lib":
348 				strTargetTypeSwitch = " -" ~ targetTypeDefault;
349 				break;
350 			case "dfl","dfllib":
351 				 bBuildSpecialLib = true;
352 				strTargetTypeSwitch = " -" ~ targetTypeDefault;
353 				break;
354 			case "c","copy":
355 				bCopy = true;
356 				break;
357 			case "force":
358 				bForce = true;
359 				break;
360 			case "init":
361 				
362 				break;
363     		default:
364     		if(i ==0) continue;
365 				if(c.indexOf(".d") != -1)
366 				{
367 					strDFile ~= " ";
368 					strDFile ~= arg;
369 				}
370 				else
371 				{
372 					strOtherArgs ~= " ";
373 					strOtherArgs ~= arg;
374 				}
375     		break;
376     	}
377 	}
378 
379    strTargetLib = bDebug ? SpecialLib ~ "_debug.lib" : SpecialLib ~ ".lib";
380  
381    if(bBuildSpecialLib)
382    {
383 	   strOtherArgs = " -of" ~ strTargetLib;
384 	   strAddLib = strLibs;
385    }
386  
387 	if(bUseSpecialLib)
388 	{
389 			if(SpecialLib == "dfl")
390 	{
391 		strLibs =strWinLibs;
392 	}
393 		strAddLib = strTargetLib ~" " ~ strLibs;
394 	}
395   
396 	buildExe();
397 }
398 
399 void buildExe()
400 {
401 	if(bForce)
402 	{
403 		removeExe(strTargetFileName);
404 	}
405 	strDC ~= " ";
406 	strDC ~= strTargetTypeSwitch;
407 	string strCommon = strAddLib ~ strTargetLflags ~ strDFile ~ strDebug;
408  
409     string buildstr = strDC ~ strAddArgsdfl ~ strOtherArgs ~ " " ~ strImportDefault ~ strCommon ~ "\r\n";
410 	buildstr = bUseSpecialLib ? buildstr : strDC ~ strAddArgs ~ strOtherArgs ~ " " ~ strImport ~ strCommon;
411 	if(bDisplayBuildStr)
412 	{
413 		writeln(buildstr);
414 	}
415  
416 	StopWatch sw;
417 	sw.start();
418 	auto status = system(buildstr);
419 	sw.stop();
420    
421    if (status != 0)
422 	{
423 		writeln("Compilation failed:\n", status);
424 	}
425 	else
426 	{
427 		writeln("\nCompile time :" , sw.peek().msecs/1000.0,"secs");
428 
429 		if(bCopy)
430 		{
431 			copyFile();
432 		}
433 	}
434 	writeln("End.");
435 }
436 
437 void copyFile()
438 {
439 	string strcopy;
440 	if(!exists(strTargetFileName)) return;
441 	if( sourceLastUpdateTime >= getTargetTime(strTargetFileName))
442 	{
443 		writeln(strTargetFileName," is up to date.");
444       return;
445     }
446 	if(strTargetFileName.indexOf("exe") != -1)
447 	{
448 		//copy(strTargetFileName,strDCEnv); //
449 		strcopy = "copy " ~ strTargetFileName~" " ~ strDCEnv;
450 	}
451 	else
452 	{
453 		string strDCLibPath = strDCEnv[0..(strDCEnv.length - "bin".length)].idup ~ "lib"; 
454 		//copy(strDCEnv,strDCLibPath);
455 		strcopy = "copy " ~ strTargetFileName ~ " " ~ strDCLibPath;
456 	}
457 	if(bDisplayCopyInfo)
458 	{
459 		writeln(strcopy);
460 	}
461 		 
462 	auto status = system(strcopy);
463 	if(status !=0)
464 	{
465 		writeln("Copy failed.");
466 	}
467 }
468   
469 bool findFiles()
470 { 
471 	bool bPackage = false; 
472 	auto packages = dirEntries(".","{package.d,all.d}",SpanMode.depth);
473 	if(packages.count >0) bPackage = true;
474 	auto dFiles = dirEntries(".","*.{d,di}",SpanMode.depth);
475 	int icount =0;
476     SysTime fileTime;
477     DirEntry rootDE ;
478   
479 	foreach(d; dFiles)
480 	{
481 	    if(!bAssignTarget)
482 	    {
483 			if(icount == 0)
484 			{
485 				strTargetName = d.name[(d.name.lastIndexOf("\\")+1) .. d.name.lastIndexOf(".")];
486 				strTargetName ~= "." ~ strTargetType; 
487 			}
488 		}
489 		if(icount ==0 )
490 		{
491 			ReadDFile(d,bPackage);
492 		}
493 		
494 		strDFile ~= " ";
495 		strDFile ~= d.name[2 ..$].idup;
496 		
497 		//sourceLastUpdateTime 
498 		rootDE = DirEntry(d);
499         if(rootDE.timeLastModified > fileTime)
500         {
501         	fileTime = rootDE.timeLastModified;
502         } 
503         icount++;
504 	}
505     sourceLastUpdateTime = fileTime;
506     
507 	strDFile = strDFile.stripRight().idup;
508 	
509 	if(icount <= 0)  
510 	{
511 		writeln("Not found any *.d files in current folder.If there is a 'source' or 'src' folder,dco will find the '*.d' from there.");
512 		 return false;
513 	}
514 	bCopy = (strDFile.indexOf("dco.d") != -1) ? true : false;
515 	return true;
516 }
517 
518 void getTargetInfo()
519 {
520 	  string root_path = getcwd();
521     string strPath;
522 	auto dFiles = dirEntries(root_path,"*.{lib,exe}",SpanMode.shallow);
523 	int i =0;
524 	foreach(d;dFiles)
525 	{
526 		i++;
527 		strTargetFileName =d;
528 	   strTargetType = d.name[d.name.lastIndexOf(".")+1..$];
529 	   break;
530 
531 	}
532 	if(i ==0)
533 	{
534 		if(strTargetName.indexOf("." ~ strTargetType) == -1)
535 		{
536 			strTargetName = strTargetName ~ "." ~ strTargetType;
537 		}
538 		strTargetFileName = root_path ~ "\\" ~ strTargetName;
539 	}
540 	return;
541 }
542  
543 void ShowUsage()
544 {
545 	writeln("
546 dco written by FrankLIKE.
547 Usage:
548 	dco [<switches...>] <files...>
549 		  
550 	for example: dco ↓ 
551 	         or: dco app.d 
552 		 
553 build for dfl2:	dco ↓ 
554 			or: dco -gui
555 			or:	dco *.d -gui
556 build for other: dco -lib
557 		     or: dco *.d -lib
558 			 or: dco *.d -release
559 	         or: dco *.d -arg -addlib
560 
561 Switches:
562     -h	       Print help(usage: -h,or -help).
563     -c	       Copy new exe or lib to 'windows/bin' or 'lib' Folder
564 		(-copy also is ok). 
565     -release   Build files's Release version(Default version is 'debug').
566     -gui       Make a Windows GUI exe without a console(For DFL).
567     -win       Make a Windows GUI exe without a console
568 		(For any other: the same to -winexe,-windows).
569     -lib       Build lib files.
570     -ini	   Create the ini file for config. 
571     -all       Build files by args,libs(Default no dfl_debug.lib) in Console.
572     -arg       Build files by args(-de -w -property -X).
573     -addlib    Build files by add libs(ole32.lib oleAut32.lib gdi32.lib 
574 		Comctl32.lib Comdlg32.lib advapi32.lib uuid.lib ws2_32.lib).
575     ");
576 } 
577 
578 void ReadDFile(string dFile,bool bPackage)
579 { 
580 	 auto file = File(dFile); 
581 	 scope(failure)  file.close();
582 	 auto range = file.byLine();
583 	 int icount = 0;
584     foreach (line; range)
585     {
586         if (!line.init && line.indexOf("import") != -1)
587         { 
588           bUseSpecialLib = !bPackage;
589           bBuildSpecialLib = bPackage;
590           if(bUseSpecialLib) strTargetLflags = strWindows;
591           
592         	if(line.indexOf("dfl") != -1)
593         	{
594         		SpecialLib = "dfl";
595 				break;
596 			}
597 			else if(line.indexOf("dgui") != -1)
598 			{
599 				strArgs = strAddArgsdfl = " -g -de -w -property -X ";
600 				SpecialLib = "dgui";
601 				break;
602 			}
603         }
604         icount++;
605         if(icount >100) break;
606     }
607     file.close(); 
608 }
609 
610 void initNewConfigFile()
611 {
612 	auto ini = File("dco.ini","w"); 
613 	ini.writeln(";DC=dmd");
614 	ini.writeln("DC=");
615 	ini.writeln(";DCStandardEnvBin=dmd2\\windows\\bin");
616 	ini.writeln("DCStandardEnvBin=");
617 	ini.writeln(";SpecialLib=dfl");
618 	ini.writeln("SpecialLib=");
619 	ini.writeln(";importPath=-I$(DMDInstallDir)windows/import");
620 	ini.writeln("importPath=");
621 	ini.writeln(";lflags=-L/su:console:4");
622 	ini.writeln(";lflags=-L/SUBSYSTEM:WINDOWS");
623 	ini.writeln("lflags=");
624 	ini.close();
625  /*
626 	auto pid = spawnProcess("notepad.exe dco.ini");
627     auto dmd = tryWait(pid);
628 	if (dmd.terminated)
629 	{
630 		if (dmd.status == 0) writeln("open dco.ini succeeded!");
631 		else writeln("open dco.ini failed");
632 	}
633 	else writeln("Still opening...");
634 	*/
635 }
636 
637 void readInJson()
638 {
639 	if(!exists("dub.json") & !exists("package.json") ) return;
640 	/*
641 	strPackageName = configKeyValue.get("name","");
642 	strArgs = configKeyValue.get("args",strAddArgs);
643 	strLibs = configKeyValue.get("libs","");
644 	strTargetName = configKeyValue.get("targetName","");
645 	strTargetType = configKeyValue.get("targetType",strTargetType); 
646 	*/
647 }
648