1
0
mirror of https://github.com/Kitware/CMake.git synced 2025-10-14 02:08:27 +08:00

install(TARGETS): Add RUNTIME_DEPENDENCIES option

This commit is contained in:
Kyle Edwards
2021-04-14 10:43:30 -04:00
parent f2617cf8e6
commit ed3633d88c
29 changed files with 928 additions and 25 deletions

View File

@@ -345,6 +345,8 @@ set(SRCS
cmGraphVizWriter.h
cmInstallGenerator.h
cmInstallGenerator.cxx
cmInstallGetRuntimeDependenciesGenerator.h
cmInstallGetRuntimeDependenciesGenerator.cxx
cmInstallExportGenerator.cxx
cmInstalledFile.h
cmInstalledFile.cxx
@@ -354,6 +356,8 @@ set(SRCS
cmInstallImportedRuntimeArtifactsGenerator.cxx
cmInstallRuntimeDependencySet.h
cmInstallRuntimeDependencySet.cxx
cmInstallRuntimeDependencySetGenerator.h
cmInstallRuntimeDependencySetGenerator.cxx
cmInstallScriptGenerator.h
cmInstallScriptGenerator.cxx
cmInstallSubdirectoryGenerator.h

View File

@@ -3044,11 +3044,10 @@ bool HandleCreateLinkCommand(std::vector<std::string> const& args,
bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args,
cmExecutionStatus& status)
{
static const std::set<std::string> supportedPlatforms = { "Windows", "Linux",
"Darwin" };
std::string platform =
status.GetMakefile().GetSafeDefinition("CMAKE_HOST_SYSTEM_NAME");
if (!supportedPlatforms.count(platform)) {
if (!cmRuntimeDependencyArchive::PlatformSupportsRuntimeDependencies(
platform)) {
status.SetError(
cmStrCat("GET_RUNTIME_DEPENDENCIES is not supported on system \"",
platform, "\""));

View File

@@ -2,8 +2,10 @@
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmInstallCommand.h"
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <iterator>
#include <set>
#include <sstream>
#include <utility>
@@ -23,7 +25,10 @@
#include "cmInstallExportGenerator.h"
#include "cmInstallFilesGenerator.h"
#include "cmInstallGenerator.h"
#include "cmInstallGetRuntimeDependenciesGenerator.h"
#include "cmInstallImportedRuntimeArtifactsGenerator.h"
#include "cmInstallRuntimeDependencySet.h"
#include "cmInstallRuntimeDependencySetGenerator.h"
#include "cmInstallScriptGenerator.h"
#include "cmInstallTargetGenerator.h"
#include "cmListFileCache.h"
@@ -31,6 +36,7 @@
#include "cmMessageType.h"
#include "cmPolicies.h"
#include "cmProperty.h"
#include "cmRuntimeDependencyArchive.h"
#include "cmStateTypes.h"
#include "cmStringAlgorithms.h"
#include "cmSubcommandTable.h"
@@ -40,6 +46,29 @@
namespace {
struct RuntimeDependenciesArgs
{
std::vector<std::string> Directories;
std::vector<std::string> PreIncludeRegexes;
std::vector<std::string> PreExcludeRegexes;
std::vector<std::string> PostIncludeRegexes;
std::vector<std::string> PostExcludeRegexes;
std::vector<std::string> PostIncludeFiles;
std::vector<std::string> PostExcludeFiles;
};
auto const RuntimeDependenciesArgHelper =
cmArgumentParser<RuntimeDependenciesArgs>{}
.Bind("DIRECTORIES"_s, &RuntimeDependenciesArgs::Directories)
.Bind("PRE_INCLUDE_REGEXES"_s, &RuntimeDependenciesArgs::PreIncludeRegexes)
.Bind("PRE_EXCLUDE_REGEXES"_s, &RuntimeDependenciesArgs::PreExcludeRegexes)
.Bind("POST_INCLUDE_REGEXES"_s,
&RuntimeDependenciesArgs::PostIncludeRegexes)
.Bind("POST_EXCLUDE_REGEXES"_s,
&RuntimeDependenciesArgs::PostExcludeRegexes)
.Bind("POST_INCLUDE_FILES"_s, &RuntimeDependenciesArgs::PostIncludeFiles)
.Bind("POST_EXCLUDE_FILES"_s, &RuntimeDependenciesArgs::PostExcludeFiles);
class Helper
{
public:
@@ -147,12 +176,106 @@ std::unique_ptr<cmInstallFilesGenerator> CreateInstallFilesGenerator(
args.GetDestination());
}
void AddInstallRuntimeDependenciesGenerator(
Helper& helper, cmInstallRuntimeDependencySet* runtimeDependencySet,
const cmInstallCommandArguments& runtimeArgs,
const cmInstallCommandArguments& libraryArgs,
const cmInstallCommandArguments& frameworkArgs,
RuntimeDependenciesArgs runtimeDependenciesArgs, bool& installsRuntime,
bool& installsLibrary, bool& installsFramework)
{
bool dllPlatform =
!helper.Makefile->GetSafeDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX").empty();
bool apple =
helper.Makefile->GetSafeDefinition("CMAKE_HOST_SYSTEM_NAME") == "Darwin";
auto const& runtimeDependenciesArgsRef =
dllPlatform ? runtimeArgs : libraryArgs;
std::vector<std::string> configurations =
runtimeDependenciesArgsRef.GetConfigurations();
if (apple) {
std::copy(frameworkArgs.GetConfigurations().begin(),
frameworkArgs.GetConfigurations().end(),
std::back_inserter(configurations));
}
// Create file(GET_RUNTIME_DEPENDENCIES) generator.
auto getRuntimeDependenciesGenerator =
cm::make_unique<cmInstallGetRuntimeDependenciesGenerator>(
runtimeDependencySet, std::move(runtimeDependenciesArgs.Directories),
std::move(runtimeDependenciesArgs.PreIncludeRegexes),
std::move(runtimeDependenciesArgs.PreExcludeRegexes),
std::move(runtimeDependenciesArgs.PostIncludeRegexes),
std::move(runtimeDependenciesArgs.PostExcludeRegexes),
std::move(runtimeDependenciesArgs.PostIncludeFiles),
std::move(runtimeDependenciesArgs.PostExcludeFiles),
runtimeDependenciesArgsRef.GetComponent(),
apple ? frameworkArgs.GetComponent() : "", true, "_CMAKE_DEPS",
"_CMAKE_RPATH", configurations,
cmInstallGenerator::SelectMessageLevel(helper.Makefile),
runtimeDependenciesArgsRef.GetExcludeFromAll() &&
(apple ? frameworkArgs.GetExcludeFromAll() : true),
helper.Makefile->GetBacktrace());
helper.Makefile->AddInstallGenerator(
std::move(getRuntimeDependenciesGenerator));
// Create the library dependencies generator.
auto libraryRuntimeDependenciesGenerator =
cm::make_unique<cmInstallRuntimeDependencySetGenerator>(
cmInstallRuntimeDependencySetGenerator::DependencyType::Library,
runtimeDependencySet, std::vector<std::string>{}, true, std::string{},
true, "_CMAKE_DEPS", "_CMAKE_RPATH", "_CMAKE_TMP",
dllPlatform ? helper.GetRuntimeDestination(&runtimeArgs)
: helper.GetLibraryDestination(&libraryArgs),
runtimeDependenciesArgsRef.GetConfigurations(),
runtimeDependenciesArgsRef.GetComponent(),
runtimeDependenciesArgsRef.GetPermissions(),
cmInstallGenerator::SelectMessageLevel(helper.Makefile),
runtimeDependenciesArgsRef.GetExcludeFromAll(),
helper.Makefile->GetBacktrace());
helper.Makefile->AddInstallGenerator(
std::move(libraryRuntimeDependenciesGenerator));
if (dllPlatform) {
installsRuntime = true;
} else {
installsLibrary = true;
}
if (apple) {
// Create the framework dependencies generator.
auto frameworkRuntimeDependenciesGenerator =
cm::make_unique<cmInstallRuntimeDependencySetGenerator>(
cmInstallRuntimeDependencySetGenerator::DependencyType::Framework,
runtimeDependencySet, std::vector<std::string>{}, true, std::string{},
true, "_CMAKE_DEPS", "_CMAKE_RPATH", "_CMAKE_TMP",
frameworkArgs.GetDestination(), frameworkArgs.GetConfigurations(),
frameworkArgs.GetComponent(), frameworkArgs.GetPermissions(),
cmInstallGenerator::SelectMessageLevel(helper.Makefile),
frameworkArgs.GetExcludeFromAll(), helper.Makefile->GetBacktrace());
helper.Makefile->AddInstallGenerator(
std::move(frameworkRuntimeDependenciesGenerator));
installsFramework = true;
}
}
std::set<std::string> const allowedTypes{
"BIN", "SBIN", "LIB", "INCLUDE", "SYSCONF",
"SHAREDSTATE", "LOCALSTATE", "RUNSTATE", "DATA", "INFO",
"LOCALE", "MAN", "DOC",
};
template <typename T>
bool AddBundleExecutable(Helper& helper,
cmInstallRuntimeDependencySet* runtimeDependencySet,
T&& bundleExecutable)
{
if (!runtimeDependencySet->AddBundleExecutable(bundleExecutable)) {
helper.SetError(
"A runtime dependency set may only have one bundle executable.");
return false;
}
return true;
}
bool HandleScriptMode(std::vector<std::string> const& args,
cmExecutionStatus& status)
{
@@ -289,13 +412,23 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
// These generic args also contain the targets and the export stuff
std::vector<std::string> targetList;
std::string exports;
std::vector<std::string> runtimeDependenciesArgVector;
std::vector<std::string> unknownArgs;
std::vector<std::string> parsedArgs;
cmInstallCommandArguments genericArgs(helper.DefaultComponentName);
genericArgs.Bind("TARGETS"_s, targetList);
genericArgs.Bind("EXPORT"_s, exports);
genericArgs.Parse(genericArgVector, &unknownArgs);
genericArgs.Bind("RUNTIME_DEPENDENCIES"_s, runtimeDependenciesArgVector);
genericArgs.Parse(genericArgVector, &unknownArgs, nullptr, &parsedArgs);
bool success = genericArgs.Finalize();
bool withRuntimeDependencies =
std::find(parsedArgs.begin(), parsedArgs.end(), "RUNTIME_DEPENDENCIES") !=
parsedArgs.end();
RuntimeDependenciesArgs runtimeDependenciesArgs =
RuntimeDependenciesArgHelper.Parse(runtimeDependenciesArgVector,
&unknownArgs);
cmInstallCommandArguments archiveArgs(helper.DefaultComponentName);
cmInstallCommandArguments libraryArgs(helper.DefaultComponentName);
cmInstallCommandArguments runtimeArgs(helper.DefaultComponentName);
@@ -402,6 +535,32 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
return false;
}
cmInstallRuntimeDependencySet* runtimeDependencySet = nullptr;
if (withRuntimeDependencies) {
auto system = helper.Makefile->GetSafeDefinition("CMAKE_HOST_SYSTEM_NAME");
if (!cmRuntimeDependencyArchive::PlatformSupportsRuntimeDependencies(
system)) {
status.SetError(
cmStrCat("TARGETS RUNTIME_DEPENDENCIES is not supported on system \"",
system, '"'));
return false;
}
if (helper.Makefile->IsOn("CMAKE_CROSSCOMPILING")) {
status.SetError("TARGETS RUNTIME_DEPENDENCIES is not supported "
"when cross-compiling.");
return false;
}
if (helper.Makefile->GetSafeDefinition("CMAKE_HOST_SYSTEM_NAME") ==
"Darwin" &&
frameworkArgs.GetDestination().empty()) {
status.SetError(
"TARGETS RUNTIME_DEPENDENCIES given no FRAMEWORK DESTINATION");
return false;
}
runtimeDependencySet = helper.Makefile->GetGlobalGenerator()
->CreateAnonymousRuntimeDependencySet();
}
// Select the mode for installing symlinks to versioned shared libraries.
cmInstallTargetGenerator::NamelinkModeType namelinkMode =
cmInstallTargetGenerator::NamelinkModeNone;
@@ -546,6 +705,9 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
target, runtimeArgs, false, helper.Makefile->GetBacktrace(),
helper.GetRuntimeDestination(nullptr));
}
if (runtimeDependencySet && runtimeGenerator) {
runtimeDependencySet->AddLibrary(runtimeGenerator.get());
}
} else {
// This is a non-DLL platform.
// If it is marked with FRAMEWORK property use the FRAMEWORK set of
@@ -591,6 +753,9 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
namelinkOnly =
(namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly);
}
if (runtimeDependencySet && libraryGenerator) {
runtimeDependencySet->AddLibrary(libraryGenerator.get());
}
}
} break;
case cmStateEnums::STATIC_LIBRARY: {
@@ -633,6 +798,9 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
libraryGenerator->SetNamelinkMode(namelinkMode);
namelinkOnly =
(namelinkMode == cmInstallTargetGenerator::NamelinkModeOnly);
if (runtimeDependencySet) {
runtimeDependencySet->AddModule(libraryGenerator.get());
}
} else {
status.SetError(
cmStrCat("TARGETS given no LIBRARY DESTINATION for module "
@@ -685,6 +853,12 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
target.GetName(), "\"."));
return false;
}
if (runtimeDependencySet) {
if (!AddBundleExecutable(helper, runtimeDependencySet,
bundleGenerator.get())) {
return false;
}
}
} else {
// Executables use the RUNTIME properties.
if (!runtimeArgs.GetDestination().empty()) {
@@ -693,6 +867,9 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
runtimeGenerator = CreateInstallTargetGenerator(
target, runtimeArgs, false, helper.Makefile->GetBacktrace(),
helper.GetRuntimeDestination(&runtimeArgs));
if (runtimeDependencySet) {
runtimeDependencySet->AddExecutable(runtimeGenerator.get());
}
}
// On DLL platforms an executable may also have an import
@@ -821,6 +998,13 @@ bool HandleTargetsMode(std::vector<std::string> const& args,
helper.Makefile->AddInstallGenerator(std::move(resourceGenerator));
}
if (withRuntimeDependencies && !runtimeDependencySet->Empty()) {
AddInstallRuntimeDependenciesGenerator(
helper, runtimeDependencySet, runtimeArgs, libraryArgs, frameworkArgs,
std::move(runtimeDependenciesArgs), installsRuntime, installsLibrary,
installsFramework);
}
// Tell the global generator about any installation component names
// specified
if (installsArchive) {

View File

@@ -43,7 +43,8 @@ void cmInstallGenerator::AddInstallRule(
std::vector<std::string> const& files, bool optional /* = false */,
const char* permissions_file /* = 0 */,
const char* permissions_dir /* = 0 */, const char* rename /* = 0 */,
const char* literal_args /* = 0 */, Indent indent)
const char* literal_args /* = 0 */, Indent indent,
const char* files_var /* = 0 */)
{
// Use the FILE command to install the file.
std::string stype;
@@ -70,37 +71,46 @@ void cmInstallGenerator::AddInstallRule(
stype = "FILE";
break;
}
os << indent;
if (cmSystemTools::FileIsFullPath(dest)) {
os << "list(APPEND CMAKE_ABSOLUTE_DESTINATION_FILES\n";
os << indent << " \"";
bool firstIteration = true;
for (std::string const& file : files) {
if (!firstIteration) {
os << ";";
if (!files.empty()) {
os << indent << "list(APPEND CMAKE_ABSOLUTE_DESTINATION_FILES\n";
os << indent << " \"";
bool firstIteration = true;
for (std::string const& file : files) {
if (!firstIteration) {
os << ";";
}
os << dest << "/";
if (rename && *rename) {
os << rename;
} else {
os << cmSystemTools::GetFilenameName(file);
}
firstIteration = false;
}
os << dest << "/";
if (rename && *rename) {
os << rename;
} else {
os << cmSystemTools::GetFilenameName(file);
}
firstIteration = false;
os << "\")\n";
}
if (files_var) {
os << indent << "foreach(_f IN LISTS " << files_var << ")\n";
os << indent.Next() << "get_filename_component(_fn \"${_f}\" NAME)\n";
os << indent.Next() << "list(APPEND CMAKE_ABSOLUTE_DESTINATION_FILES \""
<< dest << "/${_fn}\")\n";
os << indent << "endforeach()\n";
}
os << "\")\n";
os << indent << "if(CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION)\n";
os << indent << indent << "message(WARNING \"ABSOLUTE path INSTALL "
os << indent.Next() << "message(WARNING \"ABSOLUTE path INSTALL "
<< "DESTINATION : ${CMAKE_ABSOLUTE_DESTINATION_FILES}\")\n";
os << indent << "endif()\n";
os << indent << "if(CMAKE_ERROR_ON_ABSOLUTE_INSTALL_DESTINATION)\n";
os << indent << indent << "message(FATAL_ERROR \"ABSOLUTE path INSTALL "
os << indent.Next() << "message(FATAL_ERROR \"ABSOLUTE path INSTALL "
<< "DESTINATION forbidden (by caller): "
<< "${CMAKE_ABSOLUTE_DESTINATION_FILES}\")\n";
os << indent << "endif()\n";
}
std::string absDest = ConvertToAbsoluteDestination(dest);
os << "file(INSTALL DESTINATION \"" << absDest << "\" TYPE " << stype;
os << indent << "file(INSTALL DESTINATION \"" << absDest << "\" TYPE "
<< stype;
if (optional) {
os << " OPTIONAL";
}
@@ -133,6 +143,9 @@ void cmInstallGenerator::AddInstallRule(
for (std::string const& f : files) {
os << "\n" << indent << " \"" << f << "\"";
}
if (files_var) {
os << " ${" << files_var << "}";
}
os << "\n" << indent << " ";
if (!(literal_args && *literal_args)) {
os << " ";

View File

@@ -50,7 +50,8 @@ public:
std::vector<std::string> const& files, bool optional = false,
const char* permissions_file = nullptr,
const char* permissions_dir = nullptr, const char* rename = nullptr,
const char* literal_args = nullptr, Indent indent = Indent());
const char* literal_args = nullptr, Indent indent = Indent(),
const char* files_var = nullptr);
/** Get the install destination as it should appear in the
installation script. */

View File

@@ -0,0 +1,206 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmInstallGetRuntimeDependenciesGenerator.h"
#include <memory>
#include <ostream>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include <cm/optional>
#include <cm/string_view>
#include <cmext/string_view>
#include "cmGeneratorExpression.h"
#include "cmInstallRuntimeDependencySet.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmOutputConverter.h"
#include "cmStringAlgorithms.h"
namespace {
template <typename T, typename F>
void WriteMultiArgument(std::ostream& os, const cm::string_view& keyword,
const std::vector<T>& list,
cmScriptGeneratorIndent indent, F transform)
{
bool first = true;
for (auto const& item : list) {
cm::optional<std::string> result = transform(item);
if (result) {
if (first) {
os << indent << " " << keyword << "\n";
first = false;
}
os << indent << " " << *result << "\n";
}
}
}
void WriteFilesArgument(
std::ostream& os, const cm::string_view& keyword,
const std::vector<std::unique_ptr<cmInstallRuntimeDependencySet::Item>>&
items,
const std::string& config, cmScriptGeneratorIndent indent)
{
WriteMultiArgument(
os, keyword, items, indent,
[config](const std::unique_ptr<cmInstallRuntimeDependencySet::Item>& i)
-> std::string { return cmStrCat('"', i->GetItemPath(config), '"'); });
}
void WriteGenexEvaluatorArgument(std::ostream& os,
const cm::string_view& keyword,
const std::vector<std::string>& genexes,
const std::string& config,
cmLocalGenerator* lg,
cmScriptGeneratorIndent indent)
{
WriteMultiArgument(
os, keyword, genexes, indent,
[config, lg](const std::string& genex) -> cm::optional<std::string> {
std::string result = cmGeneratorExpression::Evaluate(genex, lg, config);
if (result.empty()) {
return cm::nullopt;
}
return cmOutputConverter::EscapeForCMake(result);
});
}
}
cmInstallGetRuntimeDependenciesGenerator::
cmInstallGetRuntimeDependenciesGenerator(
cmInstallRuntimeDependencySet* runtimeDependencySet,
std::vector<std::string> directories,
std::vector<std::string> preIncludeRegexes,
std::vector<std::string> preExcludeRegexes,
std::vector<std::string> postIncludeRegexes,
std::vector<std::string> postExcludeRegexes,
std::vector<std::string> postIncludeFiles,
std::vector<std::string> postExcludeFiles, std::string libraryComponent,
std::string frameworkComponent, bool noInstallRPath, const char* depsVar,
const char* rpathPrefix, std::vector<std::string> const& configurations,
MessageLevel message, bool exclude_from_all, cmListFileBacktrace backtrace)
: cmInstallGenerator("", configurations, "", message, exclude_from_all,
false, std::move(backtrace))
, RuntimeDependencySet(runtimeDependencySet)
, Directories(std::move(directories))
, PreIncludeRegexes(std::move(preIncludeRegexes))
, PreExcludeRegexes(std::move(preExcludeRegexes))
, PostIncludeRegexes(std::move(postIncludeRegexes))
, PostExcludeRegexes(std::move(postExcludeRegexes))
, PostIncludeFiles(std::move(postIncludeFiles))
, PostExcludeFiles(std::move(postExcludeFiles))
, LibraryComponent(std::move(libraryComponent))
, FrameworkComponent(std::move(frameworkComponent))
, NoInstallRPath(noInstallRPath)
, DepsVar(depsVar)
, RPathPrefix(rpathPrefix)
{
this->ActionsPerConfig = true;
}
bool cmInstallGetRuntimeDependenciesGenerator::Compute(cmLocalGenerator* lg)
{
this->LocalGenerator = lg;
return true;
}
void cmInstallGetRuntimeDependenciesGenerator::GenerateScript(std::ostream& os)
{
// Track indentation.
Indent indent;
// Begin this block of installation.
os << indent << "if(";
if (this->FrameworkComponent.empty() ||
this->FrameworkComponent == this->LibraryComponent) {
os << this->CreateComponentTest(this->LibraryComponent,
this->ExcludeFromAll);
} else {
os << this->CreateComponentTest(this->LibraryComponent, true) << " OR "
<< this->CreateComponentTest(this->FrameworkComponent,
this->ExcludeFromAll);
}
os << ")\n";
// Generate the script possibly with per-configuration code.
this->GenerateScriptConfigs(os, indent.Next());
// End this block of installation.
os << indent << "endif()\n\n";
}
void cmInstallGetRuntimeDependenciesGenerator::GenerateScriptForConfig(
std::ostream& os, const std::string& config, Indent indent)
{
std::string installNameTool =
this->LocalGenerator->GetMakefile()->GetSafeDefinition(
"CMAKE_INSTALL_NAME_TOOL");
os << indent << "file(GET_RUNTIME_DEPENDENCIES\n"
<< indent << " RESOLVED_DEPENDENCIES_VAR " << this->DepsVar << '\n';
WriteFilesArgument(os, "EXECUTABLES"_s,
this->RuntimeDependencySet->GetExecutables(), config,
indent);
WriteFilesArgument(os, "LIBRARIES"_s,
this->RuntimeDependencySet->GetLibraries(), config,
indent);
WriteFilesArgument(os, "MODULES"_s, this->RuntimeDependencySet->GetModules(),
config, indent);
if (this->RuntimeDependencySet->GetBundleExecutable()) {
os << indent << " BUNDLE_EXECUTABLE \""
<< this->RuntimeDependencySet->GetBundleExecutable()->GetItemPath(
config)
<< "\"\n";
}
WriteGenexEvaluatorArgument(os, "DIRECTORIES"_s, this->Directories, config,
this->LocalGenerator, indent);
WriteGenexEvaluatorArgument(os, "PRE_INCLUDE_REGEXES"_s,
this->PreIncludeRegexes, config,
this->LocalGenerator, indent);
WriteGenexEvaluatorArgument(os, "PRE_EXCLUDE_REGEXES"_s,
this->PreExcludeRegexes, config,
this->LocalGenerator, indent);
WriteGenexEvaluatorArgument(os, "POST_INCLUDE_REGEXES"_s,
this->PostIncludeRegexes, config,
this->LocalGenerator, indent);
WriteGenexEvaluatorArgument(os, "POST_EXCLUDE_REGEXES"_s,
this->PostExcludeRegexes, config,
this->LocalGenerator, indent);
WriteGenexEvaluatorArgument(os, "POST_INCLUDE_FILES"_s,
this->PostIncludeFiles, config,
this->LocalGenerator, indent);
WriteGenexEvaluatorArgument(os, "POST_EXCLUDE_FILES"_s,
this->PostExcludeFiles, config,
this->LocalGenerator, indent);
std::set<std::string> postExcludeFiles;
auto const addPostExclude =
[config, &postExcludeFiles, this](
const std::vector<std::unique_ptr<cmInstallRuntimeDependencySet::Item>>&
tgts) {
for (auto const& item : tgts) {
item->AddPostExcludeFiles(config, postExcludeFiles,
this->RuntimeDependencySet);
}
};
addPostExclude(this->RuntimeDependencySet->GetExecutables());
addPostExclude(this->RuntimeDependencySet->GetLibraries());
addPostExclude(this->RuntimeDependencySet->GetModules());
bool first = true;
for (auto const& file : postExcludeFiles) {
if (first) {
os << indent << " POST_EXCLUDE_FILES_STRICT\n";
first = false;
}
os << indent << " \"" << file << "\"\n";
}
if (!installNameTool.empty() && !this->NoInstallRPath) {
os << indent << " RPATH_PREFIX " << this->RPathPrefix << '\n';
}
os << indent << " )\n";
}

View File

@@ -0,0 +1,56 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#pragma once
#include <iosfwd>
#include <string>
#include <vector>
#include "cmInstallGenerator.h"
#include "cmListFileCache.h"
#include "cmScriptGenerator.h"
class cmLocalGenerator;
class cmInstallRuntimeDependencySet;
class cmInstallGetRuntimeDependenciesGenerator : public cmInstallGenerator
{
public:
cmInstallGetRuntimeDependenciesGenerator(
cmInstallRuntimeDependencySet* runtimeDependencySet,
std::vector<std::string> directories,
std::vector<std::string> preIncludeRegexes,
std::vector<std::string> preExcludeRegexes,
std::vector<std::string> postIncludeRegexes,
std::vector<std::string> postExcludeRegexes,
std::vector<std::string> postIncludeFiles,
std::vector<std::string> postExcludeFiles, std::string libraryComponent,
std::string frameworkComponent, bool noInstallRPath, const char* depsVar,
const char* rpathPrefix, std::vector<std::string> const& configurations,
MessageLevel message, bool exclude_from_all,
cmListFileBacktrace backtrace);
bool Compute(cmLocalGenerator* lg) override;
protected:
void GenerateScript(std::ostream& os) override;
void GenerateScriptForConfig(std::ostream& os, const std::string& config,
Indent indent) override;
private:
cmInstallRuntimeDependencySet* RuntimeDependencySet;
std::vector<std::string> Directories;
std::vector<std::string> PreIncludeRegexes;
std::vector<std::string> PreExcludeRegexes;
std::vector<std::string> PostIncludeRegexes;
std::vector<std::string> PostExcludeRegexes;
std::vector<std::string> PostIncludeFiles;
std::vector<std::string> PostExcludeFiles;
std::string LibraryComponent;
std::string FrameworkComponent;
bool NoInstallRPath;
const char* DepsVar;
const char* RPathPrefix;
cmLocalGenerator* LocalGenerator = nullptr;
};

View File

@@ -0,0 +1,276 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#include "cmInstallRuntimeDependencySetGenerator.h"
#include <ostream>
#include <string>
#include <utility>
#include <vector>
#include "cmGeneratorExpression.h"
#include "cmInstallGenerator.h"
#include "cmInstallType.h"
#include "cmListFileCache.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmOutputConverter.h"
#include "cmScriptGenerator.h"
#include "cmStringAlgorithms.h"
#include "cmake.h"
cmInstallRuntimeDependencySetGenerator::cmInstallRuntimeDependencySetGenerator(
DependencyType type, cmInstallRuntimeDependencySet* dependencySet,
std::vector<std::string> installRPaths, bool noInstallRPath,
std::string installNameDir, bool noInstallName, const char* depsVar,
const char* rpathPrefix, const char* tmpVarPrefix, std::string destination,
std::vector<std::string> const& configurations, std::string component,
std::string permissions, MessageLevel message, bool exclude_from_all,
cmListFileBacktrace backtrace)
: cmInstallGenerator(std::move(destination), configurations,
std::move(component), message, exclude_from_all, false,
std::move(backtrace))
, Type(type)
, DependencySet(dependencySet)
, InstallRPaths(std::move(installRPaths))
, NoInstallRPath(noInstallRPath)
, InstallNameDir(std::move(installNameDir))
, NoInstallName(noInstallName)
, Permissions(std::move(permissions))
, DepsVar(depsVar)
, RPathPrefix(rpathPrefix)
, TmpVarPrefix(tmpVarPrefix)
{
this->ActionsPerConfig = true;
}
bool cmInstallRuntimeDependencySetGenerator::Compute(cmLocalGenerator* lg)
{
this->LocalGenerator = lg;
return true;
}
void cmInstallRuntimeDependencySetGenerator::GenerateScriptForConfig(
std::ostream& os, const std::string& config, Indent indent)
{
if (!this->LocalGenerator->GetMakefile()
->GetSafeDefinition("CMAKE_INSTALL_NAME_TOOL")
.empty() &&
!this->NoInstallName) {
std::string installNameDir = "@rpath/";
if (!this->InstallNameDir.empty()) {
installNameDir = this->InstallNameDir;
cmGeneratorExpression::ReplaceInstallPrefix(installNameDir,
"${CMAKE_INSTALL_PREFIX}");
installNameDir = cmGeneratorExpression::Evaluate(
installNameDir, this->LocalGenerator, config);
if (installNameDir.empty()) {
this->LocalGenerator->GetMakefile()->GetCMakeInstance()->IssueMessage(
MessageType::FATAL_ERROR,
"INSTALL_NAME_DIR argument must not evaluate to an "
"empty string",
this->Backtrace);
return;
}
if (installNameDir.back() != '/') {
installNameDir += '/';
}
}
os << indent << "set(" << this->TmpVarPrefix << "_install_name_dir \""
<< installNameDir << "\")\n";
}
os << indent << "foreach(" << this->TmpVarPrefix << "_dep IN LISTS "
<< this->DepsVar << ")\n";
if (!this->LocalGenerator->GetMakefile()
->GetSafeDefinition("CMAKE_INSTALL_NAME_TOOL")
.empty()) {
std::vector<std::string> evaluatedRPaths;
for (auto const& rpath : this->InstallRPaths) {
std::string result =
cmGeneratorExpression::Evaluate(rpath, this->LocalGenerator, config);
if (!result.empty()) {
evaluatedRPaths.push_back(std::move(result));
}
}
switch (this->Type) {
case DependencyType::Library:
this->GenerateAppleLibraryScript(os, config, evaluatedRPaths,
indent.Next());
break;
case DependencyType::Framework:
this->GenerateAppleFrameworkScript(os, config, evaluatedRPaths,
indent.Next());
break;
}
} else {
std::string depVar = cmStrCat(this->TmpVarPrefix, "_dep");
this->AddInstallRule(
os, this->GetDestination(config), cmInstallType_SHARED_LIBRARY, {},
false, this->Permissions.c_str(), nullptr, nullptr,
" FOLLOW_SYMLINK_CHAIN", indent.Next(), depVar.c_str());
if (this->LocalGenerator->GetMakefile()->GetSafeDefinition(
"CMAKE_SYSTEM_NAME") == "Linux" &&
!this->NoInstallRPath) {
std::string evaluatedRPath;
for (auto const& rpath : this->InstallRPaths) {
std::string result =
cmGeneratorExpression::Evaluate(rpath, this->LocalGenerator, config);
if (!result.empty()) {
if (evaluatedRPath.empty()) {
evaluatedRPath = std::move(result);
} else {
evaluatedRPath += ':';
evaluatedRPath += result;
}
}
}
os << indent.Next() << "get_filename_component(" << this->TmpVarPrefix
<< "_dep_name \"${" << this->TmpVarPrefix << "_dep}\" NAME)\n";
if (evaluatedRPath.empty()) {
os << indent.Next() << "file(RPATH_REMOVE FILE \""
<< GetDestDirPath(
ConvertToAbsoluteDestination(this->GetDestination(config)))
<< "/${" << this->TmpVarPrefix << "_dep_name}\")\n";
} else {
os << indent.Next() << "file(RPATH_SET FILE \""
<< GetDestDirPath(
ConvertToAbsoluteDestination(this->GetDestination(config)))
<< "/${" << this->TmpVarPrefix << "_dep_name}\" NEW_RPATH "
<< cmOutputConverter::EscapeForCMake(evaluatedRPath) << ")\n";
}
}
}
os << indent << "endforeach()\n";
}
void cmInstallRuntimeDependencySetGenerator::GenerateAppleLibraryScript(
std::ostream& os, const std::string& config,
const std::vector<std::string>& evaluatedRPaths, Indent indent)
{
os << indent << "if(NOT " << this->TmpVarPrefix
<< "_dep MATCHES \"\\\\.framework/\")\n";
auto depName = cmStrCat(this->TmpVarPrefix, "_dep");
this->AddInstallRule(
os, this->GetDestination(config), cmInstallType_SHARED_LIBRARY, {}, false,
this->Permissions.c_str(), nullptr, nullptr, " FOLLOW_SYMLINK_CHAIN",
indent.Next(), depName.c_str());
os << indent.Next() << "get_filename_component(" << this->TmpVarPrefix
<< "_dep_name \"${" << this->TmpVarPrefix << "_dep}\" NAME)\n";
auto depNameVar = cmStrCat("${", this->TmpVarPrefix, "_dep_name}");
this->GenerateInstallNameFixup(os, config, evaluatedRPaths,
cmStrCat("${", this->TmpVarPrefix, "_dep}"),
depNameVar, indent.Next());
os << indent << "endif()\n";
}
void cmInstallRuntimeDependencySetGenerator::GenerateAppleFrameworkScript(
std::ostream& os, const std::string& config,
const std::vector<std::string>& evaluatedRPaths, Indent indent)
{
os << indent << "if(" << this->TmpVarPrefix
<< "_dep MATCHES \"^(.*/)?([^/]*\\\\.framework)/(.*)$\")\n"
<< indent.Next() << "set(" << this->TmpVarPrefix
<< "_dir \"${CMAKE_MATCH_1}\")\n"
<< indent.Next() << "set(" << this->TmpVarPrefix
<< "_name \"${CMAKE_MATCH_2}\")\n"
<< indent.Next() << "set(" << this->TmpVarPrefix
<< "_file \"${CMAKE_MATCH_3}\")\n"
<< indent.Next() << "set(" << this->TmpVarPrefix << "_path \"${"
<< this->TmpVarPrefix << "_dir}${" << this->TmpVarPrefix << "_name}\")\n";
auto depName = cmStrCat(this->TmpVarPrefix, "_path");
this->AddInstallRule(
os, this->GetDestination(config), cmInstallType_DIRECTORY, {}, false,
this->Permissions.c_str(), nullptr, nullptr, " USE_SOURCE_PERMISSIONS",
indent.Next(), depName.c_str());
auto depNameVar = cmStrCat("${", this->TmpVarPrefix, "_name}/${",
this->TmpVarPrefix, "_file}");
this->GenerateInstallNameFixup(os, config, evaluatedRPaths,
cmStrCat("${", this->TmpVarPrefix, "_dep}"),
depNameVar, indent.Next());
os << indent << "endif()\n";
}
void cmInstallRuntimeDependencySetGenerator::GenerateInstallNameFixup(
std::ostream& os, const std::string& config,
const std::vector<std::string>& evaluatedRPaths, const std::string& filename,
const std::string& depName, Indent indent)
{
if (!(this->NoInstallRPath && this->NoInstallName)) {
auto indent2 = indent;
if (evaluatedRPaths.empty() && this->NoInstallName) {
indent2 = indent2.Next();
os << indent << "if(" << this->RPathPrefix << "_" << filename << ")\n";
}
os << indent2 << "set(" << this->TmpVarPrefix << "_rpath_args)\n";
if (!this->NoInstallRPath) {
os << indent2 << "foreach(" << this->TmpVarPrefix << "_rpath IN LISTS "
<< this->RPathPrefix << '_' << filename << ")\n"
<< indent2.Next() << "list(APPEND " << this->TmpVarPrefix
<< "_rpath_args -delete_rpath \"${" << this->TmpVarPrefix
<< "_rpath}\")\n"
<< indent2 << "endforeach()\n";
}
os << indent2 << "execute_process(COMMAND \""
<< this->LocalGenerator->GetMakefile()->GetSafeDefinition(
"CMAKE_INSTALL_NAME_TOOL")
<< "\" ${" << this->TmpVarPrefix << "_rpath_args}\n";
if (!this->NoInstallRPath) {
for (auto const& rpath : evaluatedRPaths) {
os << indent2 << " -add_rpath "
<< cmOutputConverter::EscapeForCMake(rpath) << "\n";
}
}
if (!this->NoInstallName) {
os << indent2 << " -id \"${" << this->TmpVarPrefix
<< "_install_name_dir}" << depName << "\"\n";
}
os << indent2 << " \""
<< GetDestDirPath(
ConvertToAbsoluteDestination(this->GetDestination(config)))
<< "/" << depName << "\")\n";
if (evaluatedRPaths.empty() && this->NoInstallName) {
os << indent << "endif()\n";
}
}
}
void cmInstallRuntimeDependencySetGenerator::GenerateStripFixup(
std::ostream& os, const std::string& config, const std::string& depName,
Indent indent)
{
std::string strip =
this->LocalGenerator->GetMakefile()->GetSafeDefinition("CMAKE_STRIP");
if (!strip.empty()) {
os << indent << "if(CMAKE_INSTALL_DO_STRIP)\n"
<< indent.Next() << "execute_process(COMMAND \"" << strip << "\" ";
if (this->LocalGenerator->GetMakefile()->GetSafeDefinition(
"CMAKE_HOST_SYSTEM_NAME") == "Darwin") {
os << "-x ";
}
os << "\""
<< GetDestDirPath(
ConvertToAbsoluteDestination(this->GetDestination(config)))
<< "/" << depName << "\")\n"
<< indent << "endif()\n";
}
}
std::string cmInstallRuntimeDependencySetGenerator::GetDestination(
std::string const& config) const
{
return cmGeneratorExpression::Evaluate(this->Destination,
this->LocalGenerator, config);
}

View File

@@ -0,0 +1,74 @@
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file Copyright.txt or https://cmake.org/licensing for details. */
#pragma once
#include <iosfwd>
#include <string>
#include <vector>
#include "cmInstallGenerator.h"
#include "cmListFileCache.h"
#include "cmScriptGenerator.h"
class cmInstallRuntimeDependencySet;
class cmLocalGenerator;
class cmInstallRuntimeDependencySetGenerator : public cmInstallGenerator
{
public:
enum class DependencyType
{
Library,
Framework,
};
cmInstallRuntimeDependencySetGenerator(
DependencyType type, cmInstallRuntimeDependencySet* dependencySet,
std::vector<std::string> installRPaths, bool noInstallRPath,
std::string installNameDir, bool noInstallName, const char* depsVar,
const char* rpathPrefix, const char* tmpVarPrefix, std::string destination,
std::vector<std::string> const& configurations, std::string component,
std::string permissions, MessageLevel message, bool exclude_from_all,
cmListFileBacktrace backtrace);
bool Compute(cmLocalGenerator* lg) override;
DependencyType GetDependencyType() const { return this->Type; }
cmInstallRuntimeDependencySet* GetRuntimeDependencySet() const
{
return this->DependencySet;
}
std::string GetDestination(std::string const& config) const;
protected:
void GenerateScriptForConfig(std::ostream& os, const std::string& config,
Indent indent) override;
private:
DependencyType Type;
cmInstallRuntimeDependencySet* DependencySet;
std::vector<std::string> InstallRPaths;
bool NoInstallRPath;
std::string InstallNameDir;
bool NoInstallName;
std::string Permissions;
const char* DepsVar;
const char* RPathPrefix;
const char* TmpVarPrefix;
cmLocalGenerator* LocalGenerator = nullptr;
void GenerateAppleLibraryScript(
std::ostream& os, const std::string& config,
const std::vector<std::string>& evaluatedRPaths, Indent indent);
void GenerateAppleFrameworkScript(
std::ostream& os, const std::string& config,
const std::vector<std::string>& evaluatedRPaths, Indent indent);
void GenerateInstallNameFixup(
std::ostream& os, const std::string& config,
const std::vector<std::string>& evaluatedRPaths,
const std::string& filename, const std::string& depName, Indent indent);
void GenerateStripFixup(std::ostream& os, const std::string& config,
const std::string& depName, Indent indent);
};

View File

@@ -397,3 +397,11 @@ cmRuntimeDependencyArchive::GetRPaths() const
{
return this->RPaths;
}
bool cmRuntimeDependencyArchive::PlatformSupportsRuntimeDependencies(
const std::string& platform)
{
static const std::set<std::string> supportedPlatforms = { "Windows", "Linux",
"Darwin" };
return supportedPlatforms.count(platform);
}

View File

@@ -53,6 +53,8 @@ public:
const std::set<std::string>& GetUnresolvedPaths() const;
const std::map<std::string, std::vector<std::string>>& GetRPaths() const;
static bool PlatformSupportsRuntimeDependencies(const std::string& platform);
private:
cmExecutionStatus& Status;
std::unique_ptr<cmBinUtilsLinker> Linker;

View File

@@ -44,7 +44,7 @@ function(check_installed expect)
do not match what we expected:
${expect}
in directory:
${CMAKE_INSTALL_PREFIX}" PARENT_SCOPE)
${CMAKE_INSTALL_PREFIX}\n" PARENT_SCOPE)
endif()
endfunction()
@@ -174,6 +174,21 @@ run_install_test(FILES-PERMISSIONS)
run_install_test(TARGETS-RPATH)
run_install_test(InstallRequiredSystemLibraries)
if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")
run_cmake(TARGETS-RUNTIME_DEPENDENCIES-macos-two-bundle)
run_cmake(TARGETS-RUNTIME_DEPENDENCIES-macos-no-framework)
endif()
if(CMAKE_SYSTEM_NAME MATCHES "^(Linux|Darwin|Windows)$")
run_install_test(TARGETS-RUNTIME_DEPENDENCIES-nodep)
run_install_test(TARGETS-RUNTIME_DEPENDENCIES-empty)
set(RunCMake_TEST_OPTIONS "-DCMAKE_SYSTEM_NAME:STRING=${CMAKE_SYSTEM_NAME}")
run_cmake(TARGETS-RUNTIME_DEPENDENCIES-cross)
unset(RunCMake_TEST_OPTIONS)
else()
run_cmake(TARGETS-RUNTIME_DEPENDENCIES-unsupported)
endif()
set(run_install_test_components 1)
run_install_test(FILES-EXCLUDE_FROM_ALL)
run_install_test(TARGETS-EXCLUDE_FROM_ALL)

View File

@@ -0,0 +1,4 @@
^CMake Error at TARGETS-RUNTIME_DEPENDENCIES-cross\.cmake:[0-9]+ \(install\):
install TARGETS RUNTIME_DEPENDENCIES is not supported when cross-compiling\.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)$

View File

@@ -0,0 +1,4 @@
enable_language(C)
add_executable(exe main.c)
install(TARGETS exe RUNTIME_DEPENDENCIES)

View File

@@ -0,0 +1 @@
check_installed([[^static;static/(liblib\.a|lib\.lib)$]])

View File

@@ -0,0 +1,9 @@
enable_language(C)
add_library(lib STATIC obj1.c)
install(TARGETS lib RUNTIME_DEPENDENCIES
LIBRARY DESTINATION lib
ARCHIVE DESTINATION static
FRAMEWORK DESTINATION fw
)

View File

@@ -0,0 +1,4 @@
^CMake Error at TARGETS-RUNTIME_DEPENDENCIES-macos-no-framework\.cmake:[0-9]+ \(install\):
install TARGETS RUNTIME_DEPENDENCIES given no FRAMEWORK DESTINATION
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$

View File

@@ -0,0 +1,4 @@
enable_language(C)
add_executable(exe main.c)
install(TARGETS exe RUNTIME_DEPENDENCIES)

View File

@@ -0,0 +1,4 @@
^CMake Error at TARGETS-RUNTIME_DEPENDENCIES-macos-two-bundle\.cmake:[0-9]+ \(install\):
install A runtime dependency set may only have one bundle executable\.
Call Stack \(most recent call first\):
CMakeLists.txt:[0-9]+ \(include\)$

View File

@@ -0,0 +1,10 @@
enable_language(C)
add_executable(exe1 MACOSX_BUNDLE main.c)
add_executable(exe2 MACOSX_BUNDLE main.c)
install(TARGETS exe1 exe2
RUNTIME_DEPENDENCIES
BUNDLE DESTINATION bundles
FRAMEWORK DESTINATION fw
)

View File

@@ -0,0 +1 @@
check_installed([[^bin;bin/exe(\.exe)?$]])

View File

@@ -0,0 +1,9 @@
enable_language(C)
add_executable(exe main.c)
install(TARGETS exe
RUNTIME_DEPENDENCIES
PRE_EXCLUDE_REGEXES ".*"
FRAMEWORK DESTINATION fw
)

View File

@@ -0,0 +1,5 @@
^CMake Error at TARGETS-RUNTIME_DEPENDENCIES-unsupported\.cmake:[0-9]+ \(install\):
install TARGETS RUNTIME_DEPENDENCIES is not supported on system "[^
]*"
Call Stack \(most recent call first\):
CMakeLists\.txt:[0-9]+ \(include\)$

View File

@@ -0,0 +1,4 @@
enable_language(C)
add_executable(exe main.c)
install(TARGETS exe RUNTIME_DEPENDENCIES)

View File

@@ -387,8 +387,10 @@ CMAKE_CXX_SOURCES="\
cmInstallFilesCommand \
cmInstallFilesGenerator \
cmInstallGenerator \
cmInstallGetRuntimeDependenciesGenerator \
cmInstallImportedRuntimeArtifactsGenerator \
cmInstallRuntimeDependencySet \
cmInstallRuntimeDependencySetGenerator \
cmInstallScriptGenerator \
cmInstallSubdirectoryGenerator \
cmInstallTargetGenerator \