Coverage Report

Created: 2026-09-14 06:43

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.72k
{
21
5.72k
  FILE* file = cmsys::SystemTools::Fopen(filePath, "rb");
22
5.72k
  if (!file) {
23
0
    return false;
24
0
  }
25
5.72k
  this->newEntry();
26
5.72k
  yyscan_t scanner;
27
5.72k
  cmGccDepfile_yylex_init(&scanner);
28
5.72k
  cmGccDepfile_yyset_extra(this, scanner);
29
5.72k
  cmGccDepfile_yyrestart(file, scanner);
30
5.72k
  cmGccDepfile_yylex(scanner);
31
5.72k
  cmGccDepfile_yylex_destroy(scanner);
32
5.72k
  this->sanitizeContent();
33
5.72k
  fclose(file);
34
5.72k
  return this->HelperState != State::Failed;
35
5.72k
}
36
37
void cmGccDepfileLexerHelper::newEntry()
38
161k
{
39
161k
  if (this->HelperState == State::Rule && !this->Content.empty()) {
40
1.52k
    if (!this->Content.back().rules.empty() &&
41
1.52k
        !this->Content.back().rules.back().empty()) {
42
894
      this->HelperState = State::Failed;
43
894
    }
44
1.52k
    return;
45
1.52k
  }
46
160k
  this->HelperState = State::Rule;
47
160k
  this->Content.emplace_back();
48
160k
  this->newRule();
49
160k
}
50
51
void cmGccDepfileLexerHelper::newRule()
52
172k
{
53
172k
  auto& entry = this->Content.back();
54
172k
  if (entry.rules.empty() || !entry.rules.back().empty()) {
55
170k
    entry.rules.emplace_back();
56
170k
  }
57
172k
}
58
59
void cmGccDepfileLexerHelper::newDependency()
60
2.14M
{
61
2.14M
  if (this->HelperState == State::Failed) {
62
542
    return;
63
542
  }
64
2.14M
  this->HelperState = State::Dependency;
65
2.14M
  auto& entry = this->Content.back();
66
2.14M
  if (entry.paths.empty() || !entry.paths.back().empty()) {
67
2.13M
    entry.paths.emplace_back();
68
2.13M
  }
69
2.14M
}
70
71
void cmGccDepfileLexerHelper::newRuleOrDependency()
72
1.97M
{
73
1.97M
  if (this->HelperState == State::Rule) {
74
12.0k
    this->newRule();
75
1.96M
  } else if (this->HelperState == State::Dependency) {
76
1.96M
    this->newDependency();
77
1.96M
  }
78
1.97M
}
79
80
void cmGccDepfileLexerHelper::addToCurrentPath(char const* s)
81
5.58M
{
82
5.58M
  if (this->Content.empty()) {
83
0
    return;
84
0
  }
85
5.58M
  cmGccStyleDependency* dep = &this->Content.back();
86
5.58M
  std::string* dst = nullptr;
87
5.58M
  switch (this->HelperState) {
88
519k
    case State::Rule: {
89
519k
      if (dep->rules.empty()) {
90
0
        return;
91
0
      }
92
519k
      dst = &dep->rules.back();
93
519k
    } break;
94
4.95M
    case State::Dependency: {
95
4.95M
      if (dep->paths.empty()) {
96
0
        return;
97
0
      }
98
4.95M
      dst = &dep->paths.back();
99
4.95M
    } break;
100
110k
    case State::Failed:
101
110k
      return;
102
5.58M
  }
103
5.47M
  dst->append(s);
104
5.47M
}
105
106
void cmGccDepfileLexerHelper::sanitizeContent()
107
5.72k
{
108
165k
  for (auto it = this->Content.begin(); it != this->Content.end();) {
109
    // remove duplicate path entries
110
160k
    std::sort(it->paths.begin(), it->paths.end());
111
160k
    auto last = std::unique(it->paths.begin(), it->paths.end());
112
160k
    it->paths.erase(last, it->paths.end());
113
114
    // Remove empty paths and normalize windows paths
115
457k
    for (auto pit = it->paths.begin(); pit != it->paths.end();) {
116
297k
      if (pit->empty()) {
117
141k
        pit = it->paths.erase(pit);
118
156k
      } 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
156k
        ++pit;
132
156k
      }
133
297k
    }
134
    // Remove empty rules
135
330k
    for (auto rit = it->rules.begin(); rit != it->rules.end();) {
136
170k
      if (rit->empty()) {
137
54.8k
        rit = it->rules.erase(rit);
138
115k
      } else {
139
115k
        ++rit;
140
115k
      }
141
170k
    }
142
    // Remove the entry if rules are empty
143
160k
    if (it->rules.empty()) {
144
54.4k
      it = this->Content.erase(it);
145
105k
    } else {
146
105k
      ++it;
147
105k
    }
148
160k
  }
149
5.72k
}