Compare commits
7 changed files with 1 additions and 27 deletions
|
|
@ -149,7 +149,7 @@ These were decided deliberately. Do not undo them without the maintainer's say-s
|
||||||
## Gotchas
|
## Gotchas
|
||||||
|
|
||||||
- A `#pragma crafter dependency` target is rebuilt on every load on purpose: clang crashes rather than erroring when it imports a BMI whose own imported PCM was rebuilt underneath it, and the depfile check is what catches a regenerated host cache. Do not optimise the rebuild away.
|
- A `#pragma crafter dependency` target is rebuilt on every load on purpose: clang crashes rather than erroring when it imports a BMI whose own imported PCM was rebuilt underneath it, and the depfile check is what catches a regenerated host cache. Do not optimise the rebuild away.
|
||||||
- A host-tooling library such as Crafter.LintRules may `import Crafter.Build;` without declaring it as a dependency, because `LoadProject` fills the cache it resolves from. Listing Crafter.Build in its `cfg.dependencies` would clone and build all of crafter-build for every consumer. Its test does link crafter-build, so that test is wired behind `IsTopLevelProject()`: without the guard, crafter-build loading the library as a dependency would load its own `project.cpp` again through the test's `GitProject` and hit the cycle guard.
|
- A host-tooling library such as Crafter.LintRules may `import Crafter.Build;` without declaring it as a dependency, because `LoadProject` fills the cache it resolves from. Listing Crafter.Build in its `cfg.dependencies` would clone and build all of crafter-build for every consumer.
|
||||||
- `crafter-build clean` leaves a pragma dependency's build alone; it lives with the clone in the cache.
|
- `crafter-build clean` leaves a pragma dependency's build alone; it lives with the clone in the cache.
|
||||||
- The bootstrap binary is compiled without `CRAFTER_BUILD_HAS_ASSET`, so `cfg.assets` only works in the self-hosted binary. Changing asset extensions means updating two maps in `Crafter.Build-Clang.cpp`: `assetOutput` and the transitive copy loop.
|
- The bootstrap binary is compiled without `CRAFTER_BUILD_HAS_ASSET`, so `cfg.assets` only works in the self-hosted binary. Changing asset extensions means updating two maps in `Crafter.Build-Clang.cpp`: `assetOutput` and the transitive copy loop.
|
||||||
- A warning is printed only when its unit actually recompiles. A build with nothing to do is silent, which is not evidence that a warning is gone.
|
- A warning is printed only when its unit actually recompiles. A build with nothing to do is silent, which is not evidence that a warning is gone.
|
||||||
|
|
|
||||||
|
|
@ -83,8 +83,6 @@ cfg.dependencies = { math };
|
||||||
|
|
||||||
Clones share the external cache and identical specs share one `Configuration`. An unpinned source is `git pull`ed on every invocation; set `.commit` for reproducible, offline-after-first-fetch builds.
|
Clones share the external cache and identical specs share one `Configuration`. An unpinned source is `git pull`ed on every invocation; set `.commit` for reproducible, offline-after-first-fetch builds.
|
||||||
|
|
||||||
A project loaded this way runs its `CrafterBuildProject` as a dependency, and `IsTopLevelProject()` is `false` while it does (`true` in the project crafter-build was pointed at, and outside any project file). Wire test-only dependencies behind it: a library whose test links crafter-build itself would otherwise load crafter-build's `project.cpp` from inside crafter-build and recurse.
|
|
||||||
|
|
||||||
### Dependencies of project.cpp itself
|
### Dependencies of project.cpp itself
|
||||||
|
|
||||||
`project.cpp` can import modules from another crafter-build project. The file has to compile before it can say anything, so the dependency is a directive next to the `import` it enables:
|
`project.cpp` can import modules from another crafter-build project. The file has to compile before it can say anything, so the dependency is a directive next to the `import` it enables:
|
||||||
|
|
|
||||||
|
|
@ -554,12 +554,6 @@ Configuration* Crafter::ProjectDependency(std::string_view name) {
|
||||||
throw std::runtime_error(std::format("ProjectDependency('{}'): not declared by {} (declared: {})", name, frame.project.string(), declared.empty() ? "none" : declared));
|
throw std::runtime_error(std::format("ProjectDependency('{}'): not declared by {} (declared: {})", name, frame.project.string(), declared.empty() ? "none" : declared));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Crafter::IsTopLevelProject() {
|
|
||||||
// One frame is the project file crafter-build was pointed at; a second
|
|
||||||
// is only ever pushed for a dependency's project file.
|
|
||||||
return LoadingFrames.size() <= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
||||||
CommandResult Crafter::RunCommandChecked(std::string_view cmd) {
|
CommandResult Crafter::RunCommandChecked(std::string_view cmd) {
|
||||||
std::array<char, 128> buffer;
|
std::array<char, 128> buffer;
|
||||||
|
|
|
||||||
|
|
@ -41,15 +41,6 @@ namespace Crafter {
|
||||||
// Throws when no such dependency was declared, or when called from
|
// Throws when no such dependency was declared, or when called from
|
||||||
// anywhere other than inside LoadProject.
|
// anywhere other than inside LoadProject.
|
||||||
export CRAFTER_API Configuration* ProjectDependency(std::string_view name);
|
export CRAFTER_API Configuration* ProjectDependency(std::string_view name);
|
||||||
// True while the project file crafter-build was pointed at runs its
|
|
||||||
// CrafterBuildProject, and when no project file is running at all; false
|
|
||||||
// inside a dependency's project file (a `#pragma crafter dependency`,
|
|
||||||
// GitProject or LocalProject target). Wire anything only the project's
|
|
||||||
// own build or test run needs behind it. The case that forced it: a
|
|
||||||
// library's test links crafter-build itself, so when crafter-build loads
|
|
||||||
// that library as a dependency, the test's GitProject loads crafter-build's
|
|
||||||
// project file in turn and the two recurse until the cycle guard throws.
|
|
||||||
export CRAFTER_API bool IsTopLevelProject();
|
|
||||||
// Resolves the directory holding distributed runtime assets (Crafter.Build
|
// Resolves the directory holding distributed runtime assets (Crafter.Build
|
||||||
// module sources, wasi-runtime/, etc). Honors CRAFTER_BUILD_HOME; otherwise
|
// module sources, wasi-runtime/, etc). Honors CRAFTER_BUILD_HOME; otherwise
|
||||||
// derives <prefix>/share/crafter-build from the running executable's path.
|
// derives <prefix>/share/crafter-build from the running executable's path.
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,5 @@ extern "C" Configuration CrafterBuildProject(std::span<const std::string_view>)
|
||||||
// Once by the module it exports, once by its configuration name; the
|
// Once by the module it exports, once by its configuration name; the
|
||||||
// test expects the same object back for both.
|
// test expects the same object back for both.
|
||||||
cfg.dependencies = { ProjectDependency("Greeter"), ProjectDependency("greeter") };
|
cfg.dependencies = { ProjectDependency("Greeter"), ProjectDependency("greeter") };
|
||||||
// Reported through a flag the test can read back: this file is the one
|
|
||||||
// LoadProject was pointed at, the greeter's is not.
|
|
||||||
cfg.compileFlags.push_back(IsTopLevelProject() ? "-DTOP_LEVEL=1" : "-DTOP_LEVEL=0");
|
|
||||||
return cfg;
|
return cfg;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,5 @@ extern "C" Configuration CrafterBuildProject(std::span<const std::string_view> a
|
||||||
std::array<fs::path, 1> ifaces = { "interfaces/Greeter" };
|
std::array<fs::path, 1> ifaces = { "interfaces/Greeter" };
|
||||||
std::array<fs::path, 0> impls = {};
|
std::array<fs::path, 0> impls = {};
|
||||||
cfg.GetInterfacesAndImplementations(ifaces, impls);
|
cfg.GetInterfacesAndImplementations(ifaces, impls);
|
||||||
// Loaded as a dependency, so this must come out false (see app/).
|
|
||||||
cfg.compileFlags.push_back(IsTopLevelProject() ? "-DTOP_LEVEL=1" : "-DTOP_LEVEL=0");
|
|
||||||
return cfg;
|
return cfg;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,14 +92,11 @@ int main(int argc, char** argv) {
|
||||||
const fs::path appSo = scratch / "app" / "build" / "project.so";
|
const fs::path appSo = scratch / "app" / "build" / "project.so";
|
||||||
std::array<std::string_view, 0> noArgs = {};
|
std::array<std::string_view, 0> noArgs = {};
|
||||||
|
|
||||||
Check(IsTopLevelProject(), "outside any project file counts as top level");
|
|
||||||
|
|
||||||
// The dependency is fetched, built and linked before project.cpp runs,
|
// The dependency is fetched, built and linked before project.cpp runs,
|
||||||
// and ProjectDependency() hands it back by either name.
|
// and ProjectDependency() hands it back by either name.
|
||||||
try {
|
try {
|
||||||
Configuration cfg = LoadProject(app, noArgs);
|
Configuration cfg = LoadProject(app, noArgs);
|
||||||
Check(cfg.name == "hello from greeter", std::format("project.cpp imported and called the dependency's module (got '{}')", cfg.name));
|
Check(cfg.name == "hello from greeter", std::format("project.cpp imported and called the dependency's module (got '{}')", cfg.name));
|
||||||
Check(std::ranges::contains(cfg.compileFlags, "-DTOP_LEVEL=1"), "the project LoadProject was pointed at sees itself as top level");
|
|
||||||
Check(cfg.dependencies.size() == 2, "ProjectDependency resolved by module name and by configuration name");
|
Check(cfg.dependencies.size() == 2, "ProjectDependency resolved by module name and by configuration name");
|
||||||
if (cfg.dependencies.size() == 2) {
|
if (cfg.dependencies.size() == 2) {
|
||||||
Check(cfg.dependencies[0] == cfg.dependencies[1], "both names resolve to the same dependency");
|
Check(cfg.dependencies[0] == cfg.dependencies[1], "both names resolve to the same dependency");
|
||||||
|
|
@ -108,7 +105,6 @@ int main(int argc, char** argv) {
|
||||||
Check(dep.target == HostTarget(), "the dependency was built for the host triple LoadProject uses");
|
Check(dep.target == HostTarget(), "the dependency was built for the host triple LoadProject uses");
|
||||||
Check(dep.march == "native", "the environment's march was overridden for host tooling");
|
Check(dep.march == "native", "the environment's march was overridden for host tooling");
|
||||||
Check(std::ranges::contains(dep.compileFlags, "-fPIC"), "the dependency archive was built position-independent");
|
Check(std::ranges::contains(dep.compileFlags, "-fPIC"), "the dependency archive was built position-independent");
|
||||||
Check(std::ranges::contains(dep.compileFlags, "-DTOP_LEVEL=0"), "a dependency's project file does not see itself as top level");
|
|
||||||
Check(fs::exists(ExpectedOutput(dep)), "the dependency archive exists");
|
Check(fs::exists(ExpectedOutput(dep)), "the dependency archive exists");
|
||||||
}
|
}
|
||||||
} catch (const std::exception& e) {
|
} catch (const std::exception& e) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue