Link properties: must be transitive over private dependency on static library

Fixes: #20022
This commit is contained in:
Marc Chevrier 2019-12-06 17:01:48 +01:00 committed by Brad King
parent a2c0c2d024
commit bbba701899
25 changed files with 192 additions and 18 deletions

View File

@ -57,6 +57,7 @@ Policies Introduced by CMake 3.17
.. toctree::
:maxdepth: 1
CMP0099: Link properties are transitive over private dependency on static libraries. </policy/CMP0099>
CMP0098: FindFLEX runs flex in CMAKE_CURRENT_BINARY_DIR when executing. </policy/CMP0098>
Policies Introduced by CMake 3.16

24
Help/policy/CMP0099.rst Normal file
View File

@ -0,0 +1,24 @@
CMP0099
-------
Target link properties :prop_tgt:`INTERFACE_LINK_OPTIONS`,
:prop_tgt:`INTERFACE_LINK_DIRECTORIES` and :prop_tgt:`INTERFACE_LINK_DEPENDS`
are now transitive over private dependencies of static libraries.
In CMake 3.16 and below the interface link properties attached to libraries
are not propagated for private dependencies of static libraries.
Only the libraries themselves are propagated to link the dependent binary.
CMake 3.17 and later prefer to propagate all interface link properties.
This policy provides compatibility for projects that have not been updated
to expect the new behavior.
The ``OLD`` behavior for this policy is to not propagate interface link
properties. The ``NEW`` behavior of this policy is to propagate interface link
properties.
This policy was introduced in CMake version 3.17. Use the
:command:`cmake_policy` command to set it to ``OLD`` or ``NEW`` explicitly.
Unlike many policies, CMake version |release| does *not* warn
when this policy is not set and simply uses ``OLD`` behavior.
.. include:: DEPRECATED.txt

View File

@ -0,0 +1,8 @@
Link-properties-transitive
--------------------------
* Target link properties :prop_tgt:`INTERFACE_LINK_OPTIONS`,
:prop_tgt:`INTERFACE_LINK_DIRECTORIES` and
:prop_tgt:`INTERFACE_LINK_DEPENDS` are now transitive over private
dependency on static libraries.
See policy :policy:`CMP0099`.

View File

