diff options
author | Brad King <brad.king@kitware.com> | 2021-06-08 12:08:57 (GMT) |
---|---|---|
committer | Kitware Robot <kwrobot@kitware.com> | 2021-06-08 12:09:04 (GMT) |
commit | acb25d50d9d37e93cafcbbc4401e1b45029b6461 (patch) | |
tree | 078d3f511f584e4f924a9380f9539aa122a2263e /Source | |
parent | 3653dc60690e6fc33d9e7bf44186b82587cf21a7 (diff) | |
parent | 8d898cb3e10d6ad44bbe542b7b219a0b788b2a0d (diff) | |
download | CMake-acb25d50d9d37e93cafcbbc4401e1b45029b6461.zip CMake-acb25d50d9d37e93cafcbbc4401e1b45029b6461.tar.gz CMake-acb25d50d9d37e93cafcbbc4401e1b45029b6461.tar.bz2 |
Merge topic 'install-with-runtime-dependencies'
8d898cb3e1 FileAPI: Add integration for runtime dependency installers
72f2448e82 Help: Add documentation for runtime dependency installation
0c3c6acaff Tests: Add tests for new options
4910132d8c install: Add RUNTIME_DEPENDENCY_SET mode
bc8a4a06a4 install(IMPORTED_RUNTIME_ARTIFACTS): Add RUNTIME_DEPENDENCY_SET option
3e7d3c252a install(TARGETS): Add RUNTIME_DEPENDENCY_SET argument
ed3633d88c install(TARGETS): Add RUNTIME_DEPENDENCIES option
f2617cf8e6 Source: Add cmInstallRuntimeDependencySet
...
Acked-by: Kitware Robot <kwrobot@kitware.com>
Merge-request: !6186
Diffstat (limited to 'Source')
-rw-r--r-- | Source/CMakeLists.txt | 6 | ||||
-rw-r--r-- | Source/cmBinUtilsMacOSMachOLinker.cxx | 22 | ||||
-rw-r--r-- | Source/cmBinUtilsMacOSMachOLinker.h | 2 | ||||
-rw-r--r-- | Source/cmFileAPICodemodel.cxx | 28 | ||||
-rw-r--r-- | Source/cmFileCommand.cxx | 211 | ||||
-rw-r--r-- | Source/cmGlobalGenerator.cxx | 24 | ||||
-rw-r--r-- | Source/cmGlobalGenerator.h | 11 | ||||
-rw-r--r-- | Source/cmInstallCommand.cxx | 361 | ||||
-rw-r--r-- | Source/cmInstallGenerator.cxx | 51 | ||||
-rw-r--r-- | Source/cmInstallGenerator.h | 3 | ||||
-rw-r--r-- | Source/cmInstallGetRuntimeDependenciesGenerator.cxx | 206 | ||||
-rw-r--r-- | Source/cmInstallGetRuntimeDependenciesGenerator.h | 56 | ||||
-rw-r--r-- | Source/cmInstallRuntimeDependencySet.cxx | 95 | ||||
-rw-r--r-- | Source/cmInstallRuntimeDependencySet.h | 163 | ||||
-rw-r--r-- | Source/cmInstallRuntimeDependencySetGenerator.cxx | 276 | ||||
-rw-r--r-- | Source/cmInstallRuntimeDependencySetGenerator.h | 74 | ||||
-rw-r--r-- | Source/cmRuntimeDependencyArchive.cxx | 39 | ||||
-rw-r--r-- | Source/cmRuntimeDependencyArchive.h | 26 | ||||
-rw-r--r-- | Source/cmSystemTools.cxx | 210 | ||||
-rw-r--r-- | Source/cmSystemTools.h | 6 |
20 files changed, 1665 insertions, 205 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 844a2ee..9a18184 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -345,6 +345,8 @@ set(SRCS cmGraphVizWriter.h cmInstallGenerator.h cmInstallGenerator.cxx + cmInstallGetRuntimeDependenciesGenerator.h + cmInstallGetRuntimeDependenciesGenerator.cxx cmInstallExportGenerator.cxx cmInstalledFile.h cmInstalledFile.cxx @@ -352,6 +354,10 @@ set(SRCS cmInstallFilesGenerator.cxx cmInstallImportedRuntimeArtifactsGenerator.h cmInstallImportedRuntimeArtifactsGenerator.cxx + cmInstallRuntimeDependencySet.h + cmInstallRuntimeDependencySet.cxx + cmInstallRuntimeDependencySetGenerator.h + cmInstallRuntimeDependencySetGenerator.cxx cmInstallScriptGenerator.h cmInstallScriptGenerator.cxx cmInstallSubdirectoryGenerator.h diff --git a/Source/cmBinUtilsMacOSMachOLinker.cxx b/Source/cmBinUtilsMacOSMachOLinker.cxx index 0f47146..47f77d8 100644 --- a/Source/cmBinUtilsMacOSMachOLinker.cxx +++ b/Source/cmBinUtilsMacOSMachOLinker.cxx @@ -65,18 +65,18 @@ bool cmBinUtilsMacOSMachOLinker::ScanDependencies( if (!executableFile.empty()) { executablePath = cmSystemTools::GetFilenamePath(executableFile); } - return this->ScanDependencies(file, executablePath); -} - -bool cmBinUtilsMacOSMachOLinker::ScanDependencies( - std::string const& file, std::string const& executablePath) -{ std::vector<std::string> libs; std::vector<std::string> rpaths; if (!this->Tool->GetFileInfo(file, libs, rpaths)) { return false; } + return this->ScanDependencies(file, libs, rpaths, executablePath); +} +bool cmBinUtilsMacOSMachOLinker::ScanDependencies( + std::string const& file, std::vector<std::string> const& libs, + std::vector<std::string> const& rpaths, std::string const& executablePath) +{ std::string loaderPath = cmSystemTools::GetFilenamePath(file); return this->GetFileDependencies(libs, executablePath, loaderPath, rpaths); } @@ -98,8 +98,14 @@ bool cmBinUtilsMacOSMachOLinker::GetFileDependencies( !IsMissingSystemDylib(path)) { auto filename = cmSystemTools::GetFilenameName(path); bool unique; - this->Archive->AddResolvedPath(filename, path, unique); - if (unique && !this->ScanDependencies(path, executablePath)) { + std::vector<std::string> libs; + std::vector<std::string> depRpaths; + if (!this->Tool->GetFileInfo(path, libs, depRpaths)) { + return false; + } + this->Archive->AddResolvedPath(filename, path, unique, depRpaths); + if (unique && + !this->ScanDependencies(path, libs, depRpaths, executablePath)) { return false; } } diff --git a/Source/cmBinUtilsMacOSMachOLinker.h b/Source/cmBinUtilsMacOSMachOLinker.h index 1c4a5fc..eae23cc 100644 --- a/Source/cmBinUtilsMacOSMachOLinker.h +++ b/Source/cmBinUtilsMacOSMachOLinker.h @@ -27,6 +27,8 @@ private: std::unique_ptr<cmBinUtilsMacOSMachOGetRuntimeDependenciesTool> Tool; bool ScanDependencies(std::string const& file, + std::vector<std::string> const& libs, + std::vector<std::string> const& rpaths, std::string const& executablePath); bool GetFileDependencies(std::vector<std::string> const& names, diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx index efe1ab6..2d6745c 100644 --- a/Source/cmFileAPICodemodel.cxx +++ b/Source/cmFileAPICodemodel.cxx @@ -15,6 +15,7 @@ #include <utility> #include <vector> +#include <cm/string_view> #include <cmext/algorithm> #include <cm3p/json/value.h> @@ -29,7 +30,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 "cmInstallSubdirectoryGenerator.h" #include "cmInstallTargetGenerator.h" @@ -877,8 +881,10 @@ Json::Value DirectoryObject::DumpInstaller(cmInstallGenerator* gen) assert(gen); Json::Value installer = Json::objectValue; - // Exclude subdirectory installers. They are implementation details. - if (dynamic_cast<cmInstallSubdirectoryGenerator*>(gen)) { + // Exclude subdirectory installers and file(GET_RUNTIME_DEPENDENCIES) + // installers. They are implementation details. + if (dynamic_cast<cmInstallSubdirectoryGenerator*>(gen) || + dynamic_cast<cmInstallGetRuntimeDependenciesGenerator*>(gen)) { return installer; } @@ -1020,6 +1026,24 @@ Json::Value DirectoryObject::DumpInstaller(cmInstallGenerator* gen) if (installImportedRuntimeArtifacts->GetOptional()) { installer["isOptional"] = true; } + } else if (auto* installRuntimeDependencySet = + dynamic_cast<cmInstallRuntimeDependencySetGenerator*>(gen)) { + installer["type"] = "runtimeDependencySet"; + installer["destination"] = + installRuntimeDependencySet->GetDestination(this->Config); + std::string name( + installRuntimeDependencySet->GetRuntimeDependencySet()->GetName()); + if (!name.empty()) { + installer["runtimeDependencySetName"] = name; + } + switch (installRuntimeDependencySet->GetDependencyType()) { + case cmInstallRuntimeDependencySetGenerator::DependencyType::Framework: + installer["runtimeDependencySetType"] = "framework"; + break; + case cmInstallRuntimeDependencySetGenerator::DependencyType::Library: + installer["runtimeDependencySetType"] = "library"; + break; + } } // Add fields common to all install generators. diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index f2d4cda..0ad59c7 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -990,50 +990,42 @@ bool HandleRPathChangeCommand(std::vector<std::string> const& args, { // Evaluate arguments. std::string file; - const char* oldRPath = nullptr; - const char* newRPath = nullptr; + std::string oldRPath; + std::string newRPath; bool removeEnvironmentRPath = false; - enum Doing - { - DoingNone, - DoingFile, - DoingOld, - DoingNew - }; - Doing doing = DoingNone; - for (unsigned int i = 1; i < args.size(); ++i) { - if (args[i] == "OLD_RPATH") { - doing = DoingOld; - } else if (args[i] == "NEW_RPATH") { - doing = DoingNew; - } else if (args[i] == "FILE") { - doing = DoingFile; - } else if (args[i] == "INSTALL_REMOVE_ENVIRONMENT_RPATH") { - removeEnvironmentRPath = true; - } else if (doing == DoingFile) { - file = args[i]; - doing = DoingNone; - } else if (doing == DoingOld) { - oldRPath = args[i].c_str(); - doing = DoingNone; - } else if (doing == DoingNew) { - newRPath = args[i].c_str(); - doing = DoingNone; - } else { - status.SetError( - cmStrCat("RPATH_CHANGE given unknown argument ", args[i])); - return false; - } + cmArgumentParser<void> parser; + std::vector<std::string> unknownArgs; + std::vector<std::string> missingArgs; + std::vector<std::string> parsedArgs; + parser.Bind("FILE"_s, file) + .Bind("OLD_RPATH"_s, oldRPath) + .Bind("NEW_RPATH"_s, newRPath) + .Bind("INSTALL_REMOVE_ENVIRONMENT_RPATH"_s, removeEnvironmentRPath); + parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs, + &parsedArgs); + if (!unknownArgs.empty()) { + status.SetError( + cmStrCat("RPATH_CHANGE given unknown argument ", unknownArgs.front())); + return false; + } + if (!missingArgs.empty()) { + status.SetError(cmStrCat("RPATH_CHANGE \"", missingArgs.front(), + "\" argument not given value.")); + return false; } if (file.empty()) { status.SetError("RPATH_CHANGE not given FILE option."); return false; } - if (!oldRPath) { + if (oldRPath.empty() && + std::find(parsedArgs.begin(), parsedArgs.end(), "OLD_RPATH") == + parsedArgs.end()) { status.SetError("RPATH_CHANGE not given OLD_RPATH option."); return false; } - if (!newRPath) { + if (newRPath.empty() && + std::find(parsedArgs.begin(), parsedArgs.end(), "NEW_RPATH") == + parsedArgs.end()) { status.SetError("RPATH_CHANGE not given NEW_RPATH option."); return false; } @@ -1065,28 +1057,85 @@ bool HandleRPathChangeCommand(std::vector<std::string> const& args, return success; } +bool HandleRPathSetCommand(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + // Evaluate arguments. + std::string file; + std::string newRPath; + cmArgumentParser<void> parser; + std::vector<std::string> unknownArgs; + std::vector<std::string> missingArgs; + std::vector<std::string> parsedArgs; + parser.Bind("FILE"_s, file).Bind("NEW_RPATH"_s, newRPath); + parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs, + &parsedArgs); + if (!unknownArgs.empty()) { + status.SetError(cmStrCat("RPATH_SET given unrecognized argument \"", + unknownArgs.front(), "\".")); + return false; + } + if (!missingArgs.empty()) { + status.SetError(cmStrCat("RPATH_SET \"", missingArgs.front(), + "\" argument not given value.")); + return false; + } + if (file.empty()) { + status.SetError("RPATH_SET not given FILE option."); + return false; + } + if (newRPath.empty() && + std::find(parsedArgs.begin(), parsedArgs.end(), "NEW_RPATH") == + parsedArgs.end()) { + status.SetError("RPATH_SET not given NEW_RPATH option."); + return false; + } + if (!cmSystemTools::FileExists(file, true)) { + status.SetError( + cmStrCat("RPATH_SET given FILE \"", file, "\" that does not exist.")); + return false; + } + bool success = true; + cmFileTimes const ft(file); + std::string emsg; + bool changed; + + if (!cmSystemTools::SetRPath(file, newRPath, &emsg, &changed)) { + status.SetError(cmStrCat("RPATH_SET could not write new RPATH:\n ", + newRPath, "\nto the file:\n ", file, "\n", + emsg)); + success = false; + } + if (success) { + if (changed) { + std::string message = + cmStrCat("Set runtime path of \"", file, "\" to \"", newRPath, '"'); + status.GetMakefile().DisplayStatus(message, -1); + } + ft.Store(file); + } + return success; +} + bool HandleRPathRemoveCommand(std::vector<std::string> const& args, cmExecutionStatus& status) { // Evaluate arguments. std::string file; - enum Doing - { - DoingNone, - DoingFile - }; - Doing doing = DoingNone; - for (unsigned int i = 1; i < args.size(); ++i) { - if (args[i] == "FILE") { - doing = DoingFile; - } else if (doing == DoingFile) { - file = args[i]; - doing = DoingNone; - } else { - status.SetError( - cmStrCat("RPATH_REMOVE given unknown argument ", args[i])); - return false; - } + cmArgumentParser<void> parser; + std::vector<std::string> unknownArgs; + std::vector<std::string> missingArgs; + parser.Bind("FILE"_s, file); + parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs); + if (!unknownArgs.empty()) { + status.SetError( + cmStrCat("RPATH_REMOVE given unknown argument ", unknownArgs.front())); + return false; + } + if (!missingArgs.empty()) { + status.SetError(cmStrCat("RPATH_REMOVE \"", missingArgs.front(), + "\" argument not given value.")); + return false; } if (file.empty()) { status.SetError("RPATH_REMOVE not given FILE option."); @@ -1123,36 +1172,31 @@ bool HandleRPathCheckCommand(std::vector<std::string> const& args, { // Evaluate arguments. std::string file; - const char* rpath = nullptr; - enum Doing - { - DoingNone, - DoingFile, - DoingRPath - }; - Doing doing = DoingNone; - for (unsigned int i = 1; i < args.size(); ++i) { - if (args[i] == "RPATH") { - doing = DoingRPath; - } else if (args[i] == "FILE") { - doing = DoingFile; - } else if (doing == DoingFile) { - file = args[i]; - doing = DoingNone; - } else if (doing == DoingRPath) { - rpath = args[i].c_str(); - doing = DoingNone; - } else { - status.SetError( - cmStrCat("RPATH_CHECK given unknown argument ", args[i])); - return false; - } + std::string rpath; + cmArgumentParser<void> parser; + std::vector<std::string> unknownArgs; + std::vector<std::string> missingArgs; + std::vector<std::string> parsedArgs; + parser.Bind("FILE"_s, file).Bind("RPATH"_s, rpath); + parser.Parse(cmMakeRange(args).advance(1), &unknownArgs, &missingArgs, + &parsedArgs); + if (!unknownArgs.empty()) { + status.SetError( + cmStrCat("RPATH_CHECK given unknown argument ", unknownArgs.front())); + return false; + } + if (!missingArgs.empty()) { + status.SetError(cmStrCat("RPATH_CHECK \"", missingArgs.front(), + "\" argument not given value.")); + return false; } if (file.empty()) { status.SetError("RPATH_CHECK not given FILE option."); return false; } - if (!rpath) { + if (rpath.empty() && + std::find(parsedArgs.begin(), parsedArgs.end(), "RPATH") == + parsedArgs.end()) { status.SetError("RPATH_CHECK not given RPATH option."); return false; } @@ -3000,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, "\"")); @@ -3032,6 +3075,7 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args, std::string ResolvedDependenciesVar; std::string UnresolvedDependenciesVar; std::string ConflictingDependenciesPrefix; + std::string RPathPrefix; std::string BundleExecutable; std::vector<std::string> Executables; std::vector<std::string> Libraries; @@ -3053,6 +3097,7 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args, &Arguments::UnresolvedDependenciesVar) .Bind("CONFLICTING_DEPENDENCIES_PREFIX"_s, &Arguments::ConflictingDependenciesPrefix) + .Bind("RPATH_PREFIX"_s, &Arguments::RPathPrefix) .Bind("BUNDLE_EXECUTABLE"_s, &Arguments::BundleExecutable) .Bind("EXECUTABLES"_s, &Arguments::Executables) .Bind("LIBRARIES"_s, &Arguments::Libraries) @@ -3135,6 +3180,11 @@ bool HandleGetRuntimeDependenciesCommand(std::vector<std::string> const& args, if (unique) { deps.push_back(firstPath); + if (!parsedArgs.RPathPrefix.empty()) { + status.GetMakefile().AddDefinition( + parsedArgs.RPathPrefix + "_" + firstPath, + cmJoin(archive.GetRPaths().at(firstPath), ";")); + } } else if (!parsedArgs.ConflictingDependenciesPrefix.empty()) { conflictingDeps.push_back(val.first); std::vector<std::string> paths; @@ -3741,6 +3791,7 @@ bool cmFileCommand(std::vector<std::string> const& args, { "DIFFERENT"_s, HandleDifferentCommand }, { "RPATH_CHANGE"_s, HandleRPathChangeCommand }, { "CHRPATH"_s, HandleRPathChangeCommand }, + { "RPATH_SET"_s, HandleRPathSetCommand }, { "RPATH_CHECK"_s, HandleRPathCheckCommand }, { "RPATH_REMOVE"_s, HandleRPathRemoveCommand }, { "READ_ELF"_s, HandleReadElfCommand }, diff --git a/Source/cmGlobalGenerator.cxx b/Source/cmGlobalGenerator.cxx index d7da0d3..9193778 100644 --- a/Source/cmGlobalGenerator.cxx +++ b/Source/cmGlobalGenerator.cxx @@ -36,6 +36,7 @@ #include "cmGeneratorExpression.h" #include "cmGeneratorTarget.h" #include "cmInstallGenerator.h" +#include "cmInstallRuntimeDependencySet.h" #include "cmLinkLineComputer.h" #include "cmListFileCache.h" #include "cmLocalGenerator.h" @@ -3332,3 +3333,26 @@ bool cmGlobalGenerator::GenerateCPackPropertiesFile() return true; } + +cmInstallRuntimeDependencySet* +cmGlobalGenerator::CreateAnonymousRuntimeDependencySet() +{ + auto set = cm::make_unique<cmInstallRuntimeDependencySet>(); + auto* retval = set.get(); + this->RuntimeDependencySets.push_back(std::move(set)); + return retval; +} + +cmInstallRuntimeDependencySet* cmGlobalGenerator::GetNamedRuntimeDependencySet( + const std::string& name) +{ + auto it = this->RuntimeDependencySetsByName.find(name); + if (it == this->RuntimeDependencySetsByName.end()) { + auto set = cm::make_unique<cmInstallRuntimeDependencySet>(name); + it = + this->RuntimeDependencySetsByName.insert(std::make_pair(name, set.get())) + .first; + this->RuntimeDependencySets.push_back(std::move(set)); + } + return it->second; +} diff --git a/Source/cmGlobalGenerator.h b/Source/cmGlobalGenerator.h index fee0359..147146e 100644 --- a/Source/cmGlobalGenerator.h +++ b/Source/cmGlobalGenerator.h @@ -42,6 +42,7 @@ class cmDirectoryId; class cmExportBuildFileGenerator; class cmExternalMakefileProjectGenerator; class cmGeneratorTarget; +class cmInstallRuntimeDependencySet; class cmLinkLineComputer; class cmLocalGenerator; class cmMakefile; @@ -528,6 +529,11 @@ public: std::string NewDeferId(); + cmInstallRuntimeDependencySet* CreateAnonymousRuntimeDependencySet(); + + cmInstallRuntimeDependencySet* GetNamedRuntimeDependencySet( + const std::string& name); + protected: // for a project collect all its targets by following depend // information, and also collect all the targets @@ -747,6 +753,11 @@ private: std::unordered_set<std::string> GeneratedFiles; + std::vector<std::unique_ptr<cmInstallRuntimeDependencySet>> + RuntimeDependencySets; + std::map<std::string, cmInstallRuntimeDependencySet*> + RuntimeDependencySetsByName; + #if !defined(CMAKE_BOOTSTRAP) // Pool of file locks cmFileLockPool FileLockPool; diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index c9bb467..79109b5 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -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,25 @@ 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::string runtimeDependencySetArg; 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.Bind("RUNTIME_DEPENDENCY_SET"_s, runtimeDependencySetArg); + 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 +537,49 @@ bool HandleTargetsMode(std::vector<std::string> const& args, return false; } + cmInstallRuntimeDependencySet* runtimeDependencySet = nullptr; + if (withRuntimeDependencies) { + if (!runtimeDependencySetArg.empty()) { + status.SetError("TARGETS cannot have both RUNTIME_DEPENDENCIES and " + "RUNTIME_DEPENDENCY_SET."); + return false; + } + 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(); + } else if (!runtimeDependencySetArg.empty()) { + auto system = helper.Makefile->GetSafeDefinition("CMAKE_HOST_SYSTEM_NAME"); + if (!cmRuntimeDependencyArchive::PlatformSupportsRuntimeDependencies( + system)) { + status.SetError(cmStrCat( + "TARGETS RUNTIME_DEPENDENCY_SET is not supported on system \"", system, + '"')); + return false; + } + runtimeDependencySet = + helper.Makefile->GetGlobalGenerator()->GetNamedRuntimeDependencySet( + runtimeDependencySetArg); + } + // Select the mode for installing symlinks to versioned shared libraries. cmInstallTargetGenerator::NamelinkModeType namelinkMode = cmInstallTargetGenerator::NamelinkModeNone; @@ -546,6 +724,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 +772,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 +817,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 +872,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 +886,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 +1017,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) { @@ -895,9 +1098,11 @@ bool HandleImportedRuntimeArtifactsMode(std::vector<std::string> const& args, // now parse the generic args (i.e. the ones not specialized on LIBRARY, // RUNTIME etc. (see above) std::vector<std::string> targetList; + std::string runtimeDependencySetArg; std::vector<std::string> unknownArgs; cmInstallCommandArguments genericArgs(helper.DefaultComponentName); - genericArgs.Bind("IMPORTED_RUNTIME_ARTIFACTS"_s, targetList); + genericArgs.Bind("IMPORTED_RUNTIME_ARTIFACTS"_s, targetList) + .Bind("RUNTIME_DEPENDENCY_SET"_s, runtimeDependencySetArg); genericArgs.Parse(genericArgVector, &unknownArgs); bool success = genericArgs.Finalize(); @@ -936,6 +1141,22 @@ bool HandleImportedRuntimeArtifactsMode(std::vector<std::string> const& args, return false; } + cmInstallRuntimeDependencySet* runtimeDependencySet = nullptr; + if (!runtimeDependencySetArg.empty()) { + auto system = helper.Makefile->GetSafeDefinition("CMAKE_HOST_SYSTEM_NAME"); + if (!cmRuntimeDependencyArchive::PlatformSupportsRuntimeDependencies( + system)) { + status.SetError( + cmStrCat("IMPORTED_RUNTIME_ARTIFACTS RUNTIME_DEPENDENCY_SET is not " + "supported on system \"", + system, '"')); + return false; + } + runtimeDependencySet = + helper.Makefile->GetGlobalGenerator()->GetNamedRuntimeDependencySet( + runtimeDependencySetArg); + } + // Check if there is something to do. if (targetList.empty()) { return true; @@ -1014,6 +1235,9 @@ bool HandleImportedRuntimeArtifactsMode(std::vector<std::string> const& args, if (target.IsDLLPlatform()) { runtimeGenerator = createInstallGenerator( target, runtimeArgs, helper.GetRuntimeDestination(&runtimeArgs)); + if (runtimeDependencySet) { + runtimeDependencySet->AddLibrary(runtimeGenerator.get()); + } } else if (target.IsFrameworkOnApple()) { if (frameworkArgs.GetDestination().empty()) { status.SetError(cmStrCat("IMPORTED_RUNTIME_ARTIFACTS given no " @@ -1024,14 +1248,23 @@ bool HandleImportedRuntimeArtifactsMode(std::vector<std::string> const& args, } frameworkGenerator = createInstallGenerator( target, frameworkArgs, frameworkArgs.GetDestination()); + if (runtimeDependencySet) { + runtimeDependencySet->AddLibrary(frameworkGenerator.get()); + } } else { libraryGenerator = createInstallGenerator( target, libraryArgs, helper.GetLibraryDestination(&libraryArgs)); + if (runtimeDependencySet) { + runtimeDependencySet->AddLibrary(libraryGenerator.get()); + } } break; case cmStateEnums::MODULE_LIBRARY: libraryGenerator = createInstallGenerator( target, libraryArgs, helper.GetLibraryDestination(&libraryArgs)); + if (runtimeDependencySet) { + runtimeDependencySet->AddModule(libraryGenerator.get()); + } break; case cmStateEnums::EXECUTABLE: if (target.IsAppBundleOnApple()) { @@ -1044,9 +1277,18 @@ bool HandleImportedRuntimeArtifactsMode(std::vector<std::string> const& args, } bundleGenerator = createInstallGenerator( target, bundleArgs, bundleArgs.GetDestination()); + if (runtimeDependencySet) { + if (!AddBundleExecutable(helper, runtimeDependencySet, + bundleGenerator.get())) { + return false; + } + } } else { runtimeGenerator = createInstallGenerator( target, runtimeArgs, helper.GetRuntimeDestination(&runtimeArgs)); + if (runtimeDependencySet) { + runtimeDependencySet->AddExecutable(runtimeGenerator.get()); + } } break; default: @@ -1700,6 +1942,120 @@ bool HandleExportMode(std::vector<std::string> const& args, return true; } +bool HandleRuntimeDependencySetMode(std::vector<std::string> const& args, + cmExecutionStatus& status) +{ + Helper helper(status); + + auto system = helper.Makefile->GetSafeDefinition("CMAKE_HOST_SYSTEM_NAME"); + if (!cmRuntimeDependencyArchive::PlatformSupportsRuntimeDependencies( + system)) { + status.SetError(cmStrCat( + "RUNTIME_DEPENDENCY_SET is not supported on system \"", system, '"')); + return false; + } + + // This is the RUNTIME_DEPENDENCY_SET mode. + cmInstallRuntimeDependencySet* runtimeDependencySet; + + struct ArgVectors + { + std::vector<std::string> Library; + std::vector<std::string> Runtime; + std::vector<std::string> Framework; + }; + + static auto const argHelper = cmArgumentParser<ArgVectors>{} + .Bind("LIBRARY"_s, &ArgVectors::Library) + .Bind("RUNTIME"_s, &ArgVectors::Runtime) + .Bind("FRAMEWORK"_s, &ArgVectors::Framework); + + std::vector<std::string> genericArgVector; + ArgVectors const argVectors = argHelper.Parse(args, &genericArgVector); + + // now parse the generic args (i.e. the ones not specialized on LIBRARY, + // RUNTIME, FRAMEWORK etc. (see above) + // These generic args also contain the runtime dependency set + std::string runtimeDependencySetArg; + std::vector<std::string> runtimeDependencyArgVector; + std::vector<std::string> parsedArgs; + cmInstallCommandArguments genericArgs(helper.DefaultComponentName); + genericArgs.Bind("RUNTIME_DEPENDENCY_SET"_s, runtimeDependencySetArg); + genericArgs.Parse(genericArgVector, &runtimeDependencyArgVector, nullptr, + &parsedArgs); + bool success = genericArgs.Finalize(); + + cmInstallCommandArguments libraryArgs(helper.DefaultComponentName); + cmInstallCommandArguments runtimeArgs(helper.DefaultComponentName); + cmInstallCommandArguments frameworkArgs(helper.DefaultComponentName); + + // Now also parse the file(GET_RUNTIME_DEPENDENCY) args + std::vector<std::string> unknownArgs; + auto runtimeDependencyArgs = RuntimeDependenciesArgHelper.Parse( + runtimeDependencyArgVector, &unknownArgs); + + // now parse the args for specific parts of the target (e.g. LIBRARY, + // RUNTIME, FRAMEWORK etc. + libraryArgs.Parse(argVectors.Library, &unknownArgs); + runtimeArgs.Parse(argVectors.Runtime, &unknownArgs); + frameworkArgs.Parse(argVectors.Framework, &unknownArgs); + + libraryArgs.SetGenericArguments(&genericArgs); + runtimeArgs.SetGenericArguments(&genericArgs); + frameworkArgs.SetGenericArguments(&genericArgs); + + success = success && libraryArgs.Finalize(); + success = success && runtimeArgs.Finalize(); + success = success && frameworkArgs.Finalize(); + + if (!success) { + return false; + } + + if (!unknownArgs.empty()) { + helper.SetError( + cmStrCat("RUNTIME_DEPENDENCY_SET given unknown argument \"", + unknownArgs.front(), "\".")); + return false; + } + + if (runtimeDependencySetArg.empty()) { + helper.SetError( + "RUNTIME_DEPENDENCY_SET not given a runtime dependency set."); + return false; + } + + runtimeDependencySet = + helper.Makefile->GetGlobalGenerator()->GetNamedRuntimeDependencySet( + runtimeDependencySetArg); + + bool installsRuntime = false; + bool installsLibrary = false; + bool installsFramework = false; + + AddInstallRuntimeDependenciesGenerator( + helper, runtimeDependencySet, runtimeArgs, libraryArgs, frameworkArgs, + std::move(runtimeDependencyArgs), installsRuntime, installsLibrary, + installsFramework); + + // Tell the global generator about any installation component names + // specified + if (installsLibrary) { + helper.Makefile->GetGlobalGenerator()->AddInstallComponent( + libraryArgs.GetComponent()); + } + if (installsRuntime) { + helper.Makefile->GetGlobalGenerator()->AddInstallComponent( + runtimeArgs.GetComponent()); + } + if (installsFramework) { + helper.Makefile->GetGlobalGenerator()->AddInstallComponent( + frameworkArgs.GetComponent()); + } + + return true; +} + bool Helper::MakeFilesFullPath(const char* modeName, const std::vector<std::string>& relFiles, std::vector<std::string>& absFiles) @@ -1934,6 +2290,7 @@ bool cmInstallCommand(std::vector<std::string> const& args, { "DIRECTORY"_s, HandleDirectoryMode }, { "EXPORT"_s, HandleExportMode }, { "EXPORT_ANDROID_MK"_s, HandleExportAndroidMKMode }, + { "RUNTIME_DEPENDENCY_SET"_s, HandleRuntimeDependencySetMode }, }; return subcommand(args[0], args, status); diff --git a/Source/cmInstallGenerator.cxx b/Source/cmInstallGenerator.cxx index 9f0d119..4cfeb47 100644 --- a/Source/cmInstallGenerator.cxx +++ b/Source/cmInstallGenerator.cxx @@ -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 << " "; diff --git a/Source/cmInstallGenerator.h b/Source/cmInstallGenerator.h index 97acb88..d342c99 100644 --- a/Source/cmInstallGenerator.h +++ b/Source/cmInstallGenerator.h @@ -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. */ diff --git a/Source/cmInstallGetRuntimeDependenciesGenerator.cxx b/Source/cmInstallGetRuntimeDependenciesGenerator.cxx new file mode 100644 index 0000000..4d585ce --- /dev/null +++ b/Source/cmInstallGetRuntimeDependenciesGenerator.cxx @@ -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"; +} diff --git a/Source/cmInstallGetRuntimeDependenciesGenerator.h b/Source/cmInstallGetRuntimeDependenciesGenerator.h new file mode 100644 index 0000000..19f6cc6 --- /dev/null +++ b/Source/cmInstallGetRuntimeDependenciesGenerator.h @@ -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; +}; diff --git a/Source/cmInstallRuntimeDependencySet.cxx b/Source/cmInstallRuntimeDependencySet.cxx new file mode 100644 index 0000000..0cef49a --- /dev/null +++ b/Source/cmInstallRuntimeDependencySet.cxx @@ -0,0 +1,95 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "cmInstallRuntimeDependencySet.h" + +#include <set> +#include <string> +#include <utility> + +#include "cmGeneratorTarget.h" +#include "cmGlobalGenerator.h" +#include "cmInstallImportedRuntimeArtifactsGenerator.h" +#include "cmInstallTargetGenerator.h" +#include "cmStateTypes.h" +#include "cmTargetDepend.h" + +cmInstallRuntimeDependencySet::cmInstallRuntimeDependencySet(std::string name) + : Name(std::move(name)) +{ +} + +void cmInstallRuntimeDependencySet::AddExecutable( + std::unique_ptr<Item> executable) +{ + this->Executables.push_back(std::move(executable)); +} + +void cmInstallRuntimeDependencySet::AddLibrary(std::unique_ptr<Item> library) +{ + this->Libraries.push_back(std::move(library)); +} + +void cmInstallRuntimeDependencySet::AddModule(std::unique_ptr<Item> module) +{ + this->Modules.push_back(std::move(module)); +} + +bool cmInstallRuntimeDependencySet::AddBundleExecutable( + std::unique_ptr<Item> bundleExecutable) +{ + if (this->BundleExecutable) { + return false; + } + this->BundleExecutable = bundleExecutable.get(); + this->AddExecutable(std::move(bundleExecutable)); + return true; +} + +std::string cmInstallRuntimeDependencySet::TargetItem::GetItemPath( + const std::string& config) const +{ + return this->Target->GetTarget()->GetFullPath(config); +} + +namespace { +const std::set<const cmGeneratorTarget*>& GetTargetDependsClosure( + std::map<const cmGeneratorTarget*, std::set<const cmGeneratorTarget*>>& + targetDepends, + const cmGeneratorTarget* tgt) +{ + auto it = targetDepends.insert({ tgt, {} }); + auto& retval = it.first->second; + if (it.second) { + auto const& deps = tgt->GetGlobalGenerator()->GetTargetDirectDepends(tgt); + for (auto const& dep : deps) { + if (!dep.IsCross() && dep.IsLink()) { + auto type = dep->GetType(); + if (type == cmStateEnums::EXECUTABLE || + type == cmStateEnums::SHARED_LIBRARY || + type == cmStateEnums::MODULE_LIBRARY) { + retval.insert(dep); + } + auto const& depDeps = GetTargetDependsClosure(targetDepends, dep); + retval.insert(depDeps.begin(), depDeps.end()); + } + } + } + return retval; +} +} + +void cmInstallRuntimeDependencySet::TargetItem::AddPostExcludeFiles( + const std::string& config, std::set<std::string>& files, + cmInstallRuntimeDependencySet* set) const +{ + for (auto const* dep : GetTargetDependsClosure(set->TargetDepends, + this->Target->GetTarget())) { + files.insert(dep->GetFullPath(config)); + } +} + +std::string cmInstallRuntimeDependencySet::ImportedTargetItem::GetItemPath( + const std::string& config) const +{ + return this->Target->GetTarget()->GetFullPath(config); +} diff --git a/Source/cmInstallRuntimeDependencySet.h b/Source/cmInstallRuntimeDependencySet.h new file mode 100644 index 0000000..7f51624 --- /dev/null +++ b/Source/cmInstallRuntimeDependencySet.h @@ -0,0 +1,163 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#pragma once + +#include <map> +#include <set> +#include <string> +#include <vector> + +#include <cm/memory> +#include <cm/string_view> +#include <cmext/string_view> + +class cmGeneratorTarget; +class cmInstallImportedRuntimeArtifactsGenerator; +class cmInstallTargetGenerator; + +class cmInstallRuntimeDependencySet +{ +public: + cmInstallRuntimeDependencySet(std::string name = ""); + + cmInstallRuntimeDependencySet(const cmInstallRuntimeDependencySet&) = delete; + cmInstallRuntimeDependencySet& operator=( + const cmInstallRuntimeDependencySet&) = delete; + + cm::string_view GetName() const { return this->Name; } + + cm::string_view GetDisplayName() const + { + if (this->Name.empty()) { + return "<anonymous>"_s; + } + return this->Name; + } + + class Item + { + public: + virtual ~Item() = default; + + virtual std::string GetItemPath(const std::string& config) const = 0; + + virtual void AddPostExcludeFiles( + const std::string& /*config*/, std::set<std::string>& /*files*/, + cmInstallRuntimeDependencySet* /*set*/) const + { + } + }; + + class TargetItem : public Item + { + public: + TargetItem(cmInstallTargetGenerator* target) + : Target(target) + { + } + + std::string GetItemPath(const std::string& config) const override; + + void AddPostExcludeFiles( + const std::string& config, std::set<std::string>& files, + cmInstallRuntimeDependencySet* set) const override; + + private: + cmInstallTargetGenerator* Target; + }; + + class ImportedTargetItem : public Item + { + public: + ImportedTargetItem(cmInstallImportedRuntimeArtifactsGenerator* target) + : Target(target) + { + } + + std::string GetItemPath(const std::string& config) const override; + + private: + cmInstallImportedRuntimeArtifactsGenerator* Target; + }; + + void AddExecutable(std::unique_ptr<Item> executable); + void AddLibrary(std::unique_ptr<Item> library); + void AddModule(std::unique_ptr<Item> module); + bool AddBundleExecutable(std::unique_ptr<Item> bundleExecutable); + + void AddExecutable(cmInstallTargetGenerator* executable) + { + this->AddExecutable(cm::make_unique<TargetItem>(executable)); + } + + void AddLibrary(cmInstallTargetGenerator* library) + { + this->AddLibrary(cm::make_unique<TargetItem>(library)); + } + + void AddModule(cmInstallTargetGenerator* module) + { + this->AddModule(cm::make_unique<TargetItem>(module)); + } + + bool AddBundleExecutable(cmInstallTargetGenerator* bundleExecutable) + { + return this->AddBundleExecutable( + cm::make_unique<TargetItem>(bundleExecutable)); + } + + void AddExecutable(cmInstallImportedRuntimeArtifactsGenerator* executable) + { + this->AddExecutable(cm::make_unique<ImportedTargetItem>(executable)); + } + + void AddLibrary(cmInstallImportedRuntimeArtifactsGenerator* library) + { + this->AddLibrary(cm::make_unique<ImportedTargetItem>(library)); + } + + void AddModule(cmInstallImportedRuntimeArtifactsGenerator* module) + { + this->AddModule(cm::make_unique<ImportedTargetItem>(module)); + } + + bool AddBundleExecutable( + cmInstallImportedRuntimeArtifactsGenerator* bundleExecutable) + { + return this->AddBundleExecutable( + cm::make_unique<ImportedTargetItem>(bundleExecutable)); + } + + const std::vector<std::unique_ptr<Item>>& GetExecutables() const + { + return this->Executables; + } + + const std::vector<std::unique_ptr<Item>>& GetLibraries() const + { + return this->Libraries; + } + + const std::vector<std::unique_ptr<Item>>& GetModules() const + { + return this->Modules; + } + + Item* GetBundleExecutable() const { return this->BundleExecutable; } + + bool Empty() const + { + return this->Executables.empty() && this->Libraries.empty() && + this->Modules.empty(); + } + +private: + std::string Name; + std::vector<std::unique_ptr<Item>> Executables; + std::vector<std::unique_ptr<Item>> Libraries; + std::vector<std::unique_ptr<Item>> Modules; + Item* BundleExecutable = nullptr; + + std::map<const cmGeneratorTarget*, std::set<const cmGeneratorTarget*>> + TargetDepends; +}; diff --git a/Source/cmInstallRuntimeDependencySetGenerator.cxx b/Source/cmInstallRuntimeDependencySetGenerator.cxx new file mode 100644 index 0000000..44f03e1 --- /dev/null +++ b/Source/cmInstallRuntimeDependencySetGenerator.cxx @@ -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); +} diff --git a/Source/cmInstallRuntimeDependencySetGenerator.h b/Source/cmInstallRuntimeDependencySetGenerator.h new file mode 100644 index 0000000..8e98b57 --- /dev/null +++ b/Source/cmInstallRuntimeDependencySetGenerator.h @@ -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); +}; diff --git a/Source/cmRuntimeDependencyArchive.cxx b/Source/cmRuntimeDependencyArchive.cxx index a6ed523..26f255d 100644 --- a/Source/cmRuntimeDependencyArchive.cxx +++ b/Source/cmRuntimeDependencyArchive.cxx @@ -198,25 +198,26 @@ void cmRuntimeDependencyArchive::SetError(const std::string& e) this->Status.SetError(e); } -std::string cmRuntimeDependencyArchive::GetBundleExecutable() +const std::string& cmRuntimeDependencyArchive::GetBundleExecutable() const { return this->BundleExecutable; } const std::vector<std::string>& -cmRuntimeDependencyArchive::GetSearchDirectories() +cmRuntimeDependencyArchive::GetSearchDirectories() const { return this->SearchDirectories; } -std::string cmRuntimeDependencyArchive::GetGetRuntimeDependenciesTool() +const std::string& cmRuntimeDependencyArchive::GetGetRuntimeDependenciesTool() + const { return this->GetMakefile()->GetSafeDefinition( "CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL"); } bool cmRuntimeDependencyArchive::GetGetRuntimeDependenciesCommand( - const std::string& search, std::vector<std::string>& command) + const std::string& search, std::vector<std::string>& command) const { // First see if it was supplied by the user std::string toolCommand = this->GetMakefile()->GetSafeDefinition( @@ -309,7 +310,7 @@ bool cmRuntimeDependencyArchive::GetGetRuntimeDependenciesCommand( return false; } -bool cmRuntimeDependencyArchive::IsPreExcluded(const std::string& name) +bool cmRuntimeDependencyArchive::IsPreExcluded(const std::string& name) const { cmsys::RegularExpressionMatch match; auto const regexMatch = @@ -326,7 +327,7 @@ bool cmRuntimeDependencyArchive::IsPreExcluded(const std::string& name) regexSearch(this->PreExcludeRegexes); } -bool cmRuntimeDependencyArchive::IsPostExcluded(const std::string& name) +bool cmRuntimeDependencyArchive::IsPostExcluded(const std::string& name) const { cmsys::RegularExpressionMatch match; auto const regexMatch = @@ -353,9 +354,9 @@ bool cmRuntimeDependencyArchive::IsPostExcluded(const std::string& name) fileSearch(this->PostExcludeFiles))); } -void cmRuntimeDependencyArchive::AddResolvedPath(const std::string& name, - const std::string& path, - bool& unique) +void cmRuntimeDependencyArchive::AddResolvedPath( + const std::string& name, const std::string& path, bool& unique, + std::vector<std::string> rpaths) { auto it = this->ResolvedPaths.emplace(name, std::set<std::string>{}).first; unique = true; @@ -366,6 +367,7 @@ void cmRuntimeDependencyArchive::AddResolvedPath(const std::string& name, } } it->second.insert(path); + this->RPaths[path] = std::move(rpaths); } void cmRuntimeDependencyArchive::AddUnresolvedPath(const std::string& name) @@ -373,18 +375,33 @@ void cmRuntimeDependencyArchive::AddUnresolvedPath(const std::string& name) this->UnresolvedPaths.insert(name); } -cmMakefile* cmRuntimeDependencyArchive::GetMakefile() +cmMakefile* cmRuntimeDependencyArchive::GetMakefile() const { return &this->Status.GetMakefile(); } const std::map<std::string, std::set<std::string>>& -cmRuntimeDependencyArchive::GetResolvedPaths() +cmRuntimeDependencyArchive::GetResolvedPaths() const { return this->ResolvedPaths; } const std::set<std::string>& cmRuntimeDependencyArchive::GetUnresolvedPaths() + const { return this->UnresolvedPaths; } + +const std::map<std::string, std::vector<std::string>>& +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); +} diff --git a/Source/cmRuntimeDependencyArchive.h b/Source/cmRuntimeDependencyArchive.h index 1dc3261..b170815 100644 --- a/Source/cmRuntimeDependencyArchive.h +++ b/Source/cmRuntimeDependencyArchive.h @@ -36,21 +36,24 @@ public: void SetError(const std::string& e); - std::string GetBundleExecutable(); - const std::vector<std::string>& GetSearchDirectories(); - std::string GetGetRuntimeDependenciesTool(); - bool GetGetRuntimeDependenciesCommand(const std::string& search, - std::vector<std::string>& command); - bool IsPreExcluded(const std::string& name); - bool IsPostExcluded(const std::string& name); + const std::string& GetBundleExecutable() const; + const std::vector<std::string>& GetSearchDirectories() const; + const std::string& GetGetRuntimeDependenciesTool() const; + bool GetGetRuntimeDependenciesCommand( + const std::string& search, std::vector<std::string>& command) const; + bool IsPreExcluded(const std::string& name) const; + bool IsPostExcluded(const std::string& name) const; void AddResolvedPath(const std::string& name, const std::string& path, - bool& unique); + bool& unique, std::vector<std::string> rpaths = {}); void AddUnresolvedPath(const std::string& name); - cmMakefile* GetMakefile(); - const std::map<std::string, std::set<std::string>>& GetResolvedPaths(); - const std::set<std::string>& GetUnresolvedPaths(); + cmMakefile* GetMakefile() const; + const std::map<std::string, std::set<std::string>>& GetResolvedPaths() const; + 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; @@ -70,4 +73,5 @@ private: std::vector<std::string> PostExcludeFilesStrict; std::map<std::string, std::set<std::string>> ResolvedPaths; std::set<std::string> UnresolvedPaths; + std::map<std::string, std::vector<std::string>> RPaths; }; diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index 9b81bf2..10d2e50 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -14,6 +14,7 @@ #include "cmSystemTools.h" +#include <cm/optional> #include <cmext/algorithm> #include <cm3p/uv.h> @@ -65,6 +66,7 @@ #include <cstdlib> #include <cstring> #include <ctime> +#include <functional> #include <iostream> #include <sstream> #include <utility> @@ -2524,6 +2526,7 @@ std::string::size_type cmSystemToolsFindRPath(cm::string_view const& have, #endif #if defined(CMake_USE_ELF_PARSER) +namespace { struct cmSystemToolsRPathInfo { unsigned long Position; @@ -2531,15 +2534,15 @@ struct cmSystemToolsRPathInfo std::string Name; std::string Value; }; -#endif + +using EmptyCallback = std::function<bool(std::string*, const cmELF&)>; +using AdjustCallback = std::function<bool( + cm::optional<std::string>&, const std::string&, const char*, std::string*)>; // FIXME: Dispatch if multiple formats are supported. -#if defined(CMake_USE_ELF_PARSER) -bool cmSystemTools::ChangeRPath(std::string const& file, - std::string const& oldRPath, - std::string const& newRPath, - bool removeEnvironmentRPath, std::string* emsg, - bool* changed) +bool AdjustRPath(std::string const& file, const EmptyCallback& emptyCallback, + const AdjustCallback& adjustCallback, std::string* emsg, + bool* changed) { if (changed) { *changed = false; @@ -2566,17 +2569,7 @@ bool cmSystemTools::ChangeRPath(std::string const& file, ++se_count; } if (se_count == 0) { - if (newRPath.empty()) { - // The new rpath is empty and there is no rpath anyway so it is - // okay. - return true; - } - if (emsg) { - *emsg = - cmStrCat("No valid ELF RPATH or RUNPATH entry exists in the file; ", - elf.GetErrorMessage()); - } - return false; + return emptyCallback(emsg, elf); } for (int i = 0; i < se_count; ++i) { @@ -2586,68 +2579,38 @@ bool cmSystemTools::ChangeRPath(std::string const& file, continue; } - // Make sure the current rpath contains the old rpath. - std::string::size_type pos = - cmSystemToolsFindRPath(se[i]->Value, oldRPath); - if (pos == std::string::npos) { - // If it contains the new rpath instead then it is okay. - if (cmSystemToolsFindRPath(se[i]->Value, newRPath) != - std::string::npos) { - remove_rpath = false; - continue; - } - if (emsg) { - std::ostringstream e; - /* clang-format off */ - e << "The current " << se_name[i] << " is:\n" - << " " << se[i]->Value << "\n" - << "which does not contain:\n" - << " " << oldRPath << "\n" - << "as was expected."; - /* clang-format on */ - *emsg = e.str(); - } - return false; - } - // Store information about the entry in the file. rp[rp_count].Position = se[i]->Position; rp[rp_count].Size = se[i]->Size; rp[rp_count].Name = se_name[i]; - std::string::size_type prefix_len = pos; - - // If oldRPath was at the end of the file's RPath, and newRPath is empty, - // we should remove the unnecessary ':' at the end. - if (newRPath.empty() && pos > 0 && se[i]->Value[pos - 1] == ':' && - pos + oldRPath.length() == se[i]->Value.length()) { - prefix_len--; - } - - // Construct the new value which preserves the part of the path - // not being changed. - if (!removeEnvironmentRPath) { - rp[rp_count].Value = se[i]->Value.substr(0, prefix_len); + // Adjust the rpath. + cm::optional<std::string> outRPath; + if (!adjustCallback(outRPath, se[i]->Value, se_name[i], emsg)) { + return false; } - rp[rp_count].Value += newRPath; - rp[rp_count].Value += se[i]->Value.substr(pos + oldRPath.length()); - if (!rp[rp_count].Value.empty()) { - remove_rpath = false; - } + if (outRPath) { + if (!outRPath->empty()) { + remove_rpath = false; + } - // Make sure there is enough room to store the new rpath and at - // least one null terminator. - if (rp[rp_count].Size < rp[rp_count].Value.length() + 1) { - if (emsg) { - *emsg = cmStrCat("The replacement path is too long for the ", - se_name[i], " entry."); + // Make sure there is enough room to store the new rpath and at + // least one null terminator. + if (rp[rp_count].Size < outRPath->length() + 1) { + if (emsg) { + *emsg = cmStrCat("The replacement path is too long for the ", + se_name[i], " entry."); + } + return false; } - return false; - } - // This entry is ready for update. - ++rp_count; + // This entry is ready for update. + rp[rp_count].Value = std::move(*outRPath); + ++rp_count; + } else { + remove_rpath = false; + } } } @@ -2706,6 +2669,99 @@ bool cmSystemTools::ChangeRPath(std::string const& file, } return true; } + +std::function<bool(std::string*, const cmELF&)> MakeEmptyCallback( + const std::string& newRPath) +{ + return [newRPath](std::string* emsg, const cmELF& elf) -> bool { + if (newRPath.empty()) { + // The new rpath is empty and there is no rpath anyway so it is + // okay. + return true; + } + if (emsg) { + *emsg = + cmStrCat("No valid ELF RPATH or RUNPATH entry exists in the file; ", + elf.GetErrorMessage()); + } + return false; + }; +}; +} + +bool cmSystemTools::ChangeRPath(std::string const& file, + std::string const& oldRPath, + std::string const& newRPath, + bool removeEnvironmentRPath, std::string* emsg, + bool* changed) +{ + auto adjustCallback = [oldRPath, newRPath, removeEnvironmentRPath]( + cm::optional<std::string>& outRPath, + const std::string& inRPath, const char* se_name, + std::string* emsg2) -> bool { + // Make sure the current rpath contains the old rpath. + std::string::size_type pos = cmSystemToolsFindRPath(inRPath, oldRPath); + if (pos == std::string::npos) { + // If it contains the new rpath instead then it is okay. + if (cmSystemToolsFindRPath(inRPath, newRPath) != std::string::npos) { + return true; + } + if (emsg2) { + std::ostringstream e; + /* clang-format off */ + e << "The current " << se_name << " is:\n" + << " " << inRPath << "\n" + << "which does not contain:\n" + << " " << oldRPath << "\n" + << "as was expected."; + /* clang-format on */ + *emsg2 = e.str(); + } + return false; + } + + std::string::size_type prefix_len = pos; + + // If oldRPath was at the end of the file's RPath, and newRPath is empty, + // we should remove the unnecessary ':' at the end. + if (newRPath.empty() && pos > 0 && inRPath[pos - 1] == ':' && + pos + oldRPath.length() == inRPath.length()) { + prefix_len--; + } + + // Construct the new value which preserves the part of the path + // not being changed. + outRPath.emplace(); + if (!removeEnvironmentRPath) { + *outRPath += inRPath.substr(0, prefix_len); + } + *outRPath += newRPath; + *outRPath += inRPath.substr(pos + oldRPath.length()); + + return true; + }; + + return AdjustRPath(file, MakeEmptyCallback(newRPath), adjustCallback, emsg, + changed); +} + +bool cmSystemTools::SetRPath(std::string const& file, + std::string const& newRPath, std::string* emsg, + bool* changed) +{ + auto adjustCallback = [newRPath](cm::optional<std::string>& outRPath, + const std::string& inRPath, + const char* /*se_name*/, std::string * + /*emsg*/) -> bool { + if (inRPath != newRPath) { + outRPath = newRPath; + } + return true; + }; + + return AdjustRPath(file, MakeEmptyCallback(newRPath), adjustCallback, emsg, + changed); +} #elif defined(CMake_USE_XCOFF_PARSER) bool cmSystemTools::ChangeRPath(std::string const& file, std::string const& oldRPath, @@ -2775,6 +2831,13 @@ bool cmSystemTools::ChangeRPath(std::string const& file, } return true; } + +bool cmSystemTools::SetRPath(std::string const& /*file*/, + std::string const& /*newRPath*/, + std::string* /*emsg*/, bool* /*changed*/) +{ + return false; +} #else bool cmSystemTools::ChangeRPath(std::string const& /*file*/, std::string const& /*oldRPath*/, @@ -2784,6 +2847,13 @@ bool cmSystemTools::ChangeRPath(std::string const& /*file*/, { return false; } + +bool cmSystemTools::SetRPath(std::string const& /*file*/, + std::string const& /*newRPath*/, + std::string* /*emsg*/, bool* /*changed*/) +{ + return false; +} #endif bool cmSystemTools::VersionCompare(cmSystemTools::CompareOp op, diff --git a/Source/cmSystemTools.h b/Source/cmSystemTools.h index 5c3b5a9..44ccbf7 100644 --- a/Source/cmSystemTools.h +++ b/Source/cmSystemTools.h @@ -456,13 +456,17 @@ public: static bool GuessLibraryInstallName(std::string const& fullPath, std::string& soname); - /** Try to set the RPATH in an ELF binary. */ + /** Try to change the RPATH in an ELF binary. */ static bool ChangeRPath(std::string const& file, std::string const& oldRPath, std::string const& newRPath, bool removeEnvironmentRPath, std::string* emsg = nullptr, bool* changed = nullptr); + /** Try to set the RPATH in an ELF binary. */ + static bool SetRPath(std::string const& file, std::string const& newRPath, + std::string* emsg = nullptr, bool* changed = nullptr); + /** Try to remove the RPATH from an ELF binary. */ static bool RemoveRPath(std::string const& file, std::string* emsg = nullptr, bool* removed = nullptr); |