root/trunk/sss/build.d

Revision 925, 16.5 kB (checked in by Gregor, 3 months ago)

sss/*: Much less verbose by default.

Line 
1 /**
2  * DSSS command "build"
3  *
4  * Authors:
5  *  Gregor Richards
6  *
7  * License:
8  *  Copyright (c) 2006, 2007  Gregor Richards
9  * 
10  *  Permission is hereby granted, free of charge, to any person obtaining a
11  *  copy of this software and associated documentation files (the "Software"),
12  *  to deal in the Software without restriction, including without limitation
13  *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  *  and/or sell copies of the Software, and to permit persons to whom the
15  *  Software is furnished to do so, subject to the following conditions:
16  * 
17  *  The above copyright notice and this permission notice shall be included in
18  *  all copies or substantial portions of the Software.
19  * 
20  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21  *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23  *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  *  DEALINGS IN THE SOFTWARE.
27  */
28
29 module sss.build;
30
31 import std.file;
32 import std.process;
33 import std.stdio;
34 import std.string;
35
36 import std.c.stdlib;
37
38 import hcf.path;
39 import hcf.process;
40
41 import sss.conf;
42 import sss.system;
43
44 /** The entry function to the DSSS "build" command */
45 int build(char[][] buildElems, DSSSConf conf = null, char[] forceFlags = "") {
46     // get the configuration
47     if (conf is null)
48         conf = readConfig(buildElems);
49    
50     // buildElems are by either soure or target, so we need one by source only
51     char[][] buildSources;
52    
53     // get the sources
54     buildSources = sourcesByElems(buildElems, conf);
55    
56     // also get a complete list, since some steps need it
57     char[][] allSources = sourcesByElems(null, conf);
58    
59     /* building is fairly complicated, involves these steps:
60      * 1) Make .di files
61      *    (so that you link against your own libraries)
62      * 2) Make fake shared libraries
63      *    (they need to exist so that other libraries can link against them)
64      * 3) Make real shared libraries
65      * 4) Make binaries
66      */
67    
68     // make the basic build line
69     char[] bl = dsss_build ~ forceFlags ~ " ";
70    
71     // add -oq if we don't have such a setting
72     if (find(forceFlags, "-o") == -1) {
73         mkdirP("dsss_objs" ~ std.path.sep ~ compilerShort());
74         bl ~= "-oqdsss_objs" ~ std.path.sep ~ compilerShort() ~ " ";
75     }
76    
77     // 1) Make .di files for everything
78     foreach (build; allSources) {
79         char[][char[]] settings = conf.settings[build];
80        
81         // basic info
82         char[] type = settings["type"];
83         char[] target = settings["target"];
84        
85         if (type == "library" && libsSafe()) {
86             writefln("Creating imports for %s", target);
87            
88             // do the predigen
89             if ("predigen" in settings) {
90                 dsssScriptedStep(conf, settings["predigen"]);
91             }
92            
93             // this is a library, so make .di files
94             char[][] srcFiles = targetToFiles(build, conf);
95            
96             // generate .di files
97             foreach (file; srcFiles) {
98                 char[] ifile = "dsss_imports" ~ std.path.sep ~ file ~ "i";
99                 if (!exists(ifile) ||
100                     fileNewer(file, ifile)) {
101                     /* BIG FAT NOTE slash FIXME:
102                      * .di files do NOT include interfaces! So, we need to just
103                      * cast .d files as .di until that's fixed */
104                     mkdirP(getDirName(ifile));
105                    
106                     // now edit the .di file to reference the appropriate library
107                    
108                     // usname = name_with_underscores
109                     char[] usname = replace(build, std.path.sep, "_");
110
111                     // if we aren't building a debug library, the debug conditional will fall through
112                     char[] debugPrefix = null;
113                     if (buildDebug)
114                         debugPrefix = "debug-";
115                    
116                     /* generate the pragmas (FIXME: this should be done in a
117                      * nicer way) */
118                     char[] defaultLibName = libraryName(build);
119                     if (defaultLibName == target) {
120                         std.file.write(ifile, std.file.read(file) ~ `
121 version (build) {
122     debug {
123         version (GNU) {
124             pragma(link, "` ~ debugPrefix ~ `DG` ~ target[2..$] ~ `");
125         } else version (DigitalMars) {
126             pragma(link, "` ~ debugPrefix ~ `DD` ~ target[2..$] ~ `");
127         } else {
128             pragma(link, "` ~ debugPrefix ~ `DO` ~ target[2..$] ~ `");
129         }
130     } else {
131         version (GNU) {
132             pragma(link, "DG` ~ target[2..$] ~ `");
133         } else version (DigitalMars) {
134             pragma(link, "DD` ~ target[2..$] ~ `");
135         } else {
136             pragma(link, "DO` ~ target[2..$] ~ `");
137         }
138     }
139 }
140 `);
141                     } else {
142                         std.file.write(ifile, std.file.read(file) ~ `
143 version (build) {
144     debug {
145         pragma(link, "` ~ debugPrefix ~ target ~ `");
146     } else {
147         pragma(link, "` ~ target ~ `");
148     }
149 }
150 `);
151                     }
152                 }
153             }
154            
155             // do the postdigen
156             if ("postdigen" in settings) {
157                 dsssScriptedStep(conf, settings["postdigen"]);
158             }
159            
160             writefln("");
161            
162         }
163     }
164    
165     // 2) Make fake shared libraries
166     if (shLibSupport()) {
167         foreach (build; allSources) {
168             char[][char[]] settings = conf.settings[build];
169            
170             // ignore this if we're not building a shared library
171             if (!("shared" in settings)) continue;
172        
173             // basic info
174             char[] type = settings["type"];
175             char[] target = settings["target"];
176        
177             if (type == "library" && libsSafe()) {
178                 char[] shlibname = getShLibName(settings);
179                 char[][] shortshlibnames = getShortShLibNames(settings);
180                 char[] shlibflag = getShLibFlag(settings);
181
182                 if (exists(shlibname)) continue;
183                
184                 writefln("Building stub shared library for %s", target);
185                
186                 // make the stub
187                 if (targetGNUOrPosix()) {
188                     char[] stubbl = bl ~ "-fPIC -shlib " ~ stubDLoc ~ " -of" ~ shlibname ~
189                         " " ~ shlibflag;
190                     vSaySystemRDie(stubbl, "-rf", shlibname ~ "_stub.rf", deleteRFiles);
191                     if (targetVersion("Posix")) {
192                         foreach (ssln; shortshlibnames) {
193                             vSaySystemDie("ln -sf " ~ shlibname ~ " " ~ ssln);
194                         }
195                     }
196                 } else {
197                     assert(0);
198                 }
199                
200                 writefln("");
201             }
202         }
203     }
204
205     char[] docbl = "";
206     /// A function to prepare for creating documentation for this build
207     void prepareDocs(char[] build, bool doc) {
208         // prepare for documentation
209         docbl = "";
210         if (doc) {
211             char[] docdir = "dsss_docs" ~ std.path.sep ~ build;
212             mkdirP(docdir);
213             docbl ~= "-full -Dq" ~ docdir ~ " -candydoc ";
214        
215             // now extract candydoc there
216             char[] origcwd = getcwd();
217             chdir(docdir);
218        
219             version (Windows) {
220                 vSayAndSystem("bsdtar -xf " ~ candyDocPrefix);
221             } else {
222                 vSayAndSystem("gunzip -c " ~ candyDocPrefix ~ " | tar -xf -");
223             }
224        
225             chdir(origcwd);
226         }
227     }
228
229     // 3) Make real libraries and do special steps and subdirs
230     foreach (build; buildSources) {
231         char[][char[]] settings = conf.settings[build];
232        
233         // basic info
234         char[] type = settings["type"];
235         char[] target = settings["target"];
236        
237         if (type == "library" || type == "sourcelibrary") {
238             char[] dotname = std.string.replace(build, std.path.sep, ".");
239            
240             // get the list of files
241             char[][] files = targetToFiles(build, conf);
242            
243             // and other necessary data
244             char[] bflags, debugflags, releaseflags;
245             if ("buildflags" in settings) {
246                 bflags = settings["buildflags"] ~ " ";
247             }
248             if ("debugflags" in settings) {
249                 debugflags = settings["debugflags"] ~ " ";
250             } else {
251                 debugflags = "-debug -gc ";
252             }
253             if ("releaseflags" in settings) {
254                 releaseflags = settings["releaseflags"] ~ " ";
255             }
256            
257             // output what we're building
258             writefln("%s => %s", build, target);
259             if (files.length == 0) {
260                 writefln("WARNING: Section %s has no files.", build);
261                 continue;
262             }
263
264             // prepare to do documentation
265             prepareDocs(build, doDocs);
266        
267             // do the prebuild
268             if ("prebuild" in settings) {
269                 dsssScriptedStep(conf, settings["prebuild"]);
270             }
271            
272             // get the file list
273             char[] fileList = std.string.join(targetToFiles(build, conf), " ");
274            
275             // if we should, build the library
276             if ((type == "library" && libsSafe()) ||
277                 doDocs /* need to build the library to get docs */ ||
278                 testLibs /* need to build the ilbrary to test it */) {
279                
280                 if (buildDebug)
281                     buildLibrary("debug-" ~ target, bl, bflags ~ debugflags, docbl, fileList, settings);
282                 buildLibrary(target, bl, bflags ~ releaseflags, docbl, fileList, settings);
283             }
284        
285             // do the postbuild
286             if ("postbuild" in settings) {
287                 dsssScriptedStep(conf, settings["postbuild"]);
288             }
289            
290             // an extra line for clarity
291             writefln("");
292            
293         } else if (type == "special") {
294             // special type, do pre/post
295             writefln("%s", target);
296             if ("prebuild" in settings) {
297                 dsssScriptedStep(conf, settings["prebuild"]);
298             }
299            
300             if ("postbuild" in settings) {
301                 dsssScriptedStep(conf, settings["postbuild"]);
302             }
303             writefln("");
304            
305         } else if (type == "subdir") {
306             // recurse
307             char[] origcwd = getcwd();
308             chdir(build);
309            
310             // the one thing that's passed in is build flags
311             char[] orig_dsss_build = dsss_build.dup;
312             if ("buildflags" in settings) {
313                 dsss_build ~= settings["buildflags"] ~ " ";
314             }
315            
316             int buildret = sss.build.build(null);
317             chdir(origcwd);
318            
319             dsss_build = orig_dsss_build;
320            
321         }         
322     }
323    
324     // 4) Binaries
325     foreach (build; buildSources) {
326         char[][char[]] settings = conf.settings[build];
327        
328         // basic info
329         char[] bfile = build;
330         char[] type = settings["type"];
331         char[] target = settings["target"];
332         int bfileplus = std.string.find(bfile, '+');
333         if (bfileplus != -1) {
334             bfile = bfile[0..bfileplus];
335         }
336        
337         if (type == "binary") {
338             // our binary build line
339             char[] bflags;
340             if ("buildflags" in settings) {
341                 bflags = settings["buildflags"];
342             }
343             if (buildDebug) {
344                 if ("debugflags" in settings) {
345                     bflags ~= " " ~ settings["debugflags"];
346                 } else {
347                     bflags ~= " -debug -gc";
348                 }
349             } else {
350                 if ("releaseflags" in settings) {
351                     bflags ~= " " ~ settings["releaseflags"];
352                 }
353             }
354            
355             char[] bbl = bl ~ bflags ~ " ";
356            
357             // output what we're building
358             writefln("%s => %s", bfile, target);
359
360             // prepare for documentation
361             prepareDocs(build, doDocs && doDocBinaries);
362             bbl ~= docbl;
363            
364             // do the prebuild
365             if ("prebuild" in settings) {
366                 dsssScriptedStep(conf, settings["prebuild"]);
367             }
368            
369             // build a build line
370             char[] ext = std.string.tolower(getExt(bfile));
371             if (ext == "d") {
372                 bbl ~= bfile ~ " -of" ~ target ~ " ";
373             } else if (ext == "brf") {
374                 bbl ~= "@" ~ getName(bfile) ~ " ";
375             } else {
376                 writefln("ERROR: I don't know how to build something with extension %s", ext);
377                 return 1;
378             }
379            
380             // then do it
381             vSaySystemRDie(bbl, "-rf", target ~ ".rf", deleteRFiles);
382            
383             // do the postbuild
384             if ("postbuild" in settings) {
385                 dsssScriptedStep(conf, settings["postbuild"]);
386             }
387            
388             // an extra line for clarity
389             writefln("");
390            
391         }
392     }
393    
394     return 0;
395 }
396
397 /**
398  * Helper function to build libraries
399  *
400  * Params:
401  *  target   = target file name (minus platform-specific parts)
402  *  bl       = the base build line
403  *  bflags   = build flags
404  *  docbl    = build flags for documentation ("" for no docs)
405  *  fileList = list of files to be compiled into the library
406  *  settings = settings for this section from DSSSConf
407  */
408 void buildLibrary(char[] target, char[] bl, char[] bflags, char[] docbl,
409                  char[] fileList, char[][char[]] settings)
410 {
411                 char[] shlibname = getShLibName(settings);
412                 char[][] shortshlibnames = getShortShLibNames(settings);
413                 char[] shlibflag = getShLibFlag(settings);
414
415                 if (targetGNUOrPosix()) {
416                     // first do a static library
417                     if (exists("lib" ~ target ~ ".a")) std.file.remove("lib" ~ target ~ ".a");
418                     char[] stbl = bl ~ docbl ~ bflags ~ " -explicit -lib " ~ fileList ~ " -oflib" ~ target ~ ".a";
419                     if (testLibs || (shLibSupport() && ("shared" in settings)))
420                         stbl ~= " -full";
421                     vSaySystemRDie(stbl, "-rf", target ~ "_static.rf", deleteRFiles);
422
423                     // perhaps test the static library
424                     if (testLibs) {
425                         writefln("Testing %s", target);
426                         char[] tbl = bl ~ bflags ~ " -unittest -full " ~ fileList ~ " " ~ dsssLibTestDPrefix ~ " -oftest_" ~ target;
427                         vSaySystemRDie(tbl, "-rf", target ~ "_test.rf", deleteRFiles);
428                         vSaySystemDie("./test_" ~ target);
429                     }
430                    
431                     if (shLibSupport() &&
432                         ("shared" in settings)) {
433                         // then make the shared library
434                         if (exists(shlibname)) std.file.remove(shlibname);
435                         char[] shbl = bl ~ bflags ~ " -fPIC -explicit -shlib -full " ~ fileList ~ " -of" ~ shlibname ~
436                         " " ~ shlibflag;
437                        
438                         // finally, the shared compile
439                         vSaySystemRDie(shbl, "-rf", target ~ "_shared.rf", deleteRFiles);
440                     }
441                    
442                 } else if (targetVersion("Windows")) {
443                     // for the moment, only do a static library
444                     if (exists(target ~ ".lib")) std.file.remove(target ~ ".lib");
445                     char[] stbl = bl ~ docbl ~ bflags ~ " -explicit -lib " ~ fileList ~ " -of" ~ target ~ ".lib";
446                     if (testLibs)
447                         stbl ~= " -full";
448                     vSaySystemRDie(stbl, "-rf", target ~ "_static.rf", deleteRFiles);
449
450                     // perhaps test the static library
451                     if (testLibs) {
452                         writefln("Testing %s", target);
453                         char[] tbl = bl ~ bflags ~ " -unittest -full " ~ fileList ~ " " ~ dsssLibTestDPrefix ~ " -oftest_" ~ target ~ ".exe";
454                         vSaySystemRDie(tbl, "-rf", target ~ "_test.rf", deleteRFiles);
455                         vSaySystemDie("test_" ~ target ~ ".exe");
456                     }
457
458                 } else {
459                     assert(0);
460                 }
461 }
Note: See TracBrowser for help on using the browser.