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