- /**
- Abstract representation of a package description file.
-
- Copyright: © 2012-2014 rejectedsoftware e.K.
- License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
- Authors: Sönke Ludwig, Matthias Dondorff
- */
- module dub.recipe.packagerecipe;
-
- import dub.compilers.compiler;
- import dub.dependency;
-
- import dub.internal.vibecompat.core.file;
- import dub.internal.vibecompat.core.log;
- import dub.internal.vibecompat.inet.url;
-
- import std.algorithm : findSplit, sort;
- import std.array : join, split;
- import std.exception : enforce;
- import std.file;
- import std.range;
-
-
- /**
- Returns the individual parts of a qualified package name.
-
- Sub qualified package names are lists of package names separated by ":". For
- example, "packa:packb:packc" references a package named "packc" that is a
- sub package of "packb", wich in turn is a sub package of "packa".
- */
- string[] getSubPackagePath(string package_name)
- {
- return package_name.split(":");
- }
-
- /**
- Returns the name of the top level package for a given (sub) package name.
-
- In case of a top level package, the qualified name is returned unmodified.
- */
- string getBasePackageName(string package_name)
- {
- return package_name.findSplit(":")[0];
- }
-
- /**
- Returns the qualified sub package part of the given package name.
-
- This is the part of the package name excluding the base package
- name. See also $(D getBasePackageName).
- */
- string getSubPackageName(string package_name)
- {
- return package_name.findSplit(":")[2];
- }
-
- unittest
- {
- assert(getSubPackagePath("packa:packb:packc") == ["packa", "packb", "packc"]);
- assert(getSubPackagePath("pack") == ["pack"]);
- assert(getBasePackageName("packa:packb:packc") == "packa");
- assert(getBasePackageName("pack") == "pack");
- assert(getSubPackageName("packa:packb:packc") == "packb:packc");
- assert(getSubPackageName("pack") == "");
- }
-
- /**
- Represents the contents of a package recipe file (dub.json/dub.sdl) in an abstract way.
-
- This structure is used to reason about package descriptions in isolation.
- For higher level package handling, see the $(D Package) class.
- */
- struct PackageRecipe {
- string name;
- string version_;
- string description;
- string homepage;
- string[] authors;
- string copyright;
- string license;
- string[] ddoxFilterArgs;
- string ddoxTool;
- BuildSettingsTemplate buildSettings;
- ConfigurationInfo[] configurations;
- BuildSettingsTemplate[string] buildTypes;
-
- SubPackage[] subPackages;
-
- deprecated("Use Package.dependencies or the dependencies of the individual BuildSettingsTemplates instead.")
- @property const(Dependency)[string] dependencies()
- const {
- Dependency[string] ret;
- foreach (n, d; this.buildSettings.dependencies)
- ret[n] = d;
- foreach (ref c; configurations)
- foreach (n, d; c.buildSettings.dependencies)
- ret[n] = d;
- return ret;
- }
-
- inout(ConfigurationInfo) getConfiguration(string name)
- inout {
- foreach (c; configurations)
- if (c.name == name)
- return c;
- throw new Exception("Unknown configuration: "~name);
- }
- }
-
- struct SubPackage
- {
- string path;
- PackageRecipe recipe;
- }
-
-
- /// Bundles information about a build configuration.
- struct ConfigurationInfo {
- string name;
- string[] platforms;
- BuildSettingsTemplate buildSettings;
-
- this(string name, BuildSettingsTemplate build_settings)
- {
- enforce(!name.empty, "Configuration name is empty.");
- this.name = name;
- this.buildSettings = build_settings;
- }
-
- bool matchesPlatform(in BuildPlatform platform)
- const {
- if( platforms.empty ) return true;
- foreach(p; platforms)
- if( platform.matchesSpecification("-"~p) )
- return true;
- return false;
- }
- }
-
- /// This keeps general information about how to build a package.
- /// It contains functions to create a specific BuildSetting, targeted at
- /// a certain BuildPlatform.
- struct BuildSettingsTemplate {
- Dependency[string] dependencies;
- string systemDependencies;
- TargetType targetType = TargetType.autodetect;
- string targetPath;
- string targetName;
- string workingDirectory;
- string mainSourceFile;
- string[string] subConfigurations;
- string[][string] dflags;
- string[][string] lflags;
- string[][string] libs;
- string[][string] sourceFiles;
- string[][string] sourcePaths;
- string[][string] excludedSourceFiles;
- string[][string] copyFiles;
- string[][string] versions;
- string[][string] debugVersions;
- string[][string] importPaths;
- string[][string] stringImportPaths;
- string[][string] preGenerateCommands;
- string[][string] postGenerateCommands;
- string[][string] preBuildCommands;
- string[][string] postBuildCommands;
- BuildRequirements[string] buildRequirements;
- BuildOptions[string] buildOptions;
-
-
- /// Constructs a BuildSettings object from this template.
- void getPlatformSettings(ref BuildSettings dst, in BuildPlatform platform, Path base_path)
- const {
- dst.targetType = this.targetType;
- if (!this.targetPath.empty) dst.targetPath = this.targetPath;
- if (!this.targetName.empty) dst.targetName = this.targetName;
- if (!this.workingDirectory.empty) dst.workingDirectory = this.workingDirectory;
- if (!this.mainSourceFile.empty) {
- dst.mainSourceFile = this.mainSourceFile;
- dst.addSourceFiles(this.mainSourceFile);
- }
-
- void collectFiles(string method)(in string[][string] paths_map, string pattern)
- {
- foreach (suffix, paths; paths_map) {
- if (!platform.matchesSpecification(suffix))
- continue;
-
- foreach (spath; paths) {
- enforce(!spath.empty, "Paths must not be empty strings.");
- auto path = Path(spath);
- if (!path.absolute) path = base_path ~ path;
- if (!existsFile(path) || !isDir(path.toNativeString())) {
- logWarn("Invalid source/import path: %s", path.toNativeString());
- continue;
- }
-
- foreach (d; dirEntries(path.toNativeString(), pattern, SpanMode.depth)) {
- import std.path : baseName;
- if (baseName(d.name)[0] == '.' || isDir(d.name)) continue;
- auto src = Path(d.name).relativeTo(base_path);
- __traits(getMember, dst, method)(src.toNativeString());
- }
- }
- }
- }
-
- // collect files from all source/import folders
- collectFiles!"addSourceFiles"(sourcePaths, "*.d");
- collectFiles!"addImportFiles"(importPaths, "*.{d,di}");
- dst.removeImportFiles(dst.sourceFiles);
- collectFiles!"addStringImportFiles"(stringImportPaths, "*");
-
- // ensure a deterministic order of files as passed to the compiler
- dst.sourceFiles.sort();
-
- getPlatformSetting!("dflags", "addDFlags")(dst, platform);
- getPlatformSetting!("lflags", "addLFlags")(dst, platform);
- getPlatformSetting!("libs", "addLibs")(dst, platform);
- getPlatformSetting!("sourceFiles", "addSourceFiles")(dst, platform);
- getPlatformSetting!("excludedSourceFiles", "removeSourceFiles")(dst, platform);
- getPlatformSetting!("copyFiles", "addCopyFiles")(dst, platform);
- getPlatformSetting!("versions", "addVersions")(dst, platform);
- getPlatformSetting!("debugVersions", "addDebugVersions")(dst, platform);
- getPlatformSetting!("importPaths", "addImportPaths")(dst, platform);
- getPlatformSetting!("stringImportPaths", "addStringImportPaths")(dst, platform);
- getPlatformSetting!("preGenerateCommands", "addPreGenerateCommands")(dst, platform);
- getPlatformSetting!("postGenerateCommands", "addPostGenerateCommands")(dst, platform);
- getPlatformSetting!("preBuildCommands", "addPreBuildCommands")(dst, platform);
- getPlatformSetting!("postBuildCommands", "addPostBuildCommands")(dst, platform);
- getPlatformSetting!("buildRequirements", "addRequirements")(dst, platform);
- getPlatformSetting!("buildOptions", "addOptions")(dst, platform);
- }
-
- void getPlatformSetting(string name, string addname)(ref BuildSettings dst, in BuildPlatform platform)
- const {
- foreach(suffix, values; __traits(getMember, this, name)){
- if( platform.matchesSpecification(suffix) )
- __traits(getMember, dst, addname)(values);
- }
- }
-
- void warnOnSpecialCompilerFlags(string package_name, string config_name)
- {
- auto nodef = false;
- auto noprop = false;
- foreach (req; this.buildRequirements) {
- if (req & BuildRequirement.noDefaultFlags) nodef = true;
- if (req & BuildRequirement.relaxProperties) noprop = true;
- }
-
- if (noprop) {
- logWarn(`Warning: "buildRequirements": ["relaxProperties"] is deprecated and is now the default behavior. Note that the -property switch will probably be removed in future versions of DMD.`);
- logWarn("");
- }
-
- if (nodef) {
- logWarn("Warning: This package uses the \"noDefaultFlags\" build requirement. Please use only for development purposes and not for released packages.");
- logWarn("");
- } else {
- string[] all_dflags;
- BuildOptions all_options;
- foreach (flags; this.dflags) all_dflags ~= flags;
- foreach (options; this.buildOptions) all_options |= options;
- .warnOnSpecialCompilerFlags(all_dflags, all_options, package_name, config_name);
- }
- }
- }