1 /** 2 GDC 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.gdc; 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 GDCCompiler : Compiler { 28 private static immutable s_options = [ 29 tuple(BuildOption.debugMode, ["-fdebug"]), 30 tuple(BuildOption.releaseMode, ["-frelease"]), 31 tuple(BuildOption.coverage, ["-fprofile-arcs", "-ftest-coverage"]), 32 tuple(BuildOption.debugInfo, ["-g"]), 33 tuple(BuildOption.debugInfoC, ["-g", "-fdebug-c"]), 34 //tuple(BuildOption.alwaysStackFrame, ["-X"]), 35 //tuple(BuildOption.stackStomping, ["-X"]), 36 tuple(BuildOption.inline, ["-finline-functions"]), 37 tuple(BuildOption.noBoundsCheck, ["-fno-bounds-check"]), 38 tuple(BuildOption.optimize, ["-O3"]), 39 tuple(BuildOption.profile, ["-pg"]), 40 tuple(BuildOption.unittests, ["-funittest"]), 41 tuple(BuildOption.verbose, ["-fd-verbose"]), 42 tuple(BuildOption.ignoreUnknownPragmas, ["-fignore-unknown-pragmas"]), 43 tuple(BuildOption.syntaxOnly, ["-fsyntax-only"]), 44 tuple(BuildOption.warnings, ["-Wall"]), 45 tuple(BuildOption.warningsAsErrors, ["-Werror", "-Wall"]), 46 tuple(BuildOption.ignoreDeprecations, ["-Wno-deprecated"]), 47 tuple(BuildOption.deprecationWarnings, ["-Wdeprecated"]), 48 tuple(BuildOption.deprecationErrors, ["-Werror", "-Wdeprecated"]), 49 tuple(BuildOption.property, ["-fproperty"]), 50 //tuple(BuildOption.profileGC, ["-?"]), 51 52 tuple(BuildOption._docs, ["-fdoc-dir=docs"]), 53 tuple(BuildOption._ddox, ["-fXf=docs.json", "-fdoc-file=__dummy.html"]), 54 ]; 55 56 @property string name() const { return "gdc"; } 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 "arm": arch_flags = ["-marm"]; break; 65 case "arm_thumb": arch_flags = ["-mthumb"]; break; 66 case "x86": arch_flags = ["-m32"]; break; 67 case "x86_64": arch_flags = ["-m64"]; break; 68 } 69 settings.addDFlags(arch_flags); 70 71 auto binary_file = getTempFile("dub_platform_probe"); 72 return probePlatform(compiler_binary, 73 arch_flags ~ ["-c", "-o", binary_file.toNativeString()], 74 arch_override); 75 } 76 77 void prepareBuildSettings(ref BuildSettings settings, BuildSetting fields = BuildSetting.all) const 78 { 79 enforceBuildRequirements(settings); 80 81 if (!(fields & BuildSetting.options)) { 82 foreach (t; s_options) 83 if (settings.options & t[0]) 84 settings.addDFlags(t[1]); 85 } 86 87 if (!(fields & BuildSetting.versions)) { 88 settings.addDFlags(settings.versions.map!(s => "-fversion="~s)().array()); 89 settings.versions = null; 90 } 91 92 if (!(fields & BuildSetting.debugVersions)) { 93 settings.addDFlags(settings.debugVersions.map!(s => "-fdebug="~s)().array()); 94 settings.debugVersions = null; 95 } 96 97 if (!(fields & BuildSetting.importPaths)) { 98 settings.addDFlags(settings.importPaths.map!(s => "-I"~s)().array()); 99 settings.importPaths = null; 100 } 101 102 if (!(fields & BuildSetting.stringImportPaths)) { 103 settings.addDFlags(settings.stringImportPaths.map!(s => "-J"~s)().array()); 104 settings.stringImportPaths = null; 105 } 106 107 if (!(fields & BuildSetting.sourceFiles)) { 108 settings.addDFlags(settings.sourceFiles); 109 settings.sourceFiles = null; 110 } 111 112 if (!(fields & BuildSetting.libs)) { 113 resolveLibs(settings); 114 settings.addDFlags(settings.libs.map!(l => "-l"~l)().array()); 115 } 116 117 if (!(fields & BuildSetting.lflags)) { 118 settings.addDFlags(lflagsToDFlags(settings.lflags)); 119 settings.lflags = null; 120 } 121 122 if (settings.targetType == TargetType.dynamicLibrary) 123 settings.addDFlags("-fPIC"); 124 125 assert(fields & BuildSetting.dflags); 126 assert(fields & BuildSetting.copyFiles); 127 } 128 129 void extractBuildOptions(ref BuildSettings settings) const 130 { 131 Appender!(string[]) newflags; 132 next_flag: foreach (f; settings.dflags) { 133 foreach (t; s_options) 134 if (t[1].canFind(f)) { 135 settings.options |= t[0]; 136 continue next_flag; 137 } 138 if (f.startsWith("-fversion=")) settings.addVersions(f[10 .. $]); 139 else if (f.startsWith("-fdebug=")) settings.addDebugVersions(f[8 .. $]); 140 else newflags ~= f; 141 } 142 settings.dflags = newflags.data; 143 } 144 145 string getTargetFileName(in BuildSettings settings, in BuildPlatform platform) 146 const { 147 assert(settings.targetName.length > 0, "No target name set."); 148 final switch (settings.targetType) { 149 case TargetType.autodetect: assert(false, "Configurations must have a concrete target type."); 150 case TargetType.none: return null; 151 case TargetType.sourceLibrary: return null; 152 case TargetType.executable: 153 if (platform.platform.canFind("windows")) 154 return settings.targetName ~ ".exe"; 155 else return settings.targetName; 156 case TargetType.library: 157 case TargetType.staticLibrary: 158 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 case TargetType.object: 180 settings.addDFlags("-c"); 181 break; 182 case TargetType.dynamicLibrary: 183 settings.addDFlags("-shared", "-fPIC"); 184 break; 185 } 186 187 if (tpath is null) 188 tpath = (Path(settings.targetPath) ~ getTargetFileName(settings, platform)).toNativeString(); 189 settings.addDFlags("-o", tpath); 190 } 191 192 void invoke(in BuildSettings settings, in BuildPlatform platform, void delegate(int, string) output_callback) 193 { 194 auto res_file = getTempFile("dub-build", ".rsp"); 195 std.file.write(res_file.toNativeString(), join(settings.dflags.map!(s => escape(s)), "\n")); 196 197 logDiagnostic("%s %s", platform.compilerBinary, join(cast(string[])settings.dflags, " ")); 198 invokeTool([platform.compilerBinary, "@"~res_file.toNativeString()], output_callback); 199 } 200 201 void invokeLinker(in BuildSettings settings, in BuildPlatform platform, string[] objects, void delegate(int, string) output_callback) 202 { 203 import std.string; 204 string[] args; 205 // As the user is supposed to call setTarget prior to invoke, -o target is already set. 206 if (settings.targetType == TargetType.staticLibrary || settings.targetType == TargetType.staticLibrary) { 207 auto tpath = extractTarget(settings.dflags); 208 assert(tpath !is null, "setTarget should be called before invoke"); 209 args = [ "ar", "rcs", tpath ] ~ objects; 210 } else { 211 args = platform.compilerBinary ~ objects ~ settings.sourceFiles ~ settings.lflags ~ settings.dflags.filter!(f => isLinkageFlag(f)).array; 212 version(linux) args ~= "-L--no-as-needed"; // avoids linker errors due to libraries being speficied in the wrong order by DMD 213 } 214 logDiagnostic("%s", args.join(" ")); 215 invokeTool(args, output_callback); 216 } 217 218 string[] lflagsToDFlags(in string[] lflags) const 219 { 220 string[] dflags; 221 foreach( f; lflags ) 222 { 223 dflags ~= "-Xlinker"; 224 dflags ~= f; 225 } 226 227 return dflags; 228 } 229 } 230 231 private string extractTarget(const string[] args) { auto i = args.countUntil("-o"); return i >= 0 ? args[i+1] : null; } 232 233 private bool isLinkageFlag(string flag) { 234 switch (flag) { 235 case "-c": 236 return false; 237 default: 238 return true; 239 } 240 } 241 242 private string escape(string str) 243 { 244 auto ret = appender!string(); 245 foreach (char ch; str) { 246 switch (ch) { 247 default: ret.put(ch); break; 248 case '\\': ret.put(`\\`); break; 249 case ' ': ret.put(`\ `); break; 250 } 251 } 252 return ret.data; 253 }