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.log; 13 import dub.internal.vibecompat.core.file; 14 import dub.internal.vibecompat.inet.path; 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.dup.join(" "), 80 info.buildSettings.debugVersions.dup.join(" "), 81 ) 82 ); 83 84 foreach(directory; info.buildSettings.importPaths) 85 script.put("include_directories(%s)\n".format(directory.sanitizeSlashes)); 86 87 if(addTarget) 88 { 89 script.put("add_%s(%s %s\n".format(targetType, name, libType)); 90 91 foreach(file; info.buildSettings.sourceFiles) 92 script.put(" %s\n".format(file.sanitizeSlashes)); 93 94 script.put(")\n"); 95 script.put( 96 "target_link_libraries(%s %s %s)\n".format( 97 name, 98 (info.dependencies ~ info.linkDependencies).dup.stdsort.uniq.map!(s => sanitize(s)).join(" "), 99 info.buildSettings.libs.dup.join(" ") 100 ) 101 ); 102 script.put( 103 `set_target_properties(%s PROPERTIES TEXT_INCLUDE_DIRECTORIES "%s")`.format( 104 name, 105 info.buildSettings.stringImportPaths.map!(s => sanitizeSlashes(s)).join(";") 106 ) ~ "\n" 107 ); 108 } 109 110 string filename = (projectRoot ~ "%s.cmake".format(name)).toNativeString; 111 File file = File(filename, "w"); 112 113 file.write(script.data); 114 file.close; 115 script.shrinkTo(0); 116 scripts.put(filename); 117 } 118 119 if(!cmakeListsPath.existsFile) 120 { 121 logWarn("You must use a fork of CMake which has D support for these scripts to function properly."); 122 logWarn("It is available at https://github.com/trentforkert/cmake"); 123 logInfo("Generating default CMakeLists.txt"); 124 script.put("cmake_minimum_required(VERSION 3.0)\n"); 125 script.put("project(%s D)\n".format(m_project.rootPackage.name)); 126 127 foreach(path; scripts.data) 128 script.put("include(%s)\n".format(path)); 129 130 File file = File(cmakeListsPath.toNativeString, "w"); 131 132 file.write(script.data); 133 file.close; 134 } 135 } 136 } 137 138 ///Transform a package name into a valid CMake target name. 139 private string sanitize(string name) 140 { 141 return name.replace(":", "_"); 142 } 143 144 private string sanitizeSlashes(string path) 145 { 146 version(Windows) 147 return path.replace("\\", "/"); 148 else 149 return path; 150 }