1
0
mirror of https://github.com/Kitware/CMake.git synced 2025-10-15 03:48:02 +08:00

target_link_libraries: Allow use with targets in other directories

Previously the command did not allow naming targets on the LHS that
were not created in the calling directory.  Lift this restriction to
enable more flexible use by projects.

Targets named on the RHS will need to be looked up during generation in
the scope of the call site rather than the scope of the LHS target.
Introduce an internal syntax in `[INTERFACE_]LINK_LIBRARIES` properties
to specify target names that need to be looked up in a directory other
than that containing the target on which the property is set.  Add
minimal documentation of the syntax to help users that encounter it.

Unfortunately CMake previously did allow such calls in the case that
only `INTERFACE` libraries are specified, but those libraries would be
looked up in the target's directory rather than the caller's.  Add
policy `CMP0079` to enable the new behavior with new lookup scope in a
compatible way.

Fixes: #17943
This commit is contained in:
Brad King
2018-09-07 12:59:52 -04:00
parent 9bbae5ae28
commit a1ad0a699b
56 changed files with 460 additions and 44 deletions

View File

@@ -18,10 +18,13 @@ All of them have the general form::
target_link_libraries(<target> ... <item>... ...)
The named ``<target>`` must have been created in the current directory by
a command such as :command:`add_executable` or :command:`add_library` and
must not be an :ref:`ALIAS target <Alias Targets>`.
Repeated calls for the same ``<target>`` append items in the order called.
The named ``<target>`` must have been created by a command such as
:command:`add_executable` or :command:`add_library` and must not be an
:ref:`ALIAS target <Alias Targets>`. If policy :policy:`CMP0079` is not
set to ``NEW`` then the target must have been created in the current
directory. Repeated calls for the same ``<target>`` append items in
the order called.
Each ``<item>`` may be:
* **A library target name**: The generated link line will have the

View File

@@ -57,6 +57,7 @@ Policies Introduced by CMake 3.13
.. toctree::
:maxdepth: 1
CMP0079: target_link_libraries allows use with targets in other directories. </policy/CMP0079>
CMP0078: UseSWIG generates standard target names. </policy/CMP0078>
CMP0077: option() honors normal variables. </policy/CMP0077>
CMP0076: target_sources() command converts relative paths to absolute. </policy/CMP0076>

40
Help/policy/CMP0079.rst Normal file
View File

@@ -0,0 +1,40 @@
CMP0079
-------
:command:`target_link_libraries` allows use with targets in other directories.
Prior to CMake 3.13 the :command:`target_link_libraries` command did not
accept targets not created in the calling directory as its first argument
for calls that update the :prop_tgt:`LINK_LIBRARIES` of the target itself.
It did accidentally accept targets from other directories on calls that
only update the :prop_tgt:`INTERFACE_LINK_LIBRARIES`, but would simply
add entries to the property as if the call were made in the original
directory. Thus link interface libraries specified this way were always
looked up by generators in the scope of the original target rather than
in the scope that called :command:`target_link_libraries`.
CMake 3.13 now allows the :command:`target_link_libraries` command to
be called from any directory to add link dependencies and link interface
libraries to targets created in other directories. The entries are added
to :prop_tgt:`LINK_LIBRARIES` and :prop_tgt:`INTERFACE_LINK_LIBRARIES`
using a special (internal) suffix to tell the generators to look up the
names in the calling scope rather than the scope that created the target.
This policy provides compatibility with projects that already use
:command:`target_link_libraries` with the ``INTERFACE`` keyword
on a target in another directory to add :prop_tgt:`INTERFACE_LINK_LIBRARIES`
entries to be looked up in the target's directory. Such projects should
be updated to be aware of the new scoping rules in that case.
The ``OLD`` behavior of this policy is to disallow
:command:`target_link_libraries` calls naming targets from another directory
except in the previously accidentally allowed case of using the ``INTERFACE``
keyword only. The ``NEW`` behavior of this policy is to allow all such
calls but use the new scoping rules.
This policy was introduced in CMake version 3.13. CMake version
|release| warns when the policy is not set and uses ``OLD`` behavior.
Use the :command:`cmake_policy` command to set it to ``OLD`` or ``NEW``
explicitly.
.. include:: DEPRECATED.txt

View File

