From 438a7e2fcef6c82dd93e0106208dbaee7e9ccfc4 Mon Sep 17 00:00:00 2001 From: Brad King Date: Wed, 4 Apr 2007 14:50:35 -0400 Subject: [PATCH] BUG: Fix utility dependencies for static libraries in VS generators. This addresses bug#4789. --- Source/cmGlobalVisualStudio6Generator.cxx | 12 +- Source/cmGlobalVisualStudio71Generator.cxx | 12 +- Source/cmGlobalVisualStudio7Generator.cxx | 15 +- Source/cmGlobalVisualStudio8Generator.h | 4 + Source/cmGlobalVisualStudioGenerator.cxx | 168 +++++++++++++++++++++ Source/cmGlobalVisualStudioGenerator.h | 9 ++ Tests/Dependency/Two/CMakeLists.txt | 17 +++ Tests/Dependency/Two/TwoCustomSrc.c | 10 ++ Tests/Dependency/Two/TwoSrc.c | 2 +- Tests/Dependency/Two/two-test.h.in | 1 + 10 files changed, 220 insertions(+), 30 deletions(-) create mode 100644 Tests/Dependency/Two/TwoCustomSrc.c create mode 100644 Tests/Dependency/Two/two-test.h.in diff --git a/Source/cmGlobalVisualStudio6Generator.cxx b/Source/cmGlobalVisualStudio6Generator.cxx index 10260d763..fb475c3bd 100644 --- a/Source/cmGlobalVisualStudio6Generator.cxx +++ b/Source/cmGlobalVisualStudio6Generator.cxx @@ -177,7 +177,10 @@ void cmGlobalVisualStudio6Generator::Generate() "echo", "Build all projects"); } } - + + // Fix utility dependencies to avoid linking to libraries. + this->FixUtilityDepends(); + // first do the superclass method this->cmGlobalGenerator::Generate(); @@ -438,12 +441,7 @@ void cmGlobalVisualStudio6Generator::WriteProject(std::ostream& fout, { if(*i != dspname) { - std::string depName = *i; - if(strncmp(depName.c_str(), "INCLUDE_EXTERNAL_MSPROJECT", 26) == 0) - { - depName.erase(depName.begin(), depName.begin() + 27); - } - + std::string depName = this->GetUtilityForTarget(target, i->c_str()); fout << "Begin Project Dependency\n"; fout << "Project_Dep_Name " << depName << "\n"; fout << "End Project Dependency\n"; diff --git a/Source/cmGlobalVisualStudio71Generator.cxx b/Source/cmGlobalVisualStudio71Generator.cxx index e29227db6..fec8df99b 100644 --- a/Source/cmGlobalVisualStudio71Generator.cxx +++ b/Source/cmGlobalVisualStudio71Generator.cxx @@ -349,17 +349,7 @@ cmGlobalVisualStudio71Generator { if(*i != dspname) { - std::string name = i->c_str(); - if(strncmp(name.c_str(), "INCLUDE_EXTERNAL_MSPROJECT", 26) == 0) - { - // kind of weird removing the first 27 letters. my - // recommendatsions: use cmCustomCommand::GetCommand() to get the - // project name or get rid of the target name starting with - // "INCLUDE_EXTERNAL_MSPROJECT_" and use another indicator/flag - // somewhere. These external project names shouldn't conflict - // with cmake target names anyways. - name.erase(name.begin(), name.begin() + 27); - } + std::string name = this->GetUtilityForTarget(target, i->c_str()); std::string guid = this->GetGUID(name.c_str()); if(guid.size() == 0) { diff --git a/Source/cmGlobalVisualStudio7Generator.cxx b/Source/cmGlobalVisualStudio7Generator.cxx index 278ebb2c4..191867473 100644 --- a/Source/cmGlobalVisualStudio7Generator.cxx +++ b/Source/cmGlobalVisualStudio7Generator.cxx @@ -235,6 +235,9 @@ void cmGlobalVisualStudio7Generator::Generate() } } + // Fix utility dependencies to avoid linking to libraries. + this->FixUtilityDepends(); + // first do the superclass method this->cmGlobalGenerator::Generate(); @@ -634,17 +637,7 @@ cmGlobalVisualStudio7Generator { if(*i != dspname) { - std::string name = *i; - if(strncmp(name.c_str(), "INCLUDE_EXTERNAL_MSPROJECT", 26) == 0) - { - // kind of weird removing the first 27 letters. my - // recommendatsions: use cmCustomCommand::GetCommand() to get the - // project name or get rid of the target name starting with - // "INCLUDE_EXTERNAL_MSPROJECT_" and use another indicator/flag - // somewhere. These external project names shouldn't conflict - // with cmake target names anyways. - name.erase(name.begin(), name.begin() + 27); - } + std::string name = this->GetUtilityForTarget(target, i->c_str()); std::string guid = this->GetGUID(name.c_str()); if(guid.size() == 0) { diff --git a/Source/cmGlobalVisualStudio8Generator.h b/Source/cmGlobalVisualStudio8Generator.h index 486c88580..38963e358 100644 --- a/Source/cmGlobalVisualStudio8Generator.h +++ b/Source/cmGlobalVisualStudio8Generator.h @@ -50,6 +50,10 @@ public: virtual void Configure(); virtual void Generate(); protected: + + // Utility target fix is not needed for VS8. + virtual void FixUtilityDepends() {} + static cmVS7FlagTable const* GetExtraFlagTableVS8(); virtual void AddPlatformDefinitions(cmMakefile* mf); virtual void WriteSLNFile(std::ostream& fout, cmLocalGenerator* root, diff --git a/Source/cmGlobalVisualStudioGenerator.cxx b/Source/cmGlobalVisualStudioGenerator.cxx index 8a7c4ff7b..46ce76ea5 100644 --- a/Source/cmGlobalVisualStudioGenerator.cxx +++ b/Source/cmGlobalVisualStudioGenerator.cxx @@ -16,6 +16,10 @@ =========================================================================*/ #include "cmGlobalVisualStudioGenerator.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmTarget.h" + //---------------------------------------------------------------------------- cmGlobalVisualStudioGenerator::cmGlobalVisualStudioGenerator() { @@ -25,3 +29,167 @@ cmGlobalVisualStudioGenerator::cmGlobalVisualStudioGenerator() cmGlobalVisualStudioGenerator::~cmGlobalVisualStudioGenerator() { } + +//---------------------------------------------------------------------------- +void cmGlobalVisualStudioGenerator::FixUtilityDepends() +{ + // For VS versions before 8: + // + // When a target that links contains a project-level dependency on a + // library target that library is automatically linked. In order to + // allow utility-style project-level dependencies that do not + // actually link we need to automatically insert an intermediate + // custom target. + // + // Here we edit the utility dependencies of a target to add the + // intermediate custom target when necessary. + for(unsigned i = 0; i < this->LocalGenerators.size(); ++i) + { + cmTargets* targets = + &(this->LocalGenerators[i]->GetMakefile()->GetTargets()); + for(cmTargets::iterator tarIt = targets->begin(); + tarIt != targets->end(); ++tarIt) + { + this->FixUtilityDependsForTarget(tarIt->second); + } + } +} + +//---------------------------------------------------------------------------- +void +cmGlobalVisualStudioGenerator::FixUtilityDependsForTarget(cmTarget& target) +{ + // Only targets that link need to be fixed. + if(target.GetType() != cmTarget::STATIC_LIBRARY && + target.GetType() != cmTarget::SHARED_LIBRARY && + target.GetType() != cmTarget::MODULE_LIBRARY && + target.GetType() != cmTarget::EXECUTABLE) + { + return; + } + + // Look at each utility dependency. + for(std::set::const_iterator ui = + target.GetUtilities().begin(); + ui != target.GetUtilities().end(); ++ui) + { + if(cmTarget* depTarget = this->FindTarget(0, ui->c_str())) + { + if(depTarget->GetType() == cmTarget::STATIC_LIBRARY || + depTarget->GetType() == cmTarget::SHARED_LIBRARY || + depTarget->GetType() == cmTarget::MODULE_LIBRARY) + { + // This utility dependency will cause an attempt to link. If + // the depender does not already link the dependee we need an + // intermediate target. + if(!this->CheckTargetLinks(target, ui->c_str())) + { + this->CreateUtilityDependTarget(*depTarget); + } + } + } + } +} + +//---------------------------------------------------------------------------- +void +cmGlobalVisualStudioGenerator::CreateUtilityDependTarget(cmTarget& target) +{ + // This target is a library on which a utility dependency exists. + // We need to create an intermediate custom target to hook up the + // dependency without causing a link. + const char* altName = target.GetProperty("ALTERNATIVE_DEPENDENCY_NAME"); + if(!altName) + { + // Create the intermediate utility target. + std::string altNameStr = target.GetName(); + altNameStr += "_UTILITY"; + const std::vector no_depends; + cmCustomCommandLines no_commands; + const char* no_working_dir = 0; + const char* no_comment = 0; + target.GetMakefile()->AddUtilityCommand(altNameStr.c_str(), true, + no_working_dir, no_depends, + no_commands, false, no_comment); + target.SetProperty("ALTERNATIVE_DEPENDENCY_NAME", altNameStr.c_str()); + + // Most targets have a GUID created in ConfigureFinalPass. Since + // that has already been called, create one for this target now. + this->CreateGUID(altNameStr.c_str()); + + // The intermediate target should depend on the original target. + if(cmTarget* alt = this->FindTarget(0, altNameStr.c_str())) + { + alt->AddUtility(target.GetName()); + } + } +} + +//---------------------------------------------------------------------------- +bool cmGlobalVisualStudioGenerator::CheckTargetLinks(cmTarget& target, + const char* name) +{ + // Return whether the given target links to a target with the given name. + if(target.GetType() == cmTarget::STATIC_LIBRARY) + { + // Static libraries never link to anything. + return false; + } + cmTarget::LinkLibraryVectorType const& libs = target.GetLinkLibraries(); + for(cmTarget::LinkLibraryVectorType::const_iterator i = libs.begin(); + i != libs.end(); ++i) + { + if(i->first == name) + { + return true; + } + } + return false; +} + +//---------------------------------------------------------------------------- +const char* +cmGlobalVisualStudioGenerator::GetUtilityForTarget(cmTarget& target, + const char* name) +{ + // Handle the external MS project special case. + if(strncmp(name, "INCLUDE_EXTERNAL_MSPROJECT", 26) == 0) + { + // Note from Ken: + // kind of weird removing the first 27 letters. my + // recommendatsions: use cmCustomCommand::GetCommand() to get the + // project name or get rid of the target name starting with + // "INCLUDE_EXTERNAL_MSPROJECT_" and use another indicator/flag + // somewhere. These external project names shouldn't conflict + // with cmake target names anyways. + return name+27; + } + + // Possibly depend on an intermediate utility target to avoid + // linking. + if(target.GetType() == cmTarget::STATIC_LIBRARY || + target.GetType() == cmTarget::SHARED_LIBRARY || + target.GetType() == cmTarget::MODULE_LIBRARY || + target.GetType() == cmTarget::EXECUTABLE) + { + // The depender is a target that links. Lookup the dependee to + // see if it provides an alternative dependency name. + if(cmTarget* depTarget = this->FindTarget(0, name)) + { + // Check for an alternative name created by FixUtilityDepends. + if(const char* altName = + depTarget->GetProperty("ALTERNATIVE_DEPENDENCY_NAME")) + { + // The alternative name is needed only if the depender does + // not really link to the dependee. + if(!this->CheckTargetLinks(target, name)) + { + return altName; + } + } + } + } + + // No special case. Just use the original dependency name. + return name; +} diff --git a/Source/cmGlobalVisualStudioGenerator.h b/Source/cmGlobalVisualStudioGenerator.h index 53bee5fba..5e08443f4 100644 --- a/Source/cmGlobalVisualStudioGenerator.h +++ b/Source/cmGlobalVisualStudioGenerator.h @@ -30,6 +30,15 @@ class cmGlobalVisualStudioGenerator : public cmGlobalGenerator public: cmGlobalVisualStudioGenerator(); virtual ~cmGlobalVisualStudioGenerator(); + +protected: + virtual void CreateGUID(const char*) {} + virtual void FixUtilityDepends(); + const char* GetUtilityForTarget(cmTarget& target, const char*); +private: + void FixUtilityDependsForTarget(cmTarget& target); + void CreateUtilityDependTarget(cmTarget& target); + bool CheckTargetLinks(cmTarget& target, const char* name); }; #endif diff --git a/Tests/Dependency/Two/CMakeLists.txt b/Tests/Dependency/Two/CMakeLists.txt index 6a9630e06..587c848f7 100644 --- a/Tests/Dependency/Two/CMakeLists.txt +++ b/Tests/Dependency/Two/CMakeLists.txt @@ -1,3 +1,20 @@ +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR}) ADD_LIBRARY( Two TwoSrc.c ) TARGET_LINK_LIBRARIES( Two Three ) +# Setup a target to cause failure if Two does not depend on it or if +# Two actually links to it. This will test that a utility dependency +# on a library target works properly. +ADD_CUSTOM_COMMAND( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/two-test.h + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${CMAKE_CURRENT_SOURCE_DIR}/two-test.h.in + ${CMAKE_CURRENT_BINARY_DIR}/two-test.h + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/two-test.h.in + ) +ADD_LIBRARY( TwoCustom TwoCustomSrc.c ${CMAKE_CURRENT_BINARY_DIR}/two-test.h) +SET_TARGET_PROPERTIES(TwoCustom PROPERTIES EXCLUDE_FROM_ALL 1) +TARGET_LINK_LIBRARIES(TwoCustom Three) + +# Add a utility dependency to make sure it works without linking. +ADD_DEPENDENCIES(Two TwoCustom) diff --git a/Tests/Dependency/Two/TwoCustomSrc.c b/Tests/Dependency/Two/TwoCustomSrc.c new file mode 100644 index 000000000..ac31dcf37 --- /dev/null +++ b/Tests/Dependency/Two/TwoCustomSrc.c @@ -0,0 +1,10 @@ +extern void NoFunction(); + +/* Provide a function that is supposed to be found in the Three + library. If Two links to TwoCustom then TwoCustom will come before + Three and this symbol will be used. Since NoFunction is not + defined, that will cause a link failure. */ +void ThreeFunction() +{ + NoFunction(); +} diff --git a/Tests/Dependency/Two/TwoSrc.c b/Tests/Dependency/Two/TwoSrc.c index 981df0975..0b3366b2b 100644 --- a/Tests/Dependency/Two/TwoSrc.c +++ b/Tests/Dependency/Two/TwoSrc.c @@ -1,4 +1,4 @@ -void ThreeFunction(); +#include void TwoFunction() { diff --git a/Tests/Dependency/Two/two-test.h.in b/Tests/Dependency/Two/two-test.h.in new file mode 100644 index 000000000..8c6a7f770 --- /dev/null +++ b/Tests/Dependency/Two/two-test.h.in @@ -0,0 +1 @@ +extern void ThreeFunction();