diff --git a/Source/cmAddCustomTargetCommand.cxx b/Source/cmAddCustomTargetCommand.cxx index d7e479a66..06998c687 100644 --- a/Source/cmAddCustomTargetCommand.cxx +++ b/Source/cmAddCustomTargetCommand.cxx @@ -161,7 +161,7 @@ bool cmAddCustomTargetCommand // Enforce name uniqueness. { std::string msg; - if(!this->Makefile->EnforceUniqueName(args[0], msg)) + if(!this->Makefile->EnforceUniqueName(args[0], msg, true)) { this->SetError(msg.c_str()); return false; diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index 25d1beff6..003788eb7 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -733,12 +733,39 @@ void cmGlobalGenerator::Configure() } } +bool cmGlobalGenerator::CheckALLOW_DUPLICATE_CUSTOM_TARGETS() +{ + // If the property is not enabled then okay. + if(!this->CMakeInstance + ->GetPropertyAsBool("ALLOW_DUPLICATE_CUSTOM_TARGETS")) + { + return true; + } + + // This generator does not support duplicate custom targets. + cmOStringStream e; + e << "This project has enabled the ALLOW_DUPLICATE_CUSTOM_TARGETS " + << "global property. " + << "The \"" << this->GetName() << "\" generator does not support " + << "duplicate custom targets. " + << "Consider using a Makefiles generator or fix the project to not " + << "use duplicat target names."; + cmSystemTools::Error(e.str().c_str()); + return false; +} + void cmGlobalGenerator::Generate() { // Some generators track files replaced during the Generate. // Start with an empty vector: this->FilesReplacedDuringGenerate.clear(); + // Check whether this generator is allowed to run. + if(!this->CheckALLOW_DUPLICATE_CUSTOM_TARGETS()) + { + return; + } + // For each existing cmLocalGenerator unsigned int i; diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index 9c13a4e2c..8ea408e61 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -258,6 +258,8 @@ protected: void SetLanguageEnabledFlag(const char* l, cmMakefile* mf); void SetLanguageEnabledMaps(const char* l, cmMakefile* mf); + virtual bool CheckALLOW_DUPLICATE_CUSTOM_TARGETS(); + // Fill the ProjectMap, this must be called after LocalGenerators // has been populated. void FillProjectMap(); diff --git a/Source/cmGlobalUnixMakefileGenerator3.h b/Source/cmGlobalUnixMakefileGenerator3.h index 4a6421ea5..cf8059985 100644 --- a/Source/cmGlobalUnixMakefileGenerator3.h +++ b/Source/cmGlobalUnixMakefileGenerator3.h @@ -162,6 +162,8 @@ protected: virtual const char* GetRebuildCacheTargetName() { return "rebuild_cache"; } virtual const char* GetCleanTargetName() { return "clean"; } + virtual bool CheckALLOW_DUPLICATE_CUSTOM_TARGETS() { return true; } + // Some make programs (Borland) do not keep a rule if there are no // dependencies or commands. This is a problem for creating rules // that might not do anything but might have other dependencies diff --git a/Source/cmMakefile.cxx b/Source/cmMakefile.cxx index 35b0aea23..429eae8c2 100644 --- a/Source/cmMakefile.cxx +++ b/Source/cmMakefile.cxx @@ -3140,7 +3140,8 @@ cmTarget* cmMakefile::FindTargetToUse(const char* name) } //---------------------------------------------------------------------------- -bool cmMakefile::EnforceUniqueName(std::string const& name, std::string& msg) +bool cmMakefile::EnforceUniqueName(std::string const& name, std::string& msg, + bool isCustom) { if(cmTarget* existing = this->FindTargetToUse(name.c_str())) { @@ -3158,21 +3159,76 @@ bool cmMakefile::EnforceUniqueName(std::string const& name, std::string& msg) } else if(!this->NeedBackwardsCompatibility(2, 4)) { - // The conflict is with a non-imported target. Produce an error - // that tells the user how to work around the problem. + // The conflict is with a non-imported target. + // Allow this if the user has requested support. + cmake* cm = + this->LocalGenerator->GetGlobalGenerator()->GetCMakeInstance(); + if(isCustom && existing->GetType() == cmTarget::UTILITY && + this != existing->GetMakefile() && + cm->GetPropertyAsBool("ALLOW_DUPLICATE_CUSTOM_TARGETS")) + { + return true; + } + + // Produce an error that tells the user how to work around the + // problem. cmOStringStream e; e << "cannot create target \"" << name << "\" because another target with the same name already exists. " - << "Logical target names must be globally unique. " - << "For executables and libraries, consider using the OUTPUT_NAME " - << "target property to create two targets with the same physical " - << "name while keeping logical names distinct. " - << "Custom targets must simply have globally unique names.\n" - << "If you are building an older project it is possible that " - << "it violated this rule but was working accidentally. " - << "Set CMAKE_BACKWARDS_COMPATIBILITY to 2.4 or lower to disable " - << "this error."; - msg = e.str(); + << "The existing target is "; + switch(existing->GetType()) + { + case cmTarget::EXECUTABLE: + e << "an executable "; + break; + case cmTarget::STATIC_LIBRARY: + e << "a static library "; + break; + case cmTarget::SHARED_LIBRARY: + e << "a shared library "; + break; + case cmTarget::MODULE_LIBRARY: + e << "a module library "; + break; + case cmTarget::UTILITY: + e << "a custom target "; + break; + default: break; + } + e << "created in source directory \"" + << existing->GetMakefile()->GetCurrentDirectory() << "\".\n" + << "\n"; + e << + "Logical target names must be globally unique because:\n" + " - Unique names may be referenced unambiguously both in CMake\n" + " code and on make tool command lines.\n" + " - Logical names are used by Xcode and VS IDE generators\n" + " to produce meaningful project names for the targets.\n" + "The logical name of executable and library targets does not " + "have to correspond to the physical file names built. " + "Consider using the OUTPUT_NAME target property to create two " + "targets with the same physical name while keeping logical " + "names distinct. " + "Custom targets must simply have globally unique names.\n" + "\n" + "If you are building an older project it is possible that " + "it violated this rule but was working accidentally because " + "CMake did not previously diagnose this problem. " + "Set CMAKE_BACKWARDS_COMPATIBILITY to 2.4 or lower to disable " + "this error.\n"; + if(isCustom && existing->GetType() == cmTarget::UTILITY) + { + e << + "\n" + "For projects that care only about Makefile generators and do " + "not wish to support Xcode or VS IDE generators, one may add\n" + " set_property(GLOBAL PROPERTY ALLOW_DUPLICATE_CUSTOM_TARGETS 1)\n" + "to the top of the project to allow duplicate custom targets " + "(target names must still be unique within each directory). " + "However, setting this property will cause non-Makefile generators " + "to produce an error and refuse to generate the project."; + } + msg = e.str(); return false; } } diff --git a/Source/cmMakefile.h b/Source/cmMakefile.h index f3714cb0d..4c99df13b 100644 --- a/Source/cmMakefile.h +++ b/Source/cmMakefile.h @@ -132,7 +132,8 @@ public: /** * Help enforce global target name uniqueness. */ - bool EnforceUniqueName(std::string const& name, std::string& msg); + bool EnforceUniqueName(std::string const& name, std::string& msg, + bool isCustom = false); /** * Perform FinalPass, Library dependency analysis etc before output of the diff --git a/Source/cmake.cxx b/Source/cmake.cxx index cf89a4ce6..84f4217e8 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -3322,6 +3322,25 @@ void cmake::DefineProperties(cmake *cm) "at the beginning of native build system generation. " "This property causes it to display details of its analysis to stderr."); + cm->DefineProperty( + "ALLOW_DUPLICATE_CUSTOM_TARGETS", cmProperty::GLOBAL, + "Allow duplicate custom targets to be created.", + "Normally CMake requires that all targets built in a project have " + "globally unique names. " + "This is necessary to generate meaningful project file names in " + "Xcode and VS IDE generators. " + "It also allows the target names to be referenced unambiguously.\n" + "Makefile generators are capable of supporting duplicate custom target " + "names. " + "For projects that care only about Makefile generators and do " + "not wish to support Xcode or VS IDE generators, one may set this " + "property to true to allow duplicate custom targets. " + "The property allows multiple add_custom_target command calls in " + "*different directories* to specify the same target name. " + "However, setting this property will cause non-Makefile generators " + "to produce an error and refuse to generate the project." + ); + cm->DefineProperty ("IN_TRY_COMPILE", cmProperty::GLOBAL, "Read-only property that is true during a try-compile configuration.",