Skip to content

chore: migrate descriptors to SPEC-001 short-name identity (mcpp 0.0.106) - #120

Merged
Sunrisepeak merged 6 commits into
mainfrom
chore/spec001-short-name-migration
Jul 25, 2026
Merged

chore: migrate descriptors to SPEC-001 short-name identity (mcpp 0.0.106)#120
Sunrisepeak merged 6 commits into
mainfrom
chore/spec001-short-name-migration

Conversation

@Sunrisepeak

Copy link
Copy Markdown
Member

What

mcpp 0.0.106 restores the canonical identity form: namespace carries the hierarchy, name is a single atomic segment.

namespace = "compat", name = "compat.zlib"   ->   name = "zlib"

45 descriptors migrated. The 3 namespace-less public module packages (imgui / ffmpeg / opencv) are unchanged — a bare name IS their full identity.

Why the rule flipped

0.0.105 briefly required the opposite (name spelled as the fully-qualified <namespace>.<name>). That was an encoding constraint, not a design rule — it existed only because mcpp re-derived the wire name instead of using the literal package.name it had already read. 0.0.106 uses the literal, and the constraint disappears.

Full normative text: mcpp docs/spec/package-identity.md (SPEC-001), landed in mcpp-community/mcpp#280.

Three short names now repeat across namespaces

imgui, ffmpeg and lua — compat's versus the default namespace's, and compat's lua versus mcpplibs.capi's.

That is legal: identity is the PAIR, and only the pair must be unique. It needs xlings >= 0.4.69 (openxlings/xlings#381), which keys the index by (namespace, name); mcpp 0.0.106 bundles it.

Verified against a local path index built from this branch:

request resolves to store dir
[dependencies] imgui mcpplibs:imgui mcpplibs-x-imgui
[dependencies.compat] imgui compat:imgui compat-x-imgui

No ambiguity — the namespace-less descriptors take the index's default namespace, so the pairs differ.

Lint inverted to match

check_package_name.lua now requires name to be atomic and the hierarchy to live in namespace. The legacy fully-qualified spelling stays accepted (the prefix is stripped before judging), so nothing published before this is invalidated — this repo simply no longer uses it.

No filename lint: the filename is not part of identity. mcpp 0.0.106 discovers descriptors by declared identity, falling back to a scan when the recommended names miss, so a descriptor may be called anything. <name>.lua / <namespace>.<name>.lua remain recommended because they hit the fast path.

Floor bump is NOT optional

min_mcpp / latest_mcpp0.0.106, in lock-step with the CI pin. A client older than 0.0.106 re-derives <ns>.<short>, misses the short-name key, and reports a bare E_NOT_FOUND with nothing pointing at the cause. The index.toml floor is what turns that into a clear E0006 upgrade message instead.

Verification

Run locally against the actually released mcpp 0.0.106 (xlings install mcpp@0.0.106, bundling xlings 0.4.69):

  • 48/48 descriptors pass lua syntax, mirror-URL, identity lint and mcpp xpkg parse.
  • Identity pairs (namespace, name) are unique; no name still carries a dot.
  • Counterexamples behave as specified: non-atomic short name rejected, dotted name without a namespace rejected, legacy FQN accepted.

Docs: docs/repository-and-schema.md gains a 包身份 section (rules, the three same-short-name pairs, why filenames are free-form); CI-behaviour and local-lint sections updated.

…106)

mcpp 0.0.106 restores the canonical identity form: `namespace` carries the
hierarchy, `name` is a single atomic segment. 0.0.105 briefly required the
opposite (name spelled as the fully-qualified `<namespace>.<name>`), which was
an encoding constraint rather than a design rule — it existed only because mcpp
re-derived the wire name instead of using the literal it had already read.

  namespace = "compat", name = "compat.zlib"   ->   name = "zlib"

45 descriptors migrated; the 3 namespace-less public module packages
(imgui/ffmpeg/opencv) are unchanged — a bare name IS their full identity.

