ENH: Support linking to shared libs with dependent libs

- Split IMPORTED_LINK_LIBRARIES into two parts:
      IMPORTED_LINK_INTERFACE_LIBRARIES
      IMPORTED_LINK_DEPENDENT_LIBRARIES
  - Add CMAKE_DEPENDENT_SHARED_LIBRARY_MODE to select behavior
  - Set mode to LINK for Darwin (fixes universal binary problem)
  - Update ExportImport test to account for changes
This commit is contained in:
Brad King 2008-01-31 15:45:31 -05:00
parent 52e75800b4
commit 2cff26fa52
11 changed files with 345 additions and 97 deletions

View File

@ -103,6 +103,12 @@ IF(XCODE)
SET(CMAKE_INCLUDE_SYSTEM_FLAG_CXX) SET(CMAKE_INCLUDE_SYSTEM_FLAG_CXX)
ENDIF(XCODE) ENDIF(XCODE)
# Need to list dependent shared libraries on link line. When building
# with -isysroot (for universal binaries), the linker always looks for
# dependent libraries under the sysroot. Listing them on the link
# line works around the problem.
SET(CMAKE_DEPENDENT_SHARED_LIBRARY_MODE "LINK")
SET(CMAKE_MacOSX_Content_COMPILE_OBJECT "\"${CMAKE_COMMAND}\" -E copy_if_different <SOURCE> <OBJECT>") SET(CMAKE_MacOSX_Content_COMPILE_OBJECT "\"${CMAKE_COMMAND}\" -E copy_if_different <SOURCE> <OBJECT>")
SET(CMAKE_C_CREATE_SHARED_LIBRARY_FORBIDDEN_FLAGS -w) SET(CMAKE_C_CREATE_SHARED_LIBRARY_FORBIDDEN_FLAGS -w)

View File

