diff --git a/source/app.d b/source/app.d index 1edf84b..4d3ce8b 100644 --- a/source/app.d +++ b/source/app.d @@ -109,7 +109,7 @@ logInfo(""); } - Dub dub = new Dub(registry_urls.map!(url => cast(PackageSupplier)new RegistryPS(Url(url))).array); + Dub dub = new Dub(registry_urls.map!(url => cast(PackageSupplier)new RegistryPackageSupplier(Url(url))).array); string def_config; bool loadCwdPackage() @@ -159,13 +159,13 @@ enforce(!install_local || !install_system, "Cannot install locally and system wide at the same time."); if( install_local ) location = InstallLocation.local; else if( install_system ) location = InstallLocation.systemWide; - if( install_version.length ) dub.install(name, new Dependency(install_version), location); + if( install_version.length ) dub.install(name, Dependency(install_version), location); else { - try dub.install(name, new Dependency(">=0.0.0"), location); + try dub.install(name, Dependency(">=0.0.0"), location); catch(Exception e){ logInfo("Installing a release version failed: %s", e.msg); logInfo("Retry with ~master..."); - dub.install(name, new Dependency("~master"), location); + dub.install(name, Dependency("~master"), location); } } break; diff --git a/source/dub/dependency.d b/source/dub/dependency.d index 2f2602f..19b6dc0 100644 --- a/source/dub/dependency.d +++ b/source/dub/dependency.d @@ -33,20 +33,18 @@ Specification (SemVer) 2.0.0-rc.2. */ struct Version { - private enum NoCheck = 0; - private this(string vers, int noCheck){ sVersion = vers; } - static const Version RELEASE = Version("0.0.0", NoCheck); - static const Version HEAD = Version(to!string(MAX_VERS)~"."~to!string(MAX_VERS)~"."~to!string(MAX_VERS), NoCheck); - static const Version INVALID = Version("", NoCheck); - static const Version MASTER = Version(MASTER_STRING, NoCheck); - static const string MASTER_STRING = "~master"; - static immutable char BRANCH_IDENT = '~'; - private { - static const size_t MAX_VERS = 9999; - static const size_t MASTER_VERS = cast(size_t)(-1); - string sVersion; + enum MAX_VERS = 9999; + enum MASTER_VERS = cast(size_t)(-1); + string m_version; } + + static @property RELEASE() { return Version("0.0.0"); } + static @property HEAD() { return Version(to!string(MAX_VERS)~"."~to!string(MAX_VERS)~"."~to!string(MAX_VERS)); } + static @property INVALID() { return Version(""); } + static @property MASTER() { return Version(MASTER_STRING); } + static @property MASTER_STRING() { return "~master"; } + static @property BRANCH_IDENT() { return '~'; } this(string vers) in { @@ -93,19 +91,14 @@ } } body { - sVersion = vers; + m_version = vers; } - this(const Version o) - { - sVersion = o.sVersion; - } - - bool opEquals(ref const Version oth) const { return sVersion == oth.sVersion; } - bool opEquals(const Version oth) const { return sVersion == oth.sVersion; } + bool opEquals(ref const Version oth) const { return m_version == oth.m_version; } + bool opEquals(const Version oth) const { return m_version == oth.m_version; } /// Returns true, if this version indicates a branch, which is not the trunk. - @property bool isBranch() const { return sVersion[0] == BRANCH_IDENT && sVersion != MASTER_STRING; } + @property bool isBranch() const { return m_version[0] == BRANCH_IDENT && m_version != MASTER_STRING; } /** Comparing Versions is generally possible, but comparing Versions @@ -115,7 +108,7 @@ int opCmp(ref const Version other) const { if(isBranch || other.isBranch) { - if(sVersion == other.sVersion) return 0; + if(m_version == other.m_version) return 0; else throw new Exception("Can't compare branch versions! (this: %s, other: %s)".format(this, other)); } @@ -140,22 +133,22 @@ } int opCmp(in Version other) const { return opCmp(other); } - string toString() const { return sVersion; } + string toString() const { return m_version; } private string[] toComparableArray() const out(result) { assert(result.length >= 3); } body { - enforce(!isBranch, "Cannot convert a branch an array representation (%s)", sVersion); + enforce(!isBranch, "Cannot convert a branch an array representation (%s)", m_version); // Master has to compare to the other regular versions, therefore a special // representation is returned for this case. - if(sVersion == MASTER_STRING) + if(m_version == MASTER_STRING) return [ to!string(Version.MASTER_VERS), to!string(Version.MASTER_VERS), to!string(Version.MASTER_VERS) ]; // Split and discard possible build metadata, this is not compared. - string vers = split(sVersion, "+")[0]; + string vers = split(m_version, "+")[0]; // Split prerelease data (may be empty) auto dashIdx = std.string.indexOf(vers, "-"); @@ -238,7 +231,7 @@ /// Representing a dependency, which is basically a version string and a /// compare methode, e.g. '>=1.0.0 <2.0.0' (i.e. a space separates the two /// version numbers) -class Dependency { +struct Dependency { private { string m_cmpA; Version m_versA; @@ -249,30 +242,28 @@ bool m_optional = false; } - this( string ves ) { - enforce( ves.length > 0); + this(string ves) + { + enforce(ves.length > 0); string orig = ves; - if(ves[0] == Version.BRANCH_IDENT) { + if (ves[0] == Version.BRANCH_IDENT) { m_cmpA = ">="; m_cmpB = "<="; m_versA = m_versB = Version(ves); - } - else { + } else { m_cmpA = skipComp(ves); size_t idx2 = std.string.indexOf(ves, " "); - if( idx2 == -1 ) { - if( m_cmpA == "<=" || m_cmpA == "<" ) { - m_versA = Version(Version.RELEASE); + if (idx2 == -1) { + if (m_cmpA == "<=" || m_cmpA == "<") { + m_versA = Version.RELEASE; m_cmpB = m_cmpA; m_cmpA = ">="; m_versB = Version(ves); - } - else if( m_cmpA == ">=" || m_cmpA == ">" ) { + } else if (m_cmpA == ">=" || m_cmpA == ">") { m_versA = Version(ves); - m_versB = Version(Version.HEAD); + m_versB = Version.HEAD; m_cmpB = "<="; - } - else { + } else { // Converts "==" to ">=a&&<=a", which makes merging easier m_versA = m_versB = Version(ves); m_cmpA = ">="; @@ -288,7 +279,7 @@ enforce(!m_versA.isBranch, "Partly a branch (A): %s", ves); enforce(!m_versB.isBranch, "Partly a branch (B): %s", ves); - if( m_versB < m_versA ) { + if (m_versB < m_versA) { swap(m_versA, m_versB); swap(m_cmpA, m_cmpB); } @@ -305,14 +296,6 @@ m_versB = ver; } - this(const Dependency o) { - m_cmpA = o.m_cmpA; m_versA = Version(o.m_versA); - m_cmpB = o.m_cmpB; m_versB = Version(o.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; } @@ -320,7 +303,8 @@ @property Version version_() const { assert(m_versA == m_versB); return m_versA; } - override string toString() const { + string toString() + const { string r; // Special "==" case if( m_versA == m_versB && m_cmpA == ">=" && m_cmpB == "<=" ){ @@ -335,10 +319,8 @@ return r; } - override bool opEquals(Object b) + bool opEquals(in ref Dependency o) { - if (this is b) return true; if (b is null) return false; if (typeid(this) != typeid(b)) return false; - Dependency o = cast(Dependency) b; // 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 @@ -373,17 +355,15 @@ /// Merges to versions Dependency merge(ref const(Dependency) o) const { - if(!valid()) - return new Dependency(this); - if(!o.valid()) - return new Dependency(o); - if( m_configuration != o.m_configuration ) - return new Dependency(">=1.0.0 <=0.0.0"); + if (!valid()) return this; + if (!o.valid()) return o; + if (m_configuration != o.m_configuration) + return Dependency(">=1.0.0 <=0.0.0"); Version a = m_versA > o.m_versA? m_versA : o.m_versA; Version b = m_versB < o.m_versB? m_versB : o.m_versB; - Dependency d = new Dependency(this); + Dependency d = this; d.m_cmpA = !doCmp(m_cmpA, a,a)? m_cmpA : o.m_cmpA; d.m_versA = a; d.m_cmpB = !doCmp(m_cmpB, b,b)? m_cmpB : o.m_cmpB; @@ -512,9 +492,9 @@ } struct RequestedDependency { - this( string pkg, const Dependency de) { - dependency = new Dependency(de); - packages[pkg] = new Dependency(de); + this( string pkg, Dependency de) { + dependency = de; + packages[pkg] = de; } Dependency dependency; Dependency[string] packages; @@ -545,7 +525,7 @@ void clearUnused() { Rebindable!(const Package)[string] unused = m_packages.dup; unused.remove(m_root.name); - forAllDependencies( (const PkgType* avail, string s, const Dependency d, const Package issuer) { + forAllDependencies( (const PkgType* avail, string s, Dependency d, const Package issuer) { if(avail && d.matches(avail.vers)) unused.remove(avail.name); }); @@ -566,7 +546,7 @@ RequestedDependency[string] missing() const { RequestedDependency[string] deps; - forAllDependencies( (const PkgType* avail, string pkgId, const Dependency d, const Package issuer) { + forAllDependencies( (const PkgType* avail, string pkgId, Dependency d, const Package issuer) { if(!d.optional && (!avail || !d.matches(avail.vers))) addDependency(deps, pkgId, d, issuer); }); @@ -575,7 +555,7 @@ RequestedDependency[string] needed() const { RequestedDependency[string] deps; - forAllDependencies( (const PkgType* avail, string pkgId, const Dependency d, const Package issuer) { + forAllDependencies( (const PkgType* avail, string pkgId, Dependency d, const Package issuer) { if(!d.optional) addDependency(deps, pkgId, d, issuer); }); @@ -584,7 +564,7 @@ RequestedDependency[string] optional() const { RequestedDependency[string] allDeps; - forAllDependencies( (const PkgType* avail, string pkgId, const Dependency d, const Package issuer) { + forAllDependencies( (const PkgType* avail, string pkgId, Dependency d, const Package issuer) { addDependency(allDeps, pkgId, d, issuer); }); RequestedDependency[string] optionalDeps; @@ -593,7 +573,7 @@ return optionalDeps; } - private void forAllDependencies(void delegate (const PkgType* avail, string pkgId, const Dependency d, const Package issuer) dg) const { + private void forAllDependencies(void delegate (const PkgType* avail, string pkgId, Dependency d, const Package issuer) dg) const { foreach(string issuerPackag, issuer; m_packages) { foreach(string depPkg, dependency; issuer.dependencies) { auto availPkg = depPkg in m_packages; @@ -602,7 +582,7 @@ } } - private static void addDependency(ref RequestedDependency[string] deps, string packageId, const Dependency d, const Package issuer) { + private static void addDependency(ref RequestedDependency[string] deps, string packageId, Dependency d, const Package issuer) { logDebug("addDependency "~packageId~", '%s'", d); auto d2 = packageId in deps; if(!d2) { @@ -610,7 +590,7 @@ } else { d2.dependency = d2.dependency.merge(d); - d2.packages[issuer.name] = new Dependency(d); + d2.packages[issuer.name] = d; } } diff --git a/source/dub/dub.d b/source/dub/dub.d index 3c55431..e2bdaeb 100644 --- a/source/dub/dub.d +++ b/source/dub/dub.d @@ -43,7 +43,7 @@ { Url url = Url.parse("http://code.dlang.org/"); logDiagnostic("Using dub registry url '%s'", url); - return [new RegistryPS(url)]; + return [new RegistryPackageSupplier(url)]; } /// The Dub class helps in getting the applications @@ -79,8 +79,8 @@ m_systemConfig = jsonFromFile(m_systemDubPath ~ "settings.json", true); PackageSupplier[] ps = additional_package_suppliers; - if (auto pp = "registryUrls" in m_userConfig) ps ~= deserializeJson!(string[])(*pp).map!(url => new RegistryPS(Url(url))).array; - if (auto pp = "registryUrls" in m_systemConfig) ps ~= deserializeJson!(string[])(*pp).map!(url => new RegistryPS(Url(url))).array; + if (auto pp = "registryUrls" in m_userConfig) ps ~= deserializeJson!(string[])(*pp).map!(url => new RegistryPackageSupplier(Url(url))).array; + if (auto pp = "registryUrls" in m_systemConfig) ps ~= deserializeJson!(string[])(*pp).map!(url => new RegistryPackageSupplier(Url(url))).array; ps ~= defaultPackageSuppliers(); m_packageSuppliers = ps; @@ -371,7 +371,7 @@ if (!ddox_pack) ddox_pack = m_packageManager.getBestPackage("ddox", "~master"); if (!ddox_pack) { logInfo("DDOX is not installed, performing user wide installation."); - ddox_pack = install("ddox", new Dependency(">=0.0.0"), InstallLocation.userWide); + ddox_pack = install("ddox", Dependency(">=0.0.0"), InstallLocation.userWide); } version(Windows) auto ddox_exe = "ddox.exe"; diff --git a/source/dub/package_.d b/source/dub/package_.d index dd5f9b6..d09ddf7 100644 --- a/source/dub/package_.d +++ b/source/dub/package_.d @@ -462,18 +462,18 @@ 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 = Dependency(Version(ver)); dep.path = Path(verspec.path.get!string()); } else { // Using the string to be able to specifiy a range of versions. - dep = new Dependency(ver); + dep = 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()); + dep = Dependency(verspec.get!string()); } this.dependencies[pkg] = dep; } diff --git a/source/dub/packagemanager.d b/source/dub/packagemanager.d index 427742b..dfd4a5a 100644 --- a/source/dub/packagemanager.d +++ b/source/dub/packagemanager.d @@ -120,10 +120,10 @@ Package getBestPackage(string name, string version_spec) { - return getBestPackage(name, new Dependency(version_spec)); + return getBestPackage(name, Dependency(version_spec)); } - Package getBestPackage(string name, in Dependency version_spec) + Package getBestPackage(string name, Dependency version_spec) { Package ret; foreach( p; getPackageIterator(name) ) diff --git a/source/dub/packagesupplier.d b/source/dub/packagesupplier.d index e71d0d7..604d53f 100644 --- a/source/dub/packagesupplier.d +++ b/source/dub/packagesupplier.d @@ -23,21 +23,26 @@ /// which is available. interface PackageSupplier { /// path: absolute path to store the package (usually in a zip format) - void retrievePackage(const Path path, const string packageId, const Dependency dep); + void retrievePackage(Path path, string packageId, Dependency dep); /// returns the metadata for the package - Json getPackageDescription(const string packageId, const Dependency dep); + Json getPackageDescription(string packageId, Dependency dep); + /// Returns a hunman readable representation of the supplier string toString(); } -class FSPackageSupplier : PackageSupplier { - private { Path m_path; } +class FileSystemPackageSupplier : PackageSupplier { + private { + Path m_path; + } + this(Path root) { m_path = root; } override string toString() { return "file repository at "~m_path.toNativeString(); } - void retrievePackage(const Path path, const string packageId, const Dependency dep) { + void retrievePackage(Path path, string packageId, Dependency dep) + { enforce(path.absolute); logInfo("Storing package '"~packageId~"', version requirements: %s", dep); auto filename = bestPackageFile(packageId, dep); @@ -45,31 +50,93 @@ copyFile(filename, path); } - Json getPackageDescription(const string packageId, const Dependency dep) { + Json getPackageDescription(string packageId, Dependency dep) + { auto filename = bestPackageFile(packageId, dep); return jsonFromZip(filename, "package.json"); } - private Path bestPackageFile( const string packageId, const Dependency dep) const { - Version bestVersion = Version(Version.RELEASE); - foreach(DirEntry d; dirEntries(m_path.toNativeString(), packageId~"*", SpanMode.shallow)) { + private Path bestPackageFile(string packageId, Dependency dep) + const { + Version bestVersion = Version.RELEASE; + foreach (DirEntry d; dirEntries(m_path.toNativeString(), packageId~"*", SpanMode.shallow)) { Path p = Path(d.name); logDebug("Entry: %s", p); enforce(to!string(p.head)[$-4..$] == ".zip"); string vers = to!string(p.head)[packageId.length+1..$-4]; logDebug("Version string: "~vers); Version v = Version(vers); - if(v > bestVersion && dep.matches(v) ) { + if (v > bestVersion && dep.matches(v)) { bestVersion = v; } } auto fileName = m_path ~ (packageId ~ "_" ~ to!string(bestVersion) ~ ".zip"); - if(bestVersion == Version.RELEASE || !existsFile(fileName)) + if (bestVersion == Version.RELEASE || !existsFile(fileName)) throw new Exception("No matching package found"); logDiagnostic("Found best matching package: '%s'", fileName); return fileName; } -} \ No newline at end of file +} + + +/// Client PackageSupplier using the registry available via registerVpmRegistry +class RegistryPackageSupplier : PackageSupplier { + private { + Url m_registryUrl; + Json[string] m_allMetadata; + } + + this(Url registry) + { + m_registryUrl = registry; + } + + override string toString() { return "registry at "~m_registryUrl.toString(); } + + void retrievePackage(Path path, string packageId, Dependency dep) + { + Json best = getBestPackage(packageId, dep); + auto url = m_registryUrl ~ Path(PackagesPath~"/"~packageId~"/"~best["version"].get!string~".zip"); + logDiagnostic("Found download URL: '%s'", url); + download(url, path); + } + + Json getPackageDescription(string packageId, Dependency dep) + { + return getBestPackage(packageId, dep); + } + + private Json getMetadata(string packageId) + { + if (auto json = packageId in m_allMetadata) + return *json; + + auto url = m_registryUrl ~ Path(PackagesPath ~ "/" ~ packageId ~ ".json"); + + logDebug("Downloading metadata for %s", packageId); + logDebug("Getting from %s", url); + + auto jsonData = cast(string)download(url); + Json json = parseJson(jsonData); + m_allMetadata[packageId] = json; + return json; + } + + private Json getBestPackage(string packageId, Dependency dep) + { + Json md = getMetadata(packageId); + Json best = null; + foreach (json; md["versions"]) { + auto cur = Version(cast(string)json["version"]); + if (dep.matches(cur) && (best == null || Version(cast(string)best["version"]) < cur)) + best = json; + } + enforce(best != null, "No package candidate found for "~packageId~" "~dep.toString()); + return best; + } +} + +private enum PackagesPath = "packages"; diff --git a/source/dub/project.d b/source/dub/project.d index 05e1920..b0ed622 100644 --- a/source/dub/project.d +++ b/source/dub/project.d @@ -128,7 +128,7 @@ foreach(dp; m_dependencies) if( dp.name == name ) return dp; - if(!isOptional) assert(false, "Unknown dependency: "~name); + if(!isOptional) enforce(false, "Unknown dependency: "~name); else return null; } @@ -533,7 +533,7 @@ this.type = id; this.packageId = pkg; this.location = location; - this.vers = cast(immutable)new Dependency(d); + this.vers = d; this.issuer = issue; } @@ -542,7 +542,7 @@ pack = pkg; type = id; packageId = pkg.name; - vers = cast(immutable)new Dependency(pkg.ver); + vers = cast(immutable)Dependency(pkg.ver); issuer = issue; } diff --git a/source/dub/registry.d b/source/dub/registry.d deleted file mode 100644 index 5e0d431..0000000 --- a/source/dub/registry.d +++ /dev/null Binary files differ