diff --git a/source/dub/dependency.d b/source/dub/dependency.d index d17b156..301e339 100644 --- a/source/dub/dependency.d +++ b/source/dub/dependency.d @@ -376,13 +376,16 @@ /** Tests if the specification matches a specific version. */ - bool matches(string vers) const { return matches(Version(vers)); } + bool matches(string vers, VersionMatchMode mode = VersionMatchMode.standard) const { return matches(Version(vers), mode); } /// ditto - bool matches(const(Version) v) const { return matches(v); } + bool matches(const(Version) v, VersionMatchMode mode = VersionMatchMode.standard) const { return matches(v, mode); } /// ditto - bool matches(ref const(Version) v) const { + bool matches(ref const(Version) v, VersionMatchMode mode = VersionMatchMode.standard) const { if (this.matchesAny) return true; if (this.isSCM) return true; + if (this.isExactVersion && mode == VersionMatchMode.strict + && this.version_.toString != v.toString) + return false; return this.m_range.matches(v); } @@ -577,6 +580,13 @@ assert(a.merge(b) == b); assert(b.merge(a) == b); + assert(Dependency("1.0.0").matches(Version("1.0.0+foo"))); + assert(Dependency("1.0.0").matches(Version("1.0.0+foo"), VersionMatchMode.standard)); + assert(!Dependency("1.0.0").matches(Version("1.0.0+foo"), VersionMatchMode.strict)); + assert(Dependency("1.0.0+foo").matches(Version("1.0.0+foo"), VersionMatchMode.strict)); + assert(Dependency("~>1.0.0+foo").matches(Version("1.0.0+foo"), VersionMatchMode.strict)); + assert(Dependency("~>1.0.0").matches(Version("1.0.0+foo"), VersionMatchMode.strict)); + logDebug("Dependency unittest success."); } @@ -727,6 +737,19 @@ /// Tests if this represents the special unknown version constant. @property bool isUnknown() const { return m_version == UNKNOWN_VERS; } + /** Tests two versions for equality, according to the selected match mode. + */ + bool matches(Version other, VersionMatchMode mode = VersionMatchMode.standard) + const { + if (this != other) + return false; + + if (mode == VersionMatchMode.strict && this.toString() != other.toString()) + return false; + + return true; + } + /** Compares two versions/branches for precedence. Versions generally have precedence over branches and the master branch @@ -948,6 +971,11 @@ } } +enum VersionMatchMode { + standard, /// Match according to SemVer rules + strict /// Also include build metadata suffix in the comparison +} + unittest { Version a, b; @@ -1013,6 +1041,11 @@ assert(Version("1.0.0+a") == Version("1.0.0+b")); assert(Version("73535568b79a0b124bc1653002637a830ce0fcb8").isSCM); + + assert(Version("1.0.0").matches(Version("1.0.0+foo"))); + assert(Version("1.0.0").matches(Version("1.0.0+foo"), VersionMatchMode.standard)); + assert(!Version("1.0.0").matches(Version("1.0.0+foo"), VersionMatchMode.strict)); + assert(Version("1.0.0+foo").matches(Version("1.0.0+foo"), VersionMatchMode.strict)); } /// Determines whether the given string is a Git hash. diff --git a/source/dub/dub.d b/source/dub/dub.d index f9c793f..b359c7e 100644 --- a/source/dub/dub.d +++ b/source/dub/dub.d @@ -535,7 +535,7 @@ foreach (ps; m_packageSuppliers) { try { auto versions = ps.getVersions(p); - if (versions.canFind!(v => dep.matches(v))) + if (versions.canFind!(v => dep.matches(v, VersionMatchMode.strict))) continue next_pack; } catch (Exception e) { logWarn("Error querying versions for %s, %s: %s", p, ps.description, e.msg); @@ -595,7 +595,8 @@ } else if (!ver.repository.empty) { pack = m_packageManager.loadSCMPackage(p, ver); } else { - pack = m_packageManager.getBestPackage(p, ver); + assert(ver.isExactVersion, "Resolved dependency is neither path, nor repository, nor exact version based!?"); + pack = m_packageManager.getPackage(p, ver.version_); if (pack && m_packageManager.isManagedPackage(pack) && ver.version_.isBranch && (options & UpgradeOptions.upgrade) != 0) { diff --git a/source/dub/internal/git.d b/source/dub/internal/git.d index d7e169f..24e3e7b 100644 --- a/source/dub/internal/git.d +++ b/source/dub/internal/git.d @@ -103,10 +103,7 @@ if (tag.startsWith("v") && isValidVersion(tag[1 .. $])) { if (num == 0) return tag[1 .. $]; const i = tag.indexOf('+'); - auto r = format("%s%scommit.%s.%s", tag[1 .. (i < 0) ? $ : i], - parts.length > 3 ? "." : "-", num, commit); - if (i > 0) r ~= tag[i .. $]; - return r; + return format("%s%scommit.%s.%s", tag[1 .. $], i >= 0 ? '.' : '+', num, commit); } return null; } @@ -115,19 +112,19 @@ // tag v1.0.0 assert(determineVersionFromGitDescribe("v1.0.0-0-deadbeef") == "1.0.0"); // 1 commit after v1.0.0 - assert(determineVersionFromGitDescribe("v1.0.0-1-deadbeef") == "1.0.0-commit.1.deadbeef"); + assert(determineVersionFromGitDescribe("v1.0.0-1-deadbeef") == "1.0.0+commit.1.deadbeef"); // tag v1.0.0+2.0.0 assert(determineVersionFromGitDescribe("v1.0.0+2.0.0-0-deadbeef") == "1.0.0+2.0.0"); // 12 commits after tag v1.0.0+2.0.0 - assert(determineVersionFromGitDescribe("v1.0.0+2.0.0-12-deadbeef") == "1.0.0-commit.12.deadbeef+2.0.0"); + assert(determineVersionFromGitDescribe("v1.0.0+2.0.0-12-deadbeef") == "1.0.0+2.0.0.commit.12.deadbeef"); // tag v1.0.0-beta.1 assert(determineVersionFromGitDescribe("v1.0.0-beta.1-0-deadbeef") == "1.0.0-beta.1"); // 2 commits after tag v1.0.0-beta.1 - assert(determineVersionFromGitDescribe("v1.0.0-beta.1-2-deadbeef") == "1.0.0-beta.1.commit.2.deadbeef"); + assert(determineVersionFromGitDescribe("v1.0.0-beta.1-2-deadbeef") == "1.0.0-beta.1+commit.2.deadbeef"); // tag v1.0.0-beta.2+2.0.0 assert(determineVersionFromGitDescribe("v1.0.0-beta.2+2.0.0-0-deadbeef") == "1.0.0-beta.2+2.0.0"); // 3 commits after tag v1.0.0-beta.2+2.0.0 - assert(determineVersionFromGitDescribe("v1.0.0-beta.2+2.0.0-3-deadbeef") == "1.0.0-beta.2.commit.3.deadbeef+2.0.0"); + assert(determineVersionFromGitDescribe("v1.0.0-beta.2+2.0.0-3-deadbeef") == "1.0.0-beta.2+2.0.0.commit.3.deadbeef"); // invalid tags assert(determineVersionFromGitDescribe("1.0.0-0-deadbeef") is null); diff --git a/source/dub/packagemanager.d b/source/dub/packagemanager.d index 31990f7..9d6aa95 100644 --- a/source/dub/packagemanager.d +++ b/source/dub/packagemanager.d @@ -173,7 +173,7 @@ } foreach (p; getPackageIterator(name)) - if (p.version_ == ver) + if (p.version_.matches(ver, isManagedPackage(p) ? VersionMatchMode.strict : VersionMatchMode.standard)) return p; return null; @@ -188,9 +188,11 @@ /// ditto Package getPackage(string name, Version ver, NativePath path) { - foreach (p; getPackageIterator(name)) - if (p.version_ == ver && p.path.startsWith(path)) + foreach (p; getPackageIterator(name)) { + auto pvm = isManagedPackage(p) ? VersionMatchMode.strict : VersionMatchMode.standard; + if (p.version_.matches(ver, pvm) && p.path.startsWith(path)) return p; + } return null; } @@ -316,9 +318,11 @@ Package getBestPackage(string name, Dependency version_spec, bool enable_overrides = true) { Package ret; - foreach (p; getPackageIterator(name)) - if (version_spec.matches(p.version_) && (!ret || p.version_ > ret.version_)) + foreach (p; getPackageIterator(name)) { + auto vmm = isManagedPackage(p) ? VersionMatchMode.strict : VersionMatchMode.standard; + if (version_spec.matches(p.version_, vmm) && (!ret || p.version_ > ret.version_)) ret = p; + } if (enable_overrides && ret) { if (auto ovr = getPackage(name, ret.version_)) diff --git a/test/issue2262-exact-cached-version-match.sh b/test/issue2262-exact-cached-version-match.sh new file mode 100755 index 0000000..faa5e5b --- /dev/null +++ b/test/issue2262-exact-cached-version-match.sh @@ -0,0 +1,49 @@ +#!/usr/bin/env bash + +. $(dirname ${BASH_SOURCE[0]})/common.sh + +PACK_PATH="$CURR_DIR"/issue2262-exact-cached-version-match + +# make sure that there are no left-over selections files +rm -f $PACK_PATH/dub.selections.json + +# make sure that there are no cached versions of the dependency +dub remove gitcompatibledubpackage@* -n || true + +# build normally, should select 1.0.4 +if ! ${DUB} build --root $PACK_PATH | grep "gitcompatibledubpackage 1\.0\.4:"; then + die $LINENO 'The initial build failed.' +fi +dub remove gitcompatibledubpackage@* -n || true + +# build with git dependency to a specific commit +cat > $PACK_PATH/dub.selections.json << EOF +{ + "fileVersion": 1, + "versions": { + "gitcompatibledubpackage": { + "repository": "git+https://github.com/dlang-community/gitcompatibledubpackage.git", + "version": "ccb31bf6a655437176ec02e04c2305a8c7c90d67" + } + } +} +EOF +if ! ${DUB} build --root $PACK_PATH | grep "gitcompatibledubpackage 1\.0\.4+commit\.2\.gccb31bf:"; then + die $LINENO 'The build with a specific commit failed.' +fi + +# select 1.0.4 again +cat > $PACK_PATH/dub.selections.json << EOF +{ + "fileVersion": 1, + "versions": { + "gitcompatibledubpackage": "1.0.4" + } +} +EOF +if ! ${DUB} build --root $PACK_PATH | grep "gitcompatibledubpackage 1\.0\.4:"; then + die $LINENO 'The second 1.0.4 build failed.' +fi + +# clean up +rm -f $PACK_PATH/dub.selections.json diff --git a/test/issue2262-exact-cached-version-match/.no_build b/test/issue2262-exact-cached-version-match/.no_build new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/test/issue2262-exact-cached-version-match/.no_build diff --git a/test/issue2262-exact-cached-version-match/dub.sdl b/test/issue2262-exact-cached-version-match/dub.sdl new file mode 100644 index 0000000..90f9893 --- /dev/null +++ b/test/issue2262-exact-cached-version-match/dub.sdl @@ -0,0 +1,2 @@ +name "testproj" +dependency "gitcompatibledubpackage" version="~>1.0.4" diff --git a/test/issue2262-exact-cached-version-match/source/app.d b/test/issue2262-exact-cached-version-match/source/app.d new file mode 100644 index 0000000..ab73b3a --- /dev/null +++ b/test/issue2262-exact-cached-version-match/source/app.d @@ -0,0 +1 @@ +void main() {}