Newer
Older
dub_jkp / source / dub / generators / cmake.d
  1. /**
  2. Generator for CMake build scripts
  3.  
  4. Copyright: © 2015 Steven Dwy
  5. License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
  6. Authors: Steven Dwy
  7. */
  8. module dub.generators.cmake;
  9.  
  10. import dub.compilers.buildsettings;
  11. import dub.generators.generator;
  12. import dub.internal.vibecompat.core.file;
  13. import dub.internal.vibecompat.inet.path;
  14. import dub.internal.logging;
  15. import dub.project;
  16.  
  17. import std.algorithm: map, uniq;
  18. import std.algorithm : stdsort = sort; // to avoid clashing with built-in sort
  19. import std.array: appender, join, replace;
  20. import std.stdio: File, write;
  21. import std.string: format;
  22.  
  23. class CMakeGenerator: ProjectGenerator
  24. {
  25. this(Project project)
  26. {
  27. super(project);
  28. }
  29.  
  30. override void generateTargets(GeneratorSettings settings, in TargetInfo[string] targets)
  31. {
  32. auto script = appender!(char[]);
  33. auto scripts = appender!(string[]);
  34. bool[string] visited;
  35. NativePath projectRoot = m_project.rootPackage.path;
  36. NativePath cmakeListsPath = projectRoot ~ "CMakeLists.txt";
  37.  
  38. foreach(name, info; targets)
  39. {
  40. if(visited.get(name, false))
  41. continue;
  42.  
  43. visited[name] = true;
  44. name = name.sanitize;
  45. string targetType;
  46. string libType;
  47. bool addTarget = true;
  48.  
  49. switch(info.buildSettings.targetType) with(TargetType)
  50. {
  51. case autodetect:
  52. throw new Exception("Don't know what to do about autodetect target type");
  53. case executable:
  54. targetType = "executable";
  55.  
  56. break;
  57. case dynamicLibrary:
  58. libType = "SHARED";
  59.  
  60. goto case;
  61. case library:
  62. case staticLibrary:
  63. targetType = "library";
  64.  
  65. break;
  66. case sourceLibrary:
  67. addTarget = false;
  68.  
  69. break;
  70. case none:
  71. continue;
  72. default:
  73. assert(false);
  74. }
  75.  
  76. script.put("include(UseD)\n");
  77. script.put(
  78. "add_d_conditions(VERSION %s DEBUG %s)\n".format(
  79. info.buildSettings.versions.join(" "),
  80. info.buildSettings.debugVersions.join(" "),
  81. )
  82. );
  83.  
  84. foreach(directory; info.buildSettings.importPaths)
  85. script.put("include_directories(%s)\n".format(directory.sanitizeSlashes));
  86.  
  87. foreach(directory; info.buildSettings.cImportPaths)
  88. script.put("c_include_directories(%s)\n".format(directory.sanitizeSlashes));
  89.  
  90. if(addTarget)
  91. {
  92. script.put("add_%s(%s %s\n".format(targetType, name, libType));
  93.  
  94. foreach(file; info.buildSettings.sourceFiles)
  95. script.put(" %s\n".format(file.sanitizeSlashes));
  96.  
  97. script.put(")\n");
  98. script.put(
  99. "target_link_libraries(%s %s %s)\n".format(
  100. name,
  101. (info.dependencies ~ info.linkDependencies).dup.stdsort.uniq.map!(s => sanitize(s)).join(" "),
  102. info.buildSettings.libs.join(" ")
  103. )
  104. );
  105. script.put(
  106. `set_target_properties(%s PROPERTIES TEXT_INCLUDE_DIRECTORIES "%s")`.format(
  107. name,
  108. info.buildSettings.stringImportPaths.map!(s => sanitizeSlashes(s)).join(";")
  109. ) ~ "\n"
  110. );
  111. }
  112.  
  113. string filename = (projectRoot ~ "%s.cmake".format(name)).toNativeString;
  114. File file = File(filename, "w");
  115.  
  116. file.write(script.data);
  117. file.close;
  118. script.shrinkTo(0);
  119. scripts.put(filename);
  120.  
  121. logInfo("Generated", Color.green, "%s.cmake", name);
  122. }
  123.  
  124. if(!cmakeListsPath.existsFile)
  125. {
  126. logWarn("You must use a fork of CMake which has D support for these scripts to function properly.");
  127. logWarn("It is available at https://github.com/trentforkert/cmake");
  128. logDiagnostic("Generating default CMakeLists.txt");
  129.  
  130. script.put("cmake_minimum_required(VERSION 3.0)\n");
  131. script.put("project(%s D)\n".format(m_project.rootPackage.name));
  132.  
  133. foreach(path; scripts.data)
  134. script.put("include(%s)\n".format(path));
  135.  
  136. File file = File(cmakeListsPath.toNativeString, "w");
  137.  
  138. file.write(script.data);
  139. file.close;
  140.  
  141. logInfo("Generated", Color.green, "CMakeLists.txt (default)");
  142. }
  143. }
  144. }
  145.  
  146. ///Transform a package name into a valid CMake target name.
  147. private string sanitize(string name)
  148. {
  149. return name.replace(":", "_");
  150. }
  151.  
  152. private string sanitizeSlashes(string path)
  153. {
  154. version(Windows)
  155. return path.replace("\\", "/");
  156. else
  157. return path;
  158. }