Coverage Report

Created: 2026-07-14 07:09

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
12
#include "LexerParser/cmGccDepfileLexer.h"
13
14
#ifdef _WIN32
15
#  include "cmsys/Encoding.h"
16
#  include "cmsys/String.h"
17
#endif
18
19
bool cmGccDepfileLexerHelper::readFile(char const* filePath)
20
5.77k
{
21
#ifdef _WIN32
22
  wchar_t* wpath = cmsysEncoding_DupToWide(filePath);
23
  FILE* file = _wfopen(wpath, L"rb");
24
  free(wpath);
25
#else
26
5.77k
  FILE* file = fopen(filePath, "r");
27
5.77k
#endif
28
5.77k
  if (!file) {
29
0
    return false;
30
0
  }
31
5.77k
  this->newEntry();
32
5.77k
  yyscan_t scanner;
33
5.77k
  cmGccDepfile_yylex_init(&scanner);
34
5.77k
  cmGccDepfile_yyset_extra(this, scanner);
35
5.77k
  cmGccDepfile_yyrestart(file, scanner);
36
5.77k
  cmGccDepfile_yylex(scanner);
37
5.77k
  cmGccDepfile_yylex_destroy(scanner);
38
5.77k
  this->sanitizeContent();
39
5.77k
  fclose(file);
40
5.77k
  return this->HelperState != State::Failed;
41
5.77k
}
42
43
void cmGccDepfileLexerHelper::newEntry()
44
80.4k
{
45
80.4k
  if (this->HelperState == State::Rule && !this->Content.empty()) {
46
1.59k
    if (!this->Content.back().rules.empty() &&
47
1.59k
        !this->Content.back().rules.back().empty()) {
48
866
      this->HelperState = State::Failed;
49
866
    }
50
1.59k
    return;
51
1.59k
  }
52
78.8k
  this->HelperState = State::Rule;
53
78.8k
  this->Content.emplace_back();
54
78.8k
  this->newRule();
55
78.8k
}
56
57
void cmGccDepfileLexerHelper::newRule()
58
104k
{
59
104k
  auto& entry = this->Content.back();
60
104k
  if (entry.rules.empty() || !entry.rules.back().empty()) {
61
102k
    entry.rules.emplace_back();
62
102k
  }
63
104k
}
64
65
void cmGccDepfileLexerHelper::newDependency()
66
2.12M
{
67
2.12M
  if (this->HelperState == State::Failed) {
68
407
    return;
69
407
  }
70
2.12M
  this->HelperState = State::Dependency;
71
2.12M
  auto& entry = this->Content.back();
72
2.12M
  if (entry.paths.empty() || !entry.paths.back().empty()) {
73
2.12M
    entry.paths.emplace_back();
74
2.12M
  }
75
2.12M
}
76
77
void cmGccDepfileLexerHelper::newRuleOrDependency()
78
2.05M
{
79
2.05M
  if (this->HelperState == State::Rule) {
80
25.4k
    this->newRule();
81
2.02M
  } else if (this->HelperState == State::Dependency) {
82
2.02M
    this->newDependency();
83
2.02M
  }
84
2.05M
}
85
86
void cmGccDepfileLexerHelper::addToCurrentPath(char const* s)
87
4.27M
{
88
4.27M
  if (this->Content.empty()) {
89
0
    return;
90
0
  }
91
4.27M
  cmGccStyleDependency* dep = &this->Content.back();
92
4.27M
  std::string* dst = nullptr;
93
4.27M
  switch (this->HelperState) {
94
554k
    case State::Rule: {
95
554k
      if (dep->rules.empty()) {
96
0
        return;
97
0
      }
98
554k
      dst = &dep->rules.back();
99
554k
    } break;
100
3.61M
    case State::Dependency: {
101
3.61M
      if (dep->paths.empty()) {
102
0
        return;
103
0
      }
104
3.61M
      dst = &dep->paths.back();
105
3.61M
    } break;
106
102k
    case State::Failed:
107
102k
      return;
108
4.27M
  }
109
4.17M
  dst->append(s);
110
4.17M
}
111
112
void cmGccDepfileLexerHelper::sanitizeContent()
113
5.77k
{
114
84.6k
  for (auto it = this->Content.begin(); it != this->Content.end();) {
115
    // remove duplicate path entries
116
78.8k
    std::sort(it->paths.begin(), it->paths.end());
117
78.8k
    auto last = std::unique(it->paths.begin(), it->paths.end());
118
78.8k
    it->paths.erase(last, it->paths.end());
119
120
    // Remove empty paths and normalize windows paths
121
307k
    for (auto pit = it->paths.begin(); pit != it->paths.end();) {
122
229k
      if (pit->empty()) {
123
59.6k
        pit = it->paths.erase(pit);
124
169k
      } else {
125
#if defined(_WIN32)
126
        // Unescape the colon following the drive letter.
127
        // Some versions of GNU compilers can escape this character.
128
        // c\:\path must be transformed to c:\path
129
        if (pit->size() >= 3) {
130
          auto pit0 = static_cast<char>(cmsysString_toupper((*pit)[0]));
131
          if (pit0 >= 'A' && pit0 <= 'Z' && (*pit)[1] == '\\' &&
132
              (*pit)[2] == ':') {
133
            pit->erase(1, 1);
134
          }
135
        }
136
#endif
137
169k
        ++pit;
138
169k
      }
139
229k
    }
140
    // Remove empty rules
141
180k
    for (auto rit = it->rules.begin(); rit != it->rules.end();) {
142
102k
      if (rit->empty()) {
143
39.4k
        rit = it->rules.erase(rit);
144
62.6k
      } else {
145
62.6k
        ++rit;
146
62.6k
      }
147
102k
    }
148
    // Remove the entry if rules are empty
149
78.8k
    if (it->rules.empty()) {
150
38.7k
      it = this->Content.erase(it);
151
40.1k
    } else {
152
40.1k
      ++it;
153
40.1k
    }
154
78.8k
  }
155
5.77k
}