@ -171,6 +171,14 @@ cmComputeLinkDepends::Compute()
this->FollowLinkEntry(qe); this->FollowLinkEntry(qe);
} }
// Complete the search of shared library dependencies.
while(!this->SharedDepQueue.empty())
{
// Handle the next entry.
this->HandleSharedDependency(this->SharedDepQueue.front());
this->SharedDepQueue.pop();
}
// Infer dependencies of targets for which they were not known. // Infer dependencies of targets for which they were not known.
this->InferDependencies(); this->InferDependencies();
@ -197,6 +205,20 @@ cmComputeLinkDepends::Compute()
return this->FinalLinkEntries; return this->FinalLinkEntries;
} }
//----------------------------------------------------------------------------
std::map<cmStdString, int>::iterator
cmComputeLinkDepends::AllocateLinkEntry(std::string const& item)
{
std::map<cmStdString, int>::value_type
index_entry(item, static_cast<int>(this->EntryList.size()));
std::map<cmStdString, int>::iterator
lei = this->LinkEntryIndex.insert(index_entry).first;
this->EntryList.push_back(LinkEntry());
this->InferredDependSets.push_back(0);
this->EntryConstraintGraph.push_back(EntryConstraintSet());
return lei;
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
int cmComputeLinkDepends::AddLinkEntry(std::string const& item) int cmComputeLinkDepends::AddLinkEntry(std::string const& item)
{ {
@ -209,14 +231,7 @@ int cmComputeLinkDepends::AddLinkEntry(std::string const& item)
} }
// Allocate a spot for the item entry. // Allocate a spot for the item entry.
{ lei = this->AllocateLinkEntry(item);
std::map<cmStdString, int>::value_type
index_entry(item, static_cast<int>(this->EntryList.size()));
lei = this->LinkEntryIndex.insert(index_entry).first;
this->EntryList.push_back(LinkEntry());
this->InferredDependSets.push_back(0);
this->EntryConstraintGraph.push_back(EntryConstraintSet());
}
// Initialize the item entry. // Initialize the item entry.
int index = lei->second; int index = lei->second;
@ -263,18 +278,17 @@ void cmComputeLinkDepends::FollowLinkEntry(BFSEntry const& qe)
if(entry.Target) if(entry.Target)
{ {
// Follow the target dependencies. // Follow the target dependencies.
if(entry.Target->IsImported()) if(cmTargetLinkInterface const* interface =
{
// Imported targets provide their own link information.
this->AddImportedLinkEntries(depender_index, entry.Target);
}
else if(cmTargetLinkInterface const* interface =
entry.Target->GetLinkInterface(this->Config)) entry.Target->GetLinkInterface(this->Config))
{ {
// This target provides its own link interface information. // This target provides its own link interface information.
this->AddLinkEntries(depender_index, *interface); this->AddLinkEntries(depender_index, interface->Libraries);
// Handle dependent shared libraries.
this->QueueSharedDependencies(depender_index, interface->SharedDeps);
} }
else if(entry.Target->GetType() != cmTarget::EXECUTABLE) else if(!entry.Target->IsImported() &&
entry.Target->GetType() != cmTarget::EXECUTABLE)
{ {
// Use the target's link implementation as the interface. // Use the target's link implementation as the interface.
this->AddTargetLinkEntries(depender_index, this->AddTargetLinkEntries(depender_index,
@ -289,13 +303,60 @@ void cmComputeLinkDepends::FollowLinkEntry(BFSEntry const& qe)
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void cmComputeLinkDepends::AddImportedLinkEntries(int depender_index, void
cmTarget* target) cmComputeLinkDepends
::QueueSharedDependencies(int depender_index,
std::vector<std::string> const& deps)
{ {
if(std::vector<std::string> const* libs = for(std::vector<std::string>::const_iterator li = deps.begin();
target->GetImportedLinkLibraries(this->Config)) li != deps.end(); ++li)
{ {
this->AddLinkEntries(depender_index, *libs); SharedDepEntry qe;
qe.Item = *li;
qe.DependerIndex = depender_index;
this->SharedDepQueue.push(qe);
}
}
//----------------------------------------------------------------------------
void cmComputeLinkDepends::HandleSharedDependency(SharedDepEntry const& dep)
{
// Check if the target already has an entry.
std::map<cmStdString, int>::iterator lei =
this->LinkEntryIndex.find(dep.Item);
if(lei == this->LinkEntryIndex.end())
{
// Allocate a spot for the item entry.
lei = this->AllocateLinkEntry(dep.Item);
// Initialize the item entry.
LinkEntry& entry = this->EntryList[lei->second];
entry.Item = dep.Item;
entry.Target = this->Makefile->FindTargetToUse(dep.Item.c_str());
// This item was added specifically because it is a dependent
// shared library. It may get special treatment
// in cmComputeLinkInformation.
entry.IsSharedDep = true;
}
// Get the link entry for this target.
int index = lei->second;
LinkEntry& entry = this->EntryList[index];
// This shared library dependency must be preceded by the item that
// listed it.
this->EntryConstraintGraph[index].insert(dep.DependerIndex);
// Target items may have their own dependencies.
if(entry.Target)
{
if(cmTargetLinkInterface const* interface =
entry.Target->GetLinkInterface(this->Config))
{
// We use just the shared dependencies, not the interface.
this->QueueSharedDependencies(index, interface->SharedDeps);
}
} }
} }

View File

@ -41,8 +41,10 @@ public:
{ {
std::string Item; std::string Item;
cmTarget* Target; cmTarget* Target;
LinkEntry(): Item(), Target(0) {} bool IsSharedDep;
LinkEntry(LinkEntry const& r): Item(r.Item), Target(r.Target) {} LinkEntry(): Item(), Target(0), IsSharedDep(false) {}
LinkEntry(LinkEntry const& r):
Item(r.Item), Target(r.Target), IsSharedDep(r.IsSharedDep) {}
}; };
typedef std::vector<LinkEntry> EntryVector; typedef std::vector<LinkEntry> EntryVector;
@ -65,8 +67,9 @@ private:
typedef cmTarget::LinkLibraryVectorType LinkLibraryVectorType; typedef cmTarget::LinkLibraryVectorType LinkLibraryVectorType;
std::map<cmStdString, int>::iterator
AllocateLinkEntry(std::string const& item);
int AddLinkEntry(std::string const& item); int AddLinkEntry(std::string const& item);
void AddImportedLinkEntries(int depender_index, cmTarget* target);
void AddVarLinkEntries(int depender_index, const char* value); void AddVarLinkEntries(int depender_index, const char* value);
void AddTargetLinkEntries(int depender_index, void AddTargetLinkEntries(int depender_index,
LinkLibraryVectorType const& libs); LinkLibraryVectorType const& libs);
@ -86,6 +89,19 @@ private:
std::queue<BFSEntry> BFSQueue; std::queue<BFSEntry> BFSQueue;
void FollowLinkEntry(BFSEntry const&); void FollowLinkEntry(BFSEntry const&);
// Shared libraries that are included only because they are
// dependencies of other shared libraries, not because they are part
// of the interface.
struct SharedDepEntry
{
std::string Item;
int DependerIndex;
};
std::queue<SharedDepEntry> SharedDepQueue;
void QueueSharedDependencies(int depender_index,
std::vector<std::string> const& deps);
void HandleSharedDependency(SharedDepEntry const& dep);
// Dependency inferral for each link item. // Dependency inferral for each link item.
struct DependSet: public std::set<int> {}; struct DependSet: public std::set<int> {};
struct DependSetList: public std::vector<DependSet> {}; struct DependSetList: public std::vector<DependSet> {};

View File

@ -234,6 +234,21 @@ cmComputeLinkInformation
// Setup framework support. // Setup framework support.
this->ComputeFrameworkInfo(); this->ComputeFrameworkInfo();
// Choose a mode for dealing with shared library dependencies.
this->SharedDependencyMode = SharedDepModeNone;
if(const char* mode =
this->Makefile->GetDefinition("CMAKE_DEPENDENT_SHARED_LIBRARY_MODE"))
{
if(strcmp(mode, "LINK") == 0)
{
this->SharedDependencyMode = SharedDepModeLink;
}
else if(strcmp(mode, "DIR") == 0)
{
this->SharedDependencyMode = SharedDepModeDir;
}
}
// Get the implicit link directories for this platform. // Get the implicit link directories for this platform.
if(const char* implicitLinks = if(const char* implicitLinks =
(this->Makefile->GetDefinition (this->Makefile->GetDefinition
@ -335,7 +350,7 @@ bool cmComputeLinkInformation::Compute()
lei = linkEntries.begin(); lei = linkEntries.begin();
lei != linkEntries.end(); ++lei) lei != linkEntries.end(); ++lei)
{ {
this->AddItem(lei->Item, lei->Target); this->AddItem(lei->Item, lei->Target, lei->IsSharedDep);
} }
// Restore the target link type so the correct system runtime // Restore the target link type so the correct system runtime
@ -358,8 +373,14 @@ bool cmComputeLinkInformation::Compute()
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void cmComputeLinkInformation::AddItem(std::string const& item, void cmComputeLinkInformation::AddItem(std::string const& item,
cmTarget* tgt) cmTarget* tgt, bool isSharedDep)
{ {
// If dropping shared library dependencies, ignore them.
if(isSharedDep && this->SharedDependencyMode == SharedDepModeNone)
{
return;
}
// Compute the proper name to use to link this library. // Compute the proper name to use to link this library.
const char* config = this->Config; const char* config = this->Config;
bool impexe = (tgt && tgt->IsExecutableWithExports()); bool impexe = (tgt && tgt->IsExecutableWithExports());
@ -370,12 +391,6 @@ void cmComputeLinkInformation::AddItem(std::string const& item,
return; return;
} }
// Keep track of shared libraries linked.
if(tgt && tgt->GetType() == cmTarget::SHARED_LIBRARY)
{
this->SharedLibrariesLinked.insert(tgt);
}
if(tgt && (tgt->GetType() == cmTarget::STATIC_LIBRARY || if(tgt && (tgt->GetType() == cmTarget::STATIC_LIBRARY ||
tgt->GetType() == cmTarget::SHARED_LIBRARY || tgt->GetType() == cmTarget::SHARED_LIBRARY ||
tgt->GetType() == cmTarget::MODULE_LIBRARY || tgt->GetType() == cmTarget::MODULE_LIBRARY ||
@ -401,6 +416,14 @@ void cmComputeLinkInformation::AddItem(std::string const& item,
(this->UseImportLibrary && (this->UseImportLibrary &&
(impexe || tgt->GetType() == cmTarget::SHARED_LIBRARY)); (impexe || tgt->GetType() == cmTarget::SHARED_LIBRARY));
// Handle shared dependencies in directory mode.
if(isSharedDep && this->SharedDependencyMode == SharedDepModeDir)
{
std::string dir = tgt->GetDirectory(config, implib);
this->SharedDependencyDirectories.push_back(dir);
return;
}
// Pass the full path to the target file. // Pass the full path to the target file.
std::string lib = tgt->GetFullPath(config, implib); std::string lib = tgt->GetFullPath(config, implib);
this->Depends.push_back(lib); this->Depends.push_back(lib);
@ -411,6 +434,7 @@ void cmComputeLinkInformation::AddItem(std::string const& item,
// link. // link.
std::string fw = tgt->GetDirectory(config, implib); std::string fw = tgt->GetDirectory(config, implib);
this->AddFrameworkItem(fw); this->AddFrameworkItem(fw);
this->SharedLibrariesLinked.insert(tgt);
} }
else else
{ {
@ -705,6 +729,12 @@ void cmComputeLinkInformation::AddTargetItem(std::string const& item,
this->Items.push_back(Item(this->LibLinkFileFlag, false)); this->Items.push_back(Item(this->LibLinkFileFlag, false));
} }
// Keep track of shared library targets linked.
if(target->GetType() == cmTarget::SHARED_LIBRARY)
{
this->SharedLibrariesLinked.insert(target);
}
// Now add the full path to the library. // Now add the full path to the library.
this->Items.push_back(Item(item, true)); this->Items.push_back(Item(item, true));
} }
@ -991,6 +1021,18 @@ void cmComputeLinkInformation::ComputeLinkerSearchDirectories()
{ {
this->AddLinkerSearchDirectories(this->OldLinkDirs); this->AddLinkerSearchDirectories(this->OldLinkDirs);
} }
// Help the linker find dependent shared libraries.
if(this->SharedDependencyMode == SharedDepModeDir)
{
// TODO: These directories should probably be added to the runtime
// path ordering analysis. However they are a bit different.
// They should be placed both on the -L path and in the rpath.
// The link-with-runtime-path feature above should be replaced by
// this.
this->AddLinkerSearchDirectories(this->SharedDependencyDirectories);
}
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------

View File

@ -58,7 +58,7 @@ public:
std::string GetChrpathTool(); std::string GetChrpathTool();
std::set<cmTarget*> const& GetSharedLibrariesLinked(); std::set<cmTarget*> const& GetSharedLibrariesLinked();
private: private:
void AddItem(std::string const& item, cmTarget* tgt); void AddItem(std::string const& item, cmTarget* tgt, bool isSharedDep);
// Output information. // Output information.
ItemVector Items; ItemVector Items;
@ -78,6 +78,14 @@ private:
const char* Config; const char* Config;
const char* LinkLanguage; const char* LinkLanguage;
// Modes for dealing with dependent shared libraries.
enum SharedDepMode
{
SharedDepModeNone, // Drop
SharedDepModeDir, // Use in runtime information
SharedDepModeLink // List file on link line
};
// System info. // System info.
bool UseImportLibrary; bool UseImportLibrary;
const char* LoaderFlag; const char* LoaderFlag;
@ -88,6 +96,7 @@ private:
std::string RuntimeSep; std::string RuntimeSep;
std::string RuntimeAlways; std::string RuntimeAlways;
bool RuntimeUseChrpath; bool RuntimeUseChrpath;
SharedDepMode SharedDependencyMode;
// Link type adjustment. // Link type adjustment.
void ComputeLinkTypeInfo(); void ComputeLinkTypeInfo();
@ -134,6 +143,7 @@ private:
void AddLinkerSearchDirectories(std::vector<std::string> const& dirs); void AddLinkerSearchDirectories(std::vector<std::string> const& dirs);
std::set<cmStdString> DirectoriesEmmitted; std::set<cmStdString> DirectoriesEmmitted;
std::set<cmStdString> ImplicitLinkDirs; std::set<cmStdString> ImplicitLinkDirs;
std::vector<std::string> SharedDependencyDirectories;
// Linker search path compatibility mode. // Linker search path compatibility mode.
std::vector<std::string> OldLinkDirs; std::vector<std::string> OldLinkDirs;

View File

@ -1104,5 +1104,7 @@ void cmDocumentVariables::DefineVariables(cmake* cm)
cmProperty::VARIABLE,0,0); cmProperty::VARIABLE,0,0);
cm->DefineProperty("CMAKE_SHARED_MODULE_RUNTIME_<LANG>_FLAG_SEP", cm->DefineProperty("CMAKE_SHARED_MODULE_RUNTIME_<LANG>_FLAG_SEP",
cmProperty::VARIABLE,0,0); cmProperty::VARIABLE,0,0);
cm->DefineProperty("CMAKE_DEPENDENT_SHARED_LIBRARY_MODE",
cmProperty::VARIABLE,0,0);
} }

View File

@ -139,7 +139,12 @@ cmExportFileGenerator
target->GetLinkInterface(config)) target->GetLinkInterface(config))
{ {
// This target provides a link interface, so use it. // This target provides a link interface, so use it.
this->SetImportLinkProperties(suffix, target, *interface, properties); this->SetImportLinkProperty(suffix, target,
"IMPORTED_LINK_INTERFACE_LIBRARIES",
interface->Libraries, properties);
this->SetImportLinkProperty(suffix, target,
"IMPORTED_LINK_DEPENDENT_LIBRARIES",
interface->SharedDeps, properties);
} }
else if(target->GetType() == cmTarget::STATIC_LIBRARY || else if(target->GetType() == cmTarget::STATIC_LIBRARY ||
target->GetType() == cmTarget::SHARED_LIBRARY) target->GetType() == cmTarget::SHARED_LIBRARY)
@ -183,17 +188,26 @@ cmExportFileGenerator
} }
// Store the entries in the property. // Store the entries in the property.
this->SetImportLinkProperties(suffix, target, actual_libs, properties); this->SetImportLinkProperty(suffix, target,
"IMPORTED_LINK_INTERFACE_LIBRARIES",
actual_libs, properties);
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
void void
cmExportFileGenerator cmExportFileGenerator
::SetImportLinkProperties(std::string const& suffix, ::SetImportLinkProperty(std::string const& suffix,
cmTarget* target, cmTarget* target,
const char* propName,
std::vector<std::string> const& libs, std::vector<std::string> const& libs,
ImportPropertyMap& properties) ImportPropertyMap& properties)
{ {
// Skip the property if there are no libraries.
if(libs.empty())
{
return;
}
// Get the makefile in which to lookup target information. // Get the makefile in which to lookup target information.
cmMakefile* mf = target->GetMakefile(); cmMakefile* mf = target->GetMakefile();
@ -233,6 +247,9 @@ cmExportFileGenerator
// known here. This is probably user-error. // known here. This is probably user-error.
this->ComplainAboutMissingTarget(target, li->c_str()); this->ComplainAboutMissingTarget(target, li->c_str());
} }
// Assume the target will be exported by another command.
// Append it with the export namespace.
link_libs += this->Namespace;
link_libs += *li; link_libs += *li;
} }
} }
@ -244,7 +261,7 @@ cmExportFileGenerator
} }
// Store the property. // Store the property.
std::string prop = "IMPORTED_LINK_LIBRARIES"; std::string prop = propName;
prop += suffix; prop += suffix;
properties[prop] = link_libs; properties[prop] = link_libs;
} }

