1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #include "cmFindCommon.h"
4
5 #include <algorithm>
6 #include <array>
7 #include <utility>
8
9 #include <cmext/algorithm>
10
11 #include "cmExecutionStatus.h"
12 #include "cmMakefile.h"
13 #include "cmMessageType.h"
14 #include "cmStringAlgorithms.h"
15 #include "cmSystemTools.h"
16 #include "cmValue.h"
17 #include "cmake.h"
18
19 cmFindCommon::PathGroup cmFindCommon::PathGroup::All("ALL");
20 cmFindCommon::PathLabel cmFindCommon::PathLabel::PackageRoot(
21 "PackageName_ROOT");
22 cmFindCommon::PathLabel cmFindCommon::PathLabel::CMake("CMAKE");
23 cmFindCommon::PathLabel cmFindCommon::PathLabel::CMakeEnvironment(
24 "CMAKE_ENVIRONMENT");
25 cmFindCommon::PathLabel cmFindCommon::PathLabel::Hints("HINTS");
26 cmFindCommon::PathLabel cmFindCommon::PathLabel::SystemEnvironment(
27 "SYSTM_ENVIRONMENT");
28 cmFindCommon::PathLabel cmFindCommon::PathLabel::CMakeSystem("CMAKE_SYSTEM");
29 cmFindCommon::PathLabel cmFindCommon::PathLabel::Guess("GUESS");
30
cmFindCommon(cmExecutionStatus & status)31 cmFindCommon::cmFindCommon(cmExecutionStatus& status)
32 : Makefile(&status.GetMakefile())
33 , Status(status)
34 {
35 this->FindRootPathMode = RootPathModeBoth;
36 this->NoDefaultPath = false;
37 this->NoPackageRootPath = false;
38 this->NoCMakePath = false;
39 this->NoCMakeEnvironmentPath = false;
40 this->NoSystemEnvironmentPath = false;
41 this->NoCMakeSystemPath = false;
42
43 // OS X Bundle and Framework search policy. The default is to
44 // search frameworks first on apple.
45 #if defined(__APPLE__)
46 this->SearchFrameworkFirst = true;
47 this->SearchAppBundleFirst = true;
48 #else
49 this->SearchFrameworkFirst = false;
50 this->SearchAppBundleFirst = false;
51 #endif
52 this->SearchFrameworkOnly = false;
53 this->SearchFrameworkLast = false;
54 this->SearchAppBundleOnly = false;
55 this->SearchAppBundleLast = false;
56
57 this->InitializeSearchPathGroups();
58
59 this->DebugMode = false;
60 }
61
SetError(std::string const & e)62 void cmFindCommon::SetError(std::string const& e)
63 {
64 this->Status.SetError(e);
65 }
66
DebugMessage(std::string const & msg) const67 void cmFindCommon::DebugMessage(std::string const& msg) const
68 {
69 if (this->Makefile) {
70 this->Makefile->IssueMessage(MessageType::LOG, msg);
71 }
72 }
73
ComputeIfDebugModeWanted()74 bool cmFindCommon::ComputeIfDebugModeWanted()
75 {
76 return this->Makefile->IsOn("CMAKE_FIND_DEBUG_MODE") ||
77 this->Makefile->GetCMakeInstance()->GetDebugFindOutput();
78 }
79
InitializeSearchPathGroups()80 void cmFindCommon::InitializeSearchPathGroups()
81 {
82 std::vector<PathLabel>* labels;
83
84 // Define the various different groups of path types
85
86 // All search paths
87 labels = &this->PathGroupLabelMap[PathGroup::All];
88 labels->push_back(PathLabel::PackageRoot);
89 labels->push_back(PathLabel::CMake);
90 labels->push_back(PathLabel::CMakeEnvironment);
91 labels->push_back(PathLabel::Hints);
92 labels->push_back(PathLabel::SystemEnvironment);
93 labels->push_back(PathLabel::CMakeSystem);
94 labels->push_back(PathLabel::Guess);
95
96 // Define the search group order
97 this->PathGroupOrder.push_back(PathGroup::All);
98
99 // Create the individual labeled search paths
100 this->LabeledPaths.insert(
101 std::make_pair(PathLabel::PackageRoot, cmSearchPath(this)));
102 this->LabeledPaths.insert(
103 std::make_pair(PathLabel::CMake, cmSearchPath(this)));
104 this->LabeledPaths.insert(
105 std::make_pair(PathLabel::CMakeEnvironment, cmSearchPath(this)));
106 this->LabeledPaths.insert(
107 std::make_pair(PathLabel::Hints, cmSearchPath(this)));
108 this->LabeledPaths.insert(
109 std::make_pair(PathLabel::SystemEnvironment, cmSearchPath(this)));
110 this->LabeledPaths.insert(
111 std::make_pair(PathLabel::CMakeSystem, cmSearchPath(this)));
112 this->LabeledPaths.insert(
113 std::make_pair(PathLabel::Guess, cmSearchPath(this)));
114 }
115
SelectDefaultRootPathMode()116 void cmFindCommon::SelectDefaultRootPathMode()
117 {
118 // Check the policy variable for this find command type.
119 std::string findRootPathVar =
120 cmStrCat("CMAKE_FIND_ROOT_PATH_MODE_", this->CMakePathName);
121 std::string rootPathMode =
122 this->Makefile->GetSafeDefinition(findRootPathVar);
123 if (rootPathMode == "NEVER") {
124 this->FindRootPathMode = RootPathModeNever;
125 } else if (rootPathMode == "ONLY") {
126 this->FindRootPathMode = RootPathModeOnly;
127 } else if (rootPathMode == "BOTH") {
128 this->FindRootPathMode = RootPathModeBoth;
129 }
130 }
131
SelectDefaultMacMode()132 void cmFindCommon::SelectDefaultMacMode()
133 {
134 std::string ff = this->Makefile->GetSafeDefinition("CMAKE_FIND_FRAMEWORK");
135 if (ff == "NEVER") {
136 this->SearchFrameworkLast = false;
137 this->SearchFrameworkFirst = false;
138 this->SearchFrameworkOnly = false;
139 } else if (ff == "ONLY") {
140 this->SearchFrameworkLast = false;
141 this->SearchFrameworkFirst = false;
142 this->SearchFrameworkOnly = true;
143 } else if (ff == "FIRST") {
144 this->SearchFrameworkLast = false;
145 this->SearchFrameworkFirst = true;
146 this->SearchFrameworkOnly = false;
147 } else if (ff == "LAST") {
148 this->SearchFrameworkLast = true;
149 this->SearchFrameworkFirst = false;
150 this->SearchFrameworkOnly = false;
151 }
152
153 std::string fab = this->Makefile->GetSafeDefinition("CMAKE_FIND_APPBUNDLE");
154 if (fab == "NEVER") {
155 this->SearchAppBundleLast = false;
156 this->SearchAppBundleFirst = false;
157 this->SearchAppBundleOnly = false;
158 } else if (fab == "ONLY") {
159 this->SearchAppBundleLast = false;
160 this->SearchAppBundleFirst = false;
161 this->SearchAppBundleOnly = true;
162 } else if (fab == "FIRST") {
163 this->SearchAppBundleLast = false;
164 this->SearchAppBundleFirst = true;
165 this->SearchAppBundleOnly = false;
166 } else if (fab == "LAST") {
167 this->SearchAppBundleLast = true;
168 this->SearchAppBundleFirst = false;
169 this->SearchAppBundleOnly = false;
170 }
171 }
172
SelectDefaultSearchModes()173 void cmFindCommon::SelectDefaultSearchModes()
174 {
175 const std::array<std::pair<bool&, std::string>, 5> search_paths = {
176 { { this->NoPackageRootPath, "CMAKE_FIND_USE_PACKAGE_ROOT_PATH" },
177 { this->NoCMakePath, "CMAKE_FIND_USE_CMAKE_PATH" },
178 { this->NoCMakeEnvironmentPath,
179 "CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH" },
180 { this->NoSystemEnvironmentPath,
181 "CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH" },
182 { this->NoCMakeSystemPath, "CMAKE_FIND_USE_CMAKE_SYSTEM_PATH" } }
183 };
184
185 for (auto const& path : search_paths) {
186 cmValue def = this->Makefile->GetDefinition(path.second);
187 if (def) {
188 path.first = !cmIsOn(*def);
189 }
190 }
191 }
192
RerootPaths(std::vector<std::string> & paths)193 void cmFindCommon::RerootPaths(std::vector<std::string>& paths)
194 {
195 #if 0
196 for(std::string const& p : paths)
197 {
198 fprintf(stderr, "[%s]\n", p.c_str());
199 }
200 #endif
201 // Short-circuit if there is nothing to do.
202 if (this->FindRootPathMode == RootPathModeNever) {
203 return;
204 }
205
206 cmValue sysroot = this->Makefile->GetDefinition("CMAKE_SYSROOT");
207 cmValue sysrootCompile =
208 this->Makefile->GetDefinition("CMAKE_SYSROOT_COMPILE");
209 cmValue sysrootLink = this->Makefile->GetDefinition("CMAKE_SYSROOT_LINK");
210 cmValue rootPath = this->Makefile->GetDefinition("CMAKE_FIND_ROOT_PATH");
211 const bool noSysroot = !cmNonempty(sysroot);
212 const bool noCompileSysroot = !cmNonempty(sysrootCompile);
213 const bool noLinkSysroot = !cmNonempty(sysrootLink);
214 const bool noRootPath = !cmNonempty(rootPath);
215 if (noSysroot && noCompileSysroot && noLinkSysroot && noRootPath) {
216 return;
217 }
218
219 // Construct the list of path roots with no trailing slashes.
220 std::vector<std::string> roots;
221 if (rootPath) {
222 cmExpandList(*rootPath, roots);
223 }
224 if (sysrootCompile) {
225 roots.emplace_back(*sysrootCompile);
226 }
227 if (sysrootLink) {
228 roots.emplace_back(*sysrootLink);
229 }
230 if (sysroot) {
231 roots.emplace_back(*sysroot);
232 }
233 for (std::string& r : roots) {
234 cmSystemTools::ConvertToUnixSlashes(r);
235 }
236
237 cmValue stagePrefix = this->Makefile->GetDefinition("CMAKE_STAGING_PREFIX");
238
239 // Copy the original set of unrooted paths.
240 std::vector<std::string> unrootedPaths = paths;
241 paths.clear();
242
243 for (std::string const& r : roots) {
244 for (std::string const& up : unrootedPaths) {
245 // Place the unrooted path under the current root if it is not
246 // already inside. Skip the unrooted path if it is relative to
247 // a user home directory or is empty.
248 std::string rootedDir;
249 if (cmSystemTools::IsSubDirectory(up, r) ||
250 (stagePrefix && cmSystemTools::IsSubDirectory(up, *stagePrefix))) {
251 rootedDir = up;
252 } else if (!up.empty() && up[0] != '~') {
253 // Start with the new root.
254 rootedDir = cmStrCat(r, '/');
255
256 // Append the original path with its old root removed.
257 rootedDir += cmSystemTools::SplitPathRootComponent(up);
258 }
259
260 // Store the new path.
261 paths.push_back(rootedDir);
262 }
263 }
264
265 // If searching both rooted and unrooted paths add the original
266 // paths again.
267 if (this->FindRootPathMode == RootPathModeBoth) {
268 cm::append(paths, unrootedPaths);
269 }
270 }
271
GetIgnoredPaths(std::vector<std::string> & ignore)272 void cmFindCommon::GetIgnoredPaths(std::vector<std::string>& ignore)
273 {
274 // null-terminated list of paths.
275 static const char* paths[] = { "CMAKE_SYSTEM_IGNORE_PATH",
276 "CMAKE_IGNORE_PATH", nullptr };
277
278 // Construct the list of path roots with no trailing slashes.
279 for (const char** pathName = paths; *pathName; ++pathName) {
280 // Get the list of paths to ignore from the variable.
281 this->Makefile->GetDefExpandList(*pathName, ignore);
282 }
283
284 for (std::string& i : ignore) {
285 cmSystemTools::ConvertToUnixSlashes(i);
286 }
287 }
288
GetIgnoredPaths(std::set<std::string> & ignore)289 void cmFindCommon::GetIgnoredPaths(std::set<std::string>& ignore)
290 {
291 std::vector<std::string> ignoreVec;
292 this->GetIgnoredPaths(ignoreVec);
293 ignore.insert(ignoreVec.begin(), ignoreVec.end());
294 }
295
CheckCommonArgument(std::string const & arg)296 bool cmFindCommon::CheckCommonArgument(std::string const& arg)
297 {
298 if (arg == "NO_DEFAULT_PATH") {
299 this->NoDefaultPath = true;
300 } else if (arg == "NO_PACKAGE_ROOT_PATH") {
301 this->NoPackageRootPath = true;
302 } else if (arg == "NO_CMAKE_PATH") {
303 this->NoCMakePath = true;
304 } else if (arg == "NO_CMAKE_ENVIRONMENT_PATH") {
305 this->NoCMakeEnvironmentPath = true;
306 } else if (arg == "NO_SYSTEM_ENVIRONMENT_PATH") {
307 this->NoSystemEnvironmentPath = true;
308 } else if (arg == "NO_CMAKE_SYSTEM_PATH") {
309 this->NoCMakeSystemPath = true;
310 } else if (arg == "NO_CMAKE_FIND_ROOT_PATH") {
311 this->FindRootPathMode = RootPathModeNever;
312 } else if (arg == "ONLY_CMAKE_FIND_ROOT_PATH") {
313 this->FindRootPathMode = RootPathModeOnly;
314 } else if (arg == "CMAKE_FIND_ROOT_PATH_BOTH") {
315 this->FindRootPathMode = RootPathModeBoth;
316 } else {
317 // The argument is not one of the above.
318 return false;
319 }
320
321 // The argument is one of the above.
322 return true;
323 }
324
AddPathSuffix(std::string const & arg)325 void cmFindCommon::AddPathSuffix(std::string const& arg)
326 {
327 std::string suffix = arg;
328
329 // Strip leading and trailing slashes.
330 if (suffix.empty()) {
331 return;
332 }
333 if (suffix.front() == '/') {
334 suffix = suffix.substr(1);
335 }
336 if (suffix.empty()) {
337 return;
338 }
339 if (suffix.back() == '/') {
340 suffix = suffix.substr(0, suffix.size() - 1);
341 }
342 if (suffix.empty()) {
343 return;
344 }
345
346 // Store the suffix.
347 this->SearchPathSuffixes.push_back(std::move(suffix));
348 }
349
AddTrailingSlash(std::string & s)350 void AddTrailingSlash(std::string& s)
351 {
352 if (!s.empty() && s.back() != '/') {
353 s += '/';
354 }
355 }
ComputeFinalPaths()356 void cmFindCommon::ComputeFinalPaths()
357 {
358 // Filter out ignored paths from the prefix list
359 std::set<std::string> ignored;
360 this->GetIgnoredPaths(ignored);
361
362 // Combine the separate path types, filtering out ignores
363 this->SearchPaths.clear();
364 std::vector<PathLabel>& allLabels = this->PathGroupLabelMap[PathGroup::All];
365 for (PathLabel const& l : allLabels) {
366 this->LabeledPaths[l].ExtractWithout(ignored, this->SearchPaths);
367 }
368
369 // Expand list of paths inside all search roots.
370 this->RerootPaths(this->SearchPaths);
371
372 // Add a trailing slash to all paths to aid the search process.
373 std::for_each(this->SearchPaths.begin(), this->SearchPaths.end(),
374 &AddTrailingSlash);
375 }
376