Merge branch 'vs-target-dependencies' into imported-target-dependencies

This commit is contained in:
Brad King 2010-11-19 13:23:17 -05:00
commit bc7395c096
6 changed files with 221 additions and 86 deletions

View File

@ -144,7 +144,7 @@ bool cmComputeTargetDepends::Compute()
//----------------------------------------------------------------------------
void
cmComputeTargetDepends::GetTargetDirectDepends(cmTarget* t,
std::set<cmTarget*>& deps)
cmTargetDependSet& deps)
{
// Lookup the index for this target. All targets should be known by
// this point.
@ -156,7 +156,9 @@ cmComputeTargetDepends::GetTargetDirectDepends(cmTarget* t,
EdgeList const& nl = this->FinalGraph[i];
for(EdgeList::const_iterator ni = nl.begin(); ni != nl.end(); ++ni)
{
deps.insert(this->Targets[*ni]);
cmTarget* dep = this->Targets[*ni];
cmTargetDependSet::iterator di = deps.insert(dep).first;
di->SetType(ni->IsStrong());
}
}
@ -445,7 +447,7 @@ cmComputeTargetDepends
int j = *ei;
if(cmap[j] == c && ei->IsStrong())
{
this->FinalGraph[i].push_back(j);
this->FinalGraph[i].push_back(cmGraphEdge(j, true));
if(!this->IntraComponent(cmap, c, j, head, emitted, visited))
{
return false;
@ -456,7 +458,7 @@ cmComputeTargetDepends
// Prepend to a linear linked-list of intra-component edges.
if(*head >= 0)
{
this->FinalGraph[i].push_back(*head);
this->FinalGraph[i].push_back(cmGraphEdge(*head, false));
}
else
{
@ -515,7 +517,7 @@ cmComputeTargetDepends
int dependee_component = *ni;
int dependee_component_head = this->ComponentHead[dependee_component];
this->FinalGraph[depender_component_tail]
.push_back(dependee_component_head);
.push_back(cmGraphEdge(dependee_component_head, ni->IsStrong()));
}
}
return true;

View File

@ -21,6 +21,7 @@
class cmComputeComponentGraph;
class cmGlobalGenerator;
class cmTarget;
class cmTargetDependSet;
/** \class cmComputeTargetDepends
* \brief Compute global interdependencies among targets.
@ -38,7 +39,7 @@ public:
bool Compute();
std::vector<cmTarget*> const& GetTargets() const { return this->Targets; }
void GetTargetDirectDepends(cmTarget* t, std::set<cmTarget*>& deps);
void GetTargetDirectDepends(cmTarget* t, cmTargetDependSet& deps);
private:
void CollectTargets();
void CollectDepends();

View File

@ -16,6 +16,7 @@
#include "cmStandardIncludes.h"
#include "cmTarget.h" // For cmTargets
#include "cmTargetDepend.h" // For cmTargetDependSet
class cmake;
class cmMakefile;
@ -233,7 +234,7 @@ public:
virtual const char* GetCleanTargetName() { return 0; }
// Class to track a set of dependencies.
class TargetDependSet: public std::set<cmTarget*> {};
typedef cmTargetDependSet TargetDependSet;
// what targets does the specified target depend on directly
// via a target_link_libraries or add_dependencies

View File

@ -236,6 +236,59 @@ std::string cmGlobalVisualStudioGenerator::GetUserMacrosRegKeyBase()
return "";
}
//----------------------------------------------------------------------------
void cmGlobalVisualStudioGenerator::FillLinkClosure(cmTarget* target,
TargetSet& linked)
{
if(linked.insert(target).second)
{
TargetDependSet const& depends = this->GetTargetDirectDepends(*target);
for(TargetDependSet::const_iterator di = depends.begin();
di != depends.end(); ++di)
{
if(di->IsLink())
{
this->FillLinkClosure(*di, linked);
}
}
}
}
//----------------------------------------------------------------------------
cmGlobalVisualStudioGenerator::TargetSet const&
cmGlobalVisualStudioGenerator::GetTargetLinkClosure(cmTarget* target)
{
TargetSetMap::iterator i = this->TargetLinkClosure.find(target);
if(i == this->TargetLinkClosure.end())
{
TargetSetMap::value_type entry(target, TargetSet());
i = this->TargetLinkClosure.insert(entry).first;
this->FillLinkClosure(target, i->second);
}
return i->second;
}
//----------------------------------------------------------------------------
void cmGlobalVisualStudioGenerator::FollowLinkDepends(
cmTarget* target, std::set<cmTarget*>& linked)
{
if(linked.insert(target).second &&
target->GetType() == cmTarget::STATIC_LIBRARY)
{
// Static library targets do not list their link dependencies so
// we must follow them transitively now.
TargetDependSet const& depends = this->GetTargetDirectDepends(*target);
for(TargetDependSet::const_iterator di = depends.begin();
di != depends.end(); ++di)
{
if(di->IsLink())
{
this->FollowLinkDepends(*di, linked);
}
}
}
}
//----------------------------------------------------------------------------
bool cmGlobalVisualStudioGenerator::ComputeTargetDepends()
{
@ -269,51 +322,94 @@ void cmGlobalVisualStudioGenerator::ComputeVSTargetDepends(cmTarget& target)
return;
}
VSDependSet& vsTargetDepend = this->VSTargetDepends[&target];
// VS <= 7.1 has two behaviors that affect solution dependencies.
//
// (1) Solution-level dependencies between a linkable target and a
// library cause that library to be linked. We use an intermedite
// empty utility target to express the dependency. (VS 8 and above
// provide a project file "LinkLibraryDependencies" setting to
// choose whether to activate this behavior. We disable it except
// when linking external project files.)
//
// (2) We cannot let static libraries depend directly on targets to
// which they "link" because the librarian tool will copy the
// targets into the static library. While the work-around for
// behavior (1) would also avoid this, it would create a large
// number of extra utility targets for little gain. Instead, use
// the above work-around only for dependencies explicitly added by
// the add_dependencies() command. Approximate link dependencies by
// leaving them out for the static library itself but following them
// transitively for other targets.
bool allowLinkable = (target.GetType() != cmTarget::STATIC_LIBRARY &&
target.GetType() != cmTarget::SHARED_LIBRARY &&
target.GetType() != cmTarget::MODULE_LIBRARY &&
target.GetType() != cmTarget::EXECUTABLE);
TargetDependSet const& depends = this->GetTargetDirectDepends(target);
// Collect implicit link dependencies (target_link_libraries).
// Static libraries cannot depend on their link implementation
// due to behavior (2), but they do not really need to.
std::set<cmTarget*> linkDepends;
if(target.GetType() != cmTarget::STATIC_LIBRARY)
{
cmTarget::LinkLibraryVectorType const& libs = target.GetLinkLibraries();
for(cmTarget::LinkLibraryVectorType::const_iterator j = libs.begin();
j != libs.end(); ++j)
for(TargetDependSet::const_iterator di = depends.begin();
di != depends.end(); ++di)
{
if(j->first != target.GetName() &&
this->FindTarget(0, j->first.c_str()))
cmTargetDepend dep = *di;
if(dep.IsLink())
{
vsTargetDepend.insert(j->first);
this->FollowLinkDepends(dep, linkDepends);
}
}
}
std::set<cmStdString> const& utils = target.GetUtilities();
for(std::set<cmStdString>::const_iterator i = utils.begin();
i != utils.end(); ++i)
{
if(*i != target.GetName())
{
std::string name = this->GetUtilityForTarget(target, i->c_str());
vsTargetDepend.insert(name);
}
}
}
//----------------------------------------------------------------------------
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)
// Collext explicit util dependencies (add_dependencies).
std::set<cmTarget*> utilDepends;
for(TargetDependSet::const_iterator di = depends.begin();
di != depends.end(); ++di)
{
// 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)
cmTargetDepend dep = *di;
if(dep.IsUtil())
{
return true;
this->FollowLinkDepends(dep, utilDepends);
}
}
// Collect all targets linked by this target so we can avoid
// intermediate targets below.
TargetSet linked;
if(target.GetType() != cmTarget::STATIC_LIBRARY)
{
linked = this->GetTargetLinkClosure(&target);
}
// Emit link dependencies.
for(std::set<cmTarget*>::iterator di = linkDepends.begin();
di != linkDepends.end(); ++di)
{
cmTarget* dep = *di;
vsTargetDepend.insert(dep->GetName());
}
// Emit util dependencies. Possibly use intermediate targets.
for(std::set<cmTarget*>::iterator di = utilDepends.begin();
di != utilDepends.end(); ++di)
{
cmTarget* dep = *di;
if(allowLinkable || !dep->IsLinkable() || linked.count(dep))
{
// Direct dependency allowed.
vsTargetDepend.insert(dep->GetName());
}
else
{
// Direct dependency on linkable target not allowed.
// Use an intermediate utility target.
vsTargetDepend.insert(this->GetUtilityDepend(dep));
}
}
return false;
}
//----------------------------------------------------------------------------
@ -329,45 +425,6 @@ std::string cmGlobalVisualStudioGenerator::GetUtilityDepend(cmTarget* target)
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 ||
target.GetType() == cmTarget::SHARED_LIBRARY ||
target.GetType() == cmTarget::MODULE_LIBRARY ||
target.GetType() == cmTarget::EXECUTABLE)
{
// The depender is a target that links.
if(cmTarget* depTarget = this->FindTarget(0, name))
{
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, name))
{
return this->GetUtilityDepend(depTarget);
}
}
}
}
// No special case. Just use the original dependency name.
return name;
}
//----------------------------------------------------------------------------
#include <windows.h>
@ -706,11 +763,22 @@ cmGlobalVisualStudioGenerator::TargetCompare
//----------------------------------------------------------------------------
cmGlobalVisualStudioGenerator::OrderedTargetDependSet
::OrderedTargetDependSet(cmGlobalGenerator::TargetDependSet const& targets)
::OrderedTargetDependSet(TargetDependSet const& targets)
{
for(cmGlobalGenerator::TargetDependSet::const_iterator ti =
for(TargetDependSet::const_iterator ti =
targets.begin(); ti != targets.end(); ++ti)
{
this->insert(*ti);
}
}
//----------------------------------------------------------------------------
cmGlobalVisualStudioGenerator::OrderedTargetDependSet
::OrderedTargetDependSet(TargetSet const& targets)
{
for(TargetSet::const_iterator ti = targets.begin();
ti != targets.end(); ++ti)
{
this->insert(*ti);
}
}

View File

@ -69,15 +69,12 @@ public:
i.e. "Can I build Debug and Release in the same tree?" */
virtual bool IsMultiConfig() { return true; }
class TargetSet: public std::set<cmTarget*> {};
struct TargetCompare
{
bool operator()(cmTarget const* l, cmTarget const* r) const;
};
class OrderedTargetDependSet: public std::multiset<cmTarget*, TargetCompare>
{
public:
OrderedTargetDependSet(cmGlobalGenerator::TargetDependSet const&);
};
class OrderedTargetDependSet;
protected:
// Does this VS version link targets to each other if there are
@ -99,6 +96,24 @@ protected:
std::string GetUtilityDepend(cmTarget* target);
typedef std::map<cmTarget*, cmStdString> UtilityDependsMap;
UtilityDependsMap UtilityDepends;
private:
void FollowLinkDepends(cmTarget* target, std::set<cmTarget*>& linked);
class TargetSetMap: public std::map<cmTarget*, TargetSet> {};
TargetSetMap TargetLinkClosure;
void FillLinkClosure(cmTarget* target, TargetSet& linked);
TargetSet const& GetTargetLinkClosure(cmTarget* target);
};
class cmGlobalVisualStudioGenerator::OrderedTargetDependSet:
public std::multiset<cmTargetDepend,
cmGlobalVisualStudioGenerator::TargetCompare>
{
public:
typedef cmGlobalGenerator::TargetDependSet TargetDependSet;
typedef cmGlobalVisualStudioGenerator::TargetSet TargetSet;
OrderedTargetDependSet(TargetDependSet const&);
OrderedTargetDependSet(TargetSet const&);
};
#endif

48
Source/cmTargetDepend.h Normal file
View File

@ -0,0 +1,48 @@
/*============================================================================
CMake - Cross Platform Makefile Generator
Copyright 2000-2010 Kitware, Inc., Insight Software Consortium
Distributed under the OSI-approved BSD License (the "License");
see accompanying file Copyright.txt for details.
This software is distributed WITHOUT ANY WARRANTY; without even the
implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the License for more information.
============================================================================*/
#ifndef cmTargetDepend_h
#define cmTargetDepend_h
#include "cmStandardIncludes.h"
class cmTarget;
/** One edge in the global target dependency graph.
It may be marked as a 'link' or 'util' edge or both. */
class cmTargetDepend
{
cmTarget* Target;
// The set order depends only on the Target, so we use
// mutable members to acheive a map with set syntax.
mutable bool Link;
mutable bool Util;
public:
cmTargetDepend(cmTarget* t): Target(t), Link(false), Util(false) {}
operator cmTarget*() const { return this->Target; }
cmTarget* operator->() const { return this->Target; }
cmTarget& operator*() const { return *this->Target; }
friend bool operator < (cmTargetDepend const& l, cmTargetDepend const& r)
{ return l.Target < r.Target; }
void SetType(bool strong) const
{
if(strong) { this->Util = true; }
else { this->Link = true; }
}
bool IsLink() const { return this->Link; }
bool IsUtil() const { return this->Util; }
};
/** Unordered set of (direct) dependencies of a target. */
class cmTargetDependSet: public std::set<cmTargetDepend> {};
#endif