Merge branch 'claude/issue-41': refuse a build that holds two builds of one module
All checks were successful
Flatpak / flatpak (push) Successful in 12m49s
All checks were successful
Flatpak / flatpak (push) Successful in 12m49s
PR #42, fixes #41. Build() now refuses a configuration whose transitive dependencies hold two configurations exporting a module of the same name, such as one project loaded along two paths with different arg lists, before it builds anything, and names both paths. Until now both builds ended up in the one program, which compiled cleanly and then read garbage. Merged cleanly on top of claude/forward-project-args; the suite, with claude/dependency-notes' fixtures, passes under the new check. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
eefd1df3eb
11 changed files with 233 additions and 4 deletions
|
|
@ -444,6 +444,42 @@ namespace {
|
|||
}
|
||||
return outcome;
|
||||
}
|
||||
|
||||
// Two configurations among `config` and its transitive dependencies that
|
||||
// export a module of the same name: the same project loaded along two
|
||||
// paths with different args, say, or from a checkout on one path and a
|
||||
// clone on the other. A program can hold one module of each name. Built
|
||||
// anyway, both PCM dirs went onto the consumer's -fprebuilt-module-path,
|
||||
// clang resolved the module by name to the first for every import, the
|
||||
// ones inside the other build's consumers included, and nothing checked
|
||||
// that it was the build they had been compiled against. The program then
|
||||
// linked code compiled against two layouts of the same types, and ran
|
||||
// (issue #41). Empty when the dependencies agree, otherwise the error,
|
||||
// naming the path to each of the two builds.
|
||||
std::string DuplicateModuleError(const Configuration& config) {
|
||||
struct Owner {
|
||||
const Configuration* cfg;
|
||||
std::string chain;
|
||||
};
|
||||
const auto describe = [](const Owner& owner) {
|
||||
return std::format("{} ({}, {}, project args {})", owner.chain, owner.cfg->path.string(), owner.cfg->VariantId(), owner.cfg->projectArgs);
|
||||
};
|
||||
std::unordered_map<std::string, Owner> owners;
|
||||
std::unordered_set<const Configuration*> visited;
|
||||
std::string error;
|
||||
std::function<void(const Configuration&, const std::string&)> walk = [&](const Configuration& c, const std::string& chain) {
|
||||
if (!error.empty() || !visited.insert(&c).second) return;
|
||||
for (const std::unique_ptr<Module>& iface : c.interfaces) {
|
||||
const auto [it, inserted] = owners.emplace(iface->name, Owner{ &c, chain });
|
||||
if (inserted || it->second.cfg == &c) continue;
|
||||
error = std::format("module {} is built twice for {}:\n {}\n {}\nA program can hold one build of each module: load {} with the same args, and from the same place, along every path.", iface->name, config.name, describe(it->second), describe(Owner{ &c, chain }), c.name);
|
||||
return;
|
||||
}
|
||||
for (const Configuration* dep : c.dependencies) walk(*dep, std::format("{} -> {}", chain, dep->name));
|
||||
};
|
||||
walk(config, config.name);
|
||||
return error;
|
||||
}
|
||||
}
|
||||
|
||||
BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, std::shared_future<BuildResult>>& depResults, std::mutex& depMutex) {
|
||||
|
|
@ -452,6 +488,11 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
|
|||
if (const Toolchain& tc = GetToolchain(); !tc.error.empty()) {
|
||||
return {tc.error, false, {}};
|
||||
}
|
||||
// Before any directory is made or dependency built, so a refused graph
|
||||
// leaves nothing behind.
|
||||
if (std::string duplicate = DuplicateModuleError(config); !duplicate.empty()) {
|
||||
return {std::move(duplicate), false, {}};
|
||||
}
|
||||
// Reset per-build cached state on every Module/ModulePartition so that
|
||||
// successive Build() calls on the same Configuration re-evaluate mtimes
|
||||
// (incremental-rebuild test scenarios).
|
||||
|
|
|
|||
Loading…
Reference in a new issue