Coverage Report

Created: 2026-02-09 06:05

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