The staleness check compared an artifact against its own source and its module imports. Headers were in neither set: the scanner reads `import` lines, and a .cppm or .cpp keeps its mtime when a header it includes is edited — so the build reported nothing to do and left objects compiled against the previous contents. Same silent mixed-layout binary as issue 27, reached through #include instead of import. Ask the compiler what it actually opened. Every C++ and C compile now passes -MD -MF <artifact>.d, and Check reads that depfile back through NewestPrerequisite, comparing every prerequisite's mtime against the artifact. A missing depfile (an object from a crafter-build that wrote none) or a prerequisite that no longer exists reads as "rebuild": neither is evidence of freshness. The parser unescapes make syntax rather than splitting on whitespace, since clang wraps depfiles onto continuation lines and escapes spaces in filenames.
46 lines
2.3 KiB
C++
46 lines
2.3 KiB
C++
// SPDX-License-Identifier: LGPL-3.0-only
|
|
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
|
|
|
module;
|
|
#include "Crafter.Build-Api.h"
|
|
export module Crafter.Build:Platform;
|
|
import std;
|
|
namespace fs = std::filesystem;
|
|
|
|
namespace Crafter {
|
|
struct Configuration;
|
|
struct CommandResult {
|
|
std::int32_t exitCode = 0;
|
|
std::string output;
|
|
bool crashed = false;
|
|
bool timedOut = false;
|
|
std::int32_t signal = 0;
|
|
};
|
|
std::string BuildStdPcm(const Configuration& config, fs::path stdPcm);
|
|
fs::path GetCacheDir();
|
|
std::string RunCommand(const std::string_view command);
|
|
CommandResult RunCommandChecked(std::string_view command);
|
|
export CRAFTER_API CommandResult RunCommandWithTimeout(std::string_view command, std::chrono::seconds timeout);
|
|
std::string GetBaseCommand(const Configuration& config);
|
|
export CRAFTER_API Configuration LoadProject(const fs::path& projectFile, std::span<const std::string_view> args);
|
|
// Resolves the directory holding distributed runtime assets (Crafter.Build
|
|
// module sources, wasi-runtime/, etc). Honors CRAFTER_BUILD_HOME; otherwise
|
|
// derives <prefix>/share/crafter-build from the running executable's path.
|
|
export CRAFTER_API fs::path GetCrafterBuildHome();
|
|
// Newest mtime among the prerequisites a compiler recorded in `depFile`
|
|
// (clang's `-MD -MF <artifact>.d`). This is how a build step learns which
|
|
// headers its source pulled in: the module scanner only reads `import`
|
|
// lines, so an `#include`d file is invisible to it until the compiler
|
|
// reports what it actually opened.
|
|
//
|
|
// Returns file_time_type::max() when freshness cannot be proven — no
|
|
// depfile (nothing has compiled this artifact since depfiles were emitted,
|
|
// including every object left over from an older crafter-build) or a
|
|
// prerequisite that no longer exists. Callers compare the result against
|
|
// their artifact's mtime, so max() reads as "rebuild".
|
|
fs::file_time_type NewestPrerequisite(const fs::path& depFile);
|
|
// Wildcard name matching ('*', '?') shared by the test and lint verbs.
|
|
bool MatchGlob(std::string_view glob, std::string_view name);
|
|
// Empty `globs` matches everything.
|
|
bool MatchAny(std::span<const std::string> globs, std::string_view name);
|
|
}
|