Every project.cpp with a sibling dependency opened with the same line,
std::vector<std::string> depArgs(args.begin(), args.end());
and handed depArgs to each GitProject and LocalProject. CrafterBuildProject
receives a span of string_views and the specs wanted owned strings, and
without the copy the sibling got none of the project's args: it was built
for the host while the project cross-compiled, and in release while it
built --debug. GitProject then turned the copy straight back into
string_views for LoadProject.
Both specs now default their args to ProjectArgs(), the args LoadProject
was given for the project file whose CrafterBuildProject is running. It
reads the per-thread stack LoadProject already keeps for IsTopLevelProject
and ProjectDependency, whose frames now record the args. A default member
initializer rather than a new type or an optional: the member stays a
std::vector<std::string>, so every existing `.args = depArgs` keeps its
exact meaning (each of the ~50 calls across the Catcrafts projects passes
one), and `.args = {}` still forwards nothing. It is the shape of
Configuration::target = HostTarget(). A project.cpp that drops its .args
needs a crafter-build with this change: an older one compiles it and
forwards nothing.
ProjectArgs() is declared in :External, next to the defaults that call
it. :Platform, where it is defined, cannot be imported there: its
unexported `struct Configuration;` would make :External's exported one an
ill-formed redeclaration, which clang rejects.
The #pragma crafter dependency path now passes `.args = {}` explicitly.
Host tooling is built for the host whatever the consumer targets and was
loaded with no args; the default would otherwise hand it the consumer's
--target= and project flags.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
114 lines
5.6 KiB
C++
114 lines
5.6 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::string error;
|
|
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);
|
|
// 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);
|
|
ExternalBuildResult BuildExternal(const ExternalDependency& dep, std::string_view target, std::atomic<bool>& cancelled);
|
|
|
|
// The args LoadProject was given for the project file currently running
|
|
// its CrafterBuildProject: what that function receives, standard flags
|
|
// included (cfg.projectArgs is only the part ApplyStandardArgs left
|
|
// over). Empty when no project file is running, and on any thread but
|
|
// the one loading it, because it reads the stack LoadProject keeps per
|
|
// thread, as IsTopLevelProject does. 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, so --target=, --march=, --debug and flags of its own
|
|
// such as --local reach a whole chain of sibling projects without being
|
|
// passed. Set them to forward something else: a copy without one of this
|
|
// project's flags, one with an extra --target=, or {} for nothing.
|
|
struct GitProjectSpec {
|
|
GitSource source;
|
|
fs::path projectFile = "project.cpp";
|
|
std::vector<std::string> args = ProjectArgs();
|
|
};
|
|
|
|
// Clones the spec's git URL into the per-project external cache (keyed by
|
|
// url+branch+commit+args+projectFile), recursively LoadProject's the
|
|
// remote's project.cpp, and returns a stable Configuration* you can drop
|
|
// into cfg.dependencies. The Configuration is owned by an internal cache
|
|
// for the lifetime of the build; identical specs share one Configuration.
|
|
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);
|
|
}
|