1 module reggae.dub.info; 2 3 import reggae.build; 4 import reggae.rules; 5 import reggae.types; 6 import reggae.sorting; 7 8 public import std.typecons: Yes, No; 9 import std.typecons: Flag; 10 import std.algorithm: map, filter, find, splitter; 11 import std.array: array, join; 12 import std.path: buildPath; 13 import std.traits: isCallable; 14 import std.range: chain; 15 16 struct DubPackage { 17 string name; 18 string path; 19 string mainSourceFile; 20 string targetFileName; 21 string[] dflags; 22 string[] lflags; 23 string[] importPaths; 24 string[] stringImportPaths; 25 string[] files; 26 string targetType; 27 string[] versions; 28 string[] dependencies; 29 string[] libs; 30 bool active; 31 string[] preBuildCommands; 32 } 33 34 bool isStaticLibrary(in string fileName) @safe pure nothrow { 35 import std.path: extension; 36 return fileName.extension == ".a"; 37 } 38 39 bool isObjectFile(in string fileName) @safe pure nothrow { 40 import reggae.rules.common: objExt; 41 import std.path: extension; 42 return fileName.extension == objExt; 43 } 44 45 string inDubPackagePath(in string packagePath, in string filePath) @safe pure nothrow { 46 import std.path: buildPath; 47 import std.algorithm: startsWith; 48 return filePath.startsWith("$project") 49 ? filePath 50 : buildPath(packagePath, filePath); 51 } 52 53 struct DubInfo { 54 55 DubPackage[] packages; 56 57 Target[] toTargets(Flag!"main" includeMain = Yes.main, 58 in string compilerFlags = "", 59 Flag!"allTogether" allTogether = No.allTogether) @safe const { 60 61 import reggae.config: options; 62 import std.functional: not; 63 64 Target[] targets; 65 66 // -unittest should only apply to the main package 67 string deUnitTest(T)(in T index, in string flags) { 68 import std.string: replace; 69 return index == 0 70 ? flags 71 : flags.replace("-unittest", "").replace("-main", ""); 72 } 73 74 const(string)[] getVersions(T)(in T index) { 75 import std.algorithm: map; 76 import std.array: array; 77 78 const(string)[] ret = index == 0 79 ? packages[index].allOf!(a => a.versions)(packages) 80 : packages[0].versions ~ packages[index].versions; 81 82 return ret.map!(a => "-version=" ~ a).array; 83 } 84 85 foreach(const i, const dubPackage; packages) { 86 const importPaths = allImportPaths(); 87 const stringImportPaths = dubPackage.allOf!(a => a.packagePaths(a.stringImportPaths))(packages); 88 auto versions = getVersions(i); 89 90 //the path must be explicit for the other packages, implicit for the "main" 91 //package 92 const projDir = i == 0 ? "" : dubPackage.path; 93 94 immutable flags = chain(dubPackage.dflags, 95 versions, 96 [options.dflags], 97 [deUnitTest(i, compilerFlags)]) 98 .join(" "); 99 100 const files = dubPackage.files. 101 filter!(a => includeMain || a != dubPackage.mainSourceFile). 102 filter!(not!isStaticLibrary). 103 filter!(not!isObjectFile). 104 map!(a => buildPath(dubPackage.path, a)) 105 .array; 106 107 auto func = allTogether ? &dlangPackageObjectFilesTogether : &dlangPackageObjectFiles; 108 targets ~= func(files, flags, importPaths, stringImportPaths, projDir); 109 // add any object files that are meant to be linked 110 targets ~= dubPackage 111 .files 112 .filter!isObjectFile 113 .map!(a => Target(inDubPackagePath(dubPackage.path, a))) 114 .array; 115 } 116 117 return targets ~ allStaticLibrarySources; 118 } 119 120 TargetName targetName() @safe const pure nothrow { 121 return TargetName(packages[0].targetFileName); 122 } 123 124 string targetType() @safe const pure nothrow { 125 return packages[0].targetType; 126 } 127 128 string[] mainLinkerFlags() @safe pure nothrow const { 129 import std.array: join; 130 131 const pack = packages[0]; 132 return (pack.targetType == "library" || pack.targetType == "staticLibrary") ? ["-lib"] : []; 133 } 134 135 string[] linkerFlags() @safe const pure nothrow { 136 const allLibs = packages.map!(a => a.libs).join; 137 return 138 allLibs.map!(a => "-L-l" ~ a).array ~ 139 packages.map!(a => a.lflags.map!(b => "-L" ~ b)).join; 140 } 141 142 string[] allImportPaths() @safe nothrow const { 143 import reggae.config: options; 144 145 string[] paths; 146 auto rng = packages.map!(a => a.packagePaths(a.importPaths)); 147 foreach(p; rng) paths ~= p; 148 return paths ~ options.projectPath; 149 } 150 151 // must be at the very end 152 private Target[] allStaticLibrarySources() @trusted nothrow const pure { 153 import std.algorithm: filter, map; 154 import std.array: array, join; 155 return packages. 156 map!(a => cast(string[])a.files.filter!isStaticLibrary.array). 157 join. 158 map!(a => Target(a)). 159 array; 160 } 161 } 162 163 164 private auto packagePaths(in DubPackage dubPackage, in string[] paths) @trusted nothrow { 165 return paths.map!(a => buildPath(dubPackage.path, a)).array; 166 } 167 168 //@trusted because of map.array 169 private string[] allOf(alias F)(in DubPackage pack, in DubPackage[] packages) @trusted nothrow { 170 171 import std.algorithm: find; 172 import std.array: array, empty, front; 173 174 string[] result; 175 //foreach(d; [pack.name] ~ pack.dependencies) doesn't compile with CTFE 176 //it seems to have to do with constness, replace string[] with const(string)[] 177 //and it won't compile 178 const dependencies = [pack.name] ~ pack.dependencies; 179 foreach(dependency; dependencies) { 180 181 auto depPack = packages.find!(a => a.name == dependency); 182 if(!depPack.empty) { 183 result ~= F(depPack.front).array; 184 } 185 } 186 return result; 187 }