Coverage Report

Created: 2026-10-28 06:38

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