Coverage Report

Created: 2026-06-15 07:03

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.78k
{
21
#ifdef _WIN32
22
  wchar_t* wpath = cmsysEncoding_DupToWide(filePath);
23
  FILE* file = _wfopen(wpath, L"rb");
24
  free(wpath);
25
#else
26
5.78k
  FILE* file = fopen(filePath, "r");
27
5.78k
#endif
28
5.78k
  if (!file) {
29
0
    return false;
30
0
  }
31
5.78k
  this->newEntry();
32
5.78k
  yyscan_t scanner;
33
5.78k
  cmGccDepfile_yylex_init(&scanner);
34
5.78k
  cmGccDepfile_yyset_extra(this, scanner);
35
5.78k
  cmGccDepfile_yyrestart(file, scanner);
36
5.78k
  cmGccDepfile_yylex(scanner);
37
5.78k
  cmGccDepfile_yylex_destroy(scanner);
38
5.78k
  this->sanitizeContent();
39
5.78k
  fclose(file);
40
5.78k
  return this->HelperState != State::Failed;
41
5.78k
}
42
43
void cmGccDepfileLexerHelper::newEntry()
44
174k
{
45
174k
  if (this->HelperState == State::Rule && !this->Content.empty()) {
46
2.41k
    if (!this->Content.back().rules.empty() &&
47
2.41k
        !this->Content.back().rules.back().empty()) {
48
1.47k
      this->HelperState = State::Failed;
49
1.47k
    }
50
2.41k
    return;
51
2.41k
  }
52
171k
  this->HelperState = State::Rule;
53
171k
  this->Content.emplace_back();
54
171k
  this->newRule();
55
171k
}
56
57
void cmGccDepfileLexerHelper::newRule()
58
212k
{
59
212k
  auto& entry = this->Content.back();
60
212k
  if (entry.rules.empty() || !entry.rules.back().empty()) {
61
210k
    entry.rules.emplace_back();
62
210k
  }
63
212k
}
64
65
void cmGccDepfileLexerHelper::newDependency()
66
2.16M
{
67
2.16M
  if (this->HelperState == State::Failed) {
68
1.26k
    return;
69
1.26k
  }
70
2.16M
  this->HelperState = State::Dependency;
71
2.16M
  auto& entry = this->Content.back();
72
2.16M
  if (entry.paths.empty() || !entry.paths.back().empty()) {
73
2.15M
    entry.paths.emplace_back();
74
2.15M
  }
75
2.16M
}
76
77
void cmGccDepfileLexerHelper::newRuleOrDependency()
78
2.03M
{
79
2.03M
  if (this->HelperState == State::Rule) {
80
40.8k
    this->newRule();
81
1.99M
  } else if (this->HelperState == State::Dependency) {
82
1.96M
    this->newDependency();
83
1.96M
  }
84
2.03M
}
85
86
void cmGccDepfileLexerHelper::addToCurrentPath(char const* s)
87
5.86M
{
88
5.86M
  if (this->Content.empty()) {
89
0
    return;
90
0
  }
91
5.86M
  cmGccStyleDependency* dep = &this->Content.back();
92
5.86M
  std::string* dst = nullptr;
93
5.86M
  switch (this->HelperState) {
94
697k
    case State::Rule: {
95
697k
      if (dep->rules.empty()) {
96
0
        return;
97
0
      }
98
697k
      dst = &dep->rules.back();
99
697k
    } break;
100
4.88M
    case State::Dependency: {
101
4.88M
      if (dep->paths.empty()) {
102
0
        return;
103
0
      }
104
4.88M
      dst = &dep->paths.back();
105
4.88M
    } break;
106
289k
    case State::Failed:
107
289k
      return;
108
5.86M
  }
109
5.57M
  dst->append(s);
110
5.57M
}
111
112
void cmGccDepfileLexerHelper::sanitizeContent()
113
5.78k
{
114
177k
  for (auto it = this->Content.begin(); it != this->Content.end();) {
115
    // remove duplicate path entries
116
171k
    std::sort(it->paths.begin(), it->paths.end());
117
171k
    auto last = std::unique(it->paths.begin(), it->paths.end());
118
171k
    it->paths.erase(last, it->paths.end());
119
120
    // Remove empty paths and normalize windows paths
121
534k
    for (auto pit = it->paths.begin(); pit != it->paths.end();) {
122
363k
      if (pit->empty()) {
123
141k
        pit = it->paths.erase(pit);
124
221k
      } 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
221k
        ++pit;
138
221k
      }
139
363k
    }
140
    // Remove empty rules
141
381k
    for (auto rit = it->rules.begin(); rit != it->rules.end();) {
142
210k
      if (rit->empty()) {
143
61.8k
        rit = it->rules.erase(rit);
144
148k
      } else {
145
148k
        ++rit;
146
148k
      }
147
210k
    }
148
    // Remove the entry if rules are empty
149
171k
    if (it->rules.empty()) {
150
60.9k
      it = this->Content.erase(it);
151
110k
    } else {
152
110k
      ++it;
153
110k
    }
154
171k
  }
155
5.78k
}