1 /**
2 	Generator for project files
3 
4 	Copyright: © 2012-2013 Matthias Dondorff, © 2013-2016 Sönke Ludwig
5 	License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
6 	Authors: Matthias Dondorff
7 */
8 module dub.generators.generator;
9 
10 import dub.compilers.compiler;
11 import dub.generators.cmake;
12 import dub.generators.build;
13 import dub.generators.sublimetext;
14 import dub.generators.visuald;
15 import dub.internal.vibecompat.core.file;
16 import dub.internal.vibecompat.core.log;
17 import dub.internal.vibecompat.inet.path;
18 import dub.package_;
19 import dub.packagemanager;
20 import dub.project;
21 
22 import std.algorithm : map, filter, canFind, balancedParens;
23 import std.array : array;
24 import std.array;
25 import std.exception;
26 import std.file;
27 import std.string;
28 
29 
30 /**
31 	Common interface for project generators/builders.
32 */
33 class ProjectGenerator
34 {
35 	/** Information about a single binary target.
36 
37 		A binary target can either be an executable or a static/dynamic library.
38 		It consists of one or more packages.
39 	*/
40 	struct TargetInfo {
41 		/// The root package of this target
42 		Package pack;
43 
44 		/// All packages compiled into this target
45 		Package[] packages;
46 
47 		/// The configuration used for building the root package
48 		string config;
49 
50 		/** Build settings used to build the target.
51 
52 			The build settings include all sources of all contained packages.
53 
54 			Depending on the specific generator implementation, it may be
55 			necessary to add any static or dynamic libraries generated for
56 			child targets ($(D linkDependencies)).
57 		*/
58 		BuildSettings buildSettings;
59 
60 		/** List of all dependencies.
61 
62 			This list includes dependencies that are not the root of a binary
63 			target.
64 		*/
65 		string[] dependencies;
66 
67 		/** List of all binary dependencies.
68 
69 			This list includes all dependencies that are the root of a binary
70 			target.
71 		*/
72 		string[] linkDependencies;
73 	}
74 
75 	protected {
76 		Project m_project;
77 		NativePath m_tempTargetExecutablePath;
78 	}
79 
80 	this(Project project)
81 	{
82 		m_project = project;
83 	}
84 
85 	/** Performs the full generator process.
86 	*/
87 	final void generate(GeneratorSettings settings)
88 	{
89 		import dub.compilers.utils : enforceBuildRequirements;
90 
91 		if (!settings.config.length) settings.config = m_project.getDefaultConfiguration(settings.platform);
92 
93 		string[string] configs = m_project.getPackageConfigs(settings.platform, settings.config);
94 		TargetInfo[string] targets;
95 
96 		foreach (pack; m_project.getTopologicalPackageList(true, null, configs)) {
97 			BuildSettings buildSettings;
98 			auto config = configs[pack.name];
99 			buildSettings.processVars(m_project, pack, pack.getBuildSettings(settings.platform, config), settings, true);
100 			targets[pack.name] = TargetInfo(pack, [pack], config, buildSettings);
101 
102 			prepareGeneration(pack, m_project, settings, buildSettings);
103 		}
104 
105 		configurePackages(m_project.rootPackage, targets, settings);
106 
107 		addBuildTypeSettings(targets, settings);
108 		foreach (ref t; targets.byValue) enforceBuildRequirements(t.buildSettings);
109 
110 		generateTargets(settings, targets);
111 
112 		foreach (pack; m_project.getTopologicalPackageList(true, null, configs)) {
113 			BuildSettings buildsettings;
114 			buildsettings.processVars(m_project, pack, pack.getBuildSettings(settings.platform, configs[pack.name]), settings, true);
115 			bool generate_binary = !(buildsettings.options & BuildOption.syntaxOnly);
116 			auto bs = &targets[m_project.rootPackage.name].buildSettings;
117 			auto targetPath = (m_tempTargetExecutablePath.empty) ? NativePath(bs.targetPath) : m_tempTargetExecutablePath;
118 			finalizeGeneration(pack, m_project, settings, buildsettings, targetPath, generate_binary);
119 		}
120 
121 		performPostGenerateActions(settings, targets);
122 	}
123 
124 	/** Overridden in derived classes to implement the actual generator functionality.
125 
126 		The function should go through all targets recursively. The first target
127 		(which is guaranteed to be there) is
128 		$(D targets[m_project.rootPackage.name]). The recursive descent is then
129 		done using the $(D TargetInfo.linkDependencies) list.
130 
131 		This method is also potentially responsible for running the pre and post
132 		build commands, while pre and post generate commands are already taken
133 		care of by the $(D generate) method.
134 
135 		Params:
136 			settings = The generator settings used for this run
137 			targets = A map from package name to TargetInfo that contains all
138 				binary targets to be built.
139 	*/
140 	protected abstract void generateTargets(GeneratorSettings settings, in TargetInfo[string] targets);
141 
142 	/** Overridable method to be invoked after the generator process has finished.
143 
144 		An examples of functionality placed here is to run the application that
145 		has just been built.
146 	*/
147 	protected void performPostGenerateActions(GeneratorSettings settings, in TargetInfo[string] targets) {}
148 
149 	/** Configure `rootPackage` and all of it's dependencies.
150 
151 		1. Merge versions, debugVersions, and inheritable build
152 		settings from dependents to their dependencies.
153 
154 		2. Define version identifiers Have_dependency_xyz for all
155 		direct dependencies of all packages.
156 
157 		3. Merge versions, debugVersions, and inheritable build settings from
158 		dependencies to their dependents, so that importer and importee are ABI
159 		compatible. This also transports all Have_dependency_xyz version
160 		identifiers to `rootPackage`.
161 
162 		4. Filter unused versions and debugVersions from all targets. The
163 		filters have previously been upwards inherited (3.) so that versions
164 		used in a dependency are also applied to all dependents.
165 
166 		Note: The upwards inheritance is done at last so that siblings do not
167 		influence each other, also see https://github.com/dlang/dub/pull/1128.
168 
169 		Note: Targets without output are integrated into their
170 		dependents and removed from `targets`.
171 	 */
172 	private void configurePackages(Package rootPackage, TargetInfo[string] targets, GeneratorSettings genSettings)
173 	{
174 		import std.algorithm : remove, sort;
175 		import std.range : repeat;
176 
177 		auto roottarget = &targets[rootPackage.name];
178 
179 		// 0. do shallow configuration (not including dependencies) of all packages
180 		TargetType determineTargetType(const ref TargetInfo ti)
181 		{
182 			TargetType tt = ti.buildSettings.targetType;
183 			if (ti.pack is rootPackage) {
184 				if (tt == TargetType.autodetect || tt == TargetType.library) tt = TargetType.staticLibrary;
185 			} else {
186 				if (tt == TargetType.autodetect || tt == TargetType.library) tt = genSettings.combined ? TargetType.sourceLibrary : TargetType.staticLibrary;
187 				else if (tt == TargetType.dynamicLibrary) {
188 					logWarn("Dynamic libraries are not yet supported as dependencies - building as static library.");
189 					tt = TargetType.staticLibrary;
190 				}
191 			}
192 			if (tt != TargetType.none && tt != TargetType.sourceLibrary && ti.buildSettings.sourceFiles.empty) {
193 				logWarn(`Configuration '%s' of package %s contains no source files. Please add {"targetType": "none"} to its package description to avoid building it.`,
194 						ti.config, ti.pack.name);
195 				tt = TargetType.none;
196 			}
197 			return tt;
198 		}
199 
200 		string[] mainSourceFiles;
201 		bool[string] hasOutput;
202 
203 		foreach (ref ti; targets.byValue)
204 		{
205 			auto bs = &ti.buildSettings;
206 			// determine the actual target type
207 			bs.targetType = determineTargetType(ti);
208 
209 			switch (bs.targetType)
210 			{
211 			case TargetType.none:
212 				// ignore any build settings for targetType none (only dependencies will be processed)
213 				*bs = BuildSettings.init;
214 				bs.targetType = TargetType.none;
215 				break;
216 
217 			case TargetType.executable:
218 				break;
219 
220 			case TargetType.dynamicLibrary:
221 				// set -fPIC for dynamic library builds
222 				ti.buildSettings.addOptions(BuildOption.pic);
223 				goto default;
224 
225 			default:
226 				// remove any mainSourceFile from non-executable builds
227 				if (bs.mainSourceFile.length) {
228 					bs.sourceFiles = bs.sourceFiles.remove!(f => f == bs.mainSourceFile);
229 					mainSourceFiles ~= bs.mainSourceFile;
230 				}
231 				break;
232 			}
233 			bool generatesBinary = bs.targetType != TargetType.sourceLibrary && bs.targetType != TargetType.none;
234 			hasOutput[ti.pack.name] = generatesBinary || ti.pack is rootPackage;
235 		}
236 
237 		// add main source files to root executable
238 		{
239 			auto bs = &roottarget.buildSettings;
240 			if (bs.targetType == TargetType.executable) bs.addSourceFiles(mainSourceFiles);
241 		}
242 
243 		if (genSettings.filterVersions)
244 			foreach (ref ti; targets.byValue)
245 				inferVersionFilters(ti);
246 
247 		// mark packages as visited (only used during upwards propagation)
248 		void[0][Package] visited;
249 
250 		// collect all dependencies
251 		void collectDependencies(Package pack, ref TargetInfo ti, TargetInfo[string] targets, size_t level = 0)
252 		{
253 			// use `visited` here as pkgs cannot depend on themselves
254 			if (pack in visited)
255 				return;
256 			// transitive dependencies must be visited multiple times, see #1350
257 			immutable transitive = !hasOutput[pack.name];
258 			if (!transitive)
259 				visited[pack] = typeof(visited[pack]).init;
260 
261 			auto bs = &ti.buildSettings;
262 			if (hasOutput[pack.name])
263 				logDebug("%sConfiguring target %s (%s %s %s)", ' '.repeat(2 * level), pack.name, bs.targetType, bs.targetPath, bs.targetName);
264 			else
265 				logDebug("%sConfiguring target without output %s", ' '.repeat(2 * level), pack.name);
266 
267 			// get specified dependencies, e.g. vibe-d ~0.8.1
268 			auto deps = pack.getDependencies(targets[pack.name].config);
269 			logDebug("deps: %s -> %(%s, %)", pack.name, deps.byKey);
270 			foreach (depname; deps.keys.sort())
271 			{
272 				auto depspec = deps[depname];
273 				// get selected package for that dependency, e.g. vibe-d 0.8.2-beta.2
274 				auto deppack = m_project.getDependency(depname, depspec.optional);
275 				if (deppack is null) continue; // optional and not selected
276 
277 				// if dependency has no output
278 				if (!hasOutput[depname]) {
279 					// add itself
280 					ti.packages ~= deppack;
281 					// and it's transitive dependencies to current target
282 					collectDependencies(deppack, ti, targets, level + 1);
283 					continue;
284 				}
285 				auto depti = &targets[depname];
286 				const depbs = &depti.buildSettings;
287 				if (depbs.targetType == TargetType.executable && ti.buildSettings.targetType != TargetType.none)
288 					continue;
289 
290 				// add to (link) dependencies
291 				ti.dependencies ~= depname;
292 				ti.linkDependencies ~= depname;
293 
294 				// recurse
295 				collectDependencies(deppack, *depti, targets, level + 1);
296 
297 				// also recursively add all link dependencies of static libraries
298 				// preserve topological sorting of dependencies for correct link order
299 				if (depbs.targetType == TargetType.staticLibrary)
300 					ti.linkDependencies = ti.linkDependencies.filter!(d => !depti.linkDependencies.canFind(d)).array ~ depti.linkDependencies;
301 			}
302 
303 			enforce(!(ti.buildSettings.targetType == TargetType.none && ti.dependencies.empty),
304 				"Package with target type \"none\" must have dependencies to build.");
305 		}
306 
307 		collectDependencies(rootPackage, *roottarget, targets);
308 		visited.clear();
309 
310 		// 1. downwards inherits versions, debugVersions, and inheritable build settings
311 		static void configureDependencies(in ref TargetInfo ti, TargetInfo[string] targets, size_t level = 0)
312 		{
313 			// do not use `visited` here as dependencies must inherit
314 			// configurations from *all* of their parents
315 			logDebug("%sConfigure dependencies of %s, deps:%(%s, %)", ' '.repeat(2 * level), ti.pack.name, ti.dependencies);
316 			foreach (depname; ti.dependencies)
317 			{
318 				auto pti = &targets[depname];
319 				mergeFromDependent(ti.buildSettings, pti.buildSettings);
320 				configureDependencies(*pti, targets, level + 1);
321 			}
322 		}
323 
324 		configureDependencies(*roottarget, targets);
325 
326 		// 2. add Have_dependency_xyz for all direct dependencies of a target
327 		// (includes incorporated non-target dependencies and their dependencies)
328 		foreach (ref ti; targets.byValue)
329 		{
330 			import std.range : chain;
331 			import dub.internal.utils : stripDlangSpecialChars;
332 
333 			auto bs = &ti.buildSettings;
334 			auto pkgnames = ti.packages.map!(p => p.name).chain(ti.dependencies);
335 			bs.addVersions(pkgnames.map!(pn => "Have_" ~ stripDlangSpecialChars(pn)).array);
336 		}
337 
338 		// 3. upwards inherit full build configurations (import paths, versions, debugVersions, versionFilters, importPaths, ...)
339 		void configureDependents(ref TargetInfo ti, TargetInfo[string] targets, size_t level = 0)
340 		{
341 			// use `visited` here as pkgs cannot depend on themselves
342 			if (ti.pack in visited)
343 				return;
344 			visited[ti.pack] = typeof(visited[ti.pack]).init;
345 
346 			logDiagnostic("%sConfiguring dependent %s, deps:%(%s, %)", ' '.repeat(2 * level), ti.pack.name, ti.dependencies);
347 			// embedded non-binary dependencies
348 			foreach (deppack; ti.packages[1 .. $])
349 				ti.buildSettings.add(targets[deppack.name].buildSettings);
350 			// binary dependencies
351 			foreach (depname; ti.dependencies)
352 			{
353 				auto pdepti = &targets[depname];
354 				configureDependents(*pdepti, targets, level + 1);
355 				mergeFromDependency(pdepti.buildSettings, ti.buildSettings, genSettings.platform);
356 			}
357 		}
358 
359 		configureDependents(*roottarget, targets);
360 		visited.clear();
361 
362 		// 4. Filter applicable version and debug version identifiers
363 		if (genSettings.filterVersions)
364 		{
365 			foreach (name, ref ti; targets)
366 			{
367 				import std.algorithm.sorting : partition;
368 
369 				auto bs = &ti.buildSettings;
370 
371 				auto filtered = bs.versions.partition!(v => bs.versionFilters.canFind(v));
372 				logDebug("Filtering out unused versions for %s: %s", name, filtered);
373 				bs.versions = bs.versions[0 .. $ - filtered.length];
374 
375 				filtered = bs.debugVersions.partition!(v => bs.debugVersionFilters.canFind(v));
376 				logDebug("Filtering out unused debug versions for %s: %s", name, filtered);
377 				bs.debugVersions = bs.debugVersions[0 .. $ - filtered.length];
378 			}
379 		}
380 
381 		// 5. override string import files in dependencies
382 		static void overrideStringImports(ref TargetInfo target,
383 			ref TargetInfo parent, TargetInfo[string] targets, string[] overrides)
384 		{
385 			// Since string import paths are inherited from dependencies in the
386 			// inheritance step above (step 3), it is guaranteed that all
387 			// following dependencies will not have string import paths either,
388 			// so we can skip the recursion here
389 			if (!target.buildSettings.stringImportPaths.length)
390 				return;
391 
392 			// do not use visited here as string imports can be overridden by *any* parent
393 			//
394 			// special support for overriding string imports in parent packages
395 			// this is a candidate for deprecation, once an alternative approach
396 			// has been found
397 			bool any_override = false;
398 
399 			// override string import files (used for up to date checking)
400 			foreach (ref f; target.buildSettings.stringImportFiles)
401 			{
402 				foreach (o; overrides)
403 				{
404 					NativePath op;
405 					if (f != o && NativePath(f).head == (op = NativePath(o)).head) {
406 						logDebug("string import %s overridden by %s", f, o);
407 						f = o;
408 						any_override = true;
409 					}
410 				}
411 			}
412 
413 			// override string import paths by prepending to the list, in
414 			// case there is any overlapping file
415 			if (any_override)
416 				target.buildSettings.prependStringImportPaths(parent.buildSettings.stringImportPaths);
417 
418 			// add all files to overrides for recursion
419 			overrides ~= target.buildSettings.stringImportFiles;
420 
421 			// recursively override all dependencies with the accumulated files/paths
422 			foreach (depname; target.dependencies)
423 				overrideStringImports(targets[depname], target, targets, overrides);
424 		}
425 
426 		// push string import paths/files down to all direct and indirect
427 		// dependencies, overriding their own
428 		foreach (depname; roottarget.dependencies)
429 			overrideStringImports(targets[depname], *roottarget, targets,
430 				roottarget.buildSettings.stringImportFiles);
431 
432 		// remove targets without output
433 		foreach (name; targets.keys)
434 		{
435 			if (!hasOutput[name])
436 				targets.remove(name);
437 		}
438 	}
439 
440 	// infer applicable version identifiers
441 	private static void inferVersionFilters(ref TargetInfo ti)
442 	{
443 		import std.algorithm.searching : any;
444 		import std.file : timeLastModified;
445 		import std.path : extension;
446 		import std.range : chain;
447 		import std.regex : ctRegex, matchAll;
448 		import std.stdio : File;
449 		import std.datetime : Clock, SysTime, UTC;
450 		import dub.compilers.utils : isLinkerFile;
451 		import dub.internal.vibecompat.data.json : Json, JSONException;
452 
453 		auto bs = &ti.buildSettings;
454 
455 		// only infer if neither version filters are specified explicitly
456 		if (bs.versionFilters.length || bs.debugVersionFilters.length)
457 		{
458 			logDebug("Using specified versionFilters for %s: %s %s", ti.pack.name,
459 				bs.versionFilters, bs.debugVersionFilters);
460 			return;
461 		}
462 
463 		// check all existing source files for version identifiers
464 		static immutable dexts = [".d", ".di"];
465 		auto srcs = chain(bs.sourceFiles, bs.importFiles, bs.stringImportFiles)
466 			.filter!(f => dexts.canFind(f.extension)).filter!exists;
467 		// try to load cached filters first
468 		auto cache = ti.pack.metadataCache;
469 		try
470 		{
471 			auto cachedFilters = cache["versionFilters"];
472 			if (cachedFilters.type != Json.Type.undefined)
473 				cachedFilters = cachedFilters[ti.config];
474 			if (cachedFilters.type != Json.Type.undefined)
475 			{
476 				immutable mtime = SysTime.fromISOExtString(cachedFilters["mtime"].get!string);
477 				if (!srcs.any!(src => src.timeLastModified > mtime))
478 				{
479 					auto versionFilters = cachedFilters["versions"][].map!(j => j.get!string).array;
480 					auto debugVersionFilters = cachedFilters["debugVersions"][].map!(j => j.get!string).array;
481 					logDebug("Using cached versionFilters for %s: %s %s", ti.pack.name,
482 						versionFilters, debugVersionFilters);
483 					bs.addVersionFilters(versionFilters);
484 					bs.addDebugVersionFilters(debugVersionFilters);
485 					return;
486 				}
487 			}
488 		}
489 		catch (JSONException e)
490 		{
491 			logWarn("Exception during loading invalid package cache %s.\n%s",
492 				ti.pack.path ~ ".dub/metadata_cache.json", e);
493 		}
494 
495 		// use ctRegex for performance reasons, only small compile time increase
496 		enum verRE = ctRegex!`(?:^|\s)version\s*\(\s*([^\s]*?)\s*\)`;
497 		enum debVerRE = ctRegex!`(?:^|\s)debug\s*\(\s*([^\s]*?)\s*\)`;
498 
499 		auto versionFilters = appender!(string[]);
500 		auto debugVersionFilters = appender!(string[]);
501 
502 		foreach (file; srcs)
503 		{
504 			foreach (line; File(file).byLine)
505 			{
506 				foreach (m; line.matchAll(verRE))
507 					if (!versionFilters.data.canFind(m[1]))
508 						versionFilters.put(m[1].idup);
509 				foreach (m; line.matchAll(debVerRE))
510 					if (!debugVersionFilters.data.canFind(m[1]))
511 						debugVersionFilters.put(m[1].idup);
512 			}
513 		}
514 		logDebug("Using inferred versionFilters for %s: %s %s", ti.pack.name,
515 			versionFilters.data, debugVersionFilters.data);
516 		bs.addVersionFilters(versionFilters.data);
517 		bs.addDebugVersionFilters(debugVersionFilters.data);
518 
519 		auto cachedFilters = cache["versionFilters"];
520 		if (cachedFilters.type == Json.Type.undefined)
521 			cachedFilters = cache["versionFilters"] = [ti.config: Json.emptyObject];
522 		cachedFilters[ti.config] = [
523 			"mtime": Json(Clock.currTime(UTC()).toISOExtString),
524 			"versions": Json(versionFilters.data.map!Json.array),
525 			"debugVersions": Json(debugVersionFilters.data.map!Json.array),
526 		];
527 		ti.pack.metadataCache = cache;
528 	}
529 
530 	private static void mergeFromDependent(in ref BuildSettings parent, ref BuildSettings child)
531 	{
532 		child.addVersions(parent.versions);
533 		child.addDebugVersions(parent.debugVersions);
534 		child.addOptions(BuildOptions(parent.options & inheritedBuildOptions));
535 	}
536 
537 	private static void mergeFromDependency(in ref BuildSettings child, ref BuildSettings parent, in ref BuildPlatform platform)
538 	{
539 		import dub.compilers.utils : isLinkerFile;
540 
541 		parent.addDFlags(child.dflags);
542 		parent.addVersions(child.versions);
543 		parent.addDebugVersions(child.debugVersions);
544 		parent.addVersionFilters(child.versionFilters);
545 		parent.addDebugVersionFilters(child.debugVersionFilters);
546 		parent.addImportPaths(child.importPaths);
547 		parent.addStringImportPaths(child.stringImportPaths);
548 		// linking of static libraries is done by parent
549 		if (child.targetType == TargetType.staticLibrary) {
550 			parent.addSourceFiles(child.sourceFiles.filter!(f => isLinkerFile(platform, f)).array);
551 			parent.addLibs(child.libs);
552 			parent.addLFlags(child.lflags);
553 		}
554 	}
555 
556 	// configure targets for build types such as release, or unittest-cov
557 	private void addBuildTypeSettings(TargetInfo[string] targets, in GeneratorSettings settings)
558 	{
559 		foreach (ref ti; targets.byValue) {
560 			ti.buildSettings.add(settings.buildSettings);
561 
562 			// add build type settings and convert plain DFLAGS to build options
563 			m_project.addBuildTypeSettings(ti.buildSettings, settings, ti.pack is m_project.rootPackage);
564 			settings.compiler.extractBuildOptions(ti.buildSettings);
565 
566 			auto tt = ti.buildSettings.targetType;
567 			enforce (tt != TargetType.sourceLibrary || ti.pack !is m_project.rootPackage || (ti.buildSettings.options & BuildOption.syntaxOnly),
568 				format("Main package must not have target type \"%s\". Cannot build.", tt));
569 		}
570 	}
571 }
572 
573 
574 struct GeneratorSettings {
575 	BuildPlatform platform;
576 	Compiler compiler;
577 	string config;
578 	string buildType;
579 	BuildSettings buildSettings;
580 	BuildMode buildMode = BuildMode.separate;
581 	int targetExitStatus;
582 
583 	bool combined; // compile all in one go instead of each dependency separately
584 	bool filterVersions;
585 
586 	// only used for generator "build"
587 	bool run, force, direct, rdmd, tempBuild, parallelBuild;
588 
589 	/// single file dub package
590 	bool single;
591 
592 	string[] runArgs;
593 	void delegate(int status, string output) compileCallback;
594 	void delegate(int status, string output) linkCallback;
595 	void delegate(int status, string output) runCallback;
596 }
597 
598 
599 /**
600 	Determines the mode in which the compiler and linker are invoked.
601 */
602 enum BuildMode {
603 	separate,                 /// Compile and link separately
604 	allAtOnce,                /// Perform compile and link with a single compiler invocation
605 	singleFile,               /// Compile each file separately
606 	//multipleObjects,          /// Generate an object file per module
607 	//multipleObjectsPerModule, /// Use the -multiobj switch to generate multiple object files per module
608 	//compileOnly               /// Do not invoke the linker (can be done using a post build command)
609 }
610 
611 
612 /**
613 	Creates a project generator of the given type for the specified project.
614 */
615 ProjectGenerator createProjectGenerator(string generator_type, Project project)
616 {
617 	assert(project !is null, "Project instance needed to create a generator.");
618 
619 	generator_type = generator_type.toLower();
620 	switch(generator_type) {
621 		default:
622 			throw new Exception("Unknown project generator: "~generator_type);
623 		case "build":
624 			logDebug("Creating build generator.");
625 			return new BuildGenerator(project);
626 		case "mono-d":
627 			throw new Exception("The Mono-D generator has been removed. Use Mono-D's built in DUB support instead.");
628 		case "visuald":
629 			logDebug("Creating VisualD generator.");
630 			return new VisualDGenerator(project);
631 		case "sublimetext":
632 			logDebug("Creating SublimeText generator.");
633 			return new SublimeTextGenerator(project);
634 		case "cmake":
635 			logDebug("Creating CMake generator.");
636 			return new CMakeGenerator(project);
637 	}
638 }
639 
640 
641 /**
642 	Runs pre-build commands and performs other required setup before project files are generated.
643 */
644 private void prepareGeneration(in Package pack, in Project proj, in GeneratorSettings settings,
645 	in BuildSettings buildsettings)
646 {
647 	if (buildsettings.preGenerateCommands.length && !isRecursiveInvocation(pack.name)) {
648 		logInfo("Running pre-generate commands for %s...", pack.name);
649 		runBuildCommands(buildsettings.preGenerateCommands, pack, proj, settings, buildsettings);
650 	}
651 }
652 
653 /**
654 	Runs post-build commands and copies required files to the binary directory.
655 */
656 private void finalizeGeneration(in Package pack, in Project proj, in GeneratorSettings settings,
657 	in BuildSettings buildsettings, NativePath target_path, bool generate_binary)
658 {
659 	import std.path : globMatch;
660 
661 	if (buildsettings.postGenerateCommands.length && !isRecursiveInvocation(pack.name)) {
662 		logInfo("Running post-generate commands for %s...", pack.name);
663 		runBuildCommands(buildsettings.postGenerateCommands, pack, proj, settings, buildsettings);
664 	}
665 
666 	if (generate_binary) {
667 		if (!exists(buildsettings.targetPath))
668 			mkdirRecurse(buildsettings.targetPath);
669 
670 		if (buildsettings.copyFiles.length) {
671 			void copyFolderRec(NativePath folder, NativePath dstfolder)
672 			{
673 				mkdirRecurse(dstfolder.toNativeString());
674 				foreach (de; iterateDirectory(folder.toNativeString())) {
675 					if (de.isDirectory) {
676 						copyFolderRec(folder ~ de.name, dstfolder ~ de.name);
677 					} else {
678 						try hardLinkFile(folder ~ de.name, dstfolder ~ de.name, true);
679 						catch (Exception e) {
680 							logWarn("Failed to copy file %s: %s", (folder ~ de.name).toNativeString(), e.msg);
681 						}
682 					}
683 				}
684 			}
685 
686 			void tryCopyDir(string file)
687 			{
688 				auto src = NativePath(file);
689 				if (!src.absolute) src = pack.path ~ src;
690 				auto dst = target_path ~ NativePath(file).head;
691 				if (src == dst) {
692 					logDiagnostic("Skipping copy of %s (same source and destination)", file);
693 					return;
694 				}
695 				logDiagnostic("  %s to %s", src.toNativeString(), dst.toNativeString());
696 				try {
697 					copyFolderRec(src, dst);
698 				} catch(Exception e) logWarn("Failed to copy %s to %s: %s", src.toNativeString(), dst.toNativeString(), e.msg);
699 			}
700 
701 			void tryCopyFile(string file)
702 			{
703 				auto src = NativePath(file);
704 				if (!src.absolute) src = pack.path ~ src;
705 				auto dst = target_path ~ NativePath(file).head;
706 				if (src == dst) {
707 					logDiagnostic("Skipping copy of %s (same source and destination)", file);
708 					return;
709 				}
710 				logDiagnostic("  %s to %s", src.toNativeString(), dst.toNativeString());
711 				try {
712 					hardLinkFile(src, dst, true);
713 				} catch(Exception e) logWarn("Failed to copy %s to %s: %s", src.toNativeString(), dst.toNativeString(), e.msg);
714 			}
715 			logInfo("Copying files for %s...", pack.name);
716 			string[] globs;
717 			foreach (f; buildsettings.copyFiles)
718 			{
719 				if (f.canFind("*", "?") ||
720 					(f.canFind("{") && f.balancedParens('{', '}')) ||
721 					(f.canFind("[") && f.balancedParens('[', ']')))
722 				{
723 					globs ~= f;
724 				}
725 				else
726 				{
727 					if (f.isDir)
728 						tryCopyDir(f);
729 					else
730 						tryCopyFile(f);
731 				}
732 			}
733 			if (globs.length) // Search all files for glob matches
734 			{
735 				foreach (f; dirEntries(pack.path.toNativeString(), SpanMode.breadth))
736 				{
737 					foreach (glob; globs)
738 					{
739 						if (f.name().globMatch(glob))
740 						{
741 							if (f.isDir)
742 								tryCopyDir(f);
743 							else
744 								tryCopyFile(f);
745 							break;
746 						}
747 					}
748 				}
749 			}
750 		}
751 
752 	}
753 }
754 
755 
756 /** Runs a list of build commands for a particular package.
757 
758 	This function sets all DUB speficic environment variables and makes sure
759 	that recursive dub invocations are detected and don't result in infinite
760 	command execution loops. The latter could otherwise happen when a command
761 	runs "dub describe" or similar functionality.
762 */
763 void runBuildCommands(in string[] commands, in Package pack, in Project proj,
764 	in GeneratorSettings settings, in BuildSettings build_settings)
765 {
766 	import dub.internal.utils : getDUBExePath, runCommands;
767 	import std.conv : to, text;
768 	import std.process : environment, escapeShellFileName;
769 
770 	string[string] env = environment.toAA();
771 	// TODO: do more elaborate things here
772 	// TODO: escape/quote individual items appropriately
773 	env["DFLAGS"]                = join(cast(string[])build_settings.dflags, " ");
774 	env["LFLAGS"]                = join(cast(string[])build_settings.lflags," ");
775 	env["VERSIONS"]              = join(cast(string[])build_settings.versions," ");
776 	env["LIBS"]                  = join(cast(string[])build_settings.libs," ");
777 	env["SOURCE_FILES"]          = join(cast(string[])build_settings.sourceFiles," ");
778 	env["IMPORT_PATHS"]          = join(cast(string[])build_settings.importPaths," ");
779 	env["STRING_IMPORT_PATHS"]   = join(cast(string[])build_settings.stringImportPaths," ");
780 
781 	env["DC"]                    = settings.platform.compilerBinary;
782 	env["DC_BASE"]               = settings.platform.compiler;
783 	env["D_FRONTEND_VER"]        = to!string(settings.platform.frontendVersion);
784 
785 	env["DUB_EXE"]               = getDUBExePath(settings.platform.compilerBinary);
786 	env["DUB_PLATFORM"]          = join(cast(string[])settings.platform.platform," ");
787 	env["DUB_ARCH"]              = join(cast(string[])settings.platform.architecture," ");
788 
789 	env["DUB_TARGET_TYPE"]       = to!string(build_settings.targetType);
790 	env["DUB_TARGET_PATH"]       = build_settings.targetPath;
791 	env["DUB_TARGET_NAME"]       = build_settings.targetName;
792 	env["DUB_TARGET_EXIT_STATUS"] = settings.targetExitStatus.text;
793 	env["DUB_WORKING_DIRECTORY"] = build_settings.workingDirectory;
794 	env["DUB_MAIN_SOURCE_FILE"]  = build_settings.mainSourceFile;
795 
796 	env["DUB_CONFIG"]            = settings.config;
797 	env["DUB_BUILD_TYPE"]        = settings.buildType;
798 	env["DUB_BUILD_MODE"]        = to!string(settings.buildMode);
799 	env["DUB_PACKAGE"]           = pack.name;
800 	env["DUB_PACKAGE_DIR"]       = pack.path.toNativeString();
801 	env["DUB_ROOT_PACKAGE"]      = proj.rootPackage.name;
802 	env["DUB_ROOT_PACKAGE_DIR"]  = proj.rootPackage.path.toNativeString();
803 	env["DUB_PACKAGE_VERSION"]   = pack.version_.toString();
804 
805 	env["DUB_COMBINED"]          = settings.combined?      "TRUE" : "";
806 	env["DUB_RUN"]               = settings.run?           "TRUE" : "";
807 	env["DUB_FORCE"]             = settings.force?         "TRUE" : "";
808 	env["DUB_DIRECT"]            = settings.direct?        "TRUE" : "";
809 	env["DUB_RDMD"]              = settings.rdmd?          "TRUE" : "";
810 	env["DUB_TEMP_BUILD"]        = settings.tempBuild?     "TRUE" : "";
811 	env["DUB_PARALLEL_BUILD"]    = settings.parallelBuild? "TRUE" : "";
812 
813 	env["DUB_RUN_ARGS"] = (cast(string[])settings.runArgs).map!(escapeShellFileName).join(" ");
814 
815 	auto depNames = proj.dependencies.map!((a) => a.name).array();
816 	storeRecursiveInvokations(env, proj.rootPackage.name ~ depNames);
817 	runCommands(commands, env, pack.path().toString());
818 }
819 
820 private bool isRecursiveInvocation(string pack)
821 {
822 	import std.algorithm : canFind, splitter;
823 	import std.process : environment;
824 
825 	return environment
826         .get("DUB_PACKAGES_USED", "")
827         .splitter(",")
828         .canFind(pack);
829 }
830 
831 private void storeRecursiveInvokations(string[string] env, string[] packs)
832 {
833 	import std.algorithm : canFind, splitter;
834 	import std.range : chain;
835 	import std.process : environment;
836 
837     env["DUB_PACKAGES_USED"] = environment
838         .get("DUB_PACKAGES_USED", "")
839         .splitter(",")
840         .chain(packs)
841         .join(",");
842 }