LINK_DEPENDS: add support for property INTERFACE_LINK_DEPENDS

Fixes: #17997
This commit is contained in:
Marc Chevrier
2018-06-26 16:21:05 +02:00
parent b142b72141
commit cc9f88af53
22 changed files with 195 additions and 42 deletions

View File

@@ -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

View 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
)

View File

@@ -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.

View File

@@ -0,0 +1,4 @@
INTERFACE_LINK_DEPENDS-property
-------------------------------
* Binary targets gained new :prop_tgt:`INTERFACE_LINK_DEPENDS` property.

View File

@@ -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);

View File

@@ -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,

View File

@@ -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,

View File

@@ -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)) {

View File

@@ -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)

View File

@@ -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()) {

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);

View File

@@ -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(

View File

@@ -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);

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -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()

View File

@@ -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

View File

@@ -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()

View File

@@ -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()