Crafter.Build/interfaces/Crafter.Build-Platform.cppm

64 lines
3.5 KiB
Text
Raw Normal View History

2026-07-23 01:24:42 +02:00
// SPDX-License-Identifier: LGPL-3.0-only
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
2025-11-15 19:20:33 +01:00
module;
#include "Crafter.Build-Api.h"
2026-04-23 01:57:25 +02:00
export module Crafter.Build:Platform;
2025-11-15 19:20:33 +01:00
import std;
2026-04-23 01:57:25 +02:00
namespace fs = std::filesystem;
2025-11-15 19:20:33 +01:00
namespace Crafter {
2026-04-23 01:57:25 +02:00
struct Configuration;
2026-04-27 07:04:42 +02:00
struct CommandResult {
2026-07-23 01:24:42 +02:00
std::int32_t exitCode = 0;
2026-04-27 07:04:42 +02:00
std::string output;
bool crashed = false;
bool timedOut = false;
2026-07-23 01:24:42 +02:00
std::int32_t signal = 0;
2026-04-27 07:04:42 +02:00
};
std::string BuildStdPcm(const Configuration& config, fs::path stdPcm);
2026-04-23 01:57:25 +02:00
fs::path GetCacheDir();
2026-04-27 07:04:42 +02:00
CommandResult RunCommandChecked(std::string_view command);
// A compile / link / archive step. Success is the command's exit status,
// never whether it printed anything: a warning does not stop the compiler
// from writing a valid object, and warnings are only emitted when a
// translation unit is actually recompiled — so keying failure off the
// output made the same unchanged source pass or fail depending on whether
// its object happened to be up to date.
//
// Returns an empty string on success (any warnings go to the user via
// Progress::Diagnostic), otherwise the compiler's diagnostics. Callers
// therefore keep the `if (!result.empty())` shape they already had.
std::string RunBuildCommand(std::string_view command);
export CRAFTER_API CommandResult RunCommandWithTimeout(std::string_view command, std::chrono::seconds timeout);
2026-04-27 07:04:42 +02:00
std::string GetBaseCommand(const Configuration& config);
export CRAFTER_API Configuration LoadProject(const fs::path& projectFile, std::span<const std::string_view> args);
feat: project.cpp declares its own dependencies with #pragma crafter dependency A project.cpp could only import std and Crafter.Build, so anything shared between build descriptions had to be a header every consumer copies. Now #pragma crafter dependency "<source>" [branch "<b>" | commit "<sha>"] [project "<file>"] names a sibling crafter-build project that project.cpp itself imports a module from. LoadProject scans for the directive, resolves it through GitProject (a URL) or LocalProject (a path relative to the project file), forces the result into host-tooling mode (the host triple LoadProject's own cache is keyed on, native march, no debug, -fPIC so the archive links into a shared object), builds it, and puts its BMIs and archive on the project.so command line. The dependency's own project.cpp goes through the same loader, so its directives resolve recursively; a cycle between project files is an error instead of unbounded recursion. The dependency is built on every load, fresh or not: clang crashes rather than erroring when it imports a BMI whose own imported PCM was rebuilt underneath it, and the module depfile — which Module::Check already reads — is what notices the host PCM cache was regenerated. Its artifact mtime is an input to project.so's staleness check, so editing the dependency recompiles project.cpp without touching it. The Linux link gains -fuse-ld=lld because release archives hold ThinLTO bitcode. ProjectDependency(name) hands the same Configuration* back from inside CrafterBuildProject, by configuration name or exported module name, so a test that links the module needn't spell the source twice. It reads a thread_local frame stack that an RAII guard keeps for the whole of LoadProject, which also provides the cycle check. ExpectedOutput() is the link gate's artifact-path lambda hoisted into the :Clang partition, so the staleness check and the -r runner share the one rule. Windows branches are wired identically but unverified. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-02 20:03:22 +02:00
// A dependency the project file currently running its CrafterBuildProject
// declared with `#pragma crafter dependency`, so it can also be wired
// into the build graph (a test that links the same module, say) without
// spelling the source twice. `name` is the dependency's Configuration
// name or any module it exports — whatever the project file `import`s.
// Throws when no such dependency was declared, or when called from
// anywhere other than inside LoadProject.
export CRAFTER_API Configuration* ProjectDependency(std::string_view name);
V2: WASI, -r flag, CI pipeline, examples & tests cleanup WASI / wasm32 target support - Auto-detect /usr/share/wasi-sysroot on Linux when target starts_with("wasm32") - Skip -march/-mtune for wasm (clang rejects them) - Apply -fno-exceptions -fno-c++-static-destructors -mllvm -wasm-enable-sjlj -D_WASI_EMULATED_SIGNAL to wasm builds (compile + std PCM, kept in sync) - .wasm output extension in expectedOutputFor and link command - EnableWasiBrowserRuntime(cfg): opt-in helper that drops index.html + runtime.js next to the .wasm; runtime.js reads window.CRAFTER_WASM_URL set in the templated index.html so a single shim handles any output name -r run flag in the CLI: build then exec the artifact (host targets only; rejects libraries; auto .exe/.wasm extension handling) CI pipeline (.forgejo/workflows/ci.yaml) - Triggers: PR/push to master + manual dispatch - Single arch-latest container job: install deps, bootstrap, self-rebuild, run tests, cross-compile mingw, package both archives, upload artifacts - Rolling 'latest' release published only on push/dispatch to master mingw cross-compile from Linux now works end-to-end: - ExternalDependency cache key includes target so per-target glslang builds don't collide; CMAKE_BUILD_TYPE=Release pinned (otherwise glslang appends 'd' to lib names and breaks linking); cross-compile cmake flags (CMAKE_SYSTEM_NAME=Windows, CMAKE_*_COMPILER_TARGET=...) - project.cpp accepts --target=<triple>; Linux-only -Wl,--export-dynamic and -ldl are gated; mingw glslang skips the standalone exe (its libgcc_eh link pulls pthread which mingw doesn't link by default) - mingw compile uses -femulated-tls so std::__once_callable etc reference the same emutls symbols libstdc++ provides - mingw link auto-adds -lstdc++exp -lpthread GetCrafterBuildHome() exposed from the Platform module; LoadProject (Linux + Windows) now both use it instead of duplicating the resolution. Examples reorg: hello-world, library, with-module, wasi, tests — each with its own README. Tests reorg: per-test directory with inner/ fixture, no shared tests/fixtures/ tree. New Wasi test verifies .wasm magic bytes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 23:24:46 +02:00
// 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);
2026-07-23 01:24:42 +02:00
// 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);
}