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