/src/CMake/Source/cmPkgConfigResolver.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 | | |
4 | | #include "cmPkgConfigResolver.h" |
5 | | |
6 | | #include <algorithm> |
7 | | #include <cstring> |
8 | | #include <iterator> |
9 | | #include <string> |
10 | | #include <utility> |
11 | | #include <vector> |
12 | | |
13 | | #include <cm/optional> |
14 | | |
15 | | #include "cmsys/String.h" |
16 | | |
17 | | #include "cmOutputConverter.h" |
18 | | #include "cmPkgConfigParser.h" |
19 | | #include "cmStringAlgorithms.h" |
20 | | |
21 | | namespace { |
22 | | |
23 | | bool HasPrefix(std::string const& value, char const* prefix) |
24 | 0 | { |
25 | 0 | std::size_t const len = std::strlen(prefix); |
26 | 0 | return value.size() >= len && value.compare(0, len, prefix) == 0; |
27 | 0 | } |
28 | | |
29 | | void AppendFlag(std::string& flagline, std::string const& flag) |
30 | 0 | { |
31 | 0 | if (!flagline.empty()) { |
32 | 0 | flagline += ' '; |
33 | 0 | } |
34 | 0 | flagline += flag; |
35 | 0 | } |
36 | | |
37 | | bool IsSystemPath(std::string const& path, |
38 | | std::vector<std::string> const& systemPaths) |
39 | 0 | { |
40 | 0 | return std::find(systemPaths.begin(), systemPaths.end(), path) != |
41 | 0 | systemPaths.end(); |
42 | 0 | } |
43 | | |
44 | | std::vector<std::string> DefaultSystemIncludePaths() |
45 | 0 | { |
46 | 0 | return std::vector<std::string>{ "/usr/include" }; |
47 | 0 | } |
48 | | |
49 | | std::vector<std::string> DefaultSystemLibraryPaths() |
50 | 0 | { |
51 | 0 | return std::vector<std::string>{ "/usr/lib" }; |
52 | 0 | } |
53 | | |
54 | | std::vector<std::string> const& SystemCflags(cmPkgConfigEnv const& env, |
55 | | std::vector<std::string>& def) |
56 | 0 | { |
57 | 0 | if (env.SysCflags) { |
58 | 0 | return *env.SysCflags; |
59 | 0 | } |
60 | 0 | def = DefaultSystemIncludePaths(); |
61 | 0 | return def; |
62 | 0 | } |
63 | | |
64 | | std::vector<std::string> const& SystemLibs(cmPkgConfigEnv const& env, |
65 | | std::vector<std::string>& def) |
66 | 0 | { |
67 | 0 | if (env.SysLibs) { |
68 | 0 | return *env.SysLibs; |
69 | 0 | } |
70 | 0 | def = DefaultSystemLibraryPaths(); |
71 | 0 | return def; |
72 | 0 | } |
73 | | |
74 | | } // namespace |
75 | | |
76 | | std::string cmPkgConfigResolver::VersionReqString( |
77 | | cmPkgConfigDependencySpec const& spec) |
78 | 0 | { |
79 | 0 | switch (spec.Operation) { |
80 | 0 | case cmPkgConfigDependencyOperation::None: |
81 | 0 | return ""; |
82 | 0 | case cmPkgConfigDependencyOperation::LessThan: |
83 | 0 | return cmStrCat('<', spec.Version); |
84 | 0 | case cmPkgConfigDependencyOperation::LessThanEqual: |
85 | 0 | return cmStrCat("<=", spec.Version); |
86 | 0 | case cmPkgConfigDependencyOperation::Equal: |
87 | 0 | return cmStrCat('=', spec.Version); |
88 | 0 | case cmPkgConfigDependencyOperation::NotEqual: |
89 | 0 | return cmStrCat("!=", spec.Version); |
90 | 0 | case cmPkgConfigDependencyOperation::GreaterThanEqual: |
91 | 0 | return cmStrCat(">=", spec.Version); |
92 | 0 | case cmPkgConfigDependencyOperation::GreaterThan: |
93 | 0 | return cmStrCat('>', spec.Version); |
94 | 0 | } |
95 | 0 | return ""; |
96 | 0 | } |
97 | | |
98 | | cmPkgConfigResolver::cmPkgConfigResolver(cmPkgConfigParser parser, |
99 | | cmPkgConfigEnv const& env, |
100 | | std::string const& pcFileDir) |
101 | 0 | : Parser{ std::move(parser) } |
102 | 0 | , Env{ &env } |
103 | 0 | { |
104 | 0 | this->Parser.ParseComplete(); |
105 | 0 | this->ConfigureParser(pcFileDir); |
106 | 0 | } |
107 | | |
108 | | cm::optional<cmPkgConfigResolver> cmPkgConfigResolver::Resolve( |
109 | | cmPkgConfigParser parser, cmPkgConfigEnv const& env, Strictness strictness, |
110 | | std::string const& pcFileDir) |
111 | 0 | { |
112 | 0 | cmPkgConfigResolver resolver{ std::move(parser), env, pcFileDir }; |
113 | 0 | if (strictness != Strictness::BestEffort && !resolver.HasRequiredFields()) { |
114 | 0 | return {}; |
115 | 0 | } |
116 | 0 | if (strictness == Strictness::Strict && resolver.HasStrictConflicts()) { |
117 | 0 | return {}; |
118 | 0 | } |
119 | 0 | return resolver; |
120 | 0 | } |
121 | | |
122 | | cm::optional<cmPkgConfigResolver> cmPkgConfigResolver::ResolveStrict( |
123 | | cmPkgConfigParser parser, cmPkgConfigEnv const& env, |
124 | | std::string const& pcFileDir) |
125 | 0 | { |
126 | 0 | return Resolve(std::move(parser), env, Strictness::Strict, pcFileDir); |
127 | 0 | } |
128 | | |
129 | | cm::optional<cmPkgConfigResolver> cmPkgConfigResolver::ResolvePermissive( |
130 | | cmPkgConfigParser parser, cmPkgConfigEnv const& env, |
131 | | std::string const& pcFileDir) |
132 | 0 | { |
133 | 0 | return Resolve(std::move(parser), env, Strictness::Permissive, pcFileDir); |
134 | 0 | } |
135 | | |
136 | | cmPkgConfigResolver cmPkgConfigResolver::ResolveBestEffort( |
137 | | cmPkgConfigParser parser, cmPkgConfigEnv const& env, |
138 | | std::string const& pcFileDir) |
139 | 0 | { |
140 | 0 | return cmPkgConfigResolver{ std::move(parser), env, pcFileDir }; |
141 | 0 | } |
142 | | |
143 | | void cmPkgConfigResolver::ConfigureParser(std::string const& pcFileDir) |
144 | 0 | { |
145 | 0 | if (this->Env->SysrootDir) { |
146 | 0 | this->Parser.SetVariable( |
147 | 0 | "pc_sysrootdir", |
148 | 0 | cmOutputConverter::EscapeForShell(*this->Env->SysrootDir, |
149 | 0 | cmOutputConverter::Shell_Flag_IsUnix)); |
150 | 0 | } else { |
151 | 0 | this->Parser.SetVariable("pc_sysrootdir", "/"); |
152 | 0 | } |
153 | |
|
154 | 0 | if (this->Env->TopBuildDir) { |
155 | 0 | this->Parser.SetVariable( |
156 | 0 | "pc_top_builddir", |
157 | 0 | cmOutputConverter::EscapeForShell(*this->Env->TopBuildDir, |
158 | 0 | cmOutputConverter::Shell_Flag_IsUnix)); |
159 | 0 | } |
160 | |
|
161 | 0 | this->Parser.SetVariable("pcfiledir", |
162 | 0 | cmOutputConverter::EscapeForShell( |
163 | 0 | pcFileDir, cmOutputConverter::Shell_Flag_IsUnix)); |
164 | 0 | } |
165 | | |
166 | | bool cmPkgConfigResolver::HasRequiredFields() const |
167 | 0 | { |
168 | 0 | return this->Parser.HasProperty("Name") && |
169 | 0 | this->Parser.HasProperty("Description") && |
170 | 0 | this->Parser.HasProperty("Version"); |
171 | 0 | } |
172 | | |
173 | | bool cmPkgConfigResolver::HasStrictConflicts() const |
174 | 0 | { |
175 | 0 | return (this->Parser.HasProperty("Cflags") && |
176 | 0 | this->Parser.HasProperty("CFlags")) || |
177 | 0 | (this->Parser.HasProperty("Cflags.private") && |
178 | 0 | this->Parser.HasProperty("CFlags.private")); |
179 | 0 | } |
180 | | |
181 | | std::string cmPkgConfigResolver::Literal(std::string const& key) const |
182 | 0 | { |
183 | 0 | return this->Parser.GetLiteral(key); |
184 | 0 | } |
185 | | |
186 | | std::string cmPkgConfigResolver::Name() const |
187 | 0 | { |
188 | 0 | return this->Literal("Name"); |
189 | 0 | } |
190 | | |
191 | | std::string cmPkgConfigResolver::Description() const |
192 | 0 | { |
193 | 0 | return this->Literal("Description"); |
194 | 0 | } |
195 | | |
196 | | std::string cmPkgConfigResolver::Version() const |
197 | 0 | { |
198 | 0 | return this->Literal("Version"); |
199 | 0 | } |
200 | | |
201 | | std::vector<std::string> cmPkgConfigResolver::Fragments( |
202 | | std::string const& key) const |
203 | 0 | { |
204 | 0 | return this->Parser.GetFragments(key); |
205 | 0 | } |
206 | | |
207 | | std::vector<cmPkgConfigDependencySpec> cmPkgConfigResolver::Dependencies( |
208 | | std::string const& key) const |
209 | 0 | { |
210 | 0 | return this->Parser.GetDependencies(key); |
211 | 0 | } |
212 | | |
213 | | std::vector<cmPkgConfigDependencySpec> cmPkgConfigResolver::Conflicts() const |
214 | 0 | { |
215 | 0 | return this->Dependencies("Conflicts"); |
216 | 0 | } |
217 | | |
218 | | std::vector<cmPkgConfigDependencySpec> cmPkgConfigResolver::Provides() const |
219 | 0 | { |
220 | 0 | return this->Dependencies("Provides"); |
221 | 0 | } |
222 | | |
223 | | std::vector<cmPkgConfigDependencySpec> cmPkgConfigResolver::Requires( |
224 | | bool priv) const |
225 | 0 | { |
226 | 0 | return this->Dependencies(priv ? "Requires.private" : "Requires"); |
227 | 0 | } |
228 | | |
229 | | cmPkgConfigCflagsResult cmPkgConfigResolver::Cflags(bool priv) const |
230 | 0 | { |
231 | 0 | auto flags = this->Fragments(priv ? "Cflags.private" : "Cflags"); |
232 | 0 | auto alt = this->Fragments(priv ? "CFlags.private" : "CFlags"); |
233 | 0 | flags.insert(flags.end(), alt.begin(), alt.end()); |
234 | 0 | return MangleCflags(flags, *this->Env); |
235 | 0 | } |
236 | | |
237 | | cmPkgConfigLibsResult cmPkgConfigResolver::Libs(bool priv) const |
238 | 0 | { |
239 | 0 | return MangleLibs(this->Fragments(priv ? "Libs.private" : "Libs"), |
240 | 0 | *this->Env); |
241 | 0 | } |
242 | | |
243 | | cmPkgConfigCflagsResult cmPkgConfigResolver::MangleCflags( |
244 | | std::vector<std::string> const& flags, cmPkgConfigEnv const& env) |
245 | 0 | { |
246 | 0 | cmPkgConfigCflagsResult result; |
247 | 0 | std::vector<std::string> defaultSystemPaths; |
248 | 0 | auto const& systemPaths = SystemCflags(env, defaultSystemPaths); |
249 | |
|
250 | 0 | for (auto const& flag : flags) { |
251 | 0 | std::string mangled = flag; |
252 | 0 | if (HasPrefix(flag, "-I") && env.SysrootDir) { |
253 | 0 | mangled = Reroot(flag, "-I", *env.SysrootDir); |
254 | 0 | } |
255 | |
|
256 | 0 | if (HasPrefix(mangled, "-I")) { |
257 | 0 | if (!env.AllowSysCflags && |
258 | 0 | IsSystemPath(mangled.substr(2), systemPaths)) { |
259 | 0 | continue; |
260 | 0 | } |
261 | 0 | AppendFlag(result.Flagline, mangled); |
262 | 0 | result.Includes.push_back(mangled); |
263 | 0 | } else { |
264 | 0 | AppendFlag(result.Flagline, mangled); |
265 | 0 | result.CompileOptions.push_back(mangled); |
266 | 0 | } |
267 | 0 | } |
268 | |
|
269 | 0 | return result; |
270 | 0 | } |
271 | | |
272 | | cmPkgConfigLibsResult cmPkgConfigResolver::MangleLibs( |
273 | | std::vector<std::string> const& flags, cmPkgConfigEnv const& env) |
274 | 0 | { |
275 | 0 | cmPkgConfigLibsResult result; |
276 | 0 | std::vector<std::string> defaultSystemPaths; |
277 | 0 | auto const& systemPaths = SystemLibs(env, defaultSystemPaths); |
278 | |
|
279 | 0 | for (auto const& flag : flags) { |
280 | 0 | std::string mangled = flag; |
281 | 0 | if (HasPrefix(flag, "-L") && env.SysrootDir) { |
282 | 0 | mangled = Reroot(flag, "-L", *env.SysrootDir); |
283 | 0 | } |
284 | |
|
285 | 0 | if (HasPrefix(mangled, "-L")) { |
286 | 0 | if (!env.AllowSysLibs && IsSystemPath(mangled.substr(2), systemPaths)) { |
287 | 0 | continue; |
288 | 0 | } |
289 | 0 | AppendFlag(result.Flagline, mangled); |
290 | 0 | result.LibDirs.push_back(mangled); |
291 | 0 | } else if (HasPrefix(mangled, "-l")) { |
292 | 0 | AppendFlag(result.Flagline, mangled); |
293 | 0 | result.LibNames.push_back(mangled); |
294 | 0 | } else { |
295 | 0 | AppendFlag(result.Flagline, mangled); |
296 | 0 | result.LinkOptions.push_back(mangled); |
297 | 0 | } |
298 | 0 | } |
299 | |
|
300 | 0 | return result; |
301 | 0 | } |
302 | | |
303 | | std::string cmPkgConfigResolver::Reroot(std::string const& flag, |
304 | | char const* prefix, |
305 | | std::string const& sysroot) |
306 | 0 | { |
307 | 0 | std::string result = prefix; |
308 | 0 | result += sysroot; |
309 | 0 | result += flag.substr(std::strlen(prefix)); |
310 | 0 | return result; |
311 | 0 | } |
312 | | |
313 | | void cmPkgConfigResolver::ReplaceSep(std::string& list) |
314 | 0 | { |
315 | 0 | #ifndef _WIN32 |
316 | 0 | std::replace(list.begin(), list.end(), ':', ';'); |
317 | | #else |
318 | | static_cast<void>(list); |
319 | | #endif |
320 | 0 | } |
321 | | |
322 | | bool cmPkgConfigResolver::CheckVersion( |
323 | | cmPkgConfigDependencySpec const& desired, std::string const& provided) |
324 | 0 | { |
325 | 0 | if (desired.Operation == cmPkgConfigDependencyOperation::None) { |
326 | 0 | return true; |
327 | 0 | } |
328 | | |
329 | 0 | auto check_with_op = [&](int comp) -> bool { |
330 | 0 | switch (desired.Operation) { |
331 | 0 | case cmPkgConfigDependencyOperation::Equal: |
332 | 0 | return comp == 0; |
333 | 0 | case cmPkgConfigDependencyOperation::NotEqual: |
334 | 0 | return comp != 0; |
335 | 0 | case cmPkgConfigDependencyOperation::GreaterThan: |
336 | 0 | return comp < 0; |
337 | 0 | case cmPkgConfigDependencyOperation::GreaterThanEqual: |
338 | 0 | return comp <= 0; |
339 | 0 | case cmPkgConfigDependencyOperation::LessThan: |
340 | 0 | return comp > 0; |
341 | 0 | case cmPkgConfigDependencyOperation::LessThanEqual: |
342 | 0 | return comp >= 0; |
343 | 0 | default: |
344 | 0 | return true; |
345 | 0 | } |
346 | 0 | }; |
347 | |
|
348 | 0 | if (desired.Version == provided) { |
349 | 0 | return check_with_op(0); |
350 | 0 | } |
351 | | |
352 | 0 | auto a_cur = desired.Version.begin(); |
353 | 0 | auto a_end = desired.Version.end(); |
354 | |
|
355 | 0 | auto b_cur = provided.begin(); |
356 | 0 | auto b_end = provided.end(); |
357 | |
|
358 | 0 | while (a_cur != a_end && b_cur != b_end) { |
359 | 0 | while (a_cur != a_end && !cmsysString_isalnum(*a_cur) && *a_cur != '~') { |
360 | 0 | ++a_cur; |
361 | 0 | } |
362 | |
|
363 | 0 | while (b_cur != b_end && !cmsysString_isalnum(*b_cur) && *b_cur != '~') { |
364 | 0 | ++b_cur; |
365 | 0 | } |
366 | |
|
367 | 0 | if (a_cur == a_end || b_cur == b_end) { |
368 | 0 | break; |
369 | 0 | } |
370 | | |
371 | 0 | if (*a_cur == '~' || *b_cur == '~') { |
372 | 0 | if (*a_cur != '~') { |
373 | 0 | return check_with_op(1); |
374 | 0 | } |
375 | | |
376 | 0 | if (*b_cur != '~') { |
377 | 0 | return check_with_op(-1); |
378 | 0 | } |
379 | | |
380 | 0 | ++a_cur; |
381 | 0 | ++b_cur; |
382 | 0 | continue; |
383 | 0 | } |
384 | | |
385 | 0 | auto a_seg = a_cur; |
386 | 0 | auto b_seg = b_cur; |
387 | 0 | bool is_num; |
388 | |
|
389 | 0 | if (cmsysString_isdigit(*a_cur)) { |
390 | 0 | is_num = true; |
391 | 0 | while (a_cur != a_end && cmsysString_isdigit(*a_cur)) { |
392 | 0 | ++a_cur; |
393 | 0 | } |
394 | |
|
395 | 0 | while (b_cur != b_end && cmsysString_isdigit(*b_cur)) { |
396 | 0 | ++b_cur; |
397 | 0 | } |
398 | |
|
399 | 0 | } else { |
400 | 0 | is_num = false; |
401 | 0 | while (a_cur != a_end && cmsysString_isalpha(*a_cur)) { |
402 | 0 | ++a_cur; |
403 | 0 | } |
404 | |
|
405 | 0 | while (b_cur != b_end && cmsysString_isalpha(*b_cur)) { |
406 | 0 | ++b_cur; |
407 | 0 | } |
408 | 0 | } |
409 | |
|
410 | 0 | auto a_len = std::distance(a_seg, a_cur); |
411 | 0 | auto b_len = std::distance(b_seg, b_cur); |
412 | |
|
413 | 0 | if (!b_len) { |
414 | 0 | return check_with_op(is_num ? 1 : -1); |
415 | 0 | } |
416 | | |
417 | 0 | if (is_num) { |
418 | 0 | while (a_seg != a_cur && *a_seg == '0') { |
419 | 0 | ++a_seg; |
420 | 0 | } |
421 | |
|
422 | 0 | while (b_seg != b_cur && *b_seg == '0') { |
423 | 0 | ++b_seg; |
424 | 0 | } |
425 | |
|
426 | 0 | a_len = std::distance(a_seg, a_cur); |
427 | 0 | b_len = std::distance(b_seg, b_cur); |
428 | |
|
429 | 0 | if (a_len != b_len) { |
430 | 0 | return check_with_op(a_len > b_len ? 1 : -1); |
431 | 0 | } |
432 | | |
433 | 0 | auto cmp = std::memcmp(&*a_seg, &*b_seg, a_len); |
434 | 0 | if (cmp) { |
435 | 0 | return check_with_op(cmp); |
436 | 0 | } |
437 | 0 | } else { |
438 | 0 | auto cmp = std::memcmp(&*a_seg, &*b_seg, std::min(a_len, b_len)); |
439 | 0 | if (cmp) { |
440 | 0 | return check_with_op(cmp); |
441 | 0 | } |
442 | | |
443 | 0 | if (a_len != b_len) { |
444 | 0 | return check_with_op(a_len > b_len ? 1 : -1); |
445 | 0 | } |
446 | 0 | } |
447 | 0 | } |
448 | | |
449 | 0 | if (a_cur == a_end) { |
450 | 0 | if (b_cur == b_end) { |
451 | 0 | return check_with_op(0); |
452 | 0 | } |
453 | 0 | return check_with_op(-1); |
454 | 0 | } |
455 | | |
456 | 0 | return check_with_op(1); |
457 | 0 | } |