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