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