Refactor VS <= 7.1 utility-depends workaround

Commit 438a7e2f (Fix utility dependencies for static libraries in VS
generators, 2007-04-04) implemented utility-only dependencies between
linkable targets by introducing an intermediate non-linkable target.
We convert a dependency of the form

  foo -> bar

to the form

  foo -> bar_UTILITY -> bar

to prevent foo from including bar on its link line.  Previously we added
the extra "_UTILITY" targets explicitly among the project targets before
dependency analysis was performed.  Now we generate them separately at
the last moment so that cmGlobalGenerator need not be aware of them.
This commit is contained in:
Brad King 2010-08-24 18:47:56 -04:00
parent 325bdb2a92
commit 79a88c35f8
7 changed files with 186 additions and 175 deletions

View File

@ -13,6 +13,7 @@
#include "cmLocalVisualStudio6Generator.h"
#include "cmMakefile.h"
#include "cmake.h"
#include "cmGeneratedFileStream.h"
// Utility function to make a valid VS6 *.dsp filename out
// of a CMake target name:
@ -284,6 +285,23 @@ void cmGlobalVisualStudio6Generator::WriteProject(std::ostream& fout,
fout << "End Project Dependency\n";
}
fout << "}}}\n\n";
UtilityDependsMap::iterator ui = this->UtilityDepends.find(&target);
if(ui != this->UtilityDepends.end())
{
const char* uname = ui->second.c_str();
fout << "Project: \"" << uname << "\"="
<< dir << "\\" << uname << ".dsp - Package Owner=<4>\n\n";
fout <<
"Package=<5>\n{{{\n}}}\n\n"
"Package=<4>\n"
"{{{\n"
"Begin Project Dependency\n"
"Project_Dep_Name " << dspname << "\n"
"End Project Dependency\n"
"}}}\n\n";
;
}
}
@ -339,6 +357,49 @@ void cmGlobalVisualStudio6Generator::WriteDSWHeader(std::ostream& fout)
fout << "# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!\n\n";
}
//----------------------------------------------------------------------------
std::string
cmGlobalVisualStudio6Generator::WriteUtilityDepend(cmTarget* target)
{
std::string pname = target->GetName();
pname += "_UTILITY";
pname = GetVS6TargetName(pname.c_str());
std::string fname = target->GetMakefile()->GetStartOutputDirectory();
fname += "/";
fname += pname;
fname += ".dsp";
cmGeneratedFileStream fout(fname.c_str());
fout.SetCopyIfDifferent(true);
fout <<
"# Microsoft Developer Studio Project File - Name=\""
<< pname << "\" - Package Owner=<4>\n"
"# Microsoft Developer Studio Generated Build File, Format Version 6.00\n"
"# ** DO NOT EDIT **\n"
"\n"
"# TARGTYPE \"Win32 (x86) Generic Project\" 0x010a\n"
"\n"
"CFG=" << pname << " - Win32 Debug\n"
"!MESSAGE \"" << pname << " - Win32 Debug\""
" (based on \"Win32 (x86) Generic Project\")\n"
"!MESSAGE \"" << pname << " - Win32 Release\" "
"(based on \"Win32 (x86) Generic Project\")\n"
"!MESSAGE \"" << pname << " - Win32 MinSizeRel\" "
"(based on \"Win32 (x86) Generic Project\")\n"
"!MESSAGE \"" << pname << " - Win32 RelWithDebInfo\" "
"(based on \"Win32 (x86) Generic Project\")\n"
"\n"
"# Begin Project\n"
"# Begin Target\n"
"# Name \"" << pname << " - Win32 Debug\"\n"
"# Name \"" << pname << " - Win32 Release\"\n"
"# Name \"" << pname << " - Win32 MinSizeRel\"\n"
"# Name \"" << pname << " - Win32 RelWithDebInfo\"\n"
"# End Target\n"
"# End Project\n"
;
return pname;
}
//----------------------------------------------------------------------------
void cmGlobalVisualStudio6Generator
::GetDocumentation(cmDocumentationEntry& entry) const

