install(TARGETS): Add option to install runtime deps built as other targets

Add a `RUNTIME_DEPENDENCY_TARGETS` option for `install(TARGETS)`
to automatically install the necessary runtime shared libraries.

Closes: #23505
This commit is contained in:
Stepanov Igor
2026-09-22 10:40:52 -04:00
committed by Brad King
parent 216cc54531
commit 53019cd2e6
9 changed files with 369 additions and 17 deletions
+34
View File
@@ -157,6 +157,7 @@ Signatures
install(TARGETS <target>... [EXPORT <export-name>] install(TARGETS <target>... [EXPORT <export-name>]
[RUNTIME_DEPENDENCIES <arg>...|RUNTIME_DEPENDENCY_SET <set-name>] [RUNTIME_DEPENDENCIES <arg>...|RUNTIME_DEPENDENCY_SET <set-name>]
[RUNTIME_DEPENDENCY_TARGETS]
[<artifact-option>...] [<artifact-option>...]
[<artifact-kind> <artifact-option>...]... [<artifact-kind> <artifact-option>...]...
[INCLUDES DESTINATION [<dir> ...]] [INCLUDES DESTINATION [<dir> ...]]
@@ -477,6 +478,36 @@ Signatures
The ``RUNTIME_DEPENDENCIES`` and ``RUNTIME_DEPENDENCY_SET`` keywords are The ``RUNTIME_DEPENDENCIES`` and ``RUNTIME_DEPENDENCY_SET`` keywords are
mutually exclusive. mutually exclusive.
``RUNTIME_DEPENDENCY_TARGETS``
.. versionadded:: 4.5
This option installs runtime artifacts of non-imported ``SHARED``
library targets from the same build that appear on the computed
link lines of the listed ``<target>...`` and, recursively, of those
shared libraries. The ``RUNTIME``, ``LIBRARY``, and ``FRAMEWORK``
destinations and other artifact options from this call apply to
those artifacts (``RUNTIME`` on DLL platforms, ``LIBRARY``
otherwise, and ``FRAMEWORK`` for macOS frameworks).
Unlike ``RUNTIME_DEPENDENCIES``, this option does not call
:command:`file(GET_RUNTIME_DEPENDENCIES)` and does not install files
that are not CMake targets of the current build.
This option may not be used with ``EXPORT``. The call may specify
only runtime artifact groups (``RUNTIME``, ``LIBRARY``,
``FRAMEWORK``, and ``BUNDLE``).
.. note::
Static, interface, and object libraries on a listed target's
link line are traversed only to discover shared libraries; they
are not themselves installed by this option. Executables and
module libraries are not installed transitively. Imported
targets are skipped. Each :command:`install(TARGETS)` call with
this option installs the collected shared libraries into the
destinations of that call, even if another call already installs
the same target.
:ref:`Interface Libraries` may be listed among the targets to install. :ref:`Interface Libraries` may be listed among the targets to install.
They install no artifacts but will be included in an associated ``EXPORT``. They install no artifacts but will be included in an associated ``EXPORT``.
If :ref:`Object Libraries` are listed but given no destination for their If :ref:`Object Libraries` are listed but given no destination for their
@@ -1180,6 +1211,9 @@ Signatures
Targets built within the build tree will never be installed as runtime Targets built within the build tree will never be installed as runtime
dependencies, nor will their own dependencies, unless the targets themselves dependencies, nor will their own dependencies, unless the targets themselves
are installed with :command:`install(TARGETS)`. are installed with :command:`install(TARGETS)`.
Use the ``RUNTIME_DEPENDENCY_TARGETS`` option of
:command:`install(TARGETS)` to install runtime artifacts of
non-imported shared libraries from the same build.
The generated install script calls :command:`file(GET_RUNTIME_DEPENDENCIES)` The generated install script calls :command:`file(GET_RUNTIME_DEPENDENCIES)`
on the build-tree files to calculate the runtime dependencies. The build-tree on the build-tree files to calculate the runtime dependencies. The build-tree
@@ -0,0 +1,10 @@
install-TARGETS-RUNTIME_DEPENDENCY_TARGETS
------------------------------------------
* The :command:`install(TARGETS)` command gained a
``RUNTIME_DEPENDENCY_TARGETS`` option to install runtime artifacts
of non-imported shared libraries from the same build that the named
targets link to. Unlike ``RUNTIME_DEPENDENCIES``, this uses CMake
target graph information rather than
:command:`file(GET_RUNTIME_DEPENDENCIES)`. The option cannot be
used with ``EXPORT`` and may only install runtime artifacts.
+210 -17
View File
@@ -19,12 +19,14 @@
#include "cmArgumentParser.h" #include "cmArgumentParser.h"
#include "cmArgumentParserTypes.h" #include "cmArgumentParserTypes.h"
#include "cmCMakePath.h" #include "cmCMakePath.h"
#include "cmComputeLinkInformation.h"
#include "cmDiagnosticContext.h" #include "cmDiagnosticContext.h"
#include "cmDiagnostics.h" #include "cmDiagnostics.h"
#include "cmExecutionStatus.h" #include "cmExecutionStatus.h"
#include "cmExperimental.h" #include "cmExperimental.h"
#include "cmExportSet.h" #include "cmExportSet.h"
#include "cmGeneratorExpression.h" #include "cmGeneratorExpression.h"
#include "cmGeneratorTarget.h"
#include "cmGlobalGenerator.h" #include "cmGlobalGenerator.h"
#include "cmInstallAndroidMKExportGenerator.h" #include "cmInstallAndroidMKExportGenerator.h"
#include "cmInstallCMakeConfigExportGenerator.h" #include "cmInstallCMakeConfigExportGenerator.h"
@@ -44,6 +46,7 @@
#include "cmInstallScriptGenerator.h" #include "cmInstallScriptGenerator.h"
#include "cmInstallTargetGenerator.h" #include "cmInstallTargetGenerator.h"
#include "cmList.h" #include "cmList.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h" #include "cmMakefile.h"
#include "cmMessageType.h" #include "cmMessageType.h"
#include "cmPackageInfoArguments.h" #include "cmPackageInfoArguments.h"
@@ -60,8 +63,16 @@
#include "cmUnreachable.h" #include "cmUnreachable.h"
#include "cmValue.h" #include "cmValue.h"
class cmListFileBacktrace;
namespace { namespace {
enum class RuntimeOnly
{
No,
Yes,
};
struct InstallCategoryFlags struct InstallCategoryFlags
{ {
bool Archive = false; bool Archive = false;
@@ -121,6 +132,57 @@ struct InstallContext
} }
}; };
void CollectEvaluatedTransitiveSharedModuleTargets(
cmLocalGenerator& lg, std::vector<std::string> const& rootNames,
std::set<cmTarget*>& out, std::string const& config)
{
std::set<cmGeneratorTarget const*> visited;
std::vector<cmGeneratorTarget const*> stack;
std::set<std::string> const rootNameSet(rootNames.begin(), rootNames.end());
auto consider = [&](cmGeneratorTarget const* dep) {
if (!dep || dep->IsImported()) {
return;
}
if (!visited.insert(dep).second) {
return;
}
cm::TargetType type = dep->GetType();
if (type == cm::TargetType::SHARED_LIBRARY) {
if (rootNameSet.find(dep->GetName()) == rootNameSet.end()) {
out.insert(dep->Target);
}
stack.push_back(dep);
}
};
auto enqueue = [&](cmGeneratorTarget const* current) {
cmComputeLinkInformation* linkInfo = current->GetLinkInformation(config);
std::set<cmGeneratorTarget const*> const& sharedTargets =
linkInfo->GetSharedLibrariesLinked();
for (cmGeneratorTarget const* dep : sharedTargets) {
consider(dep);
}
};
for (std::string const& name : rootNames) {
cmGeneratorTarget* root = lg.FindGeneratorTargetToUse(name);
if (!root) {
root = lg.GetGlobalGenerator()->FindGeneratorTarget(name);
}
if (!root) {
continue;
}
visited.insert(root);
enqueue(root);
}
while (!stack.empty()) {
cmGeneratorTarget const* current = stack.back();
stack.pop_back();
enqueue(current);
}
}
struct RuntimeDependenciesArgs struct RuntimeDependenciesArgs
{ {
ArgumentParser::MaybeEmpty<std::vector<std::string>> Directories; ArgumentParser::MaybeEmpty<std::vector<std::string>> Directories;
@@ -417,7 +479,8 @@ void RegisterInstallComponents(cmMakefile* makefile, InstallContext const& ctx,
bool CreateInstallGeneratorsForTarget( bool CreateInstallGeneratorsForTarget(
Helper& helper, cmTarget& target, InstallContext const& ctx, Helper& helper, cmTarget& target, InstallContext const& ctx,
InstallCategoryFlags& flags, std::string& error, InstallCategoryFlags& flags, std::string& error,
cm::optional<std::vector<std::string>> configurations = {}) cm::optional<std::vector<std::string>> configurations = {},
RuntimeOnly runtimeOnly = RuntimeOnly::No)
{ {
cmInstallCommandArguments const& archiveArgs = *ctx.ArchiveArgs; cmInstallCommandArguments const& archiveArgs = *ctx.ArchiveArgs;
cmInstallCommandArguments const& libraryArgs = *ctx.LibraryArgs; cmInstallCommandArguments const& libraryArgs = *ctx.LibraryArgs;
@@ -514,27 +577,32 @@ bool CreateInstallGeneratorsForTarget(
// cygwin. Currently no other platform is a DLL platform. // cygwin. Currently no other platform is a DLL platform.
if (target.IsDLLPlatform()) { if (target.IsDLLPlatform()) {
// When in namelink only mode skip all libraries on Windows. // When in namelink only mode skip all libraries on Windows.
if (namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly) { if (runtimeOnly == RuntimeOnly::No &&
namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly) {
namelinkOnly = true; namelinkOnly = true;
return addTargetExport(); return addTargetExport();
} }
// This is a DLL platform. // This is a DLL platform.
if (!archiveArgs.GetDestination().empty()) { if (runtimeOnly == RuntimeOnly::No &&
!archiveArgs.GetDestination().empty()) {
// The import library uses the ARCHIVE properties. // The import library uses the ARCHIVE properties.
archiveGenerator = CreateInstallTargetGenerator( archiveGenerator = CreateInstallTargetGenerator(
target, archiveArgs, true, helper.CaptureContext(), false, false, target, archiveArgs, true, helper.CaptureContext(), false, false,
configurations); configurations);
artifactsSpecified = true; artifactsSpecified = true;
} }
if (!runtimeArgs.GetDestination().empty()) { if (runtimeOnly == RuntimeOnly::Yes ||
!runtimeArgs.GetDestination().empty()) {
// The DLL uses the RUNTIME properties. // The DLL uses the RUNTIME properties.
runtimeGenerator = CreateInstallTargetGenerator( runtimeGenerator = CreateInstallTargetGenerator(
target, runtimeArgs, false, helper.CaptureContext(), false, false, target, runtimeArgs, false, helper.CaptureContext(),
helper.GetRuntimeDestination(&runtimeArgs), false, false,
configurations); configurations);
artifactsSpecified = true; artifactsSpecified = true;
} }
if (!archiveGenerator && !runtimeGenerator) { if (runtimeOnly == RuntimeOnly::No && !archiveGenerator &&
!runtimeGenerator) {
archiveGenerator = CreateInstallTargetGenerator( archiveGenerator = CreateInstallTargetGenerator(
target, archiveArgs, true, helper.CaptureContext(), target, archiveArgs, true, helper.CaptureContext(),
helper.GetArchiveDestination(nullptr), false, false, helper.GetArchiveDestination(nullptr), false, false,
@@ -553,7 +621,8 @@ bool CreateInstallGeneratorsForTarget(
// INSTALL properties. Otherwise, use the LIBRARY properties. // INSTALL properties. Otherwise, use the LIBRARY properties.
if (target.IsFrameworkOnApple()) { if (target.IsFrameworkOnApple()) {
// When in namelink only mode skip frameworks. // When in namelink only mode skip frameworks.
if (namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly) { if (runtimeOnly == RuntimeOnly::No &&
namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly) {
namelinkOnly = true; namelinkOnly = true;
return addTargetExport(); return addTargetExport();
} }
@@ -574,7 +643,8 @@ bool CreateInstallGeneratorsForTarget(
if (!libraryArgs.GetDestination().empty()) { if (!libraryArgs.GetDestination().empty()) {
artifactsSpecified = true; artifactsSpecified = true;
} }
if (namelinkMode != cmInstallTargetGenerator::NamelinkModeOnly) { if (runtimeOnly == RuntimeOnly::Yes ||
namelinkMode != cmInstallTargetGenerator::NamelinkModeOnly) {
libraryGenerator = CreateInstallTargetGenerator( libraryGenerator = CreateInstallTargetGenerator(
target, libraryArgs, false, helper.CaptureContext(), target, libraryArgs, false, helper.CaptureContext(),
helper.GetLibraryDestination(&libraryArgs), false, false, helper.GetLibraryDestination(&libraryArgs), false, false,
@@ -582,7 +652,8 @@ bool CreateInstallGeneratorsForTarget(
libraryGenerator->SetNamelinkMode( libraryGenerator->SetNamelinkMode(
cmInstallTargetGenerator::NamelinkModeSkip); cmInstallTargetGenerator::NamelinkModeSkip);
} }
if (namelinkMode != cmInstallTargetGenerator::NamelinkModeSkip) { if (runtimeOnly == RuntimeOnly::No &&
namelinkMode != cmInstallTargetGenerator::NamelinkModeSkip) {
namelinkGenerator = CreateInstallTargetGenerator( namelinkGenerator = CreateInstallTargetGenerator(
target, libraryArgs, false, helper.CaptureContext(), target, libraryArgs, false, helper.CaptureContext(),
helper.GetLibraryDestination(&libraryArgs), false, true, helper.GetLibraryDestination(&libraryArgs), false, true,
@@ -590,10 +661,11 @@ bool CreateInstallGeneratorsForTarget(
namelinkGenerator->SetNamelinkMode( namelinkGenerator->SetNamelinkMode(
cmInstallTargetGenerator::NamelinkModeOnly); cmInstallTargetGenerator::NamelinkModeOnly);
} }
namelinkOnly = namelinkOnly = runtimeOnly == RuntimeOnly::No &&
(namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly); (namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly);
if (target.GetMakefile()->PlatformSupportsAppleTextStubs() && if (runtimeOnly == RuntimeOnly::No &&
target.GetMakefile()->PlatformSupportsAppleTextStubs() &&
target.IsSharedLibraryWithExports()) { target.IsSharedLibraryWithExports()) {
// Apple .tbd files use the ARCHIVE properties // Apple .tbd files use the ARCHIVE properties
if (!archiveArgs.GetDestination().empty()) { if (!archiveArgs.GetDestination().empty()) {
@@ -625,6 +697,9 @@ bool CreateInstallGeneratorsForTarget(
} }
} break; } break;
case cm::TargetType::STATIC_LIBRARY: { case cm::TargetType::STATIC_LIBRARY: {
if (runtimeOnly == RuntimeOnly::Yes) {
break;
}
// If it is marked with FRAMEWORK property use the FRAMEWORK set of // If it is marked with FRAMEWORK property use the FRAMEWORK set of
// INSTALL properties. Otherwise, use the LIBRARY properties. // INSTALL properties. Otherwise, use the LIBRARY properties.
if (target.IsFrameworkOnApple()) { if (target.IsFrameworkOnApple()) {
@@ -671,14 +746,20 @@ bool CreateInstallGeneratorsForTarget(
target, libraryArgs, false, helper.CaptureContext(), moduleDest, false, target, libraryArgs, false, helper.CaptureContext(), moduleDest, false,
false, configurations); false, configurations);
libraryGenerator->SetNamelinkMode(namelinkMode); libraryGenerator->SetNamelinkMode(
namelinkOnly = runtimeOnly == RuntimeOnly::Yes
? cmInstallTargetGenerator::NamelinkModeNone
: namelinkMode);
namelinkOnly = runtimeOnly == RuntimeOnly::No &&
(namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly); (namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly);
if (runtimeDependencySet) { if (runtimeDependencySet) {
runtimeDependencySet->AddModule(libraryGenerator.get()); runtimeDependencySet->AddModule(libraryGenerator.get());
} }
} break; } break;
case cm::TargetType::OBJECT_LIBRARY: { case cm::TargetType::OBJECT_LIBRARY: {
if (runtimeOnly == RuntimeOnly::Yes) {
break;
}
// Objects use OBJECT properties. // Objects use OBJECT properties.
if (!objectArgs.GetDestination().empty()) { if (!objectArgs.GetDestination().empty()) {
// Verify that we know where the objects are to install them. // Verify that we know where the objects are to install them.
@@ -699,6 +780,9 @@ bool CreateInstallGeneratorsForTarget(
} }
} break; } break;
case cm::TargetType::EXECUTABLE: { case cm::TargetType::EXECUTABLE: {
if (runtimeOnly == RuntimeOnly::Yes) {
break;
}
if (target.IsAppBundleOnApple()) { if (target.IsAppBundleOnApple()) {
// Application bundles use the BUNDLE properties. // Application bundles use the BUNDLE properties.
if (!bundleArgs.GetDestination().empty()) { if (!bundleArgs.GetDestination().empty()) {
@@ -760,7 +844,8 @@ bool CreateInstallGeneratorsForTarget(
// FRAMEWORK. For other target types or on other platforms, they are not // FRAMEWORK. For other target types or on other platforms, they are not
// installed automatically and so we need to create install files // installed automatically and so we need to create install files
// generators for them. // generators for them.
bool createInstallGeneratorsForTargetFileSets = true; bool createInstallGeneratorsForTargetFileSets =
runtimeOnly == RuntimeOnly::No;
if (target.IsFrameworkOnApple()) { if (target.IsFrameworkOnApple()) {
createInstallGeneratorsForTargetFileSets = false; createInstallGeneratorsForTargetFileSets = false;
@@ -836,7 +921,7 @@ bool CreateInstallGeneratorsForTarget(
} }
} }
if (!namelinkOnly) { if (runtimeOnly == RuntimeOnly::No && !namelinkOnly) {
for (std::size_t i = 0; i < fileSetArgs.size(); i++) { for (std::size_t i = 0; i < fileSetArgs.size(); i++) {
fileSetGenerators.push_back(CreateInstallFileSetGenerator( fileSetGenerators.push_back(CreateInstallFileSetGenerator(
helper, target, fileSetArgs[i], configurations)); helper, target, fileSetArgs[i], configurations));
@@ -844,7 +929,8 @@ bool CreateInstallGeneratorsForTarget(
} }
} }
if (!cxxModuleBmiArgs.GetDestination().empty()) { if (runtimeOnly == RuntimeOnly::No &&
!cxxModuleBmiArgs.GetDestination().empty()) {
cxxModuleBmiGenerator = cm::make_unique<cmInstallCxxModuleBmiGenerator>( cxxModuleBmiGenerator = cm::make_unique<cmInstallCxxModuleBmiGenerator>(
target.GetName(), helper.GetCxxModulesBmiDestination(&cxxModuleBmiArgs), target.GetName(), helper.GetCxxModulesBmiDestination(&cxxModuleBmiArgs),
cxxModuleBmiArgs.GetPermissions(), cxxModuleBmiArgs.GetPermissions(),
@@ -857,7 +943,12 @@ bool CreateInstallGeneratorsForTarget(
} }
// Add this install rule to an export if one was specified. // Add this install rule to an export if one was specified.
if (!addTargetExport()) { // Transitive runtime artifacts from RUNTIME_DEPENDENCY_TARGETS are
// included; skip if this call created no runtime install generators.
bool const hasRuntimeArtifact = libraryGenerator || runtimeGenerator ||
frameworkGenerator || bundleGenerator;
if ((runtimeOnly == RuntimeOnly::No || hasRuntimeArtifact) &&
!addTargetExport()) {
return false; return false;
} }
@@ -893,6 +984,63 @@ bool CreateInstallGeneratorsForTarget(
return true; return true;
} }
bool InstallTargetsGroupedByConfig(
Helper& helper, InstallContext const& ctx, InstallCategoryFlags& flags,
cmLocalGenerator& lg,
std::map<std::string, std::set<cmTarget*>> const& byConfig,
std::vector<std::string> const& configs)
{
std::map<cmTarget*, std::size_t> hits;
for (std::pair<std::string const, std::set<cmTarget*>> const& e : byConfig) {
for (cmTarget* t : e.second) {
++hits[t];
}
}
std::set<cmTarget*> common;
std::size_t const n = configs.size();
for (std::pair<cmTarget* const, std::size_t> const& h : hits) {
if (h.second == n) {
common.insert(h.first);
}
}
auto installDep =
[&](cmTarget& dep,
cm::optional<std::vector<std::string>> configurations) -> bool {
std::string error;
if (!CreateInstallGeneratorsForTarget(helper, dep, ctx, flags, error,
configurations, RuntimeOnly::Yes)) {
lg.IssueMessage(MessageType::FATAL_ERROR, error);
return false;
}
return true;
};
for (cmTarget* dep : common) {
if (!installDep(*dep, cm::nullopt)) {
return false;
}
}
std::map<cmTarget*, std::vector<std::string>> configSpecific;
for (std::string const& config : configs) {
auto it = byConfig.find(config);
if (it == byConfig.end()) {
continue;
}
for (cmTarget* dep : it->second) {
if (common.find(dep) == common.end()) {
configSpecific[dep].push_back(config);
}
}
}
return std::all_of(
configSpecific.begin(), configSpecific.end(),
[&](std::pair<cmTarget* const, std::vector<std::string>> const& e) {
return installDep(*e.first, e.second);
});
}
void CheckAbsoluteDestination(Helper& helper, std::string const& destination) void CheckAbsoluteDestination(Helper& helper, std::string const& destination)
{ {
// Check for an absolute destination. // Check for an absolute destination.
@@ -1045,6 +1193,7 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>> cm::optional<ArgumentParser::MaybeEmpty<std::vector<std::string>>>
runtimeDependenciesArgVector; runtimeDependenciesArgVector;
std::string runtimeDependencySetArg; std::string runtimeDependencySetArg;
bool installTransitiveDependencies = false;
std::vector<std::string> unknownArgs; std::vector<std::string> unknownArgs;
cmInstallCommandArguments genericArgs(helper.DefaultComponentName, cmInstallCommandArguments genericArgs(helper.DefaultComponentName,
*helper.Makefile); *helper.Makefile);
@@ -1052,6 +1201,8 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
genericArgs.Bind("EXPORT"_s, exports); genericArgs.Bind("EXPORT"_s, exports);
genericArgs.Bind("RUNTIME_DEPENDENCIES"_s, runtimeDependenciesArgVector); genericArgs.Bind("RUNTIME_DEPENDENCIES"_s, runtimeDependenciesArgVector);
genericArgs.Bind("RUNTIME_DEPENDENCY_SET"_s, runtimeDependencySetArg); genericArgs.Bind("RUNTIME_DEPENDENCY_SET"_s, runtimeDependencySetArg);
genericArgs.Bind("RUNTIME_DEPENDENCY_TARGETS"_s,
installTransitiveDependencies);
genericArgs.Parse(genericArgVector, &unknownArgs); genericArgs.Parse(genericArgVector, &unknownArgs);
bool success = genericArgs.Finalize(); bool success = genericArgs.Finalize();
@@ -1224,6 +1375,14 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
return false; return false;
} }
if (installTransitiveDependencies) {
if (!exports.empty()) {
status.SetError("TARGETS RUNTIME_DEPENDENCY_TARGETS cannot be used "
"with EXPORT.");
return false;
}
}
cmInstallRuntimeDependencySet* runtimeDependencySet = nullptr; cmInstallRuntimeDependencySet* runtimeDependencySet = nullptr;
if (runtimeDependenciesArgVector) { if (runtimeDependenciesArgVector) {
if (!runtimeDependencySetArg.empty()) { if (!runtimeDependencySetArg.empty()) {
@@ -1348,6 +1507,40 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
ctx->RuntimeDependencySet = runtimeDependencySet; ctx->RuntimeDependencySet = runtimeDependencySet;
ctx->BindGenericArguments(); ctx->BindGenericArguments();
if (installTransitiveDependencies) {
std::vector<std::string> rootNames;
rootNames.reserve(targets.size());
for (cmTarget const* t : targets) {
rootNames.push_back(t->GetName());
}
helper.Makefile->AddGeneratorAction(
[ctx, rootNames](cmLocalGenerator& lg, cmListFileBacktrace const&) {
std::vector<std::string> configs =
lg.GetMakefile()->GetGeneratorConfigs(
cmMakefile::IncludeEmptyConfig);
std::map<std::string, std::set<cmTarget*>> transitiveTargetsByConfig;
for (std::string const& config : configs) {
CollectEvaluatedTransitiveSharedModuleTargets(
lg, rootNames, transitiveTargetsByConfig[config], config);
}
cmExecutionStatus genStatus(*lg.GetMakefile());
Helper genHelper(genStatus);
InstallCategoryFlags flags;
if (!InstallTargetsGroupedByConfig(genHelper, *ctx, flags, lg,
transitiveTargetsByConfig,
configs)) {
return;
}
RegisterInstallComponents(lg.GetMakefile(), *ctx, flags);
},
cmMakefile::GeneratorActionWhen::AfterGeneratorTargets);
}
InstallCategoryFlags flags; InstallCategoryFlags flags;
flags.FileSet.resize(ctx->FileSetArgs.size(), false); flags.FileSet.resize(ctx->FileSetArgs.size(), false);
@@ -191,6 +191,8 @@ elseif (CMake_TEST_install_VARIANT STREQUAL "Targets")
run_install_test(TARGETS-Defaults) run_install_test(TARGETS-Defaults)
run_install_test(TARGETS-Module-Destination) run_install_test(TARGETS-Module-Destination)
run_install_test(TARGETS-SymbolicComponent) run_install_test(TARGETS-SymbolicComponent)
run_install_test(TARGETS-RUNTIME_DEPENDENCY_TARGETS)
run_cmake(TARGETS-RUNTIME_DEPENDENCY_TARGETS-EXPORT)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux") if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
run_install_test(TARGETS-NAMELINK-No-Tweak) run_install_test(TARGETS-NAMELINK-No-Tweak)
@@ -0,0 +1,4 @@
^CMake Error at TARGETS-RUNTIME_DEPENDENCY_TARGETS-EXPORT\.cmake:[0-9]+ \(install\):
install TARGETS RUNTIME_DEPENDENCY_TARGETS cannot be used with EXPORT\.
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$
@@ -0,0 +1,6 @@
enable_language(C)
add_executable(exe main.c)
install(TARGETS exe RUNTIME_DEPENDENCY_TARGETS
EXPORT e
RUNTIME DESTINATION bin)
@@ -0,0 +1,73 @@
if(WIN32)
set(_check_files
[[bin]]
[[bin/first]]
[[bin/first/exe_1\.exe]]
[[bin/first/(lib)?shared_1\.dll]]
[[bin/first/(lib)?shared_2\.dll]]
[[bin/first/(lib)?shared_3\.dll]]
[[bin/second]]
[[bin/second/exe_2\.exe]]
[[bin/second/(lib)?shared_1\.dll]]
[[bin/second/(lib)?shared_2\.dll]]
[[bin/second/(lib)?shared_3\.dll]])
elseif(MSYS)
set(_check_files
[[bin]]
[[bin/first]]
[[bin/first/exe_1\.exe]]
[[bin/first/msys-shared_1\.dll]]
[[bin/first/msys-shared_2\.dll]]
[[bin/first/msys-shared_3\.dll]]
[[bin/second]]
[[bin/second/exe_2\.exe]]
[[bin/second/msys-shared_1\.dll]]
[[bin/second/msys-shared_2\.dll]]
[[bin/second/msys-shared_3\.dll]])
elseif(CYGWIN)
set(_check_files
[[bin]]
[[bin/first]]
[[bin/first/cygshared_1\.dll]]
[[bin/first/cygshared_2\.dll]]
[[bin/first/cygshared_3\.dll]]
[[bin/first/exe_1\.exe]]
[[bin/second]]
[[bin/second/cygshared_1\.dll]]
[[bin/second/cygshared_2\.dll]]
[[bin/second/cygshared_3\.dll]]
[[bin/second/exe_2\.exe]])
elseif(AIX)
set(_check_files
[[bin]]
[[bin/first]]
[[bin/first/exe_1]]
[[bin/second]]
[[bin/second/exe_2]]
[[lib]]
[[lib/first]]
[[lib/first/libshared_1\.a]]
[[lib/first/libshared_2\.a]]
[[lib/first/libshared_3\.a]]
[[lib/second]]
[[lib/second/libshared_1\.a]]
[[lib/second/libshared_2\.a]]
[[lib/second/libshared_3\.a]])
else()
set(_check_files
[[bin]]
[[bin/first]]
[[bin/first/exe_1]]
[[bin/second]]
[[bin/second/exe_2]]
[[lib]]
[[lib/first]]
[[lib/first/libshared_1\.(dylib|so)]]
[[lib/first/libshared_2\.(dylib|so)]]
[[lib/first/libshared_3\.(dylib|so)]]
[[lib/second]]
[[lib/second/libshared_1\.(dylib|so)]]
[[lib/second/libshared_2\.(dylib|so)]]
[[lib/second/libshared_3\.(dylib|so)]])
endif()
check_installed("^${_check_files}$")
@@ -0,0 +1,29 @@
enable_language(C)
add_executable(exe_1 main.c)
add_library(static STATIC obj1.c)
add_library(shared_1 SHARED obj5.c)
add_library(shared_2 SHARED obj3.c)
add_library(shared_3 SHARED obj4.c)
add_executable(exe_2 main.c)
add_library(iface INTERFACE)
target_link_libraries(exe_1 PRIVATE static shared_1)
target_link_libraries(static PRIVATE shared_2)
target_link_libraries(shared_1 PRIVATE shared_3)
target_link_libraries(exe_2 PRIVATE iface)
target_link_libraries(iface INTERFACE static shared_1)
install(
TARGETS exe_1 RUNTIME_DEPENDENCY_TARGETS
COMPONENT exc
LIBRARY DESTINATION lib/first
RUNTIME DESTINATION bin/first)
install(
TARGETS exe_2 RUNTIME_DEPENDENCY_TARGETS
COMPONENT exc
LIBRARY DESTINATION lib/second
RUNTIME DESTINATION bin/second)