View File

@ -70,8 +70,8 @@ protected:
void SetImportLinkProperties(const char* config, void SetImportLinkProperties(const char* config,
std::string const& suffix, cmTarget* target, std::string const& suffix, cmTarget* target,
ImportPropertyMap& properties); ImportPropertyMap& properties);
void SetImportLinkProperties(std::string const& suffix, void SetImportLinkProperty(std::string const& suffix,
cmTarget* target, cmTarget* target, const char* propName,
std::vector<std::string> const& libs, std::vector<std::string> const& libs,
ImportPropertyMap& properties); ImportPropertyMap& properties);

View File

@ -188,15 +188,38 @@ void cmTarget::DefineProperties(cmake *cm)
"from which the target is imported."); "from which the target is imported.");
cm->DefineProperty cm->DefineProperty
("IMPORTED_LINK_LIBRARIES", cmProperty::TARGET, ("IMPORTED_LINK_DEPENDENT_LIBRARIES", cmProperty::TARGET,
"Transitive link dependencies of an IMPORTED target.", "Dependent shared libraries of an imported shared library.",
"Lists dependencies that must be linked when an IMPORTED library " "Shared libraries may be linked to other shared libraries as part "
"of their implementation. On some platforms the linker searches "
"for the dependent libraries of shared libraries they are including "
"in the link. CMake gives the paths to these libraries to the linker "
"by listing them on the link line explicitly. This property lists "
"the dependent shared libraries of an imported library. The list "
"should be disjoint from the list of interface libraries in the "
"IMPORTED_LINK_INTERFACE_LIBRARIES property. On platforms requiring "
"dependent shared libraries to be found at link time CMake uses this "
"list to add the dependent libraries to the link command line.");
cm->DefineProperty
("IMPORTED_LINK_DEPENDENT_LIBRARIES_<CONFIG>", cmProperty::TARGET,
"Per-configuration version of IMPORTED_LINK_DEPENDENT_LIBRARIES.",
"This property is used when loading settings for the <CONFIG> "
"configuration of an imported target. "
"Configuration names correspond to those provided by the project "
"from which the target is imported.");
cm->DefineProperty
("IMPORTED_LINK_INTERFACE_LIBRARIES", cmProperty::TARGET,
"Transitive link interface of an IMPORTED target.",
"Lists libraries whose interface is included when an IMPORTED library "
"target is linked to another target. " "target is linked to another target. "
"The libraries will be included on the link line for the target. "
"Ignored for non-imported targets."); "Ignored for non-imported targets.");
cm->DefineProperty cm->DefineProperty
("IMPORTED_LINK_LIBRARIES_<CONFIG>", cmProperty::TARGET, ("IMPORTED_LINK_INTERFACE_LIBRARIES_<CONFIG>", cmProperty::TARGET,
"Per-configuration version of IMPORTED_LINK_LIBRARIES property.", "Per-configuration version of IMPORTED_LINK_INTERFACE_LIBRARIES.",
"This property is used when loading settings for the <CONFIG> " "This property is used when loading settings for the <CONFIG> "
"configuration of an imported target. " "configuration of an imported target. "
"Configuration names correspond to those provided by the project " "Configuration names correspond to those provided by the project "
@ -3045,43 +3068,56 @@ void cmTarget::ComputeImportInfo(std::string const& desired_config,
} }
} }
// Get the link dependencies. // Get the link interface.
{ {
std::string linkProp = "IMPORTED_LINK_LIBRARIES"; std::string linkProp = "IMPORTED_LINK_INTERFACE_LIBRARIES";
linkProp += suffix; linkProp += suffix;
if(const char* config_libs = this->GetProperty(linkProp.c_str())) if(const char* config_libs = this->GetProperty(linkProp.c_str()))
{ {
cmSystemTools::ExpandListArgument(config_libs, info.LinkLibraries); cmSystemTools::ExpandListArgument(config_libs,
info.LinkInterface.Libraries);
} }
else if(const char* libs = this->GetProperty("IMPORTED_LINK_LIBRARIES")) else if(const char* libs =
this->GetProperty("IMPORTED_LINK_INTERFACE_LIBRARIES"))
{ {
cmSystemTools::ExpandListArgument(libs, info.LinkLibraries); cmSystemTools::ExpandListArgument(libs,
} info.LinkInterface.Libraries);
} }
} }
//---------------------------------------------------------------------------- // Get the link dependencies.
std::vector<std::string> const*
cmTarget::GetImportedLinkLibraries(const char* config)
{ {
if(cmTarget::ImportInfo const* info = this->GetImportInfo(config)) std::string linkProp = "IMPORTED_LINK_DEPENDENT_LIBRARIES";
linkProp += suffix;
if(const char* config_libs = this->GetProperty(linkProp.c_str()))
{ {
return &info->LinkLibraries; cmSystemTools::ExpandListArgument(config_libs,
info.LinkInterface.SharedDeps);
} }
else else if(const char* libs =
this->GetProperty("IMPORTED_LINK_DEPENDENT_LIBRARIES"))
{ {
return 0; cmSystemTools::ExpandListArgument(libs, info.LinkInterface.SharedDeps);
}
} }
} }
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
cmTargetLinkInterface const* cmTarget::GetLinkInterface(const char* config) cmTargetLinkInterface const* cmTarget::GetLinkInterface(const char* config)
{ {
// Link interfaces are supported only for non-imported shared // Imported targets have their own link interface.
// libraries and executables that export symbols. Imported targets if(this->IsImported())
// provide their own link information. {
if(this->IsImported() || if(cmTarget::ImportInfo const* info = this->GetImportInfo(config))
(this->GetType() != cmTarget::SHARED_LIBRARY && {
return &info->LinkInterface;
}
return 0;
}
// Link interfaces are supported only for shared libraries and
// executables that export symbols.
if((this->GetType() != cmTarget::SHARED_LIBRARY &&
!this->IsExecutableWithExports())) !this->IsExecutableWithExports()))
{ {
return 0; return 0;
@ -3139,15 +3175,79 @@ cmTargetLinkInterface* cmTarget::ComputeLinkInterface(const char* config)
return 0; return 0;
} }
// Return the interface libraries even if the list is empty. // Allocate the interface.
if(cmTargetLinkInterface* interface = new cmTargetLinkInterface) cmTargetLinkInterface* interface = new cmTargetLinkInterface;
if(!interface)
{ {
cmSystemTools::ExpandListArgument(libs, *interface);
return interface;
}
return 0; return 0;
} }
// Expand the list of libraries in the interface.
cmSystemTools::ExpandListArgument(libs, interface->Libraries);
// Now we need to construct a list of shared library dependencies
// not included in the interface.
if(this->GetType() == cmTarget::SHARED_LIBRARY)
{
// Use a set to keep track of what libraries have been emitted to
// either list.
std::set<cmStdString> emitted;
for(std::vector<std::string>::const_iterator
li = interface->Libraries.begin();
li != interface->Libraries.end(); ++li)
{
emitted.insert(*li);
}
// Compute which library configuration to link.
cmTarget::LinkLibraryType linkType = cmTarget::OPTIMIZED;
if(config && cmSystemTools::UpperCase(config) == "DEBUG")
{
linkType = cmTarget::DEBUG;
}
// Construct the list of libs linked for this configuration.
cmTarget::LinkLibraryVectorType const& libs =
this->GetOriginalLinkLibraries();
for(cmTarget::LinkLibraryVectorType::const_iterator li = libs.begin();
li != libs.end(); ++li)
{
// Skip entries that will resolve to the target itself, are empty,
// or are not meant for this configuration.
if(li->first == this->GetName() || li->first.empty() ||
!(li->second == cmTarget::GENERAL || li->second == linkType))
{
continue;
}
// Skip entries that have already been emitted into either list.
if(!emitted.insert(li->first).second)
{
continue;
}
// Add this entry if it is a shared library.
if(cmTarget* tgt = this->Makefile->FindTargetToUse(li->first.c_str()))
{
if(tgt->GetType() == cmTarget::SHARED_LIBRARY)
{
interface->SharedDeps.push_back(li->first);
}
}
else
{
// TODO: Recognize shared library file names. Perhaps this
// should be moved to cmComputeLinkInformation, but that creates
// a chicken-and-egg problem since this list is needed for its
// construction.
}
}
}
// Return the completed interface.
return interface;
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
cmComputeLinkInformation* cmComputeLinkInformation*
cmTarget::GetLinkInformation(const char* config) cmTarget::GetLinkInformation(const char* config)

