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 "cmSearchPath.h"
4 
5 #include <algorithm>
6 #include <cassert>
7 #include <utility>
8 
9 #include "cmFindCommon.h"
10 #include "cmMakefile.h"
11 #include "cmStringAlgorithms.h"
12 #include "cmSystemTools.h"
13 #include "cmValue.h"
14 
cmSearchPath(cmFindCommon * findCmd)15 cmSearchPath::cmSearchPath(cmFindCommon* findCmd)
16   : FC(findCmd)
17 {
18 }
19 
20 cmSearchPath::~cmSearchPath() = default;
21 
ExtractWithout(const std::set<std::string> & ignore,std::vector<std::string> & outPaths,bool clear) const22 void cmSearchPath::ExtractWithout(const std::set<std::string>& ignore,
23                                   std::vector<std::string>& outPaths,
24                                   bool clear) const
25 {
26   if (clear) {
27     outPaths.clear();
28   }
29   for (std::string const& path : this->Paths) {
30     if (ignore.count(path) == 0) {
31       outPaths.push_back(path);
32     }
33   }
34 }
35 
AddPath(const std::string & path)36 void cmSearchPath::AddPath(const std::string& path)
37 {
38   this->AddPathInternal(path);
39 }
40 
AddUserPath(const std::string & path)41 void cmSearchPath::AddUserPath(const std::string& path)
42 {
43   assert(this->FC != nullptr);
44 
45   std::vector<std::string> outPaths;
46 
47   // We should view the registry as the target application would view
48   // it.
49   cmSystemTools::KeyWOW64 view = cmSystemTools::KeyWOW64_32;
50   cmSystemTools::KeyWOW64 other_view = cmSystemTools::KeyWOW64_64;
51   if (this->FC->Makefile->PlatformIs64Bit()) {
52     view = cmSystemTools::KeyWOW64_64;
53     other_view = cmSystemTools::KeyWOW64_32;
54   }
55 
56   // Expand using the view of the target application.
57   std::string expanded = path;
58   cmSystemTools::ExpandRegistryValues(expanded, view);
59   cmSystemTools::GlobDirs(expanded, outPaths);
60 
61   // Executables can be either 32-bit or 64-bit, so expand using the
62   // alternative view.
63   if (expanded != path && this->FC->CMakePathName == "PROGRAM") {
64     expanded = path;
65     cmSystemTools::ExpandRegistryValues(expanded, other_view);
66     cmSystemTools::GlobDirs(expanded, outPaths);
67   }
68 
69   // Process them all from the current directory
70   for (std::string const& p : outPaths) {
71     this->AddPathInternal(
72       p, this->FC->Makefile->GetCurrentSourceDirectory().c_str());
73   }
74 }
75 
AddCMakePath(const std::string & variable)76 void cmSearchPath::AddCMakePath(const std::string& variable)
77 {
78   assert(this->FC != nullptr);
79 
80   // Get a path from a CMake variable.
81   if (cmValue value = this->FC->Makefile->GetDefinition(variable)) {
82     std::vector<std::string> expanded = cmExpandedList(*value);
83 
84     for (std::string const& p : expanded) {
85       this->AddPathInternal(
86         p, this->FC->Makefile->GetCurrentSourceDirectory().c_str());
87     }
88   }
89 }
90 
AddEnvPath(const std::string & variable)91 void cmSearchPath::AddEnvPath(const std::string& variable)
92 {
93   std::vector<std::string> expanded;
94   cmSystemTools::GetPath(expanded, variable.c_str());
95   for (std::string const& p : expanded) {
96     this->AddPathInternal(p);
97   }
98 }
99 
AddCMakePrefixPath(const std::string & variable)100 void cmSearchPath::AddCMakePrefixPath(const std::string& variable)
101 {
102   assert(this->FC != nullptr);
103 
104   // Get a path from a CMake variable.
105   if (cmValue value = this->FC->Makefile->GetDefinition(variable)) {
106     std::vector<std::string> expanded = cmExpandedList(*value);
107 
108     this->AddPrefixPaths(
109       expanded, this->FC->Makefile->GetCurrentSourceDirectory().c_str());
110   }
111 }
112 
cmSearchPathStripBin(std::string const & s)113 static std::string cmSearchPathStripBin(std::string const& s)
114 {
115   // If the path is a PREFIX/bin case then add its parent instead.
116   if ((cmHasLiteralSuffix(s, "/bin")) || (cmHasLiteralSuffix(s, "/sbin"))) {
117     return cmSystemTools::GetFilenamePath(s);
118   }
119   return s;
120 }
121 
AddEnvPrefixPath(const std::string & variable,bool stripBin)122 void cmSearchPath::AddEnvPrefixPath(const std::string& variable, bool stripBin)
123 {
124   std::vector<std::string> expanded;
125   cmSystemTools::GetPath(expanded, variable.c_str());
126   if (stripBin) {
127     std::transform(expanded.begin(), expanded.end(), expanded.begin(),
128                    cmSearchPathStripBin);
129   }
130   this->AddPrefixPaths(expanded);
131 }
132 
AddSuffixes(const std::vector<std::string> & suffixes)133 void cmSearchPath::AddSuffixes(const std::vector<std::string>& suffixes)
134 {
135   std::vector<std::string> inPaths;
136   inPaths.swap(this->Paths);
137   this->Paths.reserve(inPaths.size() * (suffixes.size() + 1));
138 
139   for (std::string& inPath : inPaths) {
140     cmSystemTools::ConvertToUnixSlashes(inPath);
141 
142     // if *i is only / then do not add a //
143     // this will get incorrectly considered a network
144     // path on windows and cause huge delays.
145     std::string p = inPath;
146     if (!p.empty() && p.back() != '/') {
147       p += "/";
148     }
149 
150     // Combine with all the suffixes
151     for (std::string const& suffix : suffixes) {
152       this->Paths.push_back(p + suffix);
153     }
154 
155     // And now the original w/o any suffix
156     this->Paths.push_back(std::move(inPath));
157   }
158 }
159 
AddPrefixPaths(const std::vector<std::string> & paths,const char * base)160 void cmSearchPath::AddPrefixPaths(const std::vector<std::string>& paths,
161                                   const char* base)
162 {
163   assert(this->FC != nullptr);
164 
165   // default for programs
166   std::string subdir = "bin";
167 
168   if (this->FC->CMakePathName == "INCLUDE") {
169     subdir = "include";
170   } else if (this->FC->CMakePathName == "LIBRARY") {
171     subdir = "lib";
172   } else if (this->FC->CMakePathName == "FRAMEWORK") {
173     subdir.clear(); // ? what to do for frameworks ?
174   }
175 
176   for (std::string const& path : paths) {
177     std::string dir = path;
178     if (!subdir.empty() && !dir.empty() && dir.back() != '/') {
179       dir += "/";
180     }
181     if (subdir == "include" || subdir == "lib") {
182       cmValue arch =
183         this->FC->Makefile->GetDefinition("CMAKE_LIBRARY_ARCHITECTURE");
184       if (cmNonempty(arch)) {
185         if (this->FC->Makefile->IsDefinitionSet("CMAKE_SYSROOT") &&
186             this->FC->Makefile->IsDefinitionSet(
187               "CMAKE_PREFIX_LIBRARY_ARCHITECTURE")) {
188           this->AddPathInternal(cmStrCat('/', *arch, dir, subdir), base);
189         } else {
190           this->AddPathInternal(cmStrCat(dir, subdir, '/', *arch), base);
191         }
192       }
193     }
194     std::string add = dir + subdir;
195     if (add != "/") {
196       this->AddPathInternal(add, base);
197     }
198     if (subdir == "bin") {
199       this->AddPathInternal(dir + "sbin", base);
200     }
201     if (!subdir.empty() && path != "/") {
202       this->AddPathInternal(path, base);
203     }
204   }
205 }
206 
AddPathInternal(const std::string & path,const char * base)207 void cmSearchPath::AddPathInternal(const std::string& path, const char* base)
208 {
209   assert(this->FC != nullptr);
210 
211   std::string collapsed = cmSystemTools::CollapseFullPath(path, base);
212 
213   if (collapsed.empty()) {
214     return;
215   }
216 
217   // Insert the path if has not already been emitted.
218   if (this->FC->SearchPathsEmitted.insert(collapsed).second) {
219     this->Paths.push_back(std::move(collapsed));
220   }
221 }
222