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