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