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