summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrad King <brad.king@kitware.com>2008-01-30 22:25:52 (GMT)
committerBrad King <brad.king@kitware.com>2008-01-30 22:25:52 (GMT)
commit7902bc06aae07a9d4cde81ab41c3c86694d80a9b (patch)
tree20810436488a804ffadb37c349d8db594d368a36
parent22be36f8d52fae0f509725253f175b0c1ec65dcc (diff)
downloadCMake-7902bc06aae07a9d4cde81ab41c3c86694d80a9b.zip
CMake-7902bc06aae07a9d4cde81ab41c3c86694d80a9b.tar.gz
CMake-7902bc06aae07a9d4cde81ab41c3c86694d80a9b.tar.bz2
ENH: Implemented link-interface specification feature.
- Shared libs and executables with exports may now have explicit transitive link dependencies specified - Created LINK_INTERFACE_LIBRARIES and related properties - Exported targets get the interface libraries as their IMPORTED_LINK_LIBRARIES property. - The export() and install(EXPORT) commands now give an error when a linked target is not included since the user can change the interface libraries instead of adding the target.
-rw-r--r--Source/cmComputeLinkDepends.cxx25
-rw-r--r--Source/cmExportBuildFileGenerator.cxx26
-rw-r--r--Source/cmExportBuildFileGenerator.h8
-rw-r--r--Source/cmExportCommand.cxx14
-rw-r--r--Source/cmExportCommand.h5
-rw-r--r--Source/cmExportFileGenerator.cxx77
-rw-r--r--Source/cmExportFileGenerator.h5
-rw-r--r--Source/cmExportInstallFileGenerator.cxx14
-rw-r--r--Source/cmTarget.cxx119
-rw-r--r--Source/cmTarget.h24
10 files changed, 273 insertions, 44 deletions
diff --git a/Source/cmComputeLinkDepends.cxx b/Source/cmComputeLinkDepends.cxx
index 6940b7c..1684641 100644
--- a/Source/cmComputeLinkDepends.cxx
+++ b/Source/cmComputeLinkDepends.cxx
@@ -263,17 +263,22 @@ void cmComputeLinkDepends::FollowLinkEntry(BFSEntry const& qe)
if(entry.Target)
{
// Follow the target dependencies.
- if(entry.Target->GetType() != cmTarget::EXECUTABLE)
+ if(entry.Target->IsImported())
{
- if(entry.Target->IsImported())
- {
- this->AddImportedLinkEntries(depender_index, entry.Target);
- }
- else
- {
- this->AddTargetLinkEntries(depender_index,
- entry.Target->GetOriginalLinkLibraries());
- }
+ // Imported targets provide their own link information.
+ this->AddImportedLinkEntries(depender_index, entry.Target);
+ }
+ else if(cmTargetLinkInterface const* interface =
+ entry.Target->GetLinkInterface(this->Config))
+ {
+ // This target provides its own link interface information.
+ this->AddLinkEntries(depender_index, *interface);
+ }
+ else if(entry.Target->GetType() != cmTarget::EXECUTABLE)
+ {
+ // Use the target's link implementation as the interface.
+ this->AddTargetLinkEntries(depender_index,
+ entry.Target->GetOriginalLinkLibraries());
}
}
else
diff --git a/Source/cmExportBuildFileGenerator.cxx b/Source/cmExportBuildFileGenerator.cxx
index f0dca0a..618cd19 100644
--- a/Source/cmExportBuildFileGenerator.cxx
+++ b/Source/cmExportBuildFileGenerator.cxx
@@ -16,6 +16,14 @@
=========================================================================*/
#include "cmExportBuildFileGenerator.h"
+#include "cmExportCommand.h"
+
+//----------------------------------------------------------------------------
+cmExportBuildFileGenerator::cmExportBuildFileGenerator()
+{
+ this->ExportCommand = 0;
+}
+
//----------------------------------------------------------------------------
bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
{
@@ -116,9 +124,19 @@ void
cmExportBuildFileGenerator
::ComplainAboutMissingTarget(cmTarget* target, const char* dep)
{
+ if(!this->ExportCommand || !this->ExportCommand->ErrorMessage.empty())
+ {
+ return;
+ }
+
cmOStringStream e;
- e << "WARNING: EXPORT(...) includes target " << target->GetName()
- << " which links to target \"" << dep
- << "\" that is not in the export set.";
- cmSystemTools::Message(e.str().c_str());
+ e << "called with target \"" << target->GetName()
+ << "\" which links to target \"" << dep
+ << "\" that is not in the export list.\n"
+ << "If the link dependency is not part of the public interface "
+ << "consider setting the LINK_INTERFACE_LIBRARIES property on \""
+ << target->GetName() << "\". Otherwise add it to the export list. "
+ << "If the link dependency is not easy to reference in this call, "
+ << "consider using the APPEND option with multiple separate calls.";
+ this->ExportCommand->ErrorMessage = e.str();
}
diff --git a/Source/cmExportBuildFileGenerator.h b/Source/cmExportBuildFileGenerator.h
index 53423f3..394e95a 100644
--- a/Source/cmExportBuildFileGenerator.h
+++ b/Source/cmExportBuildFileGenerator.h
@@ -19,6 +19,8 @@
#include "cmExportFileGenerator.h"
+class cmExportCommand;
+
/** \class cmExportBuildFileGenerator
* \brief Generate a file exporting targets from a build tree.
*
@@ -31,12 +33,17 @@
class cmExportBuildFileGenerator: public cmExportFileGenerator
{
public:
+ cmExportBuildFileGenerator();
+
/** Set the list of targets to export. */
void SetExports(std::vector<cmTarget*> const* exports)
{ this->Exports = exports; }
/** Set whether to append generated code to the output file. */
void SetAppendMode(bool append) { this->AppendMode = append; }
+
+ /** Set the command instance through which errors should be reported. */
+ void SetCommand(cmExportCommand* cmd) { this->ExportCommand = cmd; }
protected:
// Implement virtual methods from the superclass.
virtual bool GenerateMainFile(std::ostream& os);
@@ -52,6 +59,7 @@ protected:
ImportPropertyMap& properties);
std::vector<cmTarget*> const* Exports;
+ cmExportCommand* ExportCommand;
};
#endif
diff --git a/Source/cmExportCommand.cxx b/Source/cmExportCommand.cxx
index cfc339c..ba47637 100644
--- a/Source/cmExportCommand.cxx
+++ b/Source/cmExportCommand.cxx
@@ -100,12 +100,6 @@ bool cmExportCommand
fname += this->Filename.GetString();
}
- // If no targets are to be exported we are done.
- if(this->Targets.GetVector().empty())
- {
- return true;
- }
-
// Collect the targets to be exported.
std::vector<cmTarget*> targets;
for(std::vector<std::string>::const_iterator
@@ -149,6 +143,7 @@ bool cmExportCommand
ebfg.SetNamespace(this->Namespace.GetCString());
ebfg.SetAppendMode(this->Append.IsEnabled());
ebfg.SetExports(&targets);
+ ebfg.SetCommand(this);
// Compute the set of configurations exported.
if(const char* types =
@@ -180,5 +175,12 @@ bool cmExportCommand
return false;
}
+ // Report generated error message if any.
+ if(!this->ErrorMessage.empty())
+ {
+ this->SetError(this->ErrorMessage.c_str());
+ return false;
+ }
+
return true;
}
diff --git a/Source/cmExportCommand.h b/Source/cmExportCommand.h
index d0e88c4..22a93f3 100644
--- a/Source/cmExportCommand.h
+++ b/Source/cmExportCommand.h
@@ -19,6 +19,8 @@
#include "cmCommand.h"
+class cmExportBuildFileGenerator;
+
/** \class cmExportLibraryDependenciesCommand
* \brief Add a test to the lists of tests to run.
*
@@ -93,6 +95,9 @@ private:
cmCAEnabler Append;
cmCAString Namespace;
cmCAString Filename;
+
+ friend class cmExportBuildFileGenerator;
+ std::string ErrorMessage;
};
diff --git a/Source/cmExportFileGenerator.cxx b/Source/cmExportFileGenerator.cxx
index 6cf30c9..c48aff8 100644
--- a/Source/cmExportFileGenerator.cxx
+++ b/Source/cmExportFileGenerator.cxx
@@ -135,9 +135,18 @@ cmExportFileGenerator
}
// Add the transitive link dependencies for this configuration.
- if(target->GetType() == cmTarget::STATIC_LIBRARY ||
- target->GetType() == cmTarget::SHARED_LIBRARY)
+ if(cmTargetLinkInterface const* interface =
+ target->GetLinkInterface(config))
{
+ // This target provides a link interface, so use it.
+ this->SetImportLinkProperties(config, suffix, target,
+ *interface, properties);
+ }
+ else if(target->GetType() == cmTarget::STATIC_LIBRARY ||
+ target->GetType() == cmTarget::SHARED_LIBRARY)
+ {
+ // The default link interface for static and shared libraries is
+ // their link implementation library list.
this->SetImportLinkProperties(config, suffix, target, properties);
}
}
@@ -148,9 +157,6 @@ cmExportFileGenerator
::SetImportLinkProperties(const char* config, std::string const& suffix,
cmTarget* target, ImportPropertyMap& properties)
{
- // Get the makefile in which to lookup target information.
- cmMakefile* mf = target->GetMakefile();
-
// Compute which library configuration to link.
cmTarget::LinkLibraryType linkType = cmTarget::OPTIMIZED;
if(config && cmSystemTools::UpperCase(config) == "DEBUG")
@@ -158,10 +164,10 @@ cmExportFileGenerator
linkType = cmTarget::DEBUG;
}
- // Construct the property value.
+ // Construct the list of libs linked for this configuration.
+ std::vector<std::string> actual_libs;
cmTarget::LinkLibraryVectorType const& libs =
target->GetOriginalLinkLibraries();
- std::string link_libs;
const char* sep = "";
for(cmTarget::LinkLibraryVectorType::const_iterator li = libs.begin();
li != libs.end(); ++li)
@@ -174,33 +180,66 @@ cmExportFileGenerator
continue;
}
- // Separate this from the previous entry.
- link_libs += sep;
- sep = ";";
+ // Store this entry.
+ actual_libs.push_back(li->first);
+ }
+
+ // Store the entries in the property.
+ this->SetImportLinkProperties(config, suffix, target,
+ actual_libs, properties);
+}
+
+//----------------------------------------------------------------------------
+void
+cmExportFileGenerator
+::SetImportLinkProperties(const char* config,
+ std::string const& suffix,
+ cmTarget* target,
+ std::vector<std::string> const& libs,
+ ImportPropertyMap& properties)
+{
+ // Get the makefile in which to lookup target information.
+ cmMakefile* mf = target->GetMakefile();
+ // Construct the property value.
+ std::string link_libs;
+ const char* sep = "";
+ for(std::vector<std::string>::const_iterator li = libs.begin();
+ li != libs.end(); ++li)
+ {
// Append this entry.
- if(cmTarget* tgt = mf->FindTargetToUse(li->first.c_str()))
+ if(cmTarget* tgt = mf->FindTargetToUse(li->c_str()))
{
- // This is a target. Make sure it is included in the export.
- if(this->ExportedTargets.find(tgt) != this->ExportedTargets.end())
+ // This is a target.
+ if(tgt->IsImported())
+ {
+ // The target is imported (and therefore is not in the
+ // export). Append the raw name.
+ link_libs += *li;
+ }
+ else if(this->ExportedTargets.find(tgt) != this->ExportedTargets.end())
{
// The target is in the export. Append it with the export
// namespace.
link_libs += this->Namespace;
- link_libs += li->first;
+ link_libs += *li;
}
else
{
- // The target is not in the export. This is probably
- // user-error. Warn but add it anyway.
- this->ComplainAboutMissingTarget(target, li->first.c_str());
- link_libs += li->first;
+ // The target is not in the export.
+ if(!this->AppendMode)
+ {
+ // We are not appending, so all exported targets should be
+ // known here. This is probably user-error.
+ this->ComplainAboutMissingTarget(target, li->c_str());
+ }
+ link_libs += *li;
}
}
else
{
// Append the raw name.
- link_libs += li->first;
+ link_libs += *li;
}
}
diff --git a/Source/cmExportFileGenerator.h b/Source/cmExportFileGenerator.h
index 27d998b..ac9cb75 100644
--- a/Source/cmExportFileGenerator.h
+++ b/Source/cmExportFileGenerator.h
@@ -70,6 +70,11 @@ protected:
void SetImportLinkProperties(const char* config,
std::string const& suffix, cmTarget* target,
ImportPropertyMap& properties);
+ void SetImportLinkProperties(const char* config,
+ std::string const& suffix,
+ cmTarget* target,
+ std::vector<std::string> const& libs,
+ ImportPropertyMap& properties);
/** Each subclass knows how to generate its kind of export file. */
virtual bool GenerateMainFile(std::ostream& os) = 0;
diff --git a/Source/cmExportInstallFileGenerator.cxx b/Source/cmExportInstallFileGenerator.cxx
index 82af4e1..14bb816 100644
--- a/Source/cmExportInstallFileGenerator.cxx
+++ b/Source/cmExportInstallFileGenerator.cxx
@@ -267,9 +267,13 @@ cmExportInstallFileGenerator
::ComplainAboutMissingTarget(cmTarget* target, const char* dep)
{
cmOStringStream e;
- e << "WARNING: INSTALL(EXPORT \"" << this->Name << "\" ...) "
- << "includes target " << target->GetName()
- << " which links to target \"" << dep
- << "\" that is not in the export set.";
- cmSystemTools::Message(e.str().c_str());
+ e << "INSTALL(EXPORT \"" << this->Name << "\" ...) "
+ << "includes target \"" << target->GetName()
+ << "\" which links to target \"" << dep
+ << "\" that is not in the export set. "
+ << "If the link dependency is not part of the public interface "
+ << "consider setting the LINK_INTERFACE_LIBRARIES property on "
+ << "target \"" << target->GetName() << "\". "
+ << "Otherwise add it to the export set.";
+ cmSystemTools::Error(e.str().c_str());
}
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index 706a45f..6575140 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -312,6 +312,28 @@ void cmTarget::DefineProperties(cmake *cm)
"The property is defined only for library and executable targets.");
cm->DefineProperty
+ ("LINK_INTERFACE_LIBRARIES", cmProperty::TARGET,
+ "List public interface libraries for a shared library or executable.",
+ "By default linking to a shared library target transitively "
+ "links to targets with which the library itself was linked. "
+ "For an executable with exports (see the ENABLE_EXPORTS property) "
+ "no default transitive link dependencies are used. "
+ "This property replaces the default transitive link dependencies with "
+ "an explict list. "
+ "When the target is linked into another target the libraries "
+ "listed (and recursively their link interface libraries) will be "
+ "provided to the other target also. "
+ "If the list is empty then no transitive link dependencies will be "
+ "incorporated when this target is linked into another target even if "
+ "the default set is non-empty.");
+
+ cm->DefineProperty
+ ("LINK_INTERFACE_LIBRARIES_<CONFIG>", cmProperty::TARGET,
+ "Per-configuration list of public interface libraries for a target.",
+ "This is the configuration-specific version of "
+ "LINK_INTERFACE_LIBRARIES.");
+
+ cm->DefineProperty
("MAP_IMPORTED_CONFIG_<CONFIG>", cmProperty::TARGET,
"Map from project configuration to IMPORTED target's configuration.",
"List configurations of an imported target that may be used for "
@@ -3041,6 +3063,80 @@ cmTarget::GetImportedLinkLibraries(const char* config)
}
//----------------------------------------------------------------------------
+cmTargetLinkInterface const* cmTarget::GetLinkInterface(const char* config)
+{
+ // Link interfaces are supported only for non-imported shared
+ // libraries and executables that export symbols. Imported targets
+ // provide their own link information.
+ if(this->IsImported() ||
+ (this->GetType() != cmTarget::SHARED_LIBRARY &&
+ !this->IsExecutableWithExports()))
+ {
+ return 0;
+ }
+
+ // Lookup any existing link interface for this configuration.
+ std::map<cmStdString, cmTargetLinkInterface*>::iterator
+ i = this->LinkInterface.find(config?config:"");
+ if(i == this->LinkInterface.end())
+ {
+ // Compute the link interface for this configuration.
+ cmTargetLinkInterface* interface = this->ComputeLinkInterface(config);
+
+ // Store the information for this configuration.
+ std::map<cmStdString, cmTargetLinkInterface*>::value_type
+ entry(config?config:"", interface);
+ i = this->LinkInterface.insert(entry).first;
+ }
+
+ return i->second;
+}
+
+//----------------------------------------------------------------------------
+cmTargetLinkInterface* cmTarget::ComputeLinkInterface(const char* config)
+{
+ // Construct the property name suffix for this configuration.
+ std::string suffix = "_";
+ if(config && *config)
+ {
+ suffix += cmSystemTools::UpperCase(config);
+ }
+ else
+ {
+ suffix += "NOCONFIG";
+ }
+
+ // Lookup the link interface libraries.
+ const char* libs = 0;
+ {
+ // Lookup the per-configuration property.
+ std::string propName = "LINK_INTERFACE_LIBRARIES";
+ propName += suffix;
+ libs = this->GetProperty(propName.c_str());
+
+ // If not set, try the generic property.
+ if(!libs)
+ {
+ libs = this->GetProperty("LINK_INTERFACE_LIBRARIES");
+ }
+ }
+
+ // If still not set, there is no link interface.
+ if(!libs)
+ {
+ return 0;
+ }
+
+ // Return the interface libraries even if the list is empty.
+ if(cmTargetLinkInterface* interface = new cmTargetLinkInterface)
+ {
+ cmSystemTools::ExpandListArgument(libs, *interface);
+ return interface;
+ }
+ return 0;
+}
+
+//----------------------------------------------------------------------------
cmComputeLinkInformation*
cmTarget::GetLinkInformation(const char* config)
{
@@ -3088,3 +3184,26 @@ cmTargetLinkInformationMap::~cmTargetLinkInformationMap()
delete i->second;
}
}
+
+//----------------------------------------------------------------------------
+cmTargetLinkInterfaceMap
+::cmTargetLinkInterfaceMap(cmTargetLinkInterfaceMap const& r): derived()
+{
+ // Ideally cmTarget instances should never be copied. However until
+ // we can make a sweep to remove that, this copy constructor avoids
+ // allowing the resources (LinkInterface) from getting copied. In
+ // the worst case this will lead to extra cmTargetLinkInterface
+ // instances. We also enforce in debug mode that the map be emptied
+ // when copied.
+ static_cast<void>(r);
+ assert(r.empty());
+}
+
+//----------------------------------------------------------------------------
+cmTargetLinkInterfaceMap::~cmTargetLinkInterfaceMap()
+{
+ for(derived::iterator i = this->begin(); i != this->end(); ++i)
+ {
+ delete i->second;
+ }
+}
diff --git a/Source/cmTarget.h b/Source/cmTarget.h
index 37ab203..bbcdafc 100644
--- a/Source/cmTarget.h
+++ b/Source/cmTarget.h
@@ -35,6 +35,20 @@ struct cmTargetLinkInformationMap:
~cmTargetLinkInformationMap();
};
+struct cmTargetLinkInterface: public std::vector<std::string>
+{
+ typedef std::vector<std::string> derived;
+};
+
+struct cmTargetLinkInterfaceMap:
+ public std::map<cmStdString, cmTargetLinkInterface*>
+{
+ typedef std::map<cmStdString, cmTargetLinkInterface*> derived;
+ cmTargetLinkInterfaceMap() {}
+ cmTargetLinkInterfaceMap(cmTargetLinkInterfaceMap const& r);
+ ~cmTargetLinkInterfaceMap();
+};
+
/** \class cmTarget
* \brief Represent a library or executable target loaded from a makefile.
*
@@ -209,6 +223,12 @@ public:
std::vector<std::string> const*
GetImportedLinkLibraries(const char* config);
+ /** Get the library interface dependencies. This is the set of
+ libraries from which something that links to this target may
+ also receive symbols. Returns 0 if the user has not specified
+ such dependencies or for static libraries. */
+ cmTargetLinkInterface const* GetLinkInterface(const char* config);
+
/** Get the directory in which this target will be built. If the
configuration name is given then the generator will add its
subdirectory for that configuration. Otherwise just the canonical
@@ -476,6 +496,10 @@ private:
cmTargetLinkInformationMap LinkInformation;
+ // Link interface.
+ cmTargetLinkInterface* ComputeLinkInterface(const char* config);
+ cmTargetLinkInterfaceMap LinkInterface;
+
// The cmMakefile instance that owns this target. This should
// always be set.
cmMakefile* Makefile;