Newer
Older
dub_jkp / source / dub / compilers / dmd.d
  1. /**
  2. DMD 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.dmd;
  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 DMDCompiler : Compiler {
  26. private static immutable s_options = [
  27. tuple(BuildOption.debugMode, ["-debug"]),
  28. tuple(BuildOption.releaseMode, ["-release"]),
  29. tuple(BuildOption.coverage, ["-cov"]),
  30. tuple(BuildOption.debugInfo, ["-g"]),
  31. tuple(BuildOption.debugInfoC, ["-g"]),
  32. tuple(BuildOption.alwaysStackFrame, ["-gs"]),
  33. tuple(BuildOption.stackStomping, ["-gx"]),
  34. tuple(BuildOption.inline, ["-inline"]),
  35. tuple(BuildOption.noBoundsCheck, ["-noboundscheck"]),
  36. tuple(BuildOption.optimize, ["-O"]),
  37. tuple(BuildOption.profile, ["-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, ["-profile=gc"]),
  49.  
  50. tuple(BuildOption._docs, ["-Dddocs"]),
  51. tuple(BuildOption._ddox, ["-Xfdocs.json", "-Df__dummy.html"]),
  52. ];
  53.  
  54. @property string name() const { return "dmd"; }
  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 = ["-m32"]; break;
  63. case "x86_64": arch_flags = ["-m64"]; break;
  64. case "x86_mscoff": arch_flags = ["-m32mscoff"]; break;
  65. }
  66. settings.addDFlags(arch_flags);
  67.  
  68. return probePlatform(compiler_binary, arch_flags ~ ["-quiet", "-c", "-o-"], arch_override);
  69. }
  70.  
  71. void prepareBuildSettings(ref BuildSettings settings, BuildSetting fields = BuildSetting.all) const
  72. {
  73. enforceBuildRequirements(settings);
  74.  
  75. if (!(fields & BuildSetting.options)) {
  76. foreach (t; s_options)
  77. if (settings.options & t[0])
  78. settings.addDFlags(t[1]);
  79. }
  80.  
  81. if (!(fields & BuildSetting.versions)) {
  82. settings.addDFlags(settings.versions.map!(s => "-version="~s)().array());
  83. settings.versions = null;
  84. }
  85.  
  86. if (!(fields & BuildSetting.debugVersions)) {
  87. settings.addDFlags(settings.debugVersions.map!(s => "-debug="~s)().array());
  88. settings.debugVersions = null;
  89. }
  90.  
  91. if (!(fields & BuildSetting.importPaths)) {
  92. settings.addDFlags(settings.importPaths.map!(s => "-I"~s)().array());
  93. settings.importPaths = null;
  94. }
  95.  
  96. if (!(fields & BuildSetting.stringImportPaths)) {
  97. settings.addDFlags(settings.stringImportPaths.map!(s => "-J"~s)().array());
  98. settings.stringImportPaths = null;
  99. }
  100.  
  101. if (!(fields & BuildSetting.libs)) {
  102. resolveLibs(settings);
  103. version(Windows) settings.addSourceFiles(settings.libs.map!(l => l~".lib")().array());
  104. else settings.addLFlags(settings.libs.map!(l => "-l"~l)().array());
  105. }
  106.  
  107. if (!(fields & BuildSetting.sourceFiles)) {
  108. settings.addDFlags(settings.sourceFiles);
  109. settings.sourceFiles = null;
  110. }
  111.  
  112. if (!(fields & BuildSetting.lflags)) {
  113. settings.addDFlags(lflagsToDFlags(settings.lflags));
  114. settings.lflags = null;
  115. }
  116.  
  117. version (Posix) {
  118. if (settings.options & BuildOption.pic)
  119. settings.addDFlags("-fPIC");
  120. }
  121.  
  122. assert(fields & BuildSetting.dflags);
  123. assert(fields & BuildSetting.copyFiles);
  124. }
  125.  
  126. void extractBuildOptions(ref BuildSettings settings) const
  127. {
  128. Appender!(string[]) newflags;
  129. next_flag: foreach (f; settings.dflags) {
  130. foreach (t; s_options)
  131. if (t[1].canFind(f)) {
  132. settings.options |= t[0];
  133. continue next_flag;
  134. }
  135. if (f.startsWith("-version=")) settings.addVersions(f[9 .. $]);
  136. else if (f.startsWith("-debug=")) settings.addDebugVersions(f[7 .. $]);
  137. else newflags ~= f;
  138. }
  139. settings.dflags = newflags.data;
  140. }
  141.  
  142. string getTargetFileName(in BuildSettings settings, in BuildPlatform platform)
  143. const {
  144. import std.conv: text;
  145. assert(settings.targetName.length > 0, "No target name set.");
  146. final switch (settings.targetType) {
  147. case TargetType.autodetect:
  148. assert(false,
  149. text("Configurations must have a concrete target type, ", settings.targetName,
  150. " has ", settings.targetType));
  151. case TargetType.none: return null;
  152. case TargetType.sourceLibrary: return null;
  153. case TargetType.executable:
  154. if (platform.platform.canFind("windows"))
  155. return settings.targetName ~ ".exe";
  156. else return settings.targetName;
  157. case TargetType.library:
  158. case TargetType.staticLibrary:
  159. if (platform.platform.canFind("windows"))
  160. return settings.targetName ~ ".lib";
  161. else return "lib" ~ settings.targetName ~ ".a";
  162. case TargetType.dynamicLibrary:
  163. if (platform.platform.canFind("windows"))
  164. return settings.targetName ~ ".dll";
  165. else return "lib" ~ settings.targetName ~ ".so";
  166. case TargetType.object:
  167. if (platform.platform.canFind("windows"))
  168. return settings.targetName ~ ".obj";
  169. else return settings.targetName ~ ".o";
  170. }
  171. }
  172.  
  173. void setTarget(ref BuildSettings settings, in BuildPlatform platform, string tpath = null) const
  174. {
  175. final switch (settings.targetType) {
  176. case TargetType.autodetect: assert(false, "Invalid target type: autodetect");
  177. case TargetType.none: assert(false, "Invalid target type: none");
  178. case TargetType.sourceLibrary: assert(false, "Invalid target type: sourceLibrary");
  179. case TargetType.executable: break;
  180. case TargetType.library:
  181. case TargetType.staticLibrary:
  182. settings.addDFlags("-lib");
  183. break;
  184. case TargetType.dynamicLibrary:
  185. version (Windows) settings.addDFlags("-shared");
  186. else version (OSX) settings.addDFlags("-shared");
  187. else settings.prependDFlags("-shared", "-defaultlib=libphobos2.so");
  188. break;
  189. case TargetType.object:
  190. settings.addDFlags("-c");
  191. break;
  192. }
  193.  
  194. if (tpath is null)
  195. tpath = (NativePath(settings.targetPath) ~ getTargetFileName(settings, platform)).toNativeString();
  196. settings.addDFlags("-of"~tpath);
  197. }
  198.  
  199. void invoke(in BuildSettings settings, in BuildPlatform platform, void delegate(int, string) output_callback)
  200. {
  201. auto res_file = getTempFile("dub-build", ".rsp");
  202. const(string)[] args = settings.dflags;
  203. if (platform.frontendVersion >= 2066) args ~= "-vcolumns";
  204. std.file.write(res_file.toNativeString(), escapeArgs(args).join("\n"));
  205.  
  206. logDiagnostic("%s %s", platform.compilerBinary, escapeArgs(args).join(" "));
  207. invokeTool([platform.compilerBinary, "@"~res_file.toNativeString()], output_callback);
  208. }
  209.  
  210. void invokeLinker(in BuildSettings settings, in BuildPlatform platform, string[] objects, void delegate(int, string) output_callback)
  211. {
  212. import std.string;
  213. auto tpath = NativePath(settings.targetPath) ~ getTargetFileName(settings, platform);
  214. auto args = ["-of"~tpath.toNativeString()];
  215. args ~= objects;
  216. args ~= settings.sourceFiles;
  217. version(linux) args ~= "-L--no-as-needed"; // avoids linker errors due to libraries being specified in the wrong order by DMD
  218. args ~= lflagsToDFlags(settings.lflags);
  219. args ~= settings.dflags.filter!(f => isLinkerDFlag(f)).array;
  220.  
  221. auto res_file = getTempFile("dub-build", ".lnk");
  222. std.file.write(res_file.toNativeString(), escapeArgs(args).join("\n"));
  223.  
  224. logDiagnostic("%s %s", platform.compilerBinary, escapeArgs(args).join(" "));
  225. invokeTool([platform.compilerBinary, "@"~res_file.toNativeString()], output_callback);
  226. }
  227.  
  228. string[] lflagsToDFlags(in string[] lflags) const
  229. {
  230. return lflags.map!(f => "-L"~f)().array();
  231. }
  232.  
  233. private auto escapeArgs(in string[] args)
  234. {
  235. return args.map!(s => s.canFind(' ') ? "\""~s~"\"" : s);
  236. }
  237.  
  238. private static bool isLinkerDFlag(string arg)
  239. {
  240. switch (arg) {
  241. default:
  242. if (arg.startsWith("-defaultlib=")) return true;
  243. return false;
  244. case "-g", "-gc", "-m32", "-m64", "-shared", "-lib", "-m32mscoff":
  245. return true;
  246. }
  247. }
  248. }