summaryrefslogtreecommitdiffstats
path: root/Source
diff options
context:
space:
mode:
authorKyle Edwards <kyle.edwards@kitware.com>2019-03-14 15:58:34 (GMT)
committerKyle Edwards <kyle.edwards@kitware.com>2019-06-10 18:35:28 (GMT)
commit1f9ef25130caa89969cc69b4873edf1723533a5f (patch)
treef48bf3864cc3c6ccd87e6efffcba2c1f582ac0e0 /Source
parentf36719111de99e5be426552e9aaba96518b888b7 (diff)
downloadCMake-1f9ef25130caa89969cc69b4873edf1723533a5f.zip
CMake-1f9ef25130caa89969cc69b4873edf1723533a5f.tar.gz
CMake-1f9ef25130caa89969cc69b4873edf1723533a5f.tar.bz2
file: Add GET_RUNTIME_DEPENDENCIES mode
Co-Authored-by: Bryon Bean <bryon.bean@kitware.com>
Diffstat (limited to 'Source')
-rw-r--r--Source/CMakeLists.txt28
-rw-r--r--Source/cmBinUtilsLinker.cxx15
-rw-r--r--Source/cmBinUtilsLinker.h30
-rw-r--r--Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.cxx18
-rw-r--r--Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h30
-rw-r--r--Source/cmBinUtilsLinuxELFLinker.cxx177
-rw-r--r--Source/cmBinUtilsLinuxELFLinker.h44
-rw-r--r--Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.cxx84
-rw-r--r--Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h26
-rw-r--r--Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.cxx19
-rw-r--r--Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h29
-rw-r--r--Source/cmBinUtilsMacOSMachOLinker.cxx228
-rw-r--r--Source/cmBinUtilsMacOSMachOLinker.h59
-rw-r--r--Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx100
-rw-r--r--Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h25
-rw-r--r--Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.cxx67
-rw-r--r--Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h25
-rw-r--r--Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.cxx18
-rw-r--r--Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h28
-rw-r--r--Source/cmBinUtilsWindowsPELinker.cxx121
-rw-r--r--Source/cmBinUtilsWindowsPELinker.h33
-rw-r--r--Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.cxx67
-rw-r--r--Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h25
-rw-r--r--Source/cmFileCommand.cxx175
-rw-r--r--Source/cmFileCommand.h2
-rw-r--r--Source/cmLDConfigLDConfigTool.cxx70
-rw-r--r--Source/cmLDConfigLDConfigTool.h22
-rw-r--r--Source/cmLDConfigTool.cxx9
-rw-r--r--Source/cmLDConfigTool.h24
-rw-r--r--Source/cmRuntimeDependencyArchive.cxx378
-rw-r--r--Source/cmRuntimeDependencyArchive.h70
31 files changed, 2046 insertions, 0 deletions
diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 695e075..af3a283 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -146,6 +146,28 @@ set(SRCS
cmArgumentParser.cxx
cmArgumentParser.h
cmBase32.cxx
+ cmBinUtilsLinker.cxx
+ cmBinUtilsLinker.h
+ cmBinUtilsLinuxELFGetRuntimeDependenciesTool.cxx
+ cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h
+ cmBinUtilsLinuxELFLinker.cxx
+ cmBinUtilsLinuxELFLinker.h
+ cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.cxx
+ cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h
+ cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.cxx
+ cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h
+ cmBinUtilsMacOSMachOLinker.cxx
+ cmBinUtilsMacOSMachOLinker.h
+ cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx
+ cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h
+ cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.cxx
+ cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h
+ cmBinUtilsWindowsPEGetRuntimeDependenciesTool.cxx
+ cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h
+ cmBinUtilsWindowsPELinker.cxx
+ cmBinUtilsWindowsPELinker.h
+ cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.cxx
+ cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h
cmCacheManager.cxx
cmCacheManager.h
cmCLocaleEnvironmentScope.h
@@ -295,6 +317,10 @@ set(SRCS
cmInstallTargetGenerator.cxx
cmInstallDirectoryGenerator.h
cmInstallDirectoryGenerator.cxx
+ cmLDConfigLDConfigTool.cxx
+ cmLDConfigLDConfigTool.h
+ cmLDConfigTool.cxx
+ cmLDConfigTool.h
cmLinkedTree.h
cmLinkItem.cxx
cmLinkItem.h
@@ -360,6 +386,8 @@ set(SRCS
cmQtAutoRcc.h
cmRST.cxx
cmRST.h
+ cmRuntimeDependencyArchive.cxx
+ cmRuntimeDependencyArchive.h
cmScriptGenerator.h
cmScriptGenerator.cxx
cmSourceFile.cxx
diff --git a/Source/cmBinUtilsLinker.cxx b/Source/cmBinUtilsLinker.cxx
new file mode 100644
index 0000000..3dac85c
--- /dev/null
+++ b/Source/cmBinUtilsLinker.cxx
@@ -0,0 +1,15 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#include "cmBinUtilsLinker.h"
+#include "cmRuntimeDependencyArchive.h"
+
+cmBinUtilsLinker::cmBinUtilsLinker(cmRuntimeDependencyArchive* archive)
+ : Archive(archive)
+{
+}
+
+void cmBinUtilsLinker::SetError(const std::string& e)
+{
+ this->Archive->SetError(e);
+}
diff --git a/Source/cmBinUtilsLinker.h b/Source/cmBinUtilsLinker.h
new file mode 100644
index 0000000..29853a5
--- /dev/null
+++ b/Source/cmBinUtilsLinker.h
@@ -0,0 +1,30 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#ifndef cmBinUtilsLinker_h
+#define cmBinUtilsLinker_h
+
+#include "cmStateTypes.h"
+
+#include <string>
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsLinker
+{
+public:
+ cmBinUtilsLinker(cmRuntimeDependencyArchive* archive);
+ virtual ~cmBinUtilsLinker() = default;
+
+ virtual bool Prepare() { return true; }
+
+ virtual bool ScanDependencies(std::string const& file,
+ cmStateEnums::TargetType type) = 0;
+
+protected:
+ cmRuntimeDependencyArchive* Archive;
+
+ void SetError(const std::string& e);
+};
+
+#endif // cmBinUtilsLinker_h
diff --git a/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.cxx
new file mode 100644
index 0000000..40de592
--- /dev/null
+++ b/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.cxx
@@ -0,0 +1,18 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#include "cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h"
+#include "cmRuntimeDependencyArchive.h"
+
+cmBinUtilsLinuxELFGetRuntimeDependenciesTool::
+ cmBinUtilsLinuxELFGetRuntimeDependenciesTool(
+ cmRuntimeDependencyArchive* archive)
+ : Archive(archive)
+{
+}
+
+void cmBinUtilsLinuxELFGetRuntimeDependenciesTool::SetError(
+ const std::string& error)
+{
+ this->Archive->SetError(error);
+}
diff --git a/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h b/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h
new file mode 100644
index 0000000..d514e7f
--- /dev/null
+++ b/Source/cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h
@@ -0,0 +1,30 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#ifndef cmBinUtilsLinuxELFGetRuntimeDependenciesTool_h
+#define cmBinUtilsLinuxELFGetRuntimeDependenciesTool_h
+
+#include <string>
+#include <vector>
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsLinuxELFGetRuntimeDependenciesTool
+{
+public:
+ cmBinUtilsLinuxELFGetRuntimeDependenciesTool(
+ cmRuntimeDependencyArchive* archive);
+ virtual ~cmBinUtilsLinuxELFGetRuntimeDependenciesTool() = default;
+
+ virtual bool GetFileInfo(std::string const& file,
+ std::vector<std::string>& needed,
+ std::vector<std::string>& rpaths,
+ std::vector<std::string>& runpaths) = 0;
+
+protected:
+ cmRuntimeDependencyArchive* Archive;
+
+ void SetError(const std::string& e);
+};
+
+#endif // cmBinUtilsLinuxELFGetRuntimeDependenciesTool_h
diff --git a/Source/cmBinUtilsLinuxELFLinker.cxx b/Source/cmBinUtilsLinuxELFLinker.cxx
new file mode 100644
index 0000000..4fb15f2
--- /dev/null
+++ b/Source/cmBinUtilsLinuxELFLinker.cxx
@@ -0,0 +1,177 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#include "cmBinUtilsLinuxELFLinker.h"
+#include "cmAlgorithms.h"
+#include "cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h"
+#include "cmLDConfigLDConfigTool.h"
+#include "cmMakefile.h"
+#include "cmMessageType.h"
+#include "cmRuntimeDependencyArchive.h"
+#include "cmSystemTools.h"
+
+#include <cmsys/RegularExpression.hxx>
+
+#include <memory>
+#include <sstream>
+
+static std::string ReplaceOrigin(const std::string& rpath,
+ const std::string& origin)
+{
+ static const cmsys::RegularExpression originRegex(
+ "(\\$ORIGIN)([^a-zA-Z0-9_]|$)");
+ static const cmsys::RegularExpression originCurlyRegex("\\${ORIGIN}");
+
+ cmsys::RegularExpressionMatch match;
+ if (originRegex.find(rpath.c_str(), match)) {
+ std::string begin = rpath.substr(0, match.start(1));
+ std::string end = rpath.substr(match.end(1));
+ return begin + origin + end;
+ }
+ if (originCurlyRegex.find(rpath.c_str(), match)) {
+ std::string begin = rpath.substr(0, match.start());
+ std::string end = rpath.substr(match.end());
+ return begin + origin + end;
+ }
+ return rpath;
+}
+
+cmBinUtilsLinuxELFLinker::cmBinUtilsLinuxELFLinker(
+ cmRuntimeDependencyArchive* archive)
+ : cmBinUtilsLinker(archive)
+{
+}
+
+bool cmBinUtilsLinuxELFLinker::Prepare()
+{
+ std::string tool = this->Archive->GetGetRuntimeDependenciesTool();
+ if (tool.empty()) {
+ tool = "objdump";
+ }
+ if (tool == "objdump") {
+ this->Tool =
+ cm::make_unique<cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool>(
+ this->Archive);
+ } else {
+ std::ostringstream e;
+ e << "Invalid value for CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL: " << tool;
+ this->SetError(e.str());
+ return false;
+ }
+
+ std::string ldConfigTool =
+ this->Archive->GetMakefile()->GetSafeDefinition("CMAKE_LDCONFIG_TOOL");
+ if (ldConfigTool.empty()) {
+ ldConfigTool = "ldconfig";
+ }
+ if (ldConfigTool == "ldconfig") {
+ this->LDConfigTool =
+ cm::make_unique<cmLDConfigLDConfigTool>(this->Archive);
+ } else {
+ std::ostringstream e;
+ e << "Invalid value for CMAKE_LDCONFIG_TOOL: " << ldConfigTool;
+ this->SetError(e.str());
+ return false;
+ }
+
+ return true;
+}
+
+bool cmBinUtilsLinuxELFLinker::ScanDependencies(
+ std::string const& file, cmStateEnums::TargetType /* unused */)
+{
+ std::vector<std::string> parentRpaths;
+ return this->ScanDependencies(file, parentRpaths);
+}
+
+bool cmBinUtilsLinuxELFLinker::ScanDependencies(
+ std::string const& file, std::vector<std::string> const& parentRpaths)
+{
+ std::string origin = cmSystemTools::GetFilenamePath(file);
+ std::vector<std::string> needed;
+ std::vector<std::string> rpaths;
+ std::vector<std::string> runpaths;
+ if (!this->Tool->GetFileInfo(file, needed, rpaths, runpaths)) {
+ return false;
+ }
+ for (auto& runpath : runpaths) {
+ runpath = ReplaceOrigin(runpath, origin);
+ }
+ for (auto& rpath : rpaths) {
+ rpath = ReplaceOrigin(rpath, origin);
+ }
+
+ std::vector<std::string> searchPaths;
+ if (!runpaths.empty()) {
+ searchPaths = runpaths;
+ } else {
+ searchPaths = rpaths;
+ searchPaths.insert(searchPaths.end(), parentRpaths.begin(),
+ parentRpaths.end());
+ }
+
+ std::vector<std::string> ldConfigPaths;
+ if (!this->LDConfigTool->GetLDConfigPaths(ldConfigPaths)) {
+ return false;
+ }
+ searchPaths.insert(searchPaths.end(), ldConfigPaths.begin(),
+ ldConfigPaths.end());
+
+ for (auto const& dep : needed) {
+ if (!this->Archive->IsPreExcluded(dep)) {
+ std::string path;
+ bool resolved = false;
+ if (dep.find('/') != std::string::npos) {
+ this->SetError("Paths to dependencies are not supported");
+ return false;
+ }
+ if (!this->ResolveDependency(dep, searchPaths, path, resolved)) {
+ return false;
+ }
+ if (resolved) {
+ if (!this->Archive->IsPostExcluded(path)) {
+ bool unique;
+ this->Archive->AddResolvedPath(dep, path, unique);
+ if (unique && !this->ScanDependencies(path, rpaths)) {
+ return false;
+ }
+ }
+ } else {
+ this->Archive->AddUnresolvedPath(dep);
+ }
+ }
+ }
+
+ return true;
+}
+
+bool cmBinUtilsLinuxELFLinker::ResolveDependency(
+ std::string const& name, std::vector<std::string> const& searchPaths,
+ std::string& path, bool& resolved)
+{
+ for (auto const& searchPath : searchPaths) {
+ path = searchPath + '/' + name;
+ if (cmSystemTools::PathExists(path)) {
+ resolved = true;
+ return true;
+ }
+ }
+
+ for (auto const& searchPath : this->Archive->GetSearchDirectories()) {
+ path = searchPath + '/' + name;
+ if (cmSystemTools::PathExists(path)) {
+ std::ostringstream warning;
+ warning << "Dependency " << name << " found in search directory:\n "
+ << searchPath
+ << "\nSee file(GET_RUNTIME_DEPENDENCIES) documentation for "
+ << "more information.";
+ this->Archive->GetMakefile()->IssueMessage(MessageType::WARNING,
+ warning.str());
+ resolved = true;
+ return true;
+ }
+ }
+
+ resolved = false;
+ return true;
+}
diff --git a/Source/cmBinUtilsLinuxELFLinker.h b/Source/cmBinUtilsLinuxELFLinker.h
new file mode 100644
index 0000000..348edc4
--- /dev/null
+++ b/Source/cmBinUtilsLinuxELFLinker.h
@@ -0,0 +1,44 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#ifndef cmBinUtilsLinuxELFLinker_h
+#define cmBinUtilsLinuxELFLinker_h
+
+#include "cmBinUtilsLinker.h"
+#include "cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h"
+#include "cmLDConfigTool.h"
+#include "cmStateTypes.h"
+
+#include <memory> // IWYU pragma: keep
+#include <string>
+#include <vector>
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsLinuxELFLinker : public cmBinUtilsLinker
+{
+public:
+ cmBinUtilsLinuxELFLinker(cmRuntimeDependencyArchive* archive);
+
+ bool Prepare() override;
+
+ bool ScanDependencies(std::string const& file,
+ cmStateEnums::TargetType type) override;
+
+private:
+ std::unique_ptr<cmBinUtilsLinuxELFGetRuntimeDependenciesTool> Tool;
+ std::unique_ptr<cmLDConfigTool> LDConfigTool;
+ bool HaveLDConfigPaths = false;
+ std::vector<std::string> LDConfigPaths;
+
+ bool ScanDependencies(std::string const& file,
+ std::vector<std::string> const& parentRpaths);
+
+ bool ResolveDependency(std::string const& name,
+ std::vector<std::string> const& searchPaths,
+ std::string& path, bool& resolved);
+
+ bool GetLDConfigPaths();
+};
+
+#endif // cmBinUtilsLinuxELFLinker_h
diff --git a/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.cxx
new file mode 100644
index 0000000..3bf7bf8
--- /dev/null
+++ b/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.cxx
@@ -0,0 +1,84 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#include "cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h"
+#include "cmRuntimeDependencyArchive.h"
+#include "cmSystemTools.h"
+#include "cmUVProcessChain.h"
+
+#include <cmsys/RegularExpression.hxx>
+
+#include <sstream>
+
+cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool::
+ cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool(
+ cmRuntimeDependencyArchive* archive)
+ : cmBinUtilsLinuxELFGetRuntimeDependenciesTool(archive)
+{
+}
+
+bool cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool::GetFileInfo(
+ std::string const& file, std::vector<std::string>& needed,
+ std::vector<std::string>& rpaths, std::vector<std::string>& runpaths)
+{
+ cmUVProcessChainBuilder builder;
+ builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT);
+
+ std::vector<std::string> command;
+ if (!this->Archive->GetGetRuntimeDependenciesCommand("objdump", command)) {
+ this->SetError("Could not find objdump");
+ return false;
+ }
+ command.emplace_back("-p");
+ command.push_back(file);
+ builder.AddCommand(command);
+
+ auto process = builder.Start();
+ if (!process.Valid()) {
+ std::ostringstream e;
+ e << "Failed to start objdump process for:\n " << file;
+ this->SetError(e.str());
+ return false;
+ }
+
+ std::string line;
+ static const cmsys::RegularExpression neededRegex("^ *NEEDED *([^\n]*)$");
+ static const cmsys::RegularExpression rpathRegex("^ *RPATH *([^\n]*)$");
+ static const cmsys::RegularExpression runpathRegex("^ *RUNPATH *([^\n]*)$");
+ while (std::getline(*process.OutputStream(), line)) {
+ cmsys::RegularExpressionMatch match;
+ if (neededRegex.find(line.c_str(), match)) {
+ needed.push_back(match.match(1));
+ } else if (rpathRegex.find(line.c_str(), match)) {
+ std::vector<std::string> rpathSplit =
+ cmSystemTools::SplitString(match.match(1), ':');
+ rpaths.reserve(rpaths.size() + rpathSplit.size());
+ for (auto const& rpath : rpathSplit) {
+ rpaths.push_back(rpath);
+ }
+ } else if (runpathRegex.find(line.c_str(), match)) {
+ std::vector<std::string> runpathSplit =
+ cmSystemTools::SplitString(match.match(1), ':');
+ runpaths.reserve(runpaths.size() + runpathSplit.size());
+ for (auto const& runpath : runpathSplit) {
+ runpaths.push_back(runpath);
+ }
+ }
+ }
+
+ if (!process.Wait()) {
+ std::ostringstream e;
+ e << "Failed to wait on objdump process for:\n " << file;
+ this->SetError(e.str());
+ return false;
+ }
+ auto status = process.GetStatus();
+ if (!status[0] || status[0]->ExitStatus != 0) {
+ std::ostringstream e;
+ e << "Failed to run objdump on:\n " << file;
+ this->SetError(e.str());
+ return false;
+ }
+
+ return true;
+}
diff --git a/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h b/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h
new file mode 100644
index 0000000..286337f
--- /dev/null
+++ b/Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.h
@@ -0,0 +1,26 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#ifndef cmBinUtilsLinuxELFGetRuntimeCollectDependenciesTool_h
+#define cmBinUtilsLinuxELFGetRuntimeCollectDependenciesTool_h
+
+#include "cmBinUtilsLinuxELFGetRuntimeDependenciesTool.h"
+
+#include <string>
+#include <vector>
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool
+ : public cmBinUtilsLinuxELFGetRuntimeDependenciesTool
+{
+public:
+ cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool(
+ cmRuntimeDependencyArchive* archive);
+
+ bool GetFileInfo(std::string const& file, std::vector<std::string>& needed,
+ std::vector<std::string>& rpaths,
+ std::vector<std::string>& runpaths) override;
+};
+
+#endif // cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool_h
diff --git a/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.cxx
new file mode 100644
index 0000000..a296a47
--- /dev/null
+++ b/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.cxx
@@ -0,0 +1,19 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#include "cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h"
+
+#include "cmRuntimeDependencyArchive.h"
+
+cmBinUtilsMacOSMachOGetRuntimeDependenciesTool::
+ cmBinUtilsMacOSMachOGetRuntimeDependenciesTool(
+ cmRuntimeDependencyArchive* archive)
+ : Archive(archive)
+{
+}
+
+void cmBinUtilsMacOSMachOGetRuntimeDependenciesTool::SetError(
+ const std::string& error)
+{
+ this->Archive->SetError(error);
+}
diff --git a/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h b/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h
new file mode 100644
index 0000000..dbb2882
--- /dev/null
+++ b/Source/cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h
@@ -0,0 +1,29 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#ifndef cmBinUtilsMacOSMachOGetRuntimeDependenciesTool_h
+#define cmBinUtilsMacOSMachOGetRuntimeDependenciesTool_h
+
+#include <string>
+#include <vector>
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsMacOSMachOGetRuntimeDependenciesTool
+{
+public:
+ cmBinUtilsMacOSMachOGetRuntimeDependenciesTool(
+ cmRuntimeDependencyArchive* archive);
+ virtual ~cmBinUtilsMacOSMachOGetRuntimeDependenciesTool() = default;
+
+ virtual bool GetFileInfo(std::string const& file,
+ std::vector<std::string>& libs,
+ std::vector<std::string>& rpaths) = 0;
+
+protected:
+ cmRuntimeDependencyArchive* Archive;
+
+ void SetError(const std::string& error);
+};
+
+#endif // cmBinUtilsMacOSMachOGetRuntimeDependenciesTool_h
diff --git a/Source/cmBinUtilsMacOSMachOLinker.cxx b/Source/cmBinUtilsMacOSMachOLinker.cxx
new file mode 100644
index 0000000..e219847
--- /dev/null
+++ b/Source/cmBinUtilsMacOSMachOLinker.cxx
@@ -0,0 +1,228 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#include "cmBinUtilsMacOSMachOLinker.h"
+
+#include "cmAlgorithms.h"
+#include "cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h"
+#include "cmRuntimeDependencyArchive.h"
+#include "cmSystemTools.h"
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+cmBinUtilsMacOSMachOLinker::cmBinUtilsMacOSMachOLinker(
+ cmRuntimeDependencyArchive* archive)
+ : cmBinUtilsLinker(archive)
+{
+}
+
+bool cmBinUtilsMacOSMachOLinker::Prepare()
+{
+ std::string tool = this->Archive->GetGetRuntimeDependenciesTool();
+ if (tool.empty()) {
+ tool = "otool";
+ }
+ if (tool == "otool") {
+ this->Tool =
+ cm::make_unique<cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool>(
+ this->Archive);
+ } else {
+ std::ostringstream e;
+ e << "Invalid value for CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL: " << tool;
+ this->SetError(e.str());
+ return false;
+ }
+
+ return true;
+}
+
+bool cmBinUtilsMacOSMachOLinker::ScanDependencies(
+ std::string const& file, cmStateEnums::TargetType type)
+{
+ std::string executableFile;
+ if (type == cmStateEnums::EXECUTABLE) {
+ executableFile = file;
+ } else {
+ executableFile = this->Archive->GetBundleExecutable();
+ }
+ std::string executablePath;
+ if (!executableFile.empty()) {
+ executablePath = cmSystemTools::GetFilenamePath(executableFile);
+ }
+ return this->ScanDependencies(file, executablePath);
+}
+
+bool cmBinUtilsMacOSMachOLinker::ScanDependencies(
+ std::string const& file, std::string const& executablePath)
+{
+ std::vector<std::string> libs, rpaths;
+ if (!this->Tool->GetFileInfo(file, libs, rpaths)) {
+ return false;
+ }
+
+ std::string loaderPath = cmSystemTools::GetFilenamePath(file);
+ return this->GetFileDependencies(libs, executablePath, loaderPath, rpaths);
+}
+
+bool cmBinUtilsMacOSMachOLinker::GetFileDependencies(
+ std::vector<std::string> const& names, std::string const& executablePath,
+ std::string const& loaderPath, std::vector<std::string> const& rpaths)
+{
+ for (std::string const& name : names) {
+ if (!this->Archive->IsPreExcluded(name)) {
+ std::string path;
+ bool resolved;
+ if (!this->ResolveDependency(name, executablePath, loaderPath, rpaths,
+ path, resolved)) {
+ return false;
+ }
+ if (resolved) {
+ if (!this->Archive->IsPostExcluded(path)) {
+ auto filename = cmSystemTools::GetFilenameName(path);
+ bool unique;
+ this->Archive->AddResolvedPath(filename, path, unique);
+ if (unique && !this->ScanDependencies(path, executablePath)) {
+ return false;
+ }
+ }
+ } else {
+ this->Archive->AddUnresolvedPath(name);
+ }
+ }
+ }
+
+ return true;
+}
+
+bool cmBinUtilsMacOSMachOLinker::ResolveDependency(
+ std::string const& name, std::string const& executablePath,
+ std::string const& loaderPath, std::vector<std::string> const& rpaths,
+ std::string& path, bool& resolved)
+{
+ resolved = false;
+ if (cmHasLiteralPrefix(name, "@rpath/")) {
+ if (!this->ResolveRPathDependency(name, executablePath, loaderPath, rpaths,
+ path, resolved)) {
+ return false;
+ }
+ } else if (cmHasLiteralPrefix(name, "@loader_path/")) {
+ if (!this->ResolveLoaderPathDependency(name, loaderPath, path, resolved)) {
+ return false;
+ }
+ } else if (cmHasLiteralPrefix(name, "@executable_path/")) {
+ if (!this->ResolveExecutablePathDependency(name, executablePath, path,
+ resolved)) {
+ return false;
+ }
+ } else {
+ resolved = true;
+ path = name;
+ }
+
+ if (resolved && !cmSystemTools::FileIsFullPath(path)) {
+ this->SetError("Resolved path is not absolute");
+ return false;
+ }
+
+ return true;
+}
+
+bool cmBinUtilsMacOSMachOLinker::ResolveExecutablePathDependency(
+ std::string const& name, std::string const& executablePath,
+ std::string& path, bool& resolved)
+{
+ if (executablePath.empty()) {
+ resolved = false;
+ return true;
+ }
+
+ // 16 is == "@executable_path".length()
+ path = name;
+ path.replace(0, 16, executablePath);
+
+ if (!cmSystemTools::PathExists(path)) {
+ resolved = false;
+ return true;
+ }
+
+ resolved = true;
+ return true;
+}
+
+bool cmBinUtilsMacOSMachOLinker::ResolveLoaderPathDependency(
+ std::string const& name, std::string const& loaderPath, std::string& path,
+ bool& resolved)
+{
+ if (loaderPath.empty()) {
+ resolved = false;
+ return true;
+ }
+
+ // 12 is "@loader_path".length();
+ path = name;
+ path.replace(0, 12, loaderPath);
+
+ if (!cmSystemTools::PathExists(path)) {
+ resolved = false;
+ return true;
+ }
+
+ resolved = true;
+ return true;
+}
+
+bool cmBinUtilsMacOSMachOLinker::ResolveRPathDependency(
+ std::string const& name, std::string const& executablePath,
+ std::string const& loaderPath, std::vector<std::string> const& rpaths,
+ std::string& path, bool& resolved)
+{
+ for (std::string const& rpath : rpaths) {
+ std::string searchFile = name;
+ searchFile.replace(0, 6, rpath);
+ if (cmHasLiteralPrefix(searchFile, "@loader_path/")) {
+ if (!this->ResolveLoaderPathDependency(searchFile, loaderPath, path,
+ resolved)) {
+ return false;
+ }
+ if (resolved) {
+ return true;
+ }
+ } else if (cmHasLiteralPrefix(searchFile, "@executable_path/")) {
+ if (!this->ResolveExecutablePathDependency(searchFile, executablePath,
+ path, resolved)) {
+ return false;
+ }
+ if (resolved) {
+ return true;
+ }
+ } else if (cmSystemTools::PathExists(searchFile)) {
+ /*
+ * paraphrasing @ben.boeckel:
+ * if /b/libB.dylib is supposed to be used,
+ * /a/libbB.dylib will be found first if it exists. CMake tries to
+ * sort rpath directories to avoid this, but sometimes there is no
+ * right answer.
+ *
+ * I believe it is possible to resolve this using otools -l
+ * then checking the LC_LOAD_DYLIB command whose name is
+ * equal to the value of search_file, UNLESS the build
+ * specifically sets the RPath to paths that will match
+ * duplicate libs; at this point can we just point to
+ * user error, or is there a reason why the advantages
+ * to this scenario outweigh its disadvantages?
+ *
+ * Also priority seems to be the order as passed in when compiled
+ * so as long as this method's resolution guarantees priority
+ * in that manner further checking should not be necessary?
+ */
+ path = searchFile;
+ resolved = true;
+ return true;
+ }
+ }
+
+ resolved = false;
+ return true;
+}
diff --git a/Source/cmBinUtilsMacOSMachOLinker.h b/Source/cmBinUtilsMacOSMachOLinker.h
new file mode 100644
index 0000000..0350d1e
--- /dev/null
+++ b/Source/cmBinUtilsMacOSMachOLinker.h
@@ -0,0 +1,59 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#ifndef cmBinUtilsMacOSMachOLinker_h
+#define cmBinUtilsMacOSMachOLinker_h
+
+#include "cmBinUtilsLinker.h"
+#include "cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h"
+#include "cmStateTypes.h"
+
+#include <memory> // IWYU pragma: keep
+#include <string>
+#include <vector>
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsMacOSMachOLinker : public cmBinUtilsLinker
+{
+public:
+ cmBinUtilsMacOSMachOLinker(cmRuntimeDependencyArchive* archive);
+
+ bool Prepare() override;
+
+ bool ScanDependencies(std::string const& file,
+ cmStateEnums::TargetType type) override;
+
+private:
+ std::unique_ptr<cmBinUtilsMacOSMachOGetRuntimeDependenciesTool> Tool;
+
+ bool ScanDependencies(std::string const& file,
+ std::string const& executablePath);
+
+ bool GetFileDependencies(std::vector<std::string> const& names,
+ std::string const& executablePath,
+ std::string const& loaderPath,
+ std::vector<std::string> const& rpaths);
+
+ bool ResolveDependency(std::string const& name,
+ std::string const& executablePath,
+ std::string const& loaderPath,
+ std::vector<std::string> const& rpaths,
+ std::string& path, bool& resolved);
+
+ bool ResolveExecutablePathDependency(std::string const& name,
+ std::string const& executablePath,
+ std::string& path, bool& resolved);
+
+ bool ResolveLoaderPathDependency(std::string const& name,
+ std::string const& loaderPath,
+ std::string& path, bool& resolved);
+
+ bool ResolveRPathDependency(std::string const& name,
+ std::string const& executablePath,
+ std::string const& loaderPath,
+ std::vector<std::string> const& rpaths,
+ std::string& path, bool& resolved);
+};
+
+#endif // cmBinUtilsMacOSMachOLinker_h
diff --git a/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx
new file mode 100644
index 0000000..bab2382
--- /dev/null
+++ b/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx
@@ -0,0 +1,100 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#include "cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h"
+
+#include "cmRuntimeDependencyArchive.h"
+#include "cmUVProcessChain.h"
+
+#include <cmsys/RegularExpression.hxx>
+
+#include <sstream>
+
+cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool::
+ cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool(
+ cmRuntimeDependencyArchive* archive)
+ : cmBinUtilsMacOSMachOGetRuntimeDependenciesTool(archive)
+{
+}
+
+bool cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool::GetFileInfo(
+ std::string const& file, std::vector<std::string>& libs,
+ std::vector<std::string>& rpaths)
+{
+ std::vector<std::string> command;
+ if (!this->Archive->GetGetRuntimeDependenciesCommand("otool", command)) {
+ this->SetError("Could not find otool");
+ return false;
+ }
+ command.emplace_back("-l");
+ command.emplace_back(file);
+
+ cmUVProcessChainBuilder builder;
+ builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
+ .AddCommand(command);
+
+ auto process = builder.Start();
+ if (!process.Valid()) {
+ std::ostringstream e;
+ e << "Failed to start otool process for:\n " << file;
+ this->SetError(e.str());
+ return false;
+ }
+
+ std::string line;
+ static const cmsys::RegularExpression rpathRegex("^ *cmd LC_RPATH$");
+ static const cmsys::RegularExpression loadDylibRegex(
+ "^ *cmd LC_LOAD_DYLIB$");
+ static const cmsys::RegularExpression pathRegex(
+ "^ *path (.*) \\(offset [0-9]+\\)$");
+ static const cmsys::RegularExpression nameRegex(
+ "^ *name (.*) \\(offset [0-9]+\\)$");
+ while (std::getline(*process.OutputStream(), line)) {
+ cmsys::RegularExpressionMatch cmdMatch;
+ if (rpathRegex.find(line.c_str(), cmdMatch)) {
+ if (!std::getline(*process.OutputStream(), line) ||
+ !std::getline(*process.OutputStream(), line)) {
+ this->SetError("Invalid output from otool");
+ return false;
+ }
+
+ cmsys::RegularExpressionMatch pathMatch;
+ if (pathRegex.find(line.c_str(), pathMatch)) {
+ rpaths.push_back(pathMatch.match(1));
+ } else {
+ this->SetError("Invalid output from otool");
+ return false;
+ }
+ } else if (loadDylibRegex.find(line.c_str(), cmdMatch)) {
+ if (!std::getline(*process.OutputStream(), line) ||
+ !std::getline(*process.OutputStream(), line)) {
+ this->SetError("Invalid output from otool");
+ return false;
+ }
+
+ cmsys::RegularExpressionMatch nameMatch;
+ if (nameRegex.find(line.c_str(), nameMatch)) {
+ libs.push_back(nameMatch.match(1));
+ } else {
+ this->SetError("Invalid output from otool");
+ return false;
+ }
+ }
+ }
+
+ if (!process.Wait()) {
+ std::ostringstream e;
+ e << "Failed to wait on otool process for:\n " << file;
+ this->SetError(e.str());
+ return false;
+ }
+ auto status = process.GetStatus();
+ if (!status[0] || status[0]->ExitStatus != 0) {
+ std::ostringstream e;
+ e << "Failed to run otool on:\n " << file;
+ this->SetError(e.str());
+ return false;
+ }
+
+ return true;
+}
diff --git a/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h b/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h
new file mode 100644
index 0000000..12bcbc1
--- /dev/null
+++ b/Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.h
@@ -0,0 +1,25 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#ifndef cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool_h
+#define cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool_h
+
+#include "cmBinUtilsMacOSMachOGetRuntimeDependenciesTool.h"
+
+#include <string>
+#include <vector>
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool
+ : public cmBinUtilsMacOSMachOGetRuntimeDependenciesTool
+{
+public:
+ cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool(
+ cmRuntimeDependencyArchive* archive);
+
+ bool GetFileInfo(std::string const& file, std::vector<std::string>& libs,
+ std::vector<std::string>& rpaths) override;
+};
+
+#endif // cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool_h
diff --git a/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.cxx
new file mode 100644
index 0000000..2b35e30
--- /dev/null
+++ b/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.cxx
@@ -0,0 +1,67 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#include "cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h"
+#include "cmRuntimeDependencyArchive.h"
+#include "cmUVProcessChain.h"
+
+#include <cmsys/RegularExpression.hxx>
+
+#include <sstream>
+
+cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool::
+ cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool(
+ cmRuntimeDependencyArchive* archive)
+ : cmBinUtilsWindowsPEGetRuntimeDependenciesTool(archive)
+{
+}
+
+bool cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool::GetFileInfo(
+ const std::string& file, std::vector<std::string>& needed)
+{
+ cmUVProcessChainBuilder builder;
+ builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT);
+
+ std::vector<std::string> command;
+ if (!this->Archive->GetGetRuntimeDependenciesCommand("dumpbin", command)) {
+ this->SetError("Could not find dumpbin");
+ return false;
+ }
+ command.emplace_back("/dependents");
+ command.push_back(file);
+ builder.AddCommand(command);
+
+ auto process = builder.Start();
+ if (!process.Valid()) {
+ std::ostringstream e;
+ e << "Failed to start dumpbin process for:\n " << file;
+ this->SetError(e.str());
+ return false;
+ }
+
+ std::string line;
+ static const cmsys::RegularExpression regex(
+ "^ ([^\n]*\\.[Dd][Ll][Ll])\r$");
+ while (std::getline(*process.OutputStream(), line)) {
+ cmsys::RegularExpressionMatch match;
+ if (regex.find(line.c_str(), match)) {
+ needed.push_back(match.match(1));
+ }
+ }
+
+ if (!process.Wait()) {
+ std::ostringstream e;
+ e << "Failed to wait on dumpbin process for:\n " << file;
+ this->SetError(e.str());
+ return false;
+ }
+ auto status = process.GetStatus();
+ if (!status[0] || status[0]->ExitStatus != 0) {
+ std::ostringstream e;
+ e << "Failed to run dumpbin on:\n " << file;
+ this->SetError(e.str());
+ return false;
+ }
+
+ return true;
+}
diff --git a/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h b/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h
new file mode 100644
index 0000000..4c17f8d
--- /dev/null
+++ b/Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h
@@ -0,0 +1,25 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#ifndef cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool_h
+#define cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool_h
+
+#include "cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h"
+
+#include <string>
+#include <vector>
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool
+ : public cmBinUtilsWindowsPEGetRuntimeDependenciesTool
+{
+public:
+ cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool(
+ cmRuntimeDependencyArchive* archive);
+
+ bool GetFileInfo(const std::string& file,
+ std::vector<std::string>& needed) override;
+};
+
+#endif // cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool_h
diff --git a/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.cxx
new file mode 100644
index 0000000..f5a4431
--- /dev/null
+++ b/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.cxx
@@ -0,0 +1,18 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#include "cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h"
+#include "cmRuntimeDependencyArchive.h"
+
+cmBinUtilsWindowsPEGetRuntimeDependenciesTool::
+ cmBinUtilsWindowsPEGetRuntimeDependenciesTool(
+ cmRuntimeDependencyArchive* archive)
+ : Archive(archive)
+{
+}
+
+void cmBinUtilsWindowsPEGetRuntimeDependenciesTool::SetError(
+ const std::string& error)
+{
+ this->Archive->SetError(error);
+}
diff --git a/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h b/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h
new file mode 100644
index 0000000..e9e402b
--- /dev/null
+++ b/Source/cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h
@@ -0,0 +1,28 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#ifndef cmBinUtilsWindowsPEGetRuntimeDependenciesTool_h
+#define cmBinUtilsWindowsPEGetRuntimeDependenciesTool_h
+
+#include <string>
+#include <vector>
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsWindowsPEGetRuntimeDependenciesTool
+{
+public:
+ cmBinUtilsWindowsPEGetRuntimeDependenciesTool(
+ cmRuntimeDependencyArchive* archive);
+ virtual ~cmBinUtilsWindowsPEGetRuntimeDependenciesTool() = default;
+
+ virtual bool GetFileInfo(const std::string& file,
+ std::vector<std::string>& needed) = 0;
+
+protected:
+ cmRuntimeDependencyArchive* Archive;
+
+ void SetError(const std::string& error);
+};
+
+#endif // cmBinUtilsWindowsPEGetRuntimeDependenciesTool_h
diff --git a/Source/cmBinUtilsWindowsPELinker.cxx b/Source/cmBinUtilsWindowsPELinker.cxx
new file mode 100644
index 0000000..796e9ed
--- /dev/null
+++ b/Source/cmBinUtilsWindowsPELinker.cxx
@@ -0,0 +1,121 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#include "cmBinUtilsWindowsPELinker.h"
+#include "cmAlgorithms.h"
+#include "cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h"
+#include "cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h"
+#include "cmRuntimeDependencyArchive.h"
+#include "cmSystemTools.h"
+
+#include <memory>
+#include <sstream>
+#include <vector>
+
+#ifdef _WIN32
+# include <windows.h>
+#endif
+
+cmBinUtilsWindowsPELinker::cmBinUtilsWindowsPELinker(
+ cmRuntimeDependencyArchive* archive)
+ : cmBinUtilsLinker(archive)
+{
+}
+
+bool cmBinUtilsWindowsPELinker::Prepare()
+{
+ std::string tool = this->Archive->GetGetRuntimeDependenciesTool();
+ if (tool.empty()) {
+ std::vector<std::string> command;
+ if (this->Archive->GetGetRuntimeDependenciesCommand("dumpbin", command)) {
+ tool = "dumpbin";
+ } else {
+ tool = "objdump";
+ }
+ }
+ if (tool == "dumpbin") {
+ this->Tool =
+ cm::make_unique<cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool>(
+ this->Archive);
+ } else if (tool == "objdump") {
+ this->Tool =
+ cm::make_unique<cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool>(
+ this->Archive);
+ } else {
+ std::ostringstream e;
+ e << "Invalid value for CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL: " << tool;
+ this->SetError(e.str());
+ return false;
+ }
+
+ return true;
+}
+
+bool cmBinUtilsWindowsPELinker::ScanDependencies(
+ std::string const& file, cmStateEnums::TargetType /* unused */)
+{
+ std::vector<std::string> needed;
+ if (!this->Tool->GetFileInfo(file, needed)) {
+ return false;
+ }
+ for (auto& n : needed) {
+ n = cmSystemTools::LowerCase(n);
+ }
+ std::string origin = cmSystemTools::GetFilenamePath(file);
+
+ for (auto const& lib : needed) {
+ if (!this->Archive->IsPreExcluded(lib)) {
+ std::string path;
+ bool resolved = false;
+ if (!this->ResolveDependency(lib, origin, path, resolved)) {
+ return false;
+ }
+ if (resolved) {
+ if (!this->Archive->IsPostExcluded(path)) {
+ bool unique;
+ this->Archive->AddResolvedPath(lib, path, unique);
+ if (unique &&
+ !this->ScanDependencies(path, cmStateEnums::SHARED_LIBRARY)) {
+ return false;
+ }
+ }
+ } else {
+ this->Archive->AddUnresolvedPath(lib);
+ }
+ }
+ }
+
+ return true;
+}
+
+bool cmBinUtilsWindowsPELinker::ResolveDependency(std::string const& name,
+ std::string const& origin,
+ std::string& path,
+ bool& resolved)
+{
+ auto dirs = this->Archive->GetSearchDirectories();
+
+#ifdef _WIN32
+ char buf[MAX_PATH];
+ unsigned int len;
+ if ((len = GetWindowsDirectoryA(buf, MAX_PATH)) > 0) {
+ dirs.insert(dirs.begin(), std::string(buf, len));
+ }
+ if ((len = GetSystemDirectoryA(buf, MAX_PATH)) > 0) {
+ dirs.insert(dirs.begin(), std::string(buf, len));
+ }
+#endif
+
+ dirs.insert(dirs.begin(), origin);
+
+ for (auto const& searchPath : dirs) {
+ path = searchPath + '/' + name;
+ if (cmSystemTools::PathExists(path)) {
+ resolved = true;
+ return true;
+ }
+ }
+
+ resolved = false;
+ return true;
+}
diff --git a/Source/cmBinUtilsWindowsPELinker.h b/Source/cmBinUtilsWindowsPELinker.h
new file mode 100644
index 0000000..d742195
--- /dev/null
+++ b/Source/cmBinUtilsWindowsPELinker.h
@@ -0,0 +1,33 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#ifndef cmBinUtilsWindowsPELinker_h
+#define cmBinUtilsWindowsPELinker_h
+
+#include "cmBinUtilsLinker.h"
+#include "cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h"
+#include "cmStateTypes.h"
+
+#include <memory> // IWYU pragma: keep
+#include <string>
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsWindowsPELinker : public cmBinUtilsLinker
+{
+public:
+ cmBinUtilsWindowsPELinker(cmRuntimeDependencyArchive* archive);
+
+ bool Prepare() override;
+
+ bool ScanDependencies(std::string const& file,
+ cmStateEnums::TargetType type) override;
+
+private:
+ std::unique_ptr<cmBinUtilsWindowsPEGetRuntimeDependenciesTool> Tool;
+
+ bool ResolveDependency(std::string const& name, std::string const& origin,
+ std::string& path, bool& resolved);
+};
+
+#endif // cmBinUtilsWindowsPELinker_h
diff --git a/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.cxx b/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.cxx
new file mode 100644
index 0000000..1f27003
--- /dev/null
+++ b/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.cxx
@@ -0,0 +1,67 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#include "cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h"
+#include "cmRuntimeDependencyArchive.h"
+#include "cmUVProcessChain.h"
+
+#include <cmsys/RegularExpression.hxx>
+
+#include <sstream>
+
+cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool::
+ cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool(
+ cmRuntimeDependencyArchive* archive)
+ : cmBinUtilsWindowsPEGetRuntimeDependenciesTool(archive)
+{
+}
+
+bool cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool::GetFileInfo(
+ const std::string& file, std::vector<std::string>& needed)
+{
+ cmUVProcessChainBuilder builder;
+ builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT);
+
+ std::vector<std::string> command;
+ if (!this->Archive->GetGetRuntimeDependenciesCommand("objdump", command)) {
+ this->SetError("Could not find objdump");
+ return false;
+ }
+ command.emplace_back("-p");
+ command.push_back(file);
+ builder.AddCommand(command);
+
+ auto process = builder.Start();
+ if (!process.Valid()) {
+ std::ostringstream e;
+ e << "Failed to start objdump process for:\n " << file;
+ this->SetError(e.str());
+ return false;
+ }
+
+ std::string line;
+ static const cmsys::RegularExpression regex(
+ "^\t*DLL Name: ([^\n]*\\.[Dd][Ll][Ll])\r$");
+ while (std::getline(*process.OutputStream(), line)) {
+ cmsys::RegularExpressionMatch match;
+ if (regex.find(line.c_str(), match)) {
+ needed.push_back(match.match(1));
+ }
+ }
+
+ if (!process.Wait()) {
+ std::ostringstream e;
+ e << "Failed to wait on objdump process for:\n " << file;
+ this->SetError(e.str());
+ return false;
+ }
+ auto status = process.GetStatus();
+ if (!status[0] || status[0]->ExitStatus != 0) {
+ std::ostringstream e;
+ e << "Failed to run objdump on:\n " << file;
+ this->SetError(e.str());
+ return false;
+ }
+
+ return true;
+}
diff --git a/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h b/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h
new file mode 100644
index 0000000..1d1a5b0
--- /dev/null
+++ b/Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h
@@ -0,0 +1,25 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#ifndef cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool_h
+#define cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool_h
+
+#include "cmBinUtilsWindowsPEGetRuntimeDependenciesTool.h"
+
+#include <string>
+#include <vector>
+
+class cmRuntimeDependencyArchive;
+
+class cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool
+ : public cmBinUtilsWindowsPEGetRuntimeDependenciesTool
+{
+public:
+ cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool(
+ cmRuntimeDependencyArchive* archive);
+
+ bool GetFileInfo(const std::string& file,
+ std::vector<std::string>& needed) override;
+};
+
+#endif // cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool_h
diff --git a/Source/cmFileCommand.cxx b/Source/cmFileCommand.cxx
index 7a3954e..980ad21 100644
--- a/Source/cmFileCommand.cxx
+++ b/Source/cmFileCommand.cxx
@@ -12,7 +12,9 @@
#include <assert.h>
#include <cmath>
#include <ctype.h>
+#include <map>
#include <memory> // IWYU pragma: keep
+#include <set>
#include <sstream>
#include <stdio.h>
#include <stdlib.h>
@@ -34,6 +36,8 @@
#include "cmMessageType.h"
#include "cmPolicies.h"
#include "cmRange.h"
+#include "cmRuntimeDependencyArchive.h"
+#include "cmState.h"
#include "cmSystemTools.h"
#include "cmTimestamp.h"
#include "cm_sys_stat.h"
@@ -184,6 +188,9 @@ bool cmFileCommand::InitialPass(std::vector<std::string> const& args,
if (subCommand == "CREATE_LINK") {
return this->HandleCreateLinkCommand(args);
}
+ if (subCommand == "GET_RUNTIME_DEPENDENCIES") {
+ return this->HandleGetRuntimeDependenciesCommand(args);
+ }
std::string e = "does not recognize sub-command " + subCommand;
this->SetError(e);
@@ -2690,3 +2697,171 @@ bool cmFileCommand::HandleCreateLinkCommand(
return true;
}
+
+bool cmFileCommand::HandleGetRuntimeDependenciesCommand(
+ std::vector<std::string> const& args)
+{
+ static const std::set<std::string> supportedPlatforms = { "Windows", "Linux",
+ "Darwin" };
+ std::string platform =
+ this->Makefile->GetSafeDefinition("CMAKE_HOST_SYSTEM_NAME");
+ if (!supportedPlatforms.count(platform)) {
+ std::ostringstream e;
+ e << "GET_RUNTIME_DEPENDENCIES is not supported on system \"" << platform
+ << "\"";
+ this->SetError(e.str());
+ cmSystemTools::SetFatalErrorOccured();
+ return false;
+ }
+
+ if (this->Makefile->GetState()->GetMode() == cmState::Project) {
+ this->Makefile->IssueMessage(MessageType::AUTHOR_WARNING,
+ "You have used file(GET_RUNTIME_DEPENDENCIES)"
+ " in project mode. This is probably not what "
+ "you intended to do. Instead, please consider"
+ " using it in an install(CODE) or "
+ "install(SCRIPT) command. For example:"
+ "\n install(CODE [["
+ "\n file(GET_RUNTIME_DEPENDENCIES"
+ "\n # ..."
+ "\n )"
+ "\n ]])");
+ }
+
+ struct Arguments
+ {
+ std::string ResolvedDependenciesVar;
+ std::string UnresolvedDependenciesVar;
+ std::string ConflictingDependenciesPrefix;
+ std::string BundleExecutable;
+ std::vector<std::string> Executables;
+ std::vector<std::string> Libraries;
+ std::vector<std::string> Directories;
+ std::vector<std::string> Modules;
+ std::vector<std::string> PreIncludeRegexes;
+ std::vector<std::string> PreExcludeRegexes;
+ std::vector<std::string> PostIncludeRegexes;
+ std::vector<std::string> PostExcludeRegexes;
+ };
+
+ static auto const parser =
+ cmArgumentParser<Arguments>{}
+ .Bind("RESOLVED_DEPENDENCIES_VAR"_s, &Arguments::ResolvedDependenciesVar)
+ .Bind("UNRESOLVED_DEPENDENCIES_VAR"_s,
+ &Arguments::UnresolvedDependenciesVar)
+ .Bind("CONFLICTING_DEPENDENCIES_PREFIX"_s,
+ &Arguments::ConflictingDependenciesPrefix)
+ .Bind("BUNDLE_EXECUTABLE"_s, &Arguments::BundleExecutable)
+ .Bind("EXECUTABLES"_s, &Arguments::Executables)
+ .Bind("LIBRARIES"_s, &Arguments::Libraries)
+ .Bind("MODULES"_s, &Arguments::Modules)
+ .Bind("DIRECTORIES"_s, &Arguments::Directories)
+ .Bind("PRE_INCLUDE_REGEXES"_s, &Arguments::PreIncludeRegexes)
+ .Bind("PRE_EXCLUDE_REGEXES"_s, &Arguments::PreExcludeRegexes)
+ .Bind("POST_INCLUDE_REGEXES"_s, &Arguments::PostIncludeRegexes)
+ .Bind("POST_EXCLUDE_REGEXES"_s, &Arguments::PostExcludeRegexes);
+
+ std::vector<std::string> unrecognizedArguments;
+ std::vector<std::string> keywordsMissingValues;
+ auto parsedArgs =
+ parser.Parse(cmMakeRange(args).advance(1), &unrecognizedArguments,
+ &keywordsMissingValues);
+ auto argIt = unrecognizedArguments.begin();
+ if (argIt != unrecognizedArguments.end()) {
+ std::ostringstream e;
+ e << "Unrecognized argument: \"" << *argIt << "\"";
+ this->SetError(e.str());
+ cmSystemTools::SetFatalErrorOccured();
+ return false;
+ }
+ argIt = keywordsMissingValues.begin();
+ if (argIt != keywordsMissingValues.end()) {
+ std::ostringstream e;
+ e << "Keyword missing value: " << *argIt;
+ this->SetError(e.str());
+ cmSystemTools::SetFatalErrorOccured();
+ return false;
+ }
+
+ cmRuntimeDependencyArchive archive(
+ this, parsedArgs.Directories, parsedArgs.BundleExecutable,
+ parsedArgs.PreIncludeRegexes, parsedArgs.PreExcludeRegexes,
+ parsedArgs.PostIncludeRegexes, parsedArgs.PostExcludeRegexes);
+ if (!archive.Prepare()) {
+ cmSystemTools::SetFatalErrorOccured();
+ return false;
+ }
+
+ if (!archive.GetRuntimeDependencies(
+ parsedArgs.Executables, parsedArgs.Libraries, parsedArgs.Modules)) {
+ cmSystemTools::SetFatalErrorOccured();
+ return false;
+ }
+
+ std::vector<std::string> deps, unresolvedDeps, conflictingDeps;
+ for (auto const& val : archive.GetResolvedPaths()) {
+ bool unique = true;
+ auto it = val.second.begin();
+ assert(it != val.second.end());
+ auto const& firstPath = *it;
+ while (++it != val.second.end()) {
+ if (!cmSystemTools::SameFile(firstPath, *it)) {
+ unique = false;
+ break;
+ }
+ }
+
+ if (unique) {
+ deps.push_back(firstPath);
+ } else if (!parsedArgs.ConflictingDependenciesPrefix.empty()) {
+ conflictingDeps.push_back(val.first);
+ std::vector<std::string> paths;
+ paths.insert(paths.begin(), val.second.begin(), val.second.end());
+ std::string varName =
+ parsedArgs.ConflictingDependenciesPrefix + "_" + val.first;
+ std::string pathsStr = cmJoin(paths, ";");
+ this->Makefile->AddDefinition(varName, pathsStr.c_str());
+ } else {
+ std::ostringstream e;
+ e << "Multiple conflicting paths found for " << val.first << ":";
+ for (auto const& path : val.second) {
+ e << "\n " << path;
+ }
+ this->SetError(e.str());
+ cmSystemTools::SetFatalErrorOccured();
+ return false;
+ }
+ }
+ if (!archive.GetUnresolvedPaths().empty()) {
+ if (!parsedArgs.UnresolvedDependenciesVar.empty()) {
+ unresolvedDeps.insert(unresolvedDeps.begin(),
+ archive.GetUnresolvedPaths().begin(),
+ archive.GetUnresolvedPaths().end());
+ } else {
+ auto it = archive.GetUnresolvedPaths().begin();
+ assert(it != archive.GetUnresolvedPaths().end());
+ std::ostringstream e;
+ e << "Could not resolve file " << *it;
+ this->SetError(e.str());
+ cmSystemTools::SetFatalErrorOccured();
+ return false;
+ }
+ }
+
+ if (!parsedArgs.ResolvedDependenciesVar.empty()) {
+ std::string val = cmJoin(deps, ";");
+ this->Makefile->AddDefinition(parsedArgs.ResolvedDependenciesVar,
+ val.c_str());
+ }
+ if (!parsedArgs.UnresolvedDependenciesVar.empty()) {
+ std::string val = cmJoin(unresolvedDeps, ";");
+ this->Makefile->AddDefinition(parsedArgs.UnresolvedDependenciesVar,
+ val.c_str());
+ }
+ if (!parsedArgs.ConflictingDependenciesPrefix.empty()) {
+ std::string val = cmJoin(conflictingDeps, ";");
+ this->Makefile->AddDefinition(
+ parsedArgs.ConflictingDependenciesPrefix + "_FILENAMES", val.c_str());
+ }
+ return true;
+}
diff --git a/Source/cmFileCommand.h b/Source/cmFileCommand.h
index 12c5115..cfff894 100644
--- a/Source/cmFileCommand.h
+++ b/Source/cmFileCommand.h
@@ -62,6 +62,8 @@ protected:
bool HandleSizeCommand(std::vector<std::string> const& args);
bool HandleReadSymlinkCommand(std::vector<std::string> const& args);
bool HandleCreateLinkCommand(std::vector<std::string> const& args);
+ bool HandleGetRuntimeDependenciesCommand(
+ std::vector<std::string> const& args);
private:
void AddEvaluationFile(const std::string& inputName,
diff --git a/Source/cmLDConfigLDConfigTool.cxx b/Source/cmLDConfigLDConfigTool.cxx
new file mode 100644
index 0000000..586ea96
--- /dev/null
+++ b/Source/cmLDConfigLDConfigTool.cxx
@@ -0,0 +1,70 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#include "cmLDConfigLDConfigTool.h"
+#include "cmMakefile.h"
+#include "cmRuntimeDependencyArchive.h"
+#include "cmSystemTools.h"
+#include "cmUVProcessChain.h"
+
+#include "cmsys/RegularExpression.hxx"
+
+#include <istream>
+#include <string>
+#include <vector>
+
+cmLDConfigLDConfigTool::cmLDConfigLDConfigTool(
+ cmRuntimeDependencyArchive* archive)
+ : cmLDConfigTool(archive)
+{
+}
+
+bool cmLDConfigLDConfigTool::GetLDConfigPaths(std::vector<std::string>& paths)
+{
+ std::string ldConfigPath =
+ this->Archive->GetMakefile()->GetSafeDefinition("CMAKE_LDCONFIG_COMMAND");
+ if (ldConfigPath.empty()) {
+ ldConfigPath = cmSystemTools::FindProgram(
+ "ldconfig", { "/sbin", "/usr/sbin", "/usr/local/sbin" });
+ if (ldConfigPath.empty()) {
+ this->Archive->SetError("Could not find ldconfig");
+ return false;
+ }
+ }
+
+ std::vector<std::string> ldConfigCommand;
+ cmSystemTools::ExpandListArgument(ldConfigPath, ldConfigCommand);
+ ldConfigCommand.emplace_back("-v");
+ ldConfigCommand.emplace_back("-N"); // Don't rebuild the cache.
+ ldConfigCommand.emplace_back("-X"); // Don't update links.
+
+ cmUVProcessChainBuilder builder;
+ builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
+ .AddCommand(ldConfigCommand);
+ auto process = builder.Start();
+ if (!process.Valid()) {
+ this->Archive->SetError("Failed to start ldconfig process");
+ return false;
+ }
+
+ std::string line;
+ static const cmsys::RegularExpression regex("^([^\t:]*):");
+ while (std::getline(*process.OutputStream(), line)) {
+ cmsys::RegularExpressionMatch match;
+ if (regex.find(line.c_str(), match)) {
+ paths.push_back(match.match(1));
+ }
+ }
+
+ if (!process.Wait()) {
+ this->Archive->SetError("Failed to wait on ldconfig process");
+ return false;
+ }
+ auto status = process.GetStatus();
+ if (!status[0] || status[0]->ExitStatus != 0) {
+ this->Archive->SetError("Failed to run ldconfig");
+ return false;
+ }
+
+ return true;
+}
diff --git a/Source/cmLDConfigLDConfigTool.h b/Source/cmLDConfigLDConfigTool.h
new file mode 100644
index 0000000..d945a9b
--- /dev/null
+++ b/Source/cmLDConfigLDConfigTool.h
@@ -0,0 +1,22 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#ifndef cmLDConfigLDConfigTool_h
+#define cmLDConfigLDConfigTool_h
+
+#include "cmLDConfigTool.h"
+
+#include <string>
+#include <vector>
+
+class cmRuntimeDependencyArchive;
+
+class cmLDConfigLDConfigTool : public cmLDConfigTool
+{
+public:
+ cmLDConfigLDConfigTool(cmRuntimeDependencyArchive* archive);
+
+ bool GetLDConfigPaths(std::vector<std::string>& paths) override;
+};
+
+#endif
diff --git a/Source/cmLDConfigTool.cxx b/Source/cmLDConfigTool.cxx
new file mode 100644
index 0000000..8d5d563
--- /dev/null
+++ b/Source/cmLDConfigTool.cxx
@@ -0,0 +1,9 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#include "cmLDConfigTool.h"
+
+cmLDConfigTool::cmLDConfigTool(cmRuntimeDependencyArchive* archive)
+ : Archive(archive)
+{
+}
diff --git a/Source/cmLDConfigTool.h b/Source/cmLDConfigTool.h
new file mode 100644
index 0000000..c816562
--- /dev/null
+++ b/Source/cmLDConfigTool.h
@@ -0,0 +1,24 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#ifndef cmLDConfigTool_h
+#define cmLDConfigTool_h
+
+#include <string>
+#include <vector>
+
+class cmRuntimeDependencyArchive;
+
+class cmLDConfigTool
+{
+public:
+ cmLDConfigTool(cmRuntimeDependencyArchive* archive);
+ virtual ~cmLDConfigTool() = default;
+
+ virtual bool GetLDConfigPaths(std::vector<std::string>& paths) = 0;
+
+protected:
+ cmRuntimeDependencyArchive* Archive;
+};
+
+#endif
diff --git a/Source/cmRuntimeDependencyArchive.cxx b/Source/cmRuntimeDependencyArchive.cxx
new file mode 100644
index 0000000..b4c6c32
--- /dev/null
+++ b/Source/cmRuntimeDependencyArchive.cxx
@@ -0,0 +1,378 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#include "cmRuntimeDependencyArchive.h"
+
+#include "cmAlgorithms.h"
+#include "cmBinUtilsLinuxELFLinker.h"
+#include "cmBinUtilsMacOSMachOLinker.h"
+#include "cmBinUtilsWindowsPELinker.h"
+#include "cmCommand.h"
+#include "cmMakefile.h"
+#include "cmStateTypes.h"
+#include "cmSystemTools.h"
+
+#if defined(_WIN32)
+# include "cmGlobalGenerator.h"
+# ifdef CMAKE_BUILD_WITH_CMAKE
+# include "cmGlobalVisualStudioVersionedGenerator.h"
+# endif
+# include "cmVSSetupHelper.h"
+# include "cmsys/Glob.hxx"
+#endif
+
+#include <algorithm>
+#include <sstream>
+#include <string>
+#include <utility>
+#include <vector>
+
+#if defined(_WIN32)
+static void AddVisualStudioPath(std::vector<std::string>& paths,
+ const std::string& prefix,
+ unsigned int version, cmGlobalGenerator* gg)
+{
+ // If generating for the VS IDE, use the same instance.
+ std::string vsloc;
+ bool found = false;
+# ifdef CMAKE_BUILD_WITH_CMAKE
+ if (gg->GetName().find(prefix) == 0) {
+ cmGlobalVisualStudioVersionedGenerator* vsgen =
+ static_cast<cmGlobalVisualStudioVersionedGenerator*>(gg);
+ if (vsgen->GetVSInstance(vsloc)) {
+ found = true;
+ }
+ }
+# endif
+
+ // Otherwise, find a VS instance ourselves.
+ if (!found) {
+ cmVSSetupAPIHelper vsSetupAPIHelper(version);
+ if (vsSetupAPIHelper.GetVSInstanceInfo(vsloc)) {
+ cmSystemTools::ConvertToUnixSlashes(vsloc);
+ found = true;
+ }
+ }
+
+ if (found) {
+ cmsys::Glob glob;
+ glob.SetListDirs(true);
+ glob.FindFiles(vsloc + "/VC/Tools/MSVC/*");
+ for (auto const& vcdir : glob.GetFiles()) {
+ paths.push_back(vcdir + "/bin/Hostx64/x64");
+ paths.push_back(vcdir + "/bin/Hostx86/x64");
+ paths.push_back(vcdir + "/bin/Hostx64/x86");
+ paths.push_back(vcdir + "/bin/Hostx86/x86");
+ }
+ }
+}
+
+static void AddRegistryPath(std::vector<std::string>& paths,
+ const std::string& path, cmMakefile* mf)
+{
+ // We should view the registry as the target application would view
+ // it.
+ cmSystemTools::KeyWOW64 view = cmSystemTools::KeyWOW64_32;
+ cmSystemTools::KeyWOW64 other_view = cmSystemTools::KeyWOW64_64;
+ if (mf->PlatformIs64Bit()) {
+ view = cmSystemTools::KeyWOW64_64;
+ other_view = cmSystemTools::KeyWOW64_32;
+ }
+
+ // Expand using the view of the target application.
+ std::string expanded = path;
+ cmSystemTools::ExpandRegistryValues(expanded, view);
+ cmSystemTools::GlobDirs(expanded, paths);
+
+ // Executables can be either 32-bit or 64-bit, so expand using the
+ // alternative view.
+ expanded = path;
+ cmSystemTools::ExpandRegistryValues(expanded, other_view);
+ cmSystemTools::GlobDirs(expanded, paths);
+}
+
+static void AddEnvPath(std::vector<std::string>& paths, const std::string& var,
+ const std::string& suffix)
+{
+ std::string value;
+ if (cmSystemTools::GetEnv(var, value)) {
+ paths.push_back(value + suffix);
+ }
+}
+#endif
+
+static cmsys::RegularExpression TransformCompile(const std::string& str)
+{
+ return cmsys::RegularExpression(str);
+}
+
+cmRuntimeDependencyArchive::cmRuntimeDependencyArchive(
+ cmCommand* command, std::vector<std::string> searchDirectories,
+ std::string bundleExecutable,
+ const std::vector<std::string>& preIncludeRegexes,
+ const std::vector<std::string>& preExcludeRegexes,
+ const std::vector<std::string>& postIncludeRegexes,
+ const std::vector<std::string>& postExcludeRegexes)
+ : Command(command)
+ , SearchDirectories(std::move(searchDirectories))
+ , BundleExecutable(std::move(bundleExecutable))
+ , PreIncludeRegexes(preIncludeRegexes.size())
+ , PreExcludeRegexes(preExcludeRegexes.size())
+ , PostIncludeRegexes(postIncludeRegexes.size())
+ , PostExcludeRegexes(postExcludeRegexes.size())
+{
+ std::transform(preIncludeRegexes.begin(), preIncludeRegexes.end(),
+ this->PreIncludeRegexes.begin(), TransformCompile);
+ std::transform(preExcludeRegexes.begin(), preExcludeRegexes.end(),
+ this->PreExcludeRegexes.begin(), TransformCompile);
+ std::transform(postIncludeRegexes.begin(), postIncludeRegexes.end(),
+ this->PostIncludeRegexes.begin(), TransformCompile);
+ std::transform(postExcludeRegexes.begin(), postExcludeRegexes.end(),
+ this->PostExcludeRegexes.begin(), TransformCompile);
+}
+
+bool cmRuntimeDependencyArchive::Prepare()
+{
+ std::string platform = this->GetMakefile()->GetSafeDefinition(
+ "CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM");
+ if (platform.empty()) {
+ std::string systemName =
+ this->GetMakefile()->GetSafeDefinition("CMAKE_HOST_SYSTEM_NAME");
+ if (systemName == "Windows") {
+ platform = "windows+pe";
+ } else if (systemName == "Darwin") {
+ platform = "macos+macho";
+ } else if (systemName == "Linux") {
+ platform = "linux+elf";
+ }
+ }
+ if (platform == "linux+elf") {
+ this->Linker = cm::make_unique<cmBinUtilsLinuxELFLinker>(this);
+ } else if (platform == "windows+pe") {
+ this->Linker = cm::make_unique<cmBinUtilsWindowsPELinker>(this);
+ } else if (platform == "macos+macho") {
+ this->Linker = cm::make_unique<cmBinUtilsMacOSMachOLinker>(this);
+ } else {
+ std::ostringstream e;
+ e << "Invalid value for CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM: "
+ << platform;
+ this->SetError(e.str());
+ return false;
+ }
+
+ return this->Linker->Prepare();
+}
+
+bool cmRuntimeDependencyArchive::GetRuntimeDependencies(
+ const std::vector<std::string>& executables,
+ const std::vector<std::string>& libraries,
+ const std::vector<std::string>& modules)
+{
+ for (auto const& exe : executables) {
+ if (!this->Linker->ScanDependencies(exe, cmStateEnums::EXECUTABLE)) {
+ return false;
+ }
+ }
+ for (auto const& lib : libraries) {
+ if (!this->Linker->ScanDependencies(lib, cmStateEnums::SHARED_LIBRARY)) {
+ return false;
+ }
+ }
+ for (auto const& mod : modules) {
+ if (!this->Linker->ScanDependencies(mod, cmStateEnums::MODULE_LIBRARY)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void cmRuntimeDependencyArchive::SetError(const std::string& e)
+{
+ this->Command->SetError(e);
+}
+
+std::string cmRuntimeDependencyArchive::GetBundleExecutable()
+{
+ return this->BundleExecutable;
+}
+
+const std::vector<std::string>&
+cmRuntimeDependencyArchive::GetSearchDirectories()
+{
+ return this->SearchDirectories;
+}
+
+std::string cmRuntimeDependencyArchive::GetGetRuntimeDependenciesTool()
+{
+ return this->GetMakefile()->GetSafeDefinition(
+ "CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL");
+}
+
+bool cmRuntimeDependencyArchive::GetGetRuntimeDependenciesCommand(
+ const std::string& search, std::vector<std::string>& command)
+{
+ // First see if it was supplied by the user
+ std::string toolCommand = this->GetMakefile()->GetSafeDefinition(
+ "CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND");
+ if (!toolCommand.empty()) {
+ cmSystemTools::ExpandListArgument(toolCommand, command);
+ return true;
+ }
+
+ // Now go searching for it
+ std::vector<std::string> paths;
+#ifdef _WIN32
+ cmGlobalGenerator* gg = this->GetMakefile()->GetGlobalGenerator();
+
+ // Add newer Visual Studio paths
+ AddVisualStudioPath(paths, "Visual Studio 16 ", 16, gg);
+ AddVisualStudioPath(paths, "Visual Studio 15 ", 15, gg);
+
+ // Add older Visual Studio paths
+ AddRegistryPath(
+ paths,
+ "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\14.0;InstallDir]/"
+ "../../VC/bin",
+ this->GetMakefile());
+ AddEnvPath(paths, "VS140COMNTOOLS", "/../../VC/bin");
+ paths.push_back(
+ "C:/Program Files (x86)/Microsoft Visual Studio 14.0/VC/bin");
+ AddRegistryPath(
+ paths,
+ "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\12.0;InstallDir]/"
+ "../../VC/bin",
+ this->GetMakefile());
+ AddEnvPath(paths, "VS120COMNTOOLS", "/../../VC/bin");
+ paths.push_back(
+ "C:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/bin");
+ AddRegistryPath(
+ paths,
+ "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\11.0;InstallDir]/"
+ "../../VC/bin",
+ this->GetMakefile());
+ AddEnvPath(paths, "VS110COMNTOOLS", "/../../VC/bin");
+ paths.push_back(
+ "C:/Program Files (x86)/Microsoft Visual Studio 11.0/VC/bin");
+ AddRegistryPath(
+ paths,
+ "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\10.0;InstallDir]/"
+ "../../VC/bin",
+ this->GetMakefile());
+ AddEnvPath(paths, "VS100COMNTOOLS", "/../../VC/bin");
+ paths.push_back(
+ "C:/Program Files (x86)/Microsoft Visual Studio 10.0/VC/bin");
+ AddRegistryPath(
+ paths,
+ "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\9.0;InstallDir]/"
+ "../../VC/bin",
+ this->GetMakefile());
+ AddEnvPath(paths, "VS90COMNTOOLS", "/../../VC/bin");
+ paths.push_back("C:/Program Files/Microsoft Visual Studio 9.0/VC/bin");
+ paths.push_back("C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/bin");
+ AddRegistryPath(
+ paths,
+ "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\8.0;InstallDir]/"
+ "../../VC/bin",
+ this->GetMakefile());
+ AddEnvPath(paths, "VS80COMNTOOLS", "/../../VC/bin");
+ paths.push_back("C:/Program Files/Microsoft Visual Studio 8/VC/BIN");
+ paths.push_back("C:/Program Files (x86)/Microsoft Visual Studio 8/VC/BIN");
+ AddRegistryPath(
+ paths,
+ "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\VisualStudio\\7.1;InstallDir]/"
+ "../../VC7/bin",
+ this->GetMakefile());
+ AddEnvPath(paths, "VS71COMNTOOLS", "/../../VC7/bin");
+ paths.push_back(
+ "C:/Program Files/Microsoft Visual Studio .NET 2003/VC7/BIN");
+ paths.push_back(
+ "C:/Program Files (x86)/Microsoft Visual Studio .NET 2003/VC7/BIN");
+#endif
+
+ std::string program = cmSystemTools::FindProgram(search, paths);
+ if (!program.empty()) {
+ command = { program };
+ return true;
+ }
+
+ // Couldn't find it
+ return false;
+}
+
+bool cmRuntimeDependencyArchive::IsPreExcluded(const std::string& name)
+{
+ cmsys::RegularExpressionMatch match;
+
+ for (auto const& regex : this->PreIncludeRegexes) {
+ if (regex.find(name.c_str(), match)) {
+ return false;
+ }
+ }
+
+ for (auto const& regex : this->PreExcludeRegexes) {
+ if (regex.find(name.c_str(), match)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool cmRuntimeDependencyArchive::IsPostExcluded(const std::string& name)
+{
+ cmsys::RegularExpressionMatch match;
+
+ for (auto const& regex : this->PostIncludeRegexes) {
+ if (regex.find(name.c_str(), match)) {
+ return false;
+ }
+ }
+
+ for (auto const& regex : this->PostExcludeRegexes) {
+ if (regex.find(name.c_str(), match)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void cmRuntimeDependencyArchive::AddResolvedPath(const std::string& name,
+ const std::string& path,
+ bool& unique)
+{
+ auto it =
+ this->ResolvedPaths
+ .insert(std::pair<std::string, std::set<std::string>>{ name, {} })
+ .first;
+ unique = true;
+ for (auto const& other : it->second) {
+ if (cmSystemTools::SameFile(path, other)) {
+ unique = false;
+ break;
+ }
+ }
+ it->second.insert(path);
+}
+
+void cmRuntimeDependencyArchive::AddUnresolvedPath(const std::string& name)
+{
+ this->UnresolvedPaths.insert(name);
+}
+
+cmMakefile* cmRuntimeDependencyArchive::GetMakefile()
+{
+ return this->Command->GetMakefile();
+}
+
+const std::map<std::string, std::set<std::string>>&
+cmRuntimeDependencyArchive::GetResolvedPaths()
+{
+ return this->ResolvedPaths;
+}
+
+const std::set<std::string>& cmRuntimeDependencyArchive::GetUnresolvedPaths()
+{
+ return this->UnresolvedPaths;
+}
diff --git a/Source/cmRuntimeDependencyArchive.h b/Source/cmRuntimeDependencyArchive.h
new file mode 100644
index 0000000..ec3ecd4
--- /dev/null
+++ b/Source/cmRuntimeDependencyArchive.h
@@ -0,0 +1,70 @@
+/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
+ file Copyright.txt or https://cmake.org/licensing for details. */
+
+#ifndef cmRuntimeDependencyArchive_h
+#define cmRuntimeDependencyArchive_h
+
+#include "cmBinUtilsLinker.h"
+
+#include "cmsys/RegularExpression.hxx"
+
+#include <map>
+#include <memory> // IWYU pragma: keep
+#include <set>
+#include <string>
+#include <vector>
+
+class cmCommand;
+class cmMakefile;
+
+class cmRuntimeDependencyArchive
+{
+public:
+ explicit cmRuntimeDependencyArchive(
+ cmCommand* command, std::vector<std::string> searchDirectories,
+ std::string bundleExecutable,
+ const std::vector<std::string>& preIncludeRegexes,
+ const std::vector<std::string>& preExcludeRegexes,
+ const std::vector<std::string>& postIncludeRegexes,
+ const std::vector<std::string>& postExcludeRegexes);
+ bool Prepare();
+ bool GetRuntimeDependencies(const std::vector<std::string>& executables,
+ const std::vector<std::string>& libraries,
+ const std::vector<std::string>& modules);
+
+ void SetError(const std::string& e);
+
+ std::string GetBundleExecutable();
+ const std::vector<std::string>& GetSearchDirectories();
+ std::string GetGetRuntimeDependenciesTool();
+ bool GetGetRuntimeDependenciesCommand(const std::string& search,
+ std::vector<std::string>& command);
+ bool IsPreExcluded(const std::string& name);
+ bool IsPostExcluded(const std::string& name);
+
+ void AddResolvedPath(const std::string& name, const std::string& path,
+ bool& unique);
+ void AddUnresolvedPath(const std::string& name);
+
+ cmMakefile* GetMakefile();
+ const std::map<std::string, std::set<std::string>>& GetResolvedPaths();
+ const std::set<std::string>& GetUnresolvedPaths();
+
+private:
+ cmCommand* Command;
+ std::unique_ptr<cmBinUtilsLinker> Linker;
+
+ std::string GetRuntimeDependenciesTool;
+ std::vector<std::string> GetRuntimeDependenciesCommand;
+
+ std::vector<std::string> SearchDirectories;
+ std::string BundleExecutable;
+ std::vector<cmsys::RegularExpression> PreIncludeRegexes;
+ std::vector<cmsys::RegularExpression> PreExcludeRegexes;
+ std::vector<cmsys::RegularExpression> PostIncludeRegexes;
+ std::vector<cmsys::RegularExpression> PostExcludeRegexes;
+ std::map<std::string, std::set<std::string>> ResolvedPaths;
+ std::set<std::string> UnresolvedPaths;
+};
+
+#endif // cmRuntimeDependencyArchive_h