1 /**
2 	Build settings definitions.
3 
4 	Copyright: © 2013-2014 rejectedsoftware e.K.
5 	License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
6 	Authors: Sönke Ludwig
7 */
8 module dub.compilers.buildsettings;
9 
10 import dub.internal.vibecompat.inet.path;
11 
12 import std.array : array;
13 import std.algorithm : filter, any;
14 import std.path : globMatch;
15 import std.typecons : BitFlags;
16 import std.algorithm.iteration : uniq;
17 import std.range : chain;
18 
19 /// BuildPlatform specific settings, like needed libraries or additional
20 /// include paths.
21 struct BuildSettings {
22 	import dub.internal.vibecompat.data.serialization : byName;
23 
24 	TargetType targetType;
25 	string targetPath;
26 	string targetName;
27 	string workingDirectory;
28 	string mainSourceFile;
29 	string[] dflags;
30 	string[] lflags;
31 	string[] libs;
32 	string[] linkerFiles;
33 	string[] sourceFiles;
34 	string[] injectSourceFiles;
35 	string[] copyFiles;
36 	string[] extraDependencyFiles;
37 	string[] versions;
38 	string[] debugVersions;
39 	string[] versionFilters;
40 	string[] debugVersionFilters;
41 	string[] importPaths;
42 	string[] stringImportPaths;
43 	string[] importFiles;
44 	string[] stringImportFiles;
45 	string[] preGenerateCommands;
46 	string[] postGenerateCommands;
47 	string[] preBuildCommands;
48 	string[] postBuildCommands;
49 	string[] preRunCommands;
50 	string[] postRunCommands;
51 	string[string] environments;
52 	string[string] buildEnvironments;
53 	string[string] runEnvironments;
54 	string[string] preGenerateEnvironments;
55 	string[string] postGenerateEnvironments;
56 	string[string] preBuildEnvironments;
57 	string[string] postBuildEnvironments;
58 	string[string] preRunEnvironments;
59 	string[string] postRunEnvironments;
60 	@byName BuildRequirements requirements;
61 	@byName BuildOptions options;
62 
63 	BuildSettings dup()
64 	const {
65 		import std.traits: FieldNameTuple;
66 		import std.algorithm: map;
67 		import std.typecons: tuple;
68 		import std.array: assocArray;
69 		BuildSettings ret;
70 		foreach (m; FieldNameTuple!BuildSettings) {
71 			static if (is(typeof(__traits(getMember, ret, m) = __traits(getMember, this, m).dup)))
72 				__traits(getMember, ret, m) = __traits(getMember, this, m).dup;
73 			else static if (is(typeof(add(__traits(getMember, ret, m), __traits(getMember, this, m)))))
74 				add(__traits(getMember, ret, m), __traits(getMember, this, m));
75 			else static if (is(typeof(__traits(getMember, ret, m) = __traits(getMember, this, m))))
76 				__traits(getMember, ret, m) = __traits(getMember, this, m);
77 			else static assert(0, "Cannot duplicate BuildSettings." ~ m);
78 		}
79 		assert(ret.targetType == targetType);
80 		assert(ret.targetName == targetName);
81 		assert(ret.importPaths == importPaths);
82 		return ret;
83 	}
84 
85 	void add(in BuildSettings bs)
86 	{
87 		addDFlags(bs.dflags);
88 		addLFlags(bs.lflags);
89 		addLibs(bs.libs);
90 		addLinkerFiles(bs.linkerFiles);
91 		addSourceFiles(bs.sourceFiles);
92 		addCopyFiles(bs.copyFiles);
93 		addExtraDependencyFiles(bs.extraDependencyFiles);
94 		addVersions(bs.versions);
95 		addDebugVersions(bs.debugVersions);
96 		addVersionFilters(bs.versionFilters);
97 		addDebugVersionFilters(bs.debugVersionFilters);
98 		addImportPaths(bs.importPaths);
99 		addStringImportPaths(bs.stringImportPaths);
100 		addImportFiles(bs.importFiles);
101 		addStringImportFiles(bs.stringImportFiles);
102 		addPreGenerateCommands(bs.preGenerateCommands);
103 		addPostGenerateCommands(bs.postGenerateCommands);
104 		addPreBuildCommands(bs.preBuildCommands);
105 		addPostBuildCommands(bs.postBuildCommands);
106 		addPreRunCommands(bs.preRunCommands);
107 		addPostRunCommands(bs.postRunCommands);
108 	}
109 
110 	void addDFlags(in string[] value...) { dflags = chain(dflags, value.dup).uniq.array; }
111 	void prependDFlags(in string[] value...) { prepend(dflags, value); }
112 	void removeDFlags(in string[] value...) { remove(dflags, value); }
113 	void addLFlags(in string[] value...) { lflags ~= value; }
114 	void addLibs(in string[] value...) { add(libs, value); }
115 	void addLinkerFiles(in string[] value...) { add(linkerFiles, value); }
116 	void addSourceFiles(in string[] value...) { add(sourceFiles, value); }
117 	void prependSourceFiles(in string[] value...) { prepend(sourceFiles, value); }
118 	void removeSourceFiles(in string[] value...) { removePaths(sourceFiles, value); }
119 	void addInjectSourceFiles(in string[] value...) { add(injectSourceFiles, value); }
120 	void addCopyFiles(in string[] value...) { add(copyFiles, value); }
121 	void addExtraDependencyFiles(in string[] value...) { add(extraDependencyFiles, value); }
122 	void addVersions(in string[] value...) { add(versions, value); }
123 	void addDebugVersions(in string[] value...) { add(debugVersions, value); }
124 	void addVersionFilters(in string[] value...) { add(versionFilters, value); }
125 	void addDebugVersionFilters(in string[] value...) { add(debugVersionFilters, value); }
126 	void addImportPaths(in string[] value...) { add(importPaths, value); }
127 	void addStringImportPaths(in string[] value...) { add(stringImportPaths, value); }
128 	void prependStringImportPaths(in string[] value...) { prepend(stringImportPaths, value); }
129 	void addImportFiles(in string[] value...) { add(importFiles, value); }
130 	void addStringImportFiles(in string[] value...) { addSI(stringImportFiles, value); }
131 	void addPreGenerateCommands(in string[] value...) { add(preGenerateCommands, value, false); }
132 	void addPostGenerateCommands(in string[] value...) { add(postGenerateCommands, value, false); }
133 	void addPreBuildCommands(in string[] value...) { add(preBuildCommands, value, false); }
134 	void addPostBuildCommands(in string[] value...) { add(postBuildCommands, value, false); }
135 	void addPreRunCommands(in string[] value...) { add(preRunCommands, value, false); }
136 	void addPostRunCommands(in string[] value...) { add(postRunCommands, value, false); }
137 	void addEnvironments(in string[string] value) { add(environments, value); }
138 	void updateEnvironments(in string[string] value) { update(environments, value); }
139 	void addBuildEnvironments(in string[string] value) { add(buildEnvironments, value); }
140 	void updateBuildEnvironments(in string[string] value) { update(buildEnvironments, value); }
141 	void addRunEnvironments(in string[string] value) { add(runEnvironments, value); }
142 	void updateRunEnvironments(in string[string] value) { update(runEnvironments, value); }
143 	void addPreGenerateEnvironments(in string[string] value) { add(preGenerateEnvironments, value); }
144 	void updatePreGenerateEnvironments(in string[string] value) { update(preGenerateEnvironments, value); }
145 	void addPostGenerateEnvironments(in string[string] value) { add(postGenerateEnvironments, value); }
146 	void updatePostGenerateEnvironments(in string[string] value) { update(postGenerateEnvironments, value); }
147 	void addPreBuildEnvironments(in string[string] value) { add(preBuildEnvironments, value); }
148 	void updatePreBuildEnvironments(in string[string] value) { update(preBuildEnvironments, value); }
149 	void addPostBuildEnvironments(in string[string] value) { add(postBuildEnvironments, value); }
150 	void updatePostBuildEnvironments(in string[string] value) { update(postBuildEnvironments, value); }
151 	void addPreRunEnvironments(in string[string] value) { add(preRunEnvironments, value); }
152 	void updatePreRunEnvironments(in string[string] value) { update(preRunEnvironments, value); }
153 	void addPostRunEnvironments(in string[string] value) { add(postRunEnvironments, value); }
154 	void updatePostRunEnvironments(in string[string] value) { update(postRunEnvironments, value); }
155 	void addRequirements(in BuildRequirement[] value...) { foreach (v; value) this.requirements |= v; }
156 	void addRequirements(in BuildRequirements value) { this.requirements |= value; }
157 	void addOptions(in BuildOption[] value...) { foreach (v; value) this.options |= v; }
158 	void addOptions(in BuildOptions value) { this.options |= value; }
159 	void removeOptions(in BuildOption[] value...) { foreach (v; value) this.options &= ~v; }
160 	void removeOptions(in BuildOptions value) { this.options &= ~value; }
161 
162 private:
163 	static auto filterDuplicates(T)(ref string[] arr, in T vals, bool noDuplicates = true)
164 	{
165 		return noDuplicates
166 			? vals.filter!(filtered => !arr.any!(item => item == filtered)).array
167 			: vals;
168 	}
169 
170 	// Append vals to arr without adding duplicates.
171 	static void add(ref string[] arr, in string[] vals, bool noDuplicates = true)
172 	{
173 		// vals might contain duplicates, add each val individually
174 		foreach (val; vals)
175 			arr ~= filterDuplicates(arr, [val], noDuplicates);
176 	}
177 	// Append vals to AA
178 	static void add(ref string[string] aa, in string[string] vals)
179 	{
180 		// vals might contain duplicated keys, add each val individually
181 		foreach (key, val; vals)
182 			if (key !in aa)
183 				aa[key] = val;
184 	}
185 	// Update vals to AA
186 	static void update(ref string[string] aa, in string[string] vals)
187 	{
188 		// If there are duplicate keys, they will be ignored and overwritten.
189 		foreach (key, val; vals)
190 			aa[key] = val;
191 	}
192 
193 	unittest
194 	{
195 		auto ary = ["-dip1000", "-vgc"];
196 		BuildSettings.add(ary, ["-dip1000", "-vgc"]);
197 		assert(ary == ["-dip1000", "-vgc"]);
198 		BuildSettings.add(ary, ["-dip1001", "-vgc"], false);
199 		assert(ary == ["-dip1000", "-vgc", "-dip1001", "-vgc"]);
200 		BuildSettings.add(ary, ["-dupflag", "-notdupflag", "-dupflag"]);
201 		assert(ary == ["-dip1000", "-vgc", "-dip1001", "-vgc", "-dupflag", "-notdupflag"]);
202 	}
203 
204 	// Prepend arr by vals without adding duplicates.
205 	static void prepend(ref string[] arr, in string[] vals, bool noDuplicates = true)
206 	{
207 		import std.range : retro;
208 		// vals might contain duplicates, add each val individually
209 		foreach (val; vals.retro)
210 			arr = filterDuplicates(arr, [val], noDuplicates) ~ arr;
211 	}
212 
213 	unittest
214 	{
215 		auto ary = ["-dip1000", "-vgc"];
216 		BuildSettings.prepend(ary, ["-dip1000", "-vgc"]);
217 		assert(ary == ["-dip1000", "-vgc"]);
218 		BuildSettings.prepend(ary, ["-dip1001", "-vgc"], false);
219 		assert(ary == ["-dip1001", "-vgc", "-dip1000", "-vgc"]);
220 		BuildSettings.prepend(ary, ["-dupflag", "-notdupflag", "-dupflag"]);
221 		assert(ary == ["-notdupflag", "-dupflag", "-dip1001", "-vgc", "-dip1000", "-vgc"]);
222 	}
223 
224 	// add string import files (avoids file name duplicates in addition to path duplicates)
225 	static void addSI(ref string[] arr, in string[] vals)
226 	{
227 		bool[string] existing;
228 		foreach (v; arr) existing[NativePath(v).head.name] = true;
229 		foreach (v; vals) {
230 			auto s = NativePath(v).head.name;
231 			if (s !in existing) {
232 				existing[s] = true;
233 				arr ~= v;
234 			}
235 		}
236 	}
237 
238 	unittest
239 	{
240 		auto ary = ["path/foo.txt"];
241 		BuildSettings.addSI(ary, ["path2/foo2.txt"]);
242 		assert(ary == ["path/foo.txt", "path2/foo2.txt"]);
243 		BuildSettings.addSI(ary, ["path2/foo.txt"]); // no duplicate basenames
244 		assert(ary == ["path/foo.txt", "path2/foo2.txt"]);
245 	}
246 
247 	static bool pathMatch(string path, string pattern)
248 	{
249 		import std.functional : memoize;
250 
251 		alias nativePath = memoize!((string stringPath) => NativePath(stringPath));
252 
253 		return nativePath(path) == nativePath(pattern) || globMatch(path, pattern);
254 	}
255 
256 	static void removeValuesFromArray(alias Match)(ref string[] arr, in string[] vals)
257 	{
258 		bool matches(string s)
259 		{
260 			return vals.any!(item => Match(s, item));
261 		}
262 		arr = arr.filter!(s => !matches(s)).array;
263 	}
264 
265 	static void removePaths(ref string[] arr, in string[] vals)
266 	{
267 		removeValuesFromArray!(pathMatch)(arr, vals);
268 	}
269 
270 	unittest
271 	{
272 		auto ary = ["path1", "root/path1", "root/path2", "root2/path1"];
273 		BuildSettings.removePaths(ary, ["path1"]);
274 		assert(ary == ["root/path1", "root/path2", "root2/path1"]);
275 		BuildSettings.removePaths(ary, ["*/path1"]);
276 		assert(ary == ["root/path2"]);
277 		BuildSettings.removePaths(ary, ["foo", "bar", "root/path2"]);
278 		assert(ary == []);
279 	}
280 
281 	static void remove(ref string[] arr, in string[] vals)
282 	{
283 		removeValuesFromArray!((a, b) => a == b)(arr, vals);
284 	}
285 
286 	unittest
287 	{
288 		import std.string : join;
289 
290 		auto ary = ["path1", "root/path1", "root/path2", "root2/path1"];
291 		BuildSettings.remove(ary, ["path1"]);
292 		assert(ary == ["root/path1", "root/path2", "root2/path1"]);
293 		BuildSettings.remove(ary, ["root/path*"]);
294 		assert(ary == ["root/path1", "root/path2", "root2/path1"]);
295 		BuildSettings.removePaths(ary, ["foo", "root/path2", "bar", "root2/path1"]);
296 		assert(ary == ["root/path1"]);
297 		BuildSettings.remove(ary, ["root/path1", "foo"]);
298 		assert(ary == []);
299 	}
300 }
301 
302 enum BuildSetting {
303 	dflags            = 1<<0,
304 	lflags            = 1<<1,
305 	libs              = 1<<2,
306 	sourceFiles       = 1<<3,
307 	copyFiles         = 1<<4,
308 	versions          = 1<<5,
309 	debugVersions     = 1<<6,
310 	importPaths       = 1<<7,
311 	stringImportPaths = 1<<8,
312 	options           = 1<<9,
313 	none = 0,
314 	commandLine = dflags|copyFiles,
315 	commandLineSeparate = commandLine|lflags,
316 	all = dflags|lflags|libs|sourceFiles|copyFiles|versions|debugVersions|importPaths|stringImportPaths|options,
317 	noOptions = all & ~options
318 }
319 
320 enum TargetType {
321 	autodetect,
322 	none,
323 	executable,
324 	library,
325 	sourceLibrary,
326 	dynamicLibrary,
327 	staticLibrary,
328 	object
329 }
330 
331 enum BuildRequirement {
332 	none = 0,                     /// No special requirements
333 	allowWarnings        = 1<<0,  /// Warnings do not abort compilation
334 	silenceWarnings      = 1<<1,  /// Don't show warnings
335 	disallowDeprecations = 1<<2,  /// Using deprecated features aborts compilation
336 	silenceDeprecations  = 1<<3,  /// Don't show deprecation warnings
337 	disallowInlining     = 1<<4,  /// Avoid function inlining, even in release builds
338 	disallowOptimization = 1<<5,  /// Avoid optimizations, even in release builds
339 	requireBoundsCheck   = 1<<6,  /// Always perform bounds checks
340 	requireContracts     = 1<<7,  /// Leave assertions and contracts enabled in release builds
341 	relaxProperties      = 1<<8,  /// DEPRECATED: Do not enforce strict property handling (-property)
342 	noDefaultFlags       = 1<<9,  /// Do not issue any of the default build flags (e.g. -debug, -w, -property etc.) - use only for development purposes
343 }
344 
345 	struct BuildRequirements {
346 		import dub.internal.vibecompat.data.serialization : ignore;
347 
348 		@ignore BitFlags!BuildRequirement values;
349 		this(BuildRequirement req) { values = req; }
350 
351 		alias values this;
352 	}
353 
354 enum BuildOption {
355 	none = 0,                     /// Use compiler defaults
356 	debugMode = 1<<0,             /// Compile in debug mode (enables contracts, -debug)
357 	releaseMode = 1<<1,           /// Compile in release mode (disables assertions and bounds checks, -release)
358 	coverage = 1<<2,              /// Enable code coverage analysis (-cov)
359 	debugInfo = 1<<3,             /// Enable symbolic debug information (-g)
360 	debugInfoC = 1<<4,            /// Enable symbolic debug information in C compatible form (-gc)
361 	alwaysStackFrame = 1<<5,      /// Always generate a stack frame (-gs)
362 	stackStomping = 1<<6,         /// Perform stack stomping (-gx)
363 	inline = 1<<7,                /// Perform function inlining (-inline)
364 	noBoundsCheck = 1<<8,         /// Disable all bounds checking (-noboundscheck)
365 	optimize = 1<<9,              /// Enable optimizations (-O)
366 	profile = 1<<10,              /// Emit profiling code (-profile)
367 	unittests = 1<<11,            /// Compile unit tests (-unittest)
368 	verbose = 1<<12,              /// Verbose compiler output (-v)
369 	ignoreUnknownPragmas = 1<<13, /// Ignores unknown pragmas during compilation (-ignore)
370 	syntaxOnly = 1<<14,           /// Don't generate object files (-o-)
371 	warnings = 1<<15,             /// Enable warnings (-wi)
372 	warningsAsErrors = 1<<16,     /// Treat warnings as errors (-w)
373 	ignoreDeprecations = 1<<17,   /// Do not warn about using deprecated features (-d)
374 	deprecationWarnings = 1<<18,  /// Warn about using deprecated features (-dw)
375 	deprecationErrors = 1<<19,    /// Stop compilation upon usage of deprecated features (-de)
376 	property = 1<<20,             /// DEPRECATED: Enforce property syntax (-property)
377 	profileGC = 1<<21,            /// Profile runtime allocations
378 	pic = 1<<22,                  /// Generate position independent code
379 	betterC = 1<<23,              /// Compile in betterC mode (-betterC)
380 	lowmem = 1<<24,               /// Compile in lowmem mode (-lowmem)
381 
382 	// for internal usage
383 	_docs = 1<<25,                // Write ddoc to docs
384 	_ddox = 1<<26                 // Compile docs.json
385 }
386 
387 	struct BuildOptions {
388 		import dub.internal.vibecompat.data.serialization : ignore;
389 
390 		@ignore BitFlags!BuildOption values;
391 		this(BuildOption opt) { values = opt; }
392 		this(BitFlags!BuildOption v) { values = v; }
393 
394 		alias values this;
395 	}
396 
397 /**
398 	All build options that will be inherited upwards in the dependency graph
399 
400 	Build options in this category fulfill one of the following properties:
401 	$(UL
402 		$(LI The option affects the semantics of the generated code)
403 		$(LI The option affects if a certain piece of code is valid or not)
404 		$(LI The option enabled meta information in dependent projects that are useful for the dependee (e.g. debug information))
405 	)
406 */
407 enum BuildOptions inheritedBuildOptions = BuildOption.debugMode | BuildOption.releaseMode
408 	| BuildOption.coverage | BuildOption.debugInfo | BuildOption.debugInfoC
409 	| BuildOption.alwaysStackFrame | BuildOption.stackStomping | BuildOption.inline
410 	| BuildOption.noBoundsCheck | BuildOption.profile | BuildOption.ignoreUnknownPragmas
411 	| BuildOption.syntaxOnly | BuildOption.warnings	| BuildOption.warningsAsErrors
412 	| BuildOption.ignoreDeprecations | BuildOption.deprecationWarnings
413 	| BuildOption.deprecationErrors | BuildOption.property | BuildOption.profileGC
414 	| BuildOption.pic;