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
feat: a library states its default link form, and the root build program reads each dependency's form (#642 E1, E2)
- [targets.<n>] linkage and its row form state a library's default form; kind =
"shared" stays the constraint. Precedence: the root edge, a written
dependency_linkage, the package default, static. An explicit override is
honoured with an information line and no degraded record.
- The link-form resolution is computed once before the root build program
(hasSources from the scanner's own package_source_files) and applied after
the scan as before.
- The root build program receives MCPP_DEP_<NAME>_LINKAGE under the names of
MCPP_DEP_<NAME>_DIR; mcpp::dep_linkage() reads it. Dependency programs do not.
- Form B descriptors read linkage the same way.
- Unit tests (linkage_form, manifest, xpkg), e2e 692 and 693, docs 04, 22, 30
with their Chinese mirrors, SPEC-004 1.4.
Copy file name to clipboardExpand all lines: docs/04-mcpp-toml.md
+39-2Lines changed: 39 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -321,6 +321,7 @@ required_features = ["gui"] # only built when feature `gui` is
321
321
|`required_features`| The target is emitted only when **every** listed feature is active in the build; otherwise it is silently skipped. A gate only — it does not activate features (use `--features` / `[features].default`). **One exception, and it is not a second rule:** when this target is requested as a host tool (`tools = [...]`, §2.14), the target is what was *asked for*, so its `required_features` become the sub-build's *inputs*. Same field, one meaning — the resolution just runs in the opposite direction. |
322
322
|`windows_subsystem`*(2026.9.12.2+)*| The PE subsystem of an executable: `"console"` (the default) or `"windows"`, a GUI program that starts without a console. Reaches this target's link and no other, and renders nothing on a target that is not PE. See the section above. |
323
323
|`windows_entry`*(2026.9.12.2+)*| The entry function the program defines: `"main"` (the default), `"wmain"`, `"WinMain"` or `"wWinMain"`. See the section above. |
324
+
|`linkage`*(2026.9.15.2+)*| A library target's **default** link form, `"static"` or `"shared"`: the form a consumer that writes no `linkage` receives. Unlike `kind = "shared"` it is not a constraint, so a consumer's explicit statement is honoured. Refused beside `kind = "shared"` and on a program target. See [`dependency_linkage`](#dependency_linkage--static-or-shared-is-the-consumers-decision). |
324
325
325
326
> **Scope (important):**`defines` / `cxxflags` / `cflags` on a target apply **only to that
326
327
> target's exclusive entry source** (its `main`) — never to shared module/impl objects, which
@@ -452,12 +453,48 @@ whose C library is linked statically — which is the **default for musl** —
452
453
packages write it without choosing anything. Absence of a statement is not a
453
454
statement.
454
455
456
+
**A package can also state a default***(2026.9.15.2+)*, which is not a
457
+
constraint:
458
+
459
+
```toml
460
+
[targets.fw]
461
+
kind = "lib"
462
+
linkage = "shared"# the form a silent consumer receives
463
+
464
+
[target.'cfg(env="android")'.targets.fw]
465
+
linkage = "shared"# the same, on the rows the selector matches
466
+
```
467
+
468
+
- The form is decided, most specific statement first, by the root's
469
+
`linkage` on the dependency's edge, the root's `dependency_linkage` when it
470
+
is written (in `[build]` or in the active profile), the package's
471
+
`linkage`, and `static`.
472
+
- An explicit statement that differs from the package's default is honoured.
473
+
It is not a degradation, so `--strict` accepts it, and one information line
474
+
(`Linkage`) names both statements.
475
+
-`kind = "shared"` and `linkage` in one table are refused, because a
476
+
constraint leaves no default to state; a row states one of `kind` and
477
+
`linkage`, and the last matching statement replaces the earlier one, so a
478
+
row's `linkage = "static"` returns a package the unconditional table
479
+
constrains to `shared` to a form its consumers choose.
480
+
- A default the target cannot honour (a fully static image, a freestanding
481
+
target) falls back without a warning, because nobody asked for it.
482
+
- An engine before 2026.9.15.2 reports `[targets.<n>] linkage` as an
483
+
unsupported key (silently for a dependency) and links the package statically;
484
+
engines from 2026.9.14.2 refuse a row table without `kind`. A package that
485
+
relies on the key states that engine floor.
486
+
487
+
The root project's build program reads the form each dependency takes in the
488
+
build through `mcpp::dep_linkage("name")` ([30 — build.mcpp](30-build-mcpp.md)),
489
+
so a generated loader entry or import declaration follows the same decision.
490
+
455
491
A request the constraint refuses is linked in the form the package allows,
456
492
with a warning that names the package's statement (`its manifest states
457
493
[targets.fw] kind = "shared", ...`); `--strict` turns the warning into an
458
494
error. `mcpp why deps` reports each dependency's form and the reason for it:
Copy file name to clipboardExpand all lines: docs/30-build-mcpp.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -885,6 +885,7 @@ The running program receives the build context as `MCPP_*` variables
885
885
|`MCPP_FEATURE_<NAME>`|`mcpp::has_feature("name")`| set to `1` per active feature (same `<NAME>` sanitization as the `MCPP_FEATURE_` compile macro) |
886
886
|`MCPP_FEATURES`| — | comma-separated active feature list |
887
887
|`MCPP_DEP_<NAME>_DIR`|`mcpp::dep_dir("name")`| the resolved install dir of each declared dependency (canonical **and** namespace-stripped name spellings; same `<NAME>` sanitization as `MCPP_FEATURE_`). Received by dependencies' build.mcpp **and** the root project's (the root runs after dependency resolution, 0.0.100+) |
888
+
|`MCPP_DEP_<NAME>_LINKAGE`*(2026.9.15.2+)*|`mcpp::dep_linkage("name")`| the link form each dependency takes in this build, `static` or `shared`, under the names `MCPP_DEP_<NAME>_DIR` uses; empty for a dependency with no library form. The value is the resolution that decides what is linked, so a generated loader entry or `dllimport` declaration follows it. Received by the **root project's** build.mcpp only: the root decides every dependency's form, and a dependency's program runs before packages discovered after it, whose programs supply facts the answer depends on, so `dep_linkage` is always empty there |
888
889
889
890
These values are folded into the re-run key **unconditionally** — changing the
890
891
target, profile, or feature set re-runs the program without any
0 commit comments