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