/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 | } |