feat: compile .c sources via a separate C rule#1
Merged
Sunrisepeak merged 1 commit intomainfrom May 8, 2026
Merged
Conversation
mcpp's ninja backend previously routed every non-.cppm source through one
`cxx_object` rule that invokes g++ with `-std=c++23 -fmodules`. .c files
went through cc1plus, which rejects mainstream C constructs (implicit
`void*` conversion from malloc/calloc, the `restrict` keyword, ...) and
silently warns on `-std=c++23`. Real-world C libraries — verified against
mbedtls 3.6.1's 108 sources — cannot build under that path.
Add a parallel C compile path:
* `BuildConfig` gains `cflags`, `cxxflags`, `cStandard`. Both the
`[build]` TOML section and the `mcpp = {}` xpkg segment parse them, so
index entries can carry per-package flags.
* The ninja backend probes the plan for `.c` sources; when present it
derives a sibling C compiler from the resolved C++ binary
(`g++ -> gcc`, `clang++ -> clang`, `c++ -> cc`, with prefix
preserved for cross toolchains like `x86_64-linux-musl-gcc`) and
emits `cc` / `cflags` / `c_object` alongside the existing `cxx_*`
set. `cflags` carries `-std=$c_standard` (default `c11`) plus the
shared opt / pic / sysroot / -B / include baseline and the user
tail.
* Compile-edge dispatch picks `cxx_module` for `.cppm`, `c_object` for
`.c`, and `cxx_object` otherwise. dyndep phase-1 skips `.c` so they
don't go through P1689 / `-fmodules`; the static-deps fallback skips
BMI implicit inputs for the same reason.
* Module scanner short-circuits `.c` files with an empty `SourceUnit`
to avoid any chance of a benign C identifier
(`import_foo`, `module_t`) being misparsed as a module statement.
Coverage: two unit tests in `test_manifest.cpp` cover both parse paths,
and a new `tests/e2e/26_c_language_support.sh` scaffolds a mixed
`.c` + modular-`.cpp` project, asserts the generated `build.ninja`
contains the expected `c_object` rule / `cc` variable / extension
dispatch, exercises user-supplied `cflags`/`cxxflags` forwarding, and
runs the resulting binary end-to-end.
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.
Summary
Adds a parallel C-language compile path so mcpp can build packages with
.csources (and depend on real-world C libraries like mbedtls). Previouslyevery non-
.cppmsource went through onecxx_objectrule with-std=c++23 -fmodules; cc1plus rejects mainstream C constructs (implicitvoid*conversion frommalloc/calloc,restrict, ...), so the onlyprojects that built were ones whose
.cfiles happened to be C++-clean.What changed
BuildConfig(src/manifest.cppm) gainscflags/cxxflags/cStandard.Both the
[build]TOML section and themcpp = {}xpkg segment(
synthesize_from_xpkg_lua) parse them — index entries can carryper-package flags.
src/build/ninja_backend.cppm: when the plan contains any.csource,derive a sibling C compiler from the resolved C++ binary
(
g++ → gcc,clang++ → clang,c++ → cc, prefix preserved forcross toolchains like
x86_64-linux-musl-gcc) and emitcc/cflags/c_objectalongside the existingcxx_*set.cflagsdefaults to-std=c11plus the shared opt / pic / sysroot / -B / include baselineand the user tail.
cxx_modulefor.cppm,c_objectfor.c,cxx_objectotherwise. Dyndep phase-1 skips.c(no P1689scan /
-fmodules); the static-deps fallback skips BMI implicits forthe same reason.
src/modgraph/scanner.cppmshort-circuits.cwith an emptySourceUnitso benign C identifiers (import_foo,module_t) can'tbe misparsed as module statements.
Verification
tests/unit/test_manifest.cppcover both parsepaths (
[build]andmcpp = {}segment).tests/e2e/26_c_language_support.shscaffolds a mixed.c+ modular-.cppproject, assertsbuild.ninjahasc_object/cc/cflags, exercises user-supplied flag forwarding, and runsthe resulting binary end-to-end.
library/*.cfiles compile via the newc_objectrule and linkinto a binary that prints the FIPS 180-4
sha256("abc")testvector and an AES-128-ECB sample correctly.
(
28_target_static.sh,30_pack_modes.sh) are pre-existing — bothreproduce on the v0.0.1 release binary unchanged.
Test plan
mcpp build(worktree, builds mcpp itself)mcpp test— all 9 unit binaries passtests/e2e/26_c_language_support.sh(new)restrict+ implicitvoid*+import std