Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/05-dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,11 @@ and `"0.0"` is `>= 0.0.0, < 0.1.0` (before 2026.10.31.1 both matched 0.0.0
alone). The dependency conflict check and the address check of `--locked` read
requirements the same way.

A range matches a pre-release key (`1.0.0-rc.1`) only when it names a
pre-release of the same version, so `*` or `^0.0` reaches none of the keys of a
package that publishes only pre-releases (`compat.glad` publishes
`0.0.0-651a425`). The error then names the exact pin to write.

### `visibility` — whether a dependency's usage crosses this package's own boundary

```toml
Expand Down
3 changes: 3 additions & 0 deletions docs/zh/05-dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@ qux = ">=1.0, <2.0" # Range combination
不带运算符的部分写法同样按 caret 的通配规则:`"0"` 为 `>= 0.0.0, < 1.0.0`,`"0.0"` 为 `>= 0.0.0, < 0.1.0`(2026.10.31.1 之前
两者都只匹配 0.0.0)。依赖冲突检查与 `--locked` 的地址核对以同样的方式读取要求。

范围只在写出同一版本的预发布时才匹配预发布键(`1.0.0-rc.1`),因此对只发布预发布键的包
(`compat.glad` 只发布 `0.0.0-651a425`),`*` 或 `^0.0` 一个键也够不着;这时的错误会给出应写的精确版本。

### `visibility` —— 一个依赖的用法是否跨越本包自己的边界

```toml
Expand Down
12 changes: 12 additions & 0 deletions src/pm/resolver.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,18 @@ resolve_semver(std::string_view ns, std::string_view shortName,
auto msg = std::format(
"dependency '{}': constraint '{}' matches none of: [{}]",
qname, constraint, join(literals));
// A range sees a pre-release only when it names one at the same numbers,
// so when every orderable key is a pre-release (`compat.glad` publishes
// `0.0.0-651a425` alone) no `*` or `^0.0` can reach any of them: the
// remedy is the same exact pin as for an unorderable key (mcpp#370).
if (std::ranges::all_of(parsed, &vr::Version::isPrerelease)) {
const auto highest = std::ranges::max_element(parsed) - parsed.begin();
msg += std::format(
"\n These keys are pre-releases, which a range matches only when "
"it names a pre-release of the same version — pin one exactly:"
"\n {} = \"{}\"",
qname, literals[static_cast<std::size_t>(highest)]);
}
if (!unorderable.empty())
msg += std::format("\n (also published, but not orderable: [{}]){}",
join(unorderable), pin_hint(unorderable));
Expand Down
38 changes: 38 additions & 0 deletions tests/e2e/196_version_identity_and_lock.sh
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ mk_pkg jdk '{ ["latest"] = { ref = "25.0.4.7.1" },
# khistory's shape: the only real release has an unorderable key.
mk_pkg kh '{ ["latest"] = { ref = "pre-v0.0.5" },
["pre-v0.0.5"] = { url = "u", sha256 = "0" } }'
# compat.glad's shape: the only key is a pre-release (mcpp#370).
mk_pkg pre '{ ["0.0.0-651a425"] = { url = "u", sha256 = "0" } }'
# Only pre-releases, more than one: the pin offered is the highest.
mk_pkg rc '{ ["1.0.0-rc.1"] = { url = "u", sha256 = "0" },
["1.0.0-rc.2"] = { url = "u", sha256 = "0" } }'
# jdk-temurin's shape, but with two REAL entries: a genuine precedence tie.
mk_pkg tie '{ ["1.0.0+a"] = { url = "u", sha256 = "0" },
["1.0.0+b"] = { url = "u", sha256 = "0" } }'
Expand Down Expand Up @@ -139,6 +144,39 @@ use_dep kh '=pre-v0.0.5'
grep -qE 'invalid version constraint|not ordered versions' b5b.log \
&& fail "'=<unorderable key>' must resolve to that key" b5b.log

# ── 4b. Only pre-releases: no range reaches them, so the error says what does ──
# The keys are orderable, so this is not the unorderable branch above, but a
# range still sees a pre-release only when it names one (mcpp#370).
for c in '*' '^0.0'; do
use_dep pre "$c"
"$MCPP" build > b4c.log 2>&1 && fail "'$c' must not reach a pre-release" b4c.log
grep -q 'matches none of' b4c.log || fail "expected '$c' to match none" b4c.log
grep -q 'These keys are pre-releases' b4c.log \
|| fail "expected the error to say why '$c' matches none" b4c.log
grep -q 'acme.pre = "0.0.0-651a425"' b4c.log \
|| fail "the error for '$c' must show the exact pin that works" b4c.log
done

use_dep rc '^1.0'
"$MCPP" build > b4d.log 2>&1 && fail "'^1.0' must not reach a pre-release" b4d.log
grep -q 'acme.rc = "1.0.0-rc.2"' b4d.log \
|| fail "the pin offered must be the highest pre-release" b4d.log

# The pin offered resolves (and fails later, at install, for want of a payload).
use_dep pre '0.0.0-651a425'
"$MCPP" build > b4f.log 2>&1 && fail "expected the unseeded payload to fail" b4f.log
grep -q 'matches none of' b4f.log \
&& fail "the exact pre-release pin the error offers must resolve" b4f.log
grep -q '0\.0\.0-651a425' b4f.log || fail "the exact key must be addressed literally" b4f.log

# A release among the keys can be reached by a range, so a miss there is an
# ordinary one and carries no pin.
use_dep im '^2'
"$MCPP" build > b4e.log 2>&1 && fail "'^2' must match none of im's keys" b4e.log
grep -q 'matches none of' b4e.log || fail "expected '^2' to match none" b4e.log
grep -q 'pre-releases' b4e.log \
&& fail "a package that publishes a release must not get the pre-release pin" b4e.log

# ── 5. A genuine precedence tie is refused, not guessed ───────────────────
use_dep tie '^1.0'
"$MCPP" build > b6.log 2>&1 && fail "a tie must not silently pick one" b6.log
Expand Down
Loading