1 module dud.pkgdescription.joining; 2 3 import std.array : array, empty, front; 4 import std.algorithm.searching : canFind, find; 5 import std.algorithm.sorting : sort; 6 import std.algorithm.iteration : uniq, filter, each; 7 import std.exception : enforce; 8 import std.format : format; 9 import std.traits : FieldNameTuple; 10 import std.typecons : nullable, Nullable, apply; 11 12 import dud.pkgdescription; 13 import dud.pkgdescription.exception; 14 import dud.pkgdescription.compare; 15 import dud.pkgdescription.duplicate; 16 import dud.pkgdescription.duplicate : ddup = dup; 17 18 @safe: 19 20 PackageDescription expandConfiguration(ref const(PackageDescription) pkg, 21 string confName) 22 { 23 PackageDescription ret = pkg.ddup(); 24 () @trusted { ret.configurations.clear(); }(); 25 26 const(PackageDescription) conf = findConfiguration(pkg, confName); 27 joinPackageDescription(ret, pkg, conf); 28 return ret; 29 } 30 31 PackageDescription expandBuildType(ref const(PackageDescription) pkg, 32 string buildTypeName) 33 { 34 PackageDescription ret = pkg.ddup(); 35 const(BuildType) buildType = findBuildType(pkg, buildTypeName); 36 joinPackageDescription(ret, pkg, buildType.pkg); 37 return ret; 38 } 39 40 void joinPackageDescription(ref PackageDescription ret, 41 ref const(PackageDescription) orig, ref const(PackageDescription) conf) 42 { 43 import dud.pkgdescription.helper : isMem; 44 static foreach(mem; FieldNameTuple!PackageDescription) { 45 // override with conf 46 static if(canFind( 47 [ isMem!"systemDependencies", isMem!"ddoxTool", isMem!"platforms" 48 , isMem!"buildOptions" 49 ], mem)) 50 { 51 __traits(getMember, ret, mem) = 52 dud.pkgdescription.duplicate.dup( 53 __traits(getMember, conf, mem)); 54 } else static if(canFind( 55 [ isMem!"targetPath", isMem!"targetName", isMem!"mainSourceFile" 56 , isMem!"workingDirectory" 57 ], mem)) 58 { 59 __traits(getMember, ret, mem) = selectIfEmpty( 60 __traits(getMember, ret, mem), 61 __traits(getMember, conf, mem)); 62 } else static if(canFind( 63 [ isMem!"dflags", isMem!"lflags", isMem!"versions" 64 , isMem!"importPaths", isMem!"sourcePaths", isMem!"sourceFiles" 65 , isMem!"stringImportPaths" , isMem!"excludedSourceFiles" 66 , isMem!"copyFiles" , isMem!"preGenerateCommands" 67 , isMem!"postGenerateCommands" , isMem!"preBuildCommands" 68 , isMem!"postBuildCommands" , isMem!"preRunCommands" 69 , isMem!"postRunCommands", isMem!"libs", isMem!"versionFilters" 70 , isMem!"debugVersionFilters", isMem!"debugVersions" 71 , isMem!"toolchainRequirements", isMem!"dependencies" 72 , isMem!"subConfigurations", isMem!"buildTypes" 73 , isMem!"targetType" 74 ], mem)) 75 { 76 __traits(getMember, ret, mem) = 77 join(__traits(getMember, orig, mem), 78 __traits(getMember, conf, mem)); 79 } else static if(canFind( 80 [ isMem!"name", isMem!"description", isMem!"homepage" 81 , isMem!"license", isMem!"authors"//, isMem!"version_" 82 , isMem!"copyright", isMem!"subPackages" 83 , isMem!"ddoxFilterArgs", isMem!"configurations" 84 , isMem!"buildRequirements" 85 ], mem)) 86 { 87 // global options not allowed to change by configuration 88 } else { 89 pragma(msg, mem); 90 } 91 } 92 } 93 94 String selectIfEmpty(const(String) a, const(String) b) { 95 return b.platforms.empty 96 ? a.ddup() 97 : b.ddup(); 98 } 99 100 UnprocessedPath selectIfEmpty(const(UnprocessedPath) a, 101 const(UnprocessedPath) b) 102 { 103 return b.path.empty 104 ? a.ddup() 105 : b.ddup(); 106 } 107 108 Path selectIfEmpty(const(Path) a, const(Path) b) { 109 return b.platforms.empty 110 ? a.ddup() 111 : b.ddup(); 112 } 113 114 string selectIfEmpty(const(string) a, const(string) b) { 115 return b.empty 116 ? a.ddup() 117 : b.ddup(); 118 } 119 120 SubConfigs join(ref const(SubConfigs) a, ref const(SubConfigs) b) { 121 SubConfigs ret = b.ddup(); 122 a.unspecifiedPlatform.byKeyValue() 123 .filter!(kv => kv.key !in ret.unspecifiedPlatform) 124 .each!(kv => ret.unspecifiedPlatform[kv.key] = kv.value); 125 126 foreach(key, value; a.configs) { 127 if(key !in ret.configs) { 128 ret.configs[key] = string[string].init; 129 } 130 foreach(key2, value2; value) { 131 if(key2 !in ret.configs[key]) { 132 ret.configs[key][key2] = value2.ddup(); 133 } 134 } 135 } 136 return ret; 137 } 138 139 BuildType[string] join(const(BuildType[string]) a, 140 const(BuildType[string]) b) 141 { 142 BuildType[string] ret = b.ddup(); 143 a.byKeyValue() 144 .filter!(it => it.key !in ret) 145 .each!(bt => ret[bt.key] = bt.value.ddup()); 146 return ret; 147 } 148 149 Dependency[] join(const(Dependency[]) a, const(Dependency[]) b) { 150 Dependency[] ret = b.ddup(); 151 a.filter!(dep => 152 !canFind!((g, h) => g.name == h.name 153 && g.platforms == h.platforms)(ret, dep)) 154 .each!(dep => ret ~= dep.ddup()); 155 return ret; 156 } 157 158 ToolchainRequirement[Toolchain] join(const(ToolchainRequirement[Toolchain]) a, 159 const(ToolchainRequirement[Toolchain]) b) 160 { 161 ToolchainRequirement[Toolchain] ret = dud.pkgdescription.duplicate.dup(a); 162 b.byKeyValue() 163 .each!(it => ret[it.key] = 164 dud.pkgdescription.duplicate.dup(it.value)); 165 return ret; 166 } 167 168 Paths join(const(Paths) a, const(Paths) b) { 169 Paths ret = dud.pkgdescription.duplicate.dup(a); 170 b.platforms 171 .filter!(it => 172 !canFind!((g, h) => areEqual(g, h))(a.platforms, it)) 173 .each!(it => ret.platforms ~= dud.pkgdescription.duplicate.dup(it)); 174 return ret; 175 } 176 177 TargetType join(const(TargetType) a, const(TargetType) b) { 178 return b != TargetType.autodetect 179 ? b 180 : a; 181 } 182 183 Strings join(const(Strings) a, const(Strings) b) { 184 Strings ret = dud.pkgdescription.duplicate.dup(a); 185 b.platforms 186 .filter!(it => 187 !canFind!((g, h) => areEqual(g, h))(a.platforms, it)) 188 .each!(it => ret.platforms ~= dud.pkgdescription.duplicate.dup(it)); 189 return ret; 190 } 191 192 string[] join(const(string[]) a, const(string[]) b) { 193 string[] ret = (dud.pkgdescription.duplicate.dup(a) 194 ~ dud.pkgdescription.duplicate.dup(b)).sort.uniq.array; 195 return ret; 196 } 197 198 // 199 // Helper 200 // 201 202 const(BuildType) findBuildType(const PackageDescription pkg, 203 string buildTypeName) 204 { 205 const(BuildType)* ret = buildTypeName in pkg.buildTypes; 206 enforce!UnknownBuildType(ret !is null, 207 format("'%s' is a unknown buildType of package '%s'", pkg.name)); 208 return *ret; 209 } 210 211 const(PackageDescription) findConfiguration(const PackageDescription pkg, 212 string confName) 213 { 214 const(PackageDescription)* ret = confName in pkg.configurations; 215 enforce!UnknownConfiguration(ret !is null, 216 format("'%s' is a unknown configuration of package '%s'", pkg.name)); 217 return *ret; 218 }