Newer
Older
dub_jkp / source / dub / compilers / ldc.d
  1. /**
  2. LDC 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.ldc;
  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.recipe.packagerecipe : ToolchainRequirements;
  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.typecons;
  24.  
  25.  
  26. class LDCCompiler : Compiler {
  27. private static immutable s_options = [
  28. tuple(BuildOption.debugMode, ["-d-debug"]),
  29. tuple(BuildOption.releaseMode, ["-release"]),
  30. //tuple(BuildOption.coverage, ["-?"]),
  31. tuple(BuildOption.debugInfo, ["-g"]),
  32. tuple(BuildOption.debugInfoC, ["-gc"]),
  33. //tuple(BuildOption.alwaysStackFrame, ["-?"]),
  34. //tuple(BuildOption.stackStomping, ["-?"]),
  35. tuple(BuildOption.inline, ["-enable-inlining", "-Hkeep-all-bodies"]),
  36. tuple(BuildOption.noBoundsCheck, ["-boundscheck=off"]),
  37. tuple(BuildOption.optimize, ["-O3"]),
  38. //tuple(BuildOption.profile, ["-?"]),
  39. tuple(BuildOption.unittests, ["-unittest"]),
  40. tuple(BuildOption.verbose, ["-v"]),
  41. tuple(BuildOption.ignoreUnknownPragmas, ["-ignore"]),
  42. tuple(BuildOption.syntaxOnly, ["-o-"]),
  43. tuple(BuildOption.warnings, ["-wi"]),
  44. tuple(BuildOption.warningsAsErrors, ["-w"]),
  45. tuple(BuildOption.ignoreDeprecations, ["-d"]),
  46. tuple(BuildOption.deprecationWarnings, ["-dw"]),
  47. tuple(BuildOption.deprecationErrors, ["-de"]),
  48. tuple(BuildOption.property, ["-property"]),
  49. //tuple(BuildOption.profileGC, ["-?"]),
  50. tuple(BuildOption.betterC, ["-betterC"]),
  51.  
  52. tuple(BuildOption._docs, ["-Dd=docs"]),
  53. tuple(BuildOption._ddox, ["-Xf=docs.json", "-Dd=__dummy_docs"]),
  54. ];
  55.  
  56. @property string name() const { return "ldc"; }
  57.  
  58. enum ldcVersionRe = `^version\s+v?(\d+\.\d+\.\d+[A-Za-z0-9.+-]*)`;
  59.  
  60. unittest {
  61. import std.regex : matchFirst, regex;
  62. auto probe = `
  63. binary /usr/bin/ldc2
  64. version 1.11.0 (DMD v2.081.2, LLVM 6.0.1)
  65. config /etc/ldc2.conf (x86_64-pc-linux-gnu)
  66. `;
  67. auto re = regex(ldcVersionRe, "m");
  68. auto c = matchFirst(probe, re);
  69. assert(c && c.length > 1 && c[1] == "1.11.0");
  70. }
  71.  
  72. BuildPlatform determinePlatform(ref BuildSettings settings, string compiler_binary, string arch_override)
  73. {
  74. string[] arch_flags;
  75. switch (arch_override) {
  76. default: throw new Exception("Unsupported architecture: "~arch_override);
  77. case "": break;
  78. case "x86": arch_flags = ["-march=x86"]; break;
  79. case "x86_64": arch_flags = ["-march=x86-64"]; break;
  80. }
  81. settings.addDFlags(arch_flags);
  82.  
  83. return probePlatform(
  84. compiler_binary,
  85. arch_flags ~ ["-c", "-o-", "-v"],
  86. arch_override,
  87. [ ldcVersionRe ]
  88. );
  89. }
  90.  
  91. void prepareBuildSettings(ref BuildSettings settings, BuildSetting fields = BuildSetting.all) const
  92. {
  93. enforceBuildRequirements(settings);
  94.  
  95. if (!(fields & BuildSetting.options)) {
  96. foreach (t; s_options)
  97. if (settings.options & t[0])
  98. settings.addDFlags(t[1]);
  99. }
  100.  
  101. // since LDC always outputs multiple object files, avoid conflicts by default
  102. settings.addDFlags("-oq", "-od=.dub/obj");
  103.  
  104. if (!(fields & BuildSetting.versions)) {
  105. settings.addDFlags(settings.versions.map!(s => "-d-version="~s)().array());
  106. settings.versions = null;
  107. }
  108.  
  109. if (!(fields & BuildSetting.debugVersions)) {
  110. settings.addDFlags(settings.debugVersions.map!(s => "-d-debug="~s)().array());
  111. settings.debugVersions = null;
  112. }
  113.  
  114. if (!(fields & BuildSetting.importPaths)) {
  115. settings.addDFlags(settings.importPaths.map!(s => "-I"~s)().array());
  116. settings.importPaths = null;
  117. }
  118.  
  119. if (!(fields & BuildSetting.stringImportPaths)) {
  120. settings.addDFlags(settings.stringImportPaths.map!(s => "-J"~s)().array());
  121. settings.stringImportPaths = null;
  122. }
  123.  
  124. if (!(fields & BuildSetting.sourceFiles)) {
  125. settings.addDFlags(settings.sourceFiles);
  126. settings.sourceFiles = null;
  127. }
  128.  
  129. if (!(fields & BuildSetting.libs)) {
  130. resolveLibs(settings);
  131. settings.addLFlags(settings.libs.map!(l => "-l"~l)().array());
  132. }
  133.  
  134. if (!(fields & BuildSetting.lflags)) {
  135. settings.addDFlags(lflagsToDFlags(settings.lflags));
  136. settings.lflags = null;
  137. }
  138.  
  139. if (settings.options & BuildOption.pic)
  140. settings.addDFlags("-relocation-model=pic");
  141.  
  142. assert(fields & BuildSetting.dflags);
  143. assert(fields & BuildSetting.copyFiles);
  144. }
  145.  
  146. void extractBuildOptions(ref BuildSettings settings) const
  147. {
  148. Appender!(string[]) newflags;
  149. next_flag: foreach (f; settings.dflags) {
  150. foreach (t; s_options)
  151. if (t[1].canFind(f)) {
  152. settings.options |= t[0];
  153. continue next_flag;
  154. }
  155. if (f.startsWith("-d-version=")) settings.addVersions(f[11 .. $]);
  156. else if (f.startsWith("-d-debug=")) settings.addDebugVersions(f[9 .. $]);
  157. else newflags ~= f;
  158. }
  159. settings.dflags = newflags.data;
  160. }
  161.  
  162. string getTargetFileName(in BuildSettings settings, in BuildPlatform platform)
  163. const {
  164. assert(settings.targetName.length > 0, "No target name set.");
  165.  
  166. final switch (settings.targetType) {
  167. case TargetType.autodetect: assert(false, "Configurations must have a concrete target type.");
  168. case TargetType.none: return null;
  169. case TargetType.sourceLibrary: return null;
  170. case TargetType.executable:
  171. if (platform.platform.canFind("windows"))
  172. return settings.targetName ~ ".exe";
  173. else return settings.targetName;
  174. case TargetType.library:
  175. case TargetType.staticLibrary:
  176. if (generatesCOFF(platform)) return settings.targetName ~ ".lib";
  177. else return "lib" ~ settings.targetName ~ ".a";
  178. case TargetType.dynamicLibrary:
  179. if (platform.platform.canFind("windows"))
  180. return settings.targetName ~ ".dll";
  181. else if (platform.platform.canFind("osx"))
  182. return "lib" ~ settings.targetName ~ ".dylib";
  183. else return "lib" ~ settings.targetName ~ ".so";
  184. case TargetType.object:
  185. if (platform.platform.canFind("windows"))
  186. return settings.targetName ~ ".obj";
  187. else return settings.targetName ~ ".o";
  188. }
  189. }
  190.  
  191. void setTarget(ref BuildSettings settings, in BuildPlatform platform, string tpath = null) const
  192. {
  193. final switch (settings.targetType) {
  194. case TargetType.autodetect: assert(false, "Invalid target type: autodetect");
  195. case TargetType.none: assert(false, "Invalid target type: none");
  196. case TargetType.sourceLibrary: assert(false, "Invalid target type: sourceLibrary");
  197. case TargetType.executable: break;
  198. case TargetType.library:
  199. case TargetType.staticLibrary:
  200. settings.addDFlags("-lib");
  201. break;
  202. case TargetType.dynamicLibrary:
  203. settings.addDFlags("-shared");
  204. break;
  205. case TargetType.object:
  206. settings.addDFlags("-c");
  207. break;
  208. }
  209.  
  210. if (tpath is null)
  211. tpath = (NativePath(settings.targetPath) ~ getTargetFileName(settings, platform)).toNativeString();
  212. settings.addDFlags("-of"~tpath);
  213. }
  214.  
  215. void invoke(in BuildSettings settings, in BuildPlatform platform, void delegate(int, string) output_callback)
  216. {
  217. auto res_file = getTempFile("dub-build", ".rsp");
  218. const(string)[] args = settings.dflags;
  219. if (platform.frontendVersion >= 2066) args ~= "-vcolumns";
  220. std.file.write(res_file.toNativeString(), escapeArgs(args).join("\n"));
  221.  
  222. logDiagnostic("%s %s", platform.compilerBinary, escapeArgs(args).join(" "));
  223. invokeTool([platform.compilerBinary, "@"~res_file.toNativeString()], output_callback);
  224. }
  225.  
  226. void invokeLinker(in BuildSettings settings, in BuildPlatform platform, string[] objects, void delegate(int, string) output_callback)
  227. {
  228. assert(false, "Separate linking not implemented for LDC");
  229. }
  230.  
  231. string[] lflagsToDFlags(in string[] lflags) const
  232. {
  233. return lflags.map!(s => "-L="~s)().array();
  234. }
  235.  
  236. private auto escapeArgs(in string[] args)
  237. {
  238. return args.map!(s => s.canFind(' ') ? "\""~s~"\"" : s);
  239. }
  240.  
  241. private static bool generatesCOFF(in BuildPlatform platform)
  242. {
  243. import std.string : splitLines, strip;
  244. import std.uni : toLower;
  245.  
  246. static bool[string] compiler_coff_map;
  247.  
  248. if (auto pret = platform.compilerBinary in compiler_coff_map)
  249. return *pret;
  250.  
  251. auto result = executeShell(escapeShellCommand([platform.compilerBinary, "-version"]));
  252. enforce (result.status == 0, "Failed to determine linker used by LDC. \""
  253. ~platform.compilerBinary~" -version\" failed with exit code "
  254. ~result.status.to!string()~".");
  255.  
  256. bool ret = result.output
  257. .splitLines
  258. .find!(l => l.strip.toLower.startsWith("default target:"))
  259. .front
  260. .canFind("msvc");
  261.  
  262. compiler_coff_map[platform.compilerBinary] = ret;
  263. return ret;
  264. }
  265. }