Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions src/cli.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -2039,21 +2039,24 @@ constexpr std::string_view kBuildCacheFile = "target/.build_cache";

void write_build_cache(const std::filesystem::path& projectRoot,
const std::filesystem::path& outputDir,
const std::string& ninjaProgram) {
const std::string& ninjaProgram,
const std::string& targetTriple) {
auto path = projectRoot / kBuildCacheFile;
std::error_code ec;
std::filesystem::create_directories(path.parent_path(), ec);
std::ofstream f(path, std::ios::trunc);
if (f) {
f << outputDir.string() << '\n';
f << ninjaProgram << '\n';
f << targetTriple << '\n';
}
}

// Compile a prepared BuildContext. Shared between `mcpp build` and `mcpp run`
// so the latter doesn't call prepare_build twice (and re-print the toolchain
// resolution banner).
int run_build_plan(BuildContext& ctx, bool verbose, bool no_cache) {
int run_build_plan(BuildContext& ctx, bool verbose, bool no_cache,
std::string_view targetOverride = "") {
if (no_cache) {
std::error_code ec;
std::filesystem::remove_all(ctx.outputDir, ec);
Expand Down Expand Up @@ -2106,7 +2109,8 @@ int run_build_plan(BuildContext& ctx, bool verbose, bool no_cache) {

// P0: save build cache for fast-path on next invocation.
if (!no_cache && !r->ninjaProgram.empty()) {
write_build_cache(ctx.projectRoot, ctx.outputDir, r->ninjaProgram);
write_build_cache(ctx.projectRoot, ctx.outputDir, r->ninjaProgram,
std::string(targetOverride));
}

mcpp::ui::finished("release", r->elapsed);
Expand All @@ -2125,17 +2129,24 @@ int run_build_plan(BuildContext& ctx, bool verbose, bool no_cache) {
// Try to fast-path: if build.ninja is newer than all inputs, just run ninja.
// Returns exit code on fast-path, or nullopt if full rebuild needed.
std::optional<int> try_fast_build(const std::filesystem::path& projectRoot,
bool verbose, bool no_cache) {
bool verbose, bool no_cache,
std::string_view currentTarget = "") {
if (no_cache) return std::nullopt;

auto cachePath = projectRoot / kBuildCacheFile;
std::error_code ec;
if (!std::filesystem::exists(cachePath, ec)) return std::nullopt;

std::ifstream f(cachePath);
std::string outputDirStr, ninjaProgram;
std::string outputDirStr, ninjaProgram, cachedTarget;
if (!std::getline(f, outputDirStr) || outputDirStr.empty()) return std::nullopt;
if (!std::getline(f, ninjaProgram) || ninjaProgram.empty()) return std::nullopt;
std::getline(f, cachedTarget); // may be empty for old cache files

// Reject cache if target triple changed (e.g. previous build used
// --target x86_64-linux-musl but this one is a default build).
if (cachedTarget != currentTarget) return std::nullopt;

std::filesystem::path outputDir(outputDirStr);

auto ninjaPath = outputDir / "build.ninja";
Expand Down Expand Up @@ -2214,7 +2225,7 @@ int cmd_build(const mcpplibs::cmdline::ParsedArgs& parsed) {
auto ctx = prepare_build(print_fp, /*includeDevDeps=*/false, /*extraTargets=*/{}, ov);
if (!ctx) { std::println(stderr, "error: {}", ctx.error()); return 2; }

return run_build_plan(*ctx, verbose, no_cache);
return run_build_plan(*ctx, verbose, no_cache, ov.target_triple);
}

int cmd_run(const mcpplibs::cmdline::ParsedArgs& parsed,
Expand Down
37 changes: 33 additions & 4 deletions tests/e2e/28_target_static.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,15 @@ then
exit 0
fi

# Detect installed GNU gcc version for the default-build regression check.
# Without a known GNU toolchain the default build would auto-install
# musl-gcc and produce another musl binary, making the assertion meaningless.
XPKGS="${MCPP_HOME:-$HOME/.mcpp}/registry/data/xpkgs"
GNU_GCC_VER=""
if [[ -d "$XPKGS/xim-x-gcc" ]]; then
GNU_GCC_VER=$(ls "$XPKGS/xim-x-gcc" 2>/dev/null | sort -V | tail -1)
fi

TMP=$(mktemp -d)
trap "rm -rf $TMP" EXIT

Expand All @@ -27,12 +36,26 @@ cd "$TMP"
cd staticapp

# Add a [target.x86_64-linux-musl] override that tells mcpp to use musl-gcc.
# If a GNU gcc is available, also pin [toolchain].linux so the default build
# uses it (preventing fallback to auto-install musl-gcc as default).
if [[ -n "$GNU_GCC_VER" ]]; then
cat >> mcpp.toml <<EOF

[toolchain]
linux = "gcc@${GNU_GCC_VER}"

[target.x86_64-linux-musl]
toolchain = "gcc@15.1.0-musl"
linkage = "static"
EOF
else
cat >> mcpp.toml <<'EOF'

[target.x86_64-linux-musl]
toolchain = "gcc@15.1.0-musl"
linkage = "static"
EOF
fi

"$MCPP" build --target x86_64-linux-musl > build.log 2>&1 || {
cat build.log; echo "build failed"; exit 1; }
Expand All @@ -58,9 +81,15 @@ fi

# Default GNU build still works (regression: --target should not have
# clobbered the default codepath for follow-up commands).
"$MCPP" build > build-gnu.log 2>&1 || {
cat build-gnu.log; echo "default GNU build broke after musl build"; exit 1; }
gnu_binary=$(find target -type d -name x86_64-linux-musl -prune -o -type f -name staticapp -print | head -1)
[[ -n "$gnu_binary" ]] || { echo "default GNU binary missing"; exit 1; }
# Skip this check if no GNU gcc is available — without it the default
# build would also use musl-gcc, making the assertion meaningless.
if [[ -n "$GNU_GCC_VER" ]]; then
"$MCPP" build > build-gnu.log 2>&1 || {
cat build-gnu.log; echo "default GNU build broke after musl build"; exit 1; }
gnu_binary=$(find target -type d -name x86_64-linux-musl -prune -o -type f -name staticapp -print | head -1)
[[ -n "$gnu_binary" ]] || { echo "default GNU binary missing"; exit 1; }
else
echo "SKIP: no GNU gcc installed — skipping default-build regression check"
fi

echo "OK"
Loading