Coverage Report

Created: 2026-07-30 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}