root/branches/bud/dsss_build/source.d

Revision 167, 70.2 kB (checked in by Gregor, 2 years ago)

dsss_build/build.d, dsss_build/source.d: Better library dependency support.

Line 
1 /**************************************************************************
2
3         @file source.d
4
5         Copyright (c) 2005 Derek Parnell
6
7         This software is provided 'as-is', without any express or implied
8         warranty. In no event will the authors be held liable for damages
9         of any kind arising from the use of this software.
10
11         Permission is hereby granted to anyone to use this software for any
12         purpose, including commercial applications, and to alter it and/or
13         redistribute it freely, subject to the following restrictions:
14
15         1. The origin of this software must not be misrepresented; you must
16            not claim that you wrote the original software. If you use this
17            software in a product, an acknowledgment within documentation of
18            said product would be appreciated but is not required.
19
20         2. Altered source versions must be plainly marked as such, and must
21            not be misrepresented as being the original software.
22
23         3. This notice may not be removed or altered from any distribution
24            of the source.
25
26         4. Derivative works are permitted, but they must carry this notice
27            in full and credit the original source.
28
29
30                         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
31
32
33         @version        Initial version, January 2005
34         @author         Derek Parnell
35
36
37 **************************************************************************/
38
39 module source;
40 private import source_bn;    // Build number for this module
41 //version(build) pragma(include, "build");
42 version(unix)  version = Unix;
43 version(Unix)  version = Posix;
44 version(linux)  version = Posix;
45 version(darwin) version = Posix;
46
47 private
48 {
49     alias char[] string;
50
51     static import util.str;
52     static import util.fdt;
53     static import util.pathex;
54     static import util.fileex;
55     static import util.macro;
56     static import util.booltype;   // definition of True and False
57     alias util.booltype.True True;
58     alias util.booltype.False False;
59     alias util.booltype.Bool Bool;
60
61     static import std.stdio;
62     static import std.path;
63     static import std.ctype;
64     static import std.file;
65     static import std.string;
66 }
67
68 public
69 {
70     enum eRuleUsage
71     {
72         Ignore,
73         Compile,
74         Link
75     }
76     string function(string pPath) AddRoot;
77     void function(string pArg) AddCompilerArg;
78     string[] function () GetImportRoots;
79     Bool function() AutoImports;
80     void function(string pPath) AddTarget;
81     void function(string pPath) AddLink;
82     void function(string pText, bool pReplace=false) AddBuildDef;
83     string[] function( ) ModulesToIgnore;
84     string function(string pFile, string[] pScanList = null) GetFullPathname;
85     string function(string pFile, int pScanList) GetFullPathnameScan;
86     string function() GetAppPath;
87
88     version(BuildVerbose) Bool vVerboseMode;
89     Bool    mMacroInput;
90     Bool    mCollectUses;
91     Bool    vForceCompile;
92     Bool    vExplicit;
93     string  ObjWritePath;
94     string  vRDFName = "default.rdf";
95     string  vPathId;
96
97 }
98
99 private
100 {
101     import util.str;
102     import util.fdt;
103     import util.pathex;
104     import util.fileex;
105     import util.macro;
106     import util.booltype;
107
108     import std.stdio;
109     import std.path;
110     import std.ctype;
111     import std.file;
112     import std.string;
113
114     const {
115         version(Windows) {
116             string ExeExt=`exe`;
117             string LibExt=`lib`;
118             string ObjExt=`obj`;
119         }
120
121         version(Posix) {
122             string ExeExt=``;
123             string LibExt=`a`;
124             string ObjExt=`o`;
125         }
126
127         // Using these as the literals confuse my text editor's
128         // 'bracket matching' algorithm.
129         static string kOpenBrace = "\x7B";
130         static string kCloseBrace = "\x7D";
131         static string kOpenParen = "\x28";
132         static string kCloseParen = "\x29";
133
134     }
135
136     bool [string] AttributeBlocks;
137     bool mainFound;
138     bool mainGUI;
139     bool mainDLL;
140     bool[string] vActiveVersions;
141     long         vVersionLevel = 0;
142     bool[string] vActiveDebugs;
143     long         vDebugLevel = 0;
144     string[eRuleUsage] vUseNames;
145
146
147     class SourceException : Error
148     {
149         this(string pMsg)
150         {
151             super (Source.classinfo.name ~ ":" ~ pMsg);
152         }
153     }
154
155 }
156
157     debug(1)
158     {
159         private import std.stdio;
160     }
161
162 // Module Constructor -----------------
163 static this()
164 {
165     AttributeBlocks["private"] = true;
166     AttributeBlocks["package"] = true;
167     AttributeBlocks["protected"] = true;
168     AttributeBlocks["public"] = true;
169     AttributeBlocks["export"] = true;
170     version(BuildVerbose) vVerboseMode = False;
171     mMacroInput = False;
172     vForceCompile = False;
173     vUseNames[eRuleUsage.Ignore] = "ignore";
174     vUseNames[eRuleUsage.Compile] = "compile";
175     vUseNames[eRuleUsage.Link] = "link";
176     debug(1)
177     {
178     writefln(__FILE__ ~ " build #%d", source_bn.auto_build_number);
179     }
180 }
181
182 // Module Destructor -----------------
183 static ~this()
184 {
185     // Clean up any resources held by the source files.
186     Source.Finalize();
187 }
188
189 // Class -----------------
190 public class Source {
191     static {
192         private
193         {
194             Source[string] smSourceIndex;
195             string[] smScanOrder;
196             bool smUseModBaseName;
197             bool[string] smUses;
198             bool[string] smUsedBy;
199         }
200         public
201         {
202             enum EMode
203             {
204                 New,            // Must be a new source file
205                 Update          // Must update an existing record
206             }
207             int FileCount() { return smSourceIndex.length; }
208
209             Source opIndex(string pFileName)
210             {
211                 Source* lObject;
212                 if ((lObject = (pFileName in smSourceIndex)) !is null)
213                     return *lObject;
214                 if ((lObject = (util.pathex.AbbreviateFileName(pFileName) in smSourceIndex)) !is null)
215                     return *lObject;
216                 return null;
217             }
218
219             int AllFiles(int delegate(inout int lCnt, inout Source) dg)
220             {
221                 int result = 0;
222                 int lCnt = 0;
223
224                 foreach (string lFileName; smScanOrder)
225                 {
226                     result = dg(lCnt, smSourceIndex[lFileName]);
227                     if (result != 0)
228                         break;
229                     lCnt++;
230                 }
231                 return result;
232             }
233
234             /* int AllFiles(int delegate(inout Source) dg)
235             {
236                 int result = 0;
237
238                 foreach (string lFileName; smScanOrder)
239                 {
240                     result = dg(smSourceIndex[lFileName]);
241                     if (result != 0)
242                         break;
243                 }
244                 return result;
245             }
246 */
247             string[] AllFiles()
248             {
249                 return smScanOrder;
250             }
251
252             bool UseModBaseName() { return smUseModBaseName; }
253             void UseModBaseName(bool pNewValue) { smUseModBaseName = pNewValue; }
254             string[] Uses() { return smUses.keys; }
255             string[] UsedBy() { return smUsedBy.keys; }
256
257         }
258     }
259
260     private {
261     string mFileName;
262     string mModuleName;
263     string mObjectName;
264     FileDateTime mObjectTime;     /* time of object file, must be newest, otherwise compile */
265     FileDateTime mDependantsTime;   /* time of newest of all dependencies */
266     FileDateTime mFileTime;
267     bool mHasBeenSearched;
268     bool mNoLink;
269     bool mNoCompile;
270     bool mIgnore;
271     int mBuildNumber;
272     string[] mReferencedImports;
273     string[] mLinkedLibs;
274     bool[string] mActiveVersions;
275     long         mVersionLevel = 0;
276     bool[string] mActiveDebugs;
277     long         mDebugLevel = 0;
278     string  mLocalArgs;
279     }
280
281     public {
282     // FileName (read only)
283     string FileName () { return mFileName.dup; }
284
285     // DependantsTime (read only)
286     FileDateTime DependantsTime() { return mDependantsTime; }
287
288     // ObjectsTime (read only)
289     FileDateTime ObjectsTime() { return mObjectTime; }
290
291     // FilesTime (read only)
292     FileDateTime FilesTime() { return mFileTime; }
293
294     // ModuleName (read only)
295     string ModuleName() { return mModuleName.dup; }
296
297     // ObjectName (read only)
298     string ObjectName() { return mObjectName.dup; }
299
300     // NoLink (read only)
301     bool NoLink() { return mNoLink; }
302
303     // NoCompile (read only)
304     bool NoCompile() { return mNoCompile; }
305
306     // Ignore
307     bool Ignore() { return mIgnore; }
308     bool Ignore(bool pNew) { mIgnore = pNew; return pNew;}
309
310     // BuildNumber (read only)
311     int BuildNumber() { return mBuildNumber; }
312
313     // LocalArgs (read only)
314     string LocalArgs () { return mLocalArgs.dup; }
315
316     // HasBeenScanned (read only)
317     bool HasBeenScanned () { return mHasBeenSearched; }
318
319 // --------------------------------------------------------------------
320     static void Finalize()
321 // --------------------------------------------------------------------
322     {
323         for(int i = smScanOrder.length-1; i >= 0; i--)
324         {
325             Source x;
326             x = smSourceIndex[smScanOrder[i]];
327             delete x;
328         }
329     }
330 // --------------------------------------------------------------------
331     this(string pFileName, EMode pMode = Source.EMode.New)
332 // --------------------------------------------------------------------
333     {
334         if (pFileName in Source.smSourceIndex)
335         {
336             if (pMode == Source.EMode.Update)
337             {
338                 Source.smSourceIndex[pFileName].create("");
339             }
340             else
341             {
342                 mIgnore = true;
343                 mHasBeenSearched = true;
344                 mNoLink = true;
345             }
346
347         }
348         else
349         {
350             create(pFileName);
351         }
352     }
353
354 // --------------------------------------------------------------------
355     void create(string pFileName)
356 // --------------------------------------------------------------------
357     {
358         string lObjectName;
359
360         mReferencedImports.length = 0;
361         mHasBeenSearched = false;
362         mIgnore = false;
363         if (pFileName.length > 0)
364         {
365             mFileName = pFileName;
366             // Store this instantiation in the list of known files.
367             smSourceIndex[pFileName] = this;
368         }
369         else
370         {
371             pFileName = mFileName;
372         }
373
374         if (std.path.getExt(pFileName) != "ddoc")
375         {
376             string lAltName;
377             mNoLink = (std.path.getExt(pFileName) == "di");
378             mModuleName = FileToModulename(pFileName, lAltName);
379             lObjectName = addExt(pFileName,ObjExt);
380             mObjectName = util.pathex.AbbreviateFileName(util.pathex.CanonicalPath(lObjectName, false));
381         }
382         else
383         {
384             mNoLink = true;
385             mModuleName = "";
386             lObjectName = "";
387             mObjectName = "";
388         }
389
390         /* If a specific path for object files has been supplied
391            on the command line, and the current source file is
392            relative to the current directory, then we need to
393            prepend the specified object location path.
394         */
395         if (ObjWritePath.length != 0 && mObjectName.length != 0)
396         {
397             if (mObjectName == lObjectName)
398             {
399                 mObjectName = ObjWritePath ~ std.path.getBaseName(mObjectName);
400                 util.pathex.MakePath(mObjectName);
401             }
402         }
403
404         if (smUseModBaseName)
405         {
406             mObjectName = getBaseName(mObjectName);
407         }
408
409         mBuildNumber = -1;
410
411         mFileTime = new util.fdt.FileDateTime(pFileName);
412         mDependantsTime  = mFileTime;
413
414         mObjectTime   = new util.fdt.FileDateTime(mObjectName);
415         UpdateDependantTime(mObjectTime);
416         version(BuildVerbose)
417         {
418             if(vVerboseMode == True)
419             {
420                 if (mFileTime !is null)
421                     writefln("Time %s for source %s", mFileTime.toString(), mFileName);
422
423                 if (mObjectTime !is null)
424                     writefln("Time %s for object %s", mObjectTime.toString(), mObjectName);
425             }
426         }
427
428         search();
429     }
430
431 debug(dtor)
432     {
433 // --------------------------------------------------------------------
434     ~this()
435 // --------------------------------------------------------------------
436     {
437         writefln("Finis: %s", mFileName);
438     }
439 }
440
441
442 // --------------------------------------------------------------------
443     void search()
444 // --------------------------------------------------------------------
445     {
446         FileDateTime lCurFileTime;
447         bool lCanUse;
448         int lTextPos = 0;
449         string lFileText;
450
451
452         if(mHasBeenSearched)
453         {
454             return;
455         }
456
457         if (util.file2.FileExists(mFileName) == false)
458         {
459             return;
460         }
461
462         version(BuildVerbose)
463         {
464             if(vVerboseMode == True)
465                 writefln("Scanning %s", util.pathex.AbbreviateFileName(mFileName));
466         }
467
468         if (mHasBeenSearched == false)
469         {
470             smScanOrder.length = smScanOrder.length + 1;
471             smScanOrder[$-1] = mFileName;
472         }
473
474         if (std.path.getExt(mFileName) != "ddoc")
475         {
476             // Grab the original text, which must exist.
477             lFileText = GetText(mFileName, GetOpt.Exists);
478
479             // Check if any macro processing is required.
480             if ((mMacroInput == True) && (std.path.getExt(mFileName) != "d"))
481             {
482                 string lNewFile;
483                 lNewFile = std.path.addExt(mFileName, "d");
484                 if (util.macro.RunMacro(lFileText, "build", lNewFile))
485                 {
486                     std.file.write(lNewFile, lFileText);
487                     mHasBeenSearched = true;
488                     mIgnore = true;
489                     version(BuildVerbose)
490                     {
491                         if(vVerboseMode == True)
492                             writefln("Macro output %s", lNewFile);
493                     }
494                     new Source(lNewFile);
495                     delete lFileText;
496                     return;
497                 }
498             }
499
500             // Extract all the module references.
501             ProcessTokens(lFileText, lTextPos);
502             delete lFileText; // Remove it from GC control.
503
504             lCurFileTime = new FileDateTime();
505             // Examine each extracted module file.
506             if ( (mReferencedImports.length > 0) && (AutoImports() == True))
507             {
508
509                 AddRoot( std.path.getDirName(util.pathex.CanonicalPath(mFileName, false)) );
510             }
511
512             foreach(string lNextFile; mReferencedImports)
513             {
514                 if (mCollectUses)
515                 {
516                     smUses[mFileName ~ " <> " ~ lNextFile] = true;
517                     smUsedBy[lNextFile ~ " <> " ~ mFileName] = true;
518                 }
519                 if(lNextFile in smSourceIndex)
520                 {
521                     // Known file so just grab its mod time
522                     lCurFileTime = smSourceIndex[lNextFile].DependantsTime;
523                 }
524                 else
525                 {
526                     // Ignore specified modules as we assume they are in the lib.
527                     lCanUse = true;
528                     if (!(ModulesToIgnore is null))
529                     {
530                         foreach(string lNextModule; ModulesToIgnore() ){
531                             int lFindPos;
532                             string lType = "package";
533                             string lFullFileName;
534                             version(Windows)
535                             {
536                                 string lLowerMod;
537                                 lFullFileName = std.string.tolower(util.pathex.CanonicalPath(lNextFile));
538                                 lLowerMod = std.string.tolower(lNextModule);
539                                 version(Windows)
540                                 {
541                                     // Just in case a Windows user used unix style separators.
542                                     lLowerMod = std.string.replace(lLowerMod, `/`, `\`);
543                                 }
544
545                                 // Check for package name
546                                 lFindPos = std.string.find(lFullFileName,
547                                                         std.path.sep ~ lLowerMod ~ std.path.sep);
548                                 if (lFindPos == -1) {
549                                     // It might be a package at the current module level.
550                                     lFindPos = std.string.find(lFullFileName,
551                                                         lLowerMod ~ std.path.sep);
552                                     if (lFindPos != 0)
553                                         lFindPos = -1;
554                                 }
555                                 // Check for module in subdirectory
556                                 if (lFindPos == -1) {
557                                     lType = "module";
558                                     lFindPos = std.string.find(lFullFileName,
559                                                         std.path.sep ~ lLowerMod ~ ".d");
560                                 }
561                                 // Check for module in current directory
562                                 if (lFindPos == -1)
563                                 {
564                                     lType = "module";
565                                     if (lFullFileName == util.pathex.CanonicalPath(lLowerMod ~ ".d"))
566                                         lFindPos = 0;
567                                 }
568                             }
569                             version(Posix)
570                             {
571                                 // Check for package name
572                                 lFullFileName = util.pathex.CanonicalPath(lNextFile);
573                                 lFindPos = std.string.find(lFullFileName,
574                                                         std.path.sep ~ lNextModule ~ std.path.sep);
575                                 if (lFindPos == -1) {
576                                     // It might be a package at the current module level.
577                                     lFindPos = std.string.find(lFullFileName,
578                                                         lNextModule ~ std.path.sep);
579                                     if (lFindPos != 0)
580                                         lFindPos = -1;
581                                 }
582                                 // Check for module in subdirectory
583                                 if (lFindPos == -1) {
584                                     lType = "module";
585                                     lFindPos = std.string.find(lFullFileName,
586                                                         std.path.sep ~ lNextModule ~ ".d");
587                                 }
588                                 // Check for module in current directory
589                                 if (lFindPos == -1) {
590                                     lType = "module";
591                                     if (lFullFileName == util.pathex.CanonicalPath(lNextModule ~ ".d"))
592                                         lFindPos = 0;
593                                 }
594                             }
595
596                             if( lFindPos >= 0) {
597                                 version(BuildVerbose)
598                                 {
599                                     if (vVerboseMode == True)
600                                         writefln("Ignoring %s (%s: %s)", lNextFile, lType, lNextModule);
601                                 }
602
603                                 lCanUse = false;
604                                 break;
605                             }
606                         }
607                     }
608                     if (lCanUse && (vExplicit == False))
609                     {
610                         // Not known yet, so add it and grab its mod time