1 /** 2 DMD compiler support. 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.compilers.dmd; 9 10 import dub.compilers.compiler; 11 import dub.internal.std.process; 12 import dub.internal.utils; 13 import dub.internal.vibecompat.core.log; 14 import dub.internal.vibecompat.inet.path; 15 import dub.platform; 16 17 import std.algorithm; 18 import std.array; 19 import std.conv; 20 import std.exception; 21 import std.file; 22 import std.random; 23 import std.typecons; 24 25 26 class DmdCompiler : Compiler { 27 private static immutable s_options = [ 28 tuple(BuildOptions.debug_, ["-debug"]), 29 tuple(BuildOptions.release, ["-release"]), 30 tuple(BuildOptions.coverage, ["-cov"]), 31 tuple(BuildOptions.debugInfo, ["-g"]), 32 tuple(BuildOptions.debugInfoC, ["-gc"]), 33 tuple(BuildOptions.alwaysStackFrame, ["-gs"]), 34 tuple(BuildOptions.stackStomping, ["-gx"]), 35 tuple(BuildOptions.inline, ["-inline"]), 36 tuple(BuildOptions.noBoundsChecks, ["-noboundscheck"]), 37 tuple(BuildOptions.optimize, ["-O"]), 38 tuple(BuildOptions.profile, ["-profile"]), 39 tuple(BuildOptions.unittests, ["-unittest"]), 40 tuple(BuildOptions.verbose, ["-v"]), 41 tuple(BuildOptions.ignoreUnknownPragmas, ["-ignore"]), 42 tuple(BuildOptions.syntaxOnly, ["-o-"]), 43 tuple(BuildOptions.warnings, ["-wi"]), 44 tuple(BuildOptions.warningsAsErrors, ["-w"]), 45 tuple(BuildOptions.ignoreDeprecations, ["-d"]), 46 tuple(BuildOptions.deprecationWarnings, ["-dw"]), 47 tuple(BuildOptions.deprecationErrors, ["-de"]), 48 tuple(BuildOptions.property, ["-property"]), 49 ]; 50 51 @property string name() const { return "dmd"; } 52 53 BuildPlatform determinePlatform(ref BuildSettings settings, string compiler_binary, string arch_override) 54 { 55 // TODO: determine platform by invoking the compiler instead 56 BuildPlatform build_platform; 57 build_platform.platform = .determinePlatform(); 58 build_platform.architecture = .determineArchitecture(); 59 build_platform.compiler = this.name; 60 build_platform.compilerBinary = compiler_binary; 61 62 switch (arch_override) { 63 default: throw new Exception("Unsupported architecture: "~arch_override); 64 case "": break; 65 case "x86": 66 build_platform.architecture = ["x86"]; 67 settings.addDFlags("-m32"); 68 break; 69 case "x86_64": 70 build_platform.architecture = ["x86_64"]; 71 settings.addDFlags("-m64"); 72 break; 73 } 74 return build_platform; 75 } 76 77 void prepareBuildSettings(ref BuildSettings settings, BuildSetting fields = BuildSetting.all) 78 { 79 enforceBuildRequirements(settings); 80 81 if (!(fields & BuildSetting.options)) { 82 foreach (t; s_options) 83 if (settings.options & t[0]) 84 settings.addDFlags(t[1]); 85 } 86 87 if (!(fields & BuildSetting.libs)) 88 resolveLibs(settings); 89 90 if (!(fields & BuildSetting.versions)) { 91 settings.addDFlags(settings.versions.map!(s => "-version="~s)().array()); 92 settings.versions = null; 93 } 94 95 if (!(fields & BuildSetting.debugVersions)) { 96 settings.addDFlags(settings.debugVersions.map!(s => "-debug="~s)().array()); 97 settings.debugVersions = null; 98 } 99 100 if (!(fields & BuildSetting.importPaths)) { 101 settings.addDFlags(settings.importPaths.map!(s => "-I"~s)().array()); 102 settings.importPaths = null; 103 } 104 105 if (!(fields & BuildSetting.stringImportPaths)) { 106 settings.addDFlags(settings.stringImportPaths.map!(s => "-J"~s)().array()); 107 settings.stringImportPaths = null; 108 } 109 110 if (!(fields & BuildSetting.sourceFiles)) { 111 settings.addDFlags(settings.sourceFiles); 112 settings.sourceFiles = null; 113 } 114 115 if (!(fields & BuildSetting.lflags)) { 116 settings.addDFlags(settings.lflags.map!(f => "-L"~f)().array()); 117 settings.lflags = null; 118 } 119 120 assert(fields & BuildSetting.dflags); 121 assert(fields & BuildSetting.copyFiles); 122 } 123 124 void extractBuildOptions(ref BuildSettings settings) 125 { 126 Appender!(string[]) newflags; 127 next_flag: foreach (f; settings.dflags) { 128 foreach (t; s_options) 129 if (t[1].canFind(f)) { 130 settings.options |= t[0]; 131 continue next_flag; 132 } 133 if (f.startsWith("-version=")) settings.addVersions(f[9 .. $]); 134 else if (f.startsWith("-debug=")) settings.addDebugVersions(f[7 .. $]); 135 else newflags ~= f; 136 } 137 settings.dflags = newflags.data; 138 } 139 140 void setTarget(ref BuildSettings settings, in BuildPlatform platform) 141 { 142 final switch (settings.targetType) { 143 case TargetType.autodetect: assert(false, "Invalid target type: autodetect"); 144 case TargetType.none: assert(false, "Invalid target type: none"); 145 case TargetType.sourceLibrary: assert(false, "Invalid target type: sourceLibrary"); 146 case TargetType.executable: break; 147 case TargetType.library: 148 case TargetType.staticLibrary: 149 settings.addDFlags("-lib"); 150 break; 151 case TargetType.dynamicLibrary: 152 settings.addDFlags("-shared"); 153 break; 154 } 155 156 auto tpath = Path(settings.targetPath) ~ getTargetFileName(settings, platform); 157 settings.addDFlags("-of"~tpath.toNativeString()); 158 } 159 160 void invoke(in BuildSettings settings, in BuildPlatform platform) 161 { 162 auto res_file = getTempDir() ~ ("dub-build-"~uniform(0, uint.max).to!string~"-.rsp"); 163 std.file.write(res_file.toNativeString(), join(settings.dflags.map!(s => s.canFind(' ') ? "\""~s~"\"" : s), "\n")); 164 scope (exit) remove(res_file.toNativeString()); 165 166 logDiagnostic("%s %s", platform.compilerBinary, join(cast(string[])settings.dflags, " ")); 167 auto compiler_pid = spawnProcess([platform.compilerBinary, "@"~res_file.toNativeString()]); 168 auto result = compiler_pid.wait(); 169 enforce(result == 0, "DMD compile run failed with exit code "~to!string(result)); 170 } 171 172 void invokeLinker(in BuildSettings settings, in BuildPlatform platform, string[] objects) 173 { 174 import std.string; 175 auto tpath = Path(settings.targetPath) ~ getTargetFileName(settings, platform); 176 auto args = [platform.compiler, "-of"~tpath.toNativeString()] ~ objects ~ settings.lflags.map!(l => "-L"~l)().array() ~ settings.sourceFiles; 177 static linkerargs = ["-g", "-gc", "-m32", "-m64"]; 178 args ~= settings.dflags.filter!(f => linkerargs.canFind(f))().array(); 179 logDiagnostic("%s", args.join(" ")); 180 auto res = spawnProcess(args).wait(); 181 enforce(res == 0, "Link command failed with exit code "~to!string(res)); 182 } 183 }