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