Skip to content

manifest: per-glob flags in [target.'cfg(os)'.build] (parity with descriptor mcpp.<os> flags/#253) — blocks vendored-opencv windows leg #258

Description

@Sunrisepeak

Summary

Feature request: support per-glob flags inside conditional target sections — [target.'cfg(<os>)'.build] (and [target.<triple>.build]) should accept the same flags = [{ glob, cflags, cxxflags, asmflags, defines }] array that [build] accepts. Today the conditional build section parses only cflags/cxxflags/ldflags/sources (src/manifest/toml.cppm:976-987), and [features.<n>.flags] is likewise global-only; the per-OS flag semantics that #253 added exist only in the xpkg descriptor grammar (mcpp.<os>.features, mcpp.<os>.flags).

This is now the single blocker for the opencv-m single-repo windows leg (Sunrisepeak/opencv-m feat/vendor-single-repo — the vendored OpenCV build that retires compat.opencv).

Why hoisting/global tricks cannot cover windows

The three frozen per-OS snapshots need different per-glob defines on the same TU paths:

  • linux vs macos deltas are pure per-OS additions with group-unique names (PNG_INTEL_SSE_OPT vs PNG_ARM_NEON_OPT, NEON_INTRINSICS, HAVE_CAMV4L2, …) — we hoisted those to per-OS global cflags and shipped the macos leg that way. Workable, but already a hack: it relies on grep-verifying that each define name is only read by its own code group.

  • windows needs both additions and REMOVALS on shared globs, e.g.:

    • **/3rdparty/zlib/**: linux/mac say HAVE_UNISTD_H=1; windows must NOT define it (clang-MSVC has no unistd.h) and adds NO_FSEEKO
    • third_party/.../core/src/alloc.cpp: HAVE_MEMALIGN/HAVE_POSIX_MEMALIGN (unix) vs HAVE_WIN32_ALIGNED_MALLOC=1 (win)
    • **/modules/videoio/**: HAVE_CAMV4L2/HAVE_FFMPEG_LIBAVDEVICE (linux) absent on windows
    • every group: WIN32 _CRT_SECURE_NO_DEPRECATE _CRT_NONSTDC_NO_DEPRECATE _SCL_SECURE_NO_WARNINGS _VARIADIC_MAX=10 _WIN32_WINNT=0x0601 _WINDOWS added

    A removal cannot be expressed by any global-flag overlay: [[build.flags]] is unconditional, so the unix -DHAVE_UNISTD_H=1 from the union table reaches windows TUs; -U counter-entries would need to be windows-only — same gap, recursed.

The only in-grammar workaround is resurrecting a per-OS tu-stub indirection layer (per-OS wrapper TUs whose paths are OS-unique so globs can key on them) for every conflicting group — the pre-0.0.97 #233-era machinery this project was happy to delete.

Proposed grammar

[target.'cfg(windows)'.build]
cflags = ["-DWIN32", "-D_WIN32_WINNT=0x0601"]          # exists today
flags  = [                                              # NEW: same GlobFlags shape as [build].flags
  { glob = "third_party/opencv/3rdparty/zlib/**", defines = ["NO_FSEEKO"] },
  { glob = "third_party/opencv/modules/core/src/alloc.cpp", defines = ["HAVE_WIN32_ALIGNED_MALLOC=1"] },
]

Merge semantics: append the matching conditional entries AFTER [build].flags in the ordered vector (same "last flag wins" rule) — that also gives -U/re--D override power over the unconditional table, which covers the removal case. Same for [target.<sel>.'features'.…] or at least [target.<sel>.build.flags] reachable from features via the existing feature-flag funnel.

Symmetry argument: the descriptor grammar already has exactly this (per-OS flags inside mcpp.<os>, #253); manifest-built packages (single-repo/vendored libraries, workspace members, path deps) are second-class without it. Dep builds already run the conditional merge through the single #229 funnel (merge_conditional_sources_flags), so the plumbing point is well-defined.

Context / evidence

  • opencv-m single-repo branch: linux (gcc16 + llvm 20/22 + x86_64-linux-musl static) and the macos wiring are green with the manifest-only approach; the parked windows tables live in the port fragments (windows-flags-PARKED.toml, windows-dnn-flags-PARKED.toml) ready to drop in the day this lands.
  • Conflict inventory was produced mechanically by tools/vendor/port_descriptor.py's cross-OS union check and is reproducible from mcpp-index pkgs/c/compat.opencv.lua @8692bb26.

Environment: mcpp 0.0.101.

Activity

  1. Sunrisepeak commented on Jul 21, 2026

    @Sunrisepeak
    MemberAuthor

    One more data point from the same opencv-m branch: with the cross-OS UNION flag table (the only in-grammar shape today), every off-OS entry now trips the #253 dead-glob warning on every build — e.g. the linux run prints ~24 warnings for the windows stub-namespace globs, the macos run warns for every x86 ISA entry, and vice versa. They are all structurally-expected, not authoring mistakes, but they bury real dead-glob signals.

    If per-OS tables land ([target.'cfg(os)'.build] flags), the noise disappears by construction. If that takes longer, a tiny stopgap would help immediately: an optional = true key on a flags entry (or a ?-prefix on the glob) meaning 'zero matches are fine, skip the warning' — authors of multi-OS packages would mark exactly the entries that are off-OS by design.

  2. Sunrisepeak commented on Jul 21, 2026

    @Sunrisepeak
    MemberAuthor

    Clarification: #253 fixed the feature axis, this issue is the OS axis

    Worth stating explicitly, because "didn't #253 already fix the dead-glob warning?" is the natural reading of the changelog.

    af25d18 (#253, 0.0.101) changed the zero-hit path in src/modgraph/scanner.cppm:933-945 so that a features.<n>.flags entry only exists in buildConfig.globFlags when its feature is active — i.e. a feature-off build no longer carries the rule at all, so it cannot warn. That is a real fix, and it fully closes the feature dimension.

    The 23 warnings we still get are all the OS dimension, which #253 did not touch:

    warning files exist on warns on
    **/modules/{core,imgproc}/**/*.{avx,avx2,avx512_skx,sse4_1,sse4_2}.cpp (10 entries) linux / windows (x86) macOS (arm64 — no x86 dispatch TUs in its source set)
    gen/windows/tu/w/<group>/** (12 entries) windows linux + macOS
    third_party/opencv-5.0.0/**/*.asm x86 targets macOS arm64

    The asymmetry is structural, not authoring error: sources is OS-conditional, flags is not.
    [target.'cfg(os)'.build] reads exactly cflags / cxxflags / ldflags / sources (+.dependencies) — src/manifest/toml.cppm:975-987 — while [[build.flags]] is global-only. One manifest covering 3 OSes therefore must present each OS with the other two OSes' flag entries, and each of those matches zero sources by construction.

    Why there is no package-side workaround (checked, all three fail)

    1. Anchor/dummy files on disk — no effect: the hit counter is over the build's source set (globFlagHits is bumped inside the per-scanned-file apply_glob_flags loop), not over the filesystem. The 12 gen/windows/tu/w/** dirs are committed and present on every OS, and they still warn on linux/macOS.
    2. A no-op TU compiled on every OS to keep each glob alive — breaks the build: the x86 ISA entries carry -mavx / -msse4.1, which clang rejects on macOS arm64.
    3. Park the off-OS entries in a feature and auto-enable it per OS — not expressible: per-OS feature activation (mcpp.<os>.features) exists only in the xpkg descriptor grammar; mcpp.toml has no equivalent key, and build.mcpp can only read MCPP_FEATURE_<N>, never enable a feature.

    So the warning is only removable inside mcpp — which is what this issue asks for.

    Implementation sketch (the plumbing already exists)

    Reading the current tree, per-OS flags looks like ~4 mechanical sites, all reusing existing code:

    1. src/manifest/types.cppm:276 — add std::vector<GlobFlags> globFlags; to ConditionalConfig (sits next to the existing sources).
    2. src/manifest/toml.cppm:975-987 — in the [target.<pred>.build] reader, parse flags with the existing parse_glob_flags_value helper (toml.cppm:62) — same grammar/validation as [build].flags, zero new parser.
    3. src/build/prepare.cppm:429 merge_conditional_sources_flags — inside the cfgpred::matches branch that already appends cc.sources, append cc.globFlags to m.buildConfig.globFlags. Appending AFTER the base entries gives the "last flag wins" override power the body asks for (which is what makes the windows removal cases expressible).
    4. src/manifest/xpkg.cppm — mirror for descriptor parity if desired (it already has parse_glob_flags_array).

    Fingerprint/scanner/per-TU landing need no change — everything downstream consumes the single buildConfig.globFlags funnel, exactly as #253 arranged.

    What lands the day this ships (opencv-m side)

    The stub-namespace workaround is currently shipping in Sunrisepeak/opencv-m#13, and it costs:

    • 703 committed stub files (gen/windows/tu/w/ 426 + gen/windows/tu/wdnn/ 277), each a one-line #include of the real TU, existing solely to give windows TUs OS-unique paths so the global flag table can key on them
    • 32 windows glob entries in mcpp.toml pointing at those stub dirs
    • the 23 warnings above, on every build, on every OS

    All three disappear by construction with [target.'cfg(os)'.build].flags — the parked tables (windows-flags-PARKED.toml, windows-dnn-flags-PARKED.toml) drop straight in.

  3. added 2 commits that reference this issue on Jul 21, 2026
  4. Sunrisepeak commented on Jul 21, 2026

    @Sunrisepeak
    MemberAuthor

    Fixed in 0.0.102 (PR #264, squashed to main as 5807e36).

    [target.'cfg(...)'.build] now accepts the same per-glob flags array [build] does, plus include_dirs/include_dirs_after; the xpkg descriptor's target_cfg gets the same for parity.

    The fix is the type behind the feature. mcpp's two conditional axes (cfg and features) each hand-picked which build fields they could carry and picked different subsets — the drift that made per-glob flags inexpressible here. Extracting BuildInputs makes membership answerable by the type instead of a hand-kept list. Conditional entries append after the base table, so GNU last-wins makes a per-OS removal expressible (-UHAVE_UNISTD_H against a base -D), and off-OS entries never enter globFlags at all, so the ~23 structurally-dead-glob warnings disappear by construction rather than by an optional=true suppression hatch (deliberately not added).

    That retires the 703 stub files / 32 globs the vendored-opencv windows leg needed. e2e 149 covers all four quadrants (match reaches TU, override/removal, off-OS silence, and a control proving a genuinely dead glob still warns).

    Note: making the unknown-key policy consistent across sections was split out as #263 — it has a compatibility surface and this issue didn't depend on it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions