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