diff --git a/source/dub/commandline.d b/source/dub/commandline.d index 9eb26aa..1027daa 100644 --- a/source/dub/commandline.d +++ b/source/dub/commandline.d @@ -61,6 +61,7 @@ bool rdmd = false; bool print_platform, print_builds, print_configs; bool place_system_wide = false, place_locally = false; + bool pre_release = false; string retrieved_version; string[] registry_urls; string[] debug_versions; @@ -84,6 +85,7 @@ "print-platform", &print_platform, "system", &place_system_wide, "local", &place_locally, + "prerelease", &pre_release, "version", &retrieved_version, "registry", ®istry_urls, "root", &root_path @@ -190,7 +192,7 @@ case "upgrade": dub.loadPackageFromCwd(); logInfo("Upgrading project in %s", dub.projectPath.toNativeString()); - dub.update(UpdateOptions.Upgrade | (annotate ? UpdateOptions.JustAnnotate : UpdateOptions.None)); + dub.update(UpdateOptions.Upgrade | (annotate ? UpdateOptions.JustAnnotate : UpdateOptions.None) | (pre_release ? UpdateOptions.preRelease : UpdateOptions.None)); return 0; case "install": warnRenamed(cmd, "fetch"); @@ -202,10 +204,10 @@ enforce(!place_locally || !place_system_wide, "Cannot place package locally and system wide at the same time."); if (place_locally) location = PlacementLocation.local; else if (place_system_wide) location = PlacementLocation.systemWide; - if (retrieved_version.length) dub.fetch(name, Dependency(retrieved_version), location, true); + if (retrieved_version.length) dub.fetch(name, Dependency(retrieved_version), location, true, false); else { try { - dub.fetch(name, Dependency(">=0.0.0"), location, true); + dub.fetch(name, Dependency(">=0.0.0"), location, true, false); logInfo( "Please note that you need to use `dub run ` " ~ "or add it to dependencies of your package to actually use/run it. " ~ @@ -214,7 +216,7 @@ catch(Exception e){ logInfo("Getting a release version failed: %s", e.msg); logInfo("Retry with ~master..."); - dub.fetch(name, Dependency("~master"), location, true); + dub.fetch(name, Dependency("~master"), location, true, true); } } break; @@ -439,8 +441,14 @@ Fetch/remove options: --version Use the specified version/branch instead of the latest - --system Put package into system wide dub cache instead of user local one - --local Put packahe to a sub folder of the current directory + --system Put package into system wide dub cache instead of the + user local one + --local Extract the package to a sub folder of the current + working directory + +Upgrade options: + -prerelease Uses the latest pre-release version, even if release + versions are available `); logInfo("DUB version %s", dubVersion); diff --git a/source/dub/dependency.d b/source/dub/dependency.d index 669a2dc..8807988 100644 --- a/source/dub/dependency.d +++ b/source/dub/dependency.d @@ -60,6 +60,10 @@ /// Returns true, if this version indicates a branch, which is not the trunk. @property bool isBranch() const { return m_version[0] == BRANCH_IDENT && m_version != MASTER_STRING; } @property bool isMaster() const { return m_version == MASTER_STRING; } + @property bool isPreRelease() const { + if (isBranch || isMaster) return true; + return isPreReleaseVersion(m_version); + } /** Comparing Versions is generally possible, but comparing Versions diff --git a/source/dub/dub.d b/source/dub/dub.d index d2c814e..bfe1b4c 100644 --- a/source/dub/dub.d +++ b/source/dub/dub.d @@ -168,7 +168,7 @@ remove(a.pack); } foreach(Action a; filter!((Action a) => a.type == Action.Type.fetch)(actions)) { - fetch(a.packageId, a.vers, a.location, (options & UpdateOptions.Upgrade) != 0); + fetch(a.packageId, a.vers, a.location, (options & UpdateOptions.Upgrade) != 0, (options & UpdateOptions.preRelease) != 0); // never update the same package more than once masterVersionUpgrades[a.packageId] = true; } @@ -202,13 +202,13 @@ string[string] cachedPackages() const { return m_project.cachedPackagesIDs(); } /// Fetches the package matching the dependency and places it in the specified location. - Package fetch(string packageId, const Dependency dep, PlacementLocation location, bool force_branch_upgrade) + Package fetch(string packageId, const Dependency dep, PlacementLocation location, bool force_branch_upgrade, bool use_prerelease) { Json pinfo; PackageSupplier supplier; foreach(ps; m_packageSuppliers){ try { - pinfo = ps.getPackageDescription(packageId, dep); + pinfo = ps.getPackageDescription(packageId, dep, use_prerelease); supplier = ps; break; } catch(Exception) {} @@ -244,7 +244,7 @@ auto tempFile = m_tempPath ~ tempfname; string sTempFile = tempFile.toNativeString(); if (exists(sTempFile)) std.file.remove(sTempFile); - supplier.retrievePackage(tempFile, packageId, dep); // Q: continue on fail? + supplier.retrievePackage(tempFile, packageId, dep, use_prerelease); // Q: continue on fail? scope(exit) std.file.remove(sTempFile); logInfo("Placing %s %s to %s...", packageId, ver, placement.toNativeString()); @@ -370,7 +370,7 @@ if (!ddox_pack) ddox_pack = m_packageManager.getBestPackage("ddox", "~master"); if (!ddox_pack) { logInfo("DDOX is not present, getting it and storing user wide"); - ddox_pack = fetch("ddox", Dependency(">=0.0.0"), PlacementLocation.userWide, false); + ddox_pack = fetch("ddox", Dependency(">=0.0.0"), PlacementLocation.userWide, false, false); } version(Windows) auto ddox_exe = "ddox.exe"; diff --git a/source/dub/packagesupplier.d b/source/dub/packagesupplier.d index 53e593c..e15a315 100644 --- a/source/dub/packagesupplier.d +++ b/source/dub/packagesupplier.d @@ -23,10 +23,10 @@ /// which is available. interface PackageSupplier { /// path: absolute path to store the package (usually in a zip format) - void retrievePackage(Path path, string packageId, Dependency dep); + void retrievePackage(Path path, string packageId, Dependency dep, bool pre_release); /// returns the metadata for the package - Json getPackageDescription(string packageId, Dependency dep); + Json getPackageDescription(string packageId, Dependency dep, bool pre_release); /// Returns a hunman readable representation of the supplier string toString(); @@ -41,39 +41,43 @@ override string toString() { return "file repository at "~m_path.toNativeString(); } - void retrievePackage(Path path, string packageId, Dependency dep) + void retrievePackage(Path path, string packageId, Dependency dep, bool pre_release) { enforce(path.absolute); logInfo("Storing package '"~packageId~"', version requirements: %s", dep); - auto filename = bestPackageFile(packageId, dep); + auto filename = bestPackageFile(packageId, dep, pre_release); enforce(existsFile(filename)); copyFile(filename, path); } - Json getPackageDescription(string packageId, Dependency dep) + Json getPackageDescription(string packageId, Dependency dep, bool pre_release) { - auto filename = bestPackageFile(packageId, dep); + auto filename = bestPackageFile(packageId, dep, pre_release); return jsonFromZip(filename, "package.json"); } - private Path bestPackageFile(string packageId, Dependency dep) + private Path bestPackageFile(string packageId, Dependency dep, bool pre_release) const { - Version bestVersion = Version.RELEASE; + Version bestver = 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)) { - bestVersion = v; - } + Version cur = Version(vers); + if (!dep.matches(cur)) continue; + if (bestver == Version.RELEASE) bestver = cur; + else if (pre_release) { + if (cur > bestver) bestver = cur; + } else if (bestver.isPreRelease) { + if (!cur.isPreRelease || cur > bestver) bestver = cur; + } else if (!cur.isPreRelease && cur > bestver) bestver = cur; } - auto fileName = m_path ~ (packageId ~ "_" ~ to!string(bestVersion) ~ ".zip"); + auto fileName = m_path ~ (packageId ~ "_" ~ to!string(bestver) ~ ".zip"); - if (bestVersion == Version.RELEASE || !existsFile(fileName)) + if (bestver == Version.RELEASE || !existsFile(fileName)) throw new Exception("No matching package found"); logDiagnostic("Found best matching package: '%s'", fileName); @@ -96,17 +100,17 @@ override string toString() { return "registry at "~m_registryUrl.toString(); } - void retrievePackage(Path path, string packageId, Dependency dep) + void retrievePackage(Path path, string packageId, Dependency dep, bool pre_release) { - Json best = getBestPackage(packageId, dep); + Json best = getBestPackage(packageId, dep, pre_release); 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) + Json getPackageDescription(string packageId, Dependency dep, bool pre_release) { - return getBestPackage(packageId, dep); + return getBestPackage(packageId, dep, pre_release); } private Json getMetadata(string packageId) @@ -125,14 +129,22 @@ return json; } - private Json getBestPackage(string packageId, Dependency dep) + private Json getBestPackage(string packageId, Dependency dep, bool pre_release) { Json md = getMetadata(packageId); Json best = null; + Version bestver; 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; + logInfo("FOUND %s %s %s", cur.toString(), dep.matches(cur), cur.isPreRelease); + if (!dep.matches(cur)) continue; + if (best == null) best = json; + else if (pre_release) { + if (cur > bestver) best = json; + } else if (bestver.isPreRelease) { + if (!cur.isPreRelease || cur > bestver) best = json; + } else if (!cur.isPreRelease && cur > bestver) best = json; + bestver = Version(cast(string)best["version"]); } enforce(best != null, "No package candidate found for "~packageId~" "~dep.toString()); return best; diff --git a/source/dub/project.d b/source/dub/project.d index a5b09a2..bcbb705 100644 --- a/source/dub/project.d +++ b/source/dub/project.d @@ -584,7 +584,7 @@ logDiagnostic("Fetching package %s (%d suppliers registered)", pkg, packageSuppliers.length); foreach (ps; packageSuppliers) { try { - auto sp = new Package(ps.getPackageDescription(ppath[0], reqDep.dependency)); + auto sp = new Package(ps.getPackageDescription(ppath[0], reqDep.dependency, false)); foreach (spn; ppath[1 .. $]) sp = sp.getSubPackage(spn); p = sp; @@ -726,7 +726,8 @@ { None = 0, JustAnnotate = 1<<0, - Upgrade = 1<<1 + Upgrade = 1<<1, + preRelease = 1<<2 // inclde pre-release versions in upgrade } diff --git a/source/dub/semver.d b/source/dub/semver.d index 8f81411..b6b1607 100644 --- a/source/dub/semver.d +++ b/source/dub/semver.d @@ -91,6 +91,18 @@ assert(!isValidVersion("1.0-1.0")); } +bool isPreReleaseVersion(string ver) +in { assert(isValidVersion(ver)); } +body { + foreach (i; 0 .. 2) { + auto di = ver.indexOf('.'); + assert(di > 0); + ver = ver[di+1 .. $]; + } + auto di = ver.indexOf('-'); + if (di < 0) return false; + return isValidNumber(ver[0 .. di]); +} /** Compares the precedence of two SemVer version strings.