Coverage Report

Created: 2026-04-29 07:01

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