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