All checks were successful
Flatpak / flatpak (pull_request) Successful in 7m27s
The README suggested giving one sibling a list without a flag only the project reads, so that the sibling would not get a build directory of its own. When another path loads the same sibling with the full list, that filter is what gives it the second directory: two builds of one project, both in the program (#41). It now says to build such a list once and give it to every sibling. It also says the default is read when the spec is made, so a spec made at namespace scope, on another thread or in a script forwards nothing, and that cfg.projectArgs, the part ApplyStandardArgs did not interpret, is no substitute: it drops the target and the build type. The comments on ProjectArgs() and on Configuration::projectArgs say the same. The spec comment named --local as a flag the default carries down a chain of siblings. Through a GitProject hop it breaks: the clone has no checkouts beside it. The GitProject comment said the clone is keyed by the args; only the Configuration is, and every arg list builds in the one clone. AGENTS.md now notes that a stale harness gives no symptom for a changed default member initialiser: project.cpp silently gets the old default. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
141 lines
7.5 KiB
C++
141 lines
7.5 KiB
C++
// SPDX-License-Identifier: LGPL-3.0-only
|
|
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
|
|
|
export module Crafter.Build:External;
|
|
import std;
|
|
namespace fs = std::filesystem;
|
|
|
|
export namespace Crafter {
|
|
struct Configuration;
|
|
|
|
struct GitSource {
|
|
std::string url;
|
|
std::string branch;
|
|
std::string commit;
|
|
};
|
|
|
|
enum class ExternalBuilder {
|
|
None,
|
|
CMake,
|
|
};
|
|
|
|
struct ExternalDependency {
|
|
std::string name;
|
|
GitSource source;
|
|
ExternalBuilder builder = ExternalBuilder::None;
|
|
std::vector<std::string> options;
|
|
std::vector<fs::path> includeDirs;
|
|
// Extra library search paths (each becomes a -L flag), interpreted
|
|
// relative to the CMake build dir. Currently only honoured for
|
|
// ExternalBuilder::CMake. The CMake builder always adds its
|
|
// top-level build dir as a -L; libDirs is for projects (msquic,
|
|
// others) whose CMakeLists set LIBRARY_OUTPUT_DIRECTORY to a subdir
|
|
// of the build tree.
|
|
std::vector<fs::path> libDirs;
|
|
std::vector<std::string> libs;
|
|
};
|
|
|
|
struct ExternalBuildResult {
|
|
std::vector<std::string> compileFlags;
|
|
std::vector<std::string> linkFlags;
|
|
fs::file_time_type latestArtifact = fs::file_time_type::min();
|
|
};
|
|
|
|
// Where this dependency's clone lives in the cache. A pure function of the
|
|
// declaration and target — BuildExternal derives its own working directory
|
|
// through this, so a caller that only needs to know where the headers ended
|
|
// up cannot drift from where they actually are.
|
|
fs::path ExternalCloneDir(const ExternalDependency& dep, std::string_view target);
|
|
// Where BuildExternal leaves this dependency: the CMake build directory,
|
|
// or the clone itself when it is only fetched. Pure, like ExternalCloneDir.
|
|
// Build() keys a pass's record of the work on it rather than on the clone,
|
|
// so a declaration that shares the clone but builds it with CMake is not
|
|
// handed the outcome of a fetch alone.
|
|
fs::path ExternalBuildDir(const ExternalDependency& dep, std::string_view target);
|
|
// The -I flags this dependency contributes, from its declared includeDirs.
|
|
// Also pure, which is what lets the linter's AST layer parse a source that
|
|
// includes an external's headers without running a build to find out where
|
|
// they are. The paths only resolve once something has fetched the clone.
|
|
std::vector<std::string> ExternalIncludeFlags(const ExternalDependency& dep, std::string_view target);
|
|
// Fetches the clone and, for ExternalBuilder::CMake, configures and builds
|
|
// it. Empty on success, otherwise what failed. A lock on the clone is held
|
|
// throughout, so another crafter-build, or another build pass in this one,
|
|
// works on it before or after this call and never during it. Not
|
|
// cancellable once started: Build() hands the outcome to every
|
|
// configuration of the pass that declares the dependency, and a run cut
|
|
// short would leave them a clone nobody fetched.
|
|
std::string BuildExternal(const ExternalDependency& dep, std::string_view target);
|
|
// What a configuration that declares `dep` compiles and links with once
|
|
// BuildExternal has run: the -I flags and, for CMake, the -L, -rpath and -l
|
|
// flags and the newest library. Per declaration, because two that share a
|
|
// clone can still differ in includeDirs, libDirs and libs.
|
|
ExternalBuildResult ExternalOutputs(const ExternalDependency& dep, std::string_view target);
|
|
|
|
// The args the project file now running its CrafterBuildProject was
|
|
// loaded with: everything that function receives, --target=, --debug and
|
|
// the other standard flags included. Not cfg.projectArgs, which holds
|
|
// only what ApplyStandardArgs left uninterpreted: `.args =
|
|
// cfg.projectArgs` would drop the target and the build type. Empty
|
|
// everywhere else, because it reads the stack LoadProject keeps per
|
|
// thread, as IsTopLevelProject does: outside a project file, on any
|
|
// thread but the one running it, once CrafterBuildProject has returned
|
|
// (a script runs later), and before it is called, which covers the
|
|
// file's static initialisers and its `#pragma crafter dependency`
|
|
// pre-pass. The specs below default their args to it. Defined beside
|
|
// LoadProject, but declared here so those defaults can see it: importing
|
|
// :Platform into this partition would make the exported `struct
|
|
// Configuration;` above a redeclaration of :Platform's unexported one,
|
|
// which clang rejects.
|
|
std::vector<std::string> ProjectArgs();
|
|
|
|
// Specification for a sibling crafter-build project to fetch and depend on.
|
|
// GitSource picks the revision: leave branch + commit empty for the
|
|
// remote's default branch HEAD (sloppy but convenient), set branch to
|
|
// track a branch tip, or set commit to pin to an immutable SHA. args are
|
|
// what the dep's CrafterBuildProject receives. They default to this
|
|
// project's own, read when the spec is made, so --target=, --march=,
|
|
// --debug and the project's own flags reach every sibling down a chain
|
|
// without being passed. Set them to forward something else: {} for
|
|
// nothing, or a list built from this project's args, such as one without
|
|
// a flag only this project reads. Give such a list to every sibling: a
|
|
// project that two paths load with two different lists is built twice,
|
|
// and the program ends up with both builds.
|
|
struct GitProjectSpec {
|
|
GitSource source;
|
|
fs::path projectFile = "project.cpp";
|
|
std::vector<std::string> args = ProjectArgs();
|
|
};
|
|
|
|
// Clones the spec's git URL into the external cache, recursively
|
|
// LoadProject's the remote's project.cpp, and returns a stable
|
|
// Configuration* you can drop into cfg.dependencies. The clone is keyed by
|
|
// url+branch+commit+projectFile alone, so every arg list builds in the
|
|
// same working tree, each into variant directories of its own. The
|
|
// Configuration is owned by an internal cache for the lifetime of the
|
|
// build, keyed by the clone and the args: identical specs share one.
|
|
Configuration* GitProject(const GitProjectSpec& spec);
|
|
|
|
// Specification for a local sibling crafter-build project (e.g. an
|
|
// example folder depending on its parent project, or a workspace where
|
|
// multiple projects live side-by-side without going through git).
|
|
struct LocalProjectSpec {
|
|
fs::path projectFile; // relative to cwd or absolute
|
|
std::vector<std::string> args = ProjectArgs(); // as GitProjectSpec::args
|
|
};
|
|
|
|
// Same as GitProject but for a local on-disk project — no fetch, no
|
|
// cache copy. Resolves projectFile to its canonical absolute path and
|
|
// recursively LoadProject's it. Returns a stable Configuration* owned
|
|
// by the same internal cache as GitProject.
|
|
Configuration* LocalProject(const LocalProjectSpec& spec);
|
|
|
|
// Clone (or update) the given git source into the shared external
|
|
// cache and return the path to the cloned working tree. Intended for
|
|
// example projects that need large asset bundles which shouldn't
|
|
// live in the parent repo — push the returned path (or specific
|
|
// files inside it) into `cfg.assets`. Reuses the same cache root as
|
|
// ExternalDependency / GitProject. Caching key = url + branch + commit;
|
|
// identical calls reuse the existing clone. Pin `source.commit` to a
|
|
// SHA for reproducible, offline-after-first-fetch builds.
|
|
fs::path GitFetch(const GitSource& source);
|
|
}
|