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