feat(features): Feature System v2 — package-owned defines + capabilities (v0.0.69)#181
Merged
Merged
Conversation
…wned defines A feature table entry may now be written in table form carrying `defines` (and `implies`); when the feature is active each bare define desugars to -D<x> on the package compile flags, alongside the automatic MCPP_FEATURE_<NAME>. The array shorthand keeps meaning implied-features. Restricts feature compile contributions to package-owned macros (no free-form cflags/ldflags) per the capability-model design. Tests: e2e/80_feature_defines.sh, unit Manifest.FeatureTableFormDefinesAndImplies. Design: .agents/docs/2026-06-29-feature-capability-model-design.md
…e-provider binding A feature/package may `requires` an abstract capability instead of a concrete package; providers declare `provides`. The resolver binds exactly one provider from the dependency graph, deterministically: - explicit [capabilities] pin (or --cap cap=provider) wins; - 0 providers / >=2 unpinned providers are hard errors (never a silent guess); - a single provider binds with no config. Link/include requirements still flow through normal dependency mechanics; this is the selection-and-validation layer that turns a silently-wrong or missing backend into a loud configure-time error. Parsed on both surfaces (TOML [features]/[package].provides/[capabilities] and the Lua descriptor). CLI: `--cap` on build/test. Tests: e2e/81_capability_binding.sh (6 cases), unit Manifest.CapabilitiesProvidesRequiresAndPins + SynthesizeFromXpkgLua.CapabilitiesAndFeatureDefines. Design: .agents/docs/2026-06-29-feature-capability-model-design.md
- docs/05-mcpp-toml.md (+ zh mirror): document the [features] table form (package-owned defines) and the new provides/requires capabilities section ([capabilities] pins, --cap, deterministic 0/1/many binding table). - CHANGELOG: 0.0.69 entry (Feature System v2 — S1 defines + S3 capabilities). - Bump mcpp.toml + MCPP_VERSION to 0.0.69. - Design doc: record Implementation Status (S1+S3 shipped, S2 next).
This was referenced Jun 29, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Fixes the class of bug where enabling a feature could not actually configure the
upstream library: e.g.
compat.eigenwith theblasfeature emitted only-DMCPP_FEATURE_BLAS, never the-DEIGEN_USE_BLASthat Eigen actually reads — sothe backend was never enabled. Root cause: a feature could only ever produce
-DMCPP_FEATURE_<NAME>+ gate sources; it could not contribute an arbitrarydefine, nor select a backend.
Design (industry-surveyed, "full coverage + less is more"):
.agents/docs/2026-06-29-feature-capability-model-design.md. Converges on twoprimitives and deliberately drops what other ecosystems regret (free-form
per-feature flags → vcpkg; constraint DSLs / soft preferences / silent first-match
→ Spack/pkg-config).
What's in this release (S1 + S3)
Stage 1 — feature
defines[features]entries may be written in table form carrying package-owneddefines (and
implies), on both the TOML manifest and the Lua descriptor:Bare names desugar to
-D<x>(like[targets.*] defines), alongside the automatic-DMCPP_FEATURE_<NAME>. Restricted by convention to package-owned macros — nofree-form
cflags/ldflags, preserving feature-union composability.Stage 3 — capabilities (
provides/requires)A capability is a shared string. A package/feature
providesit; a featurerequiresit instead of a concrete package. The resolver binds exactly oneprovider from the graph, deterministically:
[capabilities]pin /--capLink/include flow through normal dependency mechanics; this is the
selection-and-validation layer. CLI:
--cap blas=openblasonbuild/test.Deferred: Stage 2
Optional-dep auto-pull + full feature-union unification need resolution-phase
reordering (higher risk) and are not required for the capability/Eigen use
case (providers are explicit dependencies). Documented as the next stage in the
design doc; this release ships S1+S3 per the "independently shippable stages"
intent.
Tests
80_feature_defines.sh,81_capability_binding.sh(6 cases).Manifest.FeatureTableFormDefinesAndImplies,Manifest.CapabilitiesProvidesRequiresAndPins,SynthesizeFromXpkgLua.CapabilitiesAndFeatureDefines.(54_package_owned_ldflags fails pre-existing/environmental — gcc path,
reproduces on the bootstrap binary unchanged).
Docs
docs/05-mcpp-toml.md(+ zh mirror): table-form features + capabilities section.0.0.69. Version bumped (mcpp.toml+MCPP_VERSION).