/src/CMake/Source/cmFindBase.cxx
Line | Count | Source |
1 | | /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
2 | | file LICENSE.rst or https://cmake.org/licensing for details. */ |
3 | | #include "cmFindBase.h" |
4 | | |
5 | | #include <algorithm> |
6 | | #include <cstddef> |
7 | | #include <deque> |
8 | | #include <iterator> |
9 | | #include <map> |
10 | | #include <memory> |
11 | | #include <utility> |
12 | | |
13 | | #include <cm/optional> |
14 | | #include <cmext/algorithm> |
15 | | #include <cmext/string_view> |
16 | | |
17 | | #include "cmCMakePath.h" |
18 | | #include "cmConfigureLog.h" |
19 | | #include "cmExecutionStatus.h" |
20 | | #include "cmList.h" |
21 | | #include "cmListFileCache.h" |
22 | | #include "cmMakefile.h" |
23 | | #include "cmMessageType.h" |
24 | | #include "cmPolicies.h" |
25 | | #include "cmRange.h" |
26 | | #include "cmSearchPath.h" |
27 | | #include "cmState.h" |
28 | | #include "cmStateTypes.h" |
29 | | #include "cmStringAlgorithms.h" |
30 | | #include "cmSystemTools.h" |
31 | | #include "cmValue.h" |
32 | | #include "cmWindowsRegistry.h" |
33 | | #include "cmake.h" |
34 | | |
35 | | cmFindBase::cmFindBase(std::string findCommandName, cmExecutionStatus& status) |
36 | 0 | : cmFindCommon(status) |
37 | 0 | , FindCommandName(std::move(findCommandName)) |
38 | 0 | { |
39 | 0 | } |
40 | | |
41 | | cmFindBase::~cmFindBase() |
42 | 0 | { |
43 | 0 | if (this->DebugState) { |
44 | 0 | this->DebugState->Write(); |
45 | 0 | } |
46 | 0 | } |
47 | | |
48 | | bool cmFindBase::ParseArguments(std::vector<std::string> const& argsIn) |
49 | 0 | { |
50 | 0 | if (argsIn.size() < 2) { |
51 | 0 | this->SetError("called with incorrect number of arguments"); |
52 | 0 | return false; |
53 | 0 | } |
54 | | |
55 | | // copy argsIn into args so it can be modified, |
56 | | // in the process extract the DOC "documentation" |
57 | | // and handle options NO_CACHE and ENV |
58 | 0 | size_t size = argsIn.size(); |
59 | 0 | std::vector<std::string> args; |
60 | 0 | bool foundDoc = false; |
61 | 0 | for (unsigned int j = 0; j < size; ++j) { |
62 | 0 | if (foundDoc || argsIn[j] != "DOC") { |
63 | 0 | if (argsIn[j] == "NO_CACHE") { |
64 | 0 | this->StoreResultInCache = false; |
65 | 0 | } else if (argsIn[j] == "ENV") { |
66 | 0 | if (j + 1 < size) { |
67 | 0 | j++; |
68 | 0 | std::vector<std::string> p = |
69 | 0 | cmSystemTools::GetEnvPathNormalized(argsIn[j]); |
70 | 0 | std::move(p.begin(), p.end(), std::back_inserter(args)); |
71 | 0 | } |
72 | 0 | } else { |
73 | 0 | args.push_back(argsIn[j]); |
74 | 0 | } |
75 | 0 | } else { |
76 | 0 | if (j + 1 < size) { |
77 | 0 | foundDoc = true; |
78 | 0 | this->VariableDocumentation = argsIn[j + 1]; |
79 | 0 | j++; |
80 | 0 | if (j >= size) { |
81 | 0 | break; |
82 | 0 | } |
83 | 0 | } |
84 | 0 | } |
85 | 0 | } |
86 | 0 | if (args.size() < 2) { |
87 | 0 | this->SetError("called with incorrect number of arguments"); |
88 | 0 | return false; |
89 | 0 | } |
90 | 0 | this->VariableName = args[0]; |
91 | 0 | this->InitialState = this->GetInitialState(); |
92 | 0 | if (this->IsFound()) { |
93 | 0 | if (this->DebugState) { |
94 | 0 | this->DebugState->FoundAt( |
95 | 0 | *this->Makefile->GetDefinition(this->VariableName)); |
96 | 0 | } |
97 | 0 | return true; |
98 | 0 | } |
99 | | |
100 | | // Find what search path locations have been enabled/disable |
101 | 0 | this->SelectDefaultSearchModes(); |
102 | | |
103 | | // Find the current root path mode. |
104 | 0 | this->SelectDefaultRootPathMode(); |
105 | | |
106 | | // Find the current bundle/framework search policy. |
107 | 0 | this->SelectDefaultMacMode(); |
108 | |
|
109 | 0 | bool newStyle = false; |
110 | 0 | bool haveRequiredOrOptional = false; |
111 | 0 | enum Doing |
112 | 0 | { |
113 | 0 | DoingNone, |
114 | 0 | DoingNames, |
115 | 0 | DoingPaths, |
116 | 0 | DoingPathSuffixes, |
117 | 0 | DoingHints |
118 | 0 | }; |
119 | 0 | Doing doing = DoingNames; // assume it starts with a name |
120 | 0 | for (unsigned int j = 1; j < args.size(); ++j) { |
121 | 0 | if (args[j] == "NAMES") { |
122 | 0 | doing = DoingNames; |
123 | 0 | newStyle = true; |
124 | 0 | } else if (args[j] == "PATHS") { |
125 | 0 | doing = DoingPaths; |
126 | 0 | newStyle = true; |
127 | 0 | } else if (args[j] == "HINTS") { |
128 | 0 | doing = DoingHints; |
129 | 0 | newStyle = true; |
130 | 0 | } else if (args[j] == "PATH_SUFFIXES") { |
131 | 0 | doing = DoingPathSuffixes; |
132 | 0 | newStyle = true; |
133 | 0 | } else if (args[j] == "NAMES_PER_DIR") { |
134 | 0 | doing = DoingNone; |
135 | 0 | if (this->NamesPerDirAllowed) { |
136 | 0 | this->NamesPerDir = true; |
137 | 0 | } else { |
138 | 0 | this->SetError("does not support NAMES_PER_DIR"); |
139 | 0 | return false; |
140 | 0 | } |
141 | 0 | } else if (args[j] == "NO_SYSTEM_PATH") { |
142 | 0 | doing = DoingNone; |
143 | 0 | this->NoDefaultPath = true; |
144 | 0 | } else if (args[j] == "REQUIRED") { |
145 | 0 | doing = DoingNone; |
146 | 0 | if (haveRequiredOrOptional && !this->Required) { |
147 | 0 | this->SetError("cannot be both REQUIRED and OPTIONAL"); |
148 | 0 | return false; |
149 | 0 | } |
150 | 0 | this->Required = true; |
151 | 0 | newStyle = true; |
152 | 0 | haveRequiredOrOptional = true; |
153 | 0 | } else if (args[j] == "OPTIONAL") { |
154 | 0 | doing = DoingNone; |
155 | 0 | if (haveRequiredOrOptional && this->Required) { |
156 | 0 | this->SetError("cannot be both REQUIRED and OPTIONAL"); |
157 | 0 | return false; |
158 | 0 | } |
159 | 0 | newStyle = true; |
160 | 0 | haveRequiredOrOptional = true; |
161 | 0 | } else if (args[j] == "REGISTRY_VIEW") { |
162 | 0 | if (++j == args.size()) { |
163 | 0 | this->SetError("missing required argument for REGISTRY_VIEW"); |
164 | 0 | return false; |
165 | 0 | } |
166 | 0 | auto view = cmWindowsRegistry::ToView(args[j]); |
167 | 0 | if (view) { |
168 | 0 | this->RegistryView = *view; |
169 | 0 | } else { |
170 | 0 | this->SetError( |
171 | 0 | cmStrCat("given invalid value for REGISTRY_VIEW: ", args[j])); |
172 | 0 | return false; |
173 | 0 | } |
174 | 0 | } else if (args[j] == "VALIDATOR") { |
175 | 0 | if (++j == args.size()) { |
176 | 0 | this->SetError("missing required argument for VALIDATOR"); |
177 | 0 | return false; |
178 | 0 | } |
179 | 0 | auto command = this->Makefile->GetState()->GetCommand(args[j]); |
180 | 0 | if (!command) { |
181 | 0 | this->SetError(cmStrCat( |
182 | 0 | "command specified for VALIDATOR is undefined: ", args[j], '.')); |
183 | 0 | return false; |
184 | 0 | } |
185 | | // ensure a macro is not specified as validator |
186 | 0 | auto const& validatorName = args[j]; |
187 | 0 | cmList macros{ this->Makefile->GetProperty("MACROS") }; |
188 | 0 | if (std::find_if(macros.begin(), macros.end(), |
189 | 0 | [&validatorName](std::string const& item) { |
190 | 0 | return cmSystemTools::Strucmp(validatorName.c_str(), |
191 | 0 | item.c_str()) == 0; |
192 | 0 | }) != macros.end()) { |
193 | 0 | this->SetError(cmStrCat( |
194 | 0 | "command specified for VALIDATOR is not a function: ", args[j], |
195 | 0 | '.')); |
196 | 0 | return false; |
197 | 0 | } |
198 | 0 | this->ValidatorName = args[j]; |
199 | 0 | } else if (this->CheckCommonArgument(args[j])) { |
200 | 0 | doing = DoingNone; |
201 | 0 | } else { |
202 | | // Some common arguments were accidentally supported by CMake |
203 | | // 2.4 and 2.6.0 in the short-hand form of the command, so we |
204 | | // must support it even though it is not documented. |
205 | 0 | if (doing == DoingNames) { |
206 | 0 | this->Names.push_back(args[j]); |
207 | 0 | } else if (doing == DoingPaths) { |
208 | 0 | this->UserGuessArgs.push_back(args[j]); |
209 | 0 | } else if (doing == DoingHints) { |
210 | 0 | this->UserHintsArgs.push_back(args[j]); |
211 | 0 | } else if (doing == DoingPathSuffixes) { |
212 | 0 | this->AddPathSuffix(args[j]); |
213 | 0 | } |
214 | 0 | } |
215 | 0 | } |
216 | | |
217 | 0 | if (!haveRequiredOrOptional) { |
218 | 0 | this->Required = this->Makefile->IsOn("CMAKE_FIND_REQUIRED"); |
219 | 0 | } |
220 | |
|
221 | 0 | if (this->VariableDocumentation.empty()) { |
222 | 0 | this->VariableDocumentation = "Where can "; |
223 | 0 | if (this->Names.empty()) { |
224 | 0 | this->VariableDocumentation += "the (unknown) library be found"; |
225 | 0 | } else if (this->Names.size() == 1) { |
226 | 0 | this->VariableDocumentation += |
227 | 0 | cmStrCat("the ", this->Names.front(), " library be found"); |
228 | 0 | } else { |
229 | 0 | this->VariableDocumentation += cmStrCat( |
230 | 0 | "one of the ", cmJoin(cmMakeRange(this->Names).retreat(1), ", "), |
231 | 0 | " or ", this->Names.back(), " libraries be found"); |
232 | 0 | } |
233 | 0 | } |
234 | | |
235 | | // look for old style |
236 | | // FIND_*(VAR name path1 path2 ...) |
237 | 0 | if (!newStyle && !this->Names.empty()) { |
238 | | // All the short-hand arguments have been recorded as names. |
239 | 0 | std::vector<std::string> shortArgs = this->Names; |
240 | 0 | this->Names.clear(); // clear out any values in Names |
241 | 0 | this->Names.push_back(shortArgs[0]); |
242 | 0 | cm::append(this->UserGuessArgs, shortArgs.begin() + 1, shortArgs.end()); |
243 | 0 | } |
244 | 0 | this->ExpandPaths(); |
245 | |
|
246 | 0 | this->ComputeFinalPaths(IgnorePaths::Yes); |
247 | |
|
248 | 0 | return true; |
249 | 0 | } |
250 | | |
251 | | bool cmFindBase::Validate(std::string const& path) const |
252 | 0 | { |
253 | 0 | if (this->ValidatorName.empty()) { |
254 | 0 | return true; |
255 | 0 | } |
256 | | |
257 | | // The validator command will be executed in an isolated scope. |
258 | 0 | cmMakefile::ScopePushPop varScope(this->Makefile); |
259 | 0 | cmMakefile::PolicyPushPop polScope(this->Makefile); |
260 | 0 | static_cast<void>(varScope); |
261 | 0 | static_cast<void>(polScope); |
262 | |
|
263 | 0 | auto resultName = |
264 | 0 | cmStrCat("CMAKE_"_s, cmSystemTools::UpperCase(this->FindCommandName), |
265 | 0 | "_VALIDATOR_STATUS"_s); |
266 | |
|
267 | 0 | this->Makefile->AddDefinitionBool(resultName, true); |
268 | |
|
269 | 0 | cmListFileFunction validator( |
270 | 0 | this->ValidatorName, 0, 0, |
271 | 0 | { cmListFileArgument(resultName, cmListFileArgument::Unquoted, 0), |
272 | 0 | cmListFileArgument(path, cmListFileArgument::Quoted, 0) }); |
273 | 0 | cmExecutionStatus status(*this->Makefile); |
274 | |
|
275 | 0 | if (this->Makefile->ExecuteCommand(validator, status)) { |
276 | 0 | return this->Makefile->GetDefinition(resultName).IsOn(); |
277 | 0 | } |
278 | 0 | return false; |
279 | 0 | } |
280 | | |
281 | | void cmFindBase::ExpandPaths() |
282 | 0 | { |
283 | 0 | if (!this->NoDefaultPath) { |
284 | 0 | if (!this->NoPackageRootPath) { |
285 | 0 | this->FillPackageRootPath(); |
286 | 0 | } |
287 | 0 | if (!this->NoCMakePath) { |
288 | 0 | this->FillCMakeVariablePath(); |
289 | 0 | } |
290 | 0 | if (!this->NoCMakeEnvironmentPath) { |
291 | 0 | this->FillCMakeEnvironmentPath(); |
292 | 0 | } |
293 | 0 | } |
294 | 0 | this->FillUserHintsPath(); |
295 | 0 | if (!this->NoDefaultPath) { |
296 | 0 | if (!this->NoSystemEnvironmentPath) { |
297 | 0 | this->FillSystemEnvironmentPath(); |
298 | 0 | } |
299 | 0 | if (!this->NoCMakeSystemPath) { |
300 | 0 | this->FillCMakeSystemVariablePath(); |
301 | 0 | } |
302 | 0 | } |
303 | 0 | this->FillUserGuessPath(); |
304 | 0 | } |
305 | | |
306 | | void cmFindBase::FillCMakeEnvironmentPath() |
307 | 0 | { |
308 | 0 | cmSearchPath& paths = this->LabeledPaths[PathLabel::CMakeEnvironment]; |
309 | | |
310 | | // Add CMAKE_*_PATH environment variables |
311 | 0 | std::string var = cmStrCat("CMAKE_", this->CMakePathName, "_PATH"); |
312 | 0 | paths.AddEnvPrefixPath("CMAKE_PREFIX_PATH"); |
313 | 0 | paths.AddEnvPath(var); |
314 | |
|
315 | 0 | if (this->CMakePathName == "PROGRAM") { |
316 | 0 | paths.AddEnvPath("CMAKE_APPBUNDLE_PATH"); |
317 | 0 | } else { |
318 | 0 | paths.AddEnvPath("CMAKE_FRAMEWORK_PATH"); |
319 | 0 | } |
320 | 0 | paths.AddSuffixes(this->SearchPathSuffixes); |
321 | 0 | } |
322 | | |
323 | | void cmFindBase::FillPackageRootPath() |
324 | 0 | { |
325 | 0 | cmSearchPath& paths = this->LabeledPaths[PathLabel::PackageRoot]; |
326 | | |
327 | | // Add the PACKAGE_ROOT_PATH from each enclosing find_package call. |
328 | 0 | for (std::vector<std::string> const& pkgPaths : |
329 | 0 | cmReverseRange(this->Makefile->FindPackageRootPathStack)) { |
330 | 0 | paths.AddPrefixPaths(pkgPaths); |
331 | 0 | } |
332 | |
|
333 | 0 | paths.AddSuffixes(this->SearchPathSuffixes); |
334 | 0 | } |
335 | | |
336 | | void cmFindBase::FillCMakeVariablePath() |
337 | 0 | { |
338 | 0 | cmSearchPath& paths = this->LabeledPaths[PathLabel::CMake]; |
339 | | |
340 | | // Add CMake variables of the same name as the previous environment |
341 | | // variables CMAKE_*_PATH to be used most of the time with -D |
342 | | // command line options |
343 | 0 | std::string var = cmStrCat("CMAKE_", this->CMakePathName, "_PATH"); |
344 | 0 | paths.AddCMakePrefixPath("CMAKE_PREFIX_PATH"); |
345 | 0 | paths.AddCMakePath(var); |
346 | |
|
347 | 0 | if (this->CMakePathName == "PROGRAM") { |
348 | 0 | paths.AddCMakePath("CMAKE_APPBUNDLE_PATH"); |
349 | 0 | } else { |
350 | 0 | paths.AddCMakePath("CMAKE_FRAMEWORK_PATH"); |
351 | 0 | } |
352 | 0 | paths.AddSuffixes(this->SearchPathSuffixes); |
353 | 0 | } |
354 | | |
355 | | void cmFindBase::FillSystemEnvironmentPath() |
356 | 0 | { |
357 | 0 | cmSearchPath& paths = this->LabeledPaths[PathLabel::SystemEnvironment]; |
358 | | |
359 | | // Add LIB or INCLUDE |
360 | 0 | if (!this->EnvironmentPath.empty()) { |
361 | 0 | paths.AddEnvPath(this->EnvironmentPath); |
362 | 0 | } |
363 | | // Add PATH |
364 | 0 | paths.AddEnvPath("PATH"); |
365 | 0 | paths.AddSuffixes(this->SearchPathSuffixes); |
366 | 0 | } |
367 | | |
368 | | namespace { |
369 | | struct entry_to_remove |
370 | | { |
371 | | entry_to_remove(std::string const& name, cmMakefile* makefile) |
372 | 0 | { |
373 | 0 | if (cmValue to_skip = makefile->GetDefinition( |
374 | 0 | cmStrCat("_CMAKE_SYSTEM_PREFIX_PATH_", name, "_PREFIX_COUNT"))) { |
375 | 0 | cmStrToLong(*to_skip, &count); |
376 | 0 | } |
377 | 0 | if (cmValue prefix_value = makefile->GetDefinition( |
378 | 0 | cmStrCat("_CMAKE_SYSTEM_PREFIX_PATH_", name, "_PREFIX_VALUE"))) { |
379 | 0 | value = *prefix_value; |
380 | 0 | } |
381 | 0 | } |
382 | 0 | bool valid() const { return count > 0 && !value.empty(); } |
383 | | |
384 | | void remove_self(std::vector<std::string>& entries) const |
385 | 0 | { |
386 | 0 | if (this->valid()) { |
387 | 0 | long to_skip = this->count; |
388 | 0 | size_t index_to_remove = 0; |
389 | 0 | for (auto const& path : entries) { |
390 | 0 | if (path == this->value && --to_skip == 0) { |
391 | 0 | break; |
392 | 0 | } |
393 | 0 | ++index_to_remove; |
394 | 0 | } |
395 | 0 | if (index_to_remove < entries.size() && to_skip == 0) { |
396 | 0 | entries.erase(entries.begin() + index_to_remove); |
397 | 0 | } |
398 | 0 | } |
399 | 0 | } |
400 | | |
401 | | long count = -1; |
402 | | std::string value; |
403 | | }; |
404 | | } |
405 | | |
406 | | void cmFindBase::FillCMakeSystemVariablePath() |
407 | 0 | { |
408 | 0 | cmSearchPath& paths = this->LabeledPaths[PathLabel::CMakeSystem]; |
409 | |
|
410 | 0 | bool const install_prefix_in_list = |
411 | 0 | !this->Makefile->IsOn("CMAKE_FIND_NO_INSTALL_PREFIX"); |
412 | 0 | bool const remove_install_prefix = this->NoCMakeInstallPath; |
413 | 0 | bool const add_install_prefix = !this->NoCMakeInstallPath && |
414 | 0 | this->Makefile->IsDefinitionSet("CMAKE_FIND_USE_INSTALL_PREFIX"); |
415 | | |
416 | | // We have 3 possible states for `CMAKE_SYSTEM_PREFIX_PATH` and |
417 | | // `CMAKE_INSTALL_PREFIX`. |
418 | | // Either we need to remove `CMAKE_INSTALL_PREFIX`, add |
419 | | // `CMAKE_INSTALL_PREFIX`, or do nothing. |
420 | | // |
421 | | // When we need to remove `CMAKE_INSTALL_PREFIX` we remove the Nth occurrence |
422 | | // of `CMAKE_INSTALL_PREFIX` from `CMAKE_SYSTEM_PREFIX_PATH`, where `N` is |
423 | | // computed by `CMakeSystemSpecificInformation.cmake` while constructing |
424 | | // `CMAKE_SYSTEM_PREFIX_PATH`. This ensures that if projects / toolchains |
425 | | // have removed `CMAKE_INSTALL_PREFIX` from the list, we don't remove |
426 | | // some other entry by mistake ( likewise for `CMAKE_STAGING_PREFIX` ) |
427 | 0 | entry_to_remove install_entry("INSTALL", this->Makefile); |
428 | 0 | entry_to_remove staging_entry("STAGING", this->Makefile); |
429 | |
|
430 | 0 | if (remove_install_prefix && install_prefix_in_list && |
431 | 0 | (install_entry.valid() || staging_entry.valid())) { |
432 | 0 | cmValue prefix_paths = |
433 | 0 | this->Makefile->GetDefinition("CMAKE_SYSTEM_PREFIX_PATH"); |
434 | | |
435 | | // remove entries from CMAKE_SYSTEM_PREFIX_PATH |
436 | 0 | cmList expanded{ *prefix_paths }; |
437 | 0 | install_entry.remove_self(expanded); |
438 | 0 | staging_entry.remove_self(expanded); |
439 | 0 | for (std::string& p : expanded) { |
440 | 0 | p = cmSystemTools::CollapseFullPath( |
441 | 0 | p, this->Makefile->GetCurrentSourceDirectory()); |
442 | 0 | } |
443 | 0 | paths.AddPrefixPaths(expanded); |
444 | 0 | } else if (add_install_prefix && !install_prefix_in_list) { |
445 | 0 | paths.AddCMakePrefixPath("CMAKE_INSTALL_PREFIX"); |
446 | 0 | paths.AddCMakePrefixPath("CMAKE_STAGING_PREFIX"); |
447 | 0 | paths.AddCMakePrefixPath("CMAKE_SYSTEM_PREFIX_PATH"); |
448 | 0 | } else { |
449 | | // Otherwise the current setup of `CMAKE_SYSTEM_PREFIX_PATH` is correct |
450 | 0 | paths.AddCMakePrefixPath("CMAKE_SYSTEM_PREFIX_PATH"); |
451 | 0 | } |
452 | |
|
453 | 0 | std::string var = cmStrCat("CMAKE_SYSTEM_", this->CMakePathName, "_PATH"); |
454 | 0 | paths.AddCMakePath(var); |
455 | |
|
456 | 0 | if (this->CMakePathName == "PROGRAM") { |
457 | 0 | paths.AddCMakePath("CMAKE_SYSTEM_APPBUNDLE_PATH"); |
458 | 0 | } else { |
459 | 0 | paths.AddCMakePath("CMAKE_SYSTEM_FRAMEWORK_PATH"); |
460 | 0 | } |
461 | 0 | paths.AddSuffixes(this->SearchPathSuffixes); |
462 | 0 | } |
463 | | |
464 | | void cmFindBase::FillUserHintsPath() |
465 | 0 | { |
466 | 0 | cmSearchPath& paths = this->LabeledPaths[PathLabel::Hints]; |
467 | |
|
468 | 0 | for (std::string const& p : this->UserHintsArgs) { |
469 | 0 | paths.AddUserPath(p); |
470 | 0 | } |
471 | 0 | paths.AddSuffixes(this->SearchPathSuffixes); |
472 | 0 | } |
473 | | |
474 | | void cmFindBase::FillUserGuessPath() |
475 | 0 | { |
476 | 0 | cmSearchPath& paths = this->LabeledPaths[PathLabel::Guess]; |
477 | |
|
478 | 0 | for (std::string const& p : this->UserGuessArgs) { |
479 | 0 | paths.AddUserPath(p); |
480 | 0 | } |
481 | 0 | paths.AddSuffixes(this->SearchPathSuffixes); |
482 | 0 | } |
483 | | |
484 | | cmFindBase::FindState cmFindBase::GetInitialState() |
485 | 0 | { |
486 | 0 | if (cmValue value = this->Makefile->GetDefinition(this->VariableName)) { |
487 | 0 | cmState* state = this->Makefile->GetState(); |
488 | 0 | cmValue cacheEntry = state->GetCacheEntryValue(this->VariableName); |
489 | 0 | bool found = !cmIsNOTFOUND(*value); |
490 | 0 | bool cached = cacheEntry != nullptr; |
491 | 0 | auto cacheType = cached ? state->GetCacheEntryType(this->VariableName) |
492 | 0 | : cmStateEnums::UNINITIALIZED; |
493 | |
|
494 | 0 | if (cached && cacheType != cmStateEnums::UNINITIALIZED) { |
495 | 0 | this->VariableType = cacheType; |
496 | 0 | if (auto const& hs = |
497 | 0 | state->GetCacheEntryProperty(this->VariableName, "HELPSTRING")) { |
498 | 0 | this->VariableDocumentation = *hs; |
499 | 0 | } |
500 | 0 | } |
501 | |
|
502 | 0 | if (found) { |
503 | | // If the user specifies the entry on the command line without a |
504 | | // type we should add the type and docstring but keep the |
505 | | // original value. Tell the subclass implementations to do |
506 | | // this. |
507 | 0 | if (cached && cacheType == cmStateEnums::UNINITIALIZED) { |
508 | 0 | this->AlreadyInCacheWithoutMetaInfo = true; |
509 | 0 | } |
510 | 0 | return FindState::Found; |
511 | 0 | } |
512 | 0 | return FindState::NotFound; |
513 | 0 | } |
514 | 0 | return FindState::Undefined; |
515 | 0 | } |
516 | | |
517 | | bool cmFindBase::IsFound() const |
518 | 0 | { |
519 | 0 | return this->InitialState == FindState::Found; |
520 | 0 | } |
521 | | |
522 | | bool cmFindBase::IsDefined() const |
523 | 0 | { |
524 | 0 | return this->InitialState != FindState::Undefined; |
525 | 0 | } |
526 | | |
527 | | void cmFindBase::NormalizeFindResult() |
528 | 0 | { |
529 | 0 | std::string foundValue; |
530 | 0 | if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0125) == |
531 | 0 | cmPolicies::NEW) { |
532 | | // ensure the path returned by find_* command is absolute |
533 | 0 | auto const& existingValue = |
534 | 0 | this->Makefile->GetDefinition(this->VariableName); |
535 | 0 | std::string value; |
536 | 0 | if (!existingValue->empty()) { |
537 | 0 | value = |
538 | 0 | cmCMakePath(*existingValue, cmCMakePath::auto_format) |
539 | 0 | .Absolute(cmCMakePath( |
540 | 0 | this->Makefile->GetCMakeInstance()->GetCMakeWorkingDirectory())) |
541 | 0 | .Normal() |
542 | 0 | .GenericString(); |
543 | 0 | if (!cmSystemTools::FileExists(value, false)) { |
544 | 0 | value = *existingValue; |
545 | 0 | } |
546 | 0 | } |
547 | |
|
548 | 0 | foundValue = value; |
549 | 0 | if (this->StoreResultInCache) { |
550 | | // If the user specifies the entry on the command line without a |
551 | | // type we should add the type and docstring but keep the original |
552 | | // value. |
553 | 0 | if (value != *existingValue || this->AlreadyInCacheWithoutMetaInfo) { |
554 | 0 | this->Makefile->GetCMakeInstance()->AddCacheEntry( |
555 | 0 | this->VariableName, value, this->VariableDocumentation, |
556 | 0 | this->VariableType); |
557 | 0 | if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0126) == |
558 | 0 | cmPolicies::NEW) { |
559 | 0 | if (this->Makefile->IsNormalDefinitionSet(this->VariableName)) { |
560 | 0 | this->Makefile->AddDefinition(this->VariableName, value); |
561 | 0 | } |
562 | 0 | } else { |
563 | | // if there was a definition then remove it |
564 | | // This is required to ensure same behavior as |
565 | | // cmMakefile::AddCacheDefinition. |
566 | 0 | this->Makefile->RemoveDefinition(this->VariableName); |
567 | 0 | } |
568 | 0 | } |
569 | 0 | } else { |
570 | | // ensure a normal variable is defined. |
571 | 0 | this->Makefile->AddDefinition(this->VariableName, value); |
572 | 0 | } |
573 | 0 | } else { |
574 | | // If the user specifies the entry on the command line without a |
575 | | // type we should add the type and docstring but keep the original |
576 | | // value. |
577 | 0 | if (this->StoreResultInCache) { |
578 | 0 | auto const& existingValue = |
579 | 0 | this->Makefile->GetCMakeInstance()->GetCacheDefinition( |
580 | 0 | this->VariableName); |
581 | 0 | foundValue = *existingValue; |
582 | 0 | if (this->AlreadyInCacheWithoutMetaInfo) { |
583 | 0 | this->Makefile->AddCacheDefinition(this->VariableName, "", |
584 | 0 | this->VariableDocumentation, |
585 | 0 | this->VariableType); |
586 | 0 | if (this->Makefile->GetPolicyStatus(cmPolicies::CMP0126) == |
587 | 0 | cmPolicies::NEW && |
588 | 0 | this->Makefile->IsNormalDefinitionSet(this->VariableName)) { |
589 | 0 | this->Makefile->AddDefinition(this->VariableName, *existingValue); |
590 | 0 | } |
591 | 0 | } |
592 | 0 | } else { |
593 | 0 | auto const& existingValue = |
594 | 0 | this->Makefile->GetSafeDefinition(this->VariableName); |
595 | 0 | foundValue = existingValue; |
596 | | // ensure a normal variable is defined. |
597 | 0 | this->Makefile->AddDefinition(this->VariableName, existingValue); |
598 | 0 | } |
599 | 0 | } |
600 | 0 | if (this->DebugState) { |
601 | 0 | this->DebugState->FoundAt(foundValue); |
602 | 0 | } |
603 | 0 | } |
604 | | |
605 | | void cmFindBase::StoreFindResult(std::string const& value) |
606 | 0 | { |
607 | 0 | bool force = |
608 | 0 | this->Makefile->GetPolicyStatus(cmPolicies::CMP0125) == cmPolicies::NEW; |
609 | 0 | bool updateNormalVariable = |
610 | 0 | this->Makefile->GetPolicyStatus(cmPolicies::CMP0126) == cmPolicies::NEW; |
611 | |
|
612 | 0 | if (!value.empty()) { |
613 | 0 | if (this->StoreResultInCache) { |
614 | 0 | this->Makefile->AddCacheDefinition(this->VariableName, value, |
615 | 0 | this->VariableDocumentation, |
616 | 0 | this->VariableType, force); |
617 | 0 | if (updateNormalVariable && |
618 | 0 | this->Makefile->IsNormalDefinitionSet(this->VariableName)) { |
619 | 0 | this->Makefile->AddDefinition(this->VariableName, value); |
620 | 0 | } |
621 | 0 | } else { |
622 | 0 | this->Makefile->AddDefinition(this->VariableName, value); |
623 | 0 | } |
624 | |
|
625 | 0 | if (this->DebugState) { |
626 | 0 | this->DebugState->FoundAt(value); |
627 | 0 | } |
628 | |
|
629 | 0 | return; |
630 | 0 | } |
631 | | |
632 | 0 | auto notFound = cmStrCat(this->VariableName, "-NOTFOUND"); |
633 | 0 | if (this->StoreResultInCache) { |
634 | 0 | this->Makefile->AddCacheDefinition(this->VariableName, notFound, |
635 | 0 | this->VariableDocumentation, |
636 | 0 | this->VariableType, force); |
637 | 0 | if (updateNormalVariable && |
638 | 0 | this->Makefile->IsNormalDefinitionSet(this->VariableName)) { |
639 | 0 | this->Makefile->AddDefinition(this->VariableName, notFound); |
640 | 0 | } |
641 | 0 | } else { |
642 | 0 | this->Makefile->AddDefinition(this->VariableName, notFound); |
643 | 0 | } |
644 | |
|
645 | 0 | if (this->Required) { |
646 | 0 | this->Makefile->IssueMessage( |
647 | 0 | MessageType::FATAL_ERROR, |
648 | 0 | cmStrCat("Could not find ", this->VariableName, " using the following ", |
649 | 0 | (this->FindCommandName == "find_file" || |
650 | 0 | this->FindCommandName == "find_path" |
651 | 0 | ? "files" |
652 | 0 | : "names"), |
653 | 0 | ": ", cmJoin(this->Names, ", "))); |
654 | 0 | cmSystemTools::SetFatalErrorOccurred(); |
655 | 0 | } |
656 | 0 | } |
657 | | |
658 | | cmFindBaseDebugState::cmFindBaseDebugState(cmFindBase const* findBase) |
659 | 0 | : cmFindCommonDebugState(findBase->FindCommandName, findBase) |
660 | 0 | , FindBaseCommand(findBase) |
661 | 0 | { |
662 | 0 | } |
663 | | |
664 | 0 | cmFindBaseDebugState::~cmFindBaseDebugState() = default; |
665 | | |
666 | | void cmFindBaseDebugState::FoundAtImpl(std::string const& path, |
667 | | std::string regexName) |
668 | 0 | { |
669 | 0 | this->FoundSearchLocation = DebugLibState{ std::move(regexName), path }; |
670 | 0 | } |
671 | | |
672 | | void cmFindBaseDebugState::FailedAtImpl(std::string const& path, |
673 | | std::string regexName) |
674 | 0 | { |
675 | 0 | this->FailedSearchLocations.emplace_back(std::move(regexName), path); |
676 | 0 | } |
677 | | |
678 | | void cmFindBaseDebugState::WriteDebug() const |
679 | 0 | { |
680 | | // clang-format off |
681 | 0 | auto buffer = |
682 | 0 | cmStrCat( |
683 | 0 | this->CommandName, " called with the following settings:" |
684 | 0 | "\n VAR: ", this->FindBaseCommand->VariableName, |
685 | 0 | "\n NAMES: ", cmWrap('"', this->FindBaseCommand->Names, '"', "\n "), |
686 | 0 | "\n Documentation: ", this->FindBaseCommand->VariableDocumentation, |
687 | 0 | "\n Framework" |
688 | 0 | "\n Only Search Frameworks: ", this->FindBaseCommand->SearchFrameworkOnly, |
689 | 0 | "\n Search Frameworks Last: ", this->FindBaseCommand->SearchFrameworkLast, |
690 | 0 | "\n Search Frameworks First: ", this->FindBaseCommand->SearchFrameworkFirst, |
691 | 0 | "\n AppBundle" |
692 | 0 | "\n Only Search AppBundle: ", this->FindBaseCommand->SearchAppBundleOnly, |
693 | 0 | "\n Search AppBundle Last: ", this->FindBaseCommand->SearchAppBundleLast, |
694 | 0 | "\n Search AppBundle First: ", this->FindBaseCommand->SearchAppBundleFirst, |
695 | 0 | '\n' |
696 | 0 | ); |
697 | | // clang-format on |
698 | |
|
699 | 0 | if (this->FindCommand->NoDefaultPath) { |
700 | 0 | buffer += " NO_DEFAULT_PATH Enabled\n"; |
701 | 0 | } else { |
702 | | // clang-format off |
703 | 0 | buffer += cmStrCat( |
704 | 0 | " CMAKE_FIND_USE_CMAKE_PATH: ", !this->FindCommand->NoCMakePath, |
705 | 0 | "\n CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH: ", !this->FindCommand->NoCMakeEnvironmentPath, |
706 | 0 | "\n CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH: ", !this->FindCommand->NoSystemEnvironmentPath, |
707 | 0 | "\n CMAKE_FIND_USE_CMAKE_SYSTEM_PATH: ", !this->FindCommand->NoCMakeSystemPath, |
708 | 0 | "\n CMAKE_FIND_USE_INSTALL_PREFIX: ", !this->FindCommand->NoCMakeInstallPath, |
709 | 0 | '\n' |
710 | 0 | ); |
711 | | // clang-format on |
712 | 0 | } |
713 | |
|
714 | 0 | buffer += |
715 | 0 | cmStrCat(this->CommandName, " considered the following locations:\n"); |
716 | 0 | for (auto const& state : this->FailedSearchLocations) { |
717 | 0 | std::string path = cmStrCat(" ", state.path); |
718 | 0 | if (!state.regexName.empty()) { |
719 | 0 | path = cmStrCat(path, '/', state.regexName); |
720 | 0 | } |
721 | 0 | buffer += cmStrCat(path, '\n'); |
722 | 0 | } |
723 | |
|
724 | 0 | if (this->HasBeenFound()) { |
725 | 0 | buffer += cmStrCat("The item was found at\n ", |
726 | 0 | this->FoundSearchLocation.path, '\n'); |
727 | 0 | } else { |
728 | 0 | buffer += "The item was not found.\n"; |
729 | 0 | } |
730 | |
|
731 | 0 | this->FindCommand->DebugMessage(buffer); |
732 | 0 | } |
733 | | |
734 | | #ifndef CMAKE_BOOTSTRAP |
735 | | void cmFindBaseDebugState::WriteEvent(cmConfigureLog& log, |
736 | | cmMakefile const& mf) const |
737 | 0 | { |
738 | 0 | log.BeginEvent("find-v1", mf); |
739 | | |
740 | | // Mode is the Command name without the "find_" prefix |
741 | 0 | log.WriteValue("mode"_s, this->CommandName.substr(5)); |
742 | 0 | log.WriteValue("variable"_s, this->FindBaseCommand->VariableName); |
743 | 0 | log.WriteValue("description"_s, |
744 | 0 | this->FindBaseCommand->VariableDocumentation); |
745 | | |
746 | | // Yes, this needs to return a `std::string`. If it returns a `const char*`, |
747 | | // the `WriteValue` method prefers the `bool` overload. There's no overload |
748 | | // for a `cm::string_view` because the underlying JSON library doesn't |
749 | | // support `string_view` arguments itself. |
750 | 0 | auto search_opt_to_str = [](bool first, bool last, |
751 | 0 | bool only) -> std::string { |
752 | 0 | return first ? "FIRST" : (last ? "LAST" : (only ? "ONLY" : "NEVER")); |
753 | 0 | }; |
754 | 0 | log.BeginObject("settings"_s); |
755 | 0 | log.WriteValue("SearchFramework"_s, |
756 | 0 | search_opt_to_str(this->FindCommand->SearchFrameworkFirst, |
757 | 0 | this->FindCommand->SearchFrameworkLast, |
758 | 0 | this->FindCommand->SearchFrameworkOnly)); |
759 | 0 | log.WriteValue("SearchAppBundle"_s, |
760 | 0 | search_opt_to_str(this->FindCommand->SearchAppBundleFirst, |
761 | 0 | this->FindCommand->SearchAppBundleLast, |
762 | 0 | this->FindCommand->SearchAppBundleOnly)); |
763 | 0 | log.WriteValue("CMAKE_FIND_USE_CMAKE_PATH"_s, |
764 | 0 | !this->FindCommand->NoCMakePath); |
765 | 0 | log.WriteValue("CMAKE_FIND_USE_CMAKE_ENVIRONMENT_PATH"_s, |
766 | 0 | !this->FindCommand->NoCMakeEnvironmentPath); |
767 | 0 | log.WriteValue("CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH"_s, |
768 | 0 | !this->FindCommand->NoSystemEnvironmentPath); |
769 | 0 | log.WriteValue("CMAKE_FIND_USE_CMAKE_SYSTEM_PATH"_s, |
770 | 0 | !this->FindCommand->NoCMakeSystemPath); |
771 | 0 | log.WriteValue("CMAKE_FIND_USE_INSTALL_PREFIX"_s, |
772 | 0 | !this->FindCommand->NoCMakeInstallPath); |
773 | 0 | log.EndObject(); |
774 | |
|
775 | 0 | if (!this->FindBaseCommand->Names.empty()) { |
776 | 0 | log.WriteValue("names"_s, this->FindBaseCommand->Names); |
777 | 0 | } |
778 | 0 | std::vector<std::string> directories; |
779 | 0 | directories.reserve(this->FailedSearchLocations.size()); |
780 | 0 | for (auto const& location : this->FailedSearchLocations) { |
781 | 0 | directories.push_back(location.path); |
782 | 0 | } |
783 | 0 | if (!this->FindCommand->SearchPaths.empty()) { |
784 | 0 | log.WriteValue("candidate_directories"_s, this->FindCommand->SearchPaths); |
785 | 0 | } |
786 | 0 | if (!directories.empty()) { |
787 | 0 | log.WriteValue("searched_directories"_s, directories); |
788 | 0 | } |
789 | 0 | if (!this->FoundSearchLocation.path.empty()) { |
790 | 0 | log.WriteValue("found"_s, this->FoundSearchLocation.path); |
791 | 0 | } else { |
792 | 0 | log.WriteValue("found"_s, false); |
793 | 0 | } |
794 | |
|
795 | 0 | this->WriteSearchVariables(log, mf); |
796 | |
|
797 | 0 | log.EndEvent(); |
798 | 0 | } |
799 | | |
800 | | std::vector<std::pair<cmFindCommonDebugState::VariableSource, std::string>> |
801 | | cmFindBaseDebugState::ExtraSearchVariables() const |
802 | 0 | { |
803 | 0 | std::vector<std::pair<cmFindCommonDebugState::VariableSource, std::string>> |
804 | 0 | extraSearches; |
805 | 0 | if (!this->FindBaseCommand->EnvironmentPath.empty()) { |
806 | 0 | extraSearches.emplace_back(VariableSource::EnvironmentList, |
807 | 0 | this->FindBaseCommand->EnvironmentPath); |
808 | 0 | } |
809 | 0 | return extraSearches; |
810 | 0 | } |
811 | | #endif |