1 /** 2 LDC 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.ldc; 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.exception; 19 import std.file; 20 import std.typecons; 21 22 23 class LDCCompiler : Compiler { 24 private static immutable s_options = [ 25 tuple(BuildOption.debugMode, ["-d-debug"]), 26 tuple(BuildOption.releaseMode, ["-release"]), 27 tuple(BuildOption.coverage, ["-cov"]), 28 tuple(BuildOption.debugInfo, ["-g"]), 29 tuple(BuildOption.debugInfoC, ["-gc"]), 30 tuple(BuildOption.alwaysStackFrame, ["-disable-fp-elim"]), 31 //tuple(BuildOption.stackStomping, ["-?"]), 32 tuple(BuildOption.inline, ["-enable-inlining", "-Hkeep-all-bodies"]), 33 tuple(BuildOption.noBoundsCheck, ["-boundscheck=off"]), 34 tuple(BuildOption.optimize, ["-O3"]), 35 tuple(BuildOption.profile, ["-fdmd-trace-functions"]), 36 tuple(BuildOption.unittests, ["-unittest"]), 37 tuple(BuildOption.verbose, ["-v"]), 38 tuple(BuildOption.ignoreUnknownPragmas, ["-ignore"]), 39 tuple(BuildOption.syntaxOnly, ["-o-"]), 40 tuple(BuildOption.warnings, ["-wi"]), 41 tuple(BuildOption.warningsAsErrors, ["-w"]), 42 tuple(BuildOption.ignoreDeprecations, ["-d"]), 43 tuple(BuildOption.deprecationWarnings, ["-dw"]), 44 tuple(BuildOption.deprecationErrors, ["-de"]), 45 tuple(BuildOption.property, ["-property"]), 46 //tuple(BuildOption.profileGC, ["-?"]), 47 tuple(BuildOption.betterC, ["-betterC"]), 48 tuple(BuildOption.lowmem, ["-lowmem"]), 49 50 tuple(BuildOption._docs, ["-Dd=docs"]), 51 tuple(BuildOption._ddox, ["-Xf=docs.json", "-Dd=__dummy_docs"]), 52 ]; 53 54 @property string name() const { return "ldc"; } 55 56 enum ldcVersionRe = `^version\s+v?(\d+\.\d+\.\d+[A-Za-z0-9.+-]*)`; 57 58 unittest { 59 import std.regex : matchFirst, regex; 60 auto probe = ` 61 binary /usr/bin/ldc2 62 version 1.11.0 (DMD v2.081.2, LLVM 6.0.1) 63 config /etc/ldc2.conf (x86_64-pc-linux-gnu) 64 `; 65 auto re = regex(ldcVersionRe, "m"); 66 auto c = matchFirst(probe, re); 67 assert(c && c.length > 1 && c[1] == "1.11.0"); 68 } 69 70 string determineVersion(string compiler_binary, string verboseOutput) 71 { 72 import std.regex : matchFirst, regex; 73 auto ver = matchFirst(verboseOutput, regex(ldcVersionRe, "m")); 74 return ver && ver.length > 1 ? ver[1] : null; 75 } 76 77 BuildPlatform determinePlatform(ref BuildSettings settings, string compiler_binary, string arch_override) 78 { 79 string[] arch_flags; 80 switch (arch_override) { 81 case "": break; 82 case "x86": arch_flags = ["-march=x86"]; break; 83 case "x86_mscoff": arch_flags = ["-march=x86"]; break; 84 case "x86_64": arch_flags = ["-march=x86-64"]; break; 85 case "aarch64": arch_flags = ["-march=aarch64"]; break; 86 case "powerpc64": arch_flags = ["-march=powerpc64"]; break; 87 default: 88 if (arch_override.canFind('-')) 89 arch_flags = ["-mtriple="~arch_override]; 90 else 91 throw new Exception("Unsupported architecture: "~arch_override); 92 break; 93 } 94 settings.addDFlags(arch_flags); 95 96 return probePlatform( 97 compiler_binary, 98 arch_flags ~ ["-c", "-o-", "-v"], 99 arch_override 100 ); 101 } 102 103 void prepareBuildSettings(ref BuildSettings settings, const scope ref BuildPlatform platform, BuildSetting fields = BuildSetting.all) const 104 { 105 import std.format : format; 106 enforceBuildRequirements(settings); 107 108 if (!(fields & BuildSetting.options)) { 109 foreach (t; s_options) 110 if (settings.options & t[0]) 111 settings.addDFlags(t[1]); 112 } 113 114 // since LDC always outputs multiple object files, avoid conflicts by default 115 settings.addDFlags("--oq", format("-od=%s/obj", settings.targetPath)); 116 117 if (!(fields & BuildSetting.versions)) { 118 settings.addDFlags(settings.versions.map!(s => "-d-version="~s)().array()); 119 settings.versions = null; 120 } 121 122 if (!(fields & BuildSetting.debugVersions)) { 123 settings.addDFlags(settings.debugVersions.map!(s => "-d-debug="~s)().array()); 124 settings.debugVersions = null; 125 } 126 127 if (!(fields & BuildSetting.importPaths)) { 128 settings.addDFlags(settings.importPaths.map!(s => "-I"~s)().array()); 129 settings.importPaths = null; 130 } 131 132 if (!(fields & BuildSetting.stringImportPaths)) { 133 settings.addDFlags(settings.stringImportPaths.map!(s => "-J"~s)().array()); 134 settings.stringImportPaths = null; 135 } 136 137 if (!(fields & BuildSetting.sourceFiles)) { 138 settings.addDFlags(settings.sourceFiles); 139 settings.sourceFiles = null; 140 } 141 142 if (!(fields & BuildSetting.libs)) { 143 resolveLibs(settings, platform); 144 settings.addLFlags(settings.libs.map!(l => "-l"~l)().array()); 145 } 146 147 if (!(fields & BuildSetting.lflags)) { 148 settings.addDFlags(lflagsToDFlags(settings.lflags)); 149 settings.lflags = null; 150 } 151 152 if (settings.options & BuildOption.pic) 153 settings.addDFlags("-relocation-model=pic"); 154 155 assert(fields & BuildSetting.dflags); 156 assert(fields & BuildSetting.copyFiles); 157 } 158 159 void extractBuildOptions(ref BuildSettings settings) const 160 { 161 Appender!(string[]) newflags; 162 next_flag: foreach (f; settings.dflags) { 163 foreach (t; s_options) 164 if (t[1].canFind(f)) { 165 settings.options |= t[0]; 166 continue next_flag; 167 } 168 if (f.startsWith("-d-version=")) settings.addVersions(f[11 .. $]); 169 else if (f.startsWith("-d-debug=")) settings.addDebugVersions(f[9 .. $]); 170 else newflags ~= f; 171 } 172 settings.dflags = newflags.data; 173 } 174 175 string getTargetFileName(in BuildSettings settings, in BuildPlatform platform) 176 const { 177 assert(settings.targetName.length > 0, "No target name set."); 178 179 const p = platform.platform; 180 final switch (settings.targetType) { 181 case TargetType.autodetect: assert(false, "Configurations must have a concrete target type."); 182 case TargetType.none: return null; 183 case TargetType.sourceLibrary: return null; 184 case TargetType.executable: 185 if (p.canFind("windows")) 186 return settings.targetName ~ ".exe"; 187 else if (p.canFind("wasm")) 188 return settings.targetName ~ ".wasm"; 189 else return settings.targetName.idup; 190 case TargetType.library: 191 case TargetType.staticLibrary: 192 if (p.canFind("windows") && !p.canFind("mingw")) 193 return settings.targetName ~ ".lib"; 194 else return "lib" ~ settings.targetName ~ ".a"; 195 case TargetType.dynamicLibrary: 196 if (p.canFind("windows")) 197 return settings.targetName ~ ".dll"; 198 else if (p.canFind("darwin")) 199 return "lib" ~ settings.targetName ~ ".dylib"; 200 else return "lib" ~ settings.targetName ~ ".so"; 201 case TargetType.object: 202 if (p.canFind("windows")) 203 return settings.targetName ~ ".obj"; 204 else return settings.targetName ~ ".o"; 205 } 206 } 207 208 void setTarget(ref BuildSettings settings, in BuildPlatform platform, string tpath = null) const 209 { 210 final switch (settings.targetType) { 211 case TargetType.autodetect: assert(false, "Invalid target type: autodetect"); 212 case TargetType.none: assert(false, "Invalid target type: none"); 213 case TargetType.sourceLibrary: assert(false, "Invalid target type: sourceLibrary"); 214 case TargetType.executable: break; 215 case TargetType.library: 216 case TargetType.staticLibrary: 217 settings.addDFlags("-lib"); 218 break; 219 case TargetType.dynamicLibrary: 220 settings.addDFlags("-shared"); 221 break; 222 case TargetType.object: 223 settings.addDFlags("-c"); 224 break; 225 } 226 227 if (tpath is null) 228 tpath = (NativePath(settings.targetPath) ~ getTargetFileName(settings, platform)).toNativeString(); 229 settings.addDFlags("-of"~tpath); 230 } 231 232 void invoke(in BuildSettings settings, in BuildPlatform platform, void delegate(int, string) output_callback) 233 { 234 auto res_file = getTempFile("dub-build", ".rsp"); 235 const(string)[] args = settings.dflags; 236 if (platform.frontendVersion >= 2066) args ~= "-vcolumns"; 237 std.file.write(res_file.toNativeString(), escapeArgs(args).join("\n")); 238 239 logDiagnostic("%s %s", platform.compilerBinary, escapeArgs(args).join(" ")); 240 string[string] env; 241 foreach (aa; [settings.environments, settings.buildEnvironments]) 242 foreach (k, v; aa) 243 env[k] = v; 244 invokeTool([platform.compilerBinary, "@"~res_file.toNativeString()], output_callback, env); 245 } 246 247 void invokeLinker(in BuildSettings settings, in BuildPlatform platform, string[] objects, void delegate(int, string) output_callback) 248 { 249 import std..string; 250 auto tpath = NativePath(settings.targetPath) ~ getTargetFileName(settings, platform); 251 auto args = ["-of"~tpath.toNativeString()]; 252 args ~= objects; 253 args ~= settings.sourceFiles; 254 if (platform.platform.canFind("linux")) 255 args ~= "-L--no-as-needed"; // avoids linker errors due to libraries being specified in the wrong order 256 args ~= lflagsToDFlags(settings.lflags); 257 args ~= settings.dflags.filter!(f => isLinkerDFlag(f)).array; 258 259 auto res_file = getTempFile("dub-build", ".lnk"); 260 std.file.write(res_file.toNativeString(), escapeArgs(args).join("\n")); 261 262 logDiagnostic("%s %s", platform.compilerBinary, escapeArgs(args).join(" ")); 263 string[string] env; 264 foreach (aa; [settings.environments, settings.buildEnvironments]) 265 foreach (k, v; aa) 266 env[k] = v; 267 invokeTool([platform.compilerBinary, "@"~res_file.toNativeString()], output_callback, env); 268 } 269 270 string[] lflagsToDFlags(in string[] lflags) const 271 { 272 return map!(f => "-L"~f)(lflags.filter!(f => f != "")()).array(); 273 } 274 275 private auto escapeArgs(in string[] args) 276 { 277 return args.map!(s => s.canFind(' ') ? "\""~s~"\"" : s); 278 } 279 280 static bool isLinkerDFlag(string arg) 281 { 282 if (arg.length > 2 && arg.startsWith("--")) 283 arg = arg[1 .. $]; // normalize to 1 leading hyphen 284 285 switch (arg) { 286 case "-g", "-gc", "-m32", "-m64", "-shared", "-lib", 287 "-betterC", "-disable-linker-strip-dead", "-static": 288 return true; 289 default: 290 return arg.startsWith("-L") 291 || arg.startsWith("-Xcc=") 292 || arg.startsWith("-defaultlib=") 293 || arg.startsWith("-platformlib=") 294 || arg.startsWith("-flto") 295 || arg.startsWith("-fsanitize=") 296 || arg.startsWith("-gcc=") 297 || arg.startsWith("-link-") 298 || arg.startsWith("-linker=") 299 || arg.startsWith("-march=") 300 || arg.startsWith("-mscrtlib=") 301 || arg.startsWith("-mtriple="); 302 } 303 } 304 }