diff --git a/source/dub/dependency.d b/source/dub/dependency.d index e34cff9..2bacedf 100644 --- a/source/dub/dependency.d +++ b/source/dub/dependency.d @@ -150,6 +150,7 @@ Version m_versB; Path m_path; string m_configuration = "library"; + bool m_optional = false; } this( string ves ) { @@ -215,10 +216,13 @@ enforce(m_versA <= m_versB); m_path = o.m_path; m_configuration = o.m_configuration; + m_optional = o.m_optional; } @property void path(Path value) { m_path = value; } @property Path path() const { return m_path; } + @property bool optional() const { return m_optional; } + @property void optional(bool optional) { m_optional = optional; } @property Version version_() const { assert(m_versA == m_versB); return m_versA; } @@ -233,6 +237,7 @@ if( m_versB != Version.HEAD ) r ~= (r.length==0?"" : " ") ~ m_cmpB ~ to!string(m_versB); if( m_versA == Version.RELEASE && m_versB == Version.HEAD ) r = ">=0.0.0"; } + // TODO(mdondorff): add information to path and optionality. return r; } @@ -240,7 +245,11 @@ { if (this is b) return true; if (b is null) return false; if (typeid(this) != typeid(b)) return false; Dependency o = cast(Dependency) b; - return o.m_cmpA == m_cmpA && o.m_cmpB == m_cmpB && o.m_versA == m_versA && o.m_versB == m_versB && o.m_configuration == m_configuration; + // TODO(mdondorff): Check if not comparing the path is correct for all clients. + return o.m_cmpA == m_cmpA && o.m_cmpB == m_cmpB + && o.m_versA == m_versA && o.m_versB == m_versB + && o.m_configuration == m_configuration + && o.m_optional == m_optional; } bool valid() const { @@ -288,6 +297,7 @@ d.m_versA = a; d.m_cmpB = !doCmp(m_cmpB, b,b)? m_cmpB : o.m_cmpB; d.m_versB = b; + d.m_optional = m_optional && o.m_optional; //logTrace(" merged: %s", d); @@ -399,6 +409,16 @@ a = new Dependency(">=1.0.0"); assert(!a.matches(Version(branch1)), "Dependency(1.0.0) matches 'branch1'"); + // Testing optional dependencies. + a = new Dependency(">=1.0.0"); + assert(!a.optional, "Default is not optional."); + b = new Dependency(a); + assert(!a.merge(b).optional, "Merging two not optional dependencies wrong."); + a.optional = true; + assert(!a.merge(b).optional, "Merging optional with not optional wrong."); + b.optional = true; + assert(a.merge(b).optional, "Merging two optional dependencies wrong."); + logTrace("Dependency Unittest sucess."); } @@ -458,7 +478,7 @@ RequestedDependency[string] missing() const { RequestedDependency[string] deps; forAllDependencies( (const PkgType* avail, string pkgId, const Dependency d, const Package issuer) { - if(!avail || !d.matches(avail.vers)) + if(!d.optional || !avail || !d.matches(avail.vers)) addDependency(deps, pkgId, d, issuer); }); return deps; @@ -467,7 +487,8 @@ RequestedDependency[string] needed() const { RequestedDependency[string] deps; forAllDependencies( (const PkgType* avail, string pkgId, const Dependency d, const Package issuer) { - addDependency(deps, pkgId, d, issuer); + if(!d.optional) + addDependency(deps, pkgId, d, issuer); }); return deps; } diff --git a/source/dub/package_.d b/source/dub/package_.d index e69ce74..24717fb 100644 --- a/source/dub/package_.d +++ b/source/dub/package_.d @@ -44,41 +44,8 @@ } /// Representing an installed package, usually constructed from a json object. -/// -/// Json file example: -/// { -/// "name": "MetalCollection", -/// "author": "VariousArtists", -/// "version": "1.0.0", -/// "url": "https://github.org/...", -/// "keywords": "a,b,c", -/// "category": "music.best", -/// "dependencies": { -/// "black-sabbath": ">=1.0.0", -/// "CowboysFromHell": "<1.0.0", -/// "BeneathTheRemains": {"version": "0.4.1", "path": "./beneath-0.4.1"} -/// } -/// "licenses": { -/// ... -/// } -/// "configurations": [ -// TODO: what and how? -/// ] -// TODO: plain like this or packed together? -/// " -/// "dflags-X" -/// "lflags-X" -/// "libs-X" -/// "files-X" -/// "copyFiles-X" -/// "versions-X" -/// "importPaths-X" -/// "stringImportPaths-X" -/// "sourcePath" -/// } -/// } -/// -/// TODO: explain configurations +/// Documentation of the package.json can be found at +/// http://registry.vibed.org/package-format class Package { static struct LocalPackageDef { string name; Version version_; Path path; } @@ -250,6 +217,8 @@ } } +/// Specifying package information without any connection to a certain +/// installed package, like Package class is doing. struct PackageInfo { string name; string version_; @@ -281,11 +250,20 @@ enforce(pkg !in this.dependencies, "The dependency '"~pkg~"' is specified more than once." ); Dependency dep; if( verspec.type == Json.Type.Object ){ + enforce("version" in verspec, "Package information provided for package " ~ pkg ~ " is missing a version field."); auto ver = verspec["version"].get!string; - if( auto pp = "path" in verspec ){ + if( auto pp = "path" in verspec ) { + // This enforces the "version" specifier to be a simple version, + // without additional range specifiers. dep = new Dependency(Version(ver)); dep.path = Path(verspec.path.get!string()); - } else dep = new Dependency(ver); + } else { + // Using the string to be able to specifiy a range of versions. + dep = new Dependency(ver); + } + if( auto po = "optional" in verspec ) { + dep.optional = verspec.optional.get!bool(); + } } else { // canonical "package-id": "version" dep = new Dependency(verspec.get!string()); @@ -327,9 +305,14 @@ if( this.dependencies ){ auto deps = Json.EmptyObject; foreach( pack, d; this.dependencies ){ - if( d.path.empty ){ + if( d.path.empty && !d.optional ){ deps[pack] = d.toString(); - } else deps[pack] = serializeToJson(["version": d.version_.toString(), "path": d.path.toString()]); + } else { + auto toJson = ["version": d.version_.toString()]; + if(!d.path.empty) toJson["path"] = d.path.toString(); + if(d.optional) toJson["optional"] = to!string(d.optional); + deps[pack] = serializeToJson(toJson); + } } ret.dependencies = deps; }