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