diff options
author | Kyle Edwards <kyle.edwards@kitware.com> | 2019-03-14 15:58:34 (GMT) |
---|---|---|
committer | Kyle Edwards <kyle.edwards@kitware.com> | 2019-06-10 18:35:28 (GMT) |
commit | 1f9ef25130caa89969cc69b4873edf1723533a5f (patch) | |
tree | f48bf3864cc3c6ccd87e6efffcba2c1f582ac0e0 /Source | |
parent | f36719111de99e5be426552e9aaba96518b888b7 (diff) | |
download | CMake-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')
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 |