Commit cc9f88af authored by Marc Chevrier's avatar Marc Chevrier

LINK_DEPENDS: add support for property INTERFACE_LINK_DEPENDS

Fixes: #17997
parent b142b721
......@@ -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
......
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.
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()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment