// 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 options; std::vector 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 libDirs; std::vector libs; }; struct ExternalBuildResult { std::string error; std::vector compileFlags; std::vector 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 ExternalIncludeFlags(const ExternalDependency& dep, std::string_view target); ExternalBuildResult BuildExternal(const ExternalDependency& dep, std::string_view target, std::atomic& 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 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 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 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); }