1 /** 2 SDL format support for PackageRecipe 3 4 Copyright: © 2014-2015 rejectedsoftware e.K. 5 License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. 6 Authors: Sönke Ludwig 7 */ 8 module dub.recipe.sdl; 9 10 import dub.compilers.compiler; 11 import dub.dependency; 12 import dub.internal.logging; 13 import dub.internal.sdlang; 14 import dub.internal.vibecompat.inet.path; 15 import dub.recipe.packagerecipe; 16 17 import std.algorithm : map; 18 import std.array : array; 19 import std.conv; 20 import std.string : startsWith; 21 22 23 void parseSDL(ref PackageRecipe recipe, string sdl, string parent_name, string filename) 24 { 25 parseSDL(recipe, parseSource(sdl, filename), parent_name); 26 } 27 28 void parseSDL(ref PackageRecipe recipe, Tag sdl, string parent_name) 29 { 30 Tag[] subpacks; 31 Tag[] configs; 32 33 // parse top-level fields 34 foreach (n; sdl.all.tags) { 35 enforceSDL(n.name.length > 0, "Anonymous tags are not allowed at the root level.", n); 36 switch (n.fullName) { 37 default: break; 38 case "name": recipe.name = n.stringTagValue; break; 39 case "version": recipe.version_ = n.stringTagValue; break; 40 case "description": recipe.description = n.stringTagValue; break; 41 case "homepage": recipe.homepage = n.stringTagValue; break; 42 case "authors": recipe.authors ~= n.stringArrayTagValue; break; 43 case "copyright": recipe.copyright = n.stringTagValue; break; 44 case "license": recipe.license = n.stringTagValue; break; 45 case "subPackage": subpacks ~= n; break; 46 case "configuration": configs ~= n; break; 47 case "buildType": 48 auto name = n.stringTagValue(true); 49 BuildSettingsTemplate bt; 50 parseBuildSettings(n, bt, parent_name); 51 recipe.buildTypes[name] = bt; 52 break; 53 case "toolchainRequirements": 54 parseToolchainRequirements(recipe.toolchainRequirements, n); 55 break; 56 case "x:ddoxFilterArgs": recipe.ddoxFilterArgs ~= n.stringArrayTagValue; break; 57 case "x:ddoxTool": recipe.ddoxTool = n.stringTagValue; break; 58 } 59 } 60 61 enforceSDL(recipe.name.length > 0, "The package \"name\" field is missing or empty.", sdl); 62 string full_name = parent_name.length ? parent_name ~ ":" ~ recipe.name : recipe.name; 63 64 // parse general build settings 65 parseBuildSettings(sdl, recipe.buildSettings, full_name); 66 67 // parse configurations 68 recipe.configurations.length = configs.length; 69 foreach (i, n; configs) { 70 parseConfiguration(n, recipe.configurations[i], full_name); 71 } 72 73 // finally parse all sub packages 74 recipe.subPackages.length = subpacks.length; 75 foreach (i, n; subpacks) { 76 if (n.values.length) { 77 recipe.subPackages[i].path = n.stringTagValue; 78 } else { 79 enforceSDL(n.attributes.length == 0, "No attributes allowed for inline sub package definitions.", n); 80 parseSDL(recipe.subPackages[i].recipe, n, full_name); 81 } 82 } 83 } 84 85 Tag toSDL(const scope ref PackageRecipe recipe) 86 { 87 Tag ret = new Tag; 88 void add(T)(string field, T value) { ret.add(new Tag(null, field, [Value(value)])); } 89 add("name", recipe.name); 90 if (recipe.version_.length) add("version", recipe.version_); 91 if (recipe.description.length) add("description", recipe.description); 92 if (recipe.homepage.length) add("homepage", recipe.homepage); 93 if (recipe.authors.length) ret.add(new Tag(null, "authors", recipe.authors.map!(a => Value(a)).array)); 94 if (recipe.copyright.length) add("copyright", recipe.copyright); 95 if (recipe.license.length) add("license", recipe.license); 96 foreach (name, settings; recipe.buildTypes) { 97 auto t = new Tag(null, "buildType", [Value(name)]); 98 t.add(settings.toSDL()); 99 ret.add(t); 100 } 101 if (!recipe.toolchainRequirements.empty) { 102 ret.add(toSDL(recipe.toolchainRequirements)); 103 } 104 if (recipe.ddoxFilterArgs.length) 105 ret.add(new Tag("x", "ddoxFilterArgs", recipe.ddoxFilterArgs.map!(a => Value(a)).array)); 106 if (recipe.ddoxTool.length) ret.add(new Tag("x", "ddoxTool", [Value(recipe.ddoxTool)])); 107 ret.add(recipe.buildSettings.toSDL()); 108 foreach(config; recipe.configurations) 109 ret.add(config.toSDL()); 110 foreach (i, subPackage; recipe.subPackages) { 111 if (subPackage.path !is null) { 112 add("subPackage", subPackage.path); 113 } else { 114 auto t = subPackage.recipe.toSDL(); 115 t.name = "subPackage"; 116 ret.add(t); 117 } 118 } 119 return ret; 120 } 121 122 private void parseBuildSettings(Tag settings, ref BuildSettingsTemplate bs, string package_name) 123 { 124 foreach (setting; settings.all.tags) 125 parseBuildSetting(setting, bs, package_name); 126 } 127 128 private void parseBuildSetting(Tag setting, ref BuildSettingsTemplate bs, string package_name) 129 { 130 switch (setting.fullName) { 131 default: break; 132 case "dependency": parseDependency(setting, bs, package_name); break; 133 case "systemDependencies": bs.systemDependencies = setting.stringTagValue; break; 134 case "targetType": bs.targetType = setting.stringTagValue.to!TargetType; break; 135 case "targetName": bs.targetName = setting.stringTagValue; break; 136 case "targetPath": bs.targetPath = setting.stringTagValue; break; 137 case "workingDirectory": bs.workingDirectory = setting.stringTagValue; break; 138 case "subConfiguration": 139 auto args = setting.stringArrayTagValue; 140 enforceSDL(args.length == 2, "Expecting package and configuration names as arguments.", setting); 141 bs.subConfigurations[expandPackageName(args[0], package_name, setting)] = args[1]; 142 break; 143 case "dflags": setting.parsePlatformStringArray(bs.dflags); break; 144 case "lflags": setting.parsePlatformStringArray(bs.lflags); break; 145 case "libs": setting.parsePlatformStringArray(bs.libs); break; 146 case "sourceFiles": setting.parsePlatformStringArray(bs.sourceFiles); break; 147 case "sourcePaths": setting.parsePlatformStringArray(bs.sourcePaths); break; 148 case "excludedSourceFiles": setting.parsePlatformStringArray(bs.excludedSourceFiles); break; 149 case "mainSourceFile": bs.mainSourceFile = setting.stringTagValue; break; 150 case "injectSourceFiles": setting.parsePlatformStringArray(bs.injectSourceFiles); break; 151 case "copyFiles": setting.parsePlatformStringArray(bs.copyFiles); break; 152 case "extraDependencyFiles": setting.parsePlatformStringArray(bs.extraDependencyFiles); break; 153 case "versions": setting.parsePlatformStringArray(bs.versions); break; 154 case "debugVersions": setting.parsePlatformStringArray(bs.debugVersions); break; 155 case "x:versionFilters": setting.parsePlatformStringArray(bs.versionFilters); break; 156 case "x:debugVersionFilters": setting.parsePlatformStringArray(bs.debugVersionFilters); break; 157 case "importPaths": setting.parsePlatformStringArray(bs.importPaths); break; 158 case "stringImportPaths": setting.parsePlatformStringArray(bs.stringImportPaths); break; 159 case "preGenerateCommands": setting.parsePlatformStringArray(bs.preGenerateCommands); break; 160 case "postGenerateCommands": setting.parsePlatformStringArray(bs.postGenerateCommands); break; 161 case "preBuildCommands": setting.parsePlatformStringArray(bs.preBuildCommands); break; 162 case "postBuildCommands": setting.parsePlatformStringArray(bs.postBuildCommands); break; 163 case "preRunCommands": setting.parsePlatformStringArray(bs.preRunCommands); break; 164 case "postRunCommands": setting.parsePlatformStringArray(bs.postRunCommands); break; 165 case "environments": setting.parsePlatformStringAA(bs.environments); break; 166 case "buildEnvironments": setting.parsePlatformStringAA(bs.buildEnvironments); break; 167 case "runEnvironments": setting.parsePlatformStringAA(bs.runEnvironments); break; 168 case "preGenerateEnvironments": setting.parsePlatformStringAA(bs.preGenerateEnvironments); break; 169 case "postGenerateEnvironments": setting.parsePlatformStringAA(bs.postGenerateEnvironments); break; 170 case "preBuildEnvironments": setting.parsePlatformStringAA(bs.preBuildEnvironments); break; 171 case "postBuildEnvironments": setting.parsePlatformStringAA(bs.postBuildEnvironments); break; 172 case "preRunEnvironments": setting.parsePlatformStringAA(bs.preRunEnvironments); break; 173 case "postRunEnvironments": setting.parsePlatformStringAA(bs.postRunEnvironments); break; 174 case "buildRequirements": setting.parsePlatformEnumArray!BuildRequirement(bs.buildRequirements); break; 175 case "buildOptions": setting.parsePlatformEnumArray!BuildOption(bs.buildOptions); break; 176 } 177 } 178 179 private void parseDependency(Tag t, ref BuildSettingsTemplate bs, string package_name) 180 { 181 enforceSDL(t.values.length != 0, "Missing dependency name.", t); 182 enforceSDL(t.values.length == 1, "Multiple dependency names.", t); 183 auto pkg = expandPackageName(t.values[0].get!string, package_name, t); 184 enforceSDL(pkg !in bs.dependencies, "The dependency '"~pkg~"' is specified more than once.", t); 185 186 Dependency dep = Dependency.any; 187 auto attrs = t.attributes; 188 189 if ("path" in attrs) { 190 dep = Dependency(NativePath(attrs["path"][0].value.get!string)); 191 } else if ("repository" in attrs) { 192 enforceSDL("version" in attrs, "Missing version specification.", t); 193 194 dep = Dependency(Repository(attrs["repository"][0].value.get!string, 195 attrs["version"][0].value.get!string)); 196 } else { 197 enforceSDL("version" in attrs, "Missing version specification.", t); 198 dep = Dependency(attrs["version"][0].value.get!string); 199 } 200 201 if ("optional" in attrs) 202 dep.optional = attrs["optional"][0].value.get!bool; 203 204 if ("default" in attrs) 205 dep.default_ = attrs["default"][0].value.get!bool; 206 207 bs.dependencies[pkg] = dep; 208 209 BuildSettingsTemplate dbs; 210 parseBuildSettings(t, bs.dependencies[pkg].settings, package_name); 211 } 212 213 private void parseConfiguration(Tag t, ref ConfigurationInfo ret, string package_name) 214 { 215 ret.name = t.stringTagValue(true); 216 foreach (f; t.tags) { 217 switch (f.fullName) { 218 default: parseBuildSetting(f, ret.buildSettings, package_name); break; 219 case "platforms": ret.platforms ~= f.stringArrayTagValue; break; 220 } 221 } 222 } 223 224 private Tag toSDL(const scope ref ConfigurationInfo config) 225 { 226 auto ret = new Tag(null, "configuration", [Value(config.name)]); 227 if (config.platforms.length) ret.add(new Tag(null, "platforms", config.platforms[].map!(p => Value(p)).array)); 228 ret.add(config.buildSettings.toSDL()); 229 return ret; 230 } 231 232 private Tag[] toSDL(const scope ref BuildSettingsTemplate bs) 233 { 234 Tag[] ret; 235 void add(string name, string value, string namespace = null) { ret ~= new Tag(namespace, name, [Value(value)]); } 236 void adda(string name, string suffix, in string[] values, string namespace = null) { 237 ret ~= new Tag(namespace, name, values[].map!(v => Value(v)).array, 238 suffix.length ? [new Attribute(null, "platform", Value(suffix))] : null); 239 } 240 void addaa(string name, string suffix, in string[string] values, string namespace = null) { 241 foreach (k, v; values) { 242 ret ~= new Tag(namespace, name, [Value(k), Value(v)], 243 suffix.length ? [new Attribute(null, "platform", Value(suffix))] : null); 244 } 245 } 246 247 string[] toNameArray(T, U)(U bits) if(is(T == enum)) { 248 string[] ret; 249 foreach (m; __traits(allMembers, T)) 250 if (bits & __traits(getMember, T, m)) 251 ret ~= m; 252 return ret; 253 } 254 255 foreach (pack, d; bs.dependencies) { 256 Attribute[] attribs; 257 d.visit!( 258 (const Repository r) { 259 attribs ~= new Attribute(null, "repository", Value(r.toString())); 260 attribs ~= new Attribute(null, "version", Value(r.ref_)); 261 }, 262 (const NativePath p) { 263 attribs ~= new Attribute(null, "path", Value(p.toString())); 264 }, 265 (const VersionRange v) { 266 attribs ~= new Attribute(null, "version", Value(v.toString())); 267 }, 268 ); 269 if (d.optional) attribs ~= new Attribute(null, "optional", Value(true)); 270 auto t = new Tag(null, "dependency", [Value(pack)], attribs); 271 if (d.settings !is typeof(d.settings).init) 272 t.add(d.settings.toSDL()); 273 ret ~= t; 274 } 275 if (bs.systemDependencies !is null) add("systemDependencies", bs.systemDependencies); 276 if (bs.targetType != TargetType.autodetect) add("targetType", bs.targetType.to!string()); 277 if (bs.targetPath.length) add("targetPath", bs.targetPath); 278 if (bs.targetName.length) add("targetName", bs.targetName); 279 if (bs.workingDirectory.length) add("workingDirectory", bs.workingDirectory); 280 if (bs.mainSourceFile.length) add("mainSourceFile", bs.mainSourceFile); 281 foreach (pack, conf; bs.subConfigurations) ret ~= new Tag(null, "subConfiguration", [Value(pack), Value(conf)]); 282 foreach (suffix, arr; bs.dflags) adda("dflags", suffix, arr); 283 foreach (suffix, arr; bs.lflags) adda("lflags", suffix, arr); 284 foreach (suffix, arr; bs.libs) adda("libs", suffix, arr); 285 foreach (suffix, arr; bs.sourceFiles) adda("sourceFiles", suffix, arr); 286 foreach (suffix, arr; bs.sourcePaths) adda("sourcePaths", suffix, arr); 287 foreach (suffix, arr; bs.excludedSourceFiles) adda("excludedSourceFiles", suffix, arr); 288 foreach (suffix, arr; bs.injectSourceFiles) adda("injectSourceFiles", suffix, arr); 289 foreach (suffix, arr; bs.copyFiles) adda("copyFiles", suffix, arr); 290 foreach (suffix, arr; bs.extraDependencyFiles) adda("extraDependencyFiles", suffix, arr); 291 foreach (suffix, arr; bs.versions) adda("versions", suffix, arr); 292 foreach (suffix, arr; bs.debugVersions) adda("debugVersions", suffix, arr); 293 foreach (suffix, arr; bs.versionFilters) adda("versionFilters", suffix, arr, "x"); 294 foreach (suffix, arr; bs.debugVersionFilters) adda("debugVersionFilters", suffix, arr, "x"); 295 foreach (suffix, arr; bs.importPaths) adda("importPaths", suffix, arr); 296 foreach (suffix, arr; bs.stringImportPaths) adda("stringImportPaths", suffix, arr); 297 foreach (suffix, arr; bs.preGenerateCommands) adda("preGenerateCommands", suffix, arr); 298 foreach (suffix, arr; bs.postGenerateCommands) adda("postGenerateCommands", suffix, arr); 299 foreach (suffix, arr; bs.preBuildCommands) adda("preBuildCommands", suffix, arr); 300 foreach (suffix, arr; bs.postBuildCommands) adda("postBuildCommands", suffix, arr); 301 foreach (suffix, arr; bs.preRunCommands) adda("preRunCommands", suffix, arr); 302 foreach (suffix, arr; bs.postRunCommands) adda("postRunCommands", suffix, arr); 303 foreach (suffix, aa; bs.environments) addaa("environments", suffix, aa); 304 foreach (suffix, aa; bs.buildEnvironments) addaa("buildEnvironments", suffix, aa); 305 foreach (suffix, aa; bs.runEnvironments) addaa("runEnvironments", suffix, aa); 306 foreach (suffix, aa; bs.preGenerateEnvironments) addaa("preGenerateEnvironments", suffix, aa); 307 foreach (suffix, aa; bs.postGenerateEnvironments) addaa("postGenerateEnvironments", suffix, aa); 308 foreach (suffix, aa; bs.preBuildEnvironments) addaa("preBuildEnvironments", suffix, aa); 309 foreach (suffix, aa; bs.postBuildEnvironments) addaa("postBuildEnvironments", suffix, aa); 310 foreach (suffix, aa; bs.preRunEnvironments) addaa("preRunEnvironments", suffix, aa); 311 foreach (suffix, aa; bs.postRunEnvironments) addaa("postRunEnvironments", suffix, aa); 312 foreach (suffix, bits; bs.buildRequirements) adda("buildRequirements", suffix, toNameArray!BuildRequirement(bits)); 313 foreach (suffix, bits; bs.buildOptions) adda("buildOptions", suffix, toNameArray!BuildOption(bits)); 314 return ret; 315 } 316 317 private void parseToolchainRequirements(ref ToolchainRequirements tr, Tag tag) 318 { 319 foreach (attr; tag.attributes) 320 tr.addRequirement(attr.name, attr.value.get!string); 321 } 322 323 private Tag toSDL(const ref ToolchainRequirements tr) 324 { 325 Attribute[] attrs; 326 if (tr.dub != Dependency.any) attrs ~= new Attribute("dub", Value(tr.dub.toString())); 327 if (tr.frontend != Dependency.any) attrs ~= new Attribute("frontend", Value(tr.frontend.toString())); 328 if (tr.dmd != Dependency.any) attrs ~= new Attribute("dmd", Value(tr.dmd.toString())); 329 if (tr.ldc != Dependency.any) attrs ~= new Attribute("ldc", Value(tr.ldc.toString())); 330 if (tr.gdc != Dependency.any) attrs ~= new Attribute("gdc", Value(tr.gdc.toString())); 331 return new Tag(null, "toolchainRequirements", null, attrs); 332 } 333 334 private string expandPackageName(string name, string parent_name, Tag tag) 335 { 336 import std.algorithm : canFind; 337 import std.string : format; 338 if (name.startsWith(":")) { 339 enforceSDL(!parent_name.canFind(':'), format("Short-hand packages syntax not allowed within sub packages: %s -> %s", parent_name, name), tag); 340 return parent_name ~ name; 341 } else return name; 342 } 343 344 private string stringTagValue(Tag t, bool allow_child_tags = false) 345 { 346 import std.string : format; 347 enforceSDL(t.values.length > 0, format("Missing string value for '%s'.", t.fullName), t); 348 enforceSDL(t.values.length == 1, format("Expected only one value for '%s'.", t.fullName), t); 349 enforceSDL(t.values[0].peek!string !is null, format("Expected value of type string for '%s'.", t.fullName), t); 350 enforceSDL(allow_child_tags || t.tags.length == 0, format("No child tags allowed for '%s'.", t.fullName), t); 351 // Q: should attributes be disallowed, or just ignored for forward compatibility reasons? 352 //enforceSDL(t.attributes.length == 0, format("No attributes allowed for '%s'.", t.fullName), t); 353 return t.values[0].get!string; 354 } 355 356 private string[] stringArrayTagValue(Tag t, bool allow_child_tags = false) 357 { 358 import std.string : format; 359 enforceSDL(allow_child_tags || t.tags.length == 0, format("No child tags allowed for '%s'.", t.fullName), t); 360 // Q: should attributes be disallowed, or just ignored for forward compatibility reasons? 361 //enforceSDL(t.attributes.length == 0, format("No attributes allowed for '%s'.", t.fullName), t); 362 363 string[] ret; 364 foreach (v; t.values) { 365 enforceSDL(t.values[0].peek!string !is null, format("Values for '%s' must be strings.", t.fullName), t); 366 ret ~= v.get!string; 367 } 368 return ret; 369 } 370 371 private void parsePlatformStringArray(Tag t, ref string[][string] dst) 372 { 373 string platform; 374 if ("platform" in t.attributes) 375 platform = t.attributes["platform"][0].value.get!string; 376 dst[platform] ~= t.values.map!(v => v.get!string).array; 377 } 378 private void parsePlatformStringAA(Tag t, ref string[string][string] dst) 379 { 380 import std.string : format; 381 string platform; 382 if ("platform" in t.attributes) 383 platform = t.attributes["platform"][0].value.get!string; 384 enforceSDL(t.values.length == 2, format("Values for '%s' must be 2 required.", t.fullName), t); 385 enforceSDL(t.values[0].peek!string !is null, format("Values for '%s' must be strings.", t.fullName), t); 386 enforceSDL(t.values[1].peek!string !is null, format("Values for '%s' must be strings.", t.fullName), t); 387 dst[platform][t.values[0].get!string] = t.values[1].get!string; 388 } 389 390 private void parsePlatformEnumArray(E, Es)(Tag t, ref Es[string] dst) 391 { 392 string platform; 393 if ("platform" in t.attributes) 394 platform = t.attributes["platform"][0].value.get!string; 395 foreach (v; t.values) { 396 if (platform !in dst) dst[platform] = Es.init; 397 dst[platform] |= v.get!string.to!E; 398 } 399 } 400 401 private void enforceSDL(bool condition, lazy string message, Tag tag, string file = __FILE__, int line = __LINE__) 402 { 403 import std.string : format; 404 if (!condition) { 405 throw new Exception(format("%s(%s): Error: %s", tag.location.file, tag.location.line + 1, message), file, line); 406 } 407 } 408 409 410 unittest { // test all possible fields 411 auto sdl = 412 `name "projectname"; 413 description "project description"; 414 homepage "http://example.com" 415 authors "author 1" "author 2" 416 authors "author 3" 417 copyright "copyright string" 418 license "license string" 419 version "1.0.0" 420 subPackage { 421 name "subpackage1" 422 } 423 subPackage { 424 name "subpackage2" 425 dependency "projectname:subpackage1" version="*" 426 } 427 subPackage "pathsp3" 428 configuration "config1" { 429 platforms "windows" "linux" 430 targetType "library" 431 } 432 configuration "config2" { 433 platforms "windows-x86" 434 targetType "executable" 435 } 436 buildType "debug" { 437 dflags "-g" "-debug" 438 } 439 buildType "release" { 440 dflags "-release" "-O" 441 } 442 toolchainRequirements dub="~>1.11.0" dmd="~>2.082" 443 x:ddoxFilterArgs "-arg1" "-arg2" 444 x:ddoxFilterArgs "-arg3" 445 x:ddoxTool "ddoxtool" 446 447 dependency ":subpackage1" optional=false path="." { 448 dflags "-g" "-debug" 449 } 450 dependency "somedep" version="1.0.0" optional=true 451 systemDependencies "system dependencies" 452 targetType "executable" 453 targetName "target name" 454 targetPath "target path" 455 workingDirectory "working directory" 456 subConfiguration ":subpackage2" "library" 457 buildRequirements "allowWarnings" "silenceDeprecations" 458 buildOptions "verbose" "ignoreUnknownPragmas" 459 libs "lib1" "lib2" 460 libs "lib3" 461 sourceFiles "source1" "source2" 462 sourceFiles "source3" 463 sourcePaths "sourcepath1" "sourcepath2" 464 sourcePaths "sourcepath3" 465 excludedSourceFiles "excluded1" "excluded2" 466 excludedSourceFiles "excluded3" 467 mainSourceFile "main source" 468 injectSourceFiles "finalbinarysourcefile.d" "extrafile" 469 copyFiles "copy1" "copy2" 470 copyFiles "copy3" 471 extraDependencyFiles "extradepfile1" "extradepfile2" 472 extraDependencyFiles "extradepfile3" 473 versions "version1" "version2" 474 versions "version3" 475 debugVersions "debug1" "debug2" 476 debugVersions "debug3" 477 x:versionFilters "version1" "version2" 478 x:versionFilters "version3" 479 x:versionFilters 480 x:debugVersionFilters "debug1" "debug2" 481 x:debugVersionFilters "debug3" 482 x:debugVersionFilters 483 importPaths "import1" "import2" 484 importPaths "import3" 485 stringImportPaths "string1" "string2" 486 stringImportPaths "string3" 487 preGenerateCommands "preg1" "preg2" 488 preGenerateCommands "preg3" 489 postGenerateCommands "postg1" "postg2" 490 postGenerateCommands "postg3" 491 preBuildCommands "preb1" "preb2" 492 preBuildCommands "preb3" 493 postBuildCommands "postb1" "postb2" 494 postBuildCommands "postb3" 495 preRunCommands "prer1" "prer2" 496 preRunCommands "prer3" 497 postRunCommands "postr1" "postr2" 498 postRunCommands "postr3" 499 environments "Var1" "env" 500 buildEnvironments "Var2" "buildEnv" 501 runEnvironments "Var3" "runEnv" 502 preGenerateEnvironments "Var4" "preGenEnv" 503 postGenerateEnvironments "Var5" "postGenEnv" 504 preBuildEnvironments "Var6" "preBuildEnv" 505 postBuildEnvironments "Var7" "postBuildEnv" 506 preRunEnvironments "Var8" "preRunEnv" 507 postRunEnvironments "Var9" "postRunEnv" 508 dflags "df1" "df2" 509 dflags "df3" 510 lflags "lf1" "lf2" 511 lflags "lf3" 512 `; 513 PackageRecipe rec1; 514 parseSDL(rec1, sdl, null, "testfile"); 515 PackageRecipe rec; 516 parseSDL(rec, rec1.toSDL(), null); // verify that all fields are serialized properly 517 518 assert(rec.name == "projectname"); 519 assert(rec.description == "project description"); 520 assert(rec.homepage == "http://example.com"); 521 assert(rec.authors == ["author 1", "author 2", "author 3"]); 522 assert(rec.copyright == "copyright string"); 523 assert(rec.license == "license string"); 524 assert(rec.version_ == "1.0.0"); 525 assert(rec.subPackages.length == 3); 526 assert(rec.subPackages[0].path == ""); 527 assert(rec.subPackages[0].recipe.name == "subpackage1"); 528 assert(rec.subPackages[1].path == ""); 529 assert(rec.subPackages[1].recipe.name == "subpackage2"); 530 assert(rec.subPackages[1].recipe.buildSettings.dependencies.length == 1); 531 assert("projectname:subpackage1" in rec.subPackages[1].recipe.buildSettings.dependencies); 532 assert(rec.subPackages[2].path == "pathsp3"); 533 assert(rec.configurations.length == 2); 534 assert(rec.configurations[0].name == "config1"); 535 assert(rec.configurations[0].platforms == ["windows", "linux"]); 536 assert(rec.configurations[0].buildSettings.targetType == TargetType.library); 537 assert(rec.configurations[1].name == "config2"); 538 assert(rec.configurations[1].platforms == ["windows-x86"]); 539 assert(rec.configurations[1].buildSettings.targetType == TargetType.executable); 540 assert(rec.buildTypes.length == 2); 541 assert(rec.buildTypes["debug"].dflags == ["": ["-g", "-debug"]]); 542 assert(rec.buildTypes["release"].dflags == ["": ["-release", "-O"]]); 543 assert(rec.toolchainRequirements.dub == Dependency("~>1.11.0")); 544 assert(rec.toolchainRequirements.frontend == Dependency.any); 545 assert(rec.toolchainRequirements.dmd == Dependency("~>2.82.0")); 546 assert(rec.toolchainRequirements.ldc == Dependency.any); 547 assert(rec.toolchainRequirements.gdc == Dependency.any); 548 assert(rec.ddoxFilterArgs == ["-arg1", "-arg2", "-arg3"], rec.ddoxFilterArgs.to!string); 549 assert(rec.ddoxTool == "ddoxtool"); 550 assert(rec.buildSettings.dependencies.length == 2); 551 assert(rec.buildSettings.dependencies["projectname:subpackage1"].optional == false); 552 assert(rec.buildSettings.dependencies["projectname:subpackage1"].path == NativePath(".")); 553 assert(rec.buildSettings.dependencies["projectname:subpackage1"].settings.dflags == ["":["-g", "-debug"]]); 554 assert(rec.buildSettings.dependencies["somedep"].version_.toString() == "1.0.0"); 555 assert(rec.buildSettings.dependencies["somedep"].optional == true); 556 assert(rec.buildSettings.systemDependencies == "system dependencies"); 557 assert(rec.buildSettings.targetType == TargetType.executable); 558 assert(rec.buildSettings.targetName == "target name"); 559 assert(rec.buildSettings.targetPath == "target path"); 560 assert(rec.buildSettings.workingDirectory == "working directory"); 561 assert(rec.buildSettings.subConfigurations.length == 1); 562 assert(rec.buildSettings.subConfigurations["projectname:subpackage2"] == "library"); 563 assert(rec.buildSettings.buildRequirements == ["": cast(Flags!BuildRequirement)(BuildRequirement.allowWarnings | BuildRequirement.silenceDeprecations)]); 564 assert(rec.buildSettings.buildOptions == ["": cast(Flags!BuildOption)(BuildOption.verbose | BuildOption.ignoreUnknownPragmas)]); 565 assert(rec.buildSettings.libs == ["": ["lib1", "lib2", "lib3"]]); 566 assert(rec.buildSettings.sourceFiles == ["": ["source1", "source2", "source3"]]); 567 assert(rec.buildSettings.sourcePaths == ["": ["sourcepath1", "sourcepath2", "sourcepath3"]]); 568 assert(rec.buildSettings.excludedSourceFiles == ["": ["excluded1", "excluded2", "excluded3"]]); 569 assert(rec.buildSettings.mainSourceFile == "main source"); 570 assert(rec.buildSettings.sourceFiles == ["": ["source1", "source2", "source3"]]); 571 assert(rec.buildSettings.injectSourceFiles == ["": ["finalbinarysourcefile.d", "extrafile"]]); 572 assert(rec.buildSettings.extraDependencyFiles == ["": ["extradepfile1", "extradepfile2", "extradepfile3"]]); 573 assert(rec.buildSettings.versions == ["": ["version1", "version2", "version3"]]); 574 assert(rec.buildSettings.debugVersions == ["": ["debug1", "debug2", "debug3"]]); 575 assert(rec.buildSettings.versionFilters == ["": ["version1", "version2", "version3"]]); 576 assert(rec.buildSettings.debugVersionFilters == ["": ["debug1", "debug2", "debug3"]]); 577 assert(rec.buildSettings.importPaths == ["": ["import1", "import2", "import3"]]); 578 assert(rec.buildSettings.stringImportPaths == ["": ["string1", "string2", "string3"]]); 579 assert(rec.buildSettings.preGenerateCommands == ["": ["preg1", "preg2", "preg3"]]); 580 assert(rec.buildSettings.postGenerateCommands == ["": ["postg1", "postg2", "postg3"]]); 581 assert(rec.buildSettings.preBuildCommands == ["": ["preb1", "preb2", "preb3"]]); 582 assert(rec.buildSettings.postBuildCommands == ["": ["postb1", "postb2", "postb3"]]); 583 assert(rec.buildSettings.preRunCommands == ["": ["prer1", "prer2", "prer3"]]); 584 assert(rec.buildSettings.postRunCommands == ["": ["postr1", "postr2", "postr3"]]); 585 assert(rec.buildSettings.environments == ["": ["Var1": "env"]]); 586 assert(rec.buildSettings.buildEnvironments == ["": ["Var2": "buildEnv"]]); 587 assert(rec.buildSettings.runEnvironments == ["": ["Var3": "runEnv"]]); 588 assert(rec.buildSettings.preGenerateEnvironments == ["": ["Var4": "preGenEnv"]]); 589 assert(rec.buildSettings.postGenerateEnvironments == ["": ["Var5": "postGenEnv"]]); 590 assert(rec.buildSettings.preBuildEnvironments == ["": ["Var6": "preBuildEnv"]]); 591 assert(rec.buildSettings.postBuildEnvironments == ["": ["Var7": "postBuildEnv"]]); 592 assert(rec.buildSettings.preRunEnvironments == ["": ["Var8": "preRunEnv"]]); 593 assert(rec.buildSettings.postRunEnvironments == ["": ["Var9": "postRunEnv"]]); 594 assert(rec.buildSettings.dflags == ["": ["df1", "df2", "df3"]]); 595 assert(rec.buildSettings.lflags == ["": ["lf1", "lf2", "lf3"]]); 596 } 597 598 unittest { // test platform identifiers 599 auto sdl = 600 `name "testproject" 601 dflags "-a" "-b" platform="windows-x86" 602 dflags "-c" platform="windows-x86" 603 dflags "-e" "-f" 604 dflags "-g" 605 dflags "-h" "-i" platform="linux" 606 dflags "-j" platform="linux" 607 `; 608 PackageRecipe rec; 609 parseSDL(rec, sdl, null, "testfile"); 610 assert(rec.buildSettings.dflags.length == 3); 611 assert(rec.buildSettings.dflags["windows-x86"] == ["-a", "-b", "-c"]); 612 assert(rec.buildSettings.dflags[""] == ["-e", "-f", "-g"]); 613 assert(rec.buildSettings.dflags["linux"] == ["-h", "-i", "-j"]); 614 } 615 616 unittest { // test for missing name field 617 import std.exception; 618 auto sdl = `description "missing name"`; 619 PackageRecipe rec; 620 assertThrown(parseSDL(rec, sdl, null, "testfile")); 621 } 622 623 unittest { // test single value fields 624 import std.exception; 625 PackageRecipe rec; 626 assertThrown!Exception(parseSDL(rec, `name "hello" "world"`, null, "testfile")); 627 assertThrown!Exception(parseSDL(rec, `name`, null, "testfile")); 628 assertThrown!Exception(parseSDL(rec, `name 10`, null, "testfile")); 629 assertThrown!Exception(parseSDL(rec, 630 `name "hello" { 631 world 632 }`, null, "testfile")); 633 assertThrown!Exception(parseSDL(rec, 634 `name "" 635 versions "hello" 10` 636 , null, "testfile")); 637 } 638 639 unittest { // test basic serialization 640 PackageRecipe p; 641 p.name = "test"; 642 p.authors = ["foo", "bar"]; 643 p.buildSettings.dflags["windows"] = ["-a"]; 644 p.buildSettings.lflags[""] = ["-b", "-c"]; 645 auto sdl = toSDL(p).toSDLDocument(); 646 assert(sdl == 647 `name "test" 648 authors "foo" "bar" 649 dflags "-a" platform="windows" 650 lflags "-b" "-c" 651 `); 652 } 653 654 unittest { 655 auto sdl = "name \"test\"\nsourcePaths"; 656 PackageRecipe rec; 657 parseSDL(rec, sdl, null, "testfile"); 658 assert("" in rec.buildSettings.sourcePaths); 659 } 660 661 unittest { 662 auto sdl = 663 `name "test" 664 dependency "package" repository="git+https://some.url" version="12345678" 665 `; 666 PackageRecipe rec; 667 parseSDL(rec, sdl, null, "testfile"); 668 auto dependency = rec.buildSettings.dependencies["package"]; 669 assert(!dependency.repository.empty); 670 assert(dependency.repository.ref_ == "12345678"); 671 } 672 673 unittest { 674 PackageRecipe p; 675 p.name = "test"; 676 677 auto repository = Repository("git+https://some.url", "12345678"); 678 p.buildSettings.dependencies["package"] = Dependency(repository); 679 auto sdl = toSDL(p).toSDLDocument(); 680 assert(sdl == 681 `name "test" 682 dependency "package" repository="git+https://some.url" version="12345678" 683 `); 684 }