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.compilers.utils; 12 import dub.internal.utils; 13 import dub.internal.vibecompat.core.log; 14 import dub.internal.vibecompat.inet.path; 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.typecons; 23 24 25 class DMDCompiler : Compiler { 26 private static immutable s_options = [ 27 tuple(BuildOption.debugMode, ["-debug"]), 28 tuple(BuildOption.releaseMode, ["-release"]), 29 tuple(BuildOption.coverage, ["-cov"]), 30 tuple(BuildOption.debugInfo, ["-g"]), 31 tuple(BuildOption.debugInfoC, ["-g"]), 32 tuple(BuildOption.alwaysStackFrame, ["-gs"]), 33 tuple(BuildOption.stackStomping, ["-gx"]), 34 tuple(BuildOption.inline, ["-inline"]), 35 tuple(BuildOption.noBoundsCheck, ["-noboundscheck"]), 36 tuple(BuildOption.optimize, ["-O"]), 37 tuple(BuildOption.profile, ["-profile"]), 38 tuple(BuildOption.unittests, ["-unittest"]), 39 tuple(BuildOption.verbose, ["-v"]), 40 tuple(BuildOption.ignoreUnknownPragmas, ["-ignore"]), 41 tuple(BuildOption.syntaxOnly, ["-o-"]), 42 tuple(BuildOption.warnings, ["-wi"]), 43 tuple(BuildOption.warningsAsErrors, ["-w"]), 44 tuple(BuildOption.ignoreDeprecations, ["-d"]), 45 tuple(BuildOption.deprecationWarnings, ["-dw"]), 46 tuple(BuildOption.deprecationErrors, ["-de"]), 47 tuple(BuildOption.property, ["-property"]), 48 tuple(BuildOption.profileGC, ["-profile=gc"]), 49 50 tuple(BuildOption._docs, ["-Dddocs"]), 51 tuple(BuildOption._ddox, ["-Xfdocs.json", "-Df__dummy.html"]), 52 ]; 53 54 @property string name() const { return "dmd"; } 55 56 BuildPlatform determinePlatform(ref BuildSettings settings, string compiler_binary, string arch_override) 57 { 58 string[] arch_flags; 59 switch (arch_override) { 60 default: throw new Exception("Unsupported architecture: "~arch_override); 61 case "": break; 62 case "x86": arch_flags = ["-m32"]; break; 63 case "x86_64": arch_flags = ["-m64"]; break; 64 case "x86_mscoff": arch_flags = ["-m32mscoff"]; break; 65 } 66 settings.addDFlags(arch_flags); 67 68 return probePlatform(compiler_binary, arch_flags ~ ["-quiet", "-c", "-o-"], arch_override); 69 } 70 71 void prepareBuildSettings(ref BuildSettings settings, BuildSetting fields = BuildSetting.all) const 72 { 73 enforceBuildRequirements(settings); 74 75 if (!(fields & BuildSetting.options)) { 76 foreach (t; s_options) 77 if (settings.options & t[0]) 78 settings.addDFlags(t[1]); 79 } 80 81 if (!(fields & BuildSetting.versions)) { 82 settings.addDFlags(settings.versions.map!(s => "-version="~s)().array()); 83 settings.versions = null; 84 } 85 86 if (!(fields & BuildSetting.debugVersions)) { 87 settings.addDFlags(settings.debugVersions.map!(s => "-debug="~s)().array()); 88 settings.debugVersions = null; 89 } 90 91 if (!(fields & BuildSetting.importPaths)) { 92 settings.addDFlags(settings.importPaths.map!(s => "-I"~s)().array()); 93 settings.importPaths = null; 94 } 95 96 if (!(fields & BuildSetting.stringImportPaths)) { 97 settings.addDFlags(settings.stringImportPaths.map!(s => "-J"~s)().array()); 98 settings.stringImportPaths = null; 99 } 100 101 if (!(fields & BuildSetting.libs)) { 102 resolveLibs(settings); 103 version(Windows) settings.addSourceFiles(settings.libs.map!(l => l~".lib")().array()); 104 else settings.addLFlags(settings.libs.map!(l => "-l"~l)().array()); 105 } 106 107 if (!(fields & BuildSetting.sourceFiles)) { 108 settings.addDFlags(settings.sourceFiles); 109 settings.sourceFiles = null; 110 } 111 112 if (!(fields & BuildSetting.lflags)) { 113 settings.addDFlags(lflagsToDFlags(settings.lflags)); 114 settings.lflags = null; 115 } 116 117 version (Posix) { 118 if (settings.options & BuildOption.pic) 119 settings.addDFlags("-fPIC"); 120 } 121 122 assert(fields & BuildSetting.dflags); 123 assert(fields & BuildSetting.copyFiles); 124 } 125 126 void extractBuildOptions(ref BuildSettings settings) const 127 { 128 Appender!(string[]) newflags; 129 next_flag: foreach (f; settings.dflags) { 130 foreach (t; s_options) 131 if (t[1].canFind(f)) { 132 settings.options |= t[0]; 133 continue next_flag; 134 } 135 if (f.startsWith("-version=")) settings.addVersions(f[9 .. $]); 136 else if (f.startsWith("-debug=")) settings.addDebugVersions(f[7 .. $]); 137 else newflags ~= f; 138 } 139 settings.dflags = newflags.data; 140 } 141 142 string getTargetFileName(in BuildSettings settings, in BuildPlatform platform) 143 const { 144 import std.conv: text; 145 assert(settings.targetName.length > 0, "No target name set."); 146 final switch (settings.targetType) { 147 case TargetType.autodetect: 148 assert(false, 149 text("Configurations must have a concrete target type, ", settings.targetName, 150 " has ", settings.targetType)); 151 case TargetType.none: return null; 152 case TargetType.sourceLibrary: return null; 153 case TargetType.executable: 154 if (platform.platform.canFind("windows")) 155 return settings.targetName ~ ".exe"; 156 else return settings.targetName; 157 case TargetType.library: 158 case TargetType.staticLibrary: 159 if (platform.platform.canFind("windows")) 160 return settings.targetName ~ ".lib"; 161 else return "lib" ~ settings.targetName ~ ".a"; 162 case TargetType.dynamicLibrary: 163 if (platform.platform.canFind("windows")) 164 return settings.targetName ~ ".dll"; 165 else return "lib" ~ settings.targetName ~ ".so"; 166 case TargetType.object: 167 if (platform.platform.canFind("windows")) 168 return settings.targetName ~ ".obj"; 169 else return settings.targetName ~ ".o"; 170 } 171 } 172 173 void setTarget(ref BuildSettings settings, in BuildPlatform platform, string tpath = null) const 174 { 175 final switch (settings.targetType) { 176 case TargetType.autodetect: assert(false, "Invalid target type: autodetect"); 177 case TargetType.none: assert(false, "Invalid target type: none"); 178 case TargetType.sourceLibrary: assert(false, "Invalid target type: sourceLibrary"); 179 case TargetType.executable: break; 180 case TargetType.library: 181 case TargetType.staticLibrary: 182 settings.addDFlags("-lib"); 183 break; 184 case TargetType.dynamicLibrary: 185 version (Windows) settings.addDFlags("-shared"); 186 else version (OSX) settings.addDFlags("-shared"); 187 else settings.prependDFlags("-shared", "-defaultlib=libphobos2.so"); 188 break; 189 case TargetType.object: 190 settings.addDFlags("-c"); 191 break; 192 } 193 194 if (tpath is null) 195 tpath = (NativePath(settings.targetPath) ~ getTargetFileName(settings, platform)).toNativeString(); 196 settings.addDFlags("-of"~tpath); 197 } 198 199 void invoke(in BuildSettings settings, in BuildPlatform platform, void delegate(int, string) output_callback) 200 { 201 auto res_file = getTempFile("dub-build", ".rsp"); 202 const(string)[] args = settings.dflags; 203 if (platform.frontendVersion >= 2066) args ~= "-vcolumns"; 204 std.file.write(res_file.toNativeString(), escapeArgs(args).join("\n")); 205 206 logDiagnostic("%s %s", platform.compilerBinary, escapeArgs(args).join(" ")); 207 invokeTool([platform.compilerBinary, "@"~res_file.toNativeString()], output_callback); 208 } 209 210 void invokeLinker(in BuildSettings settings, in BuildPlatform platform, string[] objects, void delegate(int, string) output_callback) 211 { 212 import std.string; 213 auto tpath = NativePath(settings.targetPath) ~ getTargetFileName(settings, platform); 214 auto args = ["-of"~tpath.toNativeString()]; 215 args ~= objects; 216 args ~= settings.sourceFiles; 217 version(linux) args ~= "-L--no-as-needed"; // avoids linker errors due to libraries being specified in the wrong order by DMD 218 args ~= lflagsToDFlags(settings.lflags); 219 args ~= settings.dflags.filter!(f => isLinkerDFlag(f)).array; 220 221 auto res_file = getTempFile("dub-build", ".lnk"); 222 std.file.write(res_file.toNativeString(), escapeArgs(args).join("\n")); 223 224 logDiagnostic("%s %s", platform.compilerBinary, escapeArgs(args).join(" ")); 225 invokeTool([platform.compilerBinary, "@"~res_file.toNativeString()], output_callback); 226 } 227 228 string[] lflagsToDFlags(in string[] lflags) const 229 { 230 return lflags.map!(f => "-L"~f)().array(); 231 } 232 233 private auto escapeArgs(in string[] args) 234 { 235 return args.map!(s => s.canFind(' ') ? "\""~s~"\"" : s); 236 } 237 238 private static bool isLinkerDFlag(string arg) 239 { 240 switch (arg) { 241 default: 242 if (arg.startsWith("-defaultlib=")) return true; 243 return false; 244 case "-g", "-gc", "-m32", "-m64", "-shared", "-lib", "-m32mscoff": 245 return true; 246 } 247 } 248 }