root/trunk/Source/source.d

Revision 53, 71.6 kB (checked in by Derek Parnell, 10 months ago)

Pre release #2 of version 3.5

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