Merge branch 'release-3.31'
[kiteware-cmake.git] / Source / cmBinUtilsWindowsPELinker.cxx
blob918f563387fe159bcb5ff325729bc6d56e7ecf14
1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
4 #include "cmBinUtilsWindowsPELinker.h"
6 #include <algorithm>
7 #include <iterator>
8 #include <sstream>
9 #include <utility>
10 #include <vector>
12 #include <cm/memory>
14 #include "cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.h"
15 #include "cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.h"
16 #include "cmRuntimeDependencyArchive.h"
17 #include "cmStringAlgorithms.h"
18 #include "cmSystemTools.h"
20 #ifdef _WIN32
21 # include <windows.h>
23 # include "cmsys/Encoding.hxx"
24 #endif
26 #ifdef _WIN32
27 namespace {
29 void ReplaceWithActualNameCasing(std::string& path)
31 WIN32_FIND_DATAW findData;
32 HANDLE hFind = ::FindFirstFileW(
33 cmsys::Encoding::ToWindowsExtendedPath(path).c_str(), &findData);
35 if (hFind != INVALID_HANDLE_VALUE) {
36 auto onDiskName = cmsys::Encoding::ToNarrow(findData.cFileName);
37 ::FindClose(hFind);
38 path.replace(path.end() - onDiskName.size(), path.end(), onDiskName);
43 #endif
45 cmBinUtilsWindowsPELinker::cmBinUtilsWindowsPELinker(
46 cmRuntimeDependencyArchive* archive)
47 : cmBinUtilsLinker(archive)
51 bool cmBinUtilsWindowsPELinker::Prepare()
53 std::string tool = this->Archive->GetGetRuntimeDependenciesTool();
54 if (tool.empty()) {
55 std::vector<std::string> command;
56 if (this->Archive->GetGetRuntimeDependenciesCommand("dumpbin", command)) {
57 tool = "dumpbin";
58 } else {
59 tool = "objdump";
62 if (tool == "dumpbin") {
63 this->Tool =
64 cm::make_unique<cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool>(
65 this->Archive);
66 } else if (tool == "objdump") {
67 this->Tool =
68 cm::make_unique<cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool>(
69 this->Archive);
70 } else {
71 std::ostringstream e;
72 e << "Invalid value for CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL: " << tool;
73 this->SetError(e.str());
74 return false;
77 return true;
80 bool cmBinUtilsWindowsPELinker::ScanDependencies(
81 std::string const& file, cmStateEnums::TargetType /* unused */)
83 std::vector<std::string> needed;
84 if (!this->Tool->GetFileInfo(file, needed)) {
85 return false;
88 struct WinPEDependency
90 WinPEDependency(std::string o)
91 : Original(std::move(o))
92 , LowerCase(cmSystemTools::LowerCase(Original))
95 std::string const Original;
96 std::string const LowerCase;
99 std::vector<WinPEDependency> depends;
100 depends.reserve(needed.size());
101 std::move(needed.begin(), needed.end(), std::back_inserter(depends));
102 std::string origin = cmSystemTools::GetFilenamePath(file);
104 for (auto const& lib : depends) {
105 if (!this->Archive->IsPreExcluded(lib.LowerCase)) {
106 std::string path;
107 bool resolved = false;
108 if (!this->ResolveDependency(lib.LowerCase, origin, path, resolved)) {
109 return false;
111 if (resolved) {
112 if (!this->Archive->IsPostExcluded(path)) {
113 #ifdef _WIN32
114 ReplaceWithActualNameCasing(path);
115 #else
116 path.replace(path.end() - lib.Original.size(), path.end(),
117 lib.Original);
118 #endif
119 bool unique;
120 this->Archive->AddResolvedPath(lib.Original, path, unique);
121 if (unique &&
122 !this->ScanDependencies(path, cmStateEnums::SHARED_LIBRARY)) {
123 return false;
126 } else {
127 this->Archive->AddUnresolvedPath(lib.Original);
132 return true;
135 bool cmBinUtilsWindowsPELinker::ResolveDependency(std::string const& name,
136 std::string const& origin,
137 std::string& path,
138 bool& resolved)
140 auto dirs = this->Archive->GetSearchDirectories();
142 #ifdef _WIN32
143 char buf[MAX_PATH];
144 unsigned int len;
145 if ((len = GetWindowsDirectoryA(buf, MAX_PATH)) > 0) {
146 dirs.insert(dirs.begin(), std::string(buf, len));
148 if ((len = GetSystemDirectoryA(buf, MAX_PATH)) > 0) {
149 dirs.insert(dirs.begin(), std::string(buf, len));
151 #endif
153 dirs.insert(dirs.begin(), origin);
155 for (auto const& searchPath : dirs) {
156 path = cmStrCat(searchPath, '/', name);
157 if (cmSystemTools::PathExists(path)) {
158 resolved = true;
159 return true;
163 resolved = false;
164 return true;