@@ -17,6 +17,8 @@ with the syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)`
manual for available expressions. See the :manual:`cmake-buildsystem(7)`
manual for more on defining buildsystem properties.
.. include:: LINK_LIBRARIES_INDIRECTION.txt
Creating Relocatable Packages
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@@ -15,3 +15,5 @@ Contents of ``LINK_LIBRARIES`` may use "generator expressions" with the
syntax ``$<...>``. See the :manual:`cmake-generator-expressions(7)` manual
for available expressions. See the :manual:`cmake-buildsystem(7)` manual
for more on defining buildsystem properties.
.. include:: LINK_LIBRARIES_INDIRECTION.txt

View File

@@ -0,0 +1,10 @@
.. note::
A call to :command:`target_link_libraries(<target> ...)` may update this
property on ``<target>``. If ``<target>`` was not created in the same
directory as the call then :command:`target_link_libraries` will add a
suffix of the form ``::@<directory-id>`` to each entry, where the
``::@`` is a separator and the ``<directory-id>`` is unspecified.
This tells the generators that the named libraries must be looked up in
the scope of the caller rather than in the scope in which the
``<target>`` was created. Valid directory ids are stripped on export
by the :command:`install(EXPORT)` and :command:`export` commands.

View File

@@ -0,0 +1,6 @@
out-of-dir-linking
------------------
* The :command:`target_link_libraries` command may now be called
to modify targets created outside the current directory.
See policy :policy:`CMP0079`.

View File

@@ -5648,11 +5648,38 @@ void cmGeneratorTarget::ComputeLinkImplementationLibraries(
cmGeneratorTarget::TargetOrString cmGeneratorTarget::ResolveTargetReference(
std::string const& name) const
{
cmLocalGenerator const* lg = this->LocalGenerator;
std::string const* lookupName = &name;
// When target_link_libraries() is called with a LHS target that is
// not created in the calling directory it adds a directory id suffix
// that we can use to look up the calling directory. It is that scope
// in which the item name is meaningful. This case is relatively rare
// so we allocate a separate string only when the directory id is present.
std::string::size_type pos = name.find(CMAKE_DIRECTORY_ID_SEP);
std::string plainName;
if (pos != std::string::npos) {
// We will look up the plain name without the directory id suffix.
plainName = name.substr(0, pos);
// We will look up in the scope of the directory id.
// If we do not recognize the id then leave the original
// syntax in place to produce an indicative error later.
cmDirectoryId const dirId =
name.substr(pos + sizeof(CMAKE_DIRECTORY_ID_SEP) - 1);
if (cmLocalGenerator const* otherLG =
this->GlobalGenerator->FindLocalGenerator(dirId)) {
lg = otherLG;
lookupName = &plainName;
}
}
TargetOrString resolved;
if (cmGeneratorTarget* tgt =
this->LocalGenerator->FindGeneratorTargetToUse(name)) {
if (cmGeneratorTarget* tgt = lg->FindGeneratorTargetToUse(*lookupName)) {
resolved.Target = tgt;
} else if (lookupName == &plainName) {
resolved.String = std::move(plainName);
} else {
resolved.String = name;
}

View File

@@ -1139,11 +1139,14 @@ void cmGlobalGenerator::ClearEnabledLanguages()
void cmGlobalGenerator::CreateLocalGenerators()
{
this->LocalGeneratorSearchIndex.clear();
cmDeleteAll(this->LocalGenerators);
this->LocalGenerators.clear();
this->LocalGenerators.reserve(this->Makefiles.size());
for (cmMakefile* m : this->Makefiles) {
this->LocalGenerators.push_back(this->CreateLocalGenerator(m));
cmLocalGenerator* lg = this->CreateLocalGenerator(m);
this->LocalGenerators.push_back(lg);
this->IndexLocalGenerator(lg);
}
}
@@ -1661,6 +1664,7 @@ void cmGlobalGenerator::ClearGeneratorMembers()
this->TargetSearchIndex.clear();
this->GeneratorTargetSearchIndex.clear();
this->MakefileSearchIndex.clear();
this->LocalGeneratorSearchIndex.clear();
this->ProjectMap.clear();
this->RuleHashes.clear();
this->DirectoryContentMap.clear();
@@ -2130,6 +2134,17 @@ cmMakefile* cmGlobalGenerator::FindMakefile(const std::string& start_dir) const
return nullptr;
}
cmLocalGenerator* cmGlobalGenerator::FindLocalGenerator(
cmDirectoryId const& id) const
{
LocalGeneratorMap::const_iterator i =
this->LocalGeneratorSearchIndex.find(id.String);
if (i != this->LocalGeneratorSearchIndex.end()) {
return i->second;
}
return nullptr;
}
void cmGlobalGenerator::AddAlias(const std::string& name,
std::string const& tgtName)
{
@@ -2184,6 +2199,12 @@ void cmGlobalGenerator::IndexMakefile(cmMakefile* mf)
MakefileMap::value_type(mf->GetCurrentSourceDirectory(), mf));
}
void cmGlobalGenerator::IndexLocalGenerator(cmLocalGenerator* lg)
{
cmDirectoryId id = lg->GetMakefile()->GetDirectoryId();
this->LocalGeneratorSearchIndex[id.String] = lg;
}
cmTarget* cmGlobalGenerator::FindTargetImpl(std::string const& name) const
{
TargetMap::const_iterator i = this->TargetSearchIndex.find(name);

View File

@@ -26,6 +26,9 @@
# include "cmFileLockPool.h"
#endif
#define CMAKE_DIRECTORY_ID_SEP "::@"
class cmDirectoryId;
class cmExportBuildFileGenerator;
class cmExternalMakefileProjectGenerator;
class cmGeneratorTarget;
@@ -284,6 +287,7 @@ public:
bool NameResolvesToFramework(const std::string& libname) const;
cmMakefile* FindMakefile(const std::string& start_dir) const;
cmLocalGenerator* FindLocalGenerator(cmDirectoryId const& id) const;
/** Append the subdirectory for the given configuration. If anything is
appended the given prefix and suffix will be appended around it, which
@@ -516,6 +520,7 @@ private:
typedef std::unordered_map<std::string, cmGeneratorTarget*>
GeneratorTargetMap;
typedef std::unordered_map<std::string, cmMakefile*> MakefileMap;
typedef std::unordered_map<std::string, cmLocalGenerator*> LocalGeneratorMap;
// Map efficiently from target name to cmTarget instance.
// Do not use this structure for looping over all targets.
// It contains both normal and globally visible imported targets.
@@ -527,6 +532,11 @@ private:
// It may not contain all of them (see note in IndexMakefile method).
MakefileMap MakefileSearchIndex;
// Map efficiently from source directory path to cmLocalGenerator instance.
// Do not use this structure for looping over all directories.
// Its order is not deterministic.
LocalGeneratorMap LocalGeneratorSearchIndex;
cmMakefile* TryCompileOuterMakefile;
// If you add a new map here, make sure it is copied
// in EnableLanguagesFromGenerator
@@ -585,6 +595,7 @@ private:
std::string const& reason) const;
void IndexMakefile(cmMakefile* mf);
void IndexLocalGenerator(cmLocalGenerator* lg);
virtual const char* GetBuildIgnoreErrorsFlag() const { return nullptr; }

View File

@@ -11,6 +11,7 @@
#include <iterator>
#include <memory> // IWYU pragma: keep
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
#include <utility>
@@ -48,6 +49,11 @@
class cmMessenger;
cmDirectoryId::cmDirectoryId(std::string s)
: String(std::move(s))
{
}
// default is not to be building executables
cmMakefile::cmMakefile(cmGlobalGenerator* globalGenerator,
cmStateSnapshot const& snapshot)
@@ -111,6 +117,17 @@ cmMakefile::~cmMakefile()
cmDeleteAll(this->EvaluationFiles);
}
cmDirectoryId cmMakefile::GetDirectoryId() const
{
// Use the instance pointer value to uniquely identify this directory.
// If we ever need to expose this to CMake language code we should
// add a read-only property in cmMakefile::GetProperty.
char buf[32];
sprintf(buf, "<%p>",
static_cast<void const*>(this)); // cast avoids format warning
return std::string(buf);
}
void cmMakefile::IssueMessage(cmake::MessageType t,
std::string const& text) const
{

View File

@@ -47,6 +47,14 @@ class cmTest;
class cmTestGenerator;
class cmVariableWatch;
/** A type-safe wrapper for a string representing a directory id. */
class cmDirectoryId
{
public:
cmDirectoryId(std::string s);
std::string String;
};
/** \class cmMakefile
* \brief Process the input CMakeLists.txt file.
*
@@ -75,6 +83,8 @@ public:
*/
~cmMakefile();
cmDirectoryId GetDirectoryId() const;
bool ReadListFile(const char* filename);
bool ReadDependentFile(const char* filename, bool noPolicyScope = true);

View File

@@ -230,7 +230,11 @@ class cmMakefile;
SELECT(POLICY, CMP0077, "option() honors normal variables.", 3, 13, 0, \
cmPolicies::WARN) \
SELECT(POLICY, CMP0078, "UseSWIG generates standard target names.", 3, 13, \
0, cmPolicies::WARN)
0, cmPolicies::WARN) \
SELECT( \
POLICY, CMP0079, \
"target_link_libraries allows use with targets in other directories.", 3, \
13, 0, cmPolicies::WARN)
#define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
#define CM_FOR_EACH_POLICY_ID(POLICY) \

View File

@@ -744,6 +744,13 @@ void cmTarget::GetTllSignatureTraces(std::ostream& s, TLLSignature sig) const
void cmTarget::AddLinkLibrary(cmMakefile& mf, const std::string& lib,
cmTargetLinkLibraryType llt)
{
this->AddLinkLibrary(mf, lib, lib, llt);
}
void cmTarget::AddLinkLibrary(cmMakefile& mf, std::string const& lib,
std::string const& libRef,
cmTargetLinkLibraryType llt)
{
cmTarget* tgt = mf.FindTargetToUse(lib);
{
@@ -751,14 +758,14 @@ void cmTarget::AddLinkLibrary(cmMakefile& mf, const std::string& lib,
const std::string libName =
(isNonImportedTarget && llt != GENERAL_LibraryType)
? targetNameGenex(lib)
: lib;
? targetNameGenex(libRef)
: libRef;
this->AppendProperty(
"LINK_LIBRARIES",
this->GetDebugGeneratorExpressions(libName, llt).c_str());
}
if (cmGeneratorExpression::Find(lib) != std::string::npos ||
if (cmGeneratorExpression::Find(lib) != std::string::npos || lib != libRef ||
(tgt &&
(tgt->GetType() == cmStateEnums::INTERFACE_LIBRARY ||
tgt->GetType() == cmStateEnums::OBJECT_LIBRARY)) ||

View File

@@ -142,6 +142,9 @@ public:
void AddLinkLibrary(cmMakefile& mf, const std::string& lib,
cmTargetLinkLibraryType llt);
void AddLinkLibrary(cmMakefile& mf, std::string const& lib,
std::string const& libRef, cmTargetLinkLibraryType llt);
enum TLLSignature
{
KeywordTLLSignature,

View File

@@ -359,30 +359,53 @@ bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib,
}
}
bool warnRemoteInterface = false;
bool rejectRemoteLinking = false;
bool encodeRemoteReference = false;
if (this->Makefile != this->Target->GetMakefile()) {
// The LHS target was created in another directory.
switch (this->Makefile->GetPolicyStatus(cmPolicies::CMP0079)) {
case cmPolicies::WARN:
warnRemoteInterface = true;
CM_FALLTHROUGH;
case cmPolicies::OLD:
rejectRemoteLinking = true;
break;
case cmPolicies::REQUIRED_ALWAYS:
case cmPolicies::REQUIRED_IF_USED:
case cmPolicies::NEW:
encodeRemoteReference = true;
break;
}
}
std::string libRef;
if (encodeRemoteReference && !cmSystemTools::FileIsFullPath(lib)) {
// This is a library name added by a caller that is not in the
// same directory as the target was created. Add a suffix to
// the name to tell ResolveLinkItem to look up the name in the
// caller's directory.
cmDirectoryId const dirId = this->Makefile->GetDirectoryId();
libRef = lib + CMAKE_DIRECTORY_ID_SEP + dirId.String;
} else {
// This is an absolute path or a library name added by a caller
// in the same directory as the target was created. We can use
// the original name directly.
libRef = lib;
}
// Handle normal case where the command was called with another keyword than
// INTERFACE / LINK_INTERFACE_LIBRARIES or none at all. (The "LINK_LIBRARIES"
// property of the target on the LHS shall be populated.)
if (this->CurrentProcessingState != ProcessingKeywordLinkInterface &&
this->CurrentProcessingState != ProcessingPlainLinkInterface) {
// Assure that the target on the LHS was created in the current directory.
cmTarget* t =
this->Makefile->FindLocalNonAliasTarget(this->Target->GetName());
if (!t) {
const std::vector<cmTarget*>& importedTargets =
this->Makefile->GetOwnedImportedTargets();
for (cmTarget* importedTarget : importedTargets) {
if (importedTarget->GetName() == this->Target->GetName()) {
t = importedTarget;
break;
}
}
}
if (!t) {
if (rejectRemoteLinking) {
std::ostringstream e;
e << "Attempt to add link library \"" << lib << "\" to target \""
<< this->Target->GetName()
<< "\" which is not built in this directory.";
<< "\" which is not built in this directory.\n"
<< "This is allowed only when policy CMP0079 is set to NEW.";
this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
return false;
}
@@ -404,7 +427,20 @@ bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib,
this->Makefile->IssueMessage(cmake::FATAL_ERROR, e.str());
}
this->Target->AddLinkLibrary(*this->Makefile, lib, llt);
this->Target->AddLinkLibrary(*this->Makefile, lib, libRef, llt);
}
if (warnRemoteInterface) {
std::ostringstream w;
/* clang-format off */
w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0079) << "\n"
"Target\n " << this->Target->GetName() << "\nis not created in this "
"directory. For compatibility with older versions of CMake, link "
"library\n " << lib << "\nwill be looked up in the directory in "
"which the target was created rather than in this calling "
"directory.";
/* clang-format on */
this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, w.str());
}
// Handle (additional) case where the command was called with PRIVATE /
@@ -415,9 +451,9 @@ bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib,
this->CurrentProcessingState == ProcessingPlainPrivateInterface) {
if (this->Target->GetType() == cmStateEnums::STATIC_LIBRARY) {
std::string configLib =
this->Target->GetDebugGeneratorExpressions(lib, llt);
if (cmGeneratorExpression::IsValidTargetName(lib) ||
cmGeneratorExpression::Find(lib) != std::string::npos) {
this->Target->GetDebugGeneratorExpressions(libRef, llt);
if (cmGeneratorExpression::IsValidTargetName(libRef) ||
cmGeneratorExpression::Find(libRef) != std::string::npos) {
configLib = "$<LINK_ONLY:" + configLib + ">";
}
this->Target->AppendProperty("INTERFACE_LINK_LIBRARIES",
@@ -431,7 +467,7 @@ bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib,
// property of the target on the LHS shall be populated.)
this->Target->AppendProperty(
"INTERFACE_LINK_LIBRARIES",
this->Target->GetDebugGeneratorExpressions(lib, llt).c_str());
this->Target->GetDebugGeneratorExpressions(libRef, llt).c_str());
// Stop processing if called without any keyword.
if (this->CurrentProcessingState == ProcessingLinkLibraries) {
@@ -464,12 +500,12 @@ bool cmTargetLinkLibrariesCommand::HandleLibrary(const std::string& lib,
for (std::string const& dc : debugConfigs) {
prop = "LINK_INTERFACE_LIBRARIES_";
prop += dc;
this->Target->AppendProperty(prop, lib.c_str());
this->Target->AppendProperty(prop, libRef.c_str());
}
}
if (llt == OPTIMIZED_LibraryType || llt == GENERAL_LibraryType) {
// Put in the non-DEBUG configuration interfaces.
this->Target->AppendProperty("LINK_INTERFACE_LIBRARIES", lib.c_str());
this->Target->AppendProperty("LINK_INTERFACE_LIBRARIES", libRef.c_str());
// Make sure the DEBUG configuration interfaces exist so that the
// general one will not be used as a fall-back.

View File

@@ -130,3 +130,15 @@ target_link_libraries(newsignature1 PRIVATE depC INTERFACE depD PUBLIC depB PRIV
assert_property(newsignature1 INTERFACE_LINK_LIBRARIES "depD;depB")
assert_property(newsignature1 LINK_LIBRARIES "depC;depB;subdirlib")
#----------------------------------------------------------------------------
# Test cross-directory linking.
cmake_policy(PUSH)
cmake_policy(SET CMP0079 NEW)
add_executable(TopDir TopDir.c)
add_subdirectory(SubDirA)
add_subdirectory(SubDirB)
target_link_libraries(SubDirB TopDirImported)
add_library(TopDirImported IMPORTED INTERFACE)
target_compile_definitions(TopDirImported INTERFACE DEF_TopDirImported)
cmake_policy(POP)

View File

@@ -0,0 +1,15 @@
add_executable(SubDirA SubDirA.c)
# Link to a target imported in this directory that would not normally
# be visible to the directory in which TopDir is defined.
target_link_libraries(TopDir PUBLIC SameNameImported)
# Link SubDirA to a target imported in this directory that has the same
# name as a target imported in SubDirB's directory. SubDirB will also
# tell us to link its copy. At compile time we verify both are linked.
target_link_libraries(SubDirA PRIVATE SameNameImported)
# Import a target with the same name as a target imported in SubDirB.
# Distinguish this copy by having a unique usage requirement.
add_library(SameNameImported IMPORTED INTERFACE)
target_compile_definitions(SameNameImported INTERFACE DEF_SameNameImportedSubDirA)

View File

@@ -0,0 +1,14 @@
#ifndef DEF_SameNameImportedSubDirA
# error "DEF_SameNameImportedSubDirA is not defined but should be!"
#endif
#ifndef DEF_SameNameImportedSubDirB
# error "DEF_SameNameImportedSubDirB is not defined but should be!"
#endif
#ifdef DEF_TopDirImported
# error "DEF_TopDirImported is defined but should not be!"
#endif
int main(void)
{
return 0;
}

View File

@@ -0,0 +1,15 @@
add_executable(SubDirB SubDirB.c)
# Link to a target imported in this directory that would not normally
# be visible to the directory in which TopDir is defined.
target_link_libraries(TopDir PUBLIC SameNameImported)
# Link SubDirA to a target imported in this directory that has the same
# name as a target imported in SubDirA's directory. We verify when
# compiling SubDirA that it sees our target and its own.
target_link_libraries(SubDirA PRIVATE SameNameImported)
# Import a target with the same name as a target imported in SubDirA.
# Distinguish this copy by having a unique usage requirement.
add_library(SameNameImported IMPORTED INTERFACE)
target_compile_definitions(SameNameImported INTERFACE DEF_SameNameImportedSubDirB)

View File

@@ -0,0 +1,14 @@
#ifdef DEF_SameNameImportedSubDirA
# error "DEF_SameNameImportedSubDirA is defined but should not be!"
#endif
#ifdef DEF_SameNameImportedSubDirB
# error "DEF_SameNameImportedSubDirB is defined but should not be!"
#endif
#ifndef DEF_TopDirImported
# error "DEF_TopDirImported is not defined but should be!"
#endif
int main(void)
{
return 0;
}

View File

@@ -0,0 +1,14 @@
#ifndef DEF_SameNameImportedSubDirA
# error "DEF_SameNameImportedSubDirA is not defined but should be!"
#endif
#ifndef DEF_SameNameImportedSubDirB
# error "DEF_SameNameImportedSubDirB is not defined but should be!"
#endif
#ifdef DEF_TopDirImported
# error "DEF_TopDirImported is defined but should not be!"
#endif
int main(void)
{
return 0;
}

View File

@@ -155,6 +155,13 @@ add_library(testStaticLibRequiredPrivate testStaticLibRequiredPrivate.c)
target_link_libraries(testLibDepends PRIVATE testStaticLibRequiredPrivate)
cmake_policy(POP)
cmake_policy(PUSH)
cmake_policy(SET CMP0079 NEW)
add_library(TopDirLib STATIC testTopDirLib.c)
add_subdirectory(SubDirLinkA)
add_subdirectory(SubDirLinkB)
cmake_policy(POP)
macro(add_include_lib _libName)
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/${_libName}.c" "/* no content */\n")
add_library(${_libName} "${CMAKE_CURRENT_BINARY_DIR}/${_libName}.c")
@@ -508,6 +515,7 @@ install(
testLibCycleA testLibCycleB
testLibNoSONAME
cmp0022NEW cmp0022OLD
TopDirLib SubDirLinkA
systemlib
EXPORT exp
RUNTIME DESTINATION $<1:bin>$<0:/wrong>
@@ -566,6 +574,7 @@ export(TARGETS testExe1 testLib1 testLib2 testLib3
testSharedLibRequired testSharedLibRequiredUser testSharedLibRequiredUser2
testSharedLibDepends renamed_on_export
cmp0022NEW cmp0022OLD
TopDirLib SubDirLinkA
systemlib
NAMESPACE bld_
FILE ExportBuildTree.cmake

View File

@@ -0,0 +1,6 @@
add_library(SubDirLinkAImported IMPORTED INTERFACE)
target_compile_definitions(SubDirLinkAImported INTERFACE DEF_SubDirLinkAImportedForExport)
target_link_libraries(TopDirLib PUBLIC SubDirLinkAImported)
add_library(SubDirLinkA STATIC SubDirLinkA.c)

View File

@@ -0,0 +1,11 @@
#ifdef DEF_SubDirLinkAImportedForExport
# error "DEF_SubDirLinkAImportedForExport is defined but should not be!"
#endif
#ifndef DEF_SubDirLinkBImportedForExport
# error "DEF_SubDirLinkBImportedForExport is not defined but should be!"
#endif
int testSubDirLinkA(void)
{
return 0;
}

View File

@@ -0,0 +1,4 @@
add_library(SubDirLinkBImported IMPORTED INTERFACE)
target_compile_definitions(SubDirLinkBImported INTERFACE DEF_SubDirLinkBImportedForExport)
target_link_libraries(SubDirLinkA PUBLIC SubDirLinkBImported)

View File

@@ -0,0 +1,11 @@
#ifndef DEF_SubDirLinkAImportedForExport
# error "DEF_SubDirLinkAImportedForExport is not defined but should be!"
#endif
#ifdef DEF_SubDirLinkBImportedForExport
# error "DEF_SubDirLinkBImportedForExport is defined but should not be!"
#endif
int testTopDirLib(void)
{
return 0;
}

View File

@@ -1,3 +1,9 @@
# Prepare imported targets that the exported project itself imported.
add_library(SubDirLinkAImported IMPORTED INTERFACE)
target_compile_definitions(SubDirLinkAImported INTERFACE DEF_SubDirLinkAImportedForImport)
add_library(SubDirLinkBImported IMPORTED INTERFACE)
target_compile_definitions(SubDirLinkBImported INTERFACE DEF_SubDirLinkBImportedForImport)
# Import targets from the exported build tree.
include(${Import_BINARY_DIR}/../Export/ExportBuildTree.cmake)
@@ -158,6 +164,11 @@ target_link_libraries(cmp0022OLD_exp_test exp_cmp0022OLD)
add_executable(cmp0022NEW_exp_test cmp0022NEW_test_vs6_2.cpp)
target_link_libraries(cmp0022NEW_exp_test exp_cmp0022NEW)
add_executable(SubDirLink_bld SubDirLink.c)
target_link_libraries(SubDirLink_bld PRIVATE bld_TopDirLib bld_SubDirLinkA)
add_executable(SubDirLink_exp SubDirLink.c)
target_link_libraries(SubDirLink_exp PRIVATE exp_TopDirLib exp_SubDirLinkA)
# Try building a plugin to an executable imported from the build tree.
add_library(imp_mod1b MODULE imp_mod1.c)
target_link_libraries(imp_mod1b bld_testExe2)

View File

@@ -0,0 +1,14 @@
#ifndef DEF_SubDirLinkAImportedForImport
# error "DEF_SubDirLinkAImportedForImport is not defined but should be!"
#endif
#ifndef DEF_SubDirLinkBImportedForImport
# error "DEF_SubDirLinkBImportedForImport is not defined but should be!"
#endif
extern int testTopDirLib(void);
extern int testSubDirLinkA(void);
int main(void)
{
return (testTopDirLib() + testSubDirLinkA() + 0);
}

View File

@@ -0,0 +1 @@
-- INTERFACE_LINK_LIBRARIES='foo::@<[Xx0-9A-Fa-f]+>'

View File

@@ -0,0 +1,2 @@
cmake_policy(SET CMP0079 NEW)
include(CMP0079-iface-common.cmake)

View File

@@ -0,0 +1 @@
-- INTERFACE_LINK_LIBRARIES='foo'

View File

@@ -0,0 +1,2 @@
cmake_policy(SET CMP0079 OLD)
include(CMP0079-iface-common.cmake)

View File

@@ -0,0 +1,17 @@
^CMake Warning \(dev\) at CMP0079-iface/CMakeLists.txt:[0-9]+ \(target_link_libraries\):
Policy CMP0079 is not set: target_link_libraries allows use with targets in
other directories. Run "cmake --help-policy CMP0079" for policy details.
Use the cmake_policy command to set the policy and suppress this warning.
Target
top
is not created in this directory. For compatibility with older versions of
CMake, link library
foo
will be looked up in the directory in which the target was created rather
than in this calling directory.
This warning is for project developers. Use -Wno-dev to suppress it.$

View File

@@ -0,0 +1 @@
-- INTERFACE_LINK_LIBRARIES='foo'

View File

@@ -0,0 +1 @@
include(CMP0079-iface-common.cmake)

View File

@@ -0,0 +1,6 @@
enable_language(C)
add_executable(top empty.c)
add_subdirectory(CMP0079-iface)
get_property(libs TARGET top PROPERTY INTERFACE_LINK_LIBRARIES)
message(STATUS "INTERFACE_LINK_LIBRARIES='${libs}'")

View File

@@ -0,0 +1 @@
target_link_libraries(top INTERFACE foo)

View File

@@ -0,0 +1,6 @@
^CMake Error at CMP0079-link-NEW-bogus.cmake:[0-9]+ \(add_executable\):
Target "top" links to target "foo::@<0xdeadbeef>" but the target was not
found. Perhaps a find_package\(\) call is missing for an IMPORTED target, or
an ALIAS target is missing\?
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@@ -0,0 +1,6 @@
cmake_policy(SET CMP0028 NEW)
cmake_policy(SET CMP0079 NEW)
enable_language(C)
add_executable(top empty.c)
set_property(TARGET top APPEND PROPERTY LINK_LIBRARIES "foo::@<0xdeadbeef>")

View File

@@ -0,0 +1 @@
-- LINK_LIBRARIES='foo::@<[Xx0-9A-Fa-f]+>'

View File

@@ -0,0 +1,2 @@
cmake_policy(SET CMP0079 NEW)
include(CMP0079-link-common.cmake)

View File

@@ -0,0 +1 @@
1

View File

@@ -0,0 +1,5 @@
^CMake Error at CMP0079-link/CMakeLists.txt:[0-9]+ \(target_link_libraries\):
Attempt to add link library "foo" to target "top" which is not built in
this directory.
This is allowed only when policy CMP0079 is set to NEW.$

View File

@@ -0,0 +1,2 @@
cmake_policy(SET CMP0079 OLD)
include(CMP0079-link-common.cmake)

View File

@@ -0,0 +1,5 @@
^CMake Error at CMP0079-link/CMakeLists.txt:[0-9]+ \(target_link_libraries\):
Attempt to add link library "foo" to target "top" which is not built in
this directory.
This is allowed only when policy CMP0079 is set to NEW.$

View File

@@ -0,0 +1 @@
include(CMP0079-link-common.cmake)

View File

@@ -0,0 +1,6 @@
enable_language(C)
add_executable(top empty.c)
add_subdirectory(CMP0079-link)
get_property(libs TARGET top PROPERTY LINK_LIBRARIES)
message(STATUS "LINK_LIBRARIES='${libs}'")

View File

@@ -0,0 +1 @@
target_link_libraries(top PUBLIC foo)

View File

@@ -1,3 +1,3 @@
cmake_minimum_required(VERSION 2.8.4)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)
include(${RunCMake_TEST}.cmake NO_POLICY_SCOPE)

View File

@@ -4,11 +4,17 @@ run_cmake(CMP0023-WARN)
run_cmake(CMP0023-NEW)
run_cmake(CMP0023-WARN-2)
run_cmake(CMP0023-NEW-2)
run_cmake(CMP0079-iface-WARN)
run_cmake(CMP0079-iface-OLD)
run_cmake(CMP0079-iface-NEW)
run_cmake(CMP0079-link-WARN)
run_cmake(CMP0079-link-OLD)
run_cmake(CMP0079-link-NEW)
run_cmake(CMP0079-link-NEW-bogus)
run_cmake(ImportedTarget)
run_cmake(ImportedTargetFailure)
run_cmake(MixedSignature)
run_cmake(Separate-PRIVATE-LINK_PRIVATE-uses)
run_cmake(SubDirTarget)
run_cmake(SharedDepNotTarget)
run_cmake(StaticPrivateDepNotExported)
run_cmake(StaticPrivateDepNotTarget)

View File

@@ -1,5 +0,0 @@
^CMake Error at SubDirTarget.cmake:[0-9]+ \(target_link_libraries\):
Attempt to add link library "m" to target "subexe" which is not built in
this directory.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)$

View File

@@ -1,3 +0,0 @@
enable_language(C)
add_subdirectory(SubDirTarget)
target_link_libraries(subexe m)

View File

@@ -1 +0,0 @@
add_executable(subexe ../empty.c)