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
|
||||
|
||||
- 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.
|
||||
- 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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
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
|
||||
|
||||
`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));
|
||||
}
|
||||
|
||||
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)
|
||||
CommandResult Crafter::RunCommandChecked(std::string_view cmd) {
|
||||
std::array<char, 128> buffer;
|
||||
|
|
|
|||
|
|
@ -41,15 +41,6 @@ namespace Crafter {
|
|||
// Throws when no such dependency was declared, or when called from
|
||||
// anywhere other than inside LoadProject.
|
||||
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
|
||||
// module sources, wasi-runtime/, etc). Honors CRAFTER_BUILD_HOME; otherwise
|
||||
// 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
|
||||
// test expects the same object back for both.
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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, 0> 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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,14 +92,11 @@ int main(int argc, char** argv) {
|
|||
const fs::path appSo = scratch / "app" / "build" / "project.so";
|
||||
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,
|
||||
// and ProjectDependency() hands it back by either name.
|
||||
try {
|
||||
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(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");
|
||||
if (cfg.dependencies.size() == 2) {
|
||||
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.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, "-DTOP_LEVEL=0"), "a dependency's project file does not see itself as top level");
|
||||
Check(fs::exists(ExpectedOutput(dep)), "the dependency archive exists");
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue