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