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 import dub.recipe.packagerecipe : ToolchainRequirements; 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.typecons; 24 25 26 class LDCCompiler : Compiler { 27 private static immutable s_options = [ 28 tuple(BuildOption.debugMode, ["-d-debug"]), 29 tuple(BuildOption.releaseMode, ["-release"]), 30 //tuple(BuildOption.coverage, ["-?"]), 31 tuple(BuildOption.debugInfo, ["-g"]), 32 tuple(BuildOption.debugInfoC, ["-gc"]), 33 //tuple(BuildOption.alwaysStackFrame, ["-?"]), 34 //tuple(BuildOption.stackStomping, ["-?"]), 35 tuple(BuildOption.inline, ["-enable-inlining", "-Hkeep-all-bodies"]), 36 tuple(BuildOption.noBoundsCheck, ["-boundscheck=off"]), 37 tuple(BuildOption.optimize, ["-O3"]), 38 //tuple(BuildOption.profile, ["-?"]), 39 tuple(BuildOption.unittests, ["-unittest"]), 40 tuple(BuildOption.verbose, ["-v"]), 41 tuple(BuildOption.ignoreUnknownPragmas, ["-ignore"]), 42 tuple(BuildOption.syntaxOnly, ["-o-"]), 43 tuple(BuildOption.warnings, ["-wi"]), 44 tuple(BuildOption.warningsAsErrors, ["-w"]), 45 tuple(BuildOption.ignoreDeprecations, ["-d"]), 46 tuple(BuildOption.deprecationWarnings, ["-dw"]), 47 tuple(BuildOption.deprecationErrors, ["-de"]), 48 tuple(BuildOption.property, ["-property"]), 49 //tuple(BuildOption.profileGC, ["-?"]), 50 tuple(BuildOption.betterC, ["-betterC"]), 51 52 tuple(BuildOption._docs, ["-Dd=docs"]), 53 tuple(BuildOption._ddox, ["-Xf=docs.json", "-Dd=__dummy_docs"]), 54 ]; 55 56 @property string name() const { return "ldc"; } 57 58 enum ldcVersionRe = `^version\s+v?(\d+\.\d+\.\d+[A-Za-z0-9.+-]*)`; 59 60 unittest { 61 import std.regex : matchFirst, regex; 62 auto probe = ` 63 binary /usr/bin/ldc2 64 version 1.11.0 (DMD v2.081.2, LLVM 6.0.1) 65 config /etc/ldc2.conf (x86_64-pc-linux-gnu) 66 `; 67 auto re = regex(ldcVersionRe, "m"); 68 auto c = matchFirst(probe, re); 69 assert(c && c.length > 1 && c[1] == "1.11.0"); 70 } 71 72 BuildPlatform determinePlatform(ref BuildSettings settings, string compiler_binary, string arch_override) 73 { 74 string[] arch_flags; 75 switch (arch_override) { 76 default: throw new Exception("Unsupported architecture: "~arch_override); 77 case "": break; 78 case "x86": arch_flags = ["-march=x86"]; break; 79 case "x86_64": arch_flags = ["-march=x86-64"]; break; 80 } 81 settings.addDFlags(arch_flags); 82 83 return probePlatform( 84 compiler_binary, 85 arch_flags ~ ["-c", "-o-", "-v"], 86 arch_override, 87 [ ldcVersionRe ] 88 ); 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 // since LDC always outputs multiple object files, avoid conflicts by default 102 settings.addDFlags("-oq", "-od=.dub/obj"); 103 104 if (!(fields & BuildSetting.versions)) { 105 settings.addDFlags(settings.versions.map!(s => "-d-version="~s)().array()); 106 settings.versions = null; 107 } 108 109 if (!(fields & BuildSetting.debugVersions)) { 110 settings.addDFlags(settings.debugVersions.map!(s => "-d-debug="~s)().array()); 111 settings.debugVersions = null; 112 } 113 114 if (!(fields & BuildSetting.importPaths)) { 115 settings.addDFlags(settings.importPaths.map!(s => "-I"~s)().array()); 116 settings.importPaths = null; 117 } 118 119 if (!(fields & BuildSetting.stringImportPaths)) { 120 settings.addDFlags(settings.stringImportPaths.map!(s => "-J"~s)().array()); 121 settings.stringImportPaths = null; 122 } 123 124 if (!(fields & BuildSetting.sourceFiles)) { 125 settings.addDFlags(settings.sourceFiles); 126 settings.sourceFiles = null; 127 } 128 129 if (!(fields & BuildSetting.libs)) { 130 resolveLibs(settings); 131 settings.addLFlags(settings.libs.map!(l => "-l"~l)().array()); 132 } 133 134 if (!(fields & BuildSetting.lflags)) { 135 settings.addDFlags(lflagsToDFlags(settings.lflags)); 136 settings.lflags = null; 137 } 138 139 if (settings.options & BuildOption.pic) 140 settings.addDFlags("-relocation-model=pic"); 141 142 assert(fields & BuildSetting.dflags); 143 assert(fields & BuildSetting.copyFiles); 144 } 145 146 void extractBuildOptions(ref BuildSettings settings) const 147 { 148 Appender!(string[]) newflags; 149 next_flag: foreach (f; settings.dflags) { 150 foreach (t; s_options) 151 if (t[1].canFind(f)) { 152 settings.options |= t[0]; 153 continue next_flag; 154 } 155 if (f.startsWith("-d-version=")) settings.addVersions(f[11 .. $]); 156 else if (f.startsWith("-d-debug=")) settings.addDebugVersions(f[9 .. $]); 157 else newflags ~= f; 158 } 159 settings.dflags = newflags.data; 160 } 161 162 string getTargetFileName(in BuildSettings settings, in BuildPlatform platform) 163 const { 164 assert(settings.targetName.length > 0, "No target name set."); 165 166 final switch (settings.targetType) { 167 case TargetType.autodetect: assert(false, "Configurations must have a concrete target type."); 168 case TargetType.none: return null; 169 case TargetType.sourceLibrary: return null; 170 case TargetType.executable: 171 if (platform.platform.canFind("windows")) 172 return settings.targetName ~ ".exe"; 173 else return settings.targetName; 174 case TargetType.library: 175 case TargetType.staticLibrary: 176 if (generatesCOFF(platform)) return settings.targetName ~ ".lib"; 177 else return "lib" ~ settings.targetName ~ ".a"; 178 case TargetType.dynamicLibrary: 179 if (platform.platform.canFind("windows")) 180 return settings.targetName ~ ".dll"; 181 else if (platform.platform.canFind("osx")) 182 return "lib" ~ settings.targetName ~ ".dylib"; 183 else return "lib" ~ settings.targetName ~ ".so"; 184 case TargetType.object: 185 if (platform.platform.canFind("windows")) 186 return settings.targetName ~ ".obj"; 187 else return settings.targetName ~ ".o"; 188 } 189 } 190 191 void setTarget(ref BuildSettings settings, in BuildPlatform platform, string tpath = null) const 192 { 193 final switch (settings.targetType) { 194 case TargetType.autodetect: assert(false, "Invalid target type: autodetect"); 195 case TargetType.none: assert(false, "Invalid target type: none"); 196 case TargetType.sourceLibrary: assert(false, "Invalid target type: sourceLibrary"); 197 case TargetType.executable: break; 198 case TargetType.library: 199 case TargetType.staticLibrary: 200 settings.addDFlags("-lib"); 201 break; 202 case TargetType.dynamicLibrary: 203 settings.addDFlags("-shared"); 204 break; 205 case TargetType.object: 206 settings.addDFlags("-c"); 207 break; 208 } 209 210 if (tpath is null) 211 tpath = (NativePath(settings.targetPath) ~ getTargetFileName(settings, platform)).toNativeString(); 212 settings.addDFlags("-of"~tpath); 213 } 214 215 void invoke(in BuildSettings settings, in BuildPlatform platform, void delegate(int, string) output_callback) 216 { 217 auto res_file = getTempFile("dub-build", ".rsp"); 218 const(string)[] args = settings.dflags; 219 if (platform.frontendVersion >= 2066) args ~= "-vcolumns"; 220 std.file.write(res_file.toNativeString(), escapeArgs(args).join("\n")); 221 222 logDiagnostic("%s %s", platform.compilerBinary, escapeArgs(args).join(" ")); 223 invokeTool([platform.compilerBinary, "@"~res_file.toNativeString()], output_callback); 224 } 225 226 void invokeLinker(in BuildSettings settings, in BuildPlatform platform, string[] objects, void delegate(int, string) output_callback) 227 { 228 assert(false, "Separate linking not implemented for LDC"); 229 } 230 231 string[] lflagsToDFlags(in string[] lflags) const 232 { 233 return lflags.map!(s => "-L="~s)().array(); 234 } 235 236 private auto escapeArgs(in string[] args) 237 { 238 return args.map!(s => s.canFind(' ') ? "\""~s~"\"" : s); 239 } 240 241 private static bool generatesCOFF(in BuildPlatform platform) 242 { 243 import std..string : splitLines, strip; 244 import std.uni : toLower; 245 246 static bool[string] compiler_coff_map; 247 248 if (auto pret = platform.compilerBinary in compiler_coff_map) 249 return *pret; 250 251 auto result = executeShell(escapeShellCommand([platform.compilerBinary, "-version"])); 252 enforce (result.status == 0, "Failed to determine linker used by LDC. \"" 253 ~platform.compilerBinary~" -version\" failed with exit code " 254 ~result.status.to!string()~"."); 255 256 bool ret = result.output 257 .splitLines 258 .find!(l => l.strip.toLower.startsWith("default target:")) 259 .front 260 .canFind("msvc"); 261 262 compiler_coff_map[platform.compilerBinary] = ret; 263 return ret; 264 } 265 }