You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit c0cc681
Browse filesBrowse the repository at this point in the historyBrowse files
design: D8 restated — the flag was the wrong question, the scope is the right one
Correction first: provisioning does not write into the project's layer. It
calls with make_xlings_env, which carries no projectDir, so the child runs in
global scope against mcpp's registry home. The earlier claim that forcing was
safely contained is withdrawn, and three measured facts follow from the real
scope, including why the runner needed a payload lookup at all.
xlings already implements both halves of the requirement: an existing payload
is mapped rather than reinstalled, and install activates whenever nothing is
active for that name. Whether mcpp receives that behaviour depends on the
environment it asks in, not on a flag.
Design P provisions in project scope, where the project's own SubOS is the
layer written; with a named SubOS the global layer is not merged, so the
declaration is what activates and no forcing is needed. Design G keeps the
global scope and forces, which lets two checkouts fight and cannot be verified.
P is recommended, and it carries one question that is not mcpp's alone: whether
a project environment inherits the machine's. Both branches are stated with
what they cost.
For mcpp the project dir is `<project>/.mcpp`, so the layer written is
789
-
`<project>/.mcpp/.xlings/subos/{<name>|_}/.xlings.json`. Two checkouts that
790
-
declare different versions of one tool cannot disturb each other, and the
791
-
machine's global choice is not touched. That is what makes forcing activation
792
-
safe to do without a further opt-in.
793
-
794
-
### 16.3 The rule falls out of the two value spellings
795
-
796
-
W1 gave the manifest two ways to write an entry, and they ask for different
797
-
things:
798
-
799
-
| Entry | The project is asking for | Activation |
800
-
|---|---|---|
801
-
|`picolibc-riscv = "1.8.12"`| this version | force it |
802
-
|`code = ""`| presence, version unconstrained | do not force; xlings' own rule applies |
803
-
804
-
Forcing on an unconstrained entry would change a version the project never
805
-
named, on a machine where something was already active. The empty spelling is
806
-
the author saying they do not care, and mcpp should not decide for them.
807
-
808
-
### 16.4 Requesting activation is not the same as obtaining it
809
-
810
-
This is the part that decides the shape. When `install_packages` forces the
811
-
switch and the switch fails, the installer logs a **warning** and the call
812
-
still exits zero:
813
-
814
-
```cpp
815
-
if (useRet != 0)
816
-
log::warn("failed to activate {}@{} in current subos", match.name, match.version);
817
-
```
818
-
819
-
So `useAfterInstall: true` alone gives mcpp a request whose outcome it cannot
820
-
read — the exact shape #531 was filed for, arrived at from the other side. The
821
-
`use_version` capability, by contrast, returns `cmd_use`'s own exit code
822
-
(`src/capabilities.cpp:208-220`), which mcpp can check.
823
-
824
-
### 16.5 The proposed shape
825
-
826
-
1. **One `install_packages` batch, unforced**, for every declared entry, as
827
-
today. Its result is already read (`!called || childRc != 0`), and its
828
-
failure keeps naming the manual command.
829
-
2. **Then one `use_version` per entry that named a version**, exit code
830
-
checked. A failure is a hard build error naming the tool, the version and
831
-
the layer, because the manifest asked for something the environment did not
832
-
give.
833
-
3. **Nothing extra for `""` entries.** Presence was the whole request, and step
834
-
1 satisfied it.
835
-
4. The provisioning **stamp covers both steps**, so the common build performs
836
-
neither. Its key is the hash of the declared set, so editing a version
837
-
re-runs the pair once.
838
-
839
-
The cost is N calls for N versioned entries on the rare path. The alternative —
840
-
a second forced `install_packages` batch — is one call, and it buys that by
841
-
giving up the exit code, which is the thing being paid for.
842
-
843
-
`use_version` takes a plain name ("Use plain name (e.g. gcc not xim:gcc)"), so
844
-
mcpp passes the target without its namespace and keeps the namespace for the
845
-
install address only (§3 W2).
846
-
847
-
### 16.6 What to verify
790
+
(`prepare.cppm:3421`), and `make_xlings_env` carries no `projectDir`
791
+
(`src/config.cpp:129`), so no `XLINGS_PROJECT_DIR` reaches the child and the
792
+
cwd walk finds nothing — mcpp writes `<project>/.mcpp/.xlings.json`, one level
793
+
below the directory a walk from the project root inspects. The child therefore
794
+
runs in **global** scope against mcpp's registry home, where
795
+
`Config::workspace_mut()` is the global workspace and `effective_workspace()`
796
+
is the global workspace.
797
+
798
+
Three consequences follow, and they explain measurements this repository
799
+
already has:
800
+
801
+
1.`active` is read from a workspace shared by **every mcpp project on the
802
+
machine**, so a version another project activated is enough to make this
803
+
project's declaration lose.
804
+
2. Forcing the switch there would let two checkouts flip each other. The
805
+
decline is protective in this scope, not obstructive.
806
+
3. The install's view and the shim's view disagree. The install activates in
807
+
the registry's global workspace; a program invoked from the project
808
+
directory resolves through the project's own layers. That is exactly the
809
+
`qemu-aarch64-static is not installed in this subos (_)` measured during the
810
+
2026.9.2.1 verification, and the reason mcpp had to add a payload-directory
811
+
lookup for the runner at all (§13.2 of the runner design).
812
+
813
+
### 16.3 The two designs
814
+
815
+
**Design P — provision in the project's environment.** mcpp calls with
816
+
`make_project_xlings_env(cfg, root)`, so `XLINGS_PROJECT_DIR=<project>/.mcpp`
817
+
and the child runs in project scope. `Config::workspace_mut()` is then the
818
+
project's own SubOS workspace at `<project>/.mcpp/.xlings/subos/{name|_}`
819
+
(`config.cpp:348-353`), and `effective_workspace()` includes the project's
820
+
layers.
821
+
822
+
Under P, xlings' native behaviour is what is wanted and no flag is needed —
823
+
with one condition. In **Anonymous** mode the merge still includes the global
824
+
layer (§13.3), so a globally active version still shadows the declaration. In
825
+
**Named** mode the global layer is dropped, `active` is empty for a name the
826
+
project has not pinned, and the install activates exactly what was declared.
827
+
So P is "provision in the project scope, and the project's environment is its
828
+
own SubOS".
829
+
830
+
**Design G — keep provisioning globally, and force.** Pass
831
+
`useAfterInstall: true`. It makes the declaration win, and it makes two
832
+
checkouts fight over one shared workspace. It also cannot be verified: a forced
833
+
switch that fails is a `log::warn` inside a call that exits zero
834
+
(`installer.cpp`), so mcpp would hold a request whose outcome it cannot read.
835
+
836
+
**P is the recommendation**, and it is the one that matches what `[xlings]`
837
+
claims to be — a project-level isolated environment. G's forcing is not needed
838
+
once the scope is right, and G's shared workspace is the thing the isolation
839
+
exists to avoid.
840
+
841
+
### 16.4 What P costs, and what has to be decided with it
842
+
843
+
P is not free, and the cost is the same fact §13.3 records: a Named SubOS does
844
+
not inherit the machine's global pins. A project that declares one gets an
845
+
environment holding what it declared and what was installed into it, and
846
+
nothing else. That is isolation working as specified, and it is a change for
847
+
every project that today relies on the machine's tools being visible.
848
+
849
+
So P carries one decision that is not mcpp's alone:
850
+
851
+
**Does an mcpp project's environment inherit the machine's, or not?**
852
+
853
+
- Inheriting (Anonymous) keeps every existing project building and leaves the
854
+
declaration able to lose to a globally active version.
855
+
- Not inheriting (Named) makes the declaration authoritative and requires the
856
+
project to declare everything it uses.
857
+
858
+
A middle position exists and should be considered rather than assumed away:
859
+
provision in project scope while staying Anonymous, and accept that a declared
860
+
version can still lose to a globally active one — then report that case rather
861
+
than silently accepting it. The installer already emits the sentence; mcpp
862
+
would have to read it, which returns to the verification problem G has.
863
+
864
+
### 16.5 What to verify, whichever is chosen
848
865
849
866
| # | Criterion |
850
867
|---|---|
851
868
| A1 | A project declaring a version different from the machine's active one builds and runs against the declared version |
852
-
| A2 | The machine's global workspace is unchanged after that build, measured on the file |
869
+
| A2 | The machine's global workspace file is unchanged after that build |
853
870
| A3 | Two checkouts declaring different versions of one tool each get their own, in one session |
854
871
| A4 | An entry with `""` on a machine where another version is active leaves that version active |
855
-
| A5 | A `use_version` that fails ends the build with a message naming tool, version and layer — not a warning |
872
+
| A5 | A declared version that could not be made active ends the build with a message naming tool, version and layer — never a warning inside a successful build|
856
873
| A6 | The second build of an unchanged project performs no install and no switch |
874
+
| A7 | A program the project did not declare still resolves, or fails with a message naming the environment it was looked up in |
857
875
858
-
A5 is the one that must be seen failing first: with `useAfterInstall` and
859
-
without the explicit switch, the same situation produces a warning inside a
860
-
successful build, which is what the design is choosing against.
876
+
A2 and A3 are the ones that fail under G today. A7 is the one that changes
877
+
meaning between the two branches of §16.4, and it is where the decision becomes
0 commit comments