Coverage Report

Created: 2026-03-12 06:35

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