1 /**
2 	Generator for direct RDMD builds.
3 	
4 	Copyright: © 2013-2013 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.generators.rdmd;
9 
10 import dub.compilers.compiler;
11 import dub.generators.generator;
12 import dub.internal.std.process;
13 import dub.internal.utils;
14 import dub.internal.vibecompat.core.file;
15 import dub.internal.vibecompat.core.log;
16 import dub.internal.vibecompat.inet.path;
17 import dub.package_;
18 import dub.packagemanager;
19 import dub.project;
20 
21 import std.algorithm;
22 import std.array;
23 import std.conv;
24 import std.exception;
25 import std.file;
26 import std.string;
27 
28 
29 class RdmdGenerator : ProjectGenerator {
30 	private {
31 		Project m_project;
32 		PackageManager m_pkgMgr;
33 	}
34 	
35 	this(Project app, PackageManager mgr)
36 	{
37 		m_project = app;
38 		m_pkgMgr = mgr;
39 	}
40 	
41 	void generateProject(GeneratorSettings settings)
42 	{
43 		//Added check for existance of [AppNameInPackagejson].d
44 		//If exists, use that as the starting file.
45 		auto mainsrc = getMainSourceFile(m_project);
46 
47 		auto buildsettings = settings.buildSettings;
48 		m_project.addBuildSettings(buildsettings, settings.platform, settings.config);
49 		m_project.addBuildTypeSettings(buildsettings, settings.platform, settings.buildType);
50 		// do not pass all source files to RDMD, only the main source file
51 		buildsettings.sourceFiles = buildsettings.sourceFiles.filter!(s => !s.endsWith(".d"))().array();
52 		settings.compiler.prepareBuildSettings(buildsettings, BuildSetting.commandLine);
53 
54 		auto generate_binary = !buildsettings.dflags.canFind("-o-");
55 
56 		// Create start script, which will be used by the calling bash/cmd script.
57 		// build "rdmd --force %DFLAGS% -I%~dp0..\source -Jviews -Isource @deps.txt %LIBS% source\app.d" ~ application arguments
58 		// or with "/" instead of "\"
59 		Path run_exe_file;
60 		if( generate_binary ){
61 			if( settings.run ){
62 				import std.random;
63 				auto rnd = to!string(uniform(uint.min, uint.max)) ~ "-";
64 				buildsettings.targetPath = (getTempDir()~".rdmd/source/").toNativeString();
65 				buildsettings.targetName = rnd ~ buildsettings.targetName;
66 				run_exe_file = Path(buildsettings.targetPath) ~ getTargetFileName(buildsettings, settings.platform);
67 			}
68 			settings.compiler.setTarget(buildsettings, settings.platform);
69 		}
70 
71 		logDiagnostic("Application output name is '%s'", getTargetFileName(buildsettings, settings.platform));
72 
73 		string[] flags = ["--build-only", "--compiler="~settings.platform.compilerBinary];
74 		flags ~= buildsettings.dflags;
75 		flags ~= (mainsrc).toNativeString();
76 
77 		prepareGeneration(buildsettings);
78 		finalizeGeneration(buildsettings, generate_binary);
79 
80 		if( buildsettings.preBuildCommands.length ){
81 			logInfo("Running pre-build commands...");
82 			runCommands(buildsettings.preBuildCommands);
83 		}
84 
85 		if( settings.config.length ) logInfo("Building configuration "~settings.config~", build type "~settings.buildType);
86 		else logInfo("Building default configuration, build type "~settings.buildType);
87 
88 		logInfo("Running rdmd...");
89 		logDiagnostic("rdmd %s", join(flags, " "));
90 		auto rdmd_pid = spawnProcess("rdmd" ~ flags);
91 		auto result = rdmd_pid.wait();
92 		enforce(result == 0, "Build command failed with exit code "~to!string(result));
93 
94 		if( buildsettings.postBuildCommands.length ){
95 			logInfo("Running post-build commands...");
96 			runCommands(buildsettings.postBuildCommands);
97 		}
98 
99 		if (generate_binary && settings.run) {
100 			if (buildsettings.targetType == TargetType.executable) {
101 				auto cwd = Path(getcwd());
102 				if (buildsettings.workingDirectory.length) {
103 					logDiagnostic("Switching to %s", (cwd ~ buildsettings.workingDirectory).toNativeString());
104 					chdir((cwd ~ buildsettings.workingDirectory).toNativeString());
105 				}
106 				scope(exit) chdir(cwd.toNativeString());
107 				logInfo("Running %s...", run_exe_file.toNativeString());
108 				auto prg_pid = spawnProcess(run_exe_file.toNativeString() ~ settings.runArgs);
109 				result = prg_pid.wait();
110 				remove(run_exe_file.toNativeString());
111 				foreach( f; buildsettings.copyFiles )
112 					remove((run_exe_file.parentPath ~ Path(f).head).toNativeString());
113 				enforce(result == 0, "Program exited with code "~to!string(result));
114 			} else logInfo("Target is a library. Skipping execution.");
115 		}
116 	}
117 }
118 
119 private Path getMainSourceFile(in Project prj)
120 {
121 	foreach( f; ["source/app.d", "src/app.d", "source/"~prj.name~".d", "src/"~prj.name~".d"])
122 		if( exists(f) )
123 			return Path(f);
124 	return Path("source/app.d");
125 }