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.internal.utils; 12 import dub.internal.vibecompat.core.log; 13 import dub.internal.vibecompat.inet.path; 14 import dub.platform; 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.random; 23 import std.typecons; 24 25 26 class GdcCompiler : Compiler { 27 private static immutable s_options = [ 28 tuple(BuildOption.debugMode, ["-fdebug"]), 29 tuple(BuildOption.releaseMode, ["-frelease"]), 30 tuple(BuildOption.coverage, ["-fprofile-arcs", "-ftest-coverage"]), 31 tuple(BuildOption.debugInfo, ["-g"]), 32 tuple(BuildOption.debugInfoC, ["-g", "-fdebug-c"]), 33 //tuple(BuildOption.alwaysStackFrame, ["-X"]), 34 //tuple(BuildOption.stackStomping, ["-X"]), 35 tuple(BuildOption.inline, ["-finline-functions"]), 36 tuple(BuildOption.noBoundsCheck, ["-fno-bounds-check"]), 37 tuple(BuildOption.optimize, ["-O3"]), 38 tuple(BuildOption.profile, ["-pg"]), 39 tuple(BuildOption.unittests, ["-funittest"]), 40 tuple(BuildOption.verbose, ["-fd-verbose"]), 41 tuple(BuildOption.ignoreUnknownPragmas, ["-fignore-unknown-pragmas"]), 42 tuple(BuildOption.syntaxOnly, ["-fsyntax-only"]), 43 tuple(BuildOption.warnings, ["-Wall"]), 44 tuple(BuildOption.warningsAsErrors, ["-Werror", "-Wall"]), 45 tuple(BuildOption.ignoreDeprecations, ["-Wno-deprecated"]), 46 tuple(BuildOption.deprecationWarnings, ["-Wdeprecated"]), 47 tuple(BuildOption.deprecationErrors, ["-Werror", "-Wdeprecated"]), 48 tuple(BuildOption.property, ["-fproperty"]), 49 ]; 50 51 @property string name() const { return "gdc"; } 52 53 BuildPlatform determinePlatform(ref BuildSettings settings, string compiler_binary, string arch_override) 54 { 55 import std.process; 56 import std..string; 57 58 auto fil = generatePlatformProbeFile(); 59 60 string[] arch_flags; 61 62 switch (arch_override) { 63 default: throw new Exception("Unsupported architecture: "~arch_override); 64 case "": break; 65 case "x86": arch_flags = ["-m32"]; break; 66 case "x86_64": arch_flags = ["-m64"]; break; 67 } 68 settings.addDFlags(arch_flags); 69 70 auto binary_file = getTempFile("dub_platform_probe"); 71 auto result = executeShell(escapeShellCommand( 72 compiler_binary ~ 73 arch_flags ~ 74 ["-c", "-o", binary_file.toNativeString(), fil.toNativeString()] 75 )); 76 enforce(result.status == 0, format("Failed to invoke the compiler %s to determine the build platform: %s", 77 compiler_binary, result.output)); 78 79 auto build_platform = readPlatformProbe(result.output); 80 build_platform.compilerBinary = compiler_binary; 81 82 if (build_platform.compiler != this.name) { 83 logWarn(`The determined compiler type "%s" doesn't match the expected type "%s". This will probably result in build errors.`, 84 build_platform.compiler, this.name); 85 } 86 87 if (arch_override.length && !build_platform.architecture.canFind(arch_override)) { 88 logWarn(`Failed to apply the selected architecture %s. Got %s.`, 89 arch_override, build_platform.architecture); 90 } 91 92 return build_platform; 93 } 94 95 void prepareBuildSettings(ref BuildSettings settings, BuildSetting fields = BuildSetting.all) const 96 { 97 enforceBuildRequirements(settings); 98 99 if (!(fields & BuildSetting.options)) { 100 foreach (t; s_options) 101 if (settings.options & t[0]) 102 settings.addDFlags(t[1]); 103 } 104 105 if (!(fields & BuildSetting.versions)) { 106 settings.addDFlags(settings.versions.map!(s => "-fversion="~s)().array()); 107 settings.versions = null; 108 } 109 110 if (!(fields & BuildSetting.debugVersions)) { 111 settings.addDFlags(settings.debugVersions.map!(s => "-fdebug="~s)().array()); 112 settings.debugVersions = null; 113 } 114 115 if (!(fields & BuildSetting.importPaths)) { 116 settings.addDFlags(settings.importPaths.map!(s => "-I"~s)().array()); 117 settings.importPaths = null; 118 } 119 120 if (!(fields & BuildSetting.stringImportPaths)) { 121 settings.addDFlags(settings.stringImportPaths.map!(s => "-J"~s)().array()); 122 settings.stringImportPaths = null; 123 } 124 125 if (!(fields & BuildSetting.sourceFiles)) { 126 settings.addDFlags(settings.sourceFiles); 127 settings.sourceFiles = null; 128 } 129 130 if (!(fields & BuildSetting.libs)) { 131 resolveLibs(settings); 132 settings.addDFlags(settings.libs.map!(l => "-l"~l)().array()); 133 } 134 135 if (!(fields & BuildSetting.lflags)) { 136 foreach( f; settings.lflags ) 137 settings.addDFlags(["-Xlinker", f]); 138 settings.lflags = null; 139 } 140 141 assert(fields & BuildSetting.dflags); 142 assert(fields & BuildSetting.copyFiles); 143 } 144 145 void extractBuildOptions(ref BuildSettings settings) const 146 { 147 Appender!(string[]) newflags; 148 next_flag: foreach (f; settings.dflags) { 149 foreach (t; s_options) 150 if (t[1].canFind(f)) { 151 settings.options |= t[0]; 152 continue next_flag; 153 } 154 if (f.startsWith("-fversion=")) settings.addVersions(f[10 .. $]); 155 else if (f.startsWith("-fdebug=")) settings.addDebugVersions(f[8 .. $]); 156 else newflags ~= f; 157 } 158 settings.dflags = newflags.data; 159 } 160 161 void setTarget(ref BuildSettings settings, in BuildPlatform platform, string tpath = null) const 162 { 163 final switch (settings.targetType) { 164 case TargetType.autodetect: assert(false, "Invalid target type: autodetect"); 165 case TargetType.none: assert(false, "Invalid target type: none"); 166 case TargetType.sourceLibrary: assert(false, "Invalid target type: sourceLibrary"); 167 case TargetType.executable: break; 168 case TargetType.library: 169 case TargetType.staticLibrary: 170 case TargetType.object: 171 settings.addDFlags("-c"); 172 break; 173 case TargetType.dynamicLibrary: 174 settings.addDFlags("-shared", "-fPIC"); 175 break; 176 } 177 178 if (tpath is null) 179 tpath = (Path(settings.targetPath) ~ getTargetFileName(settings, platform)).toNativeString(); 180 settings.addDFlags("-o", tpath); 181 } 182 183 void invoke(in BuildSettings settings, in BuildPlatform platform, void delegate(int, string) output_callback) 184 { 185 auto res_file = getTempFile("dub-build", ".rsp"); 186 std.file.write(res_file.toNativeString(), join(settings.dflags.map!(s => escape(s)), "\n")); 187 188 logDiagnostic("%s %s", platform.compilerBinary, join(cast(string[])settings.dflags, " ")); 189 invokeTool([platform.compilerBinary, "@"~res_file.toNativeString()], output_callback); 190 } 191 192 void invokeLinker(in BuildSettings settings, in BuildPlatform platform, string[] objects, void delegate(int, string) output_callback) 193 { 194 import std..string; 195 string[] args; 196 // As the user is supposed to call setTarget prior to invoke, -o target is already set. 197 if (settings.targetType == TargetType.staticLibrary || settings.targetType == TargetType.staticLibrary) { 198 auto tpath = extractTarget(settings.dflags); 199 assert(tpath !is null, "setTarget should be called before invoke"); 200 args = [ "ar", "rcs", tpath ] ~ objects; 201 } else { 202 args = platform.compilerBinary ~ objects ~ settings.sourceFiles ~ settings.lflags ~ settings.dflags.filter!(f => isLinkageFlag(f)).array; 203 version(linux) args ~= "-L--no-as-needed"; // avoids linker errors due to libraries being speficied in the wrong order by DMD 204 } 205 logDiagnostic("%s", args.join(" ")); 206 invokeTool(args, output_callback); 207 } 208 } 209 210 private string extractTarget(const string[] args) { auto i = args.countUntil("-o"); return i >= 0 ? args[i+1] : null; } 211 212 private bool isLinkageFlag(string flag) { 213 switch (flag) { 214 case "-c": 215 return false; 216 default: 217 return true; 218 } 219 } 220 221 private string escape(string str) 222 { 223 auto ret = appender!string(); 224 foreach (char ch; str) { 225 switch (ch) { 226 default: ret.put(ch); break; 227 case '\\': ret.put(`\\`); break; 228 case ' ': ret.put(`\ `); break; 229 } 230 } 231 return ret.data; 232 }