View File

@ -35,9 +35,13 @@ struct cmTargetLinkInformationMap:
~cmTargetLinkInformationMap(); ~cmTargetLinkInformationMap();
}; };
struct cmTargetLinkInterface: public std::vector<std::string> struct cmTargetLinkInterface
{ {
typedef std::vector<std::string> derived; // Libraries listed in the interface.
std::vector<std::string> Libraries;
// Shared library dependencies needed for linking on some platforms.
std::vector<std::string> SharedDeps;
}; };
struct cmTargetLinkInterfaceMap: struct cmTargetLinkInterfaceMap:
@ -218,11 +222,6 @@ public:
bool IsImported() const {return this->IsImportedTarget;} bool IsImported() const {return this->IsImportedTarget;}
/** Get link libraries for the given configuration of an imported
target. */
std::vector<std::string> const*
GetImportedLinkLibraries(const char* config);
/** Get the library interface dependencies. This is the set of /** Get the library interface dependencies. This is the set of
libraries from which something that links to this target may libraries from which something that links to this target may
also receive symbols. Returns 0 if the user has not specified also receive symbols. Returns 0 if the user has not specified
@ -487,7 +486,7 @@ private:
std::string Location; std::string Location;
std::string SOName; std::string SOName;
std::string ImportLibrary; std::string ImportLibrary;
std::vector<std::string> LinkLibraries; cmTargetLinkInterface LinkInterface;
}; };
typedef std::map<cmStdString, ImportInfo> ImportInfoMapType; typedef std::map<cmStdString, ImportInfo> ImportInfoMapType;
ImportInfoMapType ImportInfoMap; ImportInfoMapType ImportInfoMap;

View File

@ -32,17 +32,11 @@ set_property(TARGET testLib4 PROPERTY FRAMEWORK 1)
add_executable(testExe3 testExe3.c) add_executable(testExe3 testExe3.c)
set_property(TARGET testExe3 PROPERTY MACOSX_BUNDLE 1) set_property(TARGET testExe3 PROPERTY MACOSX_BUNDLE 1)
# Install helper targets that are not part of the interface.
install(
TARGETS testExe2libImp testLib3Imp
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
)
# Install and export from install tree. # Install and export from install tree.
install( install(
TARGETS testExe1 testLib1 testLib2 testExe2 testLib3 testLib4 testExe3 TARGETS
testExe2libImp testLib3Imp
testExe1 testLib1 testLib2 testExe2 testLib3 testLib4 testExe3
testExe2lib testExe2lib
EXPORT exp EXPORT exp
RUNTIME DESTINATION bin RUNTIME DESTINATION bin
@ -55,6 +49,7 @@ install(EXPORT exp NAMESPACE exp_ DESTINATION lib/exp)
# Export from build tree. # Export from build tree.
export(TARGETS testExe1 testLib1 testLib2 testLib3 export(TARGETS testExe1 testLib1 testLib2 testLib3
testExe2libImp testLib3Imp
NAMESPACE bld_ NAMESPACE bld_
FILE ExportBuildTree.cmake FILE ExportBuildTree.cmake
) )