/src/CMake/Source/cmTargetTraceDependencies.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 "cmTargetTraceDependencies.h" |
4 | | |
5 | | #include <sstream> |
6 | | #include <utility> |
7 | | |
8 | | #include <cmext/algorithm> |
9 | | #include <cmext/string_view> |
10 | | |
11 | | #include "cmCustomCommand.h" |
12 | | #include "cmCustomCommandGenerator.h" |
13 | | #include "cmGeneratorTarget.h" |
14 | | #include "cmGlobalGenerator.h" |
15 | | #include "cmList.h" |
16 | | #include "cmListFileCache.h" |
17 | | #include "cmMakefile.h" |
18 | | #include "cmMessageType.h" |
19 | | #include "cmSourceFile.h" |
20 | | #include "cmStateTypes.h" |
21 | | #include "cmStringAlgorithms.h" |
22 | | #include "cmSystemTools.h" |
23 | | #include "cmTarget.h" |
24 | | #include "cmValue.h" |
25 | | |
26 | | cmTargetTraceDependencies::cmTargetTraceDependencies(cmGeneratorTarget* target) |
27 | 0 | : GeneratorTarget(target) |
28 | 0 | { |
29 | | // Convenience. |
30 | 0 | this->Makefile = target->Target->GetMakefile(); |
31 | 0 | this->LocalGenerator = target->GetLocalGenerator(); |
32 | 0 | this->GlobalGenerator = this->LocalGenerator->GetGlobalGenerator(); |
33 | 0 | this->CurrentEntry = nullptr; |
34 | | |
35 | | // Queue all the source files already specified for the target. |
36 | 0 | std::set<cmSourceFile*> emitted; |
37 | 0 | std::vector<std::string> const& configs = |
38 | 0 | this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); |
39 | 0 | for (std::string const& c : configs) { |
40 | 0 | std::vector<cmSourceFile*> sources; |
41 | 0 | this->GeneratorTarget->GetSourceFiles(sources, c); |
42 | 0 | for (cmSourceFile* sf : sources) { |
43 | 0 | std::set<cmGeneratorTarget const*> const tgts = |
44 | 0 | this->GlobalGenerator->GetFilenameTargetDepends(sf); |
45 | 0 | if (cm::contains(tgts, this->GeneratorTarget)) { |
46 | 0 | std::ostringstream e; |
47 | 0 | e << "Evaluation output file\n \"" << sf->ResolveFullPath() |
48 | 0 | << "\"\ndepends on the sources of a target it is used in. This " |
49 | 0 | "is a dependency loop and is not allowed."; |
50 | 0 | this->GeneratorTarget->LocalGenerator->IssueMessage( |
51 | 0 | MessageType::FATAL_ERROR, e.str()); |
52 | 0 | return; |
53 | 0 | } |
54 | 0 | if (emitted.insert(sf).second && this->SourcesQueued.insert(sf).second) { |
55 | 0 | this->SourceQueue.push(sf); |
56 | 0 | } |
57 | 0 | } |
58 | 0 | } |
59 | | |
60 | | // Queue pre-build, pre-link, and post-build rule dependencies. |
61 | 0 | this->CheckCustomCommands(this->GeneratorTarget->GetPreBuildCommands()); |
62 | 0 | this->CheckCustomCommands(this->GeneratorTarget->GetPreLinkCommands()); |
63 | 0 | this->CheckCustomCommands(this->GeneratorTarget->GetPostBuildCommands()); |
64 | 0 | } |
65 | | |
66 | | void cmTargetTraceDependencies::Trace() |
67 | 0 | { |
68 | | // Process one dependency at a time until the queue is empty. |
69 | 0 | while (!this->SourceQueue.empty()) { |
70 | | // Get the next source from the queue. |
71 | 0 | cmSourceFile* sf = this->SourceQueue.front(); |
72 | 0 | this->SourceQueue.pop(); |
73 | 0 | this->CurrentEntry = &this->GeneratorTarget->SourceDepends[sf]; |
74 | | |
75 | | // Queue dependencies added explicitly by the user. |
76 | 0 | if (cmValue additionalDeps = sf->GetProperty("OBJECT_DEPENDS")) { |
77 | 0 | cmList objDeps{ *additionalDeps }; |
78 | 0 | for (auto& objDep : objDeps) { |
79 | 0 | if (cmSystemTools::FileIsFullPath(objDep)) { |
80 | 0 | objDep = cmSystemTools::CollapseFullPath(objDep); |
81 | 0 | } |
82 | 0 | } |
83 | 0 | this->FollowNames(objDeps); |
84 | 0 | } |
85 | | |
86 | | // Queue the source needed to generate this file, if any. |
87 | 0 | this->FollowName(sf->ResolveFullPath()); |
88 | | |
89 | | // Queue dependencies added programmatically by commands. |
90 | 0 | this->FollowNames(sf->GetDepends()); |
91 | | |
92 | | // Queue custom command dependencies. |
93 | 0 | if (cmCustomCommand const* cc = sf->GetCustomCommand()) { |
94 | 0 | this->CheckCustomCommand(*cc); |
95 | 0 | } |
96 | 0 | } |
97 | 0 | this->CurrentEntry = nullptr; |
98 | |
|
99 | 0 | this->GeneratorTarget->AddTracedSources(this->NewSources); |
100 | 0 | } |
101 | | |
102 | | void cmTargetTraceDependencies::QueueSource(cmSourceFile* sf) |
103 | 0 | { |
104 | 0 | if (this->SourcesQueued.insert(sf).second) { |
105 | 0 | this->SourceQueue.push(sf); |
106 | | |
107 | | // Make sure this file is in the target at the end. |
108 | 0 | this->NewSources.push_back(sf->ResolveFullPath()); |
109 | 0 | } |
110 | 0 | } |
111 | | |
112 | | void cmTargetTraceDependencies::FollowName(std::string const& name) |
113 | 0 | { |
114 | | // Use lower bound with key comparison to not repeat the search for the |
115 | | // insert position if the name could not be found (which is the common case). |
116 | 0 | auto i = this->NameMap.lower_bound(name); |
117 | 0 | if (i == this->NameMap.end() || i->first != name) { |
118 | | // Check if we know how to generate this file. |
119 | 0 | cmSourcesWithOutput sources = |
120 | 0 | this->LocalGenerator->GetSourcesWithOutput(name); |
121 | | // If we failed to find a target or source and we have a relative path, it |
122 | | // might be a valid source if made relative to the current binary |
123 | | // directory. |
124 | 0 | if (!sources.Target && !sources.Source && |
125 | 0 | !cmSystemTools::FileIsFullPath(name)) { |
126 | 0 | auto fullname = |
127 | 0 | cmStrCat(this->Makefile->GetCurrentBinaryDirectory(), '/', name); |
128 | 0 | fullname = cmSystemTools::CollapseFullPath( |
129 | 0 | fullname, this->Makefile->GetHomeOutputDirectory()); |
130 | 0 | sources = this->LocalGenerator->GetSourcesWithOutput(fullname); |
131 | 0 | } |
132 | 0 | i = this->NameMap.emplace_hint(i, name, sources); |
133 | 0 | } |
134 | 0 | if (cmTarget* t = i->second.Target) { |
135 | | // The name is a byproduct of a utility target or a PRE_BUILD, PRE_LINK, or |
136 | | // POST_BUILD command. |
137 | 0 | this->GeneratorTarget->Target->AddUtility(t->GetName(), false); |
138 | |
|
139 | 0 | this->GeneratorTarget->Target->AddCodegenDependency(t->GetName()); |
140 | 0 | } |
141 | 0 | if (cmSourceFile* sf = i->second.Source) { |
142 | | // For now only follow the dependency if the source file is not a |
143 | | // byproduct. Semantics of byproducts in a non-Ninja context will have to |
144 | | // be defined first. |
145 | 0 | if (!i->second.SourceIsByproduct) { |
146 | | // Record the dependency we just followed. |
147 | 0 | if (this->CurrentEntry) { |
148 | 0 | this->CurrentEntry->Depends.push_back(sf); |
149 | 0 | } |
150 | 0 | this->QueueSource(sf); |
151 | 0 | } |
152 | 0 | } |
153 | 0 | } |
154 | | |
155 | | void cmTargetTraceDependencies::FollowNames( |
156 | | std::vector<std::string> const& names) |
157 | 0 | { |
158 | 0 | for (std::string const& name : names) { |
159 | 0 | this->FollowName(name); |
160 | 0 | } |
161 | 0 | } |
162 | | |
163 | | bool cmTargetTraceDependencies::IsUtility(std::string const& dep) |
164 | 0 | { |
165 | | // Dependencies on targets (utilities) are supposed to be named by |
166 | | // just the target name. However for compatibility we support |
167 | | // naming the output file generated by the target (assuming there is |
168 | | // no output-name property which old code would not have set). In |
169 | | // that case the target name will be the file basename of the |
170 | | // dependency. |
171 | 0 | std::string util; |
172 | 0 | if (cmHasSuffix(dep, ".exe"_s)) { |
173 | 0 | util = cmSystemTools::GetFilenameWithoutLastExtension(dep); |
174 | 0 | } else { |
175 | 0 | util = cmSystemTools::GetFilenameName(dep); |
176 | 0 | } |
177 | | |
178 | | // Check for a target with this name. |
179 | 0 | if (cmGeneratorTarget* t = |
180 | 0 | this->GeneratorTarget->GetLocalGenerator()->FindGeneratorTargetToUse( |
181 | 0 | util)) { |
182 | | // If we find the target and the dep was given as a full path, |
183 | | // then make sure it was not a full path to something else, and |
184 | | // the fact that the name matched a target was just a coincidence. |
185 | 0 | if (cmSystemTools::FileIsFullPath(dep)) { |
186 | 0 | if (t->GetType() >= cmStateEnums::EXECUTABLE && |
187 | 0 | t->GetType() <= cmStateEnums::MODULE_LIBRARY) { |
188 | | // This is really only for compatibility so we do not need to |
189 | | // worry about configuration names and output names. |
190 | 0 | std::string tLocation = t->GetLocationForBuild(); |
191 | 0 | tLocation = cmSystemTools::GetFilenamePath(tLocation); |
192 | 0 | std::string depLocation = cmSystemTools::GetFilenamePath(dep); |
193 | 0 | depLocation = cmSystemTools::CollapseFullPath(depLocation); |
194 | 0 | tLocation = cmSystemTools::CollapseFullPath(tLocation); |
195 | 0 | if (depLocation == tLocation) { |
196 | 0 | this->GeneratorTarget->Target->AddUtility(util, false); |
197 | 0 | return true; |
198 | 0 | } |
199 | 0 | } |
200 | 0 | } else { |
201 | | // The original name of the dependency was not a full path. It |
202 | | // must name a target, so add the target-level dependency. |
203 | 0 | this->GeneratorTarget->Target->AddUtility(util, true); |
204 | 0 | return true; |
205 | 0 | } |
206 | 0 | } |
207 | | |
208 | | // The dependency does not name a target built in this project. |
209 | 0 | return false; |
210 | 0 | } |
211 | | |
212 | | void cmTargetTraceDependencies::CheckCustomCommand(cmCustomCommand const& cc) |
213 | 0 | { |
214 | | // Collect dependencies referenced by all configurations. |
215 | 0 | std::set<std::string> depends; |
216 | 0 | for (std::string const& config : |
217 | 0 | this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig)) { |
218 | 0 | for (cmCustomCommandGenerator const& ccg : |
219 | 0 | this->LocalGenerator->MakeCustomCommandGenerators(cc, config)) { |
220 | | // Collect target-level dependencies referenced in command lines. |
221 | 0 | for (auto const& util : ccg.GetUtilities()) { |
222 | 0 | this->GeneratorTarget->Target->AddUtility(util); |
223 | |
|
224 | 0 | if (ccg.GetCC().GetCodegen()) { |
225 | 0 | this->GeneratorTarget->Target->AddCodegenDependency( |
226 | 0 | util.Value.first); |
227 | 0 | } |
228 | 0 | } |
229 | | |
230 | | // Collect file-level dependencies referenced in DEPENDS. |
231 | 0 | depends.insert(ccg.GetDepends().begin(), ccg.GetDepends().end()); |
232 | 0 | } |
233 | 0 | } |
234 | | |
235 | | // Queue file-level dependencies. |
236 | 0 | for (std::string const& dep : depends) { |
237 | 0 | if (!this->IsUtility(dep)) { |
238 | | // The dependency does not name a target and may be a file we |
239 | | // know how to generate. Queue it. |
240 | 0 | this->FollowName(dep); |
241 | 0 | } else { |
242 | 0 | this->GeneratorTarget->Target->AddCodegenDependency(dep); |
243 | 0 | } |
244 | 0 | } |
245 | 0 | } |
246 | | |
247 | | void cmTargetTraceDependencies::CheckCustomCommands( |
248 | | std::vector<cmCustomCommand> const& commands) |
249 | 0 | { |
250 | 0 | for (cmCustomCommand const& command : commands) { |
251 | 0 | this->CheckCustomCommand(command); |
252 | 0 | } |
253 | 0 | } |