All checks were successful
Flatpak / flatpak (push) Successful in 12m18s
Brings in dependency notes: a crafter-build project that ships an AGENTS.consumer.md beside its project.cpp has it kept in a marked section of each consumer's AGENTS.md, refreshed by `crafter-build` and `crafter-build test`, chosen by cfg.dependencyNotes (Transitive, Direct, None). The new partition is Crafter.Build:Agents; the loader records where each GitProject and LocalProject came from and what a project file's pragmas resolved to. The branch started before master's four external-fetch commits. Both touch External.cpp, the Clang partition, project.cpp, README.md and AGENTS.md, but in different places, so the merge needed no hand reconciliation; the merged tree passes build.sh, the self-host build, format --check and the test suite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
143 lines
7.4 KiB
C++
143 lines
7.4 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);
|
|
|
|
// 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
|
|
// forwarded verbatim to the dep's CrafterBuildProject — typically you
|
|
// pass through the parent's args so target/march/debug propagate.
|
|
struct GitProjectSpec {
|
|
GitSource source;
|
|
fs::path projectFile = "project.cpp";
|
|
std::vector<std::string> args;
|
|
};
|
|
|
|
// 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; // forwarded to dep's CrafterBuildProject
|
|
};
|
|
|
|
// 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);
|
|
}
|
|
|
|
namespace Crafter {
|
|
// Where GitProject or LocalProject loaded a Configuration from. The
|
|
// loader keeps this beside the cache that owns the Configuration, since
|
|
// the Configuration itself cannot say: only the one they return has its
|
|
// path rebased, and the configurations a dependency's project file makes
|
|
// for itself keep a path such as "./", which resolves against the
|
|
// consumer's directory.
|
|
struct LoadedProject {
|
|
fs::path projectFile; // absolute
|
|
// What a consumer's AGENTS.md calls it: the repository's name for a
|
|
// git project, with the path below the clone's root for one that is
|
|
// not at it, and the directory's name for a local one. The same on
|
|
// every machine and for a --local checkout and its clone alike,
|
|
// which a path into the cache or a sibling checkout is not, and free
|
|
// of what a URL can carry, credentials included.
|
|
std::string name;
|
|
// Which library this is, whatever the revision: the repository and
|
|
// the project file in it for git, the project file for a local one.
|
|
// Two specs that pin one library differently are one library.
|
|
std::string identity;
|
|
};
|
|
// The LoadedProject behind a Configuration that GitProject or
|
|
// LocalProject returned; nullopt for any other: the project crafter-build
|
|
// was pointed at, and every configuration a project file makes itself.
|
|
std::optional<LoadedProject> LoadedProjectOf(const Configuration& cfg);
|
|
}
|