Crafter.Build/interfaces/Crafter.Build-External.cppm

141 lines
7.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®
2026-04-27 07:04:42 +02:00
export module Crafter.Build:External;
import std;
namespace fs = std::filesystem;
export namespace Crafter {
2026-04-30 02:20:19 +02:00
struct Configuration;
2026-04-27 07:04:42 +02:00
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;
2026-05-06 03:59:19 +02:00
// 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;
2026-04-27 07:04:42 +02:00
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();
};
feat(lint): no-char-pointer reads the AST, retiring the interop denylist The rule was `\bchar\s*\*` over the text minus a substring denylist — argv, getenv, setenv, dlerror, c_str, .data(, reinterpret_cast, extern ". Every entry was a patch for one interop site, the list could only grow as libraries arrived, and each entry disabled the rule for the whole LINE it appeared on. It now walks declarations and asks the question the denylist was approximating: whose header dictates this spelling? A declaration with C language linkage, or one whose initialiser binds to an entity declared outside the project root, is somebody else's API and keeps its spelling. getenv, c_str and friends are exempt because of where they are declared, not because they are named here, so a new external library needs no new entry. Two bugs found while testing this, both of which had made the rule silently pass over the entire repository: clang_getCursorLanguage cannot be used to detect extern "C". Its default answer is CXLanguage_C for a plain function, variable or parameter even in a C++ translation unit, so isExternC was true almost everywhere and exempted everything. Replaced by tracking CXCursor_LinkageSpec depth during the walk, reading the extent text to tell extern "C" from extern "C++". Attributing any foreign reference in a subtree to the enclosing declaration was too broad: a function that merely touched libc++ somewhere in its body would exempt its own signature. Narrowed to initialiser contexts — a variable, field or parameter — which is where a binding to a foreign API actually occurs. Also: functions now carry their RESULT type rather than the whole function type, since the parameters arrive as their own declarations and would otherwise be reported twice. main's parameters are exempt structurally, its signature being fixed by the language rather than chosen here. Two sites keep an explicit lint-disable, both Crafter::Run taking main's argv verbatim. That is two visible, reasoned suppressions in place of a denylist that silently disabled the rule for every line mentioning one of eight tokens. ExternalCloneDir and ExternalIncludeFlags are now exposed from :External, so a source that includes an external dependency's headers can be parsed without running a build to discover where they are. BuildExternal derives its own working directory through the same function, so the two cannot drift. Crafter.Build-Shader.cpp needed this to parse at all. A file with no compile command — project.cpp, which LoadProject builds with its own flags — is not a translation unit of the build graph, so AST rules skip it the way a rule self-filters by extension. That is distinct from a file that should have parsed and did not, which stays an error. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 23:46:32 +02:00
// 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.
refactor: drop Windows host support crafter-build is moving to a single Flatpak distribution, and there is no Windows host in that picture: no build.cmd, no MSVC or mingw host build, no DLL plus launcher. Delete every host-Windows path instead of leaving it to rot unverified: the cmd.exe command runner and quoting, the LOCALAPPDATA cache, the MSVC and mingw-host LoadProject variants with their LIBCXX_DIR and CRAFTER_MINGW_DIR plumbing, the winsock port probe, the LoadLibrary libclang loader, the console-width and isatty shims, and the preprocessor guards around the Linux bodies. The x86_64-w64-mingw32 cross-compile target stays. It is target logic (output names, -femulated-tls, the libstdc++ link extras, copying a dependency DLL next to a mingw exe, wine as the runner) and did not depend on any of the removed code. The msvc target goes with the host that was the only thing able to use it. Three target-path warts fixed on the way: ExpectedOutput named a mingw static library <name>.lib while the archive step wrote lib<name>.a, so those libraries relinked on every run; the import lib is no longer copied next to a mingw exe, since the dependency's own bin dir is already on the link line; and llvm-ar now writes every archive, not only the LTO ones, so Debug and Release archives are made the same way. CRAFTER_API and Crafter.Build-Api.h existed only for the DLL boundary. Nothing downstream used them, so they are removed rather than kept as an empty macro nobody can verify. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-02 21:56:38 +02:00
fs::path ExternalCloneDir(const ExternalDependency& dep, std::string_view target);
fix(external): fetch and build each clone once per build pass `crafter-build test` builds a project's tests side by side, and a project whose tests each copy its externalDependencies hands every one of them the same unpinned dependency. Each test's Build() pulled the shared clone. Before the clone had a lock, those pulls ran at the same time and broke its .git/FETCH_HEAD, which failed every test of Crafter.Graphics (issue #34). Since 653dd0b they wait for each other, but every one is still a network round trip: 26 tests cost 26 pulls of each dependency, one after another, and for a CMake dependency as many configure checks and builds. depResults already records one build pass, keyed by the PcmDir of every dependency configuration built in it. An external dependency now goes in the same map, keyed by the directory its build leaves (ExternalBuildDir: the clone, or for CMake its build directory). The first configuration that needs it fetches and builds it; the others wait for that and take its outcome. Keying on the build directory rather than the clone keeps a CMake declaration from taking the outcome of a header-only declaration of the same clone, which only fetched it. A fresh depResults is a fresh pass, so the next build still pulls, and every caller of Build() gets this: RunTests, a plain build, and the pragma dependencies LoadProject builds. To make that possible BuildExternal is split. BuildExternal is now the shared work, lock, fetch, configure and build, and returns the error. ExternalOutputs computes what one declaration compiles and links with, because two declarations of one clone can differ in includeDirs, libDirs and libs; ExternalBuildResult loses its error field. The work no longer stops between steps when the configuration that started it is cancelled: others may be waiting on it, and a run cut short would leave them a clone nobody fetched. A dependency configuration's Build() already runs to completion for the same reason. The promise and future the dependency threads used to build or wait for a configuration are now one helper, OncePerPass, which both kinds use. An exception thrown in BuildExternal used to escape its thread into std::terminate; it is now a build error, as for a dependency. With eight tests that share a local dependency, a test run fetched it eight times before this and fetches it once now (SharedExternalFetch, next commit). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 02:09:24 +01:00
// 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);
feat(lint): no-char-pointer reads the AST, retiring the interop denylist The rule was `\bchar\s*\*` over the text minus a substring denylist — argv, getenv, setenv, dlerror, c_str, .data(, reinterpret_cast, extern ". Every entry was a patch for one interop site, the list could only grow as libraries arrived, and each entry disabled the rule for the whole LINE it appeared on. It now walks declarations and asks the question the denylist was approximating: whose header dictates this spelling? A declaration with C language linkage, or one whose initialiser binds to an entity declared outside the project root, is somebody else's API and keeps its spelling. getenv, c_str and friends are exempt because of where they are declared, not because they are named here, so a new external library needs no new entry. Two bugs found while testing this, both of which had made the rule silently pass over the entire repository: clang_getCursorLanguage cannot be used to detect extern "C". Its default answer is CXLanguage_C for a plain function, variable or parameter even in a C++ translation unit, so isExternC was true almost everywhere and exempted everything. Replaced by tracking CXCursor_LinkageSpec depth during the walk, reading the extent text to tell extern "C" from extern "C++". Attributing any foreign reference in a subtree to the enclosing declaration was too broad: a function that merely touched libc++ somewhere in its body would exempt its own signature. Narrowed to initialiser contexts — a variable, field or parameter — which is where a binding to a foreign API actually occurs. Also: functions now carry their RESULT type rather than the whole function type, since the parameters arrive as their own declarations and would otherwise be reported twice. main's parameters are exempt structurally, its signature being fixed by the language rather than chosen here. Two sites keep an explicit lint-disable, both Crafter::Run taking main's argv verbatim. That is two visible, reasoned suppressions in place of a denylist that silently disabled the rule for every line mentioning one of eight tokens. ExternalCloneDir and ExternalIncludeFlags are now exposed from :External, so a source that includes an external dependency's headers can be parsed without running a build to discover where they are. BuildExternal derives its own working directory through the same function, so the two cannot drift. Crafter.Build-Shader.cpp needed this to parse at all. A file with no compile command — project.cpp, which LoadProject builds with its own flags — is not a translation unit of the build graph, so AST rules skip it the way a rule self-filters by extension. That is distinct from a file that should have parsed and did not, which stays an error. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 23:46:32 +02:00
// 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.
refactor: drop Windows host support crafter-build is moving to a single Flatpak distribution, and there is no Windows host in that picture: no build.cmd, no MSVC or mingw host build, no DLL plus launcher. Delete every host-Windows path instead of leaving it to rot unverified: the cmd.exe command runner and quoting, the LOCALAPPDATA cache, the MSVC and mingw-host LoadProject variants with their LIBCXX_DIR and CRAFTER_MINGW_DIR plumbing, the winsock port probe, the LoadLibrary libclang loader, the console-width and isatty shims, and the preprocessor guards around the Linux bodies. The x86_64-w64-mingw32 cross-compile target stays. It is target logic (output names, -femulated-tls, the libstdc++ link extras, copying a dependency DLL next to a mingw exe, wine as the runner) and did not depend on any of the removed code. The msvc target goes with the host that was the only thing able to use it. Three target-path warts fixed on the way: ExpectedOutput named a mingw static library <name>.lib while the archive step wrote lib<name>.a, so those libraries relinked on every run; the import lib is no longer copied next to a mingw exe, since the dependency's own bin dir is already on the link line; and llvm-ar now writes every archive, not only the LTO ones, so Debug and Release archives are made the same way. CRAFTER_API and Crafter.Build-Api.h existed only for the DLL boundary. Nothing downstream used them, so they are removed rather than kept as an empty macro nobody can verify. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-02 21:56:38 +02:00
std::vector<std::string> ExternalIncludeFlags(const ExternalDependency& dep, std::string_view target);
fix(external): fetch and build each clone once per build pass `crafter-build test` builds a project's tests side by side, and a project whose tests each copy its externalDependencies hands every one of them the same unpinned dependency. Each test's Build() pulled the shared clone. Before the clone had a lock, those pulls ran at the same time and broke its .git/FETCH_HEAD, which failed every test of Crafter.Graphics (issue #34). Since 653dd0b they wait for each other, but every one is still a network round trip: 26 tests cost 26 pulls of each dependency, one after another, and for a CMake dependency as many configure checks and builds. depResults already records one build pass, keyed by the PcmDir of every dependency configuration built in it. An external dependency now goes in the same map, keyed by the directory its build leaves (ExternalBuildDir: the clone, or for CMake its build directory). The first configuration that needs it fetches and builds it; the others wait for that and take its outcome. Keying on the build directory rather than the clone keeps a CMake declaration from taking the outcome of a header-only declaration of the same clone, which only fetched it. A fresh depResults is a fresh pass, so the next build still pulls, and every caller of Build() gets this: RunTests, a plain build, and the pragma dependencies LoadProject builds. To make that possible BuildExternal is split. BuildExternal is now the shared work, lock, fetch, configure and build, and returns the error. ExternalOutputs computes what one declaration compiles and links with, because two declarations of one clone can differ in includeDirs, libDirs and libs; ExternalBuildResult loses its error field. The work no longer stops between steps when the configuration that started it is cancelled: others may be waiting on it, and a run cut short would leave them a clone nobody fetched. A dependency configuration's Build() already runs to completion for the same reason. The promise and future the dependency threads used to build or wait for a configuration are now one helper, OncePerPass, which both kinds use. An exception thrown in BuildExternal used to escape its thread into std::terminate; it is now a build error, as for a dependency. With eight tests that share a local dependency, a test run fetched it eight times before this and fetches it once now (SharedExternalFetch, next commit). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-31 02:09:24 +01:00
// 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);
2026-04-30 02:20:19 +02:00
// 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.
feat(external): forward a project's args to its sibling projects by default 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>
2026-10-31 02:56:14 +01:00
std::vector<std::string> ProjectArgs();
2026-04-30 02:20:19 +02:00
// 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
feat(external): forward a project's args to its sibling projects by default 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>
2026-10-31 02:56:14 +01:00
// 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.
2026-04-30 02:20:19 +02:00
struct GitProjectSpec {
GitSource source;
fs::path projectFile = "project.cpp";
feat(external): forward a project's args to its sibling projects by default 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>
2026-10-31 02:56:14 +01:00
std::vector<std::string> args = ProjectArgs();
2026-04-30 02:20:19 +02:00
};
// 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.
refactor: drop Windows host support crafter-build is moving to a single Flatpak distribution, and there is no Windows host in that picture: no build.cmd, no MSVC or mingw host build, no DLL plus launcher. Delete every host-Windows path instead of leaving it to rot unverified: the cmd.exe command runner and quoting, the LOCALAPPDATA cache, the MSVC and mingw-host LoadProject variants with their LIBCXX_DIR and CRAFTER_MINGW_DIR plumbing, the winsock port probe, the LoadLibrary libclang loader, the console-width and isatty shims, and the preprocessor guards around the Linux bodies. The x86_64-w64-mingw32 cross-compile target stays. It is target logic (output names, -femulated-tls, the libstdc++ link extras, copying a dependency DLL next to a mingw exe, wine as the runner) and did not depend on any of the removed code. The msvc target goes with the host that was the only thing able to use it. Three target-path warts fixed on the way: ExpectedOutput named a mingw static library <name>.lib while the archive step wrote lib<name>.a, so those libraries relinked on every run; the import lib is no longer copied next to a mingw exe, since the dependency's own bin dir is already on the link line; and llvm-ar now writes every archive, not only the LTO ones, so Debug and Release archives are made the same way. CRAFTER_API and Crafter.Build-Api.h existed only for the DLL boundary. Nothing downstream used them, so they are removed rather than kept as an empty macro nobody can verify. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-02 21:56:38 +02:00
Configuration* GitProject(const GitProjectSpec& spec);
2026-04-30 02:20:19 +02:00
// 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 {
feat(external): forward a project's args to its sibling projects by default 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>
2026-10-31 02:56:14 +01:00
fs::path projectFile; // relative to cwd or absolute
std::vector<std::string> args = ProjectArgs(); // as GitProjectSpec::args
2026-04-30 02:20:19 +02:00
};
// 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.
refactor: drop Windows host support crafter-build is moving to a single Flatpak distribution, and there is no Windows host in that picture: no build.cmd, no MSVC or mingw host build, no DLL plus launcher. Delete every host-Windows path instead of leaving it to rot unverified: the cmd.exe command runner and quoting, the LOCALAPPDATA cache, the MSVC and mingw-host LoadProject variants with their LIBCXX_DIR and CRAFTER_MINGW_DIR plumbing, the winsock port probe, the LoadLibrary libclang loader, the console-width and isatty shims, and the preprocessor guards around the Linux bodies. The x86_64-w64-mingw32 cross-compile target stays. It is target logic (output names, -femulated-tls, the libstdc++ link extras, copying a dependency DLL next to a mingw exe, wine as the runner) and did not depend on any of the removed code. The msvc target goes with the host that was the only thing able to use it. Three target-path warts fixed on the way: ExpectedOutput named a mingw static library <name>.lib while the archive step wrote lib<name>.a, so those libraries relinked on every run; the import lib is no longer copied next to a mingw exe, since the dependency's own bin dir is already on the link line; and llvm-ar now writes every archive, not only the LTO ones, so Debug and Release archives are made the same way. CRAFTER_API and Crafter.Build-Api.h existed only for the DLL boundary. Nothing downstream used them, so they are removed rather than kept as an empty macro nobody can verify. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-02 21:56:38 +02:00
Configuration* LocalProject(const LocalProjectSpec& spec);
2026-05-19 00:50:06 +02:00
// 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.
refactor: drop Windows host support crafter-build is moving to a single Flatpak distribution, and there is no Windows host in that picture: no build.cmd, no MSVC or mingw host build, no DLL plus launcher. Delete every host-Windows path instead of leaving it to rot unverified: the cmd.exe command runner and quoting, the LOCALAPPDATA cache, the MSVC and mingw-host LoadProject variants with their LIBCXX_DIR and CRAFTER_MINGW_DIR plumbing, the winsock port probe, the LoadLibrary libclang loader, the console-width and isatty shims, and the preprocessor guards around the Linux bodies. The x86_64-w64-mingw32 cross-compile target stays. It is target logic (output names, -femulated-tls, the libstdc++ link extras, copying a dependency DLL next to a mingw exe, wine as the runner) and did not depend on any of the removed code. The msvc target goes with the host that was the only thing able to use it. Three target-path warts fixed on the way: ExpectedOutput named a mingw static library <name>.lib while the archive step wrote lib<name>.a, so those libraries relinked on every run; the import lib is no longer copied next to a mingw exe, since the dependency's own bin dir is already on the link line; and llvm-ar now writes every archive, not only the LTO ones, so Debug and Release archives are made the same way. CRAFTER_API and Crafter.Build-Api.h existed only for the DLL boundary. Nothing downstream used them, so they are removed rather than kept as an empty macro nobody can verify. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-02 21:56:38 +02:00
fs::path GitFetch(const GitSource& source);
2026-04-27 07:04:42 +02:00
}