Coverage Report

Created: 2026-07-30 06:52

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmGccDepfileLexerHelper.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
#include "cmGccDepfileLexerHelper.h"
4
5
#include <algorithm>
6
#include <cstdio>
7
#include <string>
8
#include <vector>
9
10
#include "cmGccDepfileReaderTypes.h"
11
#include "cmSystemTools.h"
12
13
#include "LexerParser/cmGccDepfileLexer.h"
14
15
#ifdef _WIN32
16
#  include "cmsys/String.h"
17
#endif
18
19
bool cmGccDepfileLexerHelper::readFile(char const* filePath)
20
5.75k
{
21
5.75k
  FILE* file = cmsys::SystemTools::Fopen(filePath, "rb");
22
5.75k
  if (!file) {
23
0
    return false;
24
0
  }
25
5.75k
  this->newEntry();
26
5.75k
  yyscan_t scanner;
27
5.75k
  cmGccDepfile_yylex_init(&scanner);
28
5.75k
  cmGccDepfile_yyset_extra(this, scanner);
29
5.75k
  cmGccDepfile_yyrestart(file, scanner);
30
5.75k
  cmGccDepfile_yylex(scanner);
31
5.75k
  cmGccDepfile_yylex_destroy(scanner);
32
5.75k
  this->sanitizeContent();
33
5.75k
  fclose(file);
34
5.75k
  return this->HelperState != State::Failed;
35
5.75k
}
36
37
void cmGccDepfileLexerHelper::newEntry()
38
102k
{
39
102k
  if (this->HelperState == State::Rule && !this->Content.empty()) {
40
1.84k
    if (!this->Content.back().rules.empty() &&
41
1.84k
        !this->Content.back().rules.back().empty()) {
42
1.01k
      this->HelperState = State::Failed;
43
1.01k
    }
44
1.84k
    return;
45
1.84k
  }
46
100k
  this->HelperState = State::Rule;
47
100k
  this->Content.emplace_back();
48
100k
  this->newRule();
49
100k
}
50
51
void cmGccDepfileLexerHelper::newRule()
52
117k
{
53
117k
  auto& entry = this->Content.back();
54
117k
  if (entry.rules.empty() || !entry.rules.back().empty()) {
55
115k
    entry.rules.emplace_back();
56
115k
  }
57
117k
}
58
59
void cmGccDepfileLexerHelper::newDependency()
60
2.09M
{
61
2.09M
  if (this->HelperState == State::Failed) {
62
547
    return;
63
547
  }
64
2.09M
  this->HelperState = State::Dependency;
65
2.09M
  auto& entry = this->Content.back();
66
2.09M
  if (entry.paths.empty() || !entry.paths.back().empty()) {
67
2.09M
    entry.paths.emplace_back();
68
2.09M
  }
69
2.09M
}
70
71
void cmGccDepfileLexerHelper::newRuleOrDependency()
72
1.99M
{
73
1.99M
  if (this->HelperState == State::Rule) {
74
16.5k
    this->newRule();
75
1.97M
  } else if (this->HelperState == State::Dependency) {
76
1.97M
    this->newDependency();
77
1.97M
  }
78
1.99M
}
79
80
void cmGccDepfileLexerHelper::addToCurrentPath(char const* s)
81
4.87M
{
82
4.87M
  if (this->Content.empty()) {
83
0
    return;
84
0
  }
85
4.87M
  cmGccStyleDependency* dep = &this->Content.back();
86
4.87M
  std::string* dst = nullptr;
87
4.87M
  switch (this->HelperState) {
88
545k
    case State::Rule: {
89
545k
      if (dep->rules.empty()) {
90
0
        return;
91
0
      }
92
545k
      dst = &dep->rules.back();
93
545k
    } break;
94
4.22M
    case State::Dependency: {
95
4.22M
      if (dep->paths.empty()) {
96
0
        return;
97
0
      }
98
4.22M
      dst = &dep->paths.back();
99
4.22M
    } break;
100
102k
    case State::Failed:
101
102k
      return;
102
4.87M
  }
103
4.77M
  dst->append(s);
104
4.77M
}
105
106
void cmGccDepfileLexerHelper::sanitizeContent()
107
5.75k
{
108
106k
  for (auto it = this->Content.begin(); it != this->Content.end();) {
109
    // remove duplicate path entries
110
100k
    std::sort(it->paths.begin(), it->paths.end());
111
100k
    auto last = std::unique(it->paths.begin(), it->paths.end());
112
100k
    it->paths.erase(last, it->paths.end());
113
114
    // Remove empty paths and normalize windows paths
115
350k
    for (auto pit = it->paths.begin(); pit != it->paths.end();) {
116
249k
      if (pit->empty()) {
117
80.5k
        pit = it->paths.erase(pit);
118
169k
      } else {
119
#if defined(_WIN32)
120
        // Unescape the colon following the drive letter.
121
        // Some versions of GNU compilers can escape this character.
122
        // c\:\path must be transformed to c:\path
123
        if (pit->size() >= 3) {
124
          auto pit0 = static_cast<char>(cmsysString_toupper((*pit)[0]));
125
          if (pit0 >= 'A' && pit0 <= 'Z' && (*pit)[1] == '\\' &&
126
              (*pit)[2] == ':') {
127
            pit->erase(1, 1);
128
          }
129
        }
130
#endif
131
169k
        ++pit;
132
169k
      }
133
249k
    }
134
    // Remove empty rules
135
215k
    for (auto rit = it->rules.begin(); rit != it->rules.end();) {
136
115k
      if (rit->empty()) {
137
58.6k
        rit = it->rules.erase(rit);
138
58.6k
      } else {
139
56.4k
        ++rit;
140
56.4k
      }
141
115k
    }
142
    // Remove the entry if rules are empty
143
100k
    if (it->rules.empty()) {
144
57.9k
      it = this->Content.erase(it);
145
57.9k
    } else {
146
42.4k
      ++it;
147
42.4k
    }
148
100k
  }
149
5.75k
}