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.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 LDCCompiler : Compiler { 28 private static immutable s_options = [ 29 tuple(BuildOption.debugMode, ["-d-debug"]), 30 tuple(BuildOption.releaseMode, ["-release"]), 31 //tuple(BuildOption.coverage, ["-?"]), 32 tuple(BuildOption.debugInfo, ["-g"]), 33 tuple(BuildOption.debugInfoC, ["-gc"]), 34 //tuple(BuildOption.alwaysStackFrame, ["-?"]), 35 //tuple(BuildOption.stackStomping, ["-?"]), 36 tuple(BuildOption.inline, ["-enable-inlining", "-Hkeep-all-bodies"]), 37 tuple(BuildOption.noBoundsCheck, ["-boundscheck=off"]), 38 tuple(BuildOption.optimize, ["-O3"]), 39 //tuple(BuildOption.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, ["-?"]), 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 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 = ["-march=x86"]; break; 65 case "x86_64": arch_flags = ["-march=x86-64"]; break; 66 } 67 settings.addDFlags(arch_flags); 68 69 return probePlatform(compiler_binary, arch_flags ~ ["-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 // since LDC always outputs multiple object files, avoid conflicts by default 83 settings.addDFlags("-oq", "-od=.dub/obj"); 84 85 if (!(fields & BuildSetting.versions)) { 86 settings.addDFlags(settings.versions.map!(s => "-d-version="~s)().array()); 87 settings.versions = null; 88 } 89 90 if (!(fields & BuildSetting.debugVersions)) { 91 settings.addDFlags(settings.debugVersions.map!(s => "-d-debug="~s)().array()); 92 settings.debugVersions = null; 93 } 94 95 if (!(fields & BuildSetting.importPaths)) { 96 settings.addDFlags(settings.importPaths.map!(s => "-I"~s)().array()); 97 settings.importPaths = null; 98 } 99 100 if (!(fields & BuildSetting.stringImportPaths)) { 101 settings.addDFlags(settings.stringImportPaths.map!(s => "-J"~s)().array()); 102 settings.stringImportPaths = null; 103 } 104 105 if (!(fields & BuildSetting.sourceFiles)) { 106 settings.addDFlags(settings.sourceFiles); 107 settings.sourceFiles = null; 108 } 109 110 if (!(fields & BuildSetting.libs)) { 111 resolveLibs(settings); 112 settings.addLFlags(settings.libs.map!(l => "-l"~l)().array()); 113 } 114 115 if (!(fields & BuildSetting.lflags)) { 116 settings.addDFlags(lflagsToDFlags(settings.lflags)); 117 settings.lflags = null; 118 } 119 120 if (settings.targetType == TargetType.dynamicLibrary) 121 settings.addDFlags("-relocation-model=pic"); 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("-d-version=")) settings.addVersions(f[11 .. $]); 137 else if (f.startsWith("-d-debug=")) settings.addDebugVersions(f[9 .. $]); 138 else newflags ~= f; 139 } 140 settings.dflags = newflags.data; 141 } 142 143 string getTargetFileName(in BuildSettings settings, in BuildPlatform platform) 144 const { 145 import std.string : splitLines, strip; 146 import std.uni : toLower; 147 148 assert(settings.targetName.length > 0, "No target name set."); 149 150 auto result = executeShell(escapeShellCommand([platform.compilerBinary, "-version"])); 151 enforce (result.status == 0, "Failed to determine linker used by LDC. \"" 152 ~platform.compilerBinary~" -version\" failed with exit code " 153 ~result.status.to!string()~"."); 154 155 bool generates_coff = result.output.splitLines.find!(l => l.strip.toLower.startsWith("default target:")).front.canFind("-windows-msvc"); 156 157 final switch (settings.targetType) { 158 case TargetType.autodetect: assert(false, "Configurations must have a concrete target type."); 159 case TargetType.none: return null; 160 case TargetType.sourceLibrary: return null; 161 case TargetType.executable: 162 if (platform.platform.canFind("windows")) 163 return settings.targetName ~ ".exe"; 164 else return settings.targetName; 165 case TargetType.library: 166 case TargetType.staticLibrary: 167 if (generates_coff) return settings.targetName ~ ".lib"; 168 else return "lib" ~ settings.targetName ~ ".a"; 169 case TargetType.dynamicLibrary: 170 if (platform.platform.canFind("windows")) 171 return settings.targetName ~ ".dll"; 172 else return "lib" ~ settings.targetName ~ ".so"; 173 case TargetType.object: 174 if (platform.platform.canFind("windows")) 175 return settings.targetName ~ ".obj"; 176 else return settings.targetName ~ ".o"; 177 } 178 } 179 180 void setTarget(ref BuildSettings settings, in BuildPlatform platform, string tpath = null) const 181 { 182 final switch (settings.targetType) { 183 case TargetType.autodetect: assert(false, "Invalid target type: autodetect"); 184 case TargetType.none: assert(false, "Invalid target type: none"); 185 case TargetType.sourceLibrary: assert(false, "Invalid target type: sourceLibrary"); 186 case TargetType.executable: break; 187 case TargetType.library: 188 case TargetType.staticLibrary: 189 settings.addDFlags("-lib"); 190 break; 191 case TargetType.dynamicLibrary: 192 settings.addDFlags("-shared"); 193 break; 194 case TargetType.object: 195 settings.addDFlags("-c"); 196 break; 197 } 198 199 if (tpath is null) 200 tpath = (Path(settings.targetPath) ~ getTargetFileName(settings, platform)).toNativeString(); 201 settings.addDFlags("-of"~tpath); 202 } 203 204 void invoke(in BuildSettings settings, in BuildPlatform platform, void delegate(int, string) output_callback) 205 { 206 auto res_file = getTempFile("dub-build", ".rsp"); 207 const(string)[] args = settings.dflags; 208 if (platform.frontendVersion >= 2066) args ~= "-vcolumns"; 209 std.file.write(res_file.toNativeString(), escapeArgs(args).join("\n")); 210 211 logDiagnostic("%s %s", platform.compilerBinary, escapeArgs(args).join(" ")); 212 invokeTool([platform.compilerBinary, "@"~res_file.toNativeString()], output_callback); 213 } 214 215 void invokeLinker(in BuildSettings settings, in BuildPlatform platform, string[] objects, void delegate(int, string) output_callback) 216 { 217 assert(false, "Separate linking not implemented for LDC"); 218 } 219 220 string[] lflagsToDFlags(in string[] lflags) const 221 { 222 return lflags.map!(s => "-L="~s)().array(); 223 } 224 225 private auto escapeArgs(in string[] args) 226 { 227 return args.map!(s => s.canFind(' ') ? "\""~s~"\"" : s); 228 } 229 }