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