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.utils; 12 import dub.internal.vibecompat.core.log; 13 import dub.internal.vibecompat.inet.path; 14 import dub.platform; 15 16 import std.algorithm; 17 import std.array; 18 import std.conv; 19 import std.exception; 20 import std.file; 21 import std.process; 22 import std.random; 23 import std.typecons; 24 25 26 class DmdCompiler : Compiler { 27 private static immutable s_options = [ 28 tuple(BuildOptions.debugMode, ["-debug"]), 29 tuple(BuildOptions.releaseMode, ["-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.noBoundsCheck, ["-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 version(Windows) settings.addSourceFiles(settings.libs.map!(l => l~".lib")().array()); 90 else settings.addLFlags(settings.libs.map!(l => "-l"~l)().array()); 91 } 92 93 if (!(fields & BuildSetting.versions)) { 94 settings.addDFlags(settings.versions.map!(s => "-version="~s)().array()); 95 settings.versions = null; 96 } 97 98 if (!(fields & BuildSetting.debugVersions)) { 99 settings.addDFlags(settings.debugVersions.map!(s => "-debug="~s)().array()); 100 settings.debugVersions = null; 101 } 102 103 if (!(fields & BuildSetting.importPaths)) { 104 settings.addDFlags(settings.importPaths.map!(s => "-I"~s)().array()); 105 settings.importPaths = null; 106 } 107 108 if (!(fields & BuildSetting.stringImportPaths)) { 109 settings.addDFlags(settings.stringImportPaths.map!(s => "-J"~s)().array()); 110 settings.stringImportPaths = null; 111 } 112 113 if (!(fields & BuildSetting.sourceFiles)) { 114 settings.addDFlags(settings.sourceFiles); 115 settings.sourceFiles = null; 116 } 117 118 if (!(fields & BuildSetting.lflags)) { 119 settings.addDFlags(settings.lflags.map!(f => "-L"~f)().array()); 120 settings.lflags = null; 121 } 122 123 assert(fields & BuildSetting.dflags); 124 assert(fields & BuildSetting.copyFiles); 125 } 126 127 void extractBuildOptions(ref BuildSettings settings) 128 { 129 Appender!(string[]) newflags; 130 next_flag: foreach (f; settings.dflags) { 131 foreach (t; s_options) 132 if (t[1].canFind(f)) { 133 settings.options |= t[0]; 134 continue next_flag; 135 } 136 if (f.startsWith("-version=")) settings.addVersions(f[9 .. $]); 137 else if (f.startsWith("-debug=")) settings.addDebugVersions(f[7 .. $]); 138 else newflags ~= f; 139 } 140 settings.dflags = newflags.data; 141 } 142 143 void setTarget(ref BuildSettings settings, in BuildPlatform platform) 144 { 145 final switch (settings.targetType) { 146 case TargetType.autodetect: assert(false, "Invalid target type: autodetect"); 147 case TargetType.none: assert(false, "Invalid target type: none"); 148 case TargetType.sourceLibrary: assert(false, "Invalid target type: sourceLibrary"); 149 case TargetType.executable: break; 150 case TargetType.library: 151 case TargetType.staticLibrary: 152 settings.addDFlags("-lib"); 153 break; 154 case TargetType.dynamicLibrary: 155 version (Windows) settings.addDFlags("-shared"); 156 else settings.addDFlags("-shared", "-fPIC"); 157 break; 158 } 159 160 auto tpath = Path(settings.targetPath) ~ getTargetFileName(settings, platform); 161 settings.addDFlags("-of"~tpath.toNativeString()); 162 } 163 164 void invoke(in BuildSettings settings, in BuildPlatform platform) 165 { 166 auto res_file = getTempDir() ~ ("dub-build-"~uniform(0, uint.max).to!string~"-.rsp"); 167 std.file.write(res_file.toNativeString(), join(settings.dflags.map!(s => s.canFind(' ') ? "\""~s~"\"" : s), "\n")); 168 scope (exit) remove(res_file.toNativeString()); 169 170 logDiagnostic("%s %s", platform.compilerBinary, join(cast(string[])settings.dflags, " ")); 171 auto compiler_pid = spawnProcess([platform.compilerBinary, "@"~res_file.toNativeString()]); 172 auto result = compiler_pid.wait(); 173 enforce(result == 0, "DMD compile run failed with exit code "~to!string(result)); 174 } 175 176 void invokeLinker(in BuildSettings settings, in BuildPlatform platform, string[] objects) 177 { 178 import std.string; 179 auto tpath = Path(settings.targetPath) ~ getTargetFileName(settings, platform); 180 auto args = [platform.compiler, "-of"~tpath.toNativeString()]; 181 args ~= objects; 182 args ~= settings.sourceFiles; 183 version(linux) args ~= "-L--no-as-needed"; // avoids linker errors due to libraries being speficied in the wrong order by DMD 184 args ~= settings.lflags.map!(l => "-L"~l)().array; 185 args ~= settings.dflags.filter!(f => isLinkerDFlag(f)).array; 186 logDiagnostic("%s", args.join(" ")); 187 auto res = spawnProcess(args).wait(); 188 enforce(res == 0, "Link command failed with exit code "~to!string(res)); 189 } 190 191 private static bool isLinkerDFlag(string arg) 192 { 193 switch (arg) { 194 default: 195 if (arg.startsWith("-defaultlib=")) return true; 196 return false; 197 case "-g", "-gc", "-m32", "-m64", "-shared": 198 return true; 199 } 200 } 201 }