mirror of
https://github.com/Kitware/CMake.git
synced 2026-01-04 21:00:17 -06:00
LINK_DEPENDS: add support for property INTERFACE_LINK_DEPENDS
Fixes: #17997
This commit is contained in:
@@ -198,6 +198,7 @@ Properties on Targets
|
||||
/prop_tgt/IMPORTED_LIBNAME_CONFIG
|
||||
/prop_tgt/IMPORTED_LIBNAME
|
||||
/prop_tgt/IMPORTED_LINK_DEPENDENT_LIBRARIES_CONFIG
|
||||
/prop_tgt/IMPORTED_LINK_DEPENDENT_LIBRARIES_CONFIG
|
||||
/prop_tgt/IMPORTED_LINK_DEPENDENT_LIBRARIES
|
||||
/prop_tgt/IMPORTED_LINK_INTERFACE_LANGUAGES_CONFIG
|
||||
/prop_tgt/IMPORTED_LINK_INTERFACE_LANGUAGES
|
||||
@@ -225,6 +226,7 @@ Properties on Targets
|
||||
/prop_tgt/INTERFACE_COMPILE_FEATURES
|
||||
/prop_tgt/INTERFACE_COMPILE_OPTIONS
|
||||
/prop_tgt/INTERFACE_INCLUDE_DIRECTORIES
|
||||
/prop_tgt/INTERFACE_LINK_DEPENDS
|
||||
/prop_tgt/INTERFACE_LINK_LIBRARIES
|
||||
/prop_tgt/INTERFACE_LINK_OPTIONS
|
||||
/prop_tgt/INTERFACE_POSITION_INDEPENDENT_CODE
|
||||
|
||||
31
Help/prop_tgt/INTERFACE_LINK_DEPENDS.rst
Normal file
31
Help/prop_tgt/INTERFACE_LINK_DEPENDS.rst
Normal file
@@ -0,0 +1,31 @@
|
||||
INTERFACE_LINK_DEPENDS
|
||||
----------------------
|
||||
|
||||
Additional public interface files on which a target binary depends for linking.
|
||||
|
||||
This property is supported only by Makefile and Ninja generators. It is
|
||||
intended to specify dependencies on "linker scripts" for custom Makefile link
|
||||
rules.
|
||||
|
||||
When target dependencies are specified using :command:`target_link_libraries`,
|
||||
CMake will read this property from all target dependencies to determine the
|
||||
build properties of the consumer.
|
||||
|
||||
Contents of ``INTERFACE_LINK_DEPENDS`` 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.
|
||||
|
||||
Link dependency files usage requirements commonly differ between the build-tree
|
||||
and the install-tree. The ``BUILD_INTERFACE`` and ``INSTALL_INTERFACE``
|
||||
generator expressions can be used to describe separate usage requirements
|
||||
based on the usage location. Relative paths are allowed within the
|
||||
``INSTALL_INTERFACE`` expression and are interpreted relative to the
|
||||
installation prefix. For example:
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
set_property(TARGET mylib PROPERTY INTERFACE_LINK_DEPENDS
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/mylinkscript>
|
||||
$<INSTALL_INTERFACE:mylinkscript> # <prefix>/mylinkscript
|
||||
)
|
||||
@@ -7,6 +7,6 @@ Specifies a semicolon-separated list of full-paths to files on which
|
||||
the link rule for this target depends. The target binary will be
|
||||
linked if any of the named files is newer than it.
|
||||
|
||||
This property is ignored by non-Makefile generators. It is intended
|
||||
to specify dependencies on "linker scripts" for custom Makefile link
|
||||
This property is supported only by Makefile and Ninja generators. It is
|
||||
intended to specify dependencies on "linker scripts" for custom Makefile link
|
||||
rules.
|
||||
|
||||
4
Help/release/dev/INTERFACE_LINK_DEPENDS-property.rst
Normal file
4
Help/release/dev/INTERFACE_LINK_DEPENDS-property.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
INTERFACE_LINK_DEPENDS-property
|
||||
-------------------------------
|
||||
|
||||
* Binary targets gained new :prop_tgt:`INTERFACE_LINK_DEPENDS` property.
|
||||
@@ -98,6 +98,9 @@ bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
|
||||
this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", gte,
|
||||
cmGeneratorExpression::BuildInterface,
|
||||
properties, missingTargets);
|
||||
this->PopulateInterfaceProperty("INTERFACE_LINK_DEPENDS", gte,
|
||||
cmGeneratorExpression::BuildInterface,
|
||||
properties, missingTargets);
|
||||
this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE", gte,
|
||||
properties);
|
||||
|
||||
|
||||
@@ -420,6 +420,37 @@ void cmExportFileGenerator::PopulateIncludeDirectoriesInterface(
|
||||
}
|
||||
}
|
||||
|
||||
void cmExportFileGenerator::PopulateLinkDependsInterface(
|
||||
cmTargetExport* tei, cmGeneratorExpression::PreprocessContext preprocessRule,
|
||||
ImportPropertyMap& properties, std::vector<std::string>& missingTargets)
|
||||
{
|
||||
cmGeneratorTarget* gt = tei->Target;
|
||||
assert(preprocessRule == cmGeneratorExpression::InstallInterface);
|
||||
|
||||
const char* propName = "INTERFACE_LINK_DEPENDS";
|
||||
const char* input = gt->GetProperty(propName);
|
||||
|
||||
if (!input) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!*input) {
|
||||
properties[propName].clear();
|
||||
return;
|
||||
}
|
||||
|
||||
std::string prepro =
|
||||
cmGeneratorExpression::Preprocess(input, preprocessRule, true);
|
||||
if (!prepro.empty()) {
|
||||
this->ResolveTargetsInGeneratorExpressions(prepro, gt, missingTargets);
|
||||
|
||||
if (!checkInterfaceDirs(prepro, gt, propName)) {
|
||||
return;
|
||||
}
|
||||
properties[propName] = prepro;
|
||||
}
|
||||
}
|
||||
|
||||
void cmExportFileGenerator::PopulateInterfaceProperty(
|
||||
const std::string& propName, cmGeneratorTarget* target,
|
||||
cmGeneratorExpression::PreprocessContext preprocessRule,
|
||||
|
||||
@@ -147,6 +147,10 @@ protected:
|
||||
cmTargetExport* target,
|
||||
cmGeneratorExpression::PreprocessContext preprocessRule,
|
||||
ImportPropertyMap& properties, std::vector<std::string>& missingTargets);
|
||||
void PopulateLinkDependsInterface(
|
||||
cmTargetExport* target,
|
||||
cmGeneratorExpression::PreprocessContext preprocessRule,
|
||||
ImportPropertyMap& properties, std::vector<std::string>& missingTargets);
|
||||
|
||||
void SetImportLinkInterface(
|
||||
const std::string& config, std::string const& suffix,
|
||||
|
||||
@@ -106,6 +106,8 @@ bool cmExportInstallFileGenerator::GenerateMainFile(std::ostream& os)
|
||||
this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", gt,
|
||||
cmGeneratorExpression::InstallInterface,
|
||||
properties, missingTargets);
|
||||
this->PopulateLinkDependsInterface(
|
||||
te, cmGeneratorExpression::InstallInterface, properties, missingTargets);
|
||||
|
||||
std::string errorMessage;
|
||||
if (!this->PopulateExportProperties(gt, properties, errorMessage)) {
|
||||
|
||||
@@ -26,7 +26,8 @@ struct cmGeneratorExpressionContext;
|
||||
SELECT(F, EvaluatingAutoUicOptions, AUTOUIC_OPTIONS) \
|
||||
SELECT(F, EvaluatingSources, SOURCES) \
|
||||
SELECT(F, EvaluatingCompileFeatures, COMPILE_FEATURES) \
|
||||
SELECT(F, EvaluatingLinkOptions, LINK_OPTIONS)
|
||||
SELECT(F, EvaluatingLinkOptions, LINK_OPTIONS) \
|
||||
SELECT(F, EvaluatingLinkDepends, LINK_DEPENDS)
|
||||
|
||||
#define CM_FOR_EACH_TRANSITIVE_PROPERTY(F) \
|
||||
CM_FOR_EACH_TRANSITIVE_PROPERTY_IMPL(F, CM_SELECT_BOTH)
|
||||
|
||||
@@ -3008,6 +3008,42 @@ void cmGeneratorTarget::GetLinkOptions(std::vector<std::string>& result,
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
void processLinkDepends(
|
||||
cmGeneratorTarget const* tgt,
|
||||
const std::vector<cmGeneratorTarget::TargetPropertyEntry*>& entries,
|
||||
std::vector<std::string>& options,
|
||||
std::unordered_set<std::string>& uniqueOptions,
|
||||
cmGeneratorExpressionDAGChecker* dagChecker, const std::string& config,
|
||||
std::string const& language)
|
||||
{
|
||||
processOptionsInternal(tgt, entries, options, uniqueOptions, dagChecker,
|
||||
config, false, "link depends", language,
|
||||
OptionsParse::None);
|
||||
}
|
||||
}
|
||||
|
||||
void cmGeneratorTarget::GetLinkDepends(std::vector<std::string>& result,
|
||||
const std::string& config,
|
||||
const std::string& language) const
|
||||
{
|
||||
if (const char* linkDepends = this->GetProperty("LINK_DEPENDS")) {
|
||||
cmSystemTools::ExpandListArgument(linkDepends, result);
|
||||
}
|
||||
|
||||
std::unordered_set<std::string> uniqueOptions;
|
||||
std::vector<cmGeneratorTarget::TargetPropertyEntry*> linkDependsEntries;
|
||||
cmGeneratorExpressionDAGChecker dagChecker(this->GetName(), "LINK_DEPENDS",
|
||||
nullptr, nullptr);
|
||||
|
||||
AddInterfaceEntries(this, config, "INTERFACE_LINK_DEPENDS",
|
||||
linkDependsEntries);
|
||||
processLinkDepends(this, linkDependsEntries, result, uniqueOptions,
|
||||
&dagChecker, config, language);
|
||||
|
||||
cmDeleteAll(linkDependsEntries);
|
||||
}
|
||||
|
||||
void cmGeneratorTarget::ComputeTargetManifest(const std::string& config) const
|
||||
{
|
||||
if (this->IsImported()) {
|
||||
|
||||
@@ -422,6 +422,10 @@ public:
|
||||
const std::string& config,
|
||||
const std::string& language) const;
|
||||
|
||||
void GetLinkDepends(std::vector<std::string>& result,
|
||||
const std::string& config,
|
||||
const std::string& language) const;
|
||||
|
||||
bool IsSystemIncludeDirectory(const std::string& dir,
|
||||
const std::string& config,
|
||||
const std::string& language) const;
|
||||
|
||||
@@ -97,15 +97,15 @@ void cmMakefileExecutableTargetGenerator::WriteDeviceExecutableRule(
|
||||
|
||||
std::vector<std::string> commands;
|
||||
|
||||
// Build list of dependencies.
|
||||
std::vector<std::string> depends;
|
||||
this->AppendLinkDepends(depends);
|
||||
|
||||
// Get the language to use for linking this library.
|
||||
std::string linkLanguage = "CUDA";
|
||||
std::string const objExt =
|
||||
this->Makefile->GetSafeDefinition("CMAKE_CUDA_OUTPUT_EXTENSION");
|
||||
|
||||
// Build list of dependencies.
|
||||
std::vector<std::string> depends;
|
||||
this->AppendLinkDepends(depends, linkLanguage);
|
||||
|
||||
// Get the name of the device object to generate.
|
||||
std::string const targetOutputReal =
|
||||
this->GeneratorTarget->ObjectDirectory + "cmake_device_link" + objExt;
|
||||
@@ -294,13 +294,6 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink)
|
||||
{
|
||||
std::vector<std::string> commands;
|
||||
|
||||
// Build list of dependencies.
|
||||
std::vector<std::string> depends;
|
||||
this->AppendLinkDepends(depends);
|
||||
if (!this->DeviceLinkObject.empty()) {
|
||||
depends.push_back(this->DeviceLinkObject);
|
||||
}
|
||||
|
||||
// Get the name of the executable to generate.
|
||||
std::string targetName;
|
||||
std::string targetNameReal;
|
||||
@@ -378,6 +371,13 @@ void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink)
|
||||
return;
|
||||
}
|
||||
|
||||
// Build list of dependencies.
|
||||
std::vector<std::string> depends;
|
||||
this->AppendLinkDepends(depends, linkLanguage);
|
||||
if (!this->DeviceLinkObject.empty()) {
|
||||
depends.push_back(this->DeviceLinkObject);
|
||||
}
|
||||
|
||||
this->NumberOfProgressActions++;
|
||||
if (!this->NoRuleMessages) {
|
||||
cmLocalUnixMakefileGenerator3::EchoProgress progress;
|
||||
|
||||
@@ -260,15 +260,15 @@ void cmMakefileLibraryTargetGenerator::WriteDeviceLibraryRules(
|
||||
// code duplication.
|
||||
std::vector<std::string> commands;
|
||||
|
||||
// Build list of dependencies.
|
||||
std::vector<std::string> depends;
|
||||
this->AppendLinkDepends(depends);
|
||||
|
||||
// Get the language to use for linking this library.
|
||||
std::string linkLanguage = "CUDA";
|
||||
std::string const objExt =
|
||||
this->Makefile->GetSafeDefinition("CMAKE_CUDA_OUTPUT_EXTENSION");
|
||||
|
||||
// Build list of dependencies.
|
||||
std::vector<std::string> depends;
|
||||
this->AppendLinkDepends(depends, linkLanguage);
|
||||
|
||||
// Create set of linking flags.
|
||||
std::string linkFlags;
|
||||
this->GetTargetLinkFlags(linkFlags, linkLanguage);
|
||||
@@ -444,13 +444,6 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
|
||||
// code duplication.
|
||||
std::vector<std::string> commands;
|
||||
|
||||
// Build list of dependencies.
|
||||
std::vector<std::string> depends;
|
||||
this->AppendLinkDepends(depends);
|
||||
if (!this->DeviceLinkObject.empty()) {
|
||||
depends.push_back(this->DeviceLinkObject);
|
||||
}
|
||||
|
||||
// Get the language to use for linking this library.
|
||||
std::string linkLanguage =
|
||||
this->GeneratorTarget->GetLinkerLanguage(this->ConfigName);
|
||||
@@ -462,6 +455,13 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
|
||||
return;
|
||||
}
|
||||
|
||||
// Build list of dependencies.
|
||||
std::vector<std::string> depends;
|
||||
this->AppendLinkDepends(depends, linkLanguage);
|
||||
if (!this->DeviceLinkObject.empty()) {
|
||||
depends.push_back(this->DeviceLinkObject);
|
||||
}
|
||||
|
||||
// Create set of linking flags.
|
||||
std::string linkFlags;
|
||||
this->LocalGenerator->AppendFlags(linkFlags, extraFlags);
|
||||
|
||||
@@ -1387,7 +1387,7 @@ void cmMakefileTargetGenerator::AppendObjectDepends(
|
||||
}
|
||||
|
||||
void cmMakefileTargetGenerator::AppendLinkDepends(
|
||||
std::vector<std::string>& depends)
|
||||
std::vector<std::string>& depends, const std::string& linkLanguage)
|
||||
{
|
||||
this->AppendObjectDepends(depends);
|
||||
|
||||
@@ -1411,10 +1411,8 @@ void cmMakefileTargetGenerator::AppendLinkDepends(
|
||||
}
|
||||
|
||||
// Add user-specified dependencies.
|
||||
if (const char* linkDepends =
|
||||
this->GeneratorTarget->GetProperty("LINK_DEPENDS")) {
|
||||
cmSystemTools::ExpandListArgument(linkDepends, depends);
|
||||
}
|
||||
this->GeneratorTarget->GetLinkDepends(depends, this->ConfigName,
|
||||
linkLanguage);
|
||||
}
|
||||
|
||||
std::string cmMakefileTargetGenerator::GetLinkRule(
|
||||
|
||||
@@ -128,7 +128,8 @@ protected:
|
||||
void AppendObjectDepends(std::vector<std::string>& depends);
|
||||
|
||||
// Append link rule dependencies (objects, etc.).
|
||||
void AppendLinkDepends(std::vector<std::string>& depends);
|
||||
void AppendLinkDepends(std::vector<std::string>& depends,
|
||||
const std::string& linkLanguage);
|
||||
|
||||
// Lookup the link rule for this target.
|
||||
std::string GetLinkRule(const std::string& linkRuleVar);
|
||||
|
||||
@@ -625,7 +625,7 @@ void cmNinjaNormalTargetGenerator::WriteDeviceLinkStatement()
|
||||
outputs.push_back(targetOutputReal);
|
||||
// Compute specific libraries to link with.
|
||||
cmNinjaDeps explicitDeps = this->GetObjects();
|
||||
cmNinjaDeps implicitDeps = this->ComputeLinkDeps();
|
||||
cmNinjaDeps implicitDeps = this->ComputeLinkDeps(this->TargetLinkLanguage);
|
||||
|
||||
std::string frameworkPath;
|
||||
std::string linkPath;
|
||||
@@ -794,7 +794,7 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement()
|
||||
|
||||
// Compute specific libraries to link with.
|
||||
cmNinjaDeps explicitDeps = this->GetObjects();
|
||||
cmNinjaDeps implicitDeps = this->ComputeLinkDeps();
|
||||
cmNinjaDeps implicitDeps = this->ComputeLinkDeps(this->TargetLinkLanguage);
|
||||
|
||||
if (!this->DeviceLinkObject.empty()) {
|
||||
explicitDeps.push_back(this->DeviceLinkObject);
|
||||
|
||||
@@ -235,7 +235,8 @@ std::string cmNinjaTargetGenerator::ComputeIncludes(
|
||||
return includesString;
|
||||
}
|
||||
|
||||
cmNinjaDeps cmNinjaTargetGenerator::ComputeLinkDeps() const
|
||||
cmNinjaDeps cmNinjaTargetGenerator::ComputeLinkDeps(
|
||||
const std::string& linkLanguage) const
|
||||
{
|
||||
// Static libraries never depend on other targets for linking.
|
||||
if (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY ||
|
||||
@@ -270,13 +271,11 @@ cmNinjaDeps cmNinjaTargetGenerator::ComputeLinkDeps() const
|
||||
}
|
||||
|
||||
// Add user-specified dependencies.
|
||||
if (const char* linkDepends =
|
||||
this->GeneratorTarget->GetProperty("LINK_DEPENDS")) {
|
||||
std::vector<std::string> linkDeps;
|
||||
cmSystemTools::ExpandListArgument(linkDepends, linkDeps);
|
||||
std::transform(linkDeps.begin(), linkDeps.end(),
|
||||
std::back_inserter(result), MapToNinjaPath());
|
||||
}
|
||||
std::vector<std::string> linkDeps;
|
||||
this->GeneratorTarget->GetLinkDepends(linkDeps, this->ConfigName,
|
||||
linkLanguage);
|
||||
std::transform(linkDeps.begin(), linkDeps.end(), std::back_inserter(result),
|
||||
MapToNinjaPath());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ protected:
|
||||
}
|
||||
|
||||
/// @return the list of link dependency for the given target @a target.
|
||||
cmNinjaDeps ComputeLinkDeps() const;
|
||||
cmNinjaDeps ComputeLinkDeps(const std::string& linkLanguage) const;
|
||||
|
||||
/// @return the source file path for the given @a source.
|
||||
std::string GetSourceFilePath(cmSourceFile const* source) const;
|
||||
|
||||
@@ -343,6 +343,17 @@ if(TEST_LINK_DEPENDS)
|
||||
${linkdep}
|
||||
is not newer than dependency
|
||||
${TEST_LINK_DEPENDS}
|
||||
")
|
||||
endif()
|
||||
|
||||
set(linkdep2 ${BuildDepends_BINARY_DIR}/Project/linkdep2${CMAKE_EXECUTABLE_SUFFIX})
|
||||
if(${linkdep2} IS_NEWER_THAN ${TEST_LINK_DEPENDS})
|
||||
message("INTERFACE_LINK_DEPENDS worked")
|
||||
else()
|
||||
message(SEND_ERROR "INTERFACE_LINK_DEPENDS failed. Executable
|
||||
${linkdep2}
|
||||
is not newer than dependency
|
||||
${TEST_LINK_DEPENDS}
|
||||
")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -107,6 +107,11 @@ set_property(
|
||||
if(TEST_LINK_DEPENDS)
|
||||
add_executable(linkdep linkdep.cxx)
|
||||
set_property(TARGET linkdep PROPERTY LINK_DEPENDS ${TEST_LINK_DEPENDS})
|
||||
|
||||
add_library(foo_interface INTERFACE)
|
||||
set_property(TARGET foo_interface PROPERTY INTERFACE_LINK_DEPENDS $<1:${TEST_LINK_DEPENDS}>)
|
||||
add_executable(linkdep2 linkdep.cxx)
|
||||
target_link_libraries(linkdep2 PRIVATE foo_interface)
|
||||
endif()
|
||||
|
||||
add_library(link_depends_no_shared_lib SHARED link_depends_no_shared_lib.c
|
||||
|
||||
@@ -607,3 +607,17 @@ target_link_options(testLinkOptions INTERFACE INTERFACE_FLAG)
|
||||
install(TARGETS testLinkOptions
|
||||
EXPORT RequiredExp DESTINATION lib)
|
||||
export(TARGETS testLinkOptions NAMESPACE bld_ APPEND FILE ExportBuildTree.cmake)
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# test export of INTERFACE_LINK_DEPENDS
|
||||
if(CMAKE_GENERATOR MATCHES "Make|Ninja")
|
||||
add_library(testLinkDepends INTERFACE)
|
||||
set_property(TARGET testLinkDepends PROPERTY INTERFACE_LINK_DEPENDS
|
||||
$<BUILD_INTERFACE:BUILD_LINK_DEPENDS>
|
||||
$<INSTALL_INTERFACE:INSTALL_LINK_DEPENDS>)
|
||||
|
||||
install(TARGETS testLinkDepends
|
||||
EXPORT RequiredExp DESTINATION lib)
|
||||
export(TARGETS testLinkDepends NAMESPACE bld_ APPEND FILE ExportBuildTree.cmake)
|
||||
endif()
|
||||
|
||||
@@ -477,3 +477,10 @@ endif()
|
||||
# check that imported libraries have the expected INTERFACE_LINK_OPTIONS property
|
||||
checkForProperty(bld_testLinkOptions "INTERFACE_LINK_OPTIONS" "INTERFACE_FLAG")
|
||||
checkForProperty(Req::testLinkOptions "INTERFACE_LINK_OPTIONS" "INTERFACE_FLAG")
|
||||
|
||||
#---------------------------------------------------------------------------------
|
||||
# check that imported libraries have the expected INTERFACE_LINK_DEPENDS property
|
||||
if(CMAKE_GENERATOR MATCHES "Make|Ninja")
|
||||
checkForProperty(bld_testLinkDepends "INTERFACE_LINK_DEPENDS" "BUILD_LINK_DEPENDS")
|
||||
checkForProperty(Req::testLinkDepends "INTERFACE_LINK_DEPENDS" "${CMAKE_INSTALL_PREFIX}/INSTALL_LINK_DEPENDS")
|
||||
endif()
|
||||
|
||||
Reference in New Issue
Block a user