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 import std.process; 56 import std.string; 57 58 auto fil = generatePlatformProbeFile(); 59 60 string[] arch_flags; 61 62 switch (arch_override) { 63 default: throw new Exception("Unsupported architecture: "~arch_override); 64 case "": break; 65 case "x86": arch_flags = ["-m32"]; break; 66 case "x86_64": arch_flags = ["-m64"]; break; 67 } 68 settings.addDFlags(arch_flags); 69 70 auto result = executeShell(escapeShellCommand(compiler_binary ~ arch_flags ~ 71 ["-quiet", "-c", "-o-", fil.toNativeString()])); 72 enforce(result.status == 0, format("Failed to invoke the compiler %s to determine the build platform: %s", 73 compiler_binary, result.output)); 74 75 auto build_platform = readPlatformProbe(result.output); 76 build_platform.compilerBinary = compiler_binary; 77 78 if (build_platform.compiler != this.name) { 79 logWarn(`The determined compiler type "%s" doesn't match the expected type "%s". This will probably result in build errors.`, 80 build_platform.compiler, this.name); 81 } 82 83 if (arch_override.length && !build_platform.architecture.canFind(arch_override)) { 84 logWarn(`Failed to apply the selected architecture %s. Got %s.`, 85 arch_override, build_platform.architecture); 86 } 87 88 return build_platform; 89 } 90 91 void prepareBuildSettings(ref BuildSettings settings, BuildSetting fields = BuildSetting.all) const 92 { 93 enforceBuildRequirements(settings); 94 95 if (!(fields & BuildSetting.options)) { 96 foreach (t; s_options) 97 if (settings.options & t[0]) 98 settings.addDFlags(t[1]); 99 } 100 101 if (!(fields & BuildSetting.versions)) { 102 settings.addDFlags(settings.versions.map!(s => "-version="~s)().array()); 103 settings.versions = null; 104 } 105 106 if (!(fields & BuildSetting.debugVersions)) { 107 settings.addDFlags(settings.debugVersions.map!(s => "-debug="~s)().array()); 108 settings.debugVersions = null; 109 } 110 111 if (!(fields & BuildSetting.importPaths)) { 112 settings.addDFlags(settings.importPaths.map!(s => "-I"~s)().array()); 113 settings.importPaths = null; 114 } 115 116 if (!(fields & BuildSetting.stringImportPaths)) { 117 settings.addDFlags(settings.stringImportPaths.map!(s => "-J"~s)().array()); 118 settings.stringImportPaths = null; 119 } 120 121 if (!(fields & BuildSetting.sourceFiles)) { 122 settings.addDFlags(settings.sourceFiles); 123 settings.sourceFiles = null; 124 } 125 126 if (!(fields & BuildSetting.libs)) { 127 resolveLibs(settings); 128 version(Windows) settings.addSourceFiles(settings.libs.map!(l => l~".lib")().array()); 129 else settings.addLFlags(settings.libs.map!(l => "-l"~l)().array()); 130 } 131 132 if (!(fields & BuildSetting.lflags)) { 133 settings.addDFlags(settings.lflags.map!(f => "-L"~f)().array()); 134 settings.lflags = null; 135 } 136 137 assert(fields & BuildSetting.dflags); 138 assert(fields & BuildSetting.copyFiles); 139 } 140 141 void extractBuildOptions(ref BuildSettings settings) const 142 { 143 Appender!(string[]) newflags; 144 next_flag: foreach (f; settings.dflags) { 145 foreach (t; s_options) 146 if (t[1].canFind(f)) { 147 settings.options |= t[0]; 148 continue next_flag; 149 } 150 if (f.startsWith("-version=")) settings.addVersions(f[9 .. $]); 151 else if (f.startsWith("-debug=")) settings.addDebugVersions(f[7 .. $]); 152 else newflags ~= f; 153 } 154 settings.dflags = newflags.data; 155 } 156 157 void setTarget(ref BuildSettings settings, in BuildPlatform platform, string tpath = null) const 158 { 159 final switch (settings.targetType) { 160 case TargetType.autodetect: assert(false, "Invalid target type: autodetect"); 161 case TargetType.none: assert(false, "Invalid target type: none"); 162 case TargetType.sourceLibrary: assert(false, "Invalid target type: sourceLibrary"); 163 case TargetType.executable: break; 164 case TargetType.library: 165 case TargetType.staticLibrary: 166 settings.addDFlags("-lib"); 167 break; 168 case TargetType.dynamicLibrary: 169 version (Windows) settings.addDFlags("-shared"); 170 else settings.addDFlags("-shared", "-fPIC"); 171 break; 172 case TargetType.object: 173 settings.addDFlags("-c"); 174 break; 175 } 176 177 if (tpath is null) 178 tpath = (Path(settings.targetPath) ~ getTargetFileName(settings, platform)).toNativeString(); 179 settings.addDFlags("-of"~tpath); 180 } 181 182 void invoke(in BuildSettings settings, in BuildPlatform platform, void delegate(int, string) output_callback) 183 { 184 auto res_file = getTempFile("dub-build", ".rsp"); 185 std.file.write(res_file.toNativeString(), join(settings.dflags.map!(s => s.canFind(' ') ? "\""~s~"\"" : s), "\n")); 186 187 logDiagnostic("%s %s", platform.compilerBinary, join(cast(string[])settings.dflags, " ")); 188 invokeTool([platform.compilerBinary, "@"~res_file.toNativeString()], output_callback); 189 } 190 191 void invokeLinker(in BuildSettings settings, in BuildPlatform platform, string[] objects, void delegate(int, string) output_callback) 192 { 193 import std.string; 194 auto tpath = Path(settings.targetPath) ~ getTargetFileName(settings, platform); 195 auto args = ["-of"~tpath.toNativeString()]; 196 args ~= objects; 197 args ~= settings.sourceFiles; 198 version(linux) args ~= "-L--no-as-needed"; // avoids linker errors due to libraries being speficied in the wrong order by DMD 199 args ~= settings.lflags.map!(l => "-L"~l)().array; 200 args ~= settings.dflags.filter!(f => isLinkerDFlag(f)).array; 201 202 auto res_file = getTempFile("dub-build", ".lnk"); 203 std.file.write(res_file.toNativeString(), join(args, "\n")); 204 205 logDiagnostic("%s %s", platform.compilerBinary, args.join(" ")); 206 invokeTool([platform.compilerBinary, "@"~res_file.toNativeString()], output_callback); 207 } 208 209 private static bool isLinkerDFlag(string arg) 210 { 211 switch (arg) { 212 default: 213 if (arg.startsWith("-defaultlib=")) return true; 214 return false; 215 case "-g", "-gc", "-m32", "-m64", "-shared", "-lib", "-m32mscoff": 216 return true; 217 } 218 } 219 }