@ -1116,7 +1116,8 @@ bool cmGeneratorTarget::GetPropertyAsBool(const std::string& prop) const
}
bool cmGeneratorTarget::MaybeHaveInterfaceProperty(
std::string const& prop, cmGeneratorExpressionContext* context) const
std::string const& prop, cmGeneratorExpressionContext* context,
bool usage_requirements_only) const
{
std::string const key = prop + '@' + context->Config;
auto i = this->MaybeInterfacePropertyExists.find(key);
@ -1135,7 +1136,7 @@ bool cmGeneratorTarget::MaybeHaveInterfaceProperty(
context->HeadTarget ? context->HeadTarget : this;
if (cmLinkInterfaceLibraries const* iface =
this->GetLinkInterfaceLibraries(context->Config, headTarget,
true)) {
usage_requirements_only)) {
if (iface->HadHeadSensitiveCondition) {
// With a different head target we may get to a library with
// this interface property.
@ -1145,7 +1146,8 @@ bool cmGeneratorTarget::MaybeHaveInterfaceProperty(
// head target, so we can follow them.
for (cmLinkItem const& lib : iface->Libraries) {
if (lib.Target &&
lib.Target->MaybeHaveInterfaceProperty(prop, context)) {
lib.Target->MaybeHaveInterfaceProperty(
prop, context, usage_requirements_only)) {
maybeInterfaceProp = true;
break;
}
@ -1159,12 +1161,14 @@ bool cmGeneratorTarget::MaybeHaveInterfaceProperty(
std::string cmGeneratorTarget::EvaluateInterfaceProperty(
std::string const& prop, cmGeneratorExpressionContext* context,
cmGeneratorExpressionDAGChecker* dagCheckerParent) const
cmGeneratorExpressionDAGChecker* dagCheckerParent,
bool usage_requirements_only) const
{
std::string result;
// If the property does not appear transitively at all, we are done.
if (!this->MaybeHaveInterfaceProperty(prop, context)) {
if (!this->MaybeHaveInterfaceProperty(prop, context,
usage_requirements_only)) {
return result;
}
@ -1196,8 +1200,8 @@ std::string cmGeneratorTarget::EvaluateInterfaceProperty(
p, context->LG, context, headTarget, &dagChecker, this);
}
if (cmLinkInterfaceLibraries const* iface =
this->GetLinkInterfaceLibraries(context->Config, headTarget, true)) {
if (cmLinkInterfaceLibraries const* iface = this->GetLinkInterfaceLibraries(
context->Config, headTarget, usage_requirements_only)) {
for (cmLinkItem const& lib : iface->Libraries) {
// Broken code can have a target in its own link interface.
// Don't follow such link interface entries so as not to create a
@ -1240,7 +1244,8 @@ void AddInterfaceEntries(cmGeneratorTarget const* headTarget,
std::string const& config, std::string const& prop,
std::string const& lang,
cmGeneratorExpressionDAGChecker* dagChecker,
std::vector<EvaluatedTargetPropertyEntry>& entries)
std::vector<EvaluatedTargetPropertyEntry>& entries,
bool usage_requirements_only = true)
{
if (cmLinkImplementationLibraries const* impl =
headTarget->GetLinkImplementationLibraries(config)) {
@ -1253,9 +1258,9 @@ void AddInterfaceEntries(cmGeneratorTarget const* headTarget,
cmGeneratorExpressionContext context(
headTarget->GetLocalGenerator(), config, false, headTarget,
headTarget, true, lib.Backtrace, lang);
cmExpandList(
lib.Target->EvaluateInterfaceProperty(prop, &context, dagChecker),
ee.Values);
cmExpandList(lib.Target->EvaluateInterfaceProperty(
prop, &context, dagChecker, usage_requirements_only),
ee.Values);
ee.ContextDependent = context.HadContextSensitiveCondition;
entries.emplace_back(std::move(ee));
}
@ -3663,7 +3668,8 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetLinkOptions(
this->LinkOptionsEntries);
AddInterfaceEntries(this, config, "INTERFACE_LINK_OPTIONS", language,
&dagChecker, entries);
&dagChecker, entries,
this->GetPolicyStatusCMP0099() != cmPolicies::NEW);
processOptions(this, entries, result, uniqueOptions, debugOptions,
"link options", OptionsParse::Shell);
@ -3918,7 +3924,8 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetLinkDirectories(
this->LinkDirectoriesEntries);
AddInterfaceEntries(this, config, "INTERFACE_LINK_DIRECTORIES", language,
&dagChecker, entries);
&dagChecker, entries,
this->GetPolicyStatusCMP0099() != cmPolicies::NEW);
processLinkDirectories(this, entries, result, uniqueDirectories,
debugDirectories);
@ -3956,7 +3963,8 @@ std::vector<BT<std::string>> cmGeneratorTarget::GetLinkDepends(
}
}
AddInterfaceEntries(this, config, "INTERFACE_LINK_DEPENDS", language,
&dagChecker, entries);
&dagChecker, entries,
this->GetPolicyStatusCMP0099() != cmPolicies::NEW);
processOptions(this, entries, result, uniqueOptions, false, "link depends",
OptionsParse::None);

View File

@ -707,7 +707,8 @@ public:
std::string EvaluateInterfaceProperty(
std::string const& prop, cmGeneratorExpressionContext* context,
cmGeneratorExpressionDAGChecker* dagCheckerParent) const;
cmGeneratorExpressionDAGChecker* dagCheckerParent,
bool usage_requirements_only = true) const;
bool HaveInstallTreeRPATH(const std::string& config) const;
@ -886,7 +887,8 @@ private:
mutable std::unordered_map<std::string, bool> MaybeInterfacePropertyExists;
bool MaybeHaveInterfaceProperty(std::string const& prop,
cmGeneratorExpressionContext* context) const;
cmGeneratorExpressionContext* context,
bool usage_requirements_only) const;
using TargetPropertyEntryVector =
std::vector<std::unique_ptr<TargetPropertyEntry>>;

View File

@ -293,7 +293,11 @@ class cmMakefile;
3, 16, 0, cmPolicies::WARN) \
SELECT(POLICY, CMP0098, \
"FindFLEX runs flex in CMAKE_CURRENT_BINARY_DIR when executing.", 3, \
17, 0, cmPolicies::WARN)
17, 0, cmPolicies::WARN) \
SELECT(POLICY, CMP0099, \
"Link properties are transitive over private dependency on static " \
"libraries.", \
3, 17, 0, cmPolicies::WARN)
#define CM_SELECT_ID(F, A1, A2, A3, A4, A5, A6) F(A1)
#define CM_FOR_EACH_POLICY_ID(POLICY) \
@ -322,7 +326,8 @@ class cmMakefile;
F(CMP0076) \
F(CMP0081) \
F(CMP0083) \
F(CMP0095)
F(CMP0095) \
F(CMP0099)
/** \class cmPolicies
* \brief Handles changes in CMake behavior and policies

View File

@ -28,6 +28,7 @@
\* CMP0081
\* CMP0083
\* CMP0095
\* CMP0099
Call Stack \(most recent call first\):
CMakeLists.txt:3 \(include\)

View File

@ -0,0 +1,4 @@
if (NOT actual_stdout MATCHES "DIR_INTERFACE")
string (APPEND RunCMake_TEST_FAILED "\nNot found expected 'DIR_INTERFACE'.")
endif()

View File

@ -0,0 +1,4 @@
cmake_policy(SET CMP0099 NEW)
include(${CMAKE_CURRENT_SOURCE_DIR}/CMP0099.cmake)

View File

@ -0,0 +1,4 @@
if (actual_stdout MATCHES "DIR_INTERFACE")
string (APPEND RunCMake_TEST_FAILED "\nFound unexpected 'DIR_INTERFACE'.")
endif()

View File

@ -0,0 +1,4 @@
cmake_policy(SET CMP0099 OLD)
include(${CMAKE_CURRENT_SOURCE_DIR}/CMP0099.cmake)

View File

@ -0,0 +1,14 @@
enable_language(C)
set(CMAKE_VERBOSE_MAKEFILE TRUE)
set(CMAKE_C_USE_RESPONSE_FILE_FOR_LIBRARIES FALSE)
add_library(LinkDirs_interface INTERFACE)
target_link_directories (LinkDirs_interface INTERFACE "/DIR_INTERFACE"})
add_library(LinkDirs_static STATIC lib.c)
target_link_libraries (LinkDirs_static PRIVATE LinkDirs_interface)
add_executable(LinkDirs_exe exe.c)
target_link_libraries (LinkDirs_exe PRIVATE LinkDirs_static)

View File

@ -1,3 +1,33 @@
include(RunCMake)
macro(run_cmake_target test subtest target)
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test}-build)
set(RunCMake_TEST_NO_CLEAN 1)
run_cmake_command(${test}-${subtest} ${CMAKE_COMMAND} --build . --target ${target} ${ARGN})
unset(RunCMake_TEST_BINARY_DIR)
unset(RunCMake_TEST_NO_CLEAN)
endmacro()
run_cmake(empty_keyword_args)
if(RunCMake_GENERATOR MATCHES "(Ninja|Makefiles)" AND
NOT RunCMake_GENERATOR MATCHES "(NMake|Borland)")
set(RunCMake_TEST_OUTPUT_MERGE TRUE)
if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Release)
endif()
if (RunCMake_GENERATOR MATCHES "Ninja")
set(VERBOSE -- -v)
endif()
run_cmake(CMP0099-NEW)
run_cmake_target(CMP0099-NEW basic LinkDirs_exe ${VERBOSE})
run_cmake(CMP0099-OLD)
run_cmake_target(CMP0099-OLD basic LinkDirs_exe ${VERBOSE})
unset(RunCMake_TEST_OPTIONS)
unset(RunCMake_TEST_OUTPUT_MERGE)
endif()

View File

@ -0,0 +1,4 @@
int main(void)
{
return 0;
}

View File

@ -0,0 +1,7 @@
#if defined(_WIN32)
__declspec(dllexport)
#endif
int flags_lib(void)
{
return 0;
}

View File

@ -0,0 +1,4 @@
if (NOT actual_stdout MATCHES "BADFLAG_INTERFACE")
string (APPEND RunCMake_TEST_FAILED "\nNot found expected 'BADFLAG_INTERFACE'.")
endif()

View File

@ -0,0 +1 @@
.*

View File

@ -0,0 +1,4 @@
cmake_policy(SET CMP0099 NEW)
include(${CMAKE_CURRENT_SOURCE_DIR}/CMP0099.cmake)

View File

@ -0,0 +1,4 @@
if (actual_stdout MATCHES "BADFLAG_INTERFACE")
string (APPEND RunCMake_TEST_FAILED "\nFound unexpected 'BADFLAG_INTERFACE'.")
endif()

View File

@ -0,0 +1 @@
.*

View File

@ -0,0 +1,4 @@
cmake_policy(SET CMP0099 OLD)
include(${CMAKE_CURRENT_SOURCE_DIR}/CMP0099.cmake)

View File

@ -0,0 +1,16 @@
enable_language(C)
set(obj "${CMAKE_C_OUTPUT_EXTENSION}")
if(BORLAND)
set(pre -)
endif()
add_library(LinkOptions_interface INTERFACE)
target_link_options (LinkOptions_interface INTERFACE ${pre}BADFLAG_INTERFACE${obj})
add_library(LinkOptions_static STATIC LinkOptionsLib.c)
target_link_libraries (LinkOptions_static PRIVATE LinkOptions_interface)
add_executable(LinkOptions_exe LinkOptionsExe.c)
target_link_libraries (LinkOptions_exe PRIVATE LinkOptions_static)

View File

@ -41,3 +41,21 @@ if(RunCMake_GENERATOR MATCHES "(Ninja|Makefile)")
endif()
run_cmake(empty_keyword_args)
if (NOT CMAKE_C_COMPILER_ID STREQUAL "Intel")
# Intel compiler does not reject bad flags or objects!
set(RunCMake_TEST_OUTPUT_MERGE TRUE)
if (NOT RunCMake_GENERATOR_IS_MULTI_CONFIG)
set(RunCMake_TEST_OPTIONS -DCMAKE_BUILD_TYPE=Release)
endif()
run_cmake(CMP0099-NEW)
run_cmake_target(CMP0099-NEW basic LinkOptions_exe)
run_cmake(CMP0099-OLD)
run_cmake_target(CMP0099-OLD basic LinkOptions_exe)
unset(RunCMake_TEST_OPTIONS)
unset(RunCMake_TEST_OUTPUT_MERGE)
endif()