From b613e09778260c26c6323026dc2ccb980c102f3e Mon Sep 17 00:00:00 2001 From: Kyle Edwards Date: Tue, 20 Apr 2021 16:16:57 -0400 Subject: cmRuntimeDependencyArchive: Fix const-ness of method signatures --- Source/cmRuntimeDependencyArchive.cxx | 18 ++++++++++-------- Source/cmRuntimeDependencyArchive.h | 20 ++++++++++---------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/Source/cmRuntimeDependencyArchive.cxx b/Source/cmRuntimeDependencyArchive.cxx index a6ed523..57b4bd6 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& -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& command) + const std::string& search, std::vector& 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 = @@ -373,18 +374,19 @@ 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>& -cmRuntimeDependencyArchive::GetResolvedPaths() +cmRuntimeDependencyArchive::GetResolvedPaths() const { return this->ResolvedPaths; } const std::set& cmRuntimeDependencyArchive::GetUnresolvedPaths() + const { return this->UnresolvedPaths; } diff --git a/Source/cmRuntimeDependencyArchive.h b/Source/cmRuntimeDependencyArchive.h index 1dc3261..3f368c5 100644 --- a/Source/cmRuntimeDependencyArchive.h +++ b/Source/cmRuntimeDependencyArchive.h @@ -36,21 +36,21 @@ public: void SetError(const std::string& e); - std::string GetBundleExecutable(); - const std::vector& GetSearchDirectories(); - std::string GetGetRuntimeDependenciesTool(); - bool GetGetRuntimeDependenciesCommand(const std::string& search, - std::vector& command); - bool IsPreExcluded(const std::string& name); - bool IsPostExcluded(const std::string& name); + const std::string& GetBundleExecutable() const; + const std::vector& GetSearchDirectories() const; + const std::string& GetGetRuntimeDependenciesTool() const; + bool GetGetRuntimeDependenciesCommand( + const std::string& search, std::vector& 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); void AddUnresolvedPath(const std::string& name); - cmMakefile* GetMakefile(); - const std::map>& GetResolvedPaths(); - const std::set& GetUnresolvedPaths(); + cmMakefile* GetMakefile() const; + const std::map>& GetResolvedPaths() const; + const std::set& GetUnresolvedPaths() const; private: cmExecutionStatus& Status; -- cgit v0.12 From 2ef3ea394f424bf2397878b00725b93ebe04c589 Mon Sep 17 00:00:00 2001 From: Kyle Edwards Date: Fri, 23 Apr 2021 11:19:11 -0400 Subject: file(GET_RUNTIME_DEPENDENCIES): Add undocumented RPATH_PREFIX option --- Source/cmBinUtilsMacOSMachOLinker.cxx | 22 +++++++++----- Source/cmBinUtilsMacOSMachOLinker.h | 2 ++ Source/cmFileCommand.cxx | 7 +++++ Source/cmRuntimeDependencyArchive.cxx | 13 ++++++-- Source/cmRuntimeDependencyArchive.h | 4 ++- .../RunCMakeTest.cmake | 1 + .../macos-rpath.cmake | 35 ++++++++++++++++++++++ 7 files changed, 72 insertions(+), 12 deletions(-) create mode 100644 Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/macos-rpath.cmake 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 libs; std::vector 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 const& libs, + std::vector 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 libs; + std::vector 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 Tool; bool ScanDependencies(std::string const& file, + std::vector const& libs, + std::vector const& rpaths, std::string const& executablePath); bool GetFileDependencies(std::vector const& names, diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index f2d4cda..b685d14 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -3032,6 +3032,7 @@ bool HandleGetRuntimeDependenciesCommand(std::vector const& args, std::string ResolvedDependenciesVar; std::string UnresolvedDependenciesVar; std::string ConflictingDependenciesPrefix; + std::string RPathPrefix; std::string BundleExecutable; std::vector Executables; std::vector Libraries; @@ -3053,6 +3054,7 @@ bool HandleGetRuntimeDependenciesCommand(std::vector 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 +3137,11 @@ bool HandleGetRuntimeDependenciesCommand(std::vector 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 paths; diff --git a/Source/cmRuntimeDependencyArchive.cxx b/Source/cmRuntimeDependencyArchive.cxx index 57b4bd6..06246d1 100644 --- a/Source/cmRuntimeDependencyArchive.cxx +++ b/Source/cmRuntimeDependencyArchive.cxx @@ -354,9 +354,9 @@ bool cmRuntimeDependencyArchive::IsPostExcluded(const std::string& name) const 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 rpaths) { auto it = this->ResolvedPaths.emplace(name, std::set{}).first; unique = true; @@ -367,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) @@ -390,3 +391,9 @@ const std::set& cmRuntimeDependencyArchive::GetUnresolvedPaths() { return this->UnresolvedPaths; } + +const std::map>& +cmRuntimeDependencyArchive::GetRPaths() const +{ + return this->RPaths; +} diff --git a/Source/cmRuntimeDependencyArchive.h b/Source/cmRuntimeDependencyArchive.h index 3f368c5..20a38e2 100644 --- a/Source/cmRuntimeDependencyArchive.h +++ b/Source/cmRuntimeDependencyArchive.h @@ -45,12 +45,13 @@ public: bool IsPostExcluded(const std::string& name) const; void AddResolvedPath(const std::string& name, const std::string& path, - bool& unique); + bool& unique, std::vector rpaths = {}); void AddUnresolvedPath(const std::string& name); cmMakefile* GetMakefile() const; const std::map>& GetResolvedPaths() const; const std::set& GetUnresolvedPaths() const; + const std::map>& GetRPaths() const; private: cmExecutionStatus& Status; @@ -70,4 +71,5 @@ private: std::vector PostExcludeFilesStrict; std::map> ResolvedPaths; std::set UnresolvedPaths; + std::map> RPaths; }; diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/RunCMakeTest.cmake b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/RunCMakeTest.cmake index c71b9ba..07679b7 100644 --- a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/RunCMakeTest.cmake +++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/RunCMakeTest.cmake @@ -36,6 +36,7 @@ endfunction() if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin") if(NOT CMake_INSTALL_NAME_TOOL_BUG) run_install_test(macos) + run_install_test(macos-rpath) run_install_test(macos-unresolved) run_install_test(macos-conflict) run_install_test(macos-notfile) diff --git a/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/macos-rpath.cmake b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/macos-rpath.cmake new file mode 100644 index 0000000..798045f --- /dev/null +++ b/Tests/RunCMake/file-GET_RUNTIME_DEPENDENCIES/macos-rpath.cmake @@ -0,0 +1,35 @@ +enable_language(C) + +file(WRITE "${CMAKE_BINARY_DIR}/toplib.c" "extern void sublib1(void);\nextern void sublib2(void);\nvoid toplib(void)\n{\n sublib1();\n sublib2();\n}\n") +add_library(toplib SHARED "${CMAKE_BINARY_DIR}/toplib.c") +file(WRITE "${CMAKE_BINARY_DIR}/sublib1.c" "extern void sublib2(void);\nvoid sublib1(void)\n{\n sublib2();\n}\n") +add_library(sublib1 SHARED "${CMAKE_BINARY_DIR}/sublib1.c") +file(WRITE "${CMAKE_BINARY_DIR}/sublib2.c" "void sublib2(void)\n{\n}\n") +add_library(sublib2 SHARED "${CMAKE_BINARY_DIR}/sublib2.c") +target_link_libraries(toplib PRIVATE sublib1 sublib2) +target_link_libraries(sublib1 PRIVATE sublib2) +set_property(TARGET toplib PROPERTY INSTALL_RPATH "@loader_path/d1;@loader_path/d2") +set_property(TARGET sublib1 PROPERTY INSTALL_RPATH "@loader_path/;@loader_path/../d2") +install(TARGETS toplib DESTINATION lib) +install(TARGETS sublib1 DESTINATION lib/d1) +install(TARGETS sublib2 DESTINATION lib/d2) + +install(CODE [[ + file(GET_RUNTIME_DEPENDENCIES + LIBRARIES + "${CMAKE_INSTALL_PREFIX}/lib/$" + RPATH_PREFIX _rpaths + ) + + set(_expected_rpath "(^|;)@loader_path/;@loader_path/\\.\\./d2$") + set(_actual_rpath "${_rpaths_${CMAKE_INSTALL_PREFIX}/lib/d1/$}") + if(NOT _actual_rpath MATCHES "${_expected_rpath}") + message(FATAL_ERROR "Expected rpath:\n ${_expected_rpath}\nActual rpath:\n ${_actual_rpath}") + endif() + + # Since RPATH_PREFIX is an undocumented option for install(), we don't really need the rpath + # for the top files anyway. + if(DEFINED "_rpaths_${CMAKE_INSTALL_PREFIX}/lib/$") + message(FATAL_ERROR "rpath for top library should not be defined") + endif() + ]]) -- cgit v0.12 From 966f7250df01df7982aa6ff4117464d797b6bf10 Mon Sep 17 00:00:00 2001 From: Kyle Edwards Date: Tue, 4 May 2021 16:21:21 -0400 Subject: Refactor: Break up logic in cmSystemTools::ChangeRPath() --- Source/cmSystemTools.cxx | 178 ++++++++++++++++++++++++++++------------------- 1 file changed, 108 insertions(+), 70 deletions(-) diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index 9b81bf2..b45228e 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -14,6 +14,7 @@ #include "cmSystemTools.h" +#include #include #include @@ -65,6 +66,7 @@ #include #include #include +#include #include #include #include @@ -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; +using AdjustCallback = std::function&, 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 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,81 @@ bool cmSystemTools::ChangeRPath(std::string const& file, } return true; } + +std::function 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& 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); +} #elif defined(CMake_USE_XCOFF_PARSER) bool cmSystemTools::ChangeRPath(std::string const& file, std::string const& oldRPath, -- cgit v0.12 From f73027b182710dd00524b2a4007838f9080c3ea1 Mon Sep 17 00:00:00 2001 From: Kyle Edwards Date: Tue, 4 May 2021 16:31:05 -0400 Subject: cmSystemTools: Add SetRPath() method --- Source/cmSystemTools.cxx | 32 ++++++++++++++++++++++++++++++++ Source/cmSystemTools.h | 6 +++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/Source/cmSystemTools.cxx b/Source/cmSystemTools.cxx index b45228e..10d2e50 100644 --- a/Source/cmSystemTools.cxx +++ b/Source/cmSystemTools.cxx @@ -2744,6 +2744,24 @@ bool cmSystemTools::ChangeRPath(std::string const& file, 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& 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, @@ -2813,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*/, @@ -2822,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); -- cgit v0.12 From fac22d65962111e61a0fd7650d0efec963cc10ac Mon Sep 17 00:00:00 2001 From: Kyle Edwards Date: Wed, 5 May 2021 11:32:15 -0400 Subject: Refactor: Modify file(RPATH_*) to use cmArgumentParser --- Source/cmFileCommand.cxx | 138 +++++++++++++++++++++-------------------------- 1 file changed, 61 insertions(+), 77 deletions(-) diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index b685d14..60a8086 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -990,50 +990,42 @@ bool HandleRPathChangeCommand(std::vector 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 parser; + std::vector unknownArgs; + std::vector missingArgs; + std::vector 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; } @@ -1070,23 +1062,20 @@ bool HandleRPathRemoveCommand(std::vector const& args, { // 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 parser; + std::vector unknownArgs; + std::vector 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 +1112,31 @@ bool HandleRPathCheckCommand(std::vector 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 parser; + std::vector unknownArgs; + std::vector missingArgs; + std::vector 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; } -- cgit v0.12 From 9f6cfe716918d7c28ddc906c49b75b922e36a8cf Mon Sep 17 00:00:00 2001 From: Kyle Edwards Date: Wed, 5 May 2021 11:25:38 -0400 Subject: file: Add undocumented RPATH_SET command --- Source/cmFileCommand.cxx | 61 ++++++++++++++++++++++++++++++++++ Tests/RunCMake/file-RPATH/Common.cmake | 37 +++++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index 60a8086..82546b9 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -1057,6 +1057,66 @@ bool HandleRPathChangeCommand(std::vector const& args, return success; } +bool HandleRPathSetCommand(std::vector const& args, + cmExecutionStatus& status) +{ + // Evaluate arguments. + std::string file; + std::string newRPath; + cmArgumentParser parser; + std::vector unknownArgs; + std::vector missingArgs; + std::vector 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 const& args, cmExecutionStatus& status) { @@ -3732,6 +3792,7 @@ bool cmFileCommand(std::vector 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/Tests/RunCMake/file-RPATH/Common.cmake b/Tests/RunCMake/file-RPATH/Common.cmake index cc1efb5..7034aad 100644 --- a/Tests/RunCMake/file-RPATH/Common.cmake +++ b/Tests/RunCMake/file-RPATH/Common.cmake @@ -62,3 +62,40 @@ foreach(f ${files}) message(FATAL_ERROR "RPATH_CHECK did not remove ${f}") endif() endforeach() + +# TODO Implement RPATH_SET in XCOFF. +if(format STREQUAL "ELF") + foreach(f ${names}) + file(COPY ${in}/${f} DESTINATION ${out} NO_SOURCE_PERMISSIONS) + endforeach() + + foreach(f ${files}) + # Set the RPATH. + file(RPATH_SET FILE "${f}" + NEW_RPATH "/new/rpath") + set(rpath) + file(STRINGS "${f}" rpath REGEX "/new/rpath" LIMIT_COUNT 1) + if(NOT rpath) + message(FATAL_ERROR "RPATH not set in ${f}") + endif() + file(STRINGS "${f}" rpath REGEX "/rpath/sample" LIMIT_COUNT 1) + if(rpath) + message(FATAL_EROR "RPATH not removed in ${f}") + endif() + + # Remove the RPATH. + file(RPATH_SET FILE "${f}" + NEW_RPATH "") + set(rpath) + file(STRINGS "${f}" rpath REGEX "/new/rpath" LIMIT_COUNT 1) + if(rpath) + message(FATAL_ERROR "RPATH not removed from ${f}") + endif() + + # Check again...this should remove the file. + file(RPATH_CHECK FILE "${f}" RPATH "/new/rpath") + if(EXISTS "${f}") + message(FATAL_ERROR "RPATH_CHECK did not remove ${f}") + endif() + endforeach() +endif() -- cgit v0.12 From f2617cf8e6aca6ec0f8c7df6999c1f713c6d7474 Mon Sep 17 00:00:00 2001 From: Kyle Edwards Date: Wed, 19 May 2021 10:29:17 -0400 Subject: Source: Add cmInstallRuntimeDependencySet --- Source/CMakeLists.txt | 2 + Source/cmGlobalGenerator.cxx | 24 +++++ Source/cmGlobalGenerator.h | 11 +++ Source/cmInstallRuntimeDependencySet.cxx | 95 ++++++++++++++++++ Source/cmInstallRuntimeDependencySet.h | 163 +++++++++++++++++++++++++++++++ bootstrap | 1 + 6 files changed, 296 insertions(+) create mode 100644 Source/cmInstallRuntimeDependencySet.cxx create mode 100644 Source/cmInstallRuntimeDependencySet.h diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 844a2ee..5bcf223 100644 --- a/Source/CMakeLists.txt +++ b/Source/CMakeLists.txt @@ -352,6 +352,8 @@ set(SRCS cmInstallFilesGenerator.cxx cmInstallImportedRuntimeArtifactsGenerator.h cmInstallImportedRuntimeArtifactsGenerator.cxx + cmInstallRuntimeDependencySet.h + cmInstallRuntimeDependencySet.cxx cmInstallScriptGenerator.h cmInstallScriptGenerator.cxx cmInstallSubdirectoryGenerator.h 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(); + 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(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 GeneratedFiles; + std::vector> + RuntimeDependencySets; + std::map + RuntimeDependencySetsByName; + #if !defined(CMAKE_BOOTSTRAP) // Pool of file locks cmFileLockPool FileLockPool; 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 +#include +#include + +#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 executable) +{ + this->Executables.push_back(std::move(executable)); +} + +void cmInstallRuntimeDependencySet::AddLibrary(std::unique_ptr library) +{ + this->Libraries.push_back(std::move(library)); +} + +void cmInstallRuntimeDependencySet::AddModule(std::unique_ptr module) +{ + this->Modules.push_back(std::move(module)); +} + +bool cmInstallRuntimeDependencySet::AddBundleExecutable( + std::unique_ptr 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& GetTargetDependsClosure( + std::map>& + 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& 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 +#include +#include +#include + +#include +#include +#include + +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 ""_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& /*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& 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 executable); + void AddLibrary(std::unique_ptr library); + void AddModule(std::unique_ptr module); + bool AddBundleExecutable(std::unique_ptr bundleExecutable); + + void AddExecutable(cmInstallTargetGenerator* executable) + { + this->AddExecutable(cm::make_unique(executable)); + } + + void AddLibrary(cmInstallTargetGenerator* library) + { + this->AddLibrary(cm::make_unique(library)); + } + + void AddModule(cmInstallTargetGenerator* module) + { + this->AddModule(cm::make_unique(module)); + } + + bool AddBundleExecutable(cmInstallTargetGenerator* bundleExecutable) + { + return this->AddBundleExecutable( + cm::make_unique(bundleExecutable)); + } + + void AddExecutable(cmInstallImportedRuntimeArtifactsGenerator* executable) + { + this->AddExecutable(cm::make_unique(executable)); + } + + void AddLibrary(cmInstallImportedRuntimeArtifactsGenerator* library) + { + this->AddLibrary(cm::make_unique(library)); + } + + void AddModule(cmInstallImportedRuntimeArtifactsGenerator* module) + { + this->AddModule(cm::make_unique(module)); + } + + bool AddBundleExecutable( + cmInstallImportedRuntimeArtifactsGenerator* bundleExecutable) + { + return this->AddBundleExecutable( + cm::make_unique(bundleExecutable)); + } + + const std::vector>& GetExecutables() const + { + return this->Executables; + } + + const std::vector>& GetLibraries() const + { + return this->Libraries; + } + + const std::vector>& 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> Executables; + std::vector> Libraries; + std::vector> Modules; + Item* BundleExecutable = nullptr; + + std::map> + TargetDepends; +}; diff --git a/bootstrap b/bootstrap index 911558d..720fa76 100755 --- a/bootstrap +++ b/bootstrap @@ -388,6 +388,7 @@ CMAKE_CXX_SOURCES="\ cmInstallFilesGenerator \ cmInstallGenerator \ cmInstallImportedRuntimeArtifactsGenerator \ + cmInstallRuntimeDependencySet \ cmInstallScriptGenerator \ cmInstallSubdirectoryGenerator \ cmInstallTargetGenerator \ -- cgit v0.12 From ed3633d88cc5faa6fd7eb68fdd774d6d1f9cfdc9 Mon Sep 17 00:00:00 2001 From: Kyle Edwards Date: Wed, 14 Apr 2021 10:43:30 -0400 Subject: install(TARGETS): Add RUNTIME_DEPENDENCIES option --- Source/CMakeLists.txt | 4 + Source/cmFileCommand.cxx | 5 +- Source/cmInstallCommand.cxx | 186 +++++++++++++- Source/cmInstallGenerator.cxx | 51 ++-- Source/cmInstallGenerator.h | 3 +- .../cmInstallGetRuntimeDependenciesGenerator.cxx | 206 +++++++++++++++ Source/cmInstallGetRuntimeDependenciesGenerator.h | 56 +++++ Source/cmInstallRuntimeDependencySetGenerator.cxx | 276 +++++++++++++++++++++ Source/cmInstallRuntimeDependencySetGenerator.h | 74 ++++++ Source/cmRuntimeDependencyArchive.cxx | 8 + Source/cmRuntimeDependencyArchive.h | 2 + Tests/RunCMake/install/RunCMakeTest.cmake | 17 +- .../TARGETS-RUNTIME_DEPENDENCIES-cross-result.txt | 1 + .../TARGETS-RUNTIME_DEPENDENCIES-cross-stderr.txt | 4 + .../TARGETS-RUNTIME_DEPENDENCIES-cross.cmake | 4 + ...GETS-RUNTIME_DEPENDENCIES-empty-all-check.cmake | 1 + .../TARGETS-RUNTIME_DEPENDENCIES-empty.cmake | 9 + ...TIME_DEPENDENCIES-macos-no-framework-result.txt | 1 + ...TIME_DEPENDENCIES-macos-no-framework-stderr.txt | 4 + ...S-RUNTIME_DEPENDENCIES-macos-no-framework.cmake | 4 + ...UNTIME_DEPENDENCIES-macos-two-bundle-result.txt | 1 + ...UNTIME_DEPENDENCIES-macos-two-bundle-stderr.txt | 4 + ...ETS-RUNTIME_DEPENDENCIES-macos-two-bundle.cmake | 10 + ...GETS-RUNTIME_DEPENDENCIES-nodep-all-check.cmake | 1 + .../TARGETS-RUNTIME_DEPENDENCIES-nodep.cmake | 9 + ...ETS-RUNTIME_DEPENDENCIES-unsupported-result.txt | 1 + ...ETS-RUNTIME_DEPENDENCIES-unsupported-stderr.txt | 5 + .../TARGETS-RUNTIME_DEPENDENCIES-unsupported.cmake | 4 + bootstrap | 2 + 29 files changed, 928 insertions(+), 25 deletions(-) create mode 100644 Source/cmInstallGetRuntimeDependenciesGenerator.cxx create mode 100644 Source/cmInstallGetRuntimeDependenciesGenerator.h create mode 100644 Source/cmInstallRuntimeDependencySetGenerator.cxx create mode 100644 Source/cmInstallRuntimeDependencySetGenerator.h create mode 100644 Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-cross-result.txt create mode 100644 Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-cross-stderr.txt create mode 100644 Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-cross.cmake create mode 100644 Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-empty-all-check.cmake create mode 100644 Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-empty.cmake create mode 100644 Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-macos-no-framework-result.txt create mode 100644 Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-macos-no-framework-stderr.txt create mode 100644 Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-macos-no-framework.cmake create mode 100644 Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-macos-two-bundle-result.txt create mode 100644 Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-macos-two-bundle-stderr.txt create mode 100644 Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-macos-two-bundle.cmake create mode 100644 Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-nodep-all-check.cmake create mode 100644 Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-nodep.cmake create mode 100644 Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-unsupported-result.txt create mode 100644 Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-unsupported-stderr.txt create mode 100644 Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-unsupported.cmake diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt index 5bcf223..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 @@ -354,6 +356,8 @@ set(SRCS cmInstallImportedRuntimeArtifactsGenerator.cxx cmInstallRuntimeDependencySet.h cmInstallRuntimeDependencySet.cxx + cmInstallRuntimeDependencySetGenerator.h + cmInstallRuntimeDependencySetGenerator.cxx cmInstallScriptGenerator.h cmInstallScriptGenerator.cxx cmInstallSubdirectoryGenerator.h diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx index 82546b9..0ad59c7 100644 --- a/Source/cmFileCommand.cxx +++ b/Source/cmFileCommand.cxx @@ -3044,11 +3044,10 @@ bool HandleCreateLinkCommand(std::vector const& args, bool HandleGetRuntimeDependenciesCommand(std::vector const& args, cmExecutionStatus& status) { - static const std::set 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, "\"")); diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index c9bb467..c36778d 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 #include #include +#include #include #include #include @@ -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 Directories; + std::vector PreIncludeRegexes; + std::vector PreExcludeRegexes; + std::vector PostIncludeRegexes; + std::vector PostExcludeRegexes; + std::vector PostIncludeFiles; + std::vector PostExcludeFiles; +}; + +auto const RuntimeDependenciesArgHelper = + cmArgumentParser{} + .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 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 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( + 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::DependencyType::Library, + runtimeDependencySet, std::vector{}, 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::DependencyType::Framework, + runtimeDependencySet, std::vector{}, 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 const allowedTypes{ "BIN", "SBIN", "LIB", "INCLUDE", "SYSCONF", "SHAREDSTATE", "LOCALSTATE", "RUNSTATE", "DATA", "INFO", "LOCALE", "MAN", "DOC", }; +template +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 const& args, cmExecutionStatus& status) { @@ -289,13 +412,23 @@ bool HandleTargetsMode(std::vector const& args, // These generic args also contain the targets and the export stuff std::vector targetList; std::string exports; + std::vector runtimeDependenciesArgVector; std::vector unknownArgs; + std::vector 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 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 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 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 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 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 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 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) { 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 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 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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "cmGeneratorExpression.h" +#include "cmInstallRuntimeDependencySet.h" +#include "cmLocalGenerator.h" +#include "cmMakefile.h" +#include "cmOutputConverter.h" +#include "cmStringAlgorithms.h" + +namespace { +template +void WriteMultiArgument(std::ostream& os, const cm::string_view& keyword, + const std::vector& list, + cmScriptGeneratorIndent indent, F transform) +{ + bool first = true; + for (auto const& item : list) { + cm::optional 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>& + items, + const std::string& config, cmScriptGeneratorIndent indent) +{ + WriteMultiArgument( + os, keyword, items, indent, + [config](const std::unique_ptr& i) + -> std::string { return cmStrCat('"', i->GetItemPath(config), '"'); }); +} + +void WriteGenexEvaluatorArgument(std::ostream& os, + const cm::string_view& keyword, + const std::vector& genexes, + const std::string& config, + cmLocalGenerator* lg, + cmScriptGeneratorIndent indent) +{ + WriteMultiArgument( + os, keyword, genexes, indent, + [config, lg](const std::string& genex) -> cm::optional { + std::string result = cmGeneratorExpression::Evaluate(genex, lg, config); + if (result.empty()) { + return cm::nullopt; + } + return cmOutputConverter::EscapeForCMake(result); + }); +} +} + +cmInstallGetRuntimeDependenciesGenerator:: + cmInstallGetRuntimeDependenciesGenerator( + cmInstallRuntimeDependencySet* runtimeDependencySet, + std::vector directories, + std::vector preIncludeRegexes, + std::vector preExcludeRegexes, + std::vector postIncludeRegexes, + std::vector postExcludeRegexes, + std::vector postIncludeFiles, + std::vector postExcludeFiles, std::string libraryComponent, + std::string frameworkComponent, bool noInstallRPath, const char* depsVar, + const char* rpathPrefix, std::vector 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 postExcludeFiles; + auto const addPostExclude = + [config, &postExcludeFiles, this]( + const std::vector>& + 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 +#include +#include + +#include "cmInstallGenerator.h" +#include "cmListFileCache.h" +#include "cmScriptGenerator.h" + +class cmLocalGenerator; +class cmInstallRuntimeDependencySet; + +class cmInstallGetRuntimeDependenciesGenerator : public cmInstallGenerator +{ +public: + cmInstallGetRuntimeDependenciesGenerator( + cmInstallRuntimeDependencySet* runtimeDependencySet, + std::vector directories, + std::vector preIncludeRegexes, + std::vector preExcludeRegexes, + std::vector postIncludeRegexes, + std::vector postExcludeRegexes, + std::vector postIncludeFiles, + std::vector postExcludeFiles, std::string libraryComponent, + std::string frameworkComponent, bool noInstallRPath, const char* depsVar, + const char* rpathPrefix, std::vector 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 Directories; + std::vector PreIncludeRegexes; + std::vector PreExcludeRegexes; + std::vector PostIncludeRegexes; + std::vector PostExcludeRegexes; + std::vector PostIncludeFiles; + std::vector PostExcludeFiles; + std::string LibraryComponent; + std::string FrameworkComponent; + bool NoInstallRPath; + const char* DepsVar; + const char* RPathPrefix; + cmLocalGenerator* LocalGenerator = nullptr; +}; 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 +#include +#include +#include + +#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 installRPaths, bool noInstallRPath, + std::string installNameDir, bool noInstallName, const char* depsVar, + const char* rpathPrefix, const char* tmpVarPrefix, std::string destination, + std::vector 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 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& 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& 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& 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 +#include +#include + +#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 installRPaths, bool noInstallRPath, + std::string installNameDir, bool noInstallName, const char* depsVar, + const char* rpathPrefix, const char* tmpVarPrefix, std::string destination, + std::vector 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 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& evaluatedRPaths, Indent indent); + void GenerateAppleFrameworkScript( + std::ostream& os, const std::string& config, + const std::vector& evaluatedRPaths, Indent indent); + void GenerateInstallNameFixup( + std::ostream& os, const std::string& config, + const std::vector& 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 06246d1..26f255d 100644 --- a/Source/cmRuntimeDependencyArchive.cxx +++ b/Source/cmRuntimeDependencyArchive.cxx @@ -397,3 +397,11 @@ cmRuntimeDependencyArchive::GetRPaths() const { return this->RPaths; } + +bool cmRuntimeDependencyArchive::PlatformSupportsRuntimeDependencies( + const std::string& platform) +{ + static const std::set supportedPlatforms = { "Windows", "Linux", + "Darwin" }; + return supportedPlatforms.count(platform); +} diff --git a/Source/cmRuntimeDependencyArchive.h b/Source/cmRuntimeDependencyArchive.h index 20a38e2..b170815 100644 --- a/Source/cmRuntimeDependencyArchive.h +++ b/Source/cmRuntimeDependencyArchive.h @@ -53,6 +53,8 @@ public: const std::set& GetUnresolvedPaths() const; const std::map>& GetRPaths() const; + static bool PlatformSupportsRuntimeDependencies(const std::string& platform); + private: cmExecutionStatus& Status; std::unique_ptr Linker; diff --git a/Tests/RunCMake/install/RunCMakeTest.cmake b/Tests/RunCMake/install/RunCMakeTest.cmake index 94887a0..0f6ec70 100644 --- a/Tests/RunCMake/install/RunCMakeTest.cmake +++ b/Tests/RunCMake/install/RunCMakeTest.cmake @@ -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) diff --git a/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-cross-result.txt b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-cross-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-cross-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-cross-stderr.txt b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-cross-stderr.txt new file mode 100644 index 0000000..b7bbb55 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-cross-stderr.txt @@ -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\)$ diff --git a/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-cross.cmake b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-cross.cmake new file mode 100644 index 0000000..36b2eb7 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-cross.cmake @@ -0,0 +1,4 @@ +enable_language(C) + +add_executable(exe main.c) +install(TARGETS exe RUNTIME_DEPENDENCIES) diff --git a/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-empty-all-check.cmake b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-empty-all-check.cmake new file mode 100644 index 0000000..dafc2a4 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-empty-all-check.cmake @@ -0,0 +1 @@ +check_installed([[^static;static/(liblib\.a|lib\.lib)$]]) diff --git a/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-empty.cmake b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-empty.cmake new file mode 100644 index 0000000..e46e1d9 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-empty.cmake @@ -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 + ) diff --git a/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-macos-no-framework-result.txt b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-macos-no-framework-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-macos-no-framework-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-macos-no-framework-stderr.txt b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-macos-no-framework-stderr.txt new file mode 100644 index 0000000..6ab14f6 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-macos-no-framework-stderr.txt @@ -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\)$ diff --git a/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-macos-no-framework.cmake b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-macos-no-framework.cmake new file mode 100644 index 0000000..36b2eb7 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-macos-no-framework.cmake @@ -0,0 +1,4 @@ +enable_language(C) + +add_executable(exe main.c) +install(TARGETS exe RUNTIME_DEPENDENCIES) diff --git a/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-macos-two-bundle-result.txt b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-macos-two-bundle-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-macos-two-bundle-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-macos-two-bundle-stderr.txt b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-macos-two-bundle-stderr.txt new file mode 100644 index 0000000..c7b49c7 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-macos-two-bundle-stderr.txt @@ -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\)$ diff --git a/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-macos-two-bundle.cmake b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-macos-two-bundle.cmake new file mode 100644 index 0000000..a848a24 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-macos-two-bundle.cmake @@ -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 + ) diff --git a/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-nodep-all-check.cmake b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-nodep-all-check.cmake new file mode 100644 index 0000000..9f455b1 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-nodep-all-check.cmake @@ -0,0 +1 @@ +check_installed([[^bin;bin/exe(\.exe)?$]]) diff --git a/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-nodep.cmake b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-nodep.cmake new file mode 100644 index 0000000..02466561 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-nodep.cmake @@ -0,0 +1,9 @@ +enable_language(C) + +add_executable(exe main.c) + +install(TARGETS exe + RUNTIME_DEPENDENCIES + PRE_EXCLUDE_REGEXES ".*" + FRAMEWORK DESTINATION fw + ) diff --git a/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-unsupported-result.txt b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-unsupported-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-unsupported-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-unsupported-stderr.txt b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-unsupported-stderr.txt new file mode 100644 index 0000000..c098a4c --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-unsupported-stderr.txt @@ -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\)$ diff --git a/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-unsupported.cmake b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-unsupported.cmake new file mode 100644 index 0000000..36b2eb7 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCIES-unsupported.cmake @@ -0,0 +1,4 @@ +enable_language(C) + +add_executable(exe main.c) +install(TARGETS exe RUNTIME_DEPENDENCIES) diff --git a/bootstrap b/bootstrap index 720fa76..f8d2f69 100755 --- a/bootstrap +++ b/bootstrap @@ -387,8 +387,10 @@ CMAKE_CXX_SOURCES="\ cmInstallFilesCommand \ cmInstallFilesGenerator \ cmInstallGenerator \ + cmInstallGetRuntimeDependenciesGenerator \ cmInstallImportedRuntimeArtifactsGenerator \ cmInstallRuntimeDependencySet \ + cmInstallRuntimeDependencySetGenerator \ cmInstallScriptGenerator \ cmInstallSubdirectoryGenerator \ cmInstallTargetGenerator \ -- cgit v0.12 From 3e7d3c252a5638d66dbbe536be79d6ed3e0e288d Mon Sep 17 00:00:00 2001 From: Kyle Edwards Date: Wed, 19 May 2021 13:36:30 -0400 Subject: install(TARGETS): Add RUNTIME_DEPENDENCY_SET argument --- Source/cmInstallCommand.cxx | 19 +++++++++++++++++++ Tests/RunCMake/install/RunCMakeTest.cmake | 2 ++ ...DENCY_SET-RUNTIME_DEPENDENCIES-conflict-result.txt | 1 + ...DENCY_SET-RUNTIME_DEPENDENCIES-conflict-stderr.txt | 5 +++++ ...DEPENDENCY_SET-RUNTIME_DEPENDENCIES-conflict.cmake | 7 +++++++ ...GETS-RUNTIME_DEPENDENCY_SET-unsupported-result.txt | 1 + ...GETS-RUNTIME_DEPENDENCY_SET-unsupported-stderr.txt | 5 +++++ .../TARGETS-RUNTIME_DEPENDENCY_SET-unsupported.cmake | 4 ++++ 8 files changed, 44 insertions(+) create mode 100644 Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCY_SET-RUNTIME_DEPENDENCIES-conflict-result.txt create mode 100644 Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCY_SET-RUNTIME_DEPENDENCIES-conflict-stderr.txt create mode 100644 Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCY_SET-RUNTIME_DEPENDENCIES-conflict.cmake create mode 100644 Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCY_SET-unsupported-result.txt create mode 100644 Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCY_SET-unsupported-stderr.txt create mode 100644 Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCY_SET-unsupported.cmake diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index c36778d..5d5a5a6 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -413,12 +413,14 @@ bool HandleTargetsMode(std::vector const& args, std::vector targetList; std::string exports; std::vector runtimeDependenciesArgVector; + std::string runtimeDependencySetArg; std::vector unknownArgs; std::vector parsedArgs; cmInstallCommandArguments genericArgs(helper.DefaultComponentName); genericArgs.Bind("TARGETS"_s, targetList); genericArgs.Bind("EXPORT"_s, exports); genericArgs.Bind("RUNTIME_DEPENDENCIES"_s, runtimeDependenciesArgVector); + genericArgs.Bind("RUNTIME_DEPENDENCY_SET"_s, runtimeDependencySetArg); genericArgs.Parse(genericArgVector, &unknownArgs, nullptr, &parsedArgs); bool success = genericArgs.Finalize(); @@ -537,6 +539,11 @@ bool HandleTargetsMode(std::vector const& args, 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)) { @@ -559,6 +566,18 @@ bool HandleTargetsMode(std::vector const& args, } 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. diff --git a/Tests/RunCMake/install/RunCMakeTest.cmake b/Tests/RunCMake/install/RunCMakeTest.cmake index 0f6ec70..bfd01ac 100644 --- a/Tests/RunCMake/install/RunCMakeTest.cmake +++ b/Tests/RunCMake/install/RunCMakeTest.cmake @@ -185,8 +185,10 @@ if(CMAKE_SYSTEM_NAME MATCHES "^(Linux|Darwin|Windows)$") set(RunCMake_TEST_OPTIONS "-DCMAKE_SYSTEM_NAME:STRING=${CMAKE_SYSTEM_NAME}") run_cmake(TARGETS-RUNTIME_DEPENDENCIES-cross) unset(RunCMake_TEST_OPTIONS) + run_cmake(TARGETS-RUNTIME_DEPENDENCY_SET-RUNTIME_DEPENDENCIES-conflict) else() run_cmake(TARGETS-RUNTIME_DEPENDENCIES-unsupported) + run_cmake(TARGETS-RUNTIME_DEPENDENCY_SET-unsupported) endif() set(run_install_test_components 1) diff --git a/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCY_SET-RUNTIME_DEPENDENCIES-conflict-result.txt b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCY_SET-RUNTIME_DEPENDENCIES-conflict-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCY_SET-RUNTIME_DEPENDENCIES-conflict-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCY_SET-RUNTIME_DEPENDENCIES-conflict-stderr.txt b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCY_SET-RUNTIME_DEPENDENCIES-conflict-stderr.txt new file mode 100644 index 0000000..e2484ec --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCY_SET-RUNTIME_DEPENDENCIES-conflict-stderr.txt @@ -0,0 +1,5 @@ +^CMake Error at TARGETS-RUNTIME_DEPENDENCY_SET-RUNTIME_DEPENDENCIES-conflict\.cmake:[0-9]+ \(install\): + install TARGETS cannot have both RUNTIME_DEPENDENCIES and + RUNTIME_DEPENDENCY_SET\. +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCY_SET-RUNTIME_DEPENDENCIES-conflict.cmake b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCY_SET-RUNTIME_DEPENDENCIES-conflict.cmake new file mode 100644 index 0000000..f373b3b --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCY_SET-RUNTIME_DEPENDENCIES-conflict.cmake @@ -0,0 +1,7 @@ +enable_language(C) + +add_executable(exe main.c) +install(TARGETS exe + RUNTIME_DEPENDENCY_SET deps + RUNTIME_DEPENDENCIES + ) diff --git a/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCY_SET-unsupported-result.txt b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCY_SET-unsupported-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCY_SET-unsupported-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCY_SET-unsupported-stderr.txt b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCY_SET-unsupported-stderr.txt new file mode 100644 index 0000000..f660346 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCY_SET-unsupported-stderr.txt @@ -0,0 +1,5 @@ +^CMake Error at TARGETS-RUNTIME_DEPENDENCY_SET-unsupported\.cmake:[0-9]+ \(install\): + install TARGETS RUNTIME_DEPENDENCY_SET is not supported on system "[^ +]*" +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCY_SET-unsupported.cmake b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCY_SET-unsupported.cmake new file mode 100644 index 0000000..be0f507 --- /dev/null +++ b/Tests/RunCMake/install/TARGETS-RUNTIME_DEPENDENCY_SET-unsupported.cmake @@ -0,0 +1,4 @@ +enable_language(C) + +add_executable(exe main.c) +install(TARGETS exe RUNTIME_DEPENDENCY_SET deps) -- cgit v0.12 From bc8a4a06a488a0cfd260901acf189e91c8906dbd Mon Sep 17 00:00:00 2001 From: Kyle Edwards Date: Wed, 2 Jun 2021 10:30:55 -0400 Subject: install(IMPORTED_RUNTIME_ARTIFACTS): Add RUNTIME_DEPENDENCY_SET option --- Source/cmInstallCommand.cxx | 41 +++++++++++++++++++++- ...S-RUNTIME_DEPENDENCY_SET-unsupported-result.txt | 1 + ...S-RUNTIME_DEPENDENCY_SET-unsupported-stderr.txt | 6 ++++ ...IFACTS-RUNTIME_DEPENDENCY_SET-unsupported.cmake | 2 ++ Tests/RunCMake/install/RunCMakeTest.cmake | 1 + 5 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 Tests/RunCMake/install/IMPORTED_RUNTIME_ARTIFACTS-RUNTIME_DEPENDENCY_SET-unsupported-result.txt create mode 100644 Tests/RunCMake/install/IMPORTED_RUNTIME_ARTIFACTS-RUNTIME_DEPENDENCY_SET-unsupported-stderr.txt create mode 100644 Tests/RunCMake/install/IMPORTED_RUNTIME_ARTIFACTS-RUNTIME_DEPENDENCY_SET-unsupported.cmake diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index 5d5a5a6..67451aa 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -1098,9 +1098,11 @@ bool HandleImportedRuntimeArtifactsMode(std::vector const& args, // now parse the generic args (i.e. the ones not specialized on LIBRARY, // RUNTIME etc. (see above) std::vector targetList; + std::string runtimeDependencySetArg; std::vector 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(); @@ -1139,6 +1141,22 @@ bool HandleImportedRuntimeArtifactsMode(std::vector 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; @@ -1217,6 +1235,9 @@ bool HandleImportedRuntimeArtifactsMode(std::vector 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 " @@ -1227,14 +1248,23 @@ bool HandleImportedRuntimeArtifactsMode(std::vector 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()) { @@ -1247,9 +1277,18 @@ bool HandleImportedRuntimeArtifactsMode(std::vector 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: diff --git a/Tests/RunCMake/install/IMPORTED_RUNTIME_ARTIFACTS-RUNTIME_DEPENDENCY_SET-unsupported-result.txt b/Tests/RunCMake/install/IMPORTED_RUNTIME_ARTIFACTS-RUNTIME_DEPENDENCY_SET-unsupported-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/IMPORTED_RUNTIME_ARTIFACTS-RUNTIME_DEPENDENCY_SET-unsupported-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/IMPORTED_RUNTIME_ARTIFACTS-RUNTIME_DEPENDENCY_SET-unsupported-stderr.txt b/Tests/RunCMake/install/IMPORTED_RUNTIME_ARTIFACTS-RUNTIME_DEPENDENCY_SET-unsupported-stderr.txt new file mode 100644 index 0000000..7ae6606 --- /dev/null +++ b/Tests/RunCMake/install/IMPORTED_RUNTIME_ARTIFACTS-RUNTIME_DEPENDENCY_SET-unsupported-stderr.txt @@ -0,0 +1,6 @@ +^CMake Error at IMPORTED_RUNTIME_ARTIFACTS-RUNTIME_DEPENDENCY_SET-unsupported\.cmake:[0-9]+ \(install\): + install IMPORTED_RUNTIME_ARTIFACTS RUNTIME_DEPENDENCY_SET is not supported + on system "[^ +]*" +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/install/IMPORTED_RUNTIME_ARTIFACTS-RUNTIME_DEPENDENCY_SET-unsupported.cmake b/Tests/RunCMake/install/IMPORTED_RUNTIME_ARTIFACTS-RUNTIME_DEPENDENCY_SET-unsupported.cmake new file mode 100644 index 0000000..04f1995 --- /dev/null +++ b/Tests/RunCMake/install/IMPORTED_RUNTIME_ARTIFACTS-RUNTIME_DEPENDENCY_SET-unsupported.cmake @@ -0,0 +1,2 @@ +add_executable(exe IMPORTED) +install(IMPORTED_RUNTIME_ARTIFACTS exe RUNTIME_DEPENDENCY_SET deps) diff --git a/Tests/RunCMake/install/RunCMakeTest.cmake b/Tests/RunCMake/install/RunCMakeTest.cmake index bfd01ac..6f6a193 100644 --- a/Tests/RunCMake/install/RunCMakeTest.cmake +++ b/Tests/RunCMake/install/RunCMakeTest.cmake @@ -189,6 +189,7 @@ if(CMAKE_SYSTEM_NAME MATCHES "^(Linux|Darwin|Windows)$") else() run_cmake(TARGETS-RUNTIME_DEPENDENCIES-unsupported) run_cmake(TARGETS-RUNTIME_DEPENDENCY_SET-unsupported) + run_cmake(IMPORTED_RUNTIME_ARTIFACTS-RUNTIME_DEPENDENCY_SET-unsupported) endif() set(run_install_test_components 1) -- cgit v0.12 From 4910132d8c94ac2c08fddd3fafc79585e7423362 Mon Sep 17 00:00:00 2001 From: Kyle Edwards Date: Wed, 19 May 2021 15:06:15 -0400 Subject: install: Add RUNTIME_DEPENDENCY_SET mode --- Source/cmInstallCommand.cxx | 115 +++++++++++++++++++++ .../RUNTIME_DEPENDENCY_SET-unsupported-result.txt | 1 + .../RUNTIME_DEPENDENCY_SET-unsupported-stderr.txt | 5 + .../RUNTIME_DEPENDENCY_SET-unsupported.cmake | 1 + Tests/RunCMake/install/RunCMakeTest.cmake | 1 + 5 files changed, 123 insertions(+) create mode 100644 Tests/RunCMake/install/RUNTIME_DEPENDENCY_SET-unsupported-result.txt create mode 100644 Tests/RunCMake/install/RUNTIME_DEPENDENCY_SET-unsupported-stderr.txt create mode 100644 Tests/RunCMake/install/RUNTIME_DEPENDENCY_SET-unsupported.cmake diff --git a/Source/cmInstallCommand.cxx b/Source/cmInstallCommand.cxx index 67451aa..79109b5 100644 --- a/Source/cmInstallCommand.cxx +++ b/Source/cmInstallCommand.cxx @@ -1942,6 +1942,120 @@ bool HandleExportMode(std::vector const& args, return true; } +bool HandleRuntimeDependencySetMode(std::vector 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 Library; + std::vector Runtime; + std::vector Framework; + }; + + static auto const argHelper = cmArgumentParser{} + .Bind("LIBRARY"_s, &ArgVectors::Library) + .Bind("RUNTIME"_s, &ArgVectors::Runtime) + .Bind("FRAMEWORK"_s, &ArgVectors::Framework); + + std::vector 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 runtimeDependencyArgVector; + std::vector 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 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& relFiles, std::vector& absFiles) @@ -2176,6 +2290,7 @@ bool cmInstallCommand(std::vector 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/Tests/RunCMake/install/RUNTIME_DEPENDENCY_SET-unsupported-result.txt b/Tests/RunCMake/install/RUNTIME_DEPENDENCY_SET-unsupported-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/install/RUNTIME_DEPENDENCY_SET-unsupported-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/install/RUNTIME_DEPENDENCY_SET-unsupported-stderr.txt b/Tests/RunCMake/install/RUNTIME_DEPENDENCY_SET-unsupported-stderr.txt new file mode 100644 index 0000000..cab309b --- /dev/null +++ b/Tests/RunCMake/install/RUNTIME_DEPENDENCY_SET-unsupported-stderr.txt @@ -0,0 +1,5 @@ +^CMake Error at RUNTIME_DEPENDENCY_SET-unsupported\.cmake:[0-9]+ \(install\): + install RUNTIME_DEPENDENCY_SET is not supported on system "[^ +]*" +Call Stack \(most recent call first\): + CMakeLists\.txt:[0-9]+ \(include\)$ diff --git a/Tests/RunCMake/install/RUNTIME_DEPENDENCY_SET-unsupported.cmake b/Tests/RunCMake/install/RUNTIME_DEPENDENCY_SET-unsupported.cmake new file mode 100644 index 0000000..60772a0 --- /dev/null +++ b/Tests/RunCMake/install/RUNTIME_DEPENDENCY_SET-unsupported.cmake @@ -0,0 +1 @@ +install(RUNTIME_DEPENDENCY_SET deps) diff --git a/Tests/RunCMake/install/RunCMakeTest.cmake b/Tests/RunCMake/install/RunCMakeTest.cmake index 6f6a193..e9a29c4 100644 --- a/Tests/RunCMake/install/RunCMakeTest.cmake +++ b/Tests/RunCMake/install/RunCMakeTest.cmake @@ -190,6 +190,7 @@ else() run_cmake(TARGETS-RUNTIME_DEPENDENCIES-unsupported) run_cmake(TARGETS-RUNTIME_DEPENDENCY_SET-unsupported) run_cmake(IMPORTED_RUNTIME_ARTIFACTS-RUNTIME_DEPENDENCY_SET-unsupported) + run_cmake(RUNTIME_DEPENDENCY_SET-unsupported) endif() set(run_install_test_components 1) -- cgit v0.12 From 0c3c6acaff7e59712bdfc6ef66292ccebe8c5bcb Mon Sep 17 00:00:00 2001 From: Kyle Edwards Date: Wed, 2 Jun 2021 09:46:37 -0400 Subject: Tests: Add tests for new options --- Tests/CMakeLists.txt | 1 + Tests/ExportImport/CMakeLists.txt | 36 +++++++---- Tests/ExportImport/Export/CMakeLists.txt | 2 + .../install-RUNTIME_DEPENDENCY_SET/CMakeLists.txt | 42 ++++++++++++ .../Export/install-RUNTIME_DEPENDENCY_SET/deplib.c | 12 ++++ Tests/ExportImport/Import/CMakeLists.txt | 6 ++ Tests/ExportImport/Import/check_installed.cmake | 20 ++++++ .../check_installed.cmake | 21 +----- .../install-RUNTIME_DEPENDENCIES/CMakeLists.txt | 74 ++++++++++++++++++++++ .../check_installed.cmake | 11 ++++ .../Import/install-RUNTIME_DEPENDENCIES/lib.c | 12 ++++ .../Import/install-RUNTIME_DEPENDENCIES/main.c | 31 +++++++++ .../Import/install-RUNTIME_DEPENDENCIES/mod.c | 12 ++++ .../Import/install-RUNTIME_DEPENDENCIES/sublib1.c | 12 ++++ .../Import/install-RUNTIME_DEPENDENCIES/sublib2.c | 12 ++++ .../install-RUNTIME_DEPENDENCY_SET/CMakeLists.txt | 33 ++++++++++ .../check_installed.cmake | 11 ++++ .../Import/install-RUNTIME_DEPENDENCY_SET/main.c | 15 +++++ Tests/RunCMake/install/RunCMakeTest.cmake | 1 + .../install/RuntimeDependencies-COMPONENTS.cmake | 39 ++++++++++++ 20 files changed, 369 insertions(+), 34 deletions(-) create mode 100644 Tests/ExportImport/Export/install-RUNTIME_DEPENDENCY_SET/CMakeLists.txt create mode 100644 Tests/ExportImport/Export/install-RUNTIME_DEPENDENCY_SET/deplib.c create mode 100644 Tests/ExportImport/Import/check_installed.cmake create mode 100644 Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/CMakeLists.txt create mode 100644 Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/check_installed.cmake create mode 100644 Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/lib.c create mode 100644 Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/main.c create mode 100644 Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/mod.c create mode 100644 Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/sublib1.c create mode 100644 Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/sublib2.c create mode 100644 Tests/ExportImport/Import/install-RUNTIME_DEPENDENCY_SET/CMakeLists.txt create mode 100644 Tests/ExportImport/Import/install-RUNTIME_DEPENDENCY_SET/check_installed.cmake create mode 100644 Tests/ExportImport/Import/install-RUNTIME_DEPENDENCY_SET/main.c create mode 100644 Tests/RunCMake/install/RuntimeDependencies-COMPONENTS.cmake diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 38b4301..1005016 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -478,6 +478,7 @@ if(BUILD_TESTING) ADD_TEST_MACRO(Preprocess Preprocess) set(ExportImport_BUILD_OPTIONS -DCMake_TEST_NESTED_MAKE_PROGRAM:FILEPATH=${CMake_TEST_EXPLICIT_MAKE_PROGRAM} -DCMake_TEST_CUDA:BOOL=${CMake_TEST_CUDA} + -DCMake_INSTALL_NAME_TOOL_BUG:BOOL=${CMake_INSTALL_NAME_TOOL_BUG} ) ADD_TEST_MACRO(ExportImport ExportImport) set_property(TEST ExportImport APPEND diff --git a/Tests/ExportImport/CMakeLists.txt b/Tests/ExportImport/CMakeLists.txt index 4999612..e3f32c2 100644 --- a/Tests/ExportImport/CMakeLists.txt +++ b/Tests/ExportImport/CMakeLists.txt @@ -77,21 +77,29 @@ set_property( PROPERTY SYMBOLIC 1 ) -# Install the imported targets. -add_custom_command( - OUTPUT ${ExportImport_BINARY_DIR}/ImportInstall - COMMAND ${CMAKE_COMMAND} -E rm -rf ${ExportImport_BINARY_DIR}/Import/install - COMMAND ${CMAKE_COMMAND} - --install ${ExportImport_BINARY_DIR}/Import - --prefix ${ExportImport_BINARY_DIR}/Import/install - ${NESTED_CONFIG_INSTALL_TYPE} - ) -add_custom_target(ImportInstallTarget ALL DEPENDS ${ExportImport_BINARY_DIR}/ImportInstall) +# Run the install tests. +set(install_deps) +set(rdep_tests) +if(CMAKE_SYSTEM_NAME MATCHES "^(Linux|Windows|Darwin)$" AND NOT CMake_INSTALL_NAME_TOOL_BUG) + set(rdep_tests RUNTIME_DEPENDENCIES RUNTIME_DEPENDENCY_SET) +endif() +foreach(name IMPORTED_RUNTIME_ARTIFACTS ${rdep_tests}) + add_custom_command( + OUTPUT ${ExportImport_BINARY_DIR}/ImportInstall-${name} + COMMAND ${CMAKE_COMMAND} -E rm -rf ${ExportImport_BINARY_DIR}/Import/install-${name}/install + COMMAND ${CMAKE_COMMAND} + --install ${ExportImport_BINARY_DIR}/Import/install-${name} + --prefix ${ExportImport_BINARY_DIR}/Import/install-${name}/install + ${NESTED_CONFIG_INSTALL_TYPE} + ) + list(APPEND install_deps ${ExportImport_BINARY_DIR}/ImportInstall-${name}) + set_property( + SOURCE ${ExportImport_BINARY_DIR}/ImportInstall-${name} + PROPERTY SYMBOLIC 1 + ) +endforeach() +add_custom_target(ImportInstallTarget ALL DEPENDS ${install_deps}) add_dependencies(ImportInstallTarget ImportTarget) -set_property( - SOURCE ${ExportImport_BINARY_DIR}/ImportInstall - PROPERTY SYMBOLIC 1 - ) add_executable(ExportImport main.c) add_dependencies(ExportImport ImportTarget) diff --git a/Tests/ExportImport/Export/CMakeLists.txt b/Tests/ExportImport/Export/CMakeLists.txt index fa0016b..a2968d4 100644 --- a/Tests/ExportImport/Export/CMakeLists.txt +++ b/Tests/ExportImport/Export/CMakeLists.txt @@ -690,3 +690,5 @@ if(NOT XCODE) install(TARGETS testLibFromGeneratedSource EXPORT testLibFromGeneratedSource_Export) install(EXPORT testLibFromGeneratedSource_Export DESTINATION lib) endif() + +add_subdirectory(install-RUNTIME_DEPENDENCY_SET) diff --git a/Tests/ExportImport/Export/install-RUNTIME_DEPENDENCY_SET/CMakeLists.txt b/Tests/ExportImport/Export/install-RUNTIME_DEPENDENCY_SET/CMakeLists.txt new file mode 100644 index 0000000..f50cc1d --- /dev/null +++ b/Tests/ExportImport/Export/install-RUNTIME_DEPENDENCY_SET/CMakeLists.txt @@ -0,0 +1,42 @@ +cmake_minimum_required(VERSION 3.20) + +set(CMAKE_SKIP_RPATH OFF) + +foreach(i 1 2 3 4 5 6 7 8 9 10 11 12) + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/dep${i}.c" +"#ifdef _WIN32 +__declspec(dllexport) +#endif + void dep${i}(void) +{ +} +") + add_library(dep${i} SHARED "${CMAKE_CURRENT_BINARY_DIR}/dep${i}.c") +endforeach() + +set_target_properties(dep9 PROPERTIES + FRAMEWORK TRUE + ) +set_target_properties(dep2 PROPERTIES + VERSION 1.2.3 + SOVERSION 1 + ) + +add_library(deplib SHARED deplib.c) +target_link_libraries(deplib PRIVATE dep1) +if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set_target_properties(deplib PROPERTIES + INSTALL_RPATH "@loader_path/" + ) +endif() + +install(TARGETS dep1 dep2 dep3 dep4 dep5 dep6 dep7 dep8 dep9 dep10 dep11 dep12 deplib EXPORT install-RUNTIME_DEPENDENCY_SET + RUNTIME DESTINATION install-RUNTIME_DEPENDENCY_SET/bin + LIBRARY DESTINATION install-RUNTIME_DEPENDENCY_SET/lib + ARCHIVE DESTINATION install-RUNTIME_DEPENDENCY_SET/lib + FRAMEWORK DESTINATION install-RUNTIME_DEPENDENCY_SET/frameworks + ) +install(EXPORT install-RUNTIME_DEPENDENCY_SET + FILE targets.cmake + DESTINATION install-RUNTIME_DEPENDENCY_SET + ) diff --git a/Tests/ExportImport/Export/install-RUNTIME_DEPENDENCY_SET/deplib.c b/Tests/ExportImport/Export/install-RUNTIME_DEPENDENCY_SET/deplib.c new file mode 100644 index 0000000..e5da196 --- /dev/null +++ b/Tests/ExportImport/Export/install-RUNTIME_DEPENDENCY_SET/deplib.c @@ -0,0 +1,12 @@ +#ifdef _WIN32 +__declspec(dllimport) +#endif + extern void dep1(void); + +#ifdef _WIN32 +__declspec(dllexport) +#endif + void deplib(void) +{ + dep1(); +} diff --git a/Tests/ExportImport/Import/CMakeLists.txt b/Tests/ExportImport/Import/CMakeLists.txt index e64c6b4..0063130 100644 --- a/Tests/ExportImport/Import/CMakeLists.txt +++ b/Tests/ExportImport/Import/CMakeLists.txt @@ -29,3 +29,9 @@ add_subdirectory(version_range) # Test install(IMPORTED_RUNTIME_ARTIFACTS) add_subdirectory(install-IMPORTED_RUNTIME_ARTIFACTS) + +# Test install(RUNTIME_DEPENDENCIES) and install(RUNTIME_DEPENDENCY_SET) +if(CMAKE_SYSTEM_NAME MATCHES "^(Linux|Windows|Darwin)$") + add_subdirectory(install-RUNTIME_DEPENDENCIES) + add_subdirectory(install-RUNTIME_DEPENDENCY_SET) +endif() diff --git a/Tests/ExportImport/Import/check_installed.cmake b/Tests/ExportImport/Import/check_installed.cmake new file mode 100644 index 0000000..6e51f92 --- /dev/null +++ b/Tests/ExportImport/Import/check_installed.cmake @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.20) + +function(check_installed expect) + file(GLOB_RECURSE actual + LIST_DIRECTORIES TRUE + RELATIVE ${CMAKE_INSTALL_PREFIX} + ${CMAKE_INSTALL_PREFIX}/* + ) + if(actual) + list(SORT actual) + endif() + if(NOT "${actual}" MATCHES "${expect}") + message(FATAL_ERROR "Installed files: + ${actual} +do not match what we expected: + ${expect} +in directory: + ${CMAKE_INSTALL_PREFIX}") + endif() +endfunction() diff --git a/Tests/ExportImport/Import/install-IMPORTED_RUNTIME_ARTIFACTS/check_installed.cmake b/Tests/ExportImport/Import/install-IMPORTED_RUNTIME_ARTIFACTS/check_installed.cmake index 29b298f..054ce9c 100644 --- a/Tests/ExportImport/Import/install-IMPORTED_RUNTIME_ARTIFACTS/check_installed.cmake +++ b/Tests/ExportImport/Import/install-IMPORTED_RUNTIME_ARTIFACTS/check_installed.cmake @@ -1,23 +1,4 @@ -cmake_minimum_required(VERSION 3.20) - -function(check_installed expect) - file(GLOB_RECURSE actual - LIST_DIRECTORIES TRUE - RELATIVE ${CMAKE_INSTALL_PREFIX} - ${CMAKE_INSTALL_PREFIX}/* - ) - if(actual) - list(SORT actual) - endif() - if(NOT "${actual}" MATCHES "${expect}") - message(FATAL_ERROR "Installed files: - ${actual} -do not match what we expected: - ${expect} -in directory: - ${CMAKE_INSTALL_PREFIX}") - endif() -endfunction() +include("${CMAKE_CURRENT_LIST_DIR}/../check_installed.cmake") if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") set(_dirs [[aaa;aaa/executables;aaa/executables/testExe1-4;aaa/executables/testExe3;aaa/libraries;aaa/libraries/libtestExe2lib\.so;aaa/libraries/libtestLib3lib(-d|-r)?\.so\.1\.2;aaa/libraries/libtestLib3lib(-d|-r)?\.so\.3;aaa/libraries/libtestLib4\.so;aaa/libraries/libtestMod1\.so;aaa/libraries/libtestMod2\.so]]) diff --git a/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/CMakeLists.txt b/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/CMakeLists.txt new file mode 100644 index 0000000..d1c4ff2 --- /dev/null +++ b/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/CMakeLists.txt @@ -0,0 +1,74 @@ +set(CMAKE_SKIP_RPATH OFF) + +# Import targets from the install tree. +include(${Import_BINARY_DIR}/../Root/install-RUNTIME_DEPENDENCY_SET/targets.cmake) + +add_executable(exe1 main.c) +add_executable(exe2 main.c) + +if(NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set_target_properties(exe1 exe2 PROPERTIES + # Multiple MACOSX_BUNDLE executables are allowed on non-macOS platforms. + MACOSX_BUNDLE TRUE + ) +endif() + +add_library(sublib1 SHARED sublib1.c) +target_link_libraries(sublib1 PRIVATE dep6) + +add_library(sublib2 SHARED sublib2.c) +target_link_libraries(sublib2 PRIVATE dep7) + +foreach(i exe1 exe2) + target_link_libraries(${i} PRIVATE + dep1 + dep2 + dep3 + dep4 + dep5 + dep10 + dep11 + dep12 + sublib1 + sublib2 + ) +endforeach() + +add_library(lib SHARED lib.c) +target_link_libraries(lib PRIVATE dep8) + +add_library(mod MODULE mod.c) +target_link_libraries(mod PRIVATE dep9) +if(NOT CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set_target_properties(mod PROPERTIES + SKIP_BUILD_RPATH TRUE + ) +endif() + +set(_framework_args) +if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(_framework_args FRAMEWORK DESTINATION subdir/frameworks) +endif() +install(TARGETS exe1 exe2 lib mod sublib1 + RUNTIME_DEPENDENCIES + PRE_INCLUDE_REGEXES "$<1:dep([2-9]|1[012])>" + PRE_EXCLUDE_REGEXES "$<1:.*>" + POST_INCLUDE_REGEXES "$<1:(bin|lib)/(lib)?dep3>" + POST_EXCLUDE_REGEXES "$<1:(bin|lib)/(lib)?dep[34]>" + POST_INCLUDE_FILES "$" "$" + POST_EXCLUDE_FILES "$" "$" + DIRECTORIES "$" + RUNTIME DESTINATION "$<1:subdir/bin>" + LIBRARY DESTINATION "$<1:subdir/lib>" + ${_framework_args} + ) + +install(TARGETS lib + RUNTIME_DEPENDENCIES + PRE_INCLUDE_REGEXES dep8 + PRE_EXCLUDE_REGEXES ".*" + DIRECTORIES "$" + ${_framework_args} + ) + +install(SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/check_installed.cmake") diff --git a/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/check_installed.cmake b/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/check_installed.cmake new file mode 100644 index 0000000..6a34697 --- /dev/null +++ b/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/check_installed.cmake @@ -0,0 +1,11 @@ +include("${CMAKE_CURRENT_LIST_DIR}/../check_installed.cmake") + +if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") + check_installed([[^lib;lib/libdep8\.so;lib/liblib\.so;subdir;subdir/bin;subdir/bin/exe1;subdir/bin/exe2;subdir/lib;subdir/lib/libdep10\.so;subdir/lib/libdep11\.so;subdir/lib/libdep2\.so\.1;subdir/lib/libdep2\.so\.1\.2\.3;subdir/lib/libdep3\.so;subdir/lib/libdep5\.so;subdir/lib/libdep6\.so;subdir/lib/libdep8\.so;subdir/lib/libdep9\.so;subdir/lib/liblib\.so;subdir/lib/libmod\.so;subdir/lib/libsublib1\.so$]]) +elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + set(_msvc_check [[bin;bin/dep8\.dll;bin/lib\.dll;lib;lib/lib\.lib;subdir;subdir/bin;subdir/bin/dep10\.dll;subdir/bin/dep11\.dll;subdir/bin/dep2\.dll;subdir/bin/dep3\.dll;subdir/bin/dep5\.dll;subdir/bin/dep6\.dll;subdir/bin/dep8\.dll;subdir/bin/dep9\.dll;subdir/bin/exe1\.exe;subdir/bin/exe2\.exe;subdir/bin/lib\.dll;subdir/bin/sublib1\.dll;subdir/lib;subdir/lib/mod\.dll]]) + set(_mingw_check [[bin;bin/libdep8\.dll;bin/liblib\.dll;lib;lib/liblib\.dll\.a;lib/liblib\.lib;subdir;subdir/bin;subdir/bin/exe1\.exe;subdir/bin/exe2\.exe;subdir/bin/libdep10\.dll;subdir/bin/libdep11\.dll;subdir/bin/libdep2\.dll;subdir/bin/libdep3\.dll;subdir/bin/libdep5\.dll;subdir/bin/libdep6\.dll;subdir/bin/libdep8\.dll;subdir/bin/libdep9\.dll;subdir/bin/liblib\.dll;subdir/bin/libsublib1\.dll;subdir/lib;subdir/lib/libmod\.dll]]) + check_installed("^(${_msvc_check}|${_mingw_check})$") +elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin") + check_installed([[^lib;lib/libdep8\.dylib;lib/liblib\.dylib;subdir;subdir/bin;subdir/bin/exe1;subdir/bin/exe2;subdir/frameworks;subdir/frameworks/dep9\.framework;subdir/frameworks/dep9\.framework/Resources;subdir/frameworks/dep9\.framework/Versions;subdir/frameworks/dep9\.framework/Versions/A;subdir/frameworks/dep9\.framework/Versions/A/Resources;subdir/frameworks/dep9\.framework/Versions/A/Resources/Info\.plist(;subdir/frameworks/dep9.framework/Versions/A/_CodeSignature;subdir/frameworks/dep9.framework/Versions/A/_CodeSignature/CodeResources)?;subdir/frameworks/dep9\.framework/Versions/A/dep9;subdir/frameworks/dep9\.framework/Versions/Current;subdir/frameworks/dep9\.framework/dep9;subdir/lib;subdir/lib/libdep10\.dylib;subdir/lib/libdep11\.dylib;subdir/lib/libdep2\.1\.2\.3\.dylib;subdir/lib/libdep2\.1\.dylib;subdir/lib/libdep3\.dylib;subdir/lib/libdep5\.dylib;subdir/lib/libdep6\.dylib;subdir/lib/libdep8\.dylib;subdir/lib/liblib\.dylib;subdir/lib/libmod\.so;subdir/lib/libsublib1\.dylib$]]) +endif() diff --git a/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/lib.c b/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/lib.c new file mode 100644 index 0000000..9a64887 --- /dev/null +++ b/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/lib.c @@ -0,0 +1,12 @@ +#ifdef _WIN32 +__declspec(dllimport) +#endif + extern void dep8(void); + +#ifdef _WIN32 +__declspec(dllexport) +#endif + void lib(void) +{ + dep8(); +} diff --git a/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/main.c b/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/main.c new file mode 100644 index 0000000..94a7862 --- /dev/null +++ b/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/main.c @@ -0,0 +1,31 @@ +#ifdef _WIN32 +# define DLLIMPORT __declspec(dllimport) +#else +# define DLLIMPORT +#endif + +DLLIMPORT extern void dep1(void); +DLLIMPORT extern void dep2(void); +DLLIMPORT extern void dep3(void); +DLLIMPORT extern void dep4(void); +DLLIMPORT extern void dep5(void); +DLLIMPORT extern void dep10(void); +DLLIMPORT extern void dep11(void); +DLLIMPORT extern void dep12(void); +DLLIMPORT extern void sublib1(void); +DLLIMPORT extern void sublib2(void); + +int main(void) +{ + dep1(); + dep2(); + dep3(); + dep4(); + dep5(); + dep10(); + dep11(); + dep12(); + sublib1(); + sublib2(); + return 0; +} diff --git a/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/mod.c b/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/mod.c new file mode 100644 index 0000000..d001299 --- /dev/null +++ b/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/mod.c @@ -0,0 +1,12 @@ +#ifdef _WIN32 +__declspec(dllimport) +#endif + extern void dep9(void); + +#ifdef _WIN32 +__declspec(dllexport) +#endif + void mod(void) +{ + dep9(); +} diff --git a/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/sublib1.c b/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/sublib1.c new file mode 100644 index 0000000..f17b902 --- /dev/null +++ b/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/sublib1.c @@ -0,0 +1,12 @@ +#ifdef _WIN32 +__declspec(dllimport) +#endif + extern void dep6(void); + +#ifdef _WIN32 +__declspec(dllexport) +#endif + void sublib1(void) +{ + dep6(); +} diff --git a/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/sublib2.c b/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/sublib2.c new file mode 100644 index 0000000..61b5c83 --- /dev/null +++ b/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCIES/sublib2.c @@ -0,0 +1,12 @@ +#ifdef _WIN32 +__declspec(dllimport) +#endif + extern void dep7(void); + +#ifdef _WIN32 +__declspec(dllexport) +#endif + void sublib2(void) +{ + dep7(); +} diff --git a/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCY_SET/CMakeLists.txt b/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCY_SET/CMakeLists.txt new file mode 100644 index 0000000..5164506 --- /dev/null +++ b/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCY_SET/CMakeLists.txt @@ -0,0 +1,33 @@ +set(CMAKE_SKIP_RPATH OFF) + +# Import targets from the install tree. +include(${Import_BINARY_DIR}/../Root/install-RUNTIME_DEPENDENCY_SET/targets.cmake) + +add_executable(exe main.c) +target_link_libraries(exe PRIVATE dep3 dep4) + +install(TARGETS exe RUNTIME_DEPENDENCY_SET myset) +install(IMPORTED_RUNTIME_ARTIFACTS deplib RUNTIME_DEPENDENCY_SET myset) + +install(RUNTIME_DEPENDENCY_SET myset + PRE_INCLUDE_REGEXES "dep[134]" + PRE_EXCLUDE_REGEXES ".*" + POST_INCLUDE_REGEXES "dep[13]" + POST_EXCLUDE_REGEXES "dep[34]" + DIRECTORIES "$" + ) +install(RUNTIME_DEPENDENCY_SET myset + PRE_INCLUDE_REGEXES "dep[134]" + PRE_EXCLUDE_REGEXES ".*" + DIRECTORIES "$" + RUNTIME DESTINATION yyy/bin + LIBRARY DESTINATION yyy/lib + ) +install(RUNTIME_DEPENDENCY_SET myset + PRE_INCLUDE_REGEXES "dep[134]" + PRE_EXCLUDE_REGEXES ".*" + DIRECTORIES "$" + DESTINATION zzz + ) + +install(SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/check_installed.cmake") diff --git a/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCY_SET/check_installed.cmake b/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCY_SET/check_installed.cmake new file mode 100644 index 0000000..052cced --- /dev/null +++ b/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCY_SET/check_installed.cmake @@ -0,0 +1,11 @@ +include("${CMAKE_CURRENT_LIST_DIR}/../check_installed.cmake") + +if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Linux") + check_installed([[^bin;bin/exe;lib;lib/libdep1\.so;lib/libdep3\.so;lib/libdeplib\.so;yyy;yyy/lib;yyy/lib/libdep1\.so;yyy/lib/libdep3\.so;yyy/lib/libdep4\.so;zzz;zzz/libdep1\.so;zzz/libdep3\.so;zzz/libdep4\.so$]]) +elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + set(_msvc_check [[bin;bin/dep1\.dll;bin/dep3\.dll;bin/deplib\.dll;bin/exe\.exe;yyy;yyy/bin;yyy/bin/dep1\.dll;yyy/bin/dep3\.dll;yyy/bin/dep4\.dll;zzz;zzz/dep1\.dll;zzz/dep3\.dll;zzz/dep4\.dll]]) + set(_mingw_check [[bin;bin/exe\.exe;bin/libdep1\.dll;bin/libdep3\.dll;bin/libdeplib\.dll;yyy;yyy/bin;yyy/bin/libdep1\.dll;yyy/bin/libdep3\.dll;yyy/bin/libdep4\.dll;zzz;zzz/libdep1\.dll;zzz/libdep3\.dll;zzz/libdep4\.dll]]) + check_installed("^(${_msvc_check}|${_mingw_check})$") +elseif(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin") + check_installed([[^bin;bin/exe;lib;lib/libdep1\.dylib;lib/libdep3\.dylib;lib/libdeplib\.dylib;yyy;yyy/lib;yyy/lib/libdep1\.dylib;yyy/lib/libdep3\.dylib;yyy/lib/libdep4\.dylib;zzz;zzz/libdep1\.dylib;zzz/libdep3\.dylib;zzz/libdep4\.dylib$]]) +endif() diff --git a/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCY_SET/main.c b/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCY_SET/main.c new file mode 100644 index 0000000..b359498 --- /dev/null +++ b/Tests/ExportImport/Import/install-RUNTIME_DEPENDENCY_SET/main.c @@ -0,0 +1,15 @@ +#ifdef _WIN32 +__declspec(dllimport) +#endif + extern void dep3(void); +#ifdef _WIN32 +__declspec(dllimport) +#endif + extern void dep4(void); + +int main(void) +{ + dep3(); + dep4(); + return 0; +} diff --git a/Tests/RunCMake/install/RunCMakeTest.cmake b/Tests/RunCMake/install/RunCMakeTest.cmake index e9a29c4..f79a3ea 100644 --- a/Tests/RunCMake/install/RunCMakeTest.cmake +++ b/Tests/RunCMake/install/RunCMakeTest.cmake @@ -186,6 +186,7 @@ if(CMAKE_SYSTEM_NAME MATCHES "^(Linux|Darwin|Windows)$") run_cmake(TARGETS-RUNTIME_DEPENDENCIES-cross) unset(RunCMake_TEST_OPTIONS) run_cmake(TARGETS-RUNTIME_DEPENDENCY_SET-RUNTIME_DEPENDENCIES-conflict) + run_cmake(RuntimeDependencies-COMPONENTS) else() run_cmake(TARGETS-RUNTIME_DEPENDENCIES-unsupported) run_cmake(TARGETS-RUNTIME_DEPENDENCY_SET-unsupported) diff --git a/Tests/RunCMake/install/RuntimeDependencies-COMPONENTS.cmake b/Tests/RunCMake/install/RuntimeDependencies-COMPONENTS.cmake new file mode 100644 index 0000000..4727de3 --- /dev/null +++ b/Tests/RunCMake/install/RuntimeDependencies-COMPONENTS.cmake @@ -0,0 +1,39 @@ +enable_language(C) + +function(check_components value) + get_cmake_property(comp COMPONENTS) + if(NOT comp STREQUAL value) + message(FATAL_ERROR "Expected value of COMPONENTS:\n ${value}\nActual value of COMPONENTS:\n ${comp}") + endif() +endfunction() + +if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + add_library(tgt MODULE obj1.c) +else() + add_executable(tgt main.c) +endif() + +install(TARGETS tgt + RUNTIME_DEPENDENCIES + RUNTIME DESTINATION bin COMPONENT bin1 + LIBRARY DESTINATION lib COMPONENT lib1 + FRAMEWORK DESTINATION fw COMPONENT fw1 + ) +if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + check_components("bin1;fw1;lib1") +else() + check_components("bin1;lib1") +endif() + +install(RUNTIME_DEPENDENCY_SET deps + RUNTIME DESTINATION bin COMPONENT bin2 + LIBRARY DESTINATION lib COMPONENT lib2 + FRAMEWORK DESTINATION fw COMPONENT fw2 + ) +if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + check_components("bin1;fw1;fw2;lib1;lib2") +elseif(CMAKE_SYSTEM_NAME STREQUAL "Windows") + check_components("bin1;bin2;lib1") +elseif() + check_components("bin1;lib1;lib2") +endif() -- cgit v0.12 From 72f2448e82d8b3e90fa733cb6c631298083ae06b Mon Sep 17 00:00:00 2001 From: Kyle Edwards Date: Wed, 19 May 2021 15:19:38 -0400 Subject: Help: Add documentation for runtime dependency installation --- Help/command/install.rst | 108 ++++++++++++++++++++++ Help/manual/cmake-generator-expressions.7.rst | 5 +- Help/release/dev/install-runtime-dependencies.rst | 9 ++ 3 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 Help/release/dev/install-runtime-dependencies.rst diff --git a/Help/command/install.rst b/Help/command/install.rst index 4d41fb3..acfaa48 100644 --- a/Help/command/install.rst +++ b/Help/command/install.rst @@ -15,6 +15,7 @@ Synopsis install(`SCRIPT`_ [...]) install(`CODE`_ [...]) install(`EXPORT`_ [...]) + install(`RUNTIME_DEPENDENCY_SET`_ [...]) Introduction ^^^^^^^^^^^^ @@ -125,6 +126,7 @@ Installing Targets .. code-block:: cmake install(TARGETS targets... [EXPORT ] + [RUNTIME_DEPENDENCIES args...|RUNTIME_DEPENDENCY_SET ] [[ARCHIVE|LIBRARY|RUNTIME|OBJECTS|FRAMEWORK|BUNDLE| PRIVATE_HEADER|PUBLIC_HEADER|RESOURCE] [DESTINATION ] @@ -339,6 +341,34 @@ top level: relative path is specified, it is treated as relative to the ``$``. +``RUNTIME_DEPENDENCY_SET`` + .. versionadded:: 3.21 + + This option causes all runtime dependencies of installed executable, shared + library, and module targets to be added to the specified runtime dependency + set. This set can then be installed later on with an + `install(RUNTIME_DEPENDENCY_SET)`_ command. + + This argument and the ``RUNTIME_DEPENDENCIES`` argument are mutually + exclusive. + +``RUNTIME_DEPENDENCIES`` + .. versionadded:: 3.21 + + This option causes all runtime dependencies of installed executable, shared + library, and module targets to be installed along with the targets + themselves. The ``RUNTIME``, ``LIBRARY``, ``FRAMEWORK``, and generic + arguments are used to determine the properties (``DESTINATION``, + ``COMPONENT``, etc.) of the installation of these dependencies. + + ``RUNTIME_DEPENDENCIES`` is semantically equivalent to calling + ``install(TARGETS ... RUNTIME_DEPENDENCY_SET)`` and then + `install(RUNTIME_DEPENDENCY_SET)`_ with a randomly generated name. It accepts + all of the same options as `install(RUNTIME_DEPENDENCY_SET)`_. + + This argument and the ``RUNTIME_DEPENDENCY_SET`` argument are mutually + exclusive. + One or more groups of properties may be specified in a single call to the ``TARGETS`` form of this command. A target may be installed more than once to different locations. Consider hypothetical targets ``myExe``, @@ -394,6 +424,7 @@ Installing Imported Runtime Artifacts .. code-block:: cmake install(IMPORTED_RUNTIME_ARTIFACTS targets... + [RUNTIME_DEPENDENCY_SET ] [[LIBRARY|RUNTIME|FRAMEWORK|BUNDLE] [DESTINATION ] [PERMISSIONS permissions...] @@ -415,6 +446,15 @@ not installed. In the case of :prop_tgt:`FRAMEWORK` libraries, :prop_tgt:`MACOSX_BUNDLE` executables, and :prop_tgt:`BUNDLE` CFBundles, the entire directory is installed. +``IMPORTED_RUNTIME_ARTIFACTS`` accepts the following additional arguments: + +``RUNTIME_DEPENDENCY_SET`` + + This option causes all runtime dependencies of installed executable, shared + library, and module targets to be added to the specified runtime dependency + set. This set can then be installed later on with an + `install(RUNTIME_DEPENDENCY_SET)`_ command. + Installing Files ^^^^^^^^^^^^^^^^ @@ -773,6 +813,74 @@ executable from the installation tree using the imported target name :command:`install_files`, and :command:`install_programs` commands is not defined. +Installing Runtime Dependencies +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. _`install(RUNTIME_DEPENDENCY_SET)`: +.. _RUNTIME_DEPENDENCY_SET: + +.. versionadded:: 3.21 + +.. code-block:: cmake + + install(RUNTIME_DEPENDENCY_SET + [[LIBRARY|RUNTIME|FRAMEWORK] + [DESTINATION ] + [PERMISSIONS permissions...] + [CONFIGURATIONS [Debug|Release|...]] + [COMPONENT ] + [NAMELINK_COMPONENT ] + [OPTIONAL] [EXCLUDE_FROM_ALL] + ] [...] + [PRE_INCLUDE_REGEXES regexes...] + [PRE_EXCLUDE_REGEXES regexes...] + [POST_INCLUDE_REGEXES regexes...] + [POST_EXCLUDE_REGEXES regexes...] + [DIRECTORIES directories...] + ) + +Installs a runtime dependency set created by one or more +`install(TARGETS)`_ or `install(IMPORTED_RUNTIME_ARTIFACTS)`_ commands. The +dependencies of targets belonging to a runtime dependency set are installed in +the ``RUNTIME`` destination and component on DLL platforms, and in the +``LIBRARY`` destination and component on non-DLL platforms. macOS frameworks +are installed in the ``FRAMEWORK`` destination and component. The generated +install script calls :command:`file(GET_RUNTIME_DEPENDENCIES)` on the +build-tree files to calculate the runtime dependencies, with the build-tree +executable files as the ``EXECUTABLES`` argument, the build-tree shared +libraries as the ``LIBRARIES`` argument, and the build-tree modules as the +``MODULES`` argument. If one of the executables is a :prop_tgt:`MACOSX_BUNDLE` +executable on a macOS platform, that executable is passed as the +``BUNDLE_EXECUTABLE`` argument. If ``RUNTIME_DEPENDENCY_SET`` is specified on +a macOS platform, at most one :prop_tgt:`MACOSX_BUNDLE` executable may be in +the runtime dependency set. The :prop_tgt:`MACOSX_BUNDLE` property has no +effect on non-macOS platforms. Targets built within the build tree will never +be installed as runtime dependencies, nor will their own dependencies, unless +the targets themselves are installed with `install(TARGETS)`_. + +This argument accepts the following sub-arguments: + +``DIRECTORIES `` + List of directories to be passed as the ``DIRECTORIES`` argument of + :command:`file(GET_RUNTIME_DEPENDENCIES)`. This argument supports + :manual:`generator expressions `. If a + ``DIRECTORIES`` argument evaluates to an empty string, it is not passed to + :command:`file(GET_RUNTIME_DEPENDENCIES)`. + +``PRE_INCLUDE_REGEXES ``, ``PRE_EXCLUDE_REGEXES ``, ``POST_INCLUDE_REGEXES ``, ``POST_EXCLUDE_REGEXES `` + List of regular expressions to be passed as their respective arguments to + :command:`file(GET_RUNTIME_DEPENDENCIES)`. These arguments support + :manual:`generator expressions `. If an + argument evaluates to an empty string, it is not passed to + :command:`file(GET_RUNTIME_DEPENDENCIES)`. + +``POST_INCLUDE_FILES ``, ``POST_EXCLUDE_FILES `` + List of files to be passed as their respective arguments to + :command:`file(GET_RUNTIME_DEPENDENCIES)`. These arguments support + :manual:`generator expressions `. If an + argument evaluates to an empty string, it is not passed to + :command:`file(GET_RUNTIME_DEPENDENCIES)`. + Generated Installation Script ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/Help/manual/cmake-generator-expressions.7.rst b/Help/manual/cmake-generator-expressions.7.rst index 775067a..0ba41c8 100644 --- a/Help/manual/cmake-generator-expressions.7.rst +++ b/Help/manual/cmake-generator-expressions.7.rst @@ -966,8 +966,9 @@ which is just the string ``tgt``. .. genex:: $ Content of the install prefix when the target is exported via - :command:`install(EXPORT)`, or when evaluated in - :prop_tgt:`INSTALL_NAME_DIR`, and empty otherwise. + :command:`install(EXPORT)`, or when evaluated in the + :prop_tgt:`INSTALL_NAME_DIR` property or the ``INSTALL_NAME_DIR`` argument of + :command:`install(RUNTIME_DEPENDENCY_SET)`, and empty otherwise. Output-Related Expressions -------------------------- diff --git a/Help/release/dev/install-runtime-dependencies.rst b/Help/release/dev/install-runtime-dependencies.rst new file mode 100644 index 0000000..58c92e6 --- /dev/null +++ b/Help/release/dev/install-runtime-dependencies.rst @@ -0,0 +1,9 @@ +install-runtime-dependencies +---------------------------- + +* The :command:`install(TARGETS)` command gained new ``RUNTIME_DEPENDENCIES`` + and ``RUNTIME_DEPENDENCY_SET`` arguments, which can be used to install + runtime dependencies using :command:`file(GET_RUNTIME_DEPENDENCIES)`. +* The :command:`install` command gained a new ``RUNTIME_DEPENDENCY_SET`` mode, + which can be used to install runtime dependencies using + :command:`file(GET_RUNTIME_DEPENDENCIES)`. -- cgit v0.12 From 8d898cb3e10d6ad44bbe542b7b219a0b788b2a0d Mon Sep 17 00:00:00 2001 From: Kyle Edwards Date: Wed, 2 Jun 2021 14:55:43 -0400 Subject: FileAPI: Add integration for runtime dependency installers --- Help/manual/cmake-file-api.7.rst | 24 +++ Source/cmFileAPICodemodel.cxx | 28 +++- Tests/RunCMake/FileAPI/codemodel-v2-check.py | 16 ++ .../FileAPI/codemodel-v2-data/directories/cxx.json | 163 ++++++++++++++++++++- .../FileAPI/codemodel-v2-data/targets/cxx_exe.json | 17 +++ Tests/RunCMake/FileAPI/cxx/CMakeLists.txt | 15 ++ 6 files changed, 259 insertions(+), 4 deletions(-) diff --git a/Help/manual/cmake-file-api.7.rst b/Help/manual/cmake-file-api.7.rst index e7d78c3..5e22ea9 100644 --- a/Help/manual/cmake-file-api.7.rst +++ b/Help/manual/cmake-file-api.7.rst @@ -752,6 +752,12 @@ with members: The ``destination`` member is populated. The ``isOptional`` member may exist. This type has no additional members. + ``runtimeDependencySet`` + An :command:`install(RUNTIME_DEPENDENCY_SET)` call or an + :command:`install(TARGETS)` call with ``RUNTIME_DEPENDENCIES``. The + ``destination`` member is populated. This type has additional members + ``runtimeDependencySetName`` and ``runtimeDependencySetType``. + ``isExcludeFromAll`` Optional member that is present with boolean value ``true`` when :command:`install` is called with the ``EXCLUDE_FROM_ALL`` option. @@ -811,6 +817,24 @@ with members: An unsigned integer 0-based index into the main "codemodel" object's ``targets`` array for the target. + ``runtimeDependencySetName`` + Optional member that is present when ``type`` is ``runtimeDependencySet`` + and the installer was created by an + :command:`install(RUNTIME_DEPENDENCY_SET)` call. The value is a string + specifying the name of the runtime dependency set that was installed. + + ``runtimeDependencySetType`` + Optional member that is present when ``type`` is ``runtimeDependencySet``. + The value is a string with one of the following values: + + ``library`` + Indicates that this installer installs dependencies that are not macOS + frameworks. + + ``framework`` + Indicates that this installer installs dependencies that are macOS + frameworks. + ``scriptFile`` Optional member that is present when ``type`` is ``script``. The value is a string specifying the path to the script file on disk, diff --git a/Source/cmFileAPICodemodel.cxx b/Source/cmFileAPICodemodel.cxx index ff11f4a..2c15c25 100644 --- a/Source/cmFileAPICodemodel.cxx +++ b/Source/cmFileAPICodemodel.cxx @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -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" @@ -876,8 +880,10 @@ Json::Value DirectoryObject::DumpInstaller(cmInstallGenerator* gen) { Json::Value installer = Json::objectValue; - // Exclude subdirectory installers. They are implementation details. - if (dynamic_cast(gen)) { + // Exclude subdirectory installers and file(GET_RUNTIME_DEPENDENCIES) + // installers. They are implementation details. + if (dynamic_cast(gen) || + dynamic_cast(gen)) { return installer; } @@ -1019,6 +1025,24 @@ Json::Value DirectoryObject::DumpInstaller(cmInstallGenerator* gen) if (installImportedRuntimeArtifacts->GetOptional()) { installer["isOptional"] = true; } + } else if (auto* installRuntimeDependencySet = + dynamic_cast(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/Tests/RunCMake/FileAPI/codemodel-v2-check.py b/Tests/RunCMake/FileAPI/codemodel-v2-check.py index 9911ad5..6cf57a3 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-check.py +++ b/Tests/RunCMake/FileAPI/codemodel-v2-check.py @@ -180,6 +180,14 @@ def check_directory(c): expected_keys.append("scriptFile") assert is_string(a["scriptFile"], e["scriptFile"]) + if e.get("runtimeDependencySetName", None) is not None: + expected_keys.append("runtimeDependencySetName") + assert is_string(a["runtimeDependencySetName"], e["runtimeDependencySetName"]) + + if e.get("runtimeDependencySetType", None) is not None: + expected_keys.append("runtimeDependencySetType") + assert is_string(a["runtimeDependencySetType"], e["runtimeDependencySetType"]) + if e["backtrace"] is not None: expected_keys.append("backtrace") check_backtrace(d, a["backtrace"], e["backtrace"]) @@ -650,6 +658,14 @@ def gen_check_directories(c, g): if "pathsNamelink" in i: i["paths"] = i["pathsNamelink"] + if sys.platform not in ("win32", "darwin") and "linux" not in sys.platform: + for e in expected: + e["installers"] = list(filter(lambda i: i["type"] != "runtimeDependencySet", e["installers"])) + + if sys.platform != "darwin": + for e in expected: + e["installers"] = list(filter(lambda i: i.get("runtimeDependencySetType", None) != "framework", e["installers"])) + return expected def check_directories(c, g): diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/cxx.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/cxx.json index 7168306..8052c1a 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/cxx.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/directories/cxx.json @@ -17,6 +17,165 @@ ], "projectName": "Cxx", "minimumCMakeVersion": "3.12", - "hasInstallRule": null, - "installers": [] + "hasInstallRule": true, + "installers": [ + { + "component": "Unspecified", + "type": "target", + "destination": "lib", + "paths": [ + "^cxx/((Debug|Release|MinSizeRel|RelWithDebInfo)/)?cxx_exe(\\.exe)?$" + ], + "isExcludeFromAll": null, + "isForAllComponents": null, + "isOptional": null, + "targetId": "^cxx_exe::@a56b12a3f5c0529fb296$", + "targetIndex": "cxx_exe", + "targetIsImportLibrary": null, + "targetInstallNamelink": null, + "exportName": null, + "exportTargets": null, + "scriptFile": null, + "backtrace": [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 38, + "command": "install", + "hasParent": true + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] + }, + { + "component": "Unspecified", + "type": "runtimeDependencySet", + "destination": "lib", + "paths": null, + "isExcludeFromAll": null, + "isForAllComponents": null, + "isOptional": null, + "targetId": null, + "targetIndex": null, + "targetIsImportLibrary": null, + "targetInstallNamelink": null, + "exportName": null, + "exportTargets": null, + "scriptFile": null, + "runtimeDependencySetType": "library", + "backtrace": [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 38, + "command": "install", + "hasParent": true + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] + }, + { + "component": "Unspecified", + "type": "runtimeDependencySet", + "destination": "fw", + "paths": null, + "isExcludeFromAll": null, + "isForAllComponents": null, + "isOptional": null, + "targetId": null, + "targetIndex": null, + "targetIsImportLibrary": null, + "targetInstallNamelink": null, + "exportName": null, + "exportTargets": null, + "scriptFile": null, + "runtimeDependencySetType": "framework", + "backtrace": [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 38, + "command": "install", + "hasParent": true + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] + }, + { + "component": "Unspecified", + "type": "runtimeDependencySet", + "destination": "lib", + "paths": null, + "isExcludeFromAll": null, + "isForAllComponents": null, + "isOptional": null, + "targetId": null, + "targetIndex": null, + "targetIsImportLibrary": null, + "targetInstallNamelink": null, + "exportName": null, + "exportTargets": null, + "scriptFile": null, + "runtimeDependencySetType": "library", + "runtimeDependencySetName": "deps", + "backtrace": [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 43, + "command": "install", + "hasParent": true + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] + }, + { + "component": "Unspecified", + "type": "runtimeDependencySet", + "destination": "fw", + "paths": null, + "isExcludeFromAll": null, + "isForAllComponents": null, + "isOptional": null, + "targetId": null, + "targetIndex": null, + "targetIsImportLibrary": null, + "targetInstallNamelink": null, + "exportName": null, + "exportTargets": null, + "scriptFile": null, + "runtimeDependencySetType": "framework", + "runtimeDependencySetName": "deps", + "backtrace": [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 43, + "command": "install", + "hasParent": true + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] + } + ] } diff --git a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe.json b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe.json index c9e652b..385fa62 100644 --- a/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe.json +++ b/Tests/RunCMake/FileAPI/codemodel-v2-data/targets/cxx_exe.json @@ -115,6 +115,23 @@ "prefix": "^(/usr/local|[A-Za-z]:.*/codemodel-v2)$", "destinations": [ { + "path": "lib", + "backtrace": [ + { + "file": "^cxx/CMakeLists\\.txt$", + "line": 38, + "command": "install", + "hasParent": true + }, + { + "file": "^cxx/CMakeLists\\.txt$", + "line": null, + "command": null, + "hasParent": false + } + ] + }, + { "path": "bin", "backtrace": [ { diff --git a/Tests/RunCMake/FileAPI/cxx/CMakeLists.txt b/Tests/RunCMake/FileAPI/cxx/CMakeLists.txt index 95c803f..3ae3b60 100644 --- a/Tests/RunCMake/FileAPI/cxx/CMakeLists.txt +++ b/Tests/RunCMake/FileAPI/cxx/CMakeLists.txt @@ -30,3 +30,18 @@ if(CMAKE_CXX_STANDARD_DEFAULT AND DEFINED CMAKE_CXX11_STANDARD_COMPILE_OPTION) target_compile_features(cxx_standard_compile_feature_exe PRIVATE cxx_decltype) file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/cxx_std_11.txt" "") endif() + +set(_rdeps) +if(CMAKE_SYSTEM_NAME MATCHES "^(Linux|Windows|Darwin)$") + set(_rdeps RUNTIME_DEPENDENCIES) +endif() +install(TARGETS cxx_exe ${_rdeps} + DESTINATION lib + FRAMEWORK DESTINATION fw + ) +if(_rdeps) + install(RUNTIME_DEPENDENCY_SET deps + DESTINATION lib + FRAMEWORK DESTINATION fw + ) +endif() -- cgit v0.12