View File

@ -96,6 +96,7 @@ private:
const char* name, const char* path,
const std::set<cmStdString>& dependencies);
void WriteDSWFooter(std::ostream& fout);
virtual std::string WriteUtilityDepend(cmTarget* target);
};
#endif

View File

@ -163,16 +163,31 @@ cmGlobalVisualStudio71Generator::WriteProject(std::ostream& fout,
ext = targetExt;
}
std::string guid = this->GetGUID(dspname);
fout << project
<< dspname << "\", \""
<< this->ConvertToSolutionPath(dir)
<< "\\" << dspname << ext << "\", \"{"
<< this->GetGUID(dspname) << "}\"\n";
<< "\\" << dspname << ext << "\", \"{" << guid << "}\"\n";
fout << "\tProjectSection(ProjectDependencies) = postProject\n";
this->WriteProjectDepends(fout, dspname, dir, t);
fout << "\tEndProjectSection\n";
fout <<"EndProject\n";
UtilityDependsMap::iterator ui = this->UtilityDepends.find(&t);
if(ui != this->UtilityDepends.end())
{
const char* uname = ui->second.c_str();
fout << "Project(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \""
<< uname << "\", \""
<< this->ConvertToSolutionPath(dir)
<< "\\" << uname << ".vcproj" << "\", \"{"
<< this->GetGUID(uname) << "}\"\n"
<< "\tProjectSection(ProjectDependencies) = postProject\n"
<< "\t\t{" << guid << "} = {" << guid << "}\n"
<< "\tEndProjectSection\n"
<< "EndProject\n";
}
}
//----------------------------------------------------------------------------

View File

