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 }