Three short names now repeat across namespaces: `imgui`, `ffmpeg` and `lua`
(compat's versus the default namespace's, and compat's `lua` versus
mcpplibs.capi's). That is legal — identity is the PAIR, and only the pair has
to be unique. It needs xlings >= 0.4.69 (openxlings/xlings#381), which keys the
index by (namespace, name); 0.0.106 bundles it. Verified against a local path
index: bare `imgui` resolves to `mcpplibs:imgui` and `[dependencies.compat]
imgui` resolves to `compat:imgui`, each installing to its own store dir.

check_package_name.lua is inverted to match: `name` must be atomic, hierarchy
belongs in `namespace`, and the legacy fully-qualified spelling stays accepted
(the prefix is stripped before judging) so nothing published before this is
invalidated. No filename lint — the filename is not part of identity.

min_mcpp/latest_mcpp move to 0.0.106 in lock-step with the CI pin. This one is
NOT optional: a client older than 0.0.106 re-derives `<ns>.<short>`, misses the
short-name key, and reports a bare E_NOT_FOUND with nothing pointing at the
cause.

Verified locally: all 48 descriptors pass the inverted lint; identity pairs are
unique; no `name` still carries a dot; the three counterexamples (non-atomic
short name, dotted name without a namespace, legacy FQN) each behave as
specified.
CI caught a real conflict the short-name migration introduced:

    build_index failed: duplicate package identity 'compat:ffmpeg'

`imgui`, `ffmpeg` and `opencv` declared `namespace = ""`, meaning "inherit the
index's default namespace". 11 workspace members register this repo as
`[indices] compat = { path = ... }`, so under those members the repo default IS
`compat` — and the namespace-less descriptors became `compat:ffmpeg` /
`compat:imgui`, colliding head-on with the freshly shortened `compat.ffmpeg`
and `compat.imgui`.

The migration exposed it, but the fragility is older and independent of it: a
namespace-less descriptor's identity depends on the NAME THE CONSUMER HAPPENS
TO REGISTER THE INDEX UNDER. The same repo mounted as `compat` and as `default`
gives those three packages two different identities. That is exactly what
SPEC-001 exists to remove — identity should be what the descriptor declares.

So rather than reverting the two colliding descriptors to the legacy spelling
(which would have unblocked CI while leaving the trap armed), declare the
namespace outright:

    namespace = "",          name = "imgui"     ->   namespace = "mcpplibs"

Identity is now context-free. Verified by simulating all three registration
styles (repo default = mcpplibs / compat / default): zero duplicate identities
in every case, where before `compat` produced two.

Consumers are unaffected: a bare `imgui` still resolves via the default-namespace
search path, and `[dependencies.compat] imgui` still resolves to compat's.
Checked against the released mcpp 0.0.106 with the index mounted as `compat` —
builds clean, no duplicate-identity error.

48/48 descriptors still pass the lua lint and `mcpp xpkg parse`.
…gration

Shortening every `package.name` (SPEC-001) also changed how a descriptor must
ADDRESS another package: `pkginfo.install_dir("compat:xcb-proto")`, not the old
`compat:compat.xcb-proto`. Two install hooks kept the old spelling, and neither
failed where the mistake was:

  * compat.xcb could not find xcb-proto's XML, so c_client.py never ran and the
    install aborted with a bare `[xcb] failed`. libxcb.so was never built, and
    CI reported it three levels downstream as
    `libX11.so: undefined reference to xcb_connection_has_error` in gui-stack,
    imgui-module and imgui-window.

  * compat.x11 only checked the lookup for nil, never `os.isdir`, so it carried
    on with a directory that does not exist and generated its keysym tables from
    no headers at all — silently, with a successful build.

Both now try the SPEC-001 spelling first and keep the pre-SPEC-001 ones as
fallbacks, so these descriptors still resolve against an unmigrated index.
compat.x11 gets the same isdir guard compat.xcb already had.

Add tests/check_cross_package_refs.lua to the lint job: it resolves every
`pkginfo.install_dir`/`build_dep` string against the identities the repository
actually declares. A reference into a foreign namespace (xim:python) is skipped,
and a ladder of spellings only needs one that resolves, so legacy fallbacks stay
legal. Matching is against the literal `package.name`, deliberately not a
normalised form — normalising the dots away would have called the broken
reference resolvable.

Verified with mcpp 0.0.106 against this branch: gui-stack 5/5, imgui-module 1/1,
imgui-window 1/1. Reverting the descriptor fix makes the lint flag exactly these
two references and nothing else.
0.0.104-0.0.106 skipped the SONAME alias whenever ninja was given explicit goal
targets, which is exactly how `mcpp test` drives a build — so gui-stack,
imgui-module and imgui-window failed here on any of those versions, with or
without this branch's descriptor changes (probe PRs #121 and #122 reproduced it
over unmodified descriptors).
The re-pin is for the soname-alias fix; SPEC-001 itself landed in 0.0.106 and
`mcpp xpkg parse` has enforced the name rule since then. The version floor
below still moves to 0.0.107 — that is the pin, not the rule's origin.
…#285)

The Windows job has failed on the ffmpeg member since 0.0.104, independently of
this branch: `mcpp test` named all 2281 of its compile units as ninja goals,
Windows joined them into a 50,781-character cmd.exe command line (cap 8191), and
the command never ran — a bare 127 from cmd.exe with no output from ninja or
mcpp. 0.0.108 routes the goal set through build.ninja as a phony edge.

Together with the soname-alias fix in 0.0.107 (which unblocked gui-stack,
imgui-module and imgui-window on linux), this should be the last version-side
blocker for this migration.
@Sunrisepeak
Sunrisepeak merged commit 7fef5ec into main Jul 25, 2026
5 checks passed
@Sunrisepeak
Sunrisepeak deleted the chore/spec001-short-name-migration branch July 25, 2026 20:10
Sunrisepeak added a commit that referenced this pull request Jul 27, 2026
…openssl (#125)

* chore: pin mcpp 0.0.109, hoist the compat index redirect, fix compat.openssl

## mcpp 0.0.109 (MCPP_VERSION + index.toml floor)

0.0.109 makes a bare dependency take BOTH halves of its wire address from the
descriptor the identity gate accepted (mcpp#286). That is the client-side other
half of the SPEC-001 short-name migration (#120): mcpp used to take the name
from the descriptor and the namespace from the request, so a bare `gtest =
"1.15.2"` addressed `mcpplibs:gtest`, a key no index has. It survived only on a
hardcoded `compat.<short>` retry that the short names removed — leaving every
bare request against this index broken on 0.0.108. min_mcpp moves with the pin
so consumers get a client that can address the index they are handed.

## [indices]: hoisted to the workspace root

The 13 members that consume `compat` no longer repeat `[indices] compat =
{ path = "../../.." }`; the root declares it once and they inherit it, with the
relative path resolved against the workspace root (mcpp#224, 0.0.97).

Exactly one entry, and `compat` specifically, because the table is keyed by
NAMESPACE:

  * declared under a name no dependency asks for, the index is never
    registered and resolution silently falls through to the published remote
    index — the checkout under test is then not what is being tested;
  * declared under several namespaces it registers N separate project repos,
    and every lookup afterwards fails with an N-way ambiguity between what is
    physically one descriptor (mcpp#238 / xlings#374).

Members needing another namespace keep their own declaration, which REPLACES
the inherited table rather than merging — which is what holds each member to
one project index repo. mcpp.toml and index.toml also join the workflow's
paths filter; a change to either can break every member and neither gated a
run before.

## compat.openssl

`name`: `compat.openssl` -> `openssl`. It was the index's last legacy
fully-qualified spelling (SPEC-001 §3.2).

`./config` now passes `--libdir=lib`. Unset, OpenSSL derives the install libdir
as `lib$target{multilib}` and `linux-x86_64` declares `multilib => "64"`, so
the archives landed in $prefix/lib64 while `-Llib` and the post-build check
looked at $prefix/lib. linux-aarch64 and both darwin64 targets declare no
multilib and resolve to plain `lib` — which is how this validated on an arm64
Mac while being broken for every x86_64 Linux consumer. Reproduced against the
published descriptor before fixing: the install tree came out as bin/ include/
lib64/ and install() failed its own archive check.

Also: linux links `-l:libssl.a -l:libcrypto.a` (naming the archive rather than
letting the driver resolve a name a shared object would win — one stray -L
ahead of ours and the consumer picks up the host libssl.so.3) plus `-ldl
-lpthread` for musl and pre-2.34 glibc; the build log moved into the install
prefix, since the old spot was deleted mid-build by the very os.tryrm that
preceded install; `RANLIB=/usr/bin/ranlib` is confined to macOS, the platform
whose llvm-ranlib rejects `-c`; a redundant `os.cd` that would have broken the
relative-srcroot fallback is gone; and install() probes for `perl` up front,
since OpenSSL's ./config IS a Perl script and no xim:perl exists to declare.

## Two members for it

`tests/examples/openssl` — direct dependency, inherits the root `compat`
redirect, so the descriptor in this checkout is what gets built. Asserts both
archives really link (SSL_CTX_new + EVP sha256) and the binary carries no
dynamic ssl/crypto dependency.

`tests/examples/asio-ssl` — the asio `ssl` feature end-to-end, a real TLS
handshake over loopback. It declares no member-level [indices] on purpose:
inheriting `compat` means asio resolves from the published index while its
feature dep, compat.openssl, comes from this checkout — which is how an
unmerged compat descriptor can be exercised through a published consumer.

The test moved out of asio-module, where it had been guarded by
MCPP_FEATURE_SSL. A feature's `defines` apply to the package's own TUs, not to
consumers, so that file compiled to nothing and passed while asserting
nothing — the reason the libdir bug reached main green. It now keys off
HAVE_ASIO_SSL, which the member sets in its own cfg-gated cxxflags.

## Docs

docs/package-types.md still taught `name = "compat.<lib>"`. README, the
repository/schema doc (new "索引重定向" section, corrected `features` row) and
the openssl design spec are updated to match what the code does.

* fix(openssl): declare xim:make on macosx, and print the log tail on failure

The macOS workspace job failed on both new members ~43s after the tarball
landed — far too early for a compile, and with nothing to go on: install()
writes everything to an on-disk log (xim's interface mode swallows subprocess
stdout) and xlings surfaces the failure as a bare
`E_INTERNAL: [openssl] failed:`.

Two changes, one for the likely cause and one so the next failure says what it
was.

macosx now declares `deps = { "xim:make@latest" }`. The previous comment
claimed it was unnecessary because "macOS ships GNU Make at /usr/bin/make" —
it ships GNU Make **3.81**, the last GPLv2 release, frozen in 2006, and
OpenSSL 3.x's generated Makefile does not build with it. That matches the
timing exactly: ~30s of Perl Configure, then make failing on sight.
compat.openblas already declares this dep on macosx.

Each build step now runs through a helper that, on failure, names the step and
prints the last 40 lines of the log. The log also opens with `make --version`,
because 3.81-vs-4.x is precisely the distinction that is invisible after the
fact. The tail is passed as one pre-formatted argument rather than a format
string — build output contains `%` often enough that formatting it is its own
failure mode.

Verified on linux (cold): log opens with "GNU Make 4.3", openssl and asio-ssl
members both pass.

* fix(openssl): drop the macosx xim:make dep, pin Apple's cc, dump build logs in CI

`xim:make` has no macOS build — xim-pkgindex's pkgs/m/make.lua declares only an
`xpm.linux` block — so yesterday's macosx dep failed resolution outright with
`E_INVALID_INPUT: package 'xim:make@latest' not found`, before install() ran.
Reverted, with the finding written down. compat.openblas declares the same dep
on macosx and is broken identically; nobody has noticed because that package is
Windows-only in the test suite, so its macOS path is never taken.

That leaves the original macOS failure — 43s after download, no message —
still undiagnosed, so this stops guessing at it:

  * The workspace job gains an `if: failure()` step that finds every
    `mcpp_*_build.log` under the members and the registry and prints its tail.
    install()-driven packages build through their own Make/Configure system,
    xim's interface mode swallows that output, and xlings reports only
    `E_INTERNAL: [<pkg>] failed:` — so a platform-specific break otherwise
    costs one full CI round-trip per hypothesis.
  * The log now opens with make/cc/perl versions, not just make's.

And one likely cause addressed while here: OpenSSL is configured and built
outside mcpp's compile rules, so it inherits none of the resolved toolchain's
flags — it just runs `cc`. On macOS the `cc` in PATH is xim's llvm, which has
no macOS SDK wired up, so every compile would fail on <stdio.h>. macOS now
builds with `CC=/usr/bin/cc`, Apple's driver, which finds the SDK itself
(same spirit as the RANLIB pin, which is already macOS-only). resolve_make()
also prefers a Homebrew gmake when one exists, since /usr/bin/make is GNU Make
3.81.

If the SDK guess is wrong, the log tail now says what is.

Verified on linux (cold): log opens with GNU Make 4.3 / gcc 16.1.0, openssl
member passes.

* fix(openssl): pass RANLIB as a make command-line assignment, not an env var

The macOS build now gets all the way through configure and compile — the CC
pin was the missing piece there — and dies in `make install_sw`, one line after
copying libcrypto.a into place:

    install libcrypto.a -> .../3.5.1/lib/libcrypto.a
    llvm-ranlib: error: Invalid option: '-c'
    make: *** [install_dev] Error 1

which is precisely what the RANLIB override exists to prevent. OpenSSL's
`darwin-common` sets `ranlib => "ranlib -c"` (Configurations/10-main.conf:1844)
and PATH resolves `ranlib` to the toolchain's llvm-ranlib, which rejects `-c`.

The override was not taking effect, and that is a regression I introduced when
confining it to macOS: I moved it from `make RANLIB=… install_sw` to
`RANLIB=… make install_sw`. The first is a command-line assignment and beats
the Makefile's own definition; the second is only an environment variable,
which a Makefile assignment overrides absent `make -e` — so it silently did
nothing and the build failed exactly as if it were not there. Restored to the
command-line form, with a comment saying why the position matters.

Also confirmed from the same log that `--libdir=lib` does its job on macOS:
the archive installs to `3.5.1/lib/`, not `lib64/`.

Verified on linux (cold): openssl member passes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant