Coverage Report

Created: 2026-03-12 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmDepends.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 "cmDepends.h"
4
5
#include <utility>
6
7
#include "cmsys/FStream.hxx"
8
9
#include "cmFileTime.h"
10
#include "cmFileTimeCache.h"
11
#include "cmGeneratedFileStream.h"
12
#include "cmList.h"
13
#include "cmLocalUnixMakefileGenerator3.h"
14
#include "cmMakefile.h"
15
#include "cmStringAlgorithms.h"
16
#include "cmSystemTools.h"
17
#include "cmValue.h"
18
19
cmDepends::cmDepends(cmLocalUnixMakefileGenerator3* lg, std::string targetDir)
20
0
  : LocalGenerator(lg)
21
0
  , TargetDirectory(std::move(targetDir))
22
0
{
23
0
}
24
25
0
cmDepends::~cmDepends() = default;
26
27
bool cmDepends::Write(std::ostream& makeDepends, std::ostream& internalDepends)
28
0
{
29
0
  std::map<std::string, std::set<std::string>> dependencies;
30
0
  {
31
    // Lookup the set of sources to scan.
32
0
    cmList pairs;
33
0
    {
34
0
      std::string const srcLang = "CMAKE_DEPENDS_CHECK_" + this->Language;
35
0
      cmMakefile* mf = this->LocalGenerator->GetMakefile();
36
0
      pairs.assign(mf->GetSafeDefinition(srcLang));
37
0
    }
38
0
    for (auto si = pairs.begin(); si != pairs.end();) {
39
      // Get the source and object file.
40
0
      std::string const& src = *si++;
41
0
      if (si == pairs.end()) {
42
0
        break;
43
0
      }
44
0
      std::string const& obj = *si++;
45
0
      dependencies[obj].insert(src);
46
0
    }
47
0
  }
48
0
  for (auto const& d : dependencies) {
49
    // Write the dependencies for this pair.
50
0
    if (!this->WriteDependencies(d.second, d.first, makeDepends,
51
0
                                 internalDepends)) {
52
0
      return false;
53
0
    }
54
0
  }
55
56
0
  return this->Finalize(makeDepends, internalDepends);
57
0
}
58
59
bool cmDepends::Finalize(std::ostream& /*unused*/, std::ostream& /*unused*/)
60
0
{
61
0
  return true;
62
0
}
63
64
bool cmDepends::Check(std::string const& makeFile,
65
                      std::string const& internalFile,
66
                      DependencyMap& validDeps)
67
0
{
68
  // Check whether dependencies must be regenerated.
69
0
  bool okay = true;
70
0
  cmsys::ifstream fin(internalFile.c_str());
71
0
  if (!(fin && this->CheckDependencies(fin, internalFile, validDeps))) {
72
    // Clear all dependencies so they will be regenerated.
73
0
    this->Clear(makeFile);
74
0
    cmSystemTools::RemoveFile(internalFile);
75
0
    this->FileTimeCache->Remove(internalFile);
76
0
    okay = false;
77
0
  }
78
0
  return okay;
79
0
}
80
81
void cmDepends::Clear(std::string const& file) const
82
0
{
83
  // Print verbose output.
84
0
  if (this->Verbose) {
85
0
    cmSystemTools::Stdout(
86
0
      cmStrCat("Clearing dependencies in \"", file, "\".\n"));
87
0
  }
88
89
  // Write an empty dependency file.
90
0
  cmGeneratedFileStream depFileStream(file);
91
0
  depFileStream << "# Empty dependencies file\n"
92
0
                   "# This may be replaced when dependencies are built.\n";
93
0
}
94
95
bool cmDepends::WriteDependencies(std::set<std::string> const& /*unused*/,
96
                                  std::string const& /*unused*/,
97
                                  std::ostream& /*unused*/,
98
                                  std::ostream& /*unused*/)
99
0
{
100
  // This should be implemented by the subclass.
101
0
  return false;
102
0
}
103
104
bool cmDepends::CheckDependencies(std::istream& internalDepends,
105
                                  std::string const& internalDependsFileName,
106
                                  DependencyMap& validDeps)
107
0
{
108
  // Read internal depends file time
109
0
  cmFileTime internalDependsTime;
110
0
  if (!this->FileTimeCache->Load(internalDependsFileName,
111
0
                                 internalDependsTime)) {
112
0
    return false;
113
0
  }
114
115
  // Parse dependencies from the stream.  If any dependee is missing
116
  // or newer than the depender then dependencies should be
117
  // regenerated.
118
0
  bool okay = true;
119
0
  bool dependerExists = false;
120
121
0
  std::string line;
122
0
  line.reserve(1024);
123
0
  std::string depender;
124
0
  std::string dependee;
125
0
  cmFileTime dependerTime;
126
0
  cmFileTime dependeeTime;
127
0
  std::vector<std::string>* currentDependencies = nullptr;
128
129
0
  while (std::getline(internalDepends, line)) {
130
    // Check if this an empty or a comment line
131
0
    if (line.empty() || line.front() == '#') {
132
0
      continue;
133
0
    }
134
    // Drop carriage return character at the end
135
0
    if (line.back() == '\r') {
136
0
      line.pop_back();
137
0
      if (line.empty()) {
138
0
        continue;
139
0
      }
140
0
    }
141
    // Check if this a depender line
142
0
    if (line.front() != ' ') {
143
0
      depender = line;
144
0
      dependerExists = this->FileTimeCache->Load(depender, dependerTime);
145
      // If we erase validDeps[this->Depender] by overwriting it with an empty
146
      // vector, we lose dependencies for dependers that have multiple
147
      // entries. No need to initialize the entry, std::map will do so on first
148
      // access.
149
0
      currentDependencies = &validDeps[depender];
150
0
      continue;
151
0
    }
152
153
    // This is a dependee line
154
0
    dependee = line.substr(1);
155
156
    // Add dependee to depender's list
157
0
    if (currentDependencies) {
158
0
      currentDependencies->push_back(dependee);
159
0
    }
160
161
    // Dependencies must be regenerated
162
    // * if the dependee does not exist
163
    // * if the depender exists and is older than the dependee.
164
    // * if the depender does not exist, but the dependee is newer than the
165
    //   depends file
166
0
    bool regenerate = false;
167
0
    bool dependeeExists = this->FileTimeCache->Load(dependee, dependeeTime);
168
0
    if (!dependeeExists) {
169
      // The dependee does not exist.
170
0
      regenerate = true;
171
172
      // Print verbose output.
173
0
      if (this->Verbose) {
174
0
        cmSystemTools::Stdout(cmStrCat("Dependee \"", dependee,
175
0
                                       "\" does not exist for depender \"",
176
0
                                       depender, "\".\n"));
177
0
      }
178
0
    } else if (dependerExists) {
179
      // The dependee and depender both exist.  Compare file times.
180
0
      if (dependerTime.Older(dependeeTime)) {
181
        // The depender is older than the dependee.
182
0
        regenerate = true;
183
184
        // Print verbose output.
185
0
        if (this->Verbose) {
186
0
          cmSystemTools::Stdout(cmStrCat("Dependee \"", dependee,
187
0
                                         "\" is newer than depender \"",
188
0
                                         depender, "\".\n"));
189
0
        }
190
0
      }
191
0
    } else {
192
      // The dependee exists, but the depender doesn't. Regenerate if the
193
      // internalDepends file is older than the dependee.
194
0
      if (internalDependsTime.Older(dependeeTime)) {
195
        // The depends-file is older than the dependee.
196
0
        regenerate = true;
197
198
        // Print verbose output.
199
0
        if (this->Verbose) {
200
0
          cmSystemTools::Stdout(cmStrCat("Dependee \"", dependee,
201
0
                                         "\" is newer than depends file \"",
202
0
                                         internalDependsFileName, "\".\n"));
203
0
        }
204
0
      }
205
0
    }
206
207
0
    if (regenerate) {
208
      // Dependencies must be regenerated.
209
0
      okay = false;
210
211
      // Remove the information of this depender from the map, it needs
212
      // to be rescanned
213
0
      if (currentDependencies) {
214
0
        validDeps.erase(depender);
215
0
        currentDependencies = nullptr;
216
0
      }
217
218
      // Remove the depender to be sure it is rebuilt.
219
0
      if (dependerExists) {
220
0
        cmSystemTools::RemoveFile(depender);
221
0
        this->FileTimeCache->Remove(depender);
222
0
        dependerExists = false;
223
0
      }
224
0
    }
225
0
  }
226
227
0
  return okay;
228
0
}
229
230
void cmDepends::SetIncludePathFromLanguage(std::string const& lang)
231
0
{
232
  // Look for the new per "TARGET_" variant first:
233
0
  std::string includePathVar =
234
0
    cmStrCat("CMAKE_", lang, "_TARGET_INCLUDE_PATH");
235
0
  cmMakefile* mf = this->LocalGenerator->GetMakefile();
236
0
  cmValue includePath = mf->GetDefinition(includePathVar);
237
0
  if (includePath) {
238
0
    cmExpandList(*includePath, this->IncludePath);
239
0
  } else {
240
    // Fallback to the old directory level variable if no per-target var:
241
0
    includePathVar = cmStrCat("CMAKE_", lang, "_INCLUDE_PATH");
242
0
    includePath = mf->GetDefinition(includePathVar);
243
0
    if (includePath) {
244
0
      cmExpandList(*includePath, this->IncludePath);
245
0
    }
246
0
  }
247
0
}