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