1 // SemiTwist D Tools
2 // STManage: STBuild
3 // Written in the D programming language.
4 
5 /++
6 Author:
7 $(WEB www.semitwist.com, Nick Sabalausky)
8 
9 This has been tested to work with DMD 2.058 through 2.062
10 +/
11 
12 //TODO: Clean all if stbuild.conf has changed
13 //TODO: Clean all if using a different build tool from last time
14 //TODO: User-defined options (ex: set preference for default: rebuild or xfbuild)
15 //TODO: Handle DMD patched for -ww
16 //      Use STBUILD_OPTS env var: STBUILD_OPTS=dmdpatch_ww;whatever...
17 //TODO: Disallow crazy characters in target names
18 //TODO? Use Goldie to parse .conf file
19 //TODO: Get obj dir from conf switches instead of hardcoding to "obj/target/mode"
20 //TODO: Translate extraArgs between rebuild/xfbuild/rdmd
21 //TODO: When using xfbuild, make sure root filenames end in ".d" (ie "src/main.d" instead of "src/main")
22 //TODO: Fix: 'stbuild.conf' errors get displayed before header message.
23 
24 module semitwist.apps.stmanage.stbuild.main;
25 
26 import semitwist.cmd.all;
27 
28 import semitwist.apps.stmanage.stbuild.cmdArgs;
29 import semitwist.apps.stmanage.stbuild.conf;
30 
31 Options cmdArgs;
32 Conf conf;
33 
34 void moveMapFiles(string subDir=".")
35 {
36 	foreach(string name; dirEntries(".", SpanMode.shallow))
37 	{
38 		if(name.globMatch("*.map"))
39 		{
40 			auto newName = "obj/"~subDir~"/"~name.baseName();
41 			if(exists(newName))
42 				remove(newName);
43 			
44 			rename(name, newName);
45 		}
46 	}
47 }
48 
49 int process(string target, string mode, bool verbose)
50 {
51 	auto processor = ( cmdArgs.cleanOnly? &clean : &build );
52 	
53 	string[] targetsToProcess = (target == conf.targetAll)? conf.targetAllElems : [target];
54 	string[] modesToProcess   = (mode   == conf.modeAll)?   conf.modeAllElems   : [mode  ];
55 	
56 	int errLevel = 0;
57 	foreach(string currTarget; targetsToProcess)
58 	foreach(string currMode;   modesToProcess  )
59 	{
60 		auto result = processor(currTarget, currMode, verbose);
61 		if(result > errLevel)
62 			errLevel = result;
63 	}
64 	
65 	return errLevel;
66 }
67 
68 int build(string target, string mode, bool verbose)
69 {
70 	mixin(deferAssert!(`target != conf.targetAll`, "target 'all' passed to build()"));
71 	mixin(deferAssert!(`mode != conf.modeAll`, "mode 'all' passed to build()"));
72 
73 	if(verbose)
74 		cmd.echo("Building %s %s...".format(target, mode));
75 
76 	auto objDir = "obj/"~target~"/"~mode;
77 	if(!exists(objDir))
78 		mkdirRecurse(objDir);
79 	
80 	int ret;
81 	auto cmdLine =
82 		buildToolExecName(cmdArgs.buildTool)~" "~
83 		cmdArgs.extraArgs~" "~
84 		conf.getFlags(target, mode, cmdArgs.buildTool);
85 	
86 	if(cmdArgs.showCmd) cmd.echo(cmdLine);
87 	ret = system(cmdLine);
88 	moveMapFiles(target~"/"~mode);
89 	
90 	return ret;
91 }
92 
93 int clean(string target, string mode, bool verbose)
94 {
95 	mixin(deferAssert!(`target != conf.targetAll`, "target 'all' passed to clean()"));
96 	mixin(deferAssert!(`mode != conf.modeAll`, "mode 'all' passed to clean()"));
97 
98 	if(verbose)
99 		cmd.echo("Cleaning %s %s...".format(target, mode));
100 
101 	auto objDir = "obj/"~target~"/"~mode; 
102 	if(exists(objDir))
103 	foreach(string name; dirEntries(objDir, SpanMode.depth))
104 	{
105 		if(name.globMatch("*.map") || name.baseName().globMatch("deps") || name.globMatch("*"~objExt))
106 			remove(name);
107 	}
108 		
109 	return 0;
110 }
111 
112 int main(string[] args)
113 {
114 	int errLevel = cmdArgs.process(args);
115 	if(errLevel != -1)
116 		return errLevel;
117 
118 	conf = cmdArgs.conf;
119 
120 	return process(cmdArgs.target, cmdArgs.mode, !cmdArgs.quiet);
121 }