1 /** 2 Compiler settings and abstraction. 3 4 Copyright: © 2013-2016 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.compiler; 9 10 public import dub.compilers.buildsettings; 11 deprecated("Please `import dub.dependency : Dependency` instead") public import dub.dependency : Dependency; 12 public import dub.platform : BuildPlatform, matchesSpecification; 13 14 import dub.internal.vibecompat.core.log; 15 import dub.internal.vibecompat.inet.path; 16 17 import std.algorithm; 18 import std.array; 19 import std.exception; 20 import std.process; 21 22 /// Exception thrown in Compiler.determinePlatform if the given architecture is 23 /// not supported. 24 class UnsupportedArchitectureException : Exception 25 { 26 this(string architecture, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null) pure nothrow @safe 27 { 28 super("Unsupported architecture: "~architecture, file, line, nextInChain); 29 } 30 } 31 32 /// Exception thrown in getCompiler if no compiler matches the given name. 33 class UnknownCompilerException : Exception 34 { 35 this(string compilerName, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null) pure nothrow @safe 36 { 37 super("Unknown compiler: "~compilerName, file, line, nextInChain); 38 } 39 } 40 41 /// Exception thrown in invokeTool and probePlatform if running the compiler 42 /// returned non-zero exit code. 43 class CompilerInvocationException : Exception 44 { 45 this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null) pure nothrow @safe 46 { 47 super(msg, file, line, nextInChain); 48 } 49 } 50 51 /** Returns a compiler handler for a given binary name. 52 53 The name will be compared against the canonical name of each registered 54 compiler handler. If no match is found, the sub strings "dmd", "gdc" and 55 "ldc", in this order, will be searched within the name. If this doesn't 56 yield a match either, an $(LREF UnknownCompilerException) will be thrown. 57 */ 58 Compiler getCompiler(string name) 59 { 60 foreach (c; s_compilers) 61 if (c.name == name) 62 return c; 63 64 // try to match names like gdmd or gdc-2.61 65 if (name.canFind("dmd")) return getCompiler("dmd"); 66 if (name.canFind("gdc")) return getCompiler("gdc"); 67 if (name.canFind("ldc")) return getCompiler("ldc"); 68 69 throw new UnknownCompilerException(name); 70 } 71 72 /** Registers a new compiler handler. 73 74 Note that by default `DMDCompiler`, `GDCCompiler` and `LDCCompiler` are 75 already registered at startup. 76 */ 77 void registerCompiler(Compiler c) 78 { 79 s_compilers ~= c; 80 } 81 82 83 interface Compiler { 84 /// Returns the canonical name of the compiler (e.g. "dmd"). 85 @property string name() const; 86 87 /** Determines the build platform properties given a set of build settings. 88 89 This will invoke the compiler to build a platform probe file, which 90 determines the target build platform's properties during compile-time. 91 92 See_Also: `dub.compilers.utils.generatePlatformProbeFile` 93 */ 94 BuildPlatform determinePlatform(ref BuildSettings settings, string compiler_binary, string arch_override = null); 95 96 /// Replaces high level fields with low level fields and converts 97 /// dmd flags to compiler-specific flags 98 void prepareBuildSettings(ref BuildSettings settings, const scope ref BuildPlatform platform, BuildSetting supported_fields = BuildSetting.all) const; 99 100 /// Removes any dflags that match one of the BuildOptions values and populates the BuildSettings.options field. 101 void extractBuildOptions(ref BuildSettings settings) const; 102 103 /// Computes the full file name of the generated binary. 104 string getTargetFileName(in BuildSettings settings, in BuildPlatform platform) const; 105 106 /// Adds the appropriate flag to set a target path 107 void setTarget(ref BuildSettings settings, in BuildPlatform platform, string targetPath = null) const; 108 109 /// Invokes the compiler using the given flags 110 void invoke(in BuildSettings settings, in BuildPlatform platform, void delegate(int, string) output_callback); 111 112 /// Invokes the underlying linker directly 113 void invokeLinker(in BuildSettings settings, in BuildPlatform platform, string[] objects, void delegate(int, string) output_callback); 114 115 /// Convert linker flags to compiler format 116 string[] lflagsToDFlags(in string[] lflags) const; 117 118 /// Determines compiler version 119 string determineVersion(string compiler_binary, string verboseOutput); 120 121 /** Runs a tool and provides common boilerplate code. 122 123 This method should be used by `Compiler` implementations to invoke the 124 compiler or linker binary. 125 */ 126 protected final void invokeTool(string[] args, void delegate(int, string) output_callback, string[string] env = null) 127 { 128 import std.string; 129 130 int status; 131 if (output_callback) { 132 auto result = executeShell(escapeShellCommand(args), env); 133 output_callback(result.status, result.output); 134 status = result.status; 135 } else { 136 auto compiler_pid = spawnShell(escapeShellCommand(args), env); 137 status = compiler_pid.wait(); 138 } 139 140 version (Posix) if (status == -9) { 141 throw new CompilerInvocationException( 142 format("%s failed with exit code %s. This may indicate that the process has run out of memory.", 143 args[0], status)); 144 } 145 enforce!CompilerInvocationException(status == 0, 146 format("%s failed with exit code %s.", args[0], status)); 147 } 148 149 /** Compiles platform probe file with the specified compiler and parses its output. 150 Params: 151 compiler_binary = binary to invoke compiler with 152 args = arguments for the probe compilation 153 arch_override = special handler for x86_mscoff 154 */ 155 protected final BuildPlatform probePlatform(string compiler_binary, string[] args, 156 string arch_override) 157 { 158 import dub.compilers.utils : generatePlatformProbeFile, readPlatformJsonProbe; 159 import std.string : format, strip; 160 161 auto fil = generatePlatformProbeFile(); 162 163 auto result = executeShell(escapeShellCommand(compiler_binary ~ args ~ fil.toNativeString())); 164 enforce!CompilerInvocationException(result.status == 0, 165 format("Failed to invoke the compiler %s to determine the build platform: %s", 166 compiler_binary, result.output)); 167 168 auto build_platform = readPlatformJsonProbe(result.output); 169 build_platform.compilerBinary = compiler_binary; 170 171 auto ver = determineVersion(compiler_binary, result.output) 172 .strip; 173 if (ver.empty) { 174 logWarn(`Could not probe the compiler version for "%s". ` ~ 175 `Toolchain requirements might be ineffective`, build_platform.compiler); 176 } 177 else { 178 build_platform.compilerVersion = ver; 179 } 180 181 // Skip the following check for LDC, emitting a warning if the specified `-arch` 182 // cmdline option does not lead to the same string being found among 183 // `build_platform.architecture`, as it's brittle and doesn't work with triples. 184 if (build_platform.compiler != "ldc") { 185 if (arch_override.length && !build_platform.architecture.canFind(arch_override) && 186 !(build_platform.compiler == "dmd" && arch_override.among("x86_omf", "x86_mscoff")) // Will be fixed in determinePlatform 187 ) { 188 logWarn(`Failed to apply the selected architecture %s. Got %s.`, 189 arch_override, build_platform.architecture); 190 } 191 } 192 193 return build_platform; 194 } 195 } 196 197 private { 198 Compiler[] s_compilers; 199 }