Ticket #17: build.d

File build.d, 95.4 kB (added by rasmus at-flajm-dot-se, 6 years ago)
Line 
1 /* *******************************************************
2    Build is a tool to assist building applications and libraries written
3    using the D programming language.
4
5  Copyright: (c) 2005 Derek Parnell
6  Authors: Derek Parnell, Melbourne
7  Initial Creation: January 2005
8  Version: 2.10
9  Date: August 2005
10  License:
11         This software is provided 'as-is', without any express or implied
12         warranty. In no event will the authors be held liable for damages
13         of any kind arising from the use of this software.
14         Permission is hereby granted to anyone to use this software for any
15         purpose, including commercial applications, and to alter it and/or
16         redistribute it freely, subject to the following restrictions:$(NL)
17         1. The origin of this software must not be misrepresented; you must
18            not claim that you wrote the original software. If you use this
19            software in a product, an acknowledgment within documentation of
20            said product would be appreciated but is not required.$(NL)
21         2. Altered source versions must be plainly marked as such, and must
22            not be misrepresented as being the original software.$(NL)
23         3. This notice may not be removed or altered from any distribution
24            of the source.$(NL)
25         4. Derivative works are permitted, but they must carry this notice
26            in full and credit the original source.
27 ******************************************************* */
28
29 module build;
30 private import build_bn;    // This module's build number
31
32 version(unix)   version = Unix;
33 version(Unix)   version = Posix;
34 version(linux)  version = Posix;
35 version(darwin) version = Posix;
36
37 version(build)
38 {
39     version(Windows) {
40         // OptLink Definition File
41         pragma (build_def, "VERSION 2.10");
42     }
43 }
44
45
46 private{
47     // --------- imports ----------------
48     import source;          // Source File class
49
50     import util.str;        // non-standard string routines.
51     import util.fdt;        // File Date-Time class
52     import util.pathex;     // Extended Path routines.
53     import util.fileex;     // Extended File routines.
54     import util.macro;      // Macro processing routines.
55     import util.booltype;   // definition of True and False
56
57     import std.c.stdio;
58     import std.file;
59     import std.outbuffer;
60     import std.path;
61     import std.stdio;
62     import std.stream;
63     import std.string;
64
65     version(Windows) {
66         import std.c.windows.windows;
67     }
68     else version(linux) {
69         import std.c.linux.linux;
70     }
71     else version(darwin) {
72         import std.c.darwin.darwin;
73     }
74
75     // --------- C externals ----------------
76     extern (C)
77     {
78         char*   getenv  (char *);
79         int     putenv  (char *);
80         int     system  (char *);
81     }
82
83
84     // --------- enums ----------------
85     enum LibOpt {Implicit, Build, DontBuild}
86
87     // --------- constants ----------------
88     const {
89         version(Windows) {
90             char[] kExeExtention=`exe`;
91             char[] kLibExtention=`lib`;
92             char[] kObjExtention=`obj`;
93             char[] kShrLibExtention=`dll`;
94         }
95
96         version(Posix) {
97             char[] kExeExtention=``;
98             char[] kLibExtention=`a`;
99             char[] kObjExtention=`o`;
100             char[] kShrLibExtention=`s`;
101         }
102         char[] kSrcExtention=`d`;
103         char[] kMacroExtention=`mac`;
104         char[] kDdocExtention=`ddoc`;
105
106     }
107
108     // ---------- Module scoped globals -----------
109     version(DigitalMars) {
110         version(Windows) {
111             char[] vCompilerExe=`dmd.exe`;
112             char[] vCompileOnlySwitch = ` -c`;
113             char[] vLinkerExe=`link.exe`;
114             char[] vConfigFile=`sc.ini`;
115             char[] vCompilerPath=``;
116             char[] vLinkerPath=``;
117             char[] vLinkerDefSwitches=`/noi/map`;
118             char[] vConfigPath=``;
119             char[] vLibPaths = ``;
120             char[] vConfigSep = ";";
121             char[] vLibrarian = `lib`;
122             char[] vLibrarianOpts = `-c -p256`;
123             char[] vLinkerDebugSymInfoSwitch = " /co";
124         }
125
126         version(Posix) {
127             char[] vCompilerExe=`gdmd`;
128             char[] vCompileOnlySwitch= ` -c`;
129             char[] vLinkerExe=`ld`;
130             char[] vConfigFile=`dmd.conf`;
131             char[] vCompilerPath=``;
132             char[] vLinkerPath=``;
133             char[] vLinkerDefSwitches=``;
134             char[] vConfigPath=`/etc/`;
135             char[] vLibPaths = ``;
136             char[] vConfigSep = ":";
137             char[] vLibrarian = `ar`;
138             char[] vLibrarianOpts = `-r`;
139             char[] vLinkerDebugSymInfoSwitch = " -g";
140         }
141
142         char[]     vVersionSwitch = "-version=";
143         char[]     vDebugSwitch = "-debug";
144         char[]     vKeepPathSwitch = "-op";
145         char[]     vOutFileSwitch = "-of";
146         char[]     vImportPathSwitch = "-I";
147         char[]     vNoLinkSwitch    = "-c";
148         char[]     vLinkOutputSwitch    = "-o ";
149         char[]     vLinkLibSwitch = "-l";
150     }
151
152     version(GNU) {
153         version(Windows) {
154             char[] vCompilerExe=`gdc.exe`;
155             char[] vCompileOnlySwitch= ` -c`;
156             char[] vLinkerExe=`gdc.exe`;
157             char[] vConfigFile=null;
158             char[] vCompilerPath=``;
159             char[] vLinkerPath=``;
160             char[] vLinkerDefSwitches=``;
161             char[] vConfigPath=null;
162             char[] vLibPaths = ``;
163             char[] vConfigSep = ";";
164             char[] vLibrarian = `lib`;
165             char[] vLibrarianOpts = `-c`;
166
167                 char[]     vVersionSwitch = "-fversion=";
168               char[]     vDebugSwitch = "-fdebug";
169               char[]     vOutFileSwitch = "-o ";
170               char[]     vKeepPathSwitch = "";
171               char[]     vImportPathSwitch = "-I ";
172               char[]     vLinkOutputSwitch    = "-o ";
173               char[]     vLinkLibSwitch = "-l";
174             char[] vLinkerDebugSymInfoSwitch = " /co";
175         }
176
177         version(Posix) {
178                 // Using GDC's GDMD wrapper script
179                 char[] vCompilerExe=`gdmd`;
180             char[] vCompileOnlySwitch= ` -c`;
181             char[] vLinkerExe=`gdmd`; // using gdmd/gdc as linker
182             char[] vConfigFile=``;
183             char[] vCompilerPath=``;
184             char[] vLinkerPath=``;
185             char[] vLinkerDefSwitches=``;
186             char[] vConfigPath=`/etc/`;
187             char[] vLibPaths = ``;
188             char[] vConfigSep = ":";
189             char[] vLibrarian = `ar`;
190             char[] vLibrarianOpts = `-r`;
191
192                 char[]     vVersionSwitch = "-version=";
193               char[]     vDebugSwitch = "-debug";
194               char[]     vKeepPathSwitch = "-op";
195               char[]     vOutFileSwitch = "-of";
196               char[]     vImportPathSwitch = "-I";
197               char[]     vLinkOutputSwitch    = "-of";
198               char[]     vLinkLibSwitch = "-L-l";
199             char[] vLinkerDebugSymInfoSwitch = " -g";
200         }
201         char[] vNoLinkSwitch = "-c";
202     }
203
204     char[]       vBuildImportPathSwitch = "-I";
205     char[]       vTemporaryPathSwitch = "-od";
206     char[]       vRunSwitch = "-exec";
207     char[]       vLibrarianPath = "";
208     char[]*      vDelayedValue = null;
209     char[]       vTemporaryPath = "";
210
211     Bool         vTestRun;
212     Bool         vScanImports;
213     Bool         vNoLink;
214     Bool         vForceCompile;
215     Bool         vSilent;
216     Bool         vCleanup;
217     Bool         vVerbose;
218     Bool         vMacroInput;
219     Bool         vNames;
220     Bool         vAllObjects;
221     Bool         vNoDef;
222     Bool         vAutoImports;
223     Bool         vExecuteProgram;
224     Bool         vUseResponseFile;
225     Bool         vConsoleApp;
226     char[]       vRunParms;
227     char[]       vTargetExe;
228     char[][]     vImportRoots;
229     char[][]     vModulesToIgnore;
230     char[][]     vModulesToNotice;
231     char[][]     vBuildDef;
232     char[][]     vDefaultLibs;
233     char[][]     vDefaultCompArgs;
234     LibOpt       vLibraryAction = LibOpt.Implicit;
235     char[]       vAppPath;
236     char[]       vAppName;
237     char[]       vAppVersion = "2.10";
238     char[]       vTargetName;           // Output name from first file name.
239     char[]       vPragmaTargetName;     // Output name from pragma.
240     char[]       vCommandTargetName;    // Output name from switches.
241     char[][]     vCmdLineSourceFiles;   // List of source files from command line
242     bool[char[]] vLinkFiles;            // List of non-source files from command line
243     char[][]     vCombinedArgs;         // All the args are gathered here prior to processing.
244     char[][]     vBuildArgs;            // Arguments passed to build
245     char[][]     vCompilerArgs;         // Arguments passed to compiler
246     char[][]     vSourceScanList;       // The list of places to find source files.
247     char[]       vRDFName = "default.rdf";
248     bool[char[]] vNestedDelim;
249     struct ExternRef
250     {
251         char[]   FilePath;
252         char[][] ToolOpts;
253     }
254     ExternRef[]  vExternals;
255
256     Substitute[] vSubstitutions;
257
258
259     const static char[] kPathId = "PATH";   // Used to locate the environment symbol
260
261     version(Windows)
262     {
263         char[]       vWinVer = "";
264         ubyte        vWinVerNum;
265     }
266
267 }
268
269 // Module constructor.
270 //-------------------------------------------------------
271 static this()
272 //-------------------------------------------------------
273 {
274     source.ActivateVersion("build"); //todo
275
276     vDefaultCompArgs ~= vKeepPathSwitch;     // Keep object files in same folder as source.
277     vModulesToIgnore ~= "phobos";  // Assume phobos will be ignored.
278     vSourceScanList ~= "." ~ std.path.sep;
279     vNoLink = False;
280     vTestRun = False;
281     vScanImports = False;
282     vForceCompile = False;
283     vSilent = False;
284     vCleanup = False;
285     vVerbose = False;
286     vMacroInput = True;
287     vNames = False;
288     vAllObjects = False;
289     vNoDef = False;
290     vAutoImports = True;
291     vExecuteProgram = False;
292     vUseResponseFile = False;
293     vConsoleApp = True;
294
295     version(Posix){
296         vDefaultCompArgs ~= vVersionSwitch ~ "Posix"; // Until such time as this is standard in dmd.
297     }
298
299     version(Windows) {
300         vWinVerNum = cast(ubyte)(GetVersion() & 0xFF);
301         vWinVer = std.string.format("%d.0", vWinVerNum);
302      }
303
304     version(DigitalMars) {
305         version(Windows) {
306             vUseResponseFile = True;
307         }
308         else
309         {
310             vUseResponseFile = False;
311         }
312      } else
313      {
314         vUseResponseFile = False;
315      }
316
317     vNestedDelim["("] = true;
318     vNestedDelim["<"] = true;
319     vNestedDelim["{"] = true;
320     vNestedDelim["["] = true;
321 }
322
323 static ~this()
324 {
325     // Clean up any resources held by the source files.
326     Source.Finalize();
327 }
328
329 //-------------------------------------------------------
330 void DisplayUsage(bool pFull = true)
331 //-------------------------------------------------------
332 {
333
334     writefln(
335         "Path and Version : %s v%s(%d)\n  built on %s"
336             ,vAppPath, vAppVersion, build_bn.auto_build_number,
337             __TIMESTAMP__);
338     if (pFull == false)
339         return;
340     else {
341
342     writefln(
343         "Usage: %s sourcefile [options objectfiles libraries]"
344             , vAppName);
345     writefln("  sourcefile D source file");
346     writefln("  -v         Verbose (passed through to D)");
347     writefln("  -V         Verbose (NOT passed through)");
348     writefln("  -names     Displays the names of the files used in building the target.");
349     writefln("  -DCPATH<path> <path> is where the compiler has been installed.");
350     writefln("             Only needed if the compiler is not in the system's");
351     writefln("             PATH list. Used if you are testing an alternate");
352     writefln("             version of the compiler.");
353     writefln("  -CFPATH<path> <path> is where the D config file has been installed.");
354     writefln("  -full      Causes all source files, except ignored modules,");
355     writefln("              to be compiled.");
356     writefln("  -link      Forces the linker to be called instead of the librarian.");
357     writefln("              (Only needed if the source files do not contain");
358     writefln("               main/WinMain)");
359     writefln("  -nolink    Ensures that the linker is not called.");
360     writefln("              (Only needed if main/WinMain is found in the source");
361     writefln("               files and you do NOT want an executable created.)");
362     writefln("  -lib       Forces the object files to be placed in a library.");
363     writefln("              (Only needed if main/WinMain is found in the source");
364     writefln("               files AND you want it in a library instead of");
365     writefln("               an executable.)");
366     writefln("  -nolib     Ensures that the object files are not used to form");
367     writefln("              a library.");
368     writefln("              (Only needed if main/WinMain is not found in the source");
369     writefln("               files and you do NOT want a library.");
370     writefln("  -obj       This is the same as having both -nolib and -nolink switches.");
371     writefln("  -allobj    Ensures that all object files are added to a");
372     writefln("              library.");
373     writefln("              (Normally only those in the same directory are added.)");
374     writefln("  -cleanup   Ensures that all object files created during the run");
375     writefln("              are removed at the end of the run, plus other work files.");
376     writefln("  -clean     Same as -cleanup");
377   version(Windows) {
378     writefln("  -gui[:x.y] Forces a GUI application to be created. The optional");
379     writefln("              :x.y can be used to build an application for a ");
380     writefln("              specific version of Windows. eg. -gui:4.0");
381     writefln("              (Only needed if WinMain is not found in the source files");
382     writefln("               or if you wish to override the default Windows version)");
383     writefln("  -dll       Forces a DLL library to be created.");
384     writefln("              (Only needed if DllMain is not found in the source files.)");
385    }
386
387     writefln("  -LIBOPT<opt> Allows you to pass <opt> to the librarian.");
388     writefln("  -LIBPATH=<pathlist> Used to add a semi-colon delimited list");
389     writefln("                of search paths for library files.");
390     writefln("  -test      Does everything as normal except it displays the commands");
391     writefln("              instead of running them.");
392     writefln("  -RDF<path> Overrides the default Rule Definition File");
393     writefln("  -R<y|n>    Indicates whether to use a response file or command line");
394     writefln("              arguments with the compiler tools.");
395     writefln("               -Ry will cause a response to be used.");
396     writefln("               -Rn will cause command line arguments to be used.");
397     writefln("               -R will reverse the current usage.");
398     writefln("  -PP<path>  Add a path to the Source Search List");
399
400   version(DigitalMars) {
401    version(Windows) {
402     writefln("               ** The default is to use a response file"); }
403    else {
404     writefln("               ** The default is to use command line arguments"); }
405   }
406   else {
407     writefln("               ** The default is to use command line arguments"); }
408
409     writefln("  -exec<param> If the link is successful, this will cause the");
410     writefln("               executable just created to run. You can give it ");
411     writefln("               run time parameters. Anything after the '-exec' will");
412     writefln("               placed in the program's command line. You will need");
413     writefln("               to quote any embedded spaces.");
414     writefln("  -od<path>  Nominate the directory where temporary (work) files");
415     writefln("             are to be created. By default they are created in");
416     writefln("             the same directory as the target file.");
417     writefln("  -X<module> Modules/Packages to ignore (eg. -Xmylib)");
418     writefln("  -M<module> Modules/Packages to notice (eg. -Mphobos)");
419     writefln("  -T<targetname> The name of the target file to create. Normally");
420     writefln("              the target name istaken from the first or only name");
421     writefln("              of the command line.");
422     writefln("  -help     Displays the full 'usage' help text. ");
423     writefln("  -h        Same as -help, displays the full 'usage' help text.");
424     writefln("  -?        Same as -help, displays the full 'usage' help text.");
425     writefln("  -silent   Avoids unnecessary messages being displayed.");
426     writefln("  -noautoimport Turns off the automatic addition of source paths");
427     writefln("              to the list of Import Roots.");
428     writefln("  -info      Displays the version and path of the Build application.");
429     writefln("  -nodef    Prevents a Module Definition File from being created.");
430     writefln("  [...]      All other options, objectfiles and libraries are");
431     writefln("              passed to the compiler");
432     writefln("*Note, you can specify all or any command line value in a ");
433     writefln("   response file. Each value appears in its own line in the");
434     writefln("   response file and you reference this file by prefixing");
435     writefln("   its name with an '@' symbol on the command line.");
436     writefln("   Example:  build @final");
437     writefln("      where a file called 'final.brf' contains the command");
438     writefln("      line values (including other response file references)");
439     writefln("   If the response file reference is just a single '@' then");
440     writefln("   build looks for a file called 'build.brf'");
441 }
442 }
443
444 //-------------------------------------------------------
445 int RunCommand(char[] pCommand)
446 //-------------------------------------------------------
447 {
448     int lRC;
449     int lTrueRC;
450
451     if (vTestRun == True) {
452         writefln("Command: '%s'",pCommand);
453         return 0;
454     }
455     else
456     {
457
458
459         if(vVerbose == True) {
460             writefln("Running '%s'",pCommand);
461         }
462
463         lRC = system(std.string.toStringz(pCommand));
464         version(Posix) lTrueRC = ((lRC & 0xFF00) >> 8);
465         version(Windows) lTrueRC = lRC;
466
467         if(vVerbose == True) {
468             if (lTrueRC == 0){
469                 writefln("Successful");
470             } else {
471                 writefln("Failed. Return code: %04x",lRC);
472             }
473         }
474         return lTrueRC;
475     }
476 }
477
478 //-------------------------------------------------------
479 char[] GetEnv(char[] pSymbol)
480 //-------------------------------------------------------
481 {
482     return std.string.toString(getenv(pSymbol));
483 }
484
485 // Scans all known source files and extacts any modules.
486 // It returns the time of the most recently modified file.
487 //-------------------------------------------------------
488 FileDateTime GetNewestDateTime()
489 //-------------------------------------------------------
490 {
491     Source lSource;
492     FileDateTime lModsTime = new FileDateTime();
493
494     foreach(int i, char[] lFileName; Source.ScanOrder)
495     {
496         // Get the next Source object to examine.
497         lSource = Source.SourceIndex[lFileName];
498         if (lSource.Ignore)
499             continue;
500
501         // Ensure that it has been processed.
502         lSource.search();
503
504         if(vVerbose == True) {
505             writefln("source file[%d] %s", i,
506                     util.pathex.AbbreviateFileName(lFileName));
507         }
508         else if(vNames == True) {
509             writefln(" [ %s ]", util.pathex.AbbreviateFileName(lFileName));
510         }
511
512         if (lSource.DependantsTime > lModsTime)
513         {
514             if(vVerbose == True) {
515                 writefln("Newer time: from %s to %s",
516                     lModsTime.toString(),
517                     lSource.DependantsTime.toString()
518                     );
519             }
520
521             lModsTime = lSource.DependantsTime;
522         }
523     }
524
525     // Examine any link file dependancies too.
526     foreach(int i, char[] lFileName; vLinkFiles.keys)
527     {
528         FileDateTime lLinkTime = GetFileTime(lFileName);
529
530         if(vVerbose == True) {
531             writefln("link file[%d] %s %s", i,
532                     util.pathex.AbbreviateFileName(lFileName), lLinkTime.toString());
533         }
534         else if(vNames == True) {
535             writefln(" [ %s ]", util.pathex.AbbreviateFileName(lFileName));
536         }
537
538         if (lLinkTime > lModsTime)
539         {
540             if(vVerbose == True) {
541                 writefln("Newer time: from %s to %s",
542                     lModsTime.toString(),
543                     lLinkTime.toString()
544                     );
545             }
546
547             lModsTime = lLinkTime;
548         }
549     }
550
551     return lModsTime;
552 }
553
554 struct Rule
555 {
556     char[] Name;
557     char[] Input;
558     char[] Output;
559     char[] Tool;
560 };
561
562
563 //-------------------------------------------------------
564 Rule[] LoadRules()
565 //-------------------------------------------------------
566 {
567     Rule[] lRules;
568     char[][] lRuleText;
569     char[] lRuleDefnFile;
570
571     static char[] kRuleKey = "rule=";
572
573     lRuleDefnFile = util.pathex.LocateFile(vRDFName,
574                         getDirName(vAppPath) ~ std.path.pathsep ~ GetEnv(kPathId));
575     if (vVerbose == True)
576         writefln("Rule Definitions from %s", lRuleDefnFile);
577     GetTextLines( lRuleDefnFile, lRuleText );
578     foreach(char[] lLine; lRuleText)
579     {
580         lLine = util.str.strip(lLine);
581         if (util.str.begins(lLine, kRuleKey ) == True)
582         {
583             lRules.length = lRules.length + 1;
584             lRules[$-1].Name = lLine[5 .. $];
585         }
586         else if (lRules.length > 0)
587         {
588             if (util.str.begins(lLine, "in=") == True)
589                 lRules[$-1].Input = lLine[3..$];
590             else if (util.str.begins(lLine, "out=") == True)
591                 lRules[$-1].Output = lLine[4..$];
592             else if (util.str.begins(lLine, "tool=") == True)
593                 lRules[$-1].Tool = lLine[5..$];
594         }
595     }
596
597     if (vVerbose == True)
598     {
599         foreach(Rule r; lRules)
600         {
601             writefln("Rule '%s' ==> in:%s, out:%s, tool:'%s'",
602                         r.Name, r.Input, r.Output, r.Tool);
603         }
604     }
605
606     return lRules;
607 }
608
609 //-------------------------------------------------------
610 int ProcessExternal( ExternRef pRef)
611 //-------------------------------------------------------
612 {
613     int lResult = -1;
614     static Rule[] lRules;
615     char[] lExtension;
616     char[] lInFile;
617     char[] lOutFile;
618     FileDateTime lInDate;
619     FileDateTime lOutDate;
620     char[] lCommand;
621
622
623     if (lRules.length == 0)
624         lRules = LoadRules();
625
626     lExtension = std.path.getExt(pRef.FilePath);
627     foreach(Rule r; lRules)
628     {
629         lInFile = "";
630         if (lExtension == r.Input)
631         {
632             lInFile = pRef.FilePath;
633             lOutFile = std.path.addExt(pRef.FilePath, r.Output);
634
635         } else if (lExtension == r.Output)
636         {
637             lOutFile = pRef.FilePath;
638             lInFile = std.path.addExt(pRef.FilePath, r.Input);
639         }
640
641         if (lInFile.length == 0)
642             continue;
643
644         lInFile = util.pathex.LocateFile(lInFile, vImportRoots);
645         if (! util.fileex.FileExists( lInFile ) )
646             throw new Exception(
647                 std.string.format("External file '%s' not found", lInFile));
648
649         lOutFile = getDirName(lInFile) ~ std.path.sep ~ getBaseName(lInFile);
650         lOutFile = std.path.addExt(lOutFile, r.Output);
651
652         lInFile = util.pathex.AbbreviateFileName(lInFile);
653         lOutFile = util.pathex.AbbreviateFileName(lOutFile);
654
655         lInDate = new FileDateTime(lInFile);
656         lOutDate = new FileDateTime(lOutFile);
657
658         if ((lInDate > lOutDate) || (vForceCompile == True))
659         {
660             char[] lKeyValues;
661
662             lKeyValues = "@IN=" ~ lInFile ~ "," ~ "@OUT=" ~ lOutFile;
663             foreach(char[] lOpt; pRef.ToolOpts)
664             {
665                 if (std.string.find(lOpt, "=") != -1)
666                     lKeyValues ~= "," ~ lOpt;
667             }
668             lCommand = util.str.Expand(r.Tool,lKeyValues);
669
670             // Run Compiler to compile the source files that need it.
671             lResult = RunCommand(lCommand);
672             if (lResult != 0)
673                 return lResult;
674         }
675         AddLink(lOutFile);
676
677     }
678
679     return 0;
680 }
681
682 /* Build the target.  Return an error code if there
683     was a problem, else return zero.
684 */
685 //-------------------------------------------------------
686 int Build()
687 //-------------------------------------------------------
688 {
689     Bool        lCompiling;
690     Bool        lLinking;
691     Bool        lBuildRequired;
692     char[][]    lFilesToLink;
693     char[][]    lFilesToCompile;
694     int         lRunResult;
695     char[]      lTargetName;
696     char[]      lTargetDir;
697     FileDateTime lTargetTime;
698     FileDateTime lMostRecentTime;
699     auto Source[]    lNonLinkingSources;
700     char[]      lDResponseFileName;
701     char[]      lLinkResponseFileName;
702     char[]      lLResponseFileName;
703     char[]      lDefName;
704     char[]      lOutText;
705     char[]      lCompilerOpts;
706     char[]      lSourcesToCompile;
707     char[]      lCommand;
708     char[][]    lObjectFiles;
709     char[][]    lLibraryFiles;
710
711
712     lCompiling = False;
713     lLinking = False;
714     lBuildRequired = False;
715
716     // Examine each supplied source file.
717     foreach( char[] lFile; vCmdLineSourceFiles)
718     {
719         new Source(GetFullPathname(lFile, vSourceScanList));
720     }
721
722     // I'm linking if I'm not building a library, and a 'main' was
723     // found, and I was not explicitly told not to link.
724     lLinking =  (vLibraryAction == LibOpt.Build) ||
725                 (Source.WasMainFound == false) ||
726                 (vNoLink == True)
727                ? False : True;
728
729     lMostRecentTime = GetNewestDateTime();
730
731     // If not explictly known, set the library action
732     // based on whether or not 'main' was found in the sources.
733     if (vLibraryAction == LibOpt.Implicit) {
734         if (Source.WasMainFound){
735             vLibraryAction = LibOpt.DontBuild;
736         }
737         else {
738             vLibraryAction = LibOpt.Build;
739         }
740     }
741
742     // I'm either creating a library, an executable or neither.
743
744     // If a target name was supplied in a pragma, use that as the
745     // default target name.
746     if (vPragmaTargetName.length != 0)
747         vTargetName = vPragmaTargetName;
748
749     // If a target name was supplied on the command line, use that
750     // instead of the default name.
751     if (vCommandTargetName.length != 0)
752     {
753         char[] lBaseName;
754
755         lBaseName = vTargetName.dup;
756         for(int i = lBaseName.length-1; i >= 0; i--)
757         {
758             if (lBaseName[i] == '.')
759             {
760                 lBaseName.length = i;
761                 break;
762             }
763         }
764         vTargetName = Expand(vCommandTargetName, "Target=" ~  lBaseName);
765     }
766
767     // Ensure that the path to the target's location will exist.
768     util.pathex.MakePath(vTargetName);
769
770
771     if (vLibraryAction == LibOpt.Build)
772         // Target is a library.
773         lTargetName = ReplaceExtention(vTargetName, kLibExtention);
774
775     else if (vNoLink == False)
776     {
777         if (Source.WasMainFound)
778             if (Source.WasMainDLL)
779                 // Target is a shared library.
780                 lTargetName = ReplaceExtention(vTargetName, kShrLibExtention);
781             else
782             {
783                 // Target is an executable
784                 lTargetName = ReplaceExtention(vTargetName, kExeExtention);
785                 vTargetExe = lTargetName;
786             }
787         else
788         {
789             // Possible error. The user wants to link but no 'main' detected
790             // so assume they know what they are doing and also assume an
791             // executable is required.
792             lTargetName = ReplaceExtention(vTargetName, kExeExtention);
793             vTargetExe = lTargetName;
794         }
795     }
796     else
797         // Not linking and not archiving, so no target is required.
798         lTargetName = "";
799
800     if (lTargetName.length > 0)
801     {
802         // Get the full name of the target's location.
803         lTargetName = util.pathex.CanonicalPath(lTargetName, false);
804         lTargetDir = getDirName(lTargetName);
805
806         // Show user if required to.
807         if((vVerbose == True) || (vNames == True) ) {
808             writefln("\nBuilding target '%s'", lTargetName);
809         }
810
811         // Shorten the target name for future usages.
812         lTargetName = util.pathex.AbbreviateFileName(lTargetName);
813
814
815         // If the target doesn't exist or it is older than
816         // most recently modified dependant file, we need to
817         // rebuild the target.
818         lTargetTime = util.fdt.GetFileTime (lTargetName);
819         if(vVerbose == True) {
820             writefln("Time %s for %s (target)", lTargetTime.toString(), lTargetName);
821             writefln("Time %s (most recent)", lMostRecentTime.toString());
822         }
823         if(lTargetTime < lMostRecentTime) {
824             lBuildRequired = True;
825         }
826     }
827     else if((vVerbose == True) || (vNames == True) ) {
828             writefln("\nCompiling only. No target will be built.");
829         }
830
831     // Run any external tools referenced in the source files.
832     foreach( ExternRef lRef; vExternals)
833     {
834         lRunResult = ProcessExternal( lRef );
835         if (lRunResult != 0)
836             // If an external tool fails, stop immediately.
837             return lRunResult;
838     }
839
840     if (Source.SourceIndex.length == 0)
841     {
842         /* It is possible to only have object and library
843            files on the command line, in which case we
844            just need to link them rather than compile.
845         */
846
847         // No files to compile, just link files, so collect
848         // all the object files to link in.
849         foreach( char[] lFileName; vLinkFiles.keys)
850         {
851             // Only include OBJECT files.
852             if (util.str.ends(lFileName , kObjExtention) == True)
853                 lFilesToLink ~= lFileName;
854         }
855     }
856     else foreach (int i, char[] lFileName; Source.ScanOrder)
857     {
858         // Check each source to see if we need to recompile it.
859         Source lCurrentSource;
860         Bool lNeedsCompiling;
861         char[] lShortFileName;
862         char[] lFileType;
863
864         lNeedsCompiling = vForceCompile;
865
866         lCurrentSource = Source.SourceIndex[lFileName];
867         if (lCurrentSource.Ignore)
868             continue;
869
870         lShortFileName = util.pathex.AbbreviateFileName(lFileName);
871         lFileType = std.path.getExt(lShortFileName);
872         if (lFileType != kSrcExtention && lFileType != kDdocExtention)
873             continue;
874
875         // Only source files are examined from here on.
876         if (lCurrentSource.NoLink || lFileType == kDdocExtention)
877         {
878             lNonLinkingSources ~= lCurrentSource;
879         }
880
881         if(lCurrentSource.FilesTime > lCurrentSource.ObjectsTime)
882         {
883             if (vVerbose == True)
884                 writefln("%s newer than its object file", lShortFileName);
885             lNeedsCompiling = True;
886
887         } else if(lCurrentSource.DependantsTime > lCurrentSource.ObjectsTime) {
888             if (vVerbose == True)
889                 writefln("%s has newer dependants than its object file.",
890                                 lShortFileName);
891
892             lNeedsCompiling = True;
893         }
894
895         if(lNeedsCompiling == True)
896         {
897             lBuildRequired = True;
898             lCompiling = True;
899             lFilesToCompile ~= lShortFileName;
900
901             // Check to see if I'm allowed to link this file.
902             if (lCurrentSource.NoLink == false)
903                 lFilesToLink ~= util.pathex.AbbreviateFileName(lCurrentSource.ObjectName);
904
905             if (vTestRun == False)
906             {
907                 if (lCurrentSource.IncrementBuildNumber())
908                 {
909                     if (vVerbose == True)
910                         writefln("New build number %d for %s",
911                                lCurrentSource.BuildNumber,
912                                lCurrentSource.ModuleName);
913
914                 }
915             }
916         }
917         else if (lCurrentSource.NoLink == false)
918         {
919             lFilesToLink ~= util.pathex.AbbreviateFileName(lCurrentSource.ObjectName);
920         }
921
922     }
923
924     if( lBuildRequired == False )
925     {
926         if (vSilent == False)
927             writefln ("Files are up to date, no build required.");
928         return 0;
929     }
930
931     foreach(char[] lFileName; lFilesToCompile)
932     {
933         lSourcesToCompile ~= util.str.enquote(lFileName) ~ "\n";
934     }
935
936     // Construct a Optlink Definition file if requested to.
937     version(Windows)
938     {
939         if (Source.WasMainGUI) {
940             AddBuildDef("EXETYPE NT");
941             if (vWinVer.length != 0)
942                 AddBuildDef("SUBSYSTEM WINDOWS," ~ vWinVer);
943             else
944                 AddBuildDef("SUBSYSTEM WINDOWS");
945             vDefaultLibs ~= "gdi32.lib";
946         }
947         else if (Source.WasMainDLL) {
948             AddBuildDef(`LIBRARY "` ~ std.path.getBaseName(lTargetName) ~ `"`);
949             AddBuildDef("EXETYPE NT");
950             if (vWinVer.length != 0)
951                 AddBuildDef("SUBSYSTEM WINDOWS," ~ vWinVer);
952             else
953                 AddBuildDef("SUBSYSTEM WINDOWS");
954             AddBuildDef("CODE PRELOAD DISCARDABLE SHARED EXECUTE");
955             AddBuildDef("DATA PRELOAD SINGLE WRITE");
956         }
957         else if (vConsoleApp == True) {
958             AddBuildDef(`EXETYPE DOS`);
959         }
960
961         if ((vNoDef == False) && (vBuildDef.length > 0))
962         {
963             lDefName = ReplaceExtention(lTargetName, "def");
964             if (vTemporaryPath.length != 0)
965             {
966                 lDefName = vTemporaryPath ~ std.path.getBaseName(lDefName);
967             }
968             lDefName = util.pathex.AbbreviateFileName(lDefName);
969             util.fileex.CreateTextFile(lDefName, vBuildDef);
970
971         }
972     }
973
974     version(Posix)
975     {
976             vDefaultLibs ~= "c";
977     }
978     // Add any library or external obj files required.
979     if (lLinking == True)
980     {
981         lLibraryFiles.length = 0;
982
983         foreach (char[] lFileName; vLinkFiles.keys)
984         {
985             char[] lCmdItem;
986
987             if (lFileName.length > 0)
988             {
989                 lCmdItem = lFileName;
990                 if ( util.str.ends(lCmdItem, "." ~ kLibExtention) == True)
991                 {
992                     // Cut off extention.
993                     lCmdItem.length = lCmdItem.length - kLibExtention.length - 1;
994 //                        lPrefix = "-L-l"; // Needed for linker to find the library.
995                     lLibraryFiles ~= lCmdItem;
996                 }
997                 else
998                 {
999                     lFilesToLink ~= lCmdItem;
1000                 }
1001             }
1002         }
1003     }
1004
1005     if ((lLinking == True) || (lCompiling == True))
1006     {
1007         // COMPILE phase ...
1008         if (lSourcesToCompile.length > 0)
1009         {
1010             // Ok, I have some compiling to do!
1011             char[] lCommandLine;
1012
1013             lCommandLine = GatherCompilerArgs(lLinking) ~ lSourcesToCompile;
1014
1015             if (vUseResponseFile == True)
1016             {
1017                 lDResponseFileName = ReplaceExtention(lTargetName, "rsp");
1018                 if (vTemporaryPath.length != 0)
1019                 {
1020                     lDResponseFileName = vTemporaryPath ~ std.path.getBaseName(lDResponseFileName);
1021                 }
1022                 lDResponseFileName = util.pathex.AbbreviateFileName(lDResponseFileName);
1023                 util.fileex.CreateTextFile(lDResponseFileName,lCommandLine);
1024                 lCommand = vCompilerPath ~ vCompilerExe ~ vCompileOnlySwitch ~ " @" ~ lDResponseFileName;
1025             }
1026             else
1027             {   // using commandline; may run into limits
1028                 lCommandLine=std.string.replace(lCommandLine, "\n", " ");
1029                 lCommand = vCompilerPath ~ vCompilerExe  ~ vCompileOnlySwitch ~ " " ~ lCommandLine;
1030             }
1031
1032             if (vVerbose == True) {
1033                 writefln("Compiling with ..........\n%s\n", lCommandLine);
1034             }
1035
1036             // Run Compiler to compile the source files that need it.
1037             lRunResult = RunCommand(lCommand);
1038             if (lRunResult != 0)
1039                 vExecuteProgram = False;
1040         }
1041
1042         // LINK phase ...
1043         if ( (lRunResult == 0) && (lFilesToLink.length > 0) && (lLinking == True))
1044         {
1045             char[] lCommandLine;
1046             char[] lLinkerSwitches;
1047             Bool IsMapping = False;
1048
1049             // Build the command line for the linker.
1050             lCommandLine = "";
1051             version(Windows)
1052             {
1053                 // Transfer linker switches from Compiler switches.
1054                 foreach (char[] lCompileArg; vCompilerArgs)
1055                 {
1056                     if (util.str.begins(lCompileArg, "-L") == True)
1057                     {
1058                         lLinkerSwitches ~= lCompileArg[2..$];
1059                     }
1060                 }
1061                 foreach( char[] lSwitch; std.string.split(vLinkerDefSwitches ~ lLinkerSwitches, "/"))
1062                 {
1063                     if (lSwitch == "nomap")
1064                     {
1065                         IsMapping = False;
1066                     }
1067                     else if (lSwitch == "map")
1068                     {
1069                         IsMapping = True;
1070                     }
1071                 }
1072
1073                 // (1) Gather the object file names
1074                 foreach(char[] lFile; lFilesToLink)
1075                 {
1076                     lCommandLine ~= lFile ~ "+";
1077                 }
1078                 if (lCommandLine.length > 0)
1079                     lCommandLine[$-1] = '\n';
1080
1081                 // (2) Set the output file name
1082                 lCommandLine ~= util.str.enquote(util.pathex.AbbreviateFileName(lTargetName)) ~ "\n";
1083
1084                 // (3) Set the map name
1085                 if (IsMapping == True)
1086                     lCommandLine ~= ReplaceExtention(lTargetName, "map");
1087
1088                 lCommandLine ~= "\n";
1089
1090                 // (4) Gather the libraries names.
1091                 // Include the default libraries first.
1092                 lLibraryFiles = vDefaultLibs ~ lLibraryFiles;
1093                 if (lLibraryFiles.length > 0)
1094                 {
1095                     foreach( char[] lLib; lLibraryFiles)
1096                     {
1097                         lCommandLine ~= util.str.enquote(lLib) ~"+";
1098                     }
1099                     lCommandLine[$-1] = '\n';
1100                 }
1101                 else
1102                     lCommandLine ~= "\n";
1103
1104                 // Include the explictly named libraries.
1105                 if (vLibPaths.length > 1)
1106                 {
1107                     if (vLibPaths[0..1] == vConfigSep)
1108                         vLibPaths = vLibPaths[1..$];
1109
1110                     // Include the paths to the libraries.
1111                     if (vVerbose == True)
1112                         writefln("Setting LIB=%s", vLibPaths);
1113                     putenv("LIB=" ~ vLibPaths[1..$]);
1114                 }
1115
1116                 // (5) Set the 'def' file name
1117                 if (lDefName.length > 0)
1118                     lCommandLine ~= util.str.enquote(lDefName) ~ "\n";
1119                 else
1120                     lCommandLine ~= "\n";
1121
1122                 // (6) Gather the resource file names
1123
1124                 // (7) Gather then switches
1125                 lCommandLine ~= vLinkerDefSwitches;
1126                 lCommandLine ~= lLinkerSwitches;
1127                 lCommandLine ~= "\n";
1128             }
1129
1130             version(Posix)
1131             {
1132                 // Transfer linker switches from Compiler switches.
1133                 foreach (char[] lCompileArg; vCompilerArgs)
1134                 {
1135                     if (util.str.begins(lCompileArg, "-L") == True)
1136                     {
1137                         lLinkerSwitches ~= lCompileArg[2..$];
1138                     }
1139                 }
1140                 foreach( char[] lSwitch; std.string.split(vLinkerDefSwitches ~ lLinkerSwitches, "/"))
1141                 {
1142                     if (lSwitch == "nomap")
1143                     {
1144                         IsMapping = False;
1145                     }
1146                     else if (lSwitch == "map")
1147                     {
1148                         IsMapping = True;
1149                     }
1150                 }
1151
1152                 // (1) Gather the object file names
1153                 foreach(char[] lFile; lFilesToLink)
1154                 {
1155                     lCommandLine ~= lFile ~ "\n";
1156                 }
1157
1158                 // (2) Set the output file name
1159                 lCommandLine ~= vLinkOutputSwitch ~ util.str.enquote(util.pathex.AbbreviateFileName(lTargetName)) ~ "\n";
1160
1161                 // (3) Set the map name
1162                 if (IsMapping == True)
1163                     lCommandLine ~= "-M\n";
1164
1165                 // (4) Gather the libraries names.
1166                 // Include the default libraries first.
1167                 foreach( char[] lLib; vDefaultLibs ~ lLibraryFiles)
1168                 {
1169                     lCommandLine ~= vLinkLibSwitch ~ util.str.enquote(lLib) ~ "\n";
1170                 }
1171
1172                 if (vLibPaths.length > 1)
1173                 {
1174                     if (vLibPaths[0..1] == vConfigSep)
1175                         vLibPaths = vLibPaths[1..$];
1176
1177                     // Include the paths to the libraries.
1178                     if (vVerbose == True)
1179                         writefln("Setting LIB=%s", vLibPaths);
1180                     char[][] lLibPaths;
1181                     lLibPaths = std.string.split(vLibPaths, vConfigSep);
1182                     foreach(char[] lLib; lLibPaths)
1183                     {
1184                         if (lLib.length > 0)
1185                             lCommandLine ~= "-L" ~ util.str.enquote(lLib) ~ "\n";
1186                     }
1187                 }
1188
1189                 // (4) Gather then switches
1190                 lCommandLine ~= vLinkerDefSwitches;
1191                 lCommandLine ~= lLinkerSwitches;
1192                 lCommandLine ~= "\n";
1193             }
1194
1195             if (vUseResponseFile == True)
1196             {
1197                 lLinkResponseFileName = ReplaceExtention(lTargetName, "ksp");
1198                 if (vTemporaryPath.length != 0)
1199                 {
1200                     lLinkResponseFileName = vTemporaryPath ~ std.path.getBaseName(lLinkResponseFileName);
1201                 }
1202                 lLinkResponseFileName = util.pathex.AbbreviateFileName(lLinkResponseFileName);
1203                 util.fileex.CreateTextFile(lLinkResponseFileName,lCommandLine);
1204                 lCommand = vLinkerPath ~ vLinkerExe ~ " @" ~ lLinkResponseFileName;
1205             }
1206             else
1207             {   // using commandline; may run into limits
1208                 lCommandLine=std.string.replace(lCommandLine, "\n", " ");
1209                 lCommand = vLinkerPath ~ vLinkerExe ~ " " ~ lCommandLine;
1210             }
1211
1212             if (vVerbose == True) {
1213                 writefln("Linking with ..........\n%s\n", lCommandLine);
1214             }
1215
1216             // Run Linker
1217             if (vSilent == True)
1218             {
1219                 version(Posix) lCommand ~= " >/dev/null";
1220                 version(Windows) lCommand ~= " >nul";
1221             }
1222
1223             lRunResult = RunCommand(lCommand);
1224             if (lRunResult != 0)
1225                 vExecuteProgram = False;
1226         }
1227
1228     }
1229     else
1230     {
1231         if (vSilent == False)
1232             writefln("No build required.");
1233         lRunResult = 0;
1234     }
1235
1236     // Now build a library if requested to.
1237     if ( (Source.WasMainDLL)  && (lRunResult == 0) )
1238     {
1239         char[] lTargetFileName;
1240         char[] lImpLibPath;
1241         ulong[] lImpManf;
1242
1243         vExecuteProgram = False;
1244
1245         lImpLibPath = util.pathex.LocateFile("implib.exe", GetEnv(kPathId));
1246         if (util.fileex.FileExists(lImpLibPath))
1247         {
1248             lImpManf = util.fileex.FindInFile(lImpLibPath, "Borland");
1249             if (lImpManf.length != 0)
1250                 lImpLibPath ~= " -a ";
1251             else
1252                 lImpLibPath ~= " /system ";
1253
1254             lTargetFileName = std.path.getBaseName(lTargetName);
1255             lRunResult = RunCommand(lImpLibPath ~ std.path.addExt(lTargetFileName, "lib")
1256                             ~ " " ~ std.path.addExt(lTargetFileName, "dll") );
1257         }
1258     }
1259     else if ( (vLibraryAction == LibOpt.Build) && (lRunResult == 0))
1260     {
1261         int lFileCount;
1262
1263         vExecuteProgram = False;
1264
1265         lOutText = vLibrarianOpts ~ std.path.linesep;
1266         lOutText ~= lTargetName ~  std.path.linesep;  // Create a new library
1267
1268         foreach( char[] lFileName; lFilesToLink)
1269         {
1270             char[] lFileDir;
1271
1272             lFileDir = getDirName(lFileName);
1273             if ((vAllObjects == True) || lFileDir == "" || lFileDir == lTargetDir)
1274             {
1275                 if (lFileName.length > 1 + kSrcExtention.length)
1276                 {
1277                     if (lFileName[$-kSrcExtention.length .. $] == kSrcExtention)
1278                     {
1279                         lFileName = lFileName[0..$-kSrcExtention.length] ~ kObjExtention;
1280                     }
1281                 }
1282                 lFileCount++;
1283                 lOutText ~= lFileName ~ std.path.linesep;
1284             }
1285         }
1286
1287         if (lFileCount > 0)
1288         {
1289             if (vUseResponseFile == True) {
1290                 lLResponseFileName = ReplaceExtention(lTargetName, "lsp");
1291                 if (vTemporaryPath.length != 0)
1292                 {
1293                     lLResponseFileName = vTemporaryPath ~ std.path.getBaseName(lLResponseFileName);
1294                 }
1295                 util.fileex.CreateTextFile(lLResponseFileName,lOutText);
1296                 lCommand = vLibrarianPath ~ vLibrarian ~ " @" ~ lLResponseFileName;
1297             }
1298             else
1299             {   // using commandline, may run into limits
1300                 lCommand  = vLibrarianPath ~ vLibrarian ~ " " ~ std.string.replace(lOutText,std.path.linesep," ");
1301             }
1302
1303             if (vVerbose == True) {
1304                 writefln("Librarian with ..........\n%s\n", lOutText);
1305             }
1306
1307             lRunResult = RunCommand(lCommand);
1308         }
1309     }
1310
1311     // Optional clean up.
1312     if (vCleanup == True)
1313     {
1314         char[][] lHitList;
1315
1316         if (vVerbose == True) {
1317             writefln("Cleaning up ...");
1318         }
1319         foreach(char[] lFileName; Source.ScanOrder)
1320         {
1321             Source lSource;
1322             lSource = Source.SourceIndex[lFileName];
1323             if (lSource.Ignore)
1324                 continue;
1325
1326             if (lSource.ObjectName.length > 0)
1327             {
1328                 lHitList ~= lSource.ObjectName;
1329             }
1330         }
1331
1332         // Build's own temprary files.
1333         lHitList ~= lDResponseFileName;
1334         lHitList ~= lLinkResponseFileName;
1335         lHitList ~= lLResponseFileName;
1336         lHitList ~= lDefName;
1337
1338         // Possible ones created by compiler, linker, and librarian.
1339         lHitList ~= ReplaceExtention(lTargetName, "map");
1340         lHitList ~= ReplaceExtention(lTargetName, "bak");
1341         lHitList ~= ReplaceExtention(lTargetName, "lst");
1342
1343         foreach(char[] lFilename; lHitList)
1344         {
1345             if (lFilename.length > 0)
1346             {
1347                 if (util.fileex.FileExists( lFilename ) )
1348                 {
1349                     if (vVerbose == True) {
1350                         writefln("  removing %s", lFilename);
1351                     }
1352                     std.file.remove(lFilename);
1353                 }
1354             }
1355         }
1356
1357     }
1358
1359     return lRunResult;
1360
1361 }
1362
1363 // -------------------------------------------
1364 char [] GatherCompilerArgs(Bool pLinking)
1365 // -------------------------------------------
1366 {
1367     char[] lOutText;
1368
1369     foreach(char [] lRoot; vImportRoots)
1370     {
1371         version(Posix)
1372             vDefaultCompArgs ~= vImportPathSwitch ~ "\"" ~ lRoot ~ "\"";
1373         else
1374             vDefaultCompArgs ~= vImportPathSwitch ~ lRoot;
1375     }
1376
1377     foreach(char[] lArg; vDefaultCompArgs)
1378     {
1379         AddCompilerArg( lArg );
1380     }
1381
1382     // Build command files for compilation.
1383     foreach (char[] lCompileArg; vCompilerArgs)
1384     {
1385         // Ignore empty args
1386         if (lCompileArg.length > 0)
1387         {
1388             // Enclose in quotes if no quotes are currently present.
1389             if (find(lCompileArg, "\"") == -1)
1390                 // Arguments containing a blank need to be quoted.
1391                 if (find(lCompileArg, " ") != -1)
1392                 {
1393                     // Strip off any trailing shell escape lead-in character.
1394                     if (lCompileArg[$-1] == '\\')
1395                         lCompileArg.length = lCompileArg.length - 1;
1396                     lOutText ~= std.string.format(`"%s"`, lCompileArg);
1397                 }
1398                 else
1399                     lOutText ~= lCompileArg;
1400             else
1401                 lOutText ~= lCompileArg;
1402
1403             // Terminate with a newline char.
1404             lOutText ~= "\n";
1405         }
1406     }
1407
1408     if (pLinking == False)
1409     { // No linking allowed.
1410         if (vNoLinkSwitch.length > 0)
1411         {
1412             if (find(vNoLinkSwitch, " ") != -1)
1413                 lOutText ~= std.string.format(`"%s"`,vNoLinkSwitch);
1414             else
1415                 lOutText ~= vNoLinkSwitch;
1416             // Terminate with a newline char.
1417             lOutText ~= "\n";
1418
1419             AddCompilerArg( vNoLinkSwitch );
1420         }
1421     }
1422
1423     return lOutText;
1424 }
1425
1426
1427 // -------------------------------------------
1428 char[][] ModulesToIgnore()
1429 // -------------------------------------------
1430 {
1431     return vModulesToIgnore;
1432 }
1433
1434
1435 // -------------------------------------------
1436 void AddExternal(char[] pPath, char[][] pOpts)
1437 // -------------------------------------------
1438 {
1439     if (pPath.length > 0) {
1440         vExternals.length = vExternals.length + 1;
1441         vExternals[$-1].FilePath = pPath;
1442         vExternals[$-1].ToolOpts = pOpts;
1443         if (vVerbose == True) {
1444             writef("Added external file to be built: %s", pPath);
1445             foreach( char[] lOpt; pOpts)
1446                 writef(" `%s`", lOpt);
1447             writefln("");
1448         }
1449     }
1450 }
1451
1452 // -------------------------------------------
1453 void AddLink(char[] pPath)
1454 // -------------------------------------------
1455 {
1456     if (pPath.length == 0)
1457         return;
1458     if ((pPath in vLinkFiles) is null)
1459         vLinkFiles[pPath] = true;
1460 }
1461
1462 // -------------------------------------------
1463 void AddTarget(char[] pPath)
1464 // -------------------------------------------
1465 {
1466     if (pPath.length == 0)
1467         return;
1468
1469     if (vPragmaTargetName.length == 0)
1470         vPragmaTargetName = pPath;
1471     else if (vPragmaTargetName != pPath) {
1472         if (vVerbose == True) {
1473             writefln("Multiple pragma(target,...) detected. '%s' will be used and '%s' rejected.",
1474                     vPragmaTargetName, pPath );
1475         }
1476     }
1477
1478 }
1479
1480 // -------------------------------------------
1481 void AddBuildDef(char[] pText, bool pReplace = false)
1482 // -------------------------------------------
1483 {
1484     int lPos;
1485     char[] lLowerText;
1486     static uint[ char[] ] lElementIdx;
1487
1488     if (vNoDef == True)
1489         return;
1490
1491     lLowerText = std.string.tolower(pText);
1492
1493     lPos = std.string.find(lLowerText, ' ');
1494     if (lPos == -1)
1495         lPos = lLowerText.length;
1496     lLowerText.length = lPos;
1497
1498     if (lLowerText in lElementIdx)
1499     {
1500         if (pReplace)
1501             vBuildDef[ lElementIdx[lLowerText] ] = pText;
1502     }
1503     else {
1504         vBuildDef ~= pText;
1505         lElementIdx[ lLowerText ] = vBuildDef.length-1;
1506     }
1507 }
1508
1509 // -------------------------------------------
1510 void AddCompilerArg(char[] pArg)
1511 // -------------------------------------------
1512 {
1513     bool lFound;
1514
1515     if (pArg.length > 0)
1516     {
1517         // Translate exported version pragmas.
1518         if (pArg.length > 3)
1519         {
1520             if (pArg[0..3] == `+v+`)
1521                 pArg = vVersionSwitch ~ pArg[3..$];
1522         }
1523
1524         lFound = false;
1525         foreach(char[] lArg; vCompilerArgs)
1526         {
1527             if (lArg == pArg)
1528             {
1529                 lFound = true;
1530                 break;
1531             }
1532         }
1533         if (! lFound )
1534             vCompilerArgs ~= pArg;
1535     }
1536 }
1537
1538 // -------------------------------------------
1539 char[] AddRoot(char[] pRootName)
1540 // -------------------------------------------
1541 {
1542     static bool [char[]] lRootHash;
1543     char[] lFullName;
1544     char[] lSearchName;
1545
1546     if(pRootName.length == 0) {
1547         return pRootName;
1548     }
1549     else {
1550     lFullName = util.pathex.CanonicalPath(pRootName);
1551     version(Windows) lSearchName = std.string.tolower(lFullName);
1552     version(Posix)   lSearchName = lFullName;
1553     if( !(lSearchName in lRootHash) )
1554     {
1555         vImportRoots ~= lFullName;
1556         lRootHash[lSearchName] = true;
1557         return lFullName;
1558     }
1559     else
1560         return "";
1561 }
1562 }
1563
1564 Bool AutoImports()
1565 {
1566     return vAutoImports;
1567 }
1568
1569 char[][] GetImportRoots()
1570 {
1571     return vImportRoots;
1572 }
1573
1574 // function to replace tokens in the form %<SYM>%  with environment data.
1575 // -------------------------------------------
1576 char[] ExpandEnvVar(char[] pLine)
1577 // -------------------------------------------
1578 {
1579     char[] lLine;
1580     char[] lSymName;
1581     int lPos;
1582     int lEnd;
1583
1584     lLine.length = 0;
1585     for( lPos = 0; lPos < pLine.length; lPos++ )
1586     {
1587         if (pLine[lPos] == '%')
1588         {
1589             lSymName.length = 0;
1590             for(lEnd = lPos+1; (lEnd < pLine.length) && (pLine[lEnd] != '%'); lEnd++ )
1591             {
1592                 lSymName ~= pLine[lEnd];
1593             }
1594             if (lSymName.length > 0)
1595             {
1596                 if (lSymName == "@P")
1597                 {
1598                     // Special symbol that refers to the DMD configuration file's directory.
1599                     lSymName = (vConfigPath ? vConfigPath[0..length-1] : "");
1600                 }
1601                 else if (lSymName == "@D")
1602                 {
1603                     // Special symbol that refers to the compiler's file's directory.
1604                     lSymName = (vCompilerPath ? vCompilerPath[0..length-1] : "");
1605                 }
1606                 else
1607                 {
1608                     lSymName = (GetEnv(std.utf.toUTF8(lSymName)));
1609                 }
1610                 lLine ~= lSymName;
1611             }
1612             lPos = lEnd;
1613         }
1614         else
1615         {
1616             lLine ~= pLine[lPos];
1617         }
1618     }
1619     return lLine;
1620 }
1621
1622 void Process_DFLAGS(char[] pText)
1623 {
1624     int      lPos;
1625     int      lEndPos;
1626     char[]   lRootName;
1627     char[][] lRoots;
1628     char[][] lArgs;
1629     int      lArg;
1630     bool     lInArg;
1631     char     lQuote;
1632
1633     lInArg = false;
1634     lArg = -1;
1635     lQuote = 0;
1636     foreach (char lArgChar; pText)
1637     {
1638         if ( (lArgChar == '"') || (lArgChar == '\'') )
1639         {
1640             if (lQuote == lArgChar)
1641             {
1642                 lQuote = 0;
1643                 continue;
1644             }
1645
1646             if (lQuote == 0)
1647             {
1648                 lQuote = lArgChar;
1649                 continue;
1650             }
1651         }
1652
1653         if (lArgChar == ' ')
1654         {
1655             if (lQuote == 0)
1656             {
1657                 lInArg = false;
1658                 continue;
1659             }
1660         }
1661
1662         if (lInArg == false)
1663         {
1664             lArg++;
1665             lArgs.length = lArg+1;
1666             lInArg = true;
1667         }
1668         lArgs[lArg] ~= lArgChar;
1669
1670     }
1671
1672     foreach(char[] lSwitch; lArgs)
1673     {
1674         if ((lSwitch.length > 0) && (lSwitch[0] == '-'))
1675         {
1676             if (vDelayedValue != null)
1677             {
1678                 // Used when an switch needs the subsequent arg to
1679                 // be its value.
1680                 *vDelayedValue = lSwitch;
1681                 vDelayedValue = null;
1682                 vBuildArgs ~= lSwitch;
1683                 continue;
1684             }
1685
1686             if (lSwitch[1] == 'I')
1687             {
1688                 lRoots = std.string.split(lSwitch[2..length], vConfigSep);
1689                 foreach(char[] lRoot; lRoots)
1690                 {
1691                     lRootName = AddRoot(lRoot);
1692                     /*
1693                     version(Posix)
1694                         vDefaultCompArgs ~= vImportPathSwitch ~ "\"" ~ lRoot ~ "\"";
1695                     else
1696                         vDefaultCompArgs ~= vImportPathSwitch ~ lRoot;
1697                         */
1698                     if(vVerbose == True) {
1699                         if (lRootName.length > 0){
1700                             writefln(" added root from config file %s", lRootName);
1701                         }
1702                     }
1703                 }
1704             }
1705             else
1706             {
1707                 version(DigitalMars)
1708                 {
1709                 if (util.str.IsLike(lSwitch,  (vOutFileSwitch ~ "*")) == True)
1710                 {
1711                     // Target name (eg. -oftestapp)
1712                     vCommandTargetName = lSwitch[vOutFileSwitch.length .. $];
1713                     vBuildArgs ~= lSwitch;
1714                     continue;
1715                 }
1716                 }
1717
1718                 version(GNU)
1719                 {
1720                     if (lSwitch == vOutFileSwitch)
1721                     {
1722                         // Target name (eg. -o testapp)
1723                         vDelayedValue = &vCommandTargetName;
1724                         vBuildArgs ~= lSwitch;
1725                         continue;
1726                     }
1727                 }
1728
1729                 if (util.str.IsLike(lSwitch,  std.utf.toUTF32(vTemporaryPathSwitch ~ "*")) == True)
1730                 {
1731                     char[] lbRoot;
1732
1733                     vTemporaryPath = lSwitch[vTemporaryPathSwitch.length .. $];
1734                     if (vTemporaryPath.length > 0 && vTemporaryPath[$-1..$] != std.path.sep)
1735                         vTemporaryPath ~= std.path.sep;
1736
1737                     version(DigitalMars)
1738                     {
1739                         lbRoot = AddRoot(vTemporaryPath);
1740                         if (lbRoot.length > 0){
1741                             if(vVerbose == True) {
1742                                     writefln("Added root from config file Object Write Path = %s",lbRoot);
1743                             }
1744                             util.pathex.MakePath(lbRoot);
1745                         }
1746                     }
1747
1748                     vBuildArgs ~= lSwitch;
1749                     // This one actually *is* passed thru.
1750                 }
1751
1752                 AddCompilerArg ( lSwitch );
1753             }
1754         }
1755     }
1756 }
1757
1758 // -------------------------------------------
1759 void ReadEnviron()
1760 // -------------------------------------------
1761 {
1762     char[]   lSymValue;
1763
1764     // Check for a environment flag before config file.
1765     lSymValue = GetEnv("DFLAGS");
1766     if (lSymValue.length > 0 )
1767     {
1768         if (vVerbose == True)
1769             writefln("Analyzing environment symbol DFLAGS=%s", lSymValue);
1770         Process_DFLAGS( lSymValue );
1771     }
1772 }
1773
1774 // -------------------------------------------
1775 void ReadConfigFile()
1776 // -------------------------------------------
1777 {
1778     char[]   lPath;
1779     char[][] lTextLines;
1780     int      lPos;
1781
1782     if (vConfigFile.length == 0)
1783     {
1784         // There is no configuration file to process
1785       return;
1786     }
1787
1788     lPath = vConfigPath ~ vConfigFile ;
1789     util.fileex.GetTextLines(lPath, lTextLines, util.fileex.GetOpt.Exists);
1790
1791     if (vVerbose == True) {
1792         writefln("Reading from config: %s", lPath);
1793     }
1794
1795     foreach(int i, char[] lLine; lTextLines)
1796     {
1797         // Strip off trailing whitespace.
1798         lLine = util.str.stripr(lLine);
1799
1800         // Replace any environment symbols with their value.
1801         if (vVerbose == True) {
1802             writefln(" Line %d: %s", i+1, lLine);
1803         }
1804         lLine = ExpandEnvVar(lLine);
1805
1806         // Examine DFLAGS
1807         lPos = find(lLine, "DFLAGS=");
1808         if(lPos == 0)
1809         {
1810             Process_DFLAGS(lLine[lPos+7..length]);
1811         } // end of DFLAGS processing.
1812
1813         // Examine LIB
1814         lPos = find(lLine, "LIB=");
1815         if(lPos == 0)
1816         {
1817             char[][] lPaths;
1818             lLine = lLine[lPos+4 .. length];
1819             lPaths.length = 1;
1820             foreach(char lChar; lLine)
1821             {
1822                 if (lChar == '"') {}
1823                 else if (lChar == ';')
1824                 {
1825                     lPaths.length = lPaths.length + 1;
1826                 }
1827                 else
1828                     lPaths[$-1] ~= lChar;
1829
1830             }
1831             for(int i = 0; i < lPaths.length; i++)
1832             {
1833                 lPaths[i] = std.string.strip(lPaths[i]);
1834             }
1835
1836             foreach( char[] lPath; lPaths)
1837             {
1838                 if (lPath.length > 0)
1839                 {
1840                     if (lPath[0] == '"' && lPath[length-1] == '"') {
1841                         lPath = lPath[1..length-1];
1842                     }
1843                     vLibPaths ~= vConfigSep ~ `"` ~ util.pathex.CanonicalPath(lPath) ~ `"`;
1844                 }
1845             }
1846             if(vVerbose == True)
1847             {
1848                 writefln(" use %s",vLibPaths);
1849             }
1850             continue;
1851         }
1852
1853         // Examine LINKCMD
1854         lPos = find(lLine, "LINKCMD=");
1855         if(lPos == 0) {
1856             // Strip out any quotes
1857             while( (lPos = find(lLine, "\"")) != -1)
1858             {
1859                 lLine = lLine[0..lPos] ~ lLine[lPos+1 .. $];
1860             }
1861
1862             vLinkerPath = std.path.getDirName(lLine[8..$]) ~ std.path.sep;
1863             vLinkerPath = util.pathex.CanonicalPath(vLinkerPath);
1864             vLinkerExe = std.path.getBaseName(lLine[7..$]);
1865             if(vVerbose == True) {
1866                 writefln(" linker path %s",vLinkerPath);
1867                 writefln(" linker is %s",vLinkerExe);
1868             }
1869             continue;
1870         }
1871
1872         lPos = find(lLine, "LIBCMD=");
1873         if(lPos == 0) {
1874             // Strip out any quotes
1875             while( (lPos = find(lLine, "\"")) != -1)
1876             {
1877                 lLine = lLine[0..lPos] ~ lLine[lPos+1 .. $];
1878             }
1879             vLibrarianPath = std.path.getDirName(lLine[7..$]) ~ std.path.sep;
1880             vLibrarianPath = util.pathex.CanonicalPath(vLibrarianPath);
1881
1882             vLibrarian = std.path.getBaseName(lLine[7..$]);
1883             if(vVerbose == True) {
1884                 writefln(" librarian path %s",vLibrarianPath);
1885                 writefln(" librarian is %s",vLibrarian);
1886             }
1887         }
1888
1889
1890         if (vLinkerPath.length == 0)
1891             vLinkerPath = vCompilerPath;
1892         if (vLibrarianPath.length == 0)
1893             vLibrarianPath = vLinkerPath;
1894     }
1895 }
1896
1897 // Display each entry in the supplied list.
1898 // -------------------------------------------
1899 void DisplayItems(char[][] pList, char[] pTitle = "")
1900 // -------------------------------------------
1901 {
1902     if (pList.length > 0) {
1903         if (pTitle.length > 0)
1904             writefln("\n%s",pTitle);
1905
1906         foreach(int lIndex, char[] lListEntry; pList) {
1907             writefln(" [%2d]: %s",lIndex,lListEntry);
1908         }
1909     }
1910 }
1911
1912 // -------------------------------------------
1913 void DisplayItems(ExternRef[] pList, char[] pTitle = "")
1914 // -------------------------------------------
1915 {
1916     if (pList.length > 0) {
1917         if (pTitle.length > 0)
1918             writefln("\n%s",pTitle);
1919
1920         foreach(int lIndex, ExternRef lListEntry; pList)
1921         {
1922             writef(" [%2d]: %s",lIndex,lListEntry.FilePath);
1923             if (lListEntry.ToolOpts.length > 0)
1924                 foreach(char[] lOpt; lListEntry.ToolOpts)
1925                     writef(" [%s]",lOpt);
1926             writefln("");
1927         }
1928     }
1929 }
1930
1931 // ------------------------------------------------
1932 char[] GetTemporaryPath()
1933 {
1934     return vTemporaryPath;
1935 }
1936
1937 // ------------------------------------------------
1938 Substitute[] GetMacros()
1939 {
1940     return vSubstitutions;
1941 }
1942
1943 // ------------------------------------------------
1944 char[] GetFullPathnameScan(char[] pFileName, int pScanList)
1945 // -------------------------------------------
1946 {
1947     if (pScanList == 0)
1948         return GetFullPathname(pFileName);
1949     else
1950         return GetFullPathname(pFileName, vSourceScanList);
1951 }
1952
1953 // ------------------------------------------------
1954 char[] GetFullPathname(char[] pFileName, char[][] pScanList = null)
1955 // -------------------------------------------
1956 {
1957     char[] lPossiblePath;
1958     char[] lLocalPath;
1959
1960     if (util.pathex.IsRelativePath(pFileName) == True && pScanList.length > 0)
1961     {
1962         // Do explicit scanning of supplied paths.
1963         foreach(char[] lNextRoot; pScanList) {
1964             lPossiblePath = ( lNextRoot ~ pFileName );
1965             if(util.fileex.FileExists(lPossiblePath)) {
1966                 return util.pathex.AbbreviateFileName(lPossiblePath);
1967             }
1968         }
1969
1970         // If not found in scan list, drop through to standard scan.
1971     }
1972
1973     // Look for file in current folder first.
1974     lLocalPath = util.pathex.CanonicalPath(pFileName, false);
1975     if(util.fileex.FileExists(lLocalPath)) {
1976         return util.pathex.AbbreviateFileName(lLocalPath);
1977     }
1978
1979     // Examine each known import root to see if the file lives there.
1980     foreach(char[] lNextRoot; vImportRoots) {
1981         lPossiblePath = ( lNextRoot ~ pFileName );
1982         if(util.fileex.FileExists(lPossiblePath)) {
1983             return util.pathex.AbbreviateFileName(lPossiblePath);
1984         }
1985     }
1986
1987     return util.pathex.AbbreviateFileName(lLocalPath);
1988 }
1989
1990
1991 // Function to locate where Complier is installed from the PATH symbol
1992 // -------------------------------------------
1993 char[] FindFileInPathList(char[] pSymName, char[] pFileName)
1994 // -------------------------------------------
1995 {
1996     char[][] lPaths;
1997     char[]   lCompilerPath;
1998     char[]   lRawValue;
1999
2000     // Assume that an environment symbol name was supplied,
2001     // but if that fails, assume its a list of paths.
2002     lRawValue = GetEnv(pSymName);
2003     if (lRawValue.length == 0)
2004         lRawValue = pSymName;
2005
2006     // Rearrange path list into an array of paths.
2007     lPaths = split(util.str.toASCII(lRawValue), std.path.pathsep);
2008
2009     lCompilerPath.length = 0;
2010     foreach(char[] lPath; lPaths)
2011     {
2012         if (lPath.length > 0)
2013         {
2014             // Ensure that the path ends with a valid separator.
2015             if (lPath[length-1] != std.path.sep[0] )
2016                 lPath ~= std.path.sep;
2017             // If the file is in the current path we can stop looking.
2018             if(util.fileex.FileExists(lPath ~ pFileName))
2019             {
2020                 // Return the path we actually found it in.
2021                 lCompilerPath = lPath;
2022                 break;
2023             }
2024         }
2025     }
2026
2027     return lCompilerPath;
2028 }
2029
2030 // -------------------------------------------
2031 int main(char[][] pArgs)
2032 // -------------------------------------------
2033 {
2034     int lBuildResult;
2035     char[] lCompPath;
2036     bool lSetPath = false;
2037
2038     /* Set routine addresses in the Source module. This allows
2039        the methods and functions in Source to access the optional
2040        functionality provided by this module.
2041     */
2042     source.AddRoot          = &AddRoot;
2043     source.GetImportRoots   = &GetImportRoots;
2044     source.ModulesToIgnore  = &ModulesToIgnore;
2045     source.AutoImports      = &AutoImports;
2046     source.AddTarget        = &AddTarget;
2047     source.AddLink          = &AddLink;
2048     source.AddExternal      = &AddExternal;
2049     source.AddBuildDef      = &AddBuildDef;
2050     source.GetFullPathname  = &GetFullPathname;
2051     source.GetFullPathnameScan = &GetFullPathnameScan;
2052     source.GetObjWritePath  = &GetTemporaryPath;
2053     source.GetMacros        = &GetMacros;
2054     source.AddCompilerArg   = &AddCompilerArg;
2055
2056     // Scan the PATH env symbol to locate the D compiler.
2057     lCompPath = FindFileInPathList(kPathId, vCompilerExe);
2058     if (lCompPath.length > 0){
2059         if (lCompPath[length-1] != std.path.sep[0])
2060             lCompPath ~= std.path.sep;
2061         vCompilerPath = lCompPath.dup;
2062         // Set config path to same as compiler unless it is already set.
2063         if (!(vConfigPath is null) && (vConfigPath.length == 0))
2064             vConfigPath = lCompPath.dup;
2065     }
2066
2067     if (vCompilerPath.length == 0)
2068     {
2069         vCompilerPath = util.pathex.GetInitCurDir;
2070         lSetPath = true;
2071     }
2072
2073     // Strip off application's path from arglist.
2074     vAppPath=pArgs[0];
2075     vAppName=getBaseName(vAppPath);
2076     version (Windows)
2077     {
2078         int DotPos;
2079         DotPos = rfind(vAppName, '.');
2080         if (DotPos != -1)
2081         {
2082             vAppName.length = DotPos;
2083         }
2084     }
2085
2086     pArgs=pArgs[1..pArgs.length];
2087     if (pArgs.length == 0) {
2088         // No other arguments so show usage message.
2089         DisplayUsage ();
2090         return 0;
2091     }
2092
2093
2094     GatherArgs( pArgs );
2095     source.mVerboseMode = vVerbose;
2096     if (vVerbose == True){
2097         writefln("*** build v%s (build %d)***", vAppVersion, build_bn.auto_build_number);
2098     }
2099
2100     foreach(char[] lArg; vCombinedArgs) {
2101         ProcessCmdLineArg( lArg );
2102     }
2103
2104     source.mMacroInput = vMacroInput;
2105     if (vMacroInput == True)
2106         ProcessMacroDefs(vVerbose);
2107
2108     if( (vTargetName.length == 0) && (vCommandTargetName.length == 0) ){
2109         throw new Exception("No target name supplied.");
2110     }
2111
2112     if ((vVerbose == True) || (vNames == True) )
2113         writefln("Current Dir '%s'", util.pathex.GetInitCurDir());
2114
2115     if (lSetPath)
2116     {
2117         if (vVerbose == True)
2118         {
2119             writefln("%s not found in PATH symbol, so assuming current directory",
2120                         vCompilerExe);
2121         }
2122     }
2123
2124     if (vCompilerPath[length-1] != std.path.sep[0])
2125         vCompilerPath ~= std.path.sep;
2126
2127
2128     if (vConfigPath.length == 0)
2129     {
2130         vConfigPath = vCompilerPath;
2131         if (vVerbose == True)
2132         {
2133             writefln("No configuration file path, so assuming current directory");
2134         }
2135     }
2136     if (util.str.ends(vConfigPath, std.path.sep) is False)
2137         vConfigPath ~= std.path.sep;
2138
2139     version(DigitalMars)
2140     {
2141         if (util.fileex.FileExists(vConfigPath ~ vConfigFile) == false)
2142         {
2143             throw new Exception(std.string.format("The configuration file '%s' was not found.",
2144                     vConfigPath ~ vConfigFile));
2145         }
2146     }
2147
2148     if (util.fileex.FileExists(vCompilerPath ~ vCompilerExe) == false)
2149     {
2150         throw new Exception(std.string.format("The compiler '%s' was not found.",
2151                 vCompilerPath ~ vCompilerExe));
2152     }
2153
2154     if (vVerbose == True){
2155         writefln("Compiler installed in %s",vCompilerPath);
2156         writefln("Configuration File installed in %s",vConfigPath);
2157     }
2158
2159     source.SetKnownVersions();
2160
2161     ReadEnviron();
2162     version(DigitalMars) ReadConfigFile();
2163
2164     // Rationalize the ignored modules list.
2165     foreach(char[] m; vModulesToNotice) {
2166         for (int i=0; i < vModulesToIgnore.length; i++) {
2167             if (vModulesToIgnore[i] == m) {
2168                 // Must remove from ignored list.
2169                 vModulesToIgnore = vModulesToIgnore[0..i] ~ vModulesToIgnore[i+1..length];
2170                 i--;
2171             }
2172         }
2173     }
2174
2175
2176     // Process the D Source files that were on the command line.
2177     // Each file must already exist. If any does not exist, then
2178     // the application will abort.
2179     bool lAllExist = true;
2180
2181     // List all missing files (if any).
2182     foreach( char[] lFile; vCmdLineSourceFiles)
2183     {
2184         if (! util.fileex.FileExists( GetFullPathname(lFile, vSourceScanList)) )
2185         {
2186             if (vSilent == False)
2187                 writefln("** File '%s' not found.", lFile);
2188             lAllExist = false;
2189         }
2190     }
2191
2192     if ( lAllExist == false)
2193     {
2194         throw new Exception ("Not all supplied files exist.");
2195     }
2196
2197     lBuildResult = Build();
2198
2199     if(vVerbose == True) {
2200         writefln("");
2201         DisplayItems(vBuildArgs,      "build args: ...............");
2202         DisplayItems(vCompilerArgs,   "compiler args: ................");
2203         DisplayItems(vCmdLineSourceFiles,   "command line files: ...............");
2204         DisplayItems(Source.ScanOrder,
2205                                       "source files: ...............");
2206         DisplayItems(vLinkFiles.keys, "link files: ...............");
2207         DisplayItems(vExternals,      "externally built files: ...............");
2208         DisplayItems(vImportRoots,    "import roots: .................");
2209         DisplayItems(vModulesToIgnore,"ignored packages: .................");
2210         DisplayItems(vModulesToNotice,"noticed package: .................");
2211     }
2212
2213     if ((vExecuteProgram == True) && (vTargetExe.length > 0))
2214     {
2215         RunCommand( vTargetExe ~ " " ~ std.string.strip(vRunParms));
2216     }
2217
2218     return lBuildResult;
2219
2220
2221 }
2222
2223 void ProcessResponseFile(char[] pArg, Bool pVerbose)
2224 {
2225     // A response file is being used.
2226     char[] lRespFileName;
2227     char[][] lRespLines;
2228
2229     if (pArg.length == 1)
2230         lRespFileName = "build.brf";
2231     else
2232         lRespFileName = pArg[1..length].dup;
2233
2234     if (std.path.getExt(lRespFileName).length == 0)
2235     {
2236         lRespFileName ~= ".brf";
2237     }
2238
2239     if (pVerbose == True)
2240         writefln("Response file %s", lRespFileName);
2241
2242     lRespLines = util.fileex.GetTextLines(lRespFileName, util.fileex.GetOpt.Exists);
2243     foreach(char[] lArg; lRespLines)
2244     {
2245         // Locate any comment text in the line.
2246         int lPos = std.string.find(lArg, "#");
2247         if (lPos != -1)
2248         {
2249             // Truncate the line at the '#' character.
2250             lArg.length = lPos;
2251         }
2252
2253         lArg = std.string.strip(lArg);
2254         if (lArg.length > 1){
2255             if (pVerbose == True)
2256                 writefln("Response file arg: %s", lArg);
2257             GatherOneArg( lArg );
2258         }
2259     }
2260 }
2261
2262 void ProcessCmdLineArg( char[] pArg )
2263 {
2264     char[] lNewPath;
2265     static char[] lImportSwitch;
2266
2267     if(util.str.begins(pArg, "-version=") == True) {
2268         char[] lVersionString;
2269
2270         lVersionString=pArg [9 .. $];
2271         source.ActivateVersion(lVersionString);
2272         pArg = vVersionSwitch ~ lVersionString;
2273     }
2274     else if(util.str.begins(pArg, "-fversion=") == True) {
2275         char[] lVersionString;
2276
2277         lVersionString=pArg [10 .. $];
2278         source.ActivateVersion(lVersionString);
2279         pArg = vVersionSwitch ~ lVersionString;
2280     }
2281
2282     else if(pArg == "-fdebug") {
2283         source.ActivateDebug("1");
2284         pArg = vDebugSwitch;
2285     }
2286     else if(pArg == "-debug") {
2287         source.ActivateDebug("1");
2288         pArg = vDebugSwitch;
2289     }
2290     else if(util.str.begins(pArg, "-fdebug=") == True) {
2291         char[] lDebugString;
2292
2293         lDebugString=pArg [8 .. $];
2294         source.ActivateDebug(lDebugString);
2295         pArg = vDebugSwitch ~ "=" ~ lDebugString;
2296     }
2297     else if(util.str.begins(pArg, "-debug=") == True) {
2298         char[] lDebugString;
2299
2300         lDebugString=pArg [7 .. $];
2301         source.ActivateDebug(lDebugString);
2302         pArg = vDebugSwitch ~ "=" ~ lDebugString;
2303     }
2304
2305     switch(pArg) {
2306         case "-full":
2307             vForceCompile = True;
2308             vBuildArgs ~= pArg;
2309             // Not passed thru.
2310             break;
2311
2312         case "-link":
2313             Source.WasMainFound = true;
2314             vBuildArgs ~= pArg;
2315             // Not passed thru.
2316             break;
2317
2318         case "-nolink":
2319             vNoLink = True;
2320             vBuildArgs ~= pArg;
2321             // Not passed thru.
2322             break;
2323
2324         case "-lib":
2325             vLibraryAction = LibOpt.Build;
2326             vBuildArgs ~= pArg;
2327             // Not passed thru.
2328             break;
2329
2330         case "-nolib":
2331             vLibraryAction = LibOpt.DontBuild;
2332             vBuildArgs ~= pArg;
2333             // Not passed thru.
2334             break;
2335
2336         case "-obj":
2337             vLibraryAction = LibOpt.DontBuild;
2338             vNoLink = True;
2339             vBuildArgs ~= pArg;
2340             // Not passed thru.
2341             break;
2342
2343         case "-nounittest":
2344             // Not passed thru. Deprecated switch is now ignored.
2345             if (vSilent == False)
2346                 writefln("Note: '-nounittest' ignored. This switch is no longer used.");
2347             break;
2348
2349         case "-info":
2350             DisplayUsage(false);
2351             vBuildArgs ~= pArg;
2352             // Not passed thru.
2353             break;
2354
2355         case "-silent":
2356             vSilent = True;
2357             vBuildArgs ~= pArg;
2358             // Not passed thru.
2359             break;
2360
2361         case "-noautoimport":
2362             vAutoImports = False;
2363             vBuildArgs ~= pArg;
2364             // Not passed thru
2365             break;
2366
2367         case "-nodef":
2368             vNoDef = True;
2369             vBuildArgs ~= pArg;
2370             // Not passed thru.
2371             break;
2372
2373         case "-nomacro":
2374             vMacroInput = False;
2375             vBuildArgs ~= pArg;
2376             // Not passed thru.
2377             break;
2378
2379         case "-usage":
2380         case "-help":
2381         case "-h":
2382         case "-?":
2383             DisplayUsage();
2384             vBuildArgs ~= pArg;
2385             // Not passed thru.
2386             break;
2387
2388         case "-allobj":
2389             vAllObjects = True;
2390             vBuildArgs ~= pArg;
2391             // Not passed thru.
2392             break;
2393
2394         case "-test":
2395             vTestRun = True;
2396             vBuildArgs ~= pArg;
2397             // Not passed thru.
2398             break;
2399
2400         case "-cleanup":
2401             // drop through ...
2402         case "-clean":
2403             vCleanup = True;
2404             vBuildArgs ~= pArg;
2405             // Not passed thru.
2406             break;
2407
2408         case "-V": /* we need verbose status earlier */
2409             vBuildArgs ~= pArg;
2410             // Not passed thru.
2411             break;
2412
2413         case "-names":
2414             vBuildArgs ~= pArg;
2415             vNames = True;
2416             // Not passed thru.
2417             break;
2418
2419         default:
2420             if (pArg.length > 0)
2421             {
2422             if (pArg[0] == '-') {
2423                 if (pArg == "-g")
2424                 {
2425                     // Requires symbolic debug info.
2426                     vLinkerDefSwitches ~= vLinkerDebugSymInfoSwitch;
2427                     AddCompilerArg(pArg);
2428                     break;
2429                 }
2430
2431                 // Test for Librarian options.
2432                 if (util.str.IsLike(pArg, "-LIBOPT*"d) == True)
2433                 {
2434                     vLibrarianOpts ~= " " ~ pArg[7..$].dup;
2435                     vBuildArgs ~= pArg;
2436                     break;
2437
2438                 }
2439
2440                 if (util.str.begins(pArg, "-LIBPATH=") == True)
2441                 {
2442                     vLibPaths ~= vConfigSep ~ pArg[9..$].dup;
2443                     vBuildArgs ~= pArg;
2444                     break;
2445                 }
2446
2447                 // Test for alternate install locations.
2448                 if (util.str.IsLike(pArg, "-DCPATH?*"d) == True)
2449                 {
2450                     lNewPath = pArg[7..length].dup;
2451                     if (vCompilerPath == vConfigPath)
2452                     {
2453                         if (vVerbose == True)
2454                             writefln("CFPATH was %s now %s", vConfigPath, lNewPath);
2455                         vConfigPath = lNewPath;
2456                     }
2457                     if (vVerbose == True)
2458                         writefln("DCPATH was %s now %s", vCompilerPath, lNewPath);
2459                     vCompilerPath = lNewPath;
2460                     vBuildArgs ~= pArg;
2461                     break;
2462
2463                 }
2464
2465                 if (util.str.IsLike(pArg,  "-CFPATH?*"d) == True)
2466                 {
2467                     if (vVerbose == True)
2468                         writefln("CFPATH was %s now %s", vConfigPath, pArg[7..length]);
2469                     vConfigPath = pArg[7..length].dup;
2470                     vBuildArgs ~= pArg;
2471                     break;
2472
2473                 }
2474
2475                 if (util.str.IsLike(pArg,  "-PP?*"d) == True)
2476                 {
2477                     char[] lNewPath;
2478                     lNewPath = pArg[3..length].dup;
2479                     if (vVerbose == True)
2480                         writefln("Added %s to Source Scan List", lNewPath);
2481                     vBuildArgs ~= pArg;
2482                     if ( util.str.ends(lNewPath, std.path.sep) == False)
2483                         lNewPath ~= std.path.sep;
2484                     vSourceScanList ~= lNewPath;
2485                     break;
2486
2487                 }
2488
2489                 if (util.str.IsLike(pArg,  "-RDF?*"d) == True)
2490                 {
2491                     if (vVerbose == True)
2492                         writefln("RDF was %s now %s", vRDFName, pArg[4..$]);
2493                     vRDFName = pArg[4..$].dup;
2494                     vBuildArgs ~= pArg;
2495                     break;
2496
2497                 }
2498
2499                 if (pArg == vBuildImportPathSwitch)
2500                 {
2501                     vDelayedValue = &lImportSwitch;
2502                     vBuildArgs ~= pArg;
2503                     break;
2504                 }
2505                 else if ( util.str.begins(pArg, vBuildImportPathSwitch) == True)
2506                 {
2507                     char [] lRoot;
2508                     foreach(char[] lCmdRoot; std.string.split(pArg[vBuildImportPathSwitch.length .. $], ";"))
2509                     {
2510                         lRoot = AddRoot(lCmdRoot);
2511                         if (lRoot.length > 0){
2512                             if(vVerbose == True) {
2513                                     writefln("Added root from command line = %s",lRoot);
2514                             }
2515                         }
2516                     }
2517                     break;
2518                 }
2519
2520                 if (util.str.IsLike(pArg,  std.utf.toUTF32(vRunSwitch ~ "*")) == True)
2521                 {
2522                     vRunParms ~= pArg[vRunSwitch.length .. $] ~ " ";
2523                     vExecuteProgram = True;
2524                     vBuildArgs ~= pArg;
2525                     break;
2526                 }
2527
2528
2529                 // Special check for Object Write Path
2530                 version(DigitalMars)
2531                 {
2532                 if (util.str.IsLike(pArg,  std.utf.toUTF32(vTemporaryPathSwitch ~ "*")) == True)
2533                 {
2534                     char[] lRoot;
2535
2536                     vTemporaryPath = pArg[vTemporaryPathSwitch.length .. $];
2537                     if (vTemporaryPath.length > 0 && vTemporaryPath[$-1..$] != std.path.sep)
2538                         vTemporaryPath ~= std.path.sep;
2539
2540                     lRoot = AddRoot(vTemporaryPath);
2541                     if (lRoot.length > 0){
2542                         if(vVerbose == True) {
2543                                 writefln("Added root from Object Write Path = %s",lRoot);
2544                         }
2545                         util.pathex.MakePath(lRoot);
2546                     }
2547
2548                     vBuildArgs ~= pArg;
2549                     // This one actually *is* passed thru.
2550                 }
2551                 }
2552
2553                 if (util.str.IsLike(pArg,  "-X?*"d) == True)
2554                 {
2555                     // Modules to ignore (eg. -Xmylib)
2556                     vModulesToIgnore ~= pArg[2..$];
2557                     vBuildArgs ~= pArg;
2558                     break;
2559                 }
2560
2561                 if (util.str.IsLike(pArg,  "-M?*"d) == True)
2562                 {
2563                     // Modules to notice (eg. -Mphobos)
2564                     vModulesToNotice ~= pArg[2..$];
2565                     vBuildArgs ~= pArg;
2566                     break;
2567                 }
2568
2569                 if (util.str.IsLike(pArg,  "-T?*"d) == True)
2570                 {
2571                     // Target name (eg. -Ttestapp)
2572                     vCommandTargetName = pArg[2..$];
2573                     vBuildArgs ~= pArg;
2574                     break;
2575                 }
2576
2577                 version(DigitalMars)
2578                 {
2579                 if (util.str.IsLike(pArg,  (vOutFileSwitch ~ "*")) == True)
2580                 {
2581                     // Target name (eg. -oftestapp)
2582                     vCommandTargetName = pArg[vOutFileSwitch.length .. $];
2583                     vBuildArgs ~= pArg;
2584                     break;
2585                 }
2586                 }
2587
2588                 version(GNU)
2589                 {
2590                 if (pArg == vOutFileSwitch)
2591                 {
2592                     // Target name (eg. -o testapp)
2593                     vDelayedValue = &vCommandTargetName;
2594                     vBuildArgs ~= pArg;
2595                     break;
2596                 }
2597                 }
2598
2599                 if (util.str.IsLike(pArg,  "-R*"d) == True)
2600                 {
2601                     // Response file usage (eg. -Ry)
2602                     if (pArg.length == 2)
2603                         vUseResponseFile = ~vUseResponseFile;
2604
2605                     else if (pArg[2] == 'y')
2606                         vUseResponseFile = True;
2607
2608                     else
2609                         vUseResponseFile = False;
2610
2611                     vBuildArgs ~= pArg;
2612                     break;
2613                 }
2614
2615                 version(Windows) {
2616                     if (util.str.IsLike(pArg,  "-gui*"d) == True)
2617                     {
2618                             Source.WasMainGUI = true;
2619                             vBuildArgs ~= pArg;
2620                             if (pArg.length == 4)
2621                                 break;
2622                             if (pArg[4] == ':')
2623                                 vWinVer = pArg[5..length];
2624                             else
2625                                 vWinVer = pArg[4..length];
2626                             break;
2627                     }
2628
2629                     if (pArg == "-dll")
2630                     {
2631                             Source.WasMainDLL = true;
2632                             vBuildArgs ~= pArg;
2633                             break;
2634                     }
2635                 }
2636
2637                 AddCompilerArg( pArg );
2638
2639             } else {
2640
2641                 if (vDelayedValue != null)
2642                 {
2643                     // Used when an switch needs the subsequent arg to
2644                     // be its value.
2645                     if ( vDelayedValue == &lImportSwitch)
2646                     {
2647                         char [] lRoot;
2648                         foreach(char[] lCmdRoot; std.string.split(pArg, ";"))
2649                         {
2650                             lRoot = AddRoot(lCmdRoot);
2651                             if (lRoot.length > 0){
2652                                 if(vVerbose == True) {
2653                                         writefln("Added root from command line = %s",lRoot);
2654                                 }
2655                             }
2656                         }
2657                     }
2658                     else
2659                     {
2660                         *vDelayedValue = pArg;
2661                         vBuildArgs ~= pArg;
2662                     }
2663                     vDelayedValue = null;
2664                     break;
2665                 }
2666
2667                 version(Windows)
2668                 {
2669                     // Convert non-standard but sometimes used unix seps
2670                     // with standard Windows seps.
2671                     pArg = std.string.replace(pArg, "/", std.path.sep);
2672                 }
2673                 switch(std.path.getExt(pArg)) {
2674                    case "":
2675                        pArg ~= "." ~ kSrcExtention;
2676                        vCmdLineSourceFiles ~= pArg;
2677                        break;
2678
2679                    case kSrcExtention:
2680                    case kMacroExtention:
2681                    case kDdocExtention:
2682                        vCmdLineSourceFiles ~= pArg;
2683                        break;
2684
2685                    default:
2686                        AddLink(GetFullPathname(pArg));
2687                 }
2688                 if(vTargetName is null) {
2689                    vTargetName = pArg;
2690                 }
2691             }
2692             break;
2693         }
2694     }
2695 }
2696
2697 void GatherOneArg( char[] pArg )
2698 {
2699     static bool[char[]] lKnownArgs;
2700
2701     pArg = std.string.strip(pArg);
2702     if (pArg.length == 0)
2703         return;
2704
2705     if ((pArg == "-V") || (pArg == "-v"))
2706         vVerbose = True;
2707
2708     if ( pArg.length >= 2 && pArg[0..2] == "--")
2709     {   // Need to remove an earlier matching argument.
2710         pArg = pArg[1..$];
2711         if ((pArg == "-V") || (pArg == "-v"))
2712             vVerbose = False;
2713         for(int i = vCombinedArgs.length-1; i >= 0; i--)
2714         {
2715             if (pArg[$-1] == '*')
2716             {
2717                 if (vCombinedArgs[i].begins(pArg[0..$-1]) == True)
2718                 {
2719                     vCombinedArgs = vCombinedArgs[0..i] ~
2720                                 vCombinedArgs[i+1 .. $];
2721                     break;
2722                 }
2723             }
2724             else if (vCombinedArgs[i] == pArg)
2725             {
2726                 vCombinedArgs = vCombinedArgs[0..i] ~
2727                                 vCombinedArgs[i+1 .. $];
2728                 break;
2729             }
2730         }
2731     }
2732     else if (pArg[0] == '@')
2733         ProcessResponseFile(pArg, vVerbose);
2734     else if (pArg[0] == '+')
2735         ProcessBuildConfig(pArg, vVerbose);
2736     else
2737     {   // Only add an argument if it is not already been added.
2738         if ( !(pArg in lKnownArgs) )
2739         {
2740             vCombinedArgs ~= pArg;
2741             lKnownArgs[pArg] = true;
2742         }
2743     }
2744 }
2745
2746 void GatherArgs( char[][] pArgs )
2747 {
2748     /* This collects together all the original command line arguments,
2749        any command line arguments in any configuration files, and
2750        the contents of any response files.
2751     */
2752
2753     // Collect from configuration file(s).
2754     ProcessBuildConfig("+", vVerbose);
2755
2756     // Collect from original command line.
2757     foreach( char[] lArg; pArgs)
2758     {
2759         GatherOneArg( lArg );
2760     }
2761
2762
2763 }
2764
2765 void ProcessMacroDefs(Bool pVerbose)
2766 {
2767     // From build.exe location
2768     ProcessOneMacroDef(pVerbose, std.path.getDirName(vAppPath));
2769
2770     // From compiler location
2771     ProcessOneMacroDef(pVerbose, vCompilerPath);
2772
2773 }
2774
2775 void ProcessOneMacroDef(Bool pVerbose, char[] pPath)
2776 {
2777     char[] lMacroDefFileName;
2778     char[][] lMacroDefLines;
2779     char[] lOpen;
2780     char[] lClose;
2781     char[] lEscapeOpen;
2782     char[] lEscapeClose;
2783     char[] lCommentLead;
2784     static char[] lDefOpen = "\"";
2785     static char[] lDefClose = "\"";
2786     static char[] lDefEscapeOpen = "\\";
2787     static char[] lDefEscapeClose = "";
2788     static char[] lDefCommentLead = "#";
2789
2790     lOpen = lDefOpen;
2791     lClose = lDefClose;
2792     lEscapeOpen = lDefEscapeOpen;
2793     lEscapeClose = lDefEscapeClose;
2794     lCommentLead = lDefCommentLead;
2795
2796     lMacroDefFileName = pPath.dup;
2797
2798     if ((lMacroDefFileName.length > 0) &&
2799         (lMacroDefFileName[$-std.path.sep.length..$] != std.path.sep) )
2800             lMacroDefFileName ~= std.path.sep;
2801
2802     lMacroDefFileName ~= "build.mdf";
2803
2804
2805     if ((pVerbose == True) && util.fileex.FileExists(lMacroDefFileName) )
2806         writefln("Build Macro Definition file %s", lMacroDefFileName);
2807     lMacroDefLines = util.fileex.GetTextLines(lMacroDefFileName, util.fileex.GetOpt.Always);
2808
2809     foreach(char[] lArg; lMacroDefLines)
2810     {
2811         char[][] lOptions;
2812
2813         lArg = ExpandEnvVar(lArg);
2814         // Locate any comment text in the line.
2815         int lPos = std.string.find(lArg, lCommentLead);
2816         if (lPos != -1)
2817         {
2818             // Truncate the line at the comment lead-in character.
2819             lArg.length = lPos;
2820         }
2821
2822         lArg = std.string.strip(lArg);
2823         if (lArg.length > 1)
2824         {
2825
2826             if (lArg.begins("replace ") == True)
2827             {
2828                 int lStartPos;
2829                 int lEndPos;
2830                 bool lEndFound;
2831                 int lDepthInc;
2832                 int lDepth;
2833                 int lDataPos;
2834                 char[] lPattern;
2835                 char[] lResolution;
2836
2837                 lArg = std.string.strip(lArg[8..$]);
2838                 if (lOpen in vNestedDelim)
2839                     lDepthInc = 1;
2840                 else
2841                     lDepthInc = 0;
2842
2843                 // Format should be <open> <pattern> <close> <open> <resolution> <close>
2844                 lStartPos = 0;
2845                 lEndPos = lStartPos + lOpen.length;
2846                 while( (lEndPos < lArg.length) &&
2847                        (lArg[lStartPos .. lEndPos] != lOpen)
2848                       )
2849                 {
2850                     lStartPos ++;
2851                     lEndPos ++;
2852                 }
2853                 if (lEndPos >= lArg.length)
2854                     continue;   // No open delim found.
2855                 lDepth += lDepthInc;
2856                 lDataPos = lEndPos;
2857                 lStartPos = lDataPos;
2858                 lEndPos = lStartPos + lClose.length;
2859                 while( (lEndPos < lArg.length) &&
2860                        (lArg[lStartPos .. lEndPos] != lClose)
2861                       )
2862                 {
2863                     lStartPos ++;
2864                     lEndPos ++;
2865                 }
2866                 if (lEndPos >= lArg.length)
2867                     continue;   // No close delim found.
2868                 lPattern = std.string.strip(lArg[lDataPos .. lStartPos]);
2869
2870                 lStartPos = lEndPos;
2871                 lEndPos = lStartPos + lOpen.length;
2872                 while( (lEndPos < lArg.length) &&
2873                        (lArg[lStartPos .. lEndPos] != lOpen)
2874                       )
2875                 {
2876                     lStartPos ++;
2877                     lEndPos ++;
2878                 }
2879                 if (lEndPos >= lArg.length)
2880                     continue;   // No open delim found.
2881                 lDepth += lDepthInc;
2882                 lDataPos = lEndPos;
2883                 lStartPos = lDataPos;
2884                 lEndPos = lStartPos + lClose.length;
2885                 while( (lEndPos < lArg.length) &&
2886                        (lArg[lStartPos .. lEndPos] != lClose)
2887                       )
2888                 {
2889                     lStartPos ++;
2890                     lEndPos ++;
2891                 }
2892                 if (lEndPos > lArg.length)
2893                     continue;   // No close delim found.
2894                 lResolution = std.string.strip(lArg[lDataPos .. lStartPos]);
2895
2896                 vSubstitutions.length = vSubstitutions.length + 1;
2897                 vSubstitutions[$-1].Pattern = lPattern;
2898                 vSubstitutions[$-1].Resolution = lResolution;
2899                 vSubstitutions[$-1].EscapeOpen = lEscapeOpen;
2900                 vSubstitutions[$-1].EscapeClose = lEscapeClose;
2901
2902             }
2903             else if (lArg.begins("delim ") == True )
2904             {
2905
2906                 lArg = std.string.strip(lArg[6..$]);
2907
2908                 lOptions = std.string.split(lArg);
2909                 foreach(char[] lOption; lOptions)
2910                 {
2911                     if (lOption == "std")
2912                     {
2913                         lOpen = lDefOpen;
2914                         lClose = lDefClose;
2915                         lEscapeOpen = lDefEscapeOpen;
2916                         lEscapeClose = lDefEscapeClose;
2917                         lCommentLead = lDefCommentLead;
2918                     }
2919                     else
2920                     {
2921                         char[][] lKeyValue;
2922                         lKeyValue = std.string.split(lOption, "=");
2923                         switch (lKeyValue[0])
2924                         {
2925                             case "open":
2926                                 lOpen = lKeyValue[1];
2927                                 break;
2928                             case "close":
2929                                 lClose = lKeyValue[1];
2930                                 break;
2931                             case "escapeopen":
2932                                 lEscapeOpen = lKeyValue[1];
2933                                 break;
2934                             case "escapeclose":
2935                                 lEscapeClose = lKeyValue[1];
2936                                 break;
2937                             case "comment":
2938                                 lCommentLead = lKeyValue[1];
2939                                 break;
2940                             default:
2941                                 if (pVerbose == True)
2942                                     writefln("Bad Macro 'delim' option '%s'",
2943                                             lOption);
2944                                 // Ignore bad options.
2945                                 break;
2946                         }
2947                     }
2948                 }
2949             }
2950             else if (vVerbose == True)
2951             {
2952                 writefln("Bad configuration command '%s'", lArg);
2953             }
2954         }
2955     }
2956 }
2957
2958 void ProcessBuildConfig(char[] pArg, Bool pVerbose)
2959 {
2960     // From build.exe location
2961     ProcessOneBuildConfig(pArg, pVerbose, std.path.getDirName(vAppPath));
2962
2963     // From compiler location
2964     ProcessOneBuildConfig(pArg, pVerbose, vCompilerPath);
2965
2966     // From current location
2967     ProcessOneBuildConfig(pArg, pVerbose, util.pathex.GetInitCurDir() );
2968
2969 }
2970
2971 void ProcessOneBuildConfig(char[] pArg, Bool pVerbose, char[] pPath)
2972 {
2973     char[] lConfigFileName;
2974     char[][] lConfigLines;
2975     bool lFoundGroup;
2976
2977
2978     lConfigFileName = pPath.dup;
2979
2980     if ((lConfigFileName.length > 0) &&
2981         (lConfigFileName[$-std.path.sep.length..$] != std.path.sep) )
2982             lConfigFileName ~= std.path.sep;
2983
2984     lConfigFileName ~= "build.cfg";
2985
2986     if ((pVerbose == True)  && util.fileex.FileExists(lConfigFileName))
2987         writefln("Build Configuration file %s [%s]", lConfigFileName, pArg);
2988
2989     lConfigLines = util.fileex.GetTextLines(lConfigFileName, util.fileex.GetOpt.Always);
2990     if (pArg.length == 1)
2991         lFoundGroup = true;
2992     else
2993     {
2994         lFoundGroup = false;
2995         pArg = pArg[1..$];
2996     }
2997
2998     foreach(char[] lArg; lConfigLines)
2999     {
3000         lArg = ExpandEnvVar(lArg);
3001         if (!lFoundGroup)
3002         {
3003             if (lArg == "[" ~ pArg ~ "]")
3004             {
3005                 lFoundGroup = true;
3006                 continue;
3007             }
3008         }
3009
3010         if (lFoundGroup)
3011         {
3012             // Locate any comment text in the line.
3013             int lPos = std.string.find(lArg, "#");
3014             if (lPos != -1)
3015             {
3016                 // Truncate the line at the '#' character.
3017                 lArg.length = lPos;
3018             }
3019
3020             lArg = std.string.strip(lArg);
3021             if (lArg.length > 1)
3022             {
3023                 if ((lArg[0] == '[') && (lArg[$-1] == ']'))
3024                     break; // Don't process any more lines.
3025
3026                 if (pVerbose == True)
3027                     writefln("Build Configuration file arg: %s", lArg);
3028
3029
3030                 while ((lPos = find(lArg, "{Group}")) != -1)
3031                 {
3032                     lArg = lArg[0..lPos] ~ pArg ~ lArg[lPos + 7 .. $];
3033                 }
3034
3035                 if (lArg.begins("CMDLINE=") == True)
3036                 {
3037                     int lStartPos;
3038                     int lEndPos;
3039                     bool lEndFound;
3040                     lArg = std.string.strip(lArg[8..$]);
3041                     lStartPos = 0;
3042                     lEndPos = 0;
3043                     lEndFound = false;
3044                     while(lEndPos < lArg.length)
3045                     {
3046                         if (!lEndFound)
3047                         {
3048                             if (lArg[lEndPos] == ' ')
3049                             {
3050                                 lEndFound = true;
3051                             }
3052                         }
3053                         else
3054                         {
3055                             if (find("-+@", lArg[lEndPos..lEndPos+1]) != -1)
3056                             {
3057                                 GatherOneArg( lArg[lStartPos..lEndPos] );
3058                                 lStartPos = lEndPos;
3059                                 lEndFound = false;
3060                             }
3061                         }
3062                         lEndPos++;
3063                     }
3064                     if (lStartPos != lEndPos)
3065                         GatherOneArg( lArg[lStartPos..lEndPos] );
3066
3067                 }
3068                 else if (lArg.begins("LIBCMD=") == True)
3069                 {
3070                     int lPos;
3071
3072                     while( (lPos = find(lArg, "\"")) != -1)
3073                     {
3074                         lArg = lArg[0..lPos] ~ lArg[lPos+1 .. $];
3075                     }
3076                     vLibrarianPath = std.path.getDirName(lArg[7..$]) ~ std.path.sep;
3077                     vLibrarianPath = util.pathex.CanonicalPath(vLibrarianPath);
3078
3079                     vLibrarian = std.path.getBaseName(lArg[7..$]);
3080                     if(vVerbose == True) {
3081                         writefln(" librarian path %s",vLibrarianPath);
3082                         writefln(" librarian is %s",vLibrarian);
3083                     }
3084                 }
3085                 else if (lArg.begins("LINKSWITCH=") == True)
3086                 {
3087                     vLinkerDefSwitches = lArg[11..$].dup;
3088                 }
3089                 else if (vVerbose == True)
3090                 {
3091                     writefln("Bad configuration command '%s'", lArg);
3092                 }
3093             }
3094         }
3095     }
3096 }