@ -408,6 +408,18 @@ void cmGlobalVisualStudio7Generator::WriteProject(std::ostream& fout,
<< this->ConvertToSolutionPath(dir)
<< "\\" << dspname << ext << "\", \"{"
<< this->GetGUID(dspname) << "}\"\nEndProject\n";
UtilityDependsMap::iterator ui = this->UtilityDepends.find(&target);
if(ui != this->UtilityDepends.end())
{
const char* uname = ui->second.c_str();
fout << "Project(\"{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}\") = \""
<< uname << "\", \""
<< this->ConvertToSolutionPath(dir)
<< "\\" << uname << ".vcproj" << "\", \"{"
<< this->GetGUID(uname) << "}\"\n"
<< "EndProject\n";
}
}
@ -440,6 +452,13 @@ cmGlobalVisualStudio7Generator
fout << "\t\t{" << dspguid << "}." << depcount << " = {" << guid << "}\n";
depcount++;
}
UtilityDependsMap::iterator ui = this->UtilityDepends.find(&target);
if(ui != this->UtilityDepends.end())
{
const char* uname = ui->second.c_str();
fout << "\t\t{" << this->GetGUID(uname) << "}.0 = {" << dspguid << "}\n";
}
}
@ -501,6 +520,61 @@ void cmGlobalVisualStudio7Generator::WriteSLNHeader(std::ostream& fout)
fout << "Microsoft Visual Studio Solution File, Format Version 7.00\n";
}
//----------------------------------------------------------------------------
std::string
cmGlobalVisualStudio7Generator::WriteUtilityDepend(cmTarget* target)
{
std::string pname = target->GetName();
pname += "_UTILITY";
std::string fname = target->GetMakefile()->GetStartOutputDirectory();
fname += "/";
fname += pname;
fname += ".vcproj";
cmGeneratedFileStream fout(fname.c_str());
fout.SetCopyIfDifferent(true);
this->CreateGUID(pname.c_str());
std::string guid = this->GetGUID(pname.c_str());
fout <<
"<?xml version=\"1.0\" encoding = \"Windows-1252\"?>\n"
"<VisualStudioProject\n"
"\tProjectType=\"Visual C++\"\n"
"\tVersion=\"" << this->GetIDEVersion() << "0\"\n"
"\tName=\"" << pname << "\"\n"
"\tProjectGUID=\"{" << guid << "}\"\n"
"\tKeyword=\"Win32Proj\">\n"
"\t<Platforms><Platform Name=\"Win32\"/></Platforms>\n"
"\t<Configurations>\n"
;
for(std::vector<std::string>::iterator i = this->Configurations.begin();
i != this->Configurations.end(); ++i)
{
fout <<
"\t\t<Configuration\n"
"\t\t\tName=\"" << *i << "|Win32\"\n"
"\t\t\tOutputDirectory=\"" << *i << "\"\n"
"\t\t\tIntermediateDirectory=\"" << pname << ".dir\\" << *i << "\"\n"
"\t\t\tConfigurationType=\"10\"\n"
"\t\t\tUseOfMFC=\"0\"\n"
"\t\t\tATLMinimizesCRunTimeLibraryUsage=\"FALSE\"\n"
"\t\t\tCharacterSet=\"2\">\n"
"\t\t</Configuration>\n"
;
}
fout <<
"\t</Configurations>\n"
"\t<Files></Files>\n"
"\t<Globals></Globals>\n"
"</VisualStudioProject>\n"
;
if(fout.Close())
{
this->FileReplacedDuringGenerate(fname);
}
return pname;
}
std::string cmGlobalVisualStudio7Generator::GetGUID(const char* name)
{
std::string guidStoreName = name;

View File

@ -113,6 +113,7 @@ protected:
bool partOfDefaultBuild);
virtual void WriteSLNFooter(std::ostream& fout);
virtual void WriteSLNHeader(std::ostream& fout);
virtual std::string WriteUtilityDepend(cmTarget* target);
virtual void AddPlatformDefinitions(cmMakefile* mf);
virtual void WriteTargetsToSolution(

View File

@ -74,9 +74,6 @@ void cmGlobalVisualStudioGenerator::Generate()
}
}
// Fix utility dependencies to avoid linking to libraries.
this->FixUtilityDepends();
// Configure CMake Visual Studio macros, for this user on this version
// of Visual Studio.
this->ConfigureCMakeVisualStudioMacros();
@ -224,134 +221,6 @@ std::string cmGlobalVisualStudioGenerator::GetUserMacrosRegKeyBase()
return "";
}
//----------------------------------------------------------------------------
void cmGlobalVisualStudioGenerator::FixUtilityDepends()
{
// Skip for VS versions 8 and above.
if(!this->VSLinksDependencies())
{
return;
}
// 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;
}
#if 0
// This feature makes a mess in SLN files for VS 7.1 and below. It
// creates an extra target for every target that is "linked" by a
// static library. Without this feature static libraries do not
// wait until their "link" dependencies are built to build. This is
// not a problem 99.9% of the time, and projects that do have the
// problem can enable this work-around by using add_dependencies.
// Static libraries cannot depend directly on the targets to which
// they link because VS will copy those targets into the library
// (for VS < 8). To work around the problem we copy the
// dependencies to be utility dependencies so that the work-around
// below is used.
if(target.GetType() == cmTarget::STATIC_LIBRARY)
{
cmTarget::LinkLibraryVectorType const& libs = target.GetLinkLibraries();
for(cmTarget::LinkLibraryVectorType::const_iterator i = libs.begin();
i != libs.end(); ++i)
{
if(cmTarget* depTarget = this->FindTarget(0, i->first.c_str(), false))
{
target.AddUtility(depTarget->GetName());
}
}
}
#endif
// 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::ComputeTargetDepends()
{
@ -433,10 +302,28 @@ bool cmGlobalVisualStudioGenerator::CheckTargetLinks(cmTarget& target,
}
//----------------------------------------------------------------------------
const char*
std::string cmGlobalVisualStudioGenerator::GetUtilityDepend(cmTarget* target)
{
UtilityDependsMap::iterator i = this->UtilityDepends.find(target);
if(i == this->UtilityDepends.end())
{
std::string name = this->WriteUtilityDepend(target);
UtilityDependsMap::value_type entry(target, name);
i = this->UtilityDepends.insert(entry).first;
}
return i->second;
}
//----------------------------------------------------------------------------
std::string
cmGlobalVisualStudioGenerator::GetUtilityForTarget(cmTarget& target,
const char* name)
{
if(!this->VSLinksDependencies())
{
return name;
}
// Possibly depend on an intermediate utility target to avoid
// linking.
if(target.GetType() == cmTarget::STATIC_LIBRARY ||
@ -444,19 +331,19 @@ cmGlobalVisualStudioGenerator::GetUtilityForTarget(cmTarget& target,
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.
// The depender is a target that links.
if(cmTarget* depTarget = this->FindTarget(0, name))
{
// Check for an alternative name created by FixUtilityDepends.
if(const char* altName =
depTarget->GetProperty("ALTERNATIVE_DEPENDENCY_NAME"))
if(depTarget->GetType() == cmTarget::STATIC_LIBRARY ||
depTarget->GetType() == cmTarget::SHARED_LIBRARY ||
depTarget->GetType() == cmTarget::MODULE_LIBRARY)
{
// The alternative name is needed only if the depender does
// not really link to the dependee.
// 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, name))
{
return altName;
return this->GetUtilityDepend(depTarget);
}
}
}
@ -466,30 +353,6 @@ cmGlobalVisualStudioGenerator::GetUtilityForTarget(cmTarget& target,
return name;
}
//----------------------------------------------------------------------------
void cmGlobalVisualStudioGenerator::GetTargetSets(
TargetDependSet& projectTargets, TargetDependSet& originalTargets,
cmLocalGenerator* root, GeneratorVector const& generators
)
{
this->cmGlobalGenerator::GetTargetSets(projectTargets, originalTargets,
root, generators);
// Add alternative dependency targets created by FixUtilityDepends.
for(TargetDependSet::iterator ti = projectTargets.begin();
ti != projectTargets.end(); ++ti)
{
cmTarget* tgt = *ti;
if(const char* altName = tgt->GetProperty("ALTERNATIVE_DEPENDENCY_NAME"))
{
if(cmTarget* alt = tgt->GetMakefile()->FindTarget(altName))
{
projectTargets.insert(alt);
}
}
}
}
//----------------------------------------------------------------------------
#include <windows.h>

View File

@ -61,7 +61,6 @@ public:
// return true if target is fortran only
bool TargetIsFortranOnly(cmTarget& t);
const char* GetUtilityForTarget(cmTarget& target, const char*);
/** Get the top-level registry key for this VS version. */
std::string GetRegistryBase();
@ -71,8 +70,6 @@ public:
virtual bool IsMultiConfig() { return true; }
protected:
void FixUtilityDepends();
// Does this VS version link targets to each other if there are
// dependencies in the SLN file? This was done for VS versions
// below 8.
@ -90,9 +87,6 @@ protected:
OrderedTargetDependSet(cmGlobalGenerator::TargetDependSet const&);
};
virtual void GetTargetSets(TargetDependSet& projectTargets,
TargetDependSet& originalTargets,
cmLocalGenerator* root, GeneratorVector const&);
virtual bool ComputeTargetDepends();
class VSDependSet: public std::set<cmStdString> {};
class VSDependMap: public std::map<cmTarget*, VSDependSet> {};
@ -100,9 +94,11 @@ protected:
void ComputeVSTargetDepends(cmTarget&);
bool CheckTargetLinks(cmTarget& target, const char* name);
private:
void FixUtilityDependsForTarget(cmTarget& target);
void CreateUtilityDependTarget(cmTarget& target);
std::string GetUtilityForTarget(cmTarget& target, const char*);
virtual std::string WriteUtilityDepend(cmTarget*) = 0;
std::string GetUtilityDepend(cmTarget* target);
typedef std::map<cmTarget*, cmStdString> UtilityDependsMap;
UtilityDependsMap UtilityDepends;
};
#endif