mirror of
https://gitlab.kitware.com/cmake/cmake.git
synced 2026-09-25 04:09:36 +03:00
cmGeneratorTarget: detect colliding target directories
Some projects might get unlucky and have targets which collide in the sha3-512 hash (truncated to 4) in a directory which also collides in the same way (easily done by sharing it). Note such problems as potentially colliding and offer potential remedies. Also add a test which generates the error. Closes: #27465
This commit is contained in:
@@ -94,6 +94,7 @@ std::string cmExportTryCompileFileGenerator::FindTargets(
|
||||
cmTarget dummyHead("try_compile_dummy_exe", cmStateEnums::EXECUTABLE,
|
||||
cmTarget::Visibility::Normal, tgt->Target->GetMakefile(),
|
||||
cmTarget::PerConfig::Yes);
|
||||
dummyHead.SetIsForTryCompile();
|
||||
|
||||
cmGeneratorTarget gDummyHead(&dummyHead, tgt->GetLocalGenerator());
|
||||
|
||||
|
||||
@@ -5483,8 +5483,27 @@ std::string cmGeneratorTarget::GetSupportDirectory(
|
||||
cmStateEnums::IntermediateDirKind kind) const
|
||||
{
|
||||
cmLocalGenerator* lg = this->GetLocalGenerator();
|
||||
return cmStrCat(lg->GetObjectOutputRoot(kind), '/',
|
||||
lg->GetTargetDirectory(this, kind));
|
||||
auto targetDir = cmStrCat(lg->GetObjectOutputRoot(kind), '/',
|
||||
lg->GetTargetDirectory(this, kind));
|
||||
|
||||
#ifndef CMAKE_BOOTSTRAP
|
||||
auto& tdr =
|
||||
this->GetGlobalGenerator()->RegisterTargetDirectory(this, targetDir);
|
||||
if (tdr.CollidesWith && !tdr.Warned) {
|
||||
this->Makefile->IssueMessage(
|
||||
MessageType::WARNING,
|
||||
cmStrCat("The '", tdr.CollidesWith->GetName(), "' and '",
|
||||
this->GetName(),
|
||||
"' targets share an intermediate directory\n ", targetDir,
|
||||
"\nwhich may cause problems with the build graph. This project "
|
||||
"is not compatible with the `SHORT` target intermediate "
|
||||
"directory strategy. Possible remedies include: moving the "
|
||||
"target into different directories or renaming a target."));
|
||||
tdr.Warned = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return targetDir;
|
||||
}
|
||||
|
||||
std::string cmGeneratorTarget::GetCMFSupportDirectory(
|
||||
|
||||
@@ -2054,6 +2054,35 @@ std::string cmGlobalGenerator::ComputeTargetShortName(
|
||||
return cmStrCat(tgtHash, dirHash);
|
||||
}
|
||||
|
||||
cmGlobalGenerator::TargetDirectoryRegistration&
|
||||
cmGlobalGenerator::RegisterTargetDirectory(cmGeneratorTarget const* tgt,
|
||||
std::string const& targetDir) const
|
||||
{
|
||||
if (!tgt->IsNormal() || tgt->GetType() == cmStateEnums::GLOBAL_TARGET ||
|
||||
tgt->Target->IsForTryCompile()) {
|
||||
static TargetDirectoryRegistration utilityRegistration(nullptr, true);
|
||||
return utilityRegistration;
|
||||
}
|
||||
|
||||
// Get the registration instance for the target.
|
||||
#if __cplusplus >= 201703L
|
||||
auto registration = this->TargetDirectoryRegistrations.try_emplace(tgt);
|
||||
#else
|
||||
auto registration = this->TargetDirectoryRegistrations.insert(
|
||||
std::make_pair(tgt, TargetDirectoryRegistration()));
|
||||
#endif
|
||||
// If it was just inserted, search for a `CollidesWith` possibility.
|
||||
if (registration.second) {
|
||||
auto& otherTargets = this->TargetDirectories[targetDir];
|
||||
if (!otherTargets.empty()) {
|
||||
registration.first->second.CollidesWith = *otherTargets.begin();
|
||||
}
|
||||
otherTargets.insert(tgt);
|
||||
}
|
||||
|
||||
return registration.first->second;
|
||||
}
|
||||
|
||||
void cmGlobalGenerator::ComputeTargetObjectDirectory(
|
||||
cmGeneratorTarget* /*unused*/) const
|
||||
{
|
||||
|
||||
@@ -648,6 +648,20 @@ public:
|
||||
virtual std::string GetShortBinaryOutputDir() const;
|
||||
std::string ComputeTargetShortName(std::string const& bindir,
|
||||
std::string const& targetName) const;
|
||||
struct TargetDirectoryRegistration
|
||||
{
|
||||
TargetDirectoryRegistration() = default;
|
||||
TargetDirectoryRegistration(cmGeneratorTarget const* t, bool w)
|
||||
: CollidesWith(t)
|
||||
, Warned(w)
|
||||
{
|
||||
}
|
||||
|
||||
cmGeneratorTarget const* CollidesWith = nullptr;
|
||||
bool Warned = false;
|
||||
};
|
||||
TargetDirectoryRegistration& RegisterTargetDirectory(
|
||||
cmGeneratorTarget const* tgt, std::string const& targetDir) const;
|
||||
|
||||
virtual void ComputeTargetObjectDirectory(cmGeneratorTarget* gt) const;
|
||||
|
||||
@@ -823,12 +837,21 @@ private:
|
||||
std::unordered_map<std::string, cmGeneratorTarget*>;
|
||||
using MakefileMap = std::unordered_map<std::string, cmMakefile*>;
|
||||
using LocalGeneratorMap = std::unordered_map<std::string, cmLocalGenerator*>;
|
||||
using TargetDirectoryRegistrationMap =
|
||||
std::map<cmGeneratorTarget const*, TargetDirectoryRegistration>;
|
||||
using TargetDirectoryMap =
|
||||
std::unordered_map<std::string, std::set<cmGeneratorTarget const*>>;
|
||||
// Map efficiently from target name to cmTarget instance.
|
||||
// Do not use this structure for looping over all targets.
|
||||
// It contains both normal and globally visible imported targets.
|
||||
TargetMap TargetSearchIndex;
|
||||
GeneratorTargetMap GeneratorTargetSearchIndex;
|
||||
|
||||
// Map from target to a directory registration.
|
||||
mutable TargetDirectoryRegistrationMap TargetDirectoryRegistrations;
|
||||
// Map from target directories to targets using it.
|
||||
mutable TargetDirectoryMap TargetDirectories;
|
||||
|
||||
// Map efficiently from source directory path to cmMakefile instance.
|
||||
// Do not use this structure for looping over all directories.
|
||||
// It may not contain all of them (see note in IndexMakefile method).
|
||||
|
||||
@@ -609,6 +609,7 @@ public:
|
||||
bool BuildInterfaceIncludesAppended;
|
||||
bool PerConfig;
|
||||
bool IsSymbolic;
|
||||
bool IsForTryCompile{ false };
|
||||
cmTarget::Visibility TargetVisibility;
|
||||
std::set<BT<std::pair<std::string, bool>>> Utilities;
|
||||
std::set<std::string> CodegenDependencies;
|
||||
@@ -2906,6 +2907,16 @@ bool cmTarget::CanCompileSources() const
|
||||
return false;
|
||||
}
|
||||
|
||||
void cmTarget::SetIsForTryCompile()
|
||||
{
|
||||
this->impl->IsForTryCompile = true;
|
||||
}
|
||||
|
||||
bool cmTarget::IsForTryCompile() const
|
||||
{
|
||||
return this->impl->IsForTryCompile;
|
||||
}
|
||||
|
||||
char const* cmTarget::GetSuffixVariableInternal(
|
||||
cmStateEnums::ArtifactType artifact) const
|
||||
{
|
||||
|
||||
@@ -237,6 +237,8 @@ public:
|
||||
bool IsRuntimeBinary() const;
|
||||
bool IsSymbolic() const;
|
||||
bool CanCompileSources() const;
|
||||
void SetIsForTryCompile();
|
||||
bool IsForTryCompile() const;
|
||||
|
||||
bool GetMappedConfig(std::string const& desiredConfig, cmValue& loc,
|
||||
cmValue& imp, std::string& suffix) const;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
CMake Warning in collides/CMakeLists.txt:
|
||||
The 'CubeTestPluginLogic' and 'TeamworkServerBlobStoreUploadSessionStore'
|
||||
targets share an intermediate directory
|
||||
|
||||
.*/Tests/RunCMake/IntermediateDirStrategy/Collides-build/(collides/)?.o/039c43fd
|
||||
|
||||
which may cause problems with the build graph. This project is not
|
||||
compatible with the `SHORT` target intermediate directory strategy.
|
||||
Possible remedies include: moving the target into different directories or
|
||||
renaming a target.
|
||||
@@ -0,0 +1,4 @@
|
||||
set(CMAKE_INTERMEDIATE_DIR_STRATEGY SHORT CACHE STRING "" FORCE)
|
||||
enable_language(C)
|
||||
|
||||
add_subdirectory(collides)
|
||||
@@ -47,5 +47,6 @@ function(run_install_test case)
|
||||
endfunction()
|
||||
|
||||
if (RunCMake_GENERATOR MATCHES "(Ninja|Makefiles|Visual Studio)")
|
||||
run_cmake(Collides)
|
||||
run_install_test(ShortObjectDoesntChangeInstall)
|
||||
endif ()
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
add_library(CubeTestPluginLogic STATIC lib1.c)
|
||||
add_library(TeamworkServerBlobStoreUploadSessionStore STATIC lib2.c)
|
||||
@@ -0,0 +1,4 @@
|
||||
int f(int a)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
int f(int a)
|
||||
{
|
||||
return a;
|
||||
}
|
||||
Reference in New Issue
Block a user