/src/CMake/Source/cmDependsCompiler.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 | | |
4 | | #include "cmDependsCompiler.h" |
5 | | |
6 | | #include <algorithm> |
7 | | #include <map> |
8 | | #include <string> |
9 | | #include <unordered_set> |
10 | | #include <utility> |
11 | | |
12 | | #include <cm/optional> |
13 | | #include <cm/string_view> |
14 | | #include <cm/vector> |
15 | | #include <cmext/string_view> |
16 | | |
17 | | #include "cmsys/FStream.hxx" |
18 | | |
19 | | #include "cmFileTime.h" |
20 | | #include "cmGccDepfileReader.h" |
21 | | #include "cmGccDepfileReaderTypes.h" |
22 | | #include "cmGlobalUnixMakefileGenerator3.h" |
23 | | #include "cmLocalUnixMakefileGenerator3.h" |
24 | | #include "cmStringAlgorithms.h" |
25 | | #include "cmSystemTools.h" |
26 | | |
27 | | bool cmDependsCompiler::CheckDependencies( |
28 | | std::string const& internalDepFile, std::vector<std::string> const& depFiles, |
29 | | cmDepends::DependencyMap& dependencies, |
30 | | std::function<bool(std::string const&)> const& isValidPath) |
31 | 0 | { |
32 | 0 | bool status = true; |
33 | 0 | bool forceReadDeps = true; |
34 | |
|
35 | 0 | cmFileTime internalDepFileTime; |
36 | | // read cached dependencies stored in internal file |
37 | 0 | if (cmSystemTools::FileExists(internalDepFile)) { |
38 | 0 | internalDepFileTime.Load(internalDepFile); |
39 | 0 | forceReadDeps = false; |
40 | | |
41 | | // read current dependencies |
42 | 0 | cmsys::ifstream fin(internalDepFile.c_str()); |
43 | 0 | if (fin) { |
44 | 0 | std::string line; |
45 | 0 | std::string depender; |
46 | 0 | std::vector<std::string>* currentDependencies = nullptr; |
47 | 0 | while (std::getline(fin, line)) { |
48 | 0 | if (line.empty() || line.front() == '#') { |
49 | 0 | continue; |
50 | 0 | } |
51 | | // Drop carriage return character at the end |
52 | 0 | if (line.back() == '\r') { |
53 | 0 | line.pop_back(); |
54 | 0 | if (line.empty()) { |
55 | 0 | continue; |
56 | 0 | } |
57 | 0 | } |
58 | | // Check if this a depender line |
59 | 0 | if (line.front() != ' ') { |
60 | 0 | depender = std::move(line); |
61 | 0 | currentDependencies = &dependencies[depender]; |
62 | 0 | continue; |
63 | 0 | } |
64 | | // This is a dependee line |
65 | 0 | if (currentDependencies) { |
66 | 0 | currentDependencies->emplace_back(line.substr(1)); |
67 | 0 | } |
68 | 0 | } |
69 | 0 | fin.close(); |
70 | 0 | } |
71 | 0 | } |
72 | | |
73 | | // Now, update dependencies map with all new compiler generated |
74 | | // dependencies files |
75 | 0 | cmFileTime depFileTime; |
76 | 0 | for (auto dep = depFiles.begin(); dep != depFiles.end(); dep++) { |
77 | 0 | auto const& source = *dep++; |
78 | 0 | auto const& target = *dep++; |
79 | 0 | auto const& format = *dep++; |
80 | 0 | auto const& depFile = *dep; |
81 | |
|
82 | 0 | if (!cmSystemTools::FileExists(depFile)) { |
83 | 0 | continue; |
84 | 0 | } |
85 | | |
86 | 0 | if (!forceReadDeps) { |
87 | 0 | depFileTime.Load(depFile); |
88 | 0 | } |
89 | 0 | if (forceReadDeps || depFileTime.Compare(internalDepFileTime) >= 0) { |
90 | 0 | status = false; |
91 | 0 | if (this->Verbose) { |
92 | 0 | cmSystemTools::Stdout(cmStrCat("Dependencies file \"", depFile, |
93 | 0 | "\" is newer than depends file \"", |
94 | 0 | internalDepFile, "\".\n")); |
95 | 0 | } |
96 | |
|
97 | 0 | std::vector<std::string> depends; |
98 | 0 | if (format == "custom"_s) { |
99 | 0 | cm::optional<cmGccDepfileContent> deps = cmReadGccDepfile( |
100 | 0 | depFile.c_str(), this->LocalGenerator->GetCurrentBinaryDirectory()); |
101 | 0 | if (!deps || deps->empty()) { |
102 | 0 | continue; |
103 | 0 | } |
104 | | |
105 | 0 | for (auto& entry : *deps) { |
106 | 0 | depends = std::move(entry.paths); |
107 | 0 | if (isValidPath) { |
108 | 0 | cm::erase_if(depends, isValidPath); |
109 | 0 | } |
110 | | // copy depends for each target, except first one, which can be |
111 | | // moved |
112 | 0 | for (auto index = entry.rules.size() - 1; index > 0; --index) { |
113 | 0 | dependencies[entry.rules[index]] = depends; |
114 | 0 | } |
115 | 0 | dependencies[entry.rules.front()] = std::move(depends); |
116 | 0 | } |
117 | 0 | } else { |
118 | 0 | if (format == "msvc"_s) { |
119 | 0 | cmsys::ifstream fin(depFile.c_str()); |
120 | 0 | if (!fin) { |
121 | 0 | continue; |
122 | 0 | } |
123 | | |
124 | 0 | std::string line; |
125 | 0 | if (!isValidPath && !source.empty()) { |
126 | | // insert source as first dependency |
127 | 0 | depends.push_back(source); |
128 | 0 | } |
129 | 0 | while (cmSystemTools::GetLineFromStream(fin, line)) { |
130 | 0 | depends.emplace_back(std::move(line)); |
131 | 0 | } |
132 | 0 | } else if (format == "gcc"_s) { |
133 | 0 | cm::optional<cmGccDepfileContent> deps = cmReadGccDepfile( |
134 | 0 | depFile.c_str(), this->LocalGenerator->GetCurrentBinaryDirectory(), |
135 | 0 | GccDepfilePrependPaths::Deps); |
136 | 0 | if (!deps || deps->empty()) { |
137 | 0 | continue; |
138 | 0 | } |
139 | | |
140 | | // dependencies generated by the compiler contains only one target |
141 | 0 | depends = std::move(deps->front().paths); |
142 | 0 | if (depends.empty()) { |
143 | | // unexpectedly empty, ignore it and continue |
144 | 0 | continue; |
145 | 0 | } |
146 | | |
147 | | // depending of the effective format of the dependencies file |
148 | | // generated by the compiler, the target can be wrongly identified |
149 | | // as a dependency so remove it from the list |
150 | 0 | if (depends.front() == target) { |
151 | 0 | depends.erase(depends.begin()); |
152 | 0 | } |
153 | | |
154 | | // ensure source file is the first dependency |
155 | 0 | if (!source.empty()) { |
156 | 0 | if (depends.front() != source) { |
157 | 0 | cm::erase(depends, source); |
158 | 0 | if (!isValidPath) { |
159 | 0 | depends.insert(depends.begin(), source); |
160 | 0 | } |
161 | 0 | } else if (isValidPath) { |
162 | | // remove first dependency because it must not be filtered out |
163 | 0 | depends.erase(depends.begin()); |
164 | 0 | } |
165 | 0 | } |
166 | 0 | } else { |
167 | | // unknown format, ignore it |
168 | 0 | continue; |
169 | 0 | } |
170 | | |
171 | 0 | if (isValidPath) { |
172 | 0 | cm::erase_if(depends, isValidPath); |
173 | 0 | if (!source.empty()) { |
174 | | // insert source as first dependency |
175 | 0 | depends.insert(depends.begin(), source); |
176 | 0 | } |
177 | 0 | } |
178 | |
|
179 | 0 | dependencies[target] = std::move(depends); |
180 | 0 | } |
181 | 0 | } |
182 | 0 | } |
183 | |
|
184 | 0 | return status; |
185 | 0 | } |
186 | | |
187 | | void cmDependsCompiler::WriteDependencies( |
188 | | cmDepends::DependencyMap const& dependencies, std::ostream& makeDepends, |
189 | | std::ostream& internalDepends) |
190 | 0 | { |
191 | | // dependencies file consumed by make tool |
192 | 0 | auto const& lineContinue = static_cast<cmGlobalUnixMakefileGenerator3*>( |
193 | 0 | this->LocalGenerator->GetGlobalGenerator()) |
194 | 0 | ->LineContinueDirective; |
195 | 0 | bool supportLongLineDepend = static_cast<cmGlobalUnixMakefileGenerator3*>( |
196 | 0 | this->LocalGenerator->GetGlobalGenerator()) |
197 | 0 | ->SupportsLongLineDependencies(); |
198 | 0 | cmDepends::DependencyMap makeDependencies(dependencies); |
199 | 0 | std::unordered_set<cm::string_view> phonyTargets; |
200 | | |
201 | | // external dependencies file |
202 | 0 | for (auto& node : makeDependencies) { |
203 | 0 | auto target = this->LocalGenerator->ConvertToMakefilePath( |
204 | 0 | this->LocalGenerator->MaybeRelativeToTopBinDir(node.first)); |
205 | 0 | auto& deps = node.second; |
206 | 0 | std::transform(deps.cbegin(), deps.cend(), deps.begin(), |
207 | 0 | [this](std::string const& dep) { |
208 | 0 | return this->LocalGenerator->ConvertToMakefilePath( |
209 | 0 | this->LocalGenerator->MaybeRelativeToTopBinDir(dep)); |
210 | 0 | }); |
211 | |
|
212 | 0 | bool first_dep = true; |
213 | 0 | if (supportLongLineDepend) { |
214 | 0 | makeDepends << target << ": "; |
215 | 0 | } |
216 | 0 | for (auto const& dep : deps) { |
217 | 0 | if (supportLongLineDepend) { |
218 | 0 | if (first_dep) { |
219 | 0 | first_dep = false; |
220 | 0 | makeDepends << dep; |
221 | 0 | } else { |
222 | 0 | makeDepends << ' ' << lineContinue << " " << dep; |
223 | 0 | } |
224 | 0 | } else { |
225 | 0 | makeDepends << target << ": " << dep << std::endl; |
226 | 0 | } |
227 | |
|
228 | 0 | phonyTargets.emplace(dep.data(), dep.length()); |
229 | 0 | } |
230 | 0 | makeDepends << std::endl << std::endl; |
231 | 0 | } |
232 | | |
233 | | // add phony targets |
234 | 0 | for (auto const& target : phonyTargets) { |
235 | 0 | makeDepends << std::endl << target << ':' << std::endl; |
236 | 0 | } |
237 | | |
238 | | // internal dependencies file |
239 | 0 | for (auto const& node : dependencies) { |
240 | 0 | internalDepends << node.first << std::endl; |
241 | 0 | for (auto const& dep : node.second) { |
242 | 0 | internalDepends << ' ' << dep << std::endl; |
243 | 0 | } |
244 | 0 | internalDepends << std::endl; |
245 | 0 | } |
246 | 0 | } |
247 | | |
248 | | void cmDependsCompiler::ClearDependencies( |
249 | | std::vector<std::string> const& depFiles) |
250 | 0 | { |
251 | 0 | for (auto dep = depFiles.begin(); dep != depFiles.end(); dep++) { |
252 | 0 | dep += 3; |
253 | 0 | cmSystemTools::RemoveFile(*dep); |
254 | 0 | } |
255 | 0 | } |