BUG: Fix utility dependencies for static libraries in VS generators. This addresses bug#4789.

This commit is contained in:
Brad King 2007-04-04 14:50:35 -04:00
parent 2803688998
commit 438a7e2fce
10 changed files with 220 additions and 30 deletions

View File

@ -177,7 +177,10 @@ void cmGlobalVisualStudio6Generator::Generate()
"echo", "Build all projects"); "echo", "Build all projects");
} }
} }
// Fix utility dependencies to avoid linking to libraries.
this->FixUtilityDepends();
// first do the superclass method // first do the superclass method
this->cmGlobalGenerator::Generate(); this->cmGlobalGenerator::Generate();
@ -438,12 +441,7 @@ void cmGlobalVisualStudio6Generator::WriteProject(std::ostream& fout,
{ {
if(*i != dspname) if(*i != dspname)
{ {
std::string depName = *i; std::string depName = this->GetUtilityForTarget(target, i->c_str());
if(strncmp(depName.c_str(), "INCLUDE_EXTERNAL_MSPROJECT", 26) == 0)
{
depName.erase(depName.begin(), depName.begin() + 27);
}
fout << "Begin Project Dependency\n"; fout << "Begin Project Dependency\n";
fout << "Project_Dep_Name " << depName << "\n"; fout << "Project_Dep_Name " << depName << "\n";
fout << "End Project Dependency\n"; fout << "End Project Dependency\n";

View File

@ -349,17 +349,7 @@ cmGlobalVisualStudio71Generator
{ {
if(*i != dspname) if(*i != dspname)
{ {
std::string name = i->c_str(); std::string name = this->GetUtilityForTarget(target, 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 guid = this->GetGUID(name.c_str()); std::string guid = this->GetGUID(name.c_str());
if(guid.size() == 0) if(guid.size() == 0)
{ {

View File

@ -235,6 +235,9 @@ void cmGlobalVisualStudio7Generator::Generate()
} }
} }
// Fix utility dependencies to avoid linking to libraries.
this->FixUtilityDepends();
// first do the superclass method // first do the superclass method
this->cmGlobalGenerator::Generate(); this->cmGlobalGenerator::Generate();
@ -634,17 +637,7 @@ cmGlobalVisualStudio7Generator
{ {
if(*i != dspname) if(*i != dspname)
{ {
std::string name = *i; std::string name = this->GetUtilityForTarget(target, 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 guid = this->GetGUID(name.c_str()); std::string guid = this->GetGUID(name.c_str());
if(guid.size() == 0) if(guid.size() == 0)
{ {

View File

@ -50,6 +50,10 @@ public:
virtual void Configure(); virtual void Configure();
virtual void Generate(); virtual void Generate();
protected: protected:
// Utility target fix is not needed for VS8.
virtual void FixUtilityDepends() {}
static cmVS7FlagTable const* GetExtraFlagTableVS8(); static cmVS7FlagTable const* GetExtraFlagTableVS8();
virtual void AddPlatformDefinitions(cmMakefile* mf); virtual void AddPlatformDefinitions(cmMakefile* mf);
virtual void WriteSLNFile(std::ostream& fout, cmLocalGenerator* root, virtual void WriteSLNFile(std::ostream& fout, cmLocalGenerator* root,

View File

@ -16,6 +16,10 @@
=========================================================================*/ =========================================================================*/
#include "cmGlobalVisualStudioGenerator.h" #include "cmGlobalVisualStudioGenerator.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmTarget.h"
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
cmGlobalVisualStudioGenerator::cmGlobalVisualStudioGenerator() cmGlobalVisualStudioGenerator::cmGlobalVisualStudioGenerator()
{ {
@ -25,3 +29,167 @@ cmGlobalVisualStudioGenerator::cmGlobalVisualStudioGenerator()
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<cmStdString>::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<std::string> 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;
}

View File

@ -30,6 +30,15 @@ class cmGlobalVisualStudioGenerator : public cmGlobalGenerator
public: public:
cmGlobalVisualStudioGenerator(); cmGlobalVisualStudioGenerator();
virtual ~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 #endif

View File

@ -1,3 +1,20 @@
INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
ADD_LIBRARY( Two TwoSrc.c ) ADD_LIBRARY( Two TwoSrc.c )
TARGET_LINK_LIBRARIES( Two Three ) 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)

View File

@ -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();
}

View File

@ -1,4 +1,4 @@
void ThreeFunction(); #include <two-test.h>
void TwoFunction() void TwoFunction()
{ {

View File

@ -0,0 +1 @@
extern void ThreeFunction();