/src/CMake/Source/cmMakefile.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 "cmConfigure.h" // IWYU pragma: keep |
4 | | |
5 | | #include "cmMakefile.h" |
6 | | |
7 | | #include <algorithm> |
8 | | #include <cassert> |
9 | | #include <cstdio> |
10 | | #include <cstdlib> |
11 | | #include <cstring> |
12 | | #include <sstream> |
13 | | #include <utility> |
14 | | |
15 | | #include <cm/iterator> |
16 | | #include <cm/memory> |
17 | | #include <cm/optional> |
18 | | #include <cm/type_traits> // IWYU pragma: keep |
19 | | #include <cm/vector> |
20 | | #include <cmext/algorithm> |
21 | | #include <cmext/string_view> |
22 | | |
23 | | #ifndef CMAKE_BOOTSTRAP |
24 | | # include <cm3p/json/value.h> |
25 | | # include <cm3p/json/writer.h> |
26 | | #endif |
27 | | |
28 | | #include "cmsys/FStream.hxx" |
29 | | #include "cmsys/RegularExpression.hxx" |
30 | | #include "cmsys/String.h" |
31 | | |
32 | | #include "cmCustomCommand.h" |
33 | | #include "cmCustomCommandLines.h" |
34 | | #include "cmCustomCommandTypes.h" |
35 | | #include "cmExecutionStatus.h" |
36 | | #include "cmExpandedCommandArgument.h" // IWYU pragma: keep |
37 | | #include "cmExportBuildFileGenerator.h" |
38 | | #include "cmFileLockPool.h" |
39 | | #include "cmFunctionBlocker.h" |
40 | | #include "cmGeneratedFileStream.h" |
41 | | #include "cmGeneratorExpression.h" |
42 | | #include "cmGeneratorExpressionEvaluationFile.h" |
43 | | #include "cmGlobalGenerator.h" |
44 | | #include "cmInstallGenerator.h" // IWYU pragma: keep |
45 | | #include "cmInstallSubdirectoryGenerator.h" |
46 | | #include "cmList.h" |
47 | | #include "cmListFileCache.h" |
48 | | #include "cmLocalGenerator.h" |
49 | | #include "cmMessageType.h" |
50 | | #include "cmRange.h" |
51 | | #include "cmSourceFile.h" |
52 | | #include "cmSourceFileLocation.h" |
53 | | #include "cmSourceGroup.h" |
54 | | #include "cmStack.h" |
55 | | #include "cmState.h" |
56 | | #include "cmStateDirectory.h" |
57 | | #include "cmStateTypes.h" |
58 | | #include "cmStringAlgorithms.h" |
59 | | #include "cmSystemTools.h" |
60 | | #include "cmTarget.h" |
61 | | #include "cmTargetLinkLibraryType.h" |
62 | | #include "cmTest.h" |
63 | | #include "cmTestGenerator.h" // IWYU pragma: keep |
64 | | #include "cmVersion.h" |
65 | | #include "cmWorkingDirectory.h" |
66 | | #include "cmake.h" |
67 | | |
68 | | #ifndef CMAKE_BOOTSTRAP |
69 | | # include "cmMakefileProfilingData.h" |
70 | | # include "cmVariableWatch.h" |
71 | | #endif |
72 | | |
73 | | #ifdef CMake_ENABLE_DEBUGGER |
74 | | # include "cmDebuggerAdapter.h" |
75 | | #endif |
76 | | |
77 | | #ifndef __has_feature |
78 | | # define __has_feature(x) 0 |
79 | | #endif |
80 | | |
81 | | // Select a recursion limit that fits within the stack size. |
82 | | // See stack size flags in '../CompileFlags.cmake'. |
83 | | #ifndef CMake_DEFAULT_RECURSION_LIMIT |
84 | | # if __has_feature(address_sanitizer) |
85 | | # define CMake_DEFAULT_RECURSION_LIMIT 400 |
86 | | # elif defined(_MSC_VER) && defined(_DEBUG) |
87 | | # define CMake_DEFAULT_RECURSION_LIMIT 600 |
88 | | # elif defined(__ibmxl__) && defined(__linux) |
89 | | # define CMake_DEFAULT_RECURSION_LIMIT 600 |
90 | | # else |
91 | 0 | # define CMake_DEFAULT_RECURSION_LIMIT 1000 |
92 | | # endif |
93 | | #endif |
94 | | |
95 | | namespace { |
96 | | std::string const kCMAKE_CURRENT_LIST_DIR = "CMAKE_CURRENT_LIST_DIR"; |
97 | | std::string const kCMAKE_CURRENT_LIST_FILE = "CMAKE_CURRENT_LIST_FILE"; |
98 | | std::string const kCMAKE_PARENT_LIST_FILE = "CMAKE_PARENT_LIST_FILE"; |
99 | | |
100 | | class FileScopeBase |
101 | | { |
102 | | protected: |
103 | | cmMakefile* Makefile; |
104 | | |
105 | | private: |
106 | | std::string OldCurrent; |
107 | | cm::optional<std::string> OldParent; |
108 | | |
109 | | public: |
110 | | FileScopeBase(cmMakefile* mf) |
111 | 1 | : Makefile(mf) |
112 | 1 | { |
113 | 1 | } |
114 | | void PushListFileVars(std::string const& newCurrent) |
115 | 1 | { |
116 | 1 | if (cmValue p = this->Makefile->GetDefinition(kCMAKE_PARENT_LIST_FILE)) { |
117 | 0 | this->OldParent = *p; |
118 | 0 | } |
119 | 1 | if (cmValue c = this->Makefile->GetDefinition(kCMAKE_CURRENT_LIST_FILE)) { |
120 | 0 | this->OldCurrent = *c; |
121 | 0 | this->Makefile->AddDefinition(kCMAKE_PARENT_LIST_FILE, this->OldCurrent); |
122 | 0 | this->Makefile->MarkVariableAsUsed(kCMAKE_PARENT_LIST_FILE); |
123 | 0 | } |
124 | 1 | this->Makefile->AddDefinition(kCMAKE_CURRENT_LIST_FILE, newCurrent); |
125 | 1 | this->Makefile->AddDefinition(kCMAKE_CURRENT_LIST_DIR, |
126 | 1 | cmSystemTools::GetFilenamePath(newCurrent)); |
127 | 1 | this->Makefile->MarkVariableAsUsed(kCMAKE_CURRENT_LIST_FILE); |
128 | 1 | this->Makefile->MarkVariableAsUsed(kCMAKE_CURRENT_LIST_DIR); |
129 | 1 | } |
130 | | void PopListFileVars() |
131 | 1 | { |
132 | 1 | if (this->OldParent) { |
133 | 0 | this->Makefile->AddDefinition(kCMAKE_PARENT_LIST_FILE, *this->OldParent); |
134 | 0 | this->Makefile->MarkVariableAsUsed(kCMAKE_PARENT_LIST_FILE); |
135 | 1 | } else { |
136 | 1 | this->Makefile->RemoveDefinition(kCMAKE_PARENT_LIST_FILE); |
137 | 1 | } |
138 | 1 | this->Makefile->AddDefinition(kCMAKE_CURRENT_LIST_FILE, this->OldCurrent); |
139 | 1 | this->Makefile->AddDefinition( |
140 | 1 | kCMAKE_CURRENT_LIST_DIR, |
141 | 1 | cmSystemTools::GetFilenamePath(this->OldCurrent)); |
142 | 1 | this->Makefile->MarkVariableAsUsed(kCMAKE_CURRENT_LIST_FILE); |
143 | 1 | this->Makefile->MarkVariableAsUsed(kCMAKE_CURRENT_LIST_DIR); |
144 | 1 | } |
145 | | }; |
146 | | } |
147 | | |
148 | | class cmMessenger; |
149 | | |
150 | | cmDirectoryId::cmDirectoryId(std::string s) |
151 | 0 | : String(std::move(s)) |
152 | 0 | { |
153 | 0 | } |
154 | | |
155 | | // default is not to be building executables |
156 | | cmMakefile::cmMakefile(cmGlobalGenerator* globalGenerator, |
157 | | cmStateSnapshot const& snapshot) |
158 | 1 | : GlobalGenerator(globalGenerator) |
159 | 1 | , StateSnapshot(snapshot) |
160 | 1 | { |
161 | 1 | this->IsSourceFileTryCompile = false; |
162 | | |
163 | 1 | this->CheckSystemVars = this->GetCMakeInstance()->GetCheckSystemVars(); |
164 | | |
165 | | // Setup the default include complaint regular expression (match nothing). |
166 | 1 | this->ComplainFileRegularExpression = "^$"; |
167 | | |
168 | 1 | this->DefineFlags = " "; |
169 | | |
170 | 1 | this->cmDefineRegex.compile("#([ \t]*)cmakedefine[ \t]+([A-Za-z_0-9]*)"); |
171 | 1 | this->cmDefine01Regex.compile("#([ \t]*)cmakedefine01[ \t]+([A-Za-z_0-9]*)"); |
172 | 1 | this->cmNamedCurly.compile("^[A-Za-z0-9/_.+-]+{"); |
173 | | |
174 | 1 | this->StateSnapshot = |
175 | 1 | this->StateSnapshot.GetState()->CreatePolicyScopeSnapshot( |
176 | 1 | this->StateSnapshot); |
177 | | |
178 | | // Enter a policy level for this directory. |
179 | 1 | this->PushPolicy(); |
180 | | |
181 | | // push empty loop block |
182 | 1 | this->PushLoopBlockBarrier(); |
183 | | |
184 | | // By default the check is not done. It is enabled by |
185 | | // cmMakefile::Configure in the top level if necessary. |
186 | 1 | this->CheckCMP0000 = false; |
187 | | |
188 | 1 | #if !defined(CMAKE_BOOTSTRAP) |
189 | 1 | this->AddSourceGroup("", "^.*$"); |
190 | 1 | this->AddSourceGroup("Source Files", CM_SOURCE_REGEX); |
191 | 1 | this->AddSourceGroup("Header Files", CM_HEADER_REGEX); |
192 | 1 | this->AddSourceGroup("Precompile Header File", CM_PCH_REGEX); |
193 | 1 | this->AddSourceGroup("CMake Rules", "\\.rule$"); |
194 | 1 | this->AddSourceGroup("Resources", CM_RESOURCE_REGEX); |
195 | 1 | this->AddSourceGroup("Object Files", "\\.(lo|o|obj)$"); |
196 | | |
197 | 1 | this->ObjectLibrariesSourceGroupIndex = this->SourceGroups.size(); |
198 | 1 | this->SourceGroups.emplace_back( |
199 | 1 | cm::make_unique<cmSourceGroup>("Object Libraries", "^MATCH_NO_SOURCES$")); |
200 | 1 | #endif |
201 | 1 | } |
202 | | |
203 | 1 | cmMakefile::~cmMakefile() = default; |
204 | | |
205 | | cmDirectoryId cmMakefile::GetDirectoryId() const |
206 | 0 | { |
207 | | // Use the instance pointer value to uniquely identify this directory. |
208 | | // If we ever need to expose this to CMake language code we should |
209 | | // add a read-only property in cmMakefile::GetProperty. |
210 | 0 | char buf[32]; |
211 | 0 | snprintf(buf, sizeof(buf), "(%p)", |
212 | 0 | static_cast<void const*>(this)); // cast avoids format warning |
213 | 0 | return std::string(buf); |
214 | 0 | } |
215 | | |
216 | | void cmMakefile::IssueMessage(MessageType t, std::string const& text) const |
217 | 0 | { |
218 | 0 | if (!this->ExecutionStatusStack.empty()) { |
219 | 0 | if ((t == MessageType::FATAL_ERROR) || |
220 | 0 | (t == MessageType::INTERNAL_ERROR)) { |
221 | 0 | this->ExecutionStatusStack.back()->SetNestedError(); |
222 | 0 | } |
223 | 0 | } |
224 | 0 | this->GetCMakeInstance()->IssueMessage(t, text, this->Backtrace); |
225 | 0 | } |
226 | | |
227 | | Message::LogLevel cmMakefile::GetCurrentLogLevel() const |
228 | 0 | { |
229 | 0 | cmake const* cmakeInstance = this->GetCMakeInstance(); |
230 | |
|
231 | 0 | Message::LogLevel const logLevelCliOrDefault = cmakeInstance->GetLogLevel(); |
232 | 0 | assert("Expected a valid log level here" && |
233 | 0 | logLevelCliOrDefault != Message::LogLevel::LOG_UNDEFINED); |
234 | |
|
235 | 0 | Message::LogLevel result = logLevelCliOrDefault; |
236 | | |
237 | | // If the log-level was set via the command line option, it takes precedence |
238 | | // over the CMAKE_MESSAGE_LOG_LEVEL variable. |
239 | 0 | if (!cmakeInstance->WasLogLevelSetViaCLI()) { |
240 | 0 | Message::LogLevel const logLevelFromVar = cmake::StringToLogLevel( |
241 | 0 | this->GetSafeDefinition("CMAKE_MESSAGE_LOG_LEVEL")); |
242 | 0 | if (logLevelFromVar != Message::LogLevel::LOG_UNDEFINED) { |
243 | 0 | result = logLevelFromVar; |
244 | 0 | } |
245 | 0 | } |
246 | |
|
247 | 0 | return result; |
248 | 0 | } |
249 | | |
250 | | void cmMakefile::IssueInvalidTargetNameError( |
251 | | std::string const& targetName) const |
252 | 0 | { |
253 | 0 | this->IssueMessage( |
254 | 0 | MessageType::FATAL_ERROR, |
255 | 0 | cmStrCat("The target name \"", targetName, |
256 | 0 | "\" is reserved or not valid for certain " |
257 | 0 | "CMake features, such as generator expressions, and may result " |
258 | 0 | "in undefined behavior.")); |
259 | 0 | } |
260 | | |
261 | | void cmMakefile::MaybeWarnCMP0074(std::string const& rootVar, cmValue rootDef, |
262 | | cm::optional<std::string> const& rootEnv) |
263 | 0 | { |
264 | | // Warn if a <PackageName>_ROOT variable we may use is set. |
265 | 0 | if ((rootDef || rootEnv) && this->WarnedCMP0074.insert(rootVar).second) { |
266 | 0 | auto e = cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0074), '\n'); |
267 | 0 | if (rootDef) { |
268 | 0 | e += cmStrCat("CMake variable ", rootVar, " is set to:\n ", *rootDef, |
269 | 0 | '\n'); |
270 | 0 | } |
271 | 0 | if (rootEnv) { |
272 | 0 | e += cmStrCat("Environment variable ", rootVar, " is set to:\n ", |
273 | 0 | *rootEnv, '\n'); |
274 | 0 | } |
275 | 0 | e += "For compatibility, CMake is ignoring the variable."; |
276 | 0 | this->IssueMessage(MessageType::AUTHOR_WARNING, e); |
277 | 0 | } |
278 | 0 | } |
279 | | |
280 | | void cmMakefile::MaybeWarnCMP0144(std::string const& rootVar, cmValue rootDef, |
281 | | cm::optional<std::string> const& rootEnv) |
282 | 0 | { |
283 | | // Warn if a <PACKAGENAME>_ROOT variable we may use is set. |
284 | 0 | if ((rootDef || rootEnv) && this->WarnedCMP0144.insert(rootVar).second) { |
285 | 0 | auto e = cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0144), '\n'); |
286 | 0 | if (rootDef) { |
287 | 0 | e += cmStrCat("CMake variable ", rootVar, " is set to:\n ", *rootDef, |
288 | 0 | '\n'); |
289 | 0 | } |
290 | 0 | if (rootEnv) { |
291 | 0 | e += cmStrCat("Environment variable ", rootVar, " is set to:\n ", |
292 | 0 | *rootEnv, '\n'); |
293 | 0 | } |
294 | 0 | e += "For compatibility, find_package is ignoring the variable, but " |
295 | 0 | "code in a .cmake module might still use it."; |
296 | 0 | this->IssueMessage(MessageType::AUTHOR_WARNING, e); |
297 | 0 | } |
298 | 0 | } |
299 | | |
300 | | cmBTStringRange cmMakefile::GetIncludeDirectoriesEntries() const |
301 | 0 | { |
302 | 0 | return this->StateSnapshot.GetDirectory().GetIncludeDirectoriesEntries(); |
303 | 0 | } |
304 | | |
305 | | cmBTStringRange cmMakefile::GetCompileOptionsEntries() const |
306 | 0 | { |
307 | 0 | return this->StateSnapshot.GetDirectory().GetCompileOptionsEntries(); |
308 | 0 | } |
309 | | |
310 | | cmBTStringRange cmMakefile::GetCompileDefinitionsEntries() const |
311 | 0 | { |
312 | 0 | return this->StateSnapshot.GetDirectory().GetCompileDefinitionsEntries(); |
313 | 0 | } |
314 | | |
315 | | cmBTStringRange cmMakefile::GetLinkOptionsEntries() const |
316 | 0 | { |
317 | 0 | return this->StateSnapshot.GetDirectory().GetLinkOptionsEntries(); |
318 | 0 | } |
319 | | |
320 | | cmBTStringRange cmMakefile::GetLinkDirectoriesEntries() const |
321 | 0 | { |
322 | 0 | return this->StateSnapshot.GetDirectory().GetLinkDirectoriesEntries(); |
323 | 0 | } |
324 | | |
325 | | cmListFileBacktrace cmMakefile::GetBacktrace() const |
326 | 0 | { |
327 | 0 | return this->Backtrace; |
328 | 0 | } |
329 | | |
330 | | cmFindPackageStack cmMakefile::GetFindPackageStack() const |
331 | 0 | { |
332 | 0 | return this->FindPackageStack; |
333 | 0 | } |
334 | | |
335 | | void cmMakefile::PrintCommandTrace(cmListFileFunction const& lff, |
336 | | cmListFileBacktrace const& bt, |
337 | | CommandMissingFromStack missing) const |
338 | 0 | { |
339 | | // Check if current file in the list of requested to trace... |
340 | 0 | std::vector<std::string> const& trace_only_this_files = |
341 | 0 | this->GetCMakeInstance()->GetTraceSources(); |
342 | 0 | std::string const& full_path = bt.Top().FilePath; |
343 | 0 | cm::string_view only_filename = |
344 | 0 | cmSystemTools::GetFilenameNameView(full_path); |
345 | 0 | bool trace = trace_only_this_files.empty(); |
346 | 0 | if (!trace) { |
347 | 0 | for (std::string const& file : trace_only_this_files) { |
348 | 0 | std::string::size_type const pos = full_path.rfind(file); |
349 | 0 | trace = (pos != std::string::npos) && |
350 | 0 | ((pos + file.size()) == full_path.size()) && |
351 | 0 | (only_filename == cmSystemTools::GetFilenameNameView(file)); |
352 | 0 | if (trace) { |
353 | 0 | break; |
354 | 0 | } |
355 | 0 | } |
356 | | // Do nothing if current file wasn't requested for trace... |
357 | 0 | if (!trace) { |
358 | 0 | return; |
359 | 0 | } |
360 | 0 | } |
361 | | |
362 | 0 | std::vector<std::string> args; |
363 | 0 | std::string temp; |
364 | 0 | bool expand = this->GetCMakeInstance()->GetTraceExpand(); |
365 | |
|
366 | 0 | args.reserve(lff.Arguments().size()); |
367 | 0 | for (cmListFileArgument const& arg : lff.Arguments()) { |
368 | 0 | if (expand && arg.Delim != cmListFileArgument::Bracket) { |
369 | 0 | temp = arg.Value; |
370 | 0 | this->ExpandVariablesInString(temp); |
371 | 0 | args.push_back(temp); |
372 | 0 | } else { |
373 | 0 | args.push_back(arg.Value); |
374 | 0 | } |
375 | 0 | } |
376 | 0 | cm::optional<std::string> const& deferId = bt.Top().DeferId; |
377 | |
|
378 | 0 | std::ostringstream msg; |
379 | 0 | switch (this->GetCMakeInstance()->GetTraceFormat()) { |
380 | 0 | case cmake::TraceFormat::JSONv1: { |
381 | 0 | #ifndef CMAKE_BOOTSTRAP |
382 | 0 | Json::Value val; |
383 | 0 | Json::StreamWriterBuilder builder; |
384 | 0 | builder["indentation"] = ""; |
385 | 0 | val["file"] = full_path; |
386 | 0 | val["line"] = static_cast<Json::Value::Int64>(lff.Line()); |
387 | 0 | if (lff.Line() != lff.LineEnd()) { |
388 | 0 | val["line_end"] = static_cast<Json::Value::Int64>(lff.LineEnd()); |
389 | 0 | } |
390 | 0 | if (deferId) { |
391 | 0 | val["defer"] = *deferId; |
392 | 0 | } |
393 | 0 | val["cmd"] = lff.OriginalName(); |
394 | 0 | val["args"] = Json::Value(Json::arrayValue); |
395 | 0 | for (std::string const& arg : args) { |
396 | 0 | val["args"].append(arg); |
397 | 0 | } |
398 | 0 | val["time"] = cmSystemTools::GetTime(); |
399 | 0 | val["frame"] = int(missing == CommandMissingFromStack::Yes) + |
400 | 0 | static_cast<Json::Value::UInt64>(this->ExecutionStatusStack.size()); |
401 | 0 | val["global_frame"] = int(missing == CommandMissingFromStack::Yes) + |
402 | 0 | static_cast<Json::Value::UInt64>(this->RecursionDepth); |
403 | 0 | msg << Json::writeString(builder, val); |
404 | 0 | #endif |
405 | 0 | break; |
406 | 0 | } |
407 | 0 | case cmake::TraceFormat::Human: |
408 | 0 | msg << full_path << '(' << lff.Line() << "):"; |
409 | 0 | if (deferId) { |
410 | 0 | msg << "DEFERRED:" << *deferId << ':'; |
411 | 0 | } |
412 | 0 | msg << " " << lff.OriginalName() << '('; |
413 | |
|
414 | 0 | for (std::string const& arg : args) { |
415 | 0 | msg << arg << ' '; |
416 | 0 | } |
417 | 0 | msg << ')'; |
418 | 0 | break; |
419 | 0 | case cmake::TraceFormat::Undefined: |
420 | 0 | msg << "INTERNAL ERROR: Trace format is Undefined"; |
421 | 0 | break; |
422 | 0 | } |
423 | | |
424 | 0 | auto& f = this->GetCMakeInstance()->GetTraceFile(); |
425 | 0 | if (f) { |
426 | 0 | f << msg.str() << '\n'; |
427 | 0 | } else { |
428 | 0 | cmSystemTools::Message(msg.str()); |
429 | 0 | } |
430 | 0 | } |
431 | | |
432 | | cmMakefile::CallRAII::CallRAII(cmMakefile* mf, std::string const& file, |
433 | | cmExecutionStatus& status) |
434 | 0 | : CallRAII(mf, cmListFileContext::FromListFilePath(file), status) |
435 | 0 | { |
436 | 0 | } |
437 | | |
438 | | cmMakefile::CallRAII::CallRAII(cmMakefile* mf, cmListFileContext const& lfc, |
439 | | cmExecutionStatus& status) |
440 | 0 | : Makefile{ mf } |
441 | 0 | { |
442 | 0 | this->Makefile->Backtrace = this->Makefile->Backtrace.Push(lfc); |
443 | 0 | ++this->Makefile->RecursionDepth; |
444 | 0 | this->Makefile->ExecutionStatusStack.push_back(&status); |
445 | 0 | } |
446 | | |
447 | | cmMakefile::CallRAII::~CallRAII() |
448 | 0 | { |
449 | 0 | if (this->Makefile) { |
450 | 0 | this->Detach(); |
451 | 0 | } |
452 | 0 | } |
453 | | |
454 | | cmMakefile* cmMakefile::CallRAII::Detach() |
455 | 0 | { |
456 | 0 | assert(this->Makefile); |
457 | |
|
458 | 0 | this->Makefile->ExecutionStatusStack.pop_back(); |
459 | 0 | --this->Makefile->RecursionDepth; |
460 | 0 | this->Makefile->Backtrace = this->Makefile->Backtrace.Pop(); |
461 | |
|
462 | 0 | auto* const mf = this->Makefile; |
463 | 0 | this->Makefile = nullptr; |
464 | 0 | return mf; |
465 | 0 | } |
466 | | |
467 | | // Helper class to make sure the call stack is valid. |
468 | | class cmMakefile::CallScope : public CallRAII |
469 | | { |
470 | | public: |
471 | | CallScope(cmMakefile* mf, cmListFileFunction const& lff, |
472 | | cm::optional<std::string> deferId, cmExecutionStatus& status) |
473 | 0 | : CallScope{ mf, lff, |
474 | 0 | cmListFileContext::FromListFileFunction( |
475 | 0 | lff, mf->StateSnapshot.GetExecutionListFile(), |
476 | 0 | std::move(deferId)), |
477 | 0 | status } |
478 | 0 | { |
479 | 0 | } |
480 | | |
481 | | CallScope(cmMakefile* mf, cmListFileFunction const& lff, |
482 | | cmListFileContext const& lfc, cmExecutionStatus& status) |
483 | 0 | : CallRAII{ mf, lfc, status } |
484 | 0 | { |
485 | 0 | #if !defined(CMAKE_BOOTSTRAP) |
486 | 0 | this->ProfilingDataRAII = |
487 | 0 | this->Makefile->GetCMakeInstance()->CreateProfilingEntry( |
488 | 0 | "script", lff.LowerCaseName(), [&lff, &lfc]() -> Json::Value { |
489 | 0 | Json::Value argsValue = Json::objectValue; |
490 | 0 | if (!lff.Arguments().empty()) { |
491 | 0 | std::string args; |
492 | 0 | for (auto const& a : lff.Arguments()) { |
493 | 0 | args = cmStrCat(args, args.empty() ? "" : " ", a.Value); |
494 | 0 | } |
495 | 0 | argsValue["functionArgs"] = args; |
496 | 0 | } |
497 | 0 | argsValue["location"] = cmStrCat(lfc.FilePath, ':', lfc.Line); |
498 | 0 | return argsValue; |
499 | 0 | }); |
500 | 0 | #endif |
501 | 0 | #ifdef CMake_ENABLE_DEBUGGER |
502 | 0 | if (this->Makefile->GetCMakeInstance()->GetDebugAdapter()) { |
503 | 0 | this->Makefile->GetCMakeInstance() |
504 | 0 | ->GetDebugAdapter() |
505 | 0 | ->OnBeginFunctionCall(mf, lfc.FilePath, lff); |
506 | 0 | } |
507 | 0 | #endif |
508 | 0 | } |
509 | | |
510 | | ~CallScope() |
511 | 0 | { |
512 | 0 | #if !defined(CMAKE_BOOTSTRAP) |
513 | 0 | this->ProfilingDataRAII.reset(); |
514 | 0 | #endif |
515 | 0 | auto* const mf = this->Detach(); |
516 | 0 | #ifdef CMake_ENABLE_DEBUGGER |
517 | 0 | if (mf->GetCMakeInstance()->GetDebugAdapter()) { |
518 | 0 | mf->GetCMakeInstance()->GetDebugAdapter()->OnEndFunctionCall(); |
519 | 0 | } |
520 | | #else |
521 | | static_cast<void>(mf); |
522 | | #endif |
523 | 0 | } |
524 | | |
525 | | CallScope(CallScope const&) = delete; |
526 | | CallScope& operator=(CallScope const&) = delete; |
527 | | |
528 | | private: |
529 | | #if !defined(CMAKE_BOOTSTRAP) |
530 | | cm::optional<cmMakefileProfilingData::RAII> ProfilingDataRAII; |
531 | | #endif |
532 | | }; |
533 | | |
534 | | void cmMakefile::OnExecuteCommand(std::function<void()> callback) |
535 | 0 | { |
536 | 0 | this->ExecuteCommandCallback = std::move(callback); |
537 | 0 | } |
538 | | |
539 | | bool cmMakefile::ExecuteCommand(cmListFileFunction const& lff, |
540 | | cmExecutionStatus& status, |
541 | | cm::optional<std::string> deferId) |
542 | 0 | { |
543 | 0 | bool result = true; |
544 | | |
545 | | // quick return if blocked |
546 | 0 | if (this->IsFunctionBlocked(lff, status)) { |
547 | | // No error. |
548 | 0 | return result; |
549 | 0 | } |
550 | | |
551 | | // Place this call on the call stack. |
552 | 0 | CallScope stack_manager(this, lff, std::move(deferId), status); |
553 | 0 | static_cast<void>(stack_manager); |
554 | | |
555 | | // Check for maximum recursion depth. |
556 | 0 | size_t depthLimit = this->GetRecursionDepthLimit(); |
557 | 0 | if (this->RecursionDepth > depthLimit) { |
558 | 0 | this->IssueMessage( |
559 | 0 | MessageType::FATAL_ERROR, |
560 | 0 | cmStrCat("Maximum recursion depth of ", depthLimit, " exceeded")); |
561 | 0 | cmSystemTools::SetFatalErrorOccurred(); |
562 | 0 | return false; |
563 | 0 | } |
564 | | |
565 | | // Lookup the command prototype. |
566 | 0 | if (cmState::Command command = |
567 | 0 | this->GetState()->GetCommandByExactName(lff.LowerCaseName())) { |
568 | | // Decide whether to invoke the command. |
569 | 0 | if (!cmSystemTools::GetFatalErrorOccurred()) { |
570 | | // if trace is enabled, print out invoke information |
571 | 0 | if (this->GetCMakeInstance()->GetTrace()) { |
572 | 0 | this->PrintCommandTrace(lff, this->Backtrace); |
573 | 0 | } |
574 | | // Try invoking the command. |
575 | 0 | bool invokeSucceeded = command(lff.Arguments(), status); |
576 | 0 | bool hadNestedError = status.GetNestedError(); |
577 | 0 | if (!invokeSucceeded || hadNestedError) { |
578 | 0 | if (!hadNestedError) { |
579 | | // The command invocation requested that we report an error. |
580 | 0 | std::string const error = |
581 | 0 | cmStrCat(lff.OriginalName(), ' ', status.GetError()); |
582 | 0 | this->IssueMessage(MessageType::FATAL_ERROR, error); |
583 | 0 | } |
584 | 0 | result = false; |
585 | 0 | if (this->GetCMakeInstance()->GetCommandFailureAction() == |
586 | 0 | cmake::CommandFailureAction::FATAL_ERROR) { |
587 | 0 | cmSystemTools::SetFatalErrorOccurred(); |
588 | 0 | } |
589 | 0 | } |
590 | 0 | if (this->GetCMakeInstance()->HasScriptModeExitCode() && |
591 | 0 | this->GetCMakeInstance()->RoleSupportsExitCode()) { |
592 | | // pass-through the exit code from inner cmake_language(EXIT) , |
593 | | // possibly from include() or similar command... |
594 | 0 | status.SetExitCode(this->GetCMakeInstance()->GetScriptModeExitCode()); |
595 | 0 | } |
596 | 0 | } |
597 | 0 | } else { |
598 | 0 | if (!cmSystemTools::GetFatalErrorOccurred()) { |
599 | 0 | std::string error = |
600 | 0 | cmStrCat("Unknown CMake command \"", lff.OriginalName(), "\"."); |
601 | 0 | this->IssueMessage(MessageType::FATAL_ERROR, error); |
602 | 0 | result = false; |
603 | 0 | cmSystemTools::SetFatalErrorOccurred(); |
604 | 0 | } |
605 | 0 | } |
606 | |
|
607 | 0 | if (this->ExecuteCommandCallback) { |
608 | 0 | this->ExecuteCommandCallback(); |
609 | 0 | } |
610 | |
|
611 | 0 | return result; |
612 | 0 | } |
613 | | |
614 | | bool cmMakefile::IsImportedTargetGlobalScope() const |
615 | 0 | { |
616 | 0 | return this->CurrentImportedTargetScope == ImportedTargetScope::Global; |
617 | 0 | } |
618 | | |
619 | | class cmMakefile::IncludeScope : public FileScopeBase |
620 | | { |
621 | | public: |
622 | | IncludeScope(cmMakefile* mf, std::string const& filenametoread, |
623 | | bool noPolicyScope); |
624 | | ~IncludeScope(); |
625 | 0 | void Quiet() { this->ReportError = false; } |
626 | | |
627 | | IncludeScope(IncludeScope const&) = delete; |
628 | | IncludeScope& operator=(IncludeScope const&) = delete; |
629 | | |
630 | | private: |
631 | | bool NoPolicyScope; |
632 | | bool ReportError = true; |
633 | | }; |
634 | | |
635 | | cmMakefile::IncludeScope::IncludeScope(cmMakefile* mf, |
636 | | std::string const& filenametoread, |
637 | | bool noPolicyScope) |
638 | 0 | : FileScopeBase(mf) |
639 | 0 | , NoPolicyScope(noPolicyScope) |
640 | 0 | { |
641 | 0 | this->Makefile->Backtrace = this->Makefile->Backtrace.Push( |
642 | 0 | cmListFileContext::FromListFilePath(filenametoread)); |
643 | |
|
644 | 0 | this->Makefile->PushFunctionBlockerBarrier(); |
645 | |
|
646 | 0 | this->Makefile->StateSnapshot = |
647 | 0 | this->Makefile->GetState()->CreateIncludeFileSnapshot( |
648 | 0 | this->Makefile->StateSnapshot, filenametoread); |
649 | 0 | if (!this->NoPolicyScope) { |
650 | 0 | this->Makefile->PushPolicy(); |
651 | 0 | } |
652 | 0 | this->PushListFileVars(filenametoread); |
653 | 0 | } |
654 | | |
655 | | cmMakefile::IncludeScope::~IncludeScope() |
656 | 0 | { |
657 | 0 | this->PopListFileVars(); |
658 | 0 | if (!this->NoPolicyScope) { |
659 | | // Pop the scope we pushed for the script. |
660 | 0 | this->Makefile->PopPolicy(); |
661 | 0 | } |
662 | 0 | this->Makefile->PopSnapshot(this->ReportError); |
663 | |
|
664 | 0 | this->Makefile->PopFunctionBlockerBarrier(this->ReportError); |
665 | |
|
666 | 0 | this->Makefile->Backtrace = this->Makefile->Backtrace.Pop(); |
667 | 0 | } |
668 | | |
669 | | bool cmMakefile::ReadDependentFile(std::string const& filename, |
670 | | bool noPolicyScope) |
671 | 0 | { |
672 | 0 | std::string filenametoread = cmSystemTools::CollapseFullPath( |
673 | 0 | filename, this->GetCurrentSourceDirectory()); |
674 | |
|
675 | 0 | IncludeScope incScope(this, filenametoread, noPolicyScope); |
676 | |
|
677 | 0 | #ifdef CMake_ENABLE_DEBUGGER |
678 | 0 | if (this->GetCMakeInstance()->GetDebugAdapter()) { |
679 | 0 | this->GetCMakeInstance()->GetDebugAdapter()->OnBeginFileParse( |
680 | 0 | this, filenametoread); |
681 | 0 | } |
682 | 0 | #endif |
683 | |
|
684 | 0 | cmListFile listFile; |
685 | 0 | if (!listFile.ParseFile(filenametoread, this->GetMessenger(), |
686 | 0 | this->Backtrace)) { |
687 | 0 | #ifdef CMake_ENABLE_DEBUGGER |
688 | 0 | if (this->GetCMakeInstance()->GetDebugAdapter()) { |
689 | 0 | this->GetCMakeInstance()->GetDebugAdapter()->OnEndFileParse(); |
690 | 0 | } |
691 | 0 | #endif |
692 | |
|
693 | 0 | return false; |
694 | 0 | } |
695 | | |
696 | 0 | #ifdef CMake_ENABLE_DEBUGGER |
697 | 0 | if (this->GetCMakeInstance()->GetDebugAdapter()) { |
698 | 0 | this->GetCMakeInstance()->GetDebugAdapter()->OnEndFileParse(); |
699 | 0 | this->GetCMakeInstance()->GetDebugAdapter()->OnFileParsedSuccessfully( |
700 | 0 | filenametoread, listFile.Functions); |
701 | 0 | } |
702 | 0 | #endif |
703 | |
|
704 | 0 | this->RunListFile(listFile, filenametoread); |
705 | 0 | if (cmSystemTools::GetFatalErrorOccurred()) { |
706 | 0 | incScope.Quiet(); |
707 | 0 | } |
708 | 0 | return true; |
709 | 0 | } |
710 | | |
711 | | class cmMakefile::ListFileScope : public FileScopeBase |
712 | | { |
713 | | public: |
714 | | ListFileScope(cmMakefile* mf, std::string const& filenametoread) |
715 | 1 | : FileScopeBase(mf) |
716 | 1 | { |
717 | 1 | this->Makefile->Backtrace = this->Makefile->Backtrace.Push( |
718 | 1 | cmListFileContext::FromListFilePath(filenametoread)); |
719 | | |
720 | 1 | this->Makefile->StateSnapshot = |
721 | 1 | this->Makefile->GetState()->CreateInlineListFileSnapshot( |
722 | 1 | this->Makefile->StateSnapshot, filenametoread); |
723 | 1 | assert(this->Makefile->StateSnapshot.IsValid()); |
724 | | |
725 | 1 | this->Makefile->PushFunctionBlockerBarrier(); |
726 | 1 | this->PushListFileVars(filenametoread); |
727 | 1 | } |
728 | | |
729 | | ~ListFileScope() |
730 | 1 | { |
731 | 1 | this->PopListFileVars(); |
732 | 1 | this->Makefile->PopSnapshot(this->ReportError); |
733 | 1 | this->Makefile->PopFunctionBlockerBarrier(this->ReportError); |
734 | 1 | this->Makefile->Backtrace = this->Makefile->Backtrace.Pop(); |
735 | 1 | } |
736 | | |
737 | 0 | void Quiet() { this->ReportError = false; } |
738 | | |
739 | | ListFileScope(ListFileScope const&) = delete; |
740 | | ListFileScope& operator=(ListFileScope const&) = delete; |
741 | | |
742 | | private: |
743 | | bool ReportError = true; |
744 | | }; |
745 | | |
746 | | class cmMakefile::DeferScope |
747 | | { |
748 | | public: |
749 | | DeferScope(cmMakefile* mf, std::string const& deferredInFile) |
750 | 0 | : Makefile(mf) |
751 | 0 | { |
752 | 0 | cmListFileContext lfc; |
753 | 0 | lfc.Line = cmListFileContext::DeferPlaceholderLine; |
754 | 0 | lfc.FilePath = deferredInFile; |
755 | 0 | this->Makefile->Backtrace = this->Makefile->Backtrace.Push(lfc); |
756 | 0 | this->Makefile->DeferRunning = true; |
757 | 0 | } |
758 | | |
759 | | ~DeferScope() |
760 | 0 | { |
761 | 0 | this->Makefile->DeferRunning = false; |
762 | 0 | this->Makefile->Backtrace = this->Makefile->Backtrace.Pop(); |
763 | 0 | } |
764 | | |
765 | | DeferScope(DeferScope const&) = delete; |
766 | | DeferScope& operator=(DeferScope const&) = delete; |
767 | | |
768 | | private: |
769 | | cmMakefile* Makefile; |
770 | | }; |
771 | | |
772 | | class cmMakefile::DeferCallScope |
773 | | { |
774 | | public: |
775 | | DeferCallScope(cmMakefile* mf, std::string const& deferredFromFile) |
776 | 0 | : Makefile(mf) |
777 | 0 | { |
778 | 0 | this->Makefile->StateSnapshot = |
779 | 0 | this->Makefile->GetState()->CreateDeferCallSnapshot( |
780 | 0 | this->Makefile->StateSnapshot, deferredFromFile); |
781 | 0 | assert(this->Makefile->StateSnapshot.IsValid()); |
782 | 0 | } |
783 | | |
784 | 0 | ~DeferCallScope() { this->Makefile->PopSnapshot(); } |
785 | | |
786 | | DeferCallScope(DeferCallScope const&) = delete; |
787 | | DeferCallScope& operator=(DeferCallScope const&) = delete; |
788 | | |
789 | | private: |
790 | | cmMakefile* Makefile; |
791 | | }; |
792 | | |
793 | | bool cmMakefile::ReadListFile(std::string const& filename) |
794 | 1 | { |
795 | 1 | std::string filenametoread = cmSystemTools::CollapseFullPath( |
796 | 1 | filename, this->GetCurrentSourceDirectory()); |
797 | | |
798 | 1 | ListFileScope scope(this, filenametoread); |
799 | | |
800 | 1 | #ifdef CMake_ENABLE_DEBUGGER |
801 | 1 | if (this->GetCMakeInstance()->GetDebugAdapter()) { |
802 | 0 | this->GetCMakeInstance()->GetDebugAdapter()->OnBeginFileParse( |
803 | 0 | this, filenametoread); |
804 | 0 | } |
805 | 1 | #endif |
806 | | |
807 | 1 | cmListFile listFile; |
808 | 1 | if (!listFile.ParseFile(filenametoread, this->GetMessenger(), |
809 | 1 | this->Backtrace)) { |
810 | 1 | #ifdef CMake_ENABLE_DEBUGGER |
811 | 1 | if (this->GetCMakeInstance()->GetDebugAdapter()) { |
812 | 0 | this->GetCMakeInstance()->GetDebugAdapter()->OnEndFileParse(); |
813 | 0 | } |
814 | 1 | #endif |
815 | | |
816 | 1 | return false; |
817 | 1 | } |
818 | | |
819 | 0 | #ifdef CMake_ENABLE_DEBUGGER |
820 | 0 | if (this->GetCMakeInstance()->GetDebugAdapter()) { |
821 | 0 | this->GetCMakeInstance()->GetDebugAdapter()->OnEndFileParse(); |
822 | 0 | this->GetCMakeInstance()->GetDebugAdapter()->OnFileParsedSuccessfully( |
823 | 0 | filenametoread, listFile.Functions); |
824 | 0 | } |
825 | 0 | #endif |
826 | |
|
827 | 0 | this->RunListFile(listFile, filenametoread); |
828 | 0 | if (cmSystemTools::GetFatalErrorOccurred()) { |
829 | 0 | scope.Quiet(); |
830 | 0 | } |
831 | 0 | return true; |
832 | 1 | } |
833 | | |
834 | | bool cmMakefile::ReadListFileAsString(std::string const& content, |
835 | | std::string const& virtualFileName) |
836 | 0 | { |
837 | 0 | std::string filenametoread = cmSystemTools::CollapseFullPath( |
838 | 0 | virtualFileName, this->GetCurrentSourceDirectory()); |
839 | |
|
840 | 0 | ListFileScope scope(this, filenametoread); |
841 | |
|
842 | 0 | cmListFile listFile; |
843 | 0 | if (!listFile.ParseString(content, virtualFileName, this->GetMessenger(), |
844 | 0 | this->Backtrace)) { |
845 | 0 | return false; |
846 | 0 | } |
847 | | |
848 | 0 | #ifdef CMake_ENABLE_DEBUGGER |
849 | 0 | if (this->GetCMakeInstance()->GetDebugAdapter()) { |
850 | 0 | this->GetCMakeInstance()->GetDebugAdapter()->OnFileParsedSuccessfully( |
851 | 0 | filenametoread, listFile.Functions); |
852 | 0 | } |
853 | 0 | #endif |
854 | |
|
855 | 0 | this->RunListFile(listFile, filenametoread); |
856 | 0 | if (cmSystemTools::GetFatalErrorOccurred()) { |
857 | 0 | scope.Quiet(); |
858 | 0 | } |
859 | 0 | return true; |
860 | 0 | } |
861 | | |
862 | | void cmMakefile::RunListFile(cmListFile const& listFile, |
863 | | std::string const& filenametoread, |
864 | | DeferCommands* defer) |
865 | 0 | { |
866 | | // add this list file to the list of dependencies |
867 | 0 | this->ListFiles.push_back(filenametoread); |
868 | | |
869 | | // Run the parsed commands. |
870 | 0 | size_t const numberFunctions = listFile.Functions.size(); |
871 | 0 | for (size_t i = 0; i < numberFunctions; ++i) { |
872 | 0 | cmExecutionStatus status(*this); |
873 | 0 | this->ExecuteCommand(listFile.Functions[i], status); |
874 | 0 | if (cmSystemTools::GetFatalErrorOccurred()) { |
875 | 0 | break; |
876 | 0 | } |
877 | 0 | if (status.HasExitCode()) { |
878 | | // cmake_language EXIT was requested, early break. |
879 | 0 | this->GetCMakeInstance()->SetScriptModeExitCode(status.GetExitCode()); |
880 | 0 | break; |
881 | 0 | } |
882 | 0 | if (status.GetReturnInvoked()) { |
883 | 0 | this->RaiseScope(status.GetReturnVariables()); |
884 | | // Exit early due to return command. |
885 | 0 | break; |
886 | 0 | } |
887 | 0 | } |
888 | | |
889 | | // Run any deferred commands. |
890 | 0 | if (defer) { |
891 | | // Add a backtrace level indicating calls are deferred. |
892 | 0 | DeferScope scope(this, filenametoread); |
893 | | |
894 | | // Iterate by index in case one deferred call schedules another. |
895 | | // NOLINTNEXTLINE(modernize-loop-convert) |
896 | 0 | for (size_t i = 0; i < defer->Commands.size(); ++i) { |
897 | 0 | DeferCommand& d = defer->Commands[i]; |
898 | 0 | if (d.Id.empty()) { |
899 | | // Canceled. |
900 | 0 | continue; |
901 | 0 | } |
902 | | // Mark as executed. |
903 | 0 | std::string id = std::move(d.Id); |
904 | | |
905 | | // The deferred call may have come from another file. |
906 | 0 | DeferCallScope callScope(this, d.FilePath); |
907 | |
|
908 | 0 | cmExecutionStatus status(*this); |
909 | 0 | this->ExecuteCommand(d.Command, status, std::move(id)); |
910 | 0 | if (cmSystemTools::GetFatalErrorOccurred()) { |
911 | 0 | break; |
912 | 0 | } |
913 | 0 | } |
914 | 0 | } |
915 | 0 | } |
916 | | |
917 | | void cmMakefile::EnforceDirectoryLevelRules() const |
918 | 0 | { |
919 | | // Diagnose a violation of CMP0000 if necessary. |
920 | 0 | if (this->CheckCMP0000) { |
921 | 0 | std::string e = |
922 | 0 | cmStrCat("No cmake_minimum_required command is present. " |
923 | 0 | "A line of code such as\n" |
924 | 0 | " cmake_minimum_required(VERSION ", |
925 | 0 | cmVersion::GetMajorVersion(), '.', cmVersion::GetMinorVersion(), |
926 | 0 | ")\n" |
927 | 0 | "should be added at the top of the file. " |
928 | 0 | "The version specified may be lower if you wish to " |
929 | 0 | "support older CMake versions for this project. " |
930 | 0 | "For more information run " |
931 | 0 | "\"cmake --help-policy CMP0000\"."); |
932 | 0 | this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e, |
933 | 0 | this->Backtrace); |
934 | 0 | cmSystemTools::SetFatalErrorOccurred(); |
935 | 0 | } |
936 | 0 | } |
937 | | |
938 | | void cmMakefile::AddEvaluationFile( |
939 | | std::string const& inputFile, std::string const& targetName, |
940 | | std::unique_ptr<cmCompiledGeneratorExpression> outputName, |
941 | | std::unique_ptr<cmCompiledGeneratorExpression> condition, |
942 | | std::string const& newLineCharacter, mode_t permissions, bool inputIsContent) |
943 | 0 | { |
944 | 0 | this->EvaluationFiles.push_back( |
945 | 0 | cm::make_unique<cmGeneratorExpressionEvaluationFile>( |
946 | 0 | inputFile, targetName, std::move(outputName), std::move(condition), |
947 | 0 | inputIsContent, newLineCharacter, permissions, |
948 | 0 | this->GetPolicyStatus(cmPolicies::CMP0070), |
949 | 0 | this->GetPolicyStatus(cmPolicies::CMP0189))); |
950 | 0 | } |
951 | | |
952 | | std::vector<std::unique_ptr<cmGeneratorExpressionEvaluationFile>> const& |
953 | | cmMakefile::GetEvaluationFiles() const |
954 | 0 | { |
955 | 0 | return this->EvaluationFiles; |
956 | 0 | } |
957 | | |
958 | | std::vector<std::unique_ptr<cmExportBuildFileGenerator>> const& |
959 | | cmMakefile::GetExportBuildFileGenerators() const |
960 | 0 | { |
961 | 0 | return this->ExportBuildFileGenerators; |
962 | 0 | } |
963 | | |
964 | | void cmMakefile::AddExportBuildFileGenerator( |
965 | | std::unique_ptr<cmExportBuildFileGenerator> gen) |
966 | 0 | { |
967 | 0 | this->ExportBuildFileGenerators.emplace_back(std::move(gen)); |
968 | 0 | } |
969 | | |
970 | | namespace { |
971 | | struct file_not_persistent |
972 | | { |
973 | | bool operator()(std::string const& path) const |
974 | 0 | { |
975 | 0 | return !(path.find("CMakeTmp") == std::string::npos && |
976 | 0 | cmSystemTools::FileExists(path)); |
977 | 0 | } |
978 | | }; |
979 | | } |
980 | | |
981 | | void cmMakefile::AddGeneratorAction(GeneratorAction&& action) |
982 | 0 | { |
983 | 0 | assert(!this->GeneratorActionsInvoked); |
984 | 0 | this->GeneratorActions.emplace_back(std::move(action), this->Backtrace); |
985 | 0 | } |
986 | | |
987 | | void cmMakefile::GeneratorAction::operator()(cmLocalGenerator& lg, |
988 | | cmListFileBacktrace const& lfbt, |
989 | | GeneratorActionWhen when) |
990 | 0 | { |
991 | 0 | if (this->When != when) { |
992 | 0 | return; |
993 | 0 | } |
994 | | |
995 | 0 | if (cc) { |
996 | 0 | CCAction(lg, lfbt, std::move(cc)); |
997 | 0 | } else { |
998 | 0 | assert(Action); |
999 | 0 | Action(lg, lfbt); |
1000 | 0 | } |
1001 | 0 | } |
1002 | | |
1003 | | void cmMakefile::DoGenerate(cmLocalGenerator& lg) |
1004 | 0 | { |
1005 | | // give all the commands a chance to do something |
1006 | | // after the file has been parsed before generation |
1007 | 0 | for (auto& action : this->GeneratorActions) { |
1008 | 0 | action.Value(lg, action.Backtrace, GeneratorActionWhen::AfterConfigure); |
1009 | 0 | } |
1010 | 0 | this->GeneratorActionsInvoked = true; |
1011 | | |
1012 | | // go through all configured files and see which ones still exist. |
1013 | | // we don't want cmake to re-run if a configured file is created and deleted |
1014 | | // during processing as that would make it a transient file that can't |
1015 | | // influence the build process |
1016 | 0 | cm::erase_if(this->OutputFiles, file_not_persistent()); |
1017 | | |
1018 | | // if a configured file is used as input for another configured file, |
1019 | | // and then deleted it will show up in the input list files so we |
1020 | | // need to scan those too |
1021 | 0 | cm::erase_if(this->ListFiles, file_not_persistent()); |
1022 | 0 | } |
1023 | | |
1024 | | // Generate the output file |
1025 | | void cmMakefile::Generate(cmLocalGenerator& lg) |
1026 | 0 | { |
1027 | 0 | this->DoGenerate(lg); |
1028 | 0 | cmValue oldValue = this->GetDefinition("CMAKE_BACKWARDS_COMPATIBILITY"); |
1029 | 0 | if (oldValue && |
1030 | 0 | cmSystemTools::VersionCompare(cmSystemTools::OP_LESS, *oldValue, |
1031 | 0 | "2.4")) { |
1032 | 0 | this->GetCMakeInstance()->IssueMessage( |
1033 | 0 | MessageType::FATAL_ERROR, |
1034 | 0 | "You have set CMAKE_BACKWARDS_COMPATIBILITY to a CMake version less " |
1035 | 0 | "than 2.4. This version of CMake only supports backwards compatibility " |
1036 | 0 | "with CMake 2.4 or later. For compatibility with older versions please " |
1037 | 0 | "use any CMake 2.8.x release or lower.", |
1038 | 0 | this->Backtrace); |
1039 | 0 | } |
1040 | 0 | } |
1041 | | |
1042 | | void cmMakefile::GenerateAfterGeneratorTargets(cmLocalGenerator& lg) |
1043 | 0 | { |
1044 | 0 | for (auto& action : this->GeneratorActions) { |
1045 | 0 | action.Value(lg, action.Backtrace, |
1046 | 0 | GeneratorActionWhen::AfterGeneratorTargets); |
1047 | 0 | } |
1048 | 0 | } |
1049 | | |
1050 | | namespace { |
1051 | | // There are still too many implicit backtraces through cmMakefile. As a |
1052 | | // workaround we reset the backtrace temporarily. |
1053 | | struct BacktraceGuard |
1054 | | { |
1055 | | BacktraceGuard(cmListFileBacktrace& lfbt, cmListFileBacktrace current) |
1056 | 0 | : Backtrace(lfbt) |
1057 | 0 | , Previous(lfbt) |
1058 | 0 | { |
1059 | 0 | this->Backtrace = std::move(current); |
1060 | 0 | } |
1061 | | |
1062 | 0 | ~BacktraceGuard() { this->Backtrace = std::move(this->Previous); } |
1063 | | |
1064 | | private: |
1065 | | cmListFileBacktrace& Backtrace; |
1066 | | cmListFileBacktrace Previous; |
1067 | | }; |
1068 | | } |
1069 | | |
1070 | | bool cmMakefile::ValidateCustomCommand( |
1071 | | cmCustomCommandLines const& commandLines) const |
1072 | 0 | { |
1073 | | // TODO: More strict? |
1074 | 0 | auto const it = |
1075 | 0 | std::find_if(commandLines.begin(), commandLines.end(), |
1076 | 0 | [](cmCustomCommandLine const& cl) { |
1077 | 0 | return !cl.empty() && !cl[0].empty() && cl[0][0] == '"'; |
1078 | 0 | }); |
1079 | 0 | if (it != commandLines.end()) { |
1080 | 0 | this->IssueMessage( |
1081 | 0 | MessageType::FATAL_ERROR, |
1082 | 0 | cmStrCat("COMMAND may not contain literal quotes:\n ", (*it)[0], '\n')); |
1083 | 0 | return false; |
1084 | 0 | } |
1085 | 0 | return true; |
1086 | 0 | } |
1087 | | |
1088 | | cmTarget* cmMakefile::GetCustomCommandTarget( |
1089 | | std::string const& target, cmObjectLibraryCommands objLibCommands, |
1090 | | cmListFileBacktrace const& lfbt) const |
1091 | 0 | { |
1092 | 0 | auto realTarget = target; |
1093 | |
|
1094 | 0 | auto ai = this->AliasTargets.find(target); |
1095 | 0 | if (ai != this->AliasTargets.end()) { |
1096 | 0 | realTarget = ai->second; |
1097 | 0 | } |
1098 | | |
1099 | | // Find the target to which to add the custom command. |
1100 | 0 | auto ti = this->Targets.find(realTarget); |
1101 | 0 | if (ti == this->Targets.end()) { |
1102 | 0 | std::string e; |
1103 | 0 | if (cmTarget const* t = this->FindTargetToUse(target)) { |
1104 | 0 | if (t->IsImported()) { |
1105 | 0 | e += cmStrCat("TARGET '", target, |
1106 | 0 | "' is IMPORTED and does not build here."); |
1107 | 0 | } else { |
1108 | 0 | e += |
1109 | 0 | cmStrCat("TARGET '", target, "' was not created in this directory."); |
1110 | 0 | } |
1111 | 0 | } else { |
1112 | 0 | e += cmStrCat("No TARGET '", target, |
1113 | 0 | "' has been created in this directory."); |
1114 | 0 | } |
1115 | 0 | this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e, lfbt); |
1116 | 0 | return nullptr; |
1117 | 0 | } |
1118 | | |
1119 | 0 | cmTarget* t = &ti->second; |
1120 | 0 | if (objLibCommands == cmObjectLibraryCommands::Reject && |
1121 | 0 | t->GetType() == cmStateEnums::OBJECT_LIBRARY) { |
1122 | 0 | auto e = cmStrCat( |
1123 | 0 | "Target \"", target, |
1124 | 0 | "\" is an OBJECT library " |
1125 | 0 | "that may not have PRE_BUILD, PRE_LINK, or POST_BUILD commands."); |
1126 | 0 | this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e, lfbt); |
1127 | 0 | return nullptr; |
1128 | 0 | } |
1129 | 0 | if (t->GetType() == cmStateEnums::INTERFACE_LIBRARY) { |
1130 | 0 | auto e = cmStrCat( |
1131 | 0 | "Target \"", target, |
1132 | 0 | "\" is an INTERFACE library " |
1133 | 0 | "that may not have PRE_BUILD, PRE_LINK, or POST_BUILD commands."); |
1134 | 0 | this->GetCMakeInstance()->IssueMessage(MessageType::FATAL_ERROR, e, lfbt); |
1135 | 0 | return nullptr; |
1136 | 0 | } |
1137 | | |
1138 | 0 | return t; |
1139 | 0 | } |
1140 | | |
1141 | | cmTarget* cmMakefile::AddCustomCommandToTarget( |
1142 | | std::string const& target, cmCustomCommandType type, |
1143 | | std::unique_ptr<cmCustomCommand> cc) |
1144 | 0 | { |
1145 | 0 | auto const& byproducts = cc->GetByproducts(); |
1146 | 0 | auto const& commandLines = cc->GetCommandLines(); |
1147 | |
|
1148 | 0 | cmTarget* t = this->GetCustomCommandTarget( |
1149 | 0 | target, cmObjectLibraryCommands::Reject, this->Backtrace); |
1150 | | |
1151 | | // Validate custom commands. |
1152 | 0 | if (!t || !this->ValidateCustomCommand(commandLines)) { |
1153 | 0 | return t; |
1154 | 0 | } |
1155 | | |
1156 | | // Always create the byproduct sources and mark them generated. |
1157 | 0 | this->CreateGeneratedOutputs(byproducts); |
1158 | |
|
1159 | 0 | cc->RecordPolicyValues(this->GetStateSnapshot()); |
1160 | | |
1161 | | // Dispatch command creation to allow generator expressions in outputs. |
1162 | 0 | this->AddGeneratorAction( |
1163 | 0 | std::move(cc), |
1164 | 0 | [this, t, type](cmLocalGenerator& lg, cmListFileBacktrace const& lfbt, |
1165 | 0 | std::unique_ptr<cmCustomCommand> tcc) { |
1166 | 0 | BacktraceGuard guard(this->Backtrace, lfbt); |
1167 | 0 | tcc->SetBacktrace(lfbt); |
1168 | 0 | detail::AddCustomCommandToTarget(lg, cmCommandOrigin::Project, t, type, |
1169 | 0 | std::move(tcc)); |
1170 | 0 | }); |
1171 | |
|
1172 | 0 | return t; |
1173 | 0 | } |
1174 | | |
1175 | | void cmMakefile::AddCustomCommandToOutput( |
1176 | | std::unique_ptr<cmCustomCommand> cc, CommandSourceCallback const& callback, |
1177 | | bool replace) |
1178 | 0 | { |
1179 | 0 | auto const& outputs = cc->GetOutputs(); |
1180 | 0 | auto const& byproducts = cc->GetByproducts(); |
1181 | 0 | auto const& commandLines = cc->GetCommandLines(); |
1182 | | |
1183 | | // Make sure there is at least one output. |
1184 | 0 | if (outputs.empty()) { |
1185 | 0 | cmSystemTools::Error("Attempt to add a custom rule with no output!"); |
1186 | 0 | return; |
1187 | 0 | } |
1188 | | |
1189 | | // Validate custom commands. |
1190 | 0 | if (!this->ValidateCustomCommand(commandLines)) { |
1191 | 0 | return; |
1192 | 0 | } |
1193 | | |
1194 | | // Always create the output sources and mark them generated. |
1195 | 0 | this->CreateGeneratedOutputs(outputs); |
1196 | 0 | this->CreateGeneratedOutputs(byproducts); |
1197 | |
|
1198 | 0 | cc->RecordPolicyValues(this->GetStateSnapshot()); |
1199 | | |
1200 | | // Dispatch command creation to allow generator expressions in outputs. |
1201 | 0 | this->AddGeneratorAction( |
1202 | 0 | std::move(cc), |
1203 | 0 | [this, replace, callback](cmLocalGenerator& lg, |
1204 | 0 | cmListFileBacktrace const& lfbt, |
1205 | 0 | std::unique_ptr<cmCustomCommand> tcc) { |
1206 | 0 | BacktraceGuard guard(this->Backtrace, lfbt); |
1207 | 0 | tcc->SetBacktrace(lfbt); |
1208 | 0 | cmSourceFile* sf = detail::AddCustomCommandToOutput( |
1209 | 0 | lg, cmCommandOrigin::Project, std::move(tcc), replace); |
1210 | 0 | if (callback && sf) { |
1211 | 0 | callback(sf); |
1212 | 0 | } |
1213 | 0 | }); |
1214 | 0 | } |
1215 | | |
1216 | | void cmMakefile::AppendCustomCommandToOutput( |
1217 | | std::string const& output, std::vector<std::string> const& depends, |
1218 | | cmImplicitDependsList const& implicit_depends, |
1219 | | cmCustomCommandLines const& commandLines) |
1220 | 0 | { |
1221 | | // Validate custom commands. |
1222 | 0 | if (this->ValidateCustomCommand(commandLines)) { |
1223 | | // Dispatch command creation to allow generator expressions in outputs. |
1224 | 0 | this->AddGeneratorAction( |
1225 | 0 | [this, output, depends, implicit_depends, |
1226 | 0 | commandLines](cmLocalGenerator& lg, cmListFileBacktrace const& lfbt) { |
1227 | 0 | BacktraceGuard guard(this->Backtrace, lfbt); |
1228 | 0 | detail::AppendCustomCommandToOutput(lg, lfbt, output, depends, |
1229 | 0 | implicit_depends, commandLines); |
1230 | 0 | }); |
1231 | 0 | } |
1232 | 0 | } |
1233 | | |
1234 | | cmTarget* cmMakefile::AddUtilityCommand(std::string const& utilityName, |
1235 | | bool excludeFromAll, |
1236 | | std::unique_ptr<cmCustomCommand> cc) |
1237 | 0 | { |
1238 | 0 | auto const& depends = cc->GetDepends(); |
1239 | 0 | auto const& byproducts = cc->GetByproducts(); |
1240 | 0 | auto const& commandLines = cc->GetCommandLines(); |
1241 | 0 | cmTarget* target = this->AddNewUtilityTarget(utilityName, excludeFromAll); |
1242 | | |
1243 | | // Validate custom commands. |
1244 | 0 | if ((commandLines.empty() && depends.empty()) || |
1245 | 0 | !this->ValidateCustomCommand(commandLines)) { |
1246 | 0 | return target; |
1247 | 0 | } |
1248 | | |
1249 | | // Always create the byproduct sources and mark them generated. |
1250 | 0 | this->CreateGeneratedOutputs(byproducts); |
1251 | |
|
1252 | 0 | cc->RecordPolicyValues(this->GetStateSnapshot()); |
1253 | | |
1254 | | // Dispatch command creation to allow generator expressions in outputs. |
1255 | 0 | this->AddGeneratorAction( |
1256 | 0 | std::move(cc), |
1257 | 0 | [this, target](cmLocalGenerator& lg, cmListFileBacktrace const& lfbt, |
1258 | 0 | std::unique_ptr<cmCustomCommand> tcc) { |
1259 | 0 | BacktraceGuard guard(this->Backtrace, lfbt); |
1260 | 0 | tcc->SetBacktrace(lfbt); |
1261 | 0 | detail::AddUtilityCommand(lg, cmCommandOrigin::Project, target, |
1262 | 0 | std::move(tcc)); |
1263 | 0 | }); |
1264 | |
|
1265 | 0 | return target; |
1266 | 0 | } |
1267 | | |
1268 | | static void s_AddDefineFlag(std::string const& flag, std::string& dflags) |
1269 | 0 | { |
1270 | | // remove any \n\r |
1271 | 0 | std::string::size_type initSize = dflags.size(); |
1272 | 0 | dflags += ' '; |
1273 | 0 | dflags += flag; |
1274 | 0 | std::string::iterator flagStart = dflags.begin() + initSize + 1; |
1275 | 0 | std::replace(flagStart, dflags.end(), '\n', ' '); |
1276 | 0 | std::replace(flagStart, dflags.end(), '\r', ' '); |
1277 | 0 | } |
1278 | | |
1279 | | void cmMakefile::AddDefineFlag(std::string const& flag) |
1280 | 0 | { |
1281 | 0 | if (flag.empty()) { |
1282 | 0 | return; |
1283 | 0 | } |
1284 | | |
1285 | | // If this is really a definition, update COMPILE_DEFINITIONS. |
1286 | 0 | if (this->ParseDefineFlag(flag, false)) { |
1287 | 0 | return; |
1288 | 0 | } |
1289 | | |
1290 | | // Add this flag that does not look like a definition. |
1291 | 0 | s_AddDefineFlag(flag, this->DefineFlags); |
1292 | 0 | } |
1293 | | |
1294 | | static void s_RemoveDefineFlag(std::string const& flag, std::string& dflags) |
1295 | 0 | { |
1296 | 0 | std::string::size_type const len = flag.length(); |
1297 | | // Remove all instances of the flag that are surrounded by |
1298 | | // whitespace or the beginning/end of the string. |
1299 | 0 | for (std::string::size_type lpos = dflags.find(flag, 0); |
1300 | 0 | lpos != std::string::npos; lpos = dflags.find(flag, lpos)) { |
1301 | 0 | std::string::size_type rpos = lpos + len; |
1302 | 0 | if ((lpos <= 0 || cmsysString_isspace(dflags[lpos - 1])) && |
1303 | 0 | (rpos >= dflags.size() || cmsysString_isspace(dflags[rpos]))) { |
1304 | 0 | dflags.erase(lpos, len); |
1305 | 0 | } else { |
1306 | 0 | ++lpos; |
1307 | 0 | } |
1308 | 0 | } |
1309 | 0 | } |
1310 | | |
1311 | | void cmMakefile::RemoveDefineFlag(std::string const& flag) |
1312 | 0 | { |
1313 | | // Check the length of the flag to remove. |
1314 | 0 | if (flag.empty()) { |
1315 | 0 | return; |
1316 | 0 | } |
1317 | | |
1318 | | // If this is really a definition, update COMPILE_DEFINITIONS. |
1319 | 0 | if (this->ParseDefineFlag(flag, true)) { |
1320 | 0 | return; |
1321 | 0 | } |
1322 | | |
1323 | | // Remove this flag that does not look like a definition. |
1324 | 0 | s_RemoveDefineFlag(flag, this->DefineFlags); |
1325 | 0 | } |
1326 | | |
1327 | | void cmMakefile::AddCompileDefinition(std::string const& option) |
1328 | 0 | { |
1329 | 0 | this->AppendProperty("COMPILE_DEFINITIONS", option); |
1330 | 0 | } |
1331 | | |
1332 | | void cmMakefile::AddCompileOption(std::string const& option) |
1333 | 0 | { |
1334 | 0 | this->AppendProperty("COMPILE_OPTIONS", option); |
1335 | 0 | } |
1336 | | |
1337 | | void cmMakefile::AddLinkOption(std::string const& option) |
1338 | 0 | { |
1339 | 0 | this->AppendProperty("LINK_OPTIONS", option); |
1340 | 0 | } |
1341 | | |
1342 | | void cmMakefile::AddLinkDirectory(std::string const& directory, bool before) |
1343 | 0 | { |
1344 | 0 | if (before) { |
1345 | 0 | this->StateSnapshot.GetDirectory().PrependLinkDirectoriesEntry( |
1346 | 0 | BT<std::string>(directory, this->Backtrace)); |
1347 | 0 | } else { |
1348 | 0 | this->StateSnapshot.GetDirectory().AppendLinkDirectoriesEntry( |
1349 | 0 | BT<std::string>(directory, this->Backtrace)); |
1350 | 0 | } |
1351 | 0 | } |
1352 | | |
1353 | | bool cmMakefile::ParseDefineFlag(std::string const& def, bool remove) |
1354 | 0 | { |
1355 | | // Create a regular expression to match valid definitions. |
1356 | 0 | static cmsys::RegularExpression valid("^[-/]D[A-Za-z_][A-Za-z0-9_]*(=.*)?$"); |
1357 | | |
1358 | | // Make sure the definition matches. |
1359 | 0 | if (!valid.find(def)) { |
1360 | 0 | return false; |
1361 | 0 | } |
1362 | | |
1363 | | // Get the definition part after the flag. |
1364 | 0 | char const* define = def.c_str() + 2; |
1365 | |
|
1366 | 0 | if (remove) { |
1367 | 0 | if (cmValue cdefs = this->GetProperty("COMPILE_DEFINITIONS")) { |
1368 | | // Expand the list. |
1369 | 0 | cmList defs{ *cdefs }; |
1370 | | |
1371 | | // Recompose the list without the definition. |
1372 | 0 | defs.remove_items({ define }); |
1373 | | |
1374 | | // Store the new list. |
1375 | 0 | this->SetProperty("COMPILE_DEFINITIONS", defs.to_string()); |
1376 | 0 | } |
1377 | 0 | } else { |
1378 | | // Append the definition to the directory property. |
1379 | 0 | this->AppendProperty("COMPILE_DEFINITIONS", define); |
1380 | 0 | } |
1381 | |
|
1382 | 0 | return true; |
1383 | 0 | } |
1384 | | |
1385 | | void cmMakefile::InitializeFromParent(cmMakefile* parent) |
1386 | 0 | { |
1387 | 0 | this->SystemIncludeDirectories = parent->SystemIncludeDirectories; |
1388 | | |
1389 | | // define flags |
1390 | 0 | this->DefineFlags = parent->DefineFlags; |
1391 | | |
1392 | | // Include transform property. There is no per-config version. |
1393 | 0 | { |
1394 | 0 | char const* prop = "IMPLICIT_DEPENDS_INCLUDE_TRANSFORM"; |
1395 | 0 | this->SetProperty(prop, parent->GetProperty(prop)); |
1396 | 0 | } |
1397 | | |
1398 | | // labels |
1399 | 0 | this->SetProperty("LABELS", parent->GetProperty("LABELS")); |
1400 | | |
1401 | | // link libraries |
1402 | 0 | this->SetProperty("LINK_LIBRARIES", parent->GetProperty("LINK_LIBRARIES")); |
1403 | | |
1404 | | // the initial project name |
1405 | 0 | this->StateSnapshot.SetProjectName(parent->StateSnapshot.GetProjectName()); |
1406 | | |
1407 | | // Copy include regular expressions. |
1408 | 0 | this->ComplainFileRegularExpression = parent->ComplainFileRegularExpression; |
1409 | | |
1410 | | // Imported targets. |
1411 | 0 | this->ImportedTargets = parent->ImportedTargets; |
1412 | | |
1413 | | // Non-global Alias targets. |
1414 | 0 | this->AliasTargets = parent->AliasTargets; |
1415 | | |
1416 | | // Recursion depth. |
1417 | 0 | this->RecursionDepth = parent->RecursionDepth; |
1418 | 0 | } |
1419 | | |
1420 | | void cmMakefile::AddInstallGenerator(std::unique_ptr<cmInstallGenerator> g) |
1421 | 0 | { |
1422 | 0 | if (g) { |
1423 | 0 | this->InstallGenerators.push_back(std::move(g)); |
1424 | 0 | } |
1425 | 0 | } |
1426 | | |
1427 | | void cmMakefile::AddTestGenerator(std::unique_ptr<cmTestGenerator> g) |
1428 | 0 | { |
1429 | 0 | if (g) { |
1430 | 0 | this->TestGenerators.push_back(std::move(g)); |
1431 | 0 | } |
1432 | 0 | } |
1433 | | |
1434 | | bool cmMakefile::ExplicitlyGeneratesSbom() const |
1435 | 0 | { |
1436 | 0 | return this->ExplicitSbomGenerator; |
1437 | 0 | } |
1438 | | |
1439 | | void cmMakefile::SetExplicitlyGeneratesSbom(bool status) |
1440 | 0 | { |
1441 | 0 | this->ExplicitSbomGenerator = status; |
1442 | 0 | } |
1443 | | |
1444 | | void cmMakefile::PushFunctionScope(std::string const& fileName, |
1445 | | cmPolicies::PolicyMap const& pm) |
1446 | 0 | { |
1447 | 0 | this->StateSnapshot = this->GetState()->CreateFunctionCallSnapshot( |
1448 | 0 | this->StateSnapshot, fileName); |
1449 | 0 | assert(this->StateSnapshot.IsValid()); |
1450 | |
|
1451 | 0 | this->PushLoopBlockBarrier(); |
1452 | |
|
1453 | 0 | #if !defined(CMAKE_BOOTSTRAP) |
1454 | 0 | this->GetGlobalGenerator()->GetFileLockPool().PushFunctionScope(); |
1455 | 0 | #endif |
1456 | |
|
1457 | 0 | this->PushFunctionBlockerBarrier(); |
1458 | |
|
1459 | 0 | this->PushPolicy(true, pm); |
1460 | 0 | } |
1461 | | |
1462 | | void cmMakefile::PopFunctionScope(bool reportError) |
1463 | 0 | { |
1464 | 0 | this->PopPolicy(); |
1465 | |
|
1466 | 0 | this->PopSnapshot(reportError); |
1467 | |
|
1468 | 0 | this->PopFunctionBlockerBarrier(reportError); |
1469 | |
|
1470 | 0 | #if !defined(CMAKE_BOOTSTRAP) |
1471 | 0 | this->GetGlobalGenerator()->GetFileLockPool().PopFunctionScope(); |
1472 | 0 | #endif |
1473 | |
|
1474 | 0 | this->PopLoopBlockBarrier(); |
1475 | 0 | } |
1476 | | |
1477 | | void cmMakefile::PushMacroScope(std::string const& fileName, |
1478 | | cmPolicies::PolicyMap const& pm) |
1479 | 0 | { |
1480 | 0 | this->StateSnapshot = |
1481 | 0 | this->GetState()->CreateMacroCallSnapshot(this->StateSnapshot, fileName); |
1482 | 0 | assert(this->StateSnapshot.IsValid()); |
1483 | |
|
1484 | 0 | this->PushFunctionBlockerBarrier(); |
1485 | |
|
1486 | 0 | this->PushPolicy(true, pm); |
1487 | 0 | } |
1488 | | |
1489 | | void cmMakefile::PopMacroScope(bool reportError) |
1490 | 0 | { |
1491 | 0 | this->PopPolicy(); |
1492 | 0 | this->PopSnapshot(reportError); |
1493 | |
|
1494 | 0 | this->PopFunctionBlockerBarrier(reportError); |
1495 | 0 | } |
1496 | | |
1497 | | bool cmMakefile::IsRootMakefile() const |
1498 | 0 | { |
1499 | 0 | return !this->StateSnapshot.GetBuildsystemDirectoryParent().IsValid(); |
1500 | 0 | } |
1501 | | |
1502 | | class cmMakefile::BuildsystemFileScope : public FileScopeBase |
1503 | | { |
1504 | | public: |
1505 | | BuildsystemFileScope(cmMakefile* mf) |
1506 | 0 | : FileScopeBase(mf) |
1507 | 0 | { |
1508 | 0 | std::string currentStart = |
1509 | 0 | this->Makefile->GetCMakeInstance()->GetCMakeListFile( |
1510 | 0 | this->Makefile->StateSnapshot.GetDirectory().GetCurrentSource()); |
1511 | 0 | this->Makefile->StateSnapshot.SetListFile(currentStart); |
1512 | 0 | this->Makefile->StateSnapshot = |
1513 | 0 | this->Makefile->StateSnapshot.GetState()->CreatePolicyScopeSnapshot( |
1514 | 0 | this->Makefile->StateSnapshot); |
1515 | 0 | this->Makefile->PushFunctionBlockerBarrier(); |
1516 | 0 | this->PushListFileVars(currentStart); |
1517 | |
|
1518 | 0 | this->GG = mf->GetGlobalGenerator(); |
1519 | 0 | this->CurrentMakefile = this->GG->GetCurrentMakefile(); |
1520 | 0 | this->Snapshot = this->GG->GetCMakeInstance()->GetCurrentSnapshot(); |
1521 | 0 | this->GG->GetCMakeInstance()->SetCurrentSnapshot(this->Snapshot); |
1522 | 0 | this->GG->SetCurrentMakefile(mf); |
1523 | 0 | #if !defined(CMAKE_BOOTSTRAP) |
1524 | 0 | this->GG->GetFileLockPool().PushFileScope(); |
1525 | 0 | #endif |
1526 | 0 | } |
1527 | | |
1528 | | ~BuildsystemFileScope() |
1529 | 0 | { |
1530 | 0 | this->PopListFileVars(); |
1531 | 0 | this->Makefile->PopFunctionBlockerBarrier(this->ReportError); |
1532 | 0 | this->Makefile->PopSnapshot(this->ReportError); |
1533 | 0 | #if !defined(CMAKE_BOOTSTRAP) |
1534 | 0 | this->GG->GetFileLockPool().PopFileScope(); |
1535 | 0 | #endif |
1536 | 0 | this->GG->SetCurrentMakefile(this->CurrentMakefile); |
1537 | 0 | this->GG->GetCMakeInstance()->SetCurrentSnapshot(this->Snapshot); |
1538 | 0 | } |
1539 | | |
1540 | 0 | void Quiet() { this->ReportError = false; } |
1541 | | |
1542 | | BuildsystemFileScope(BuildsystemFileScope const&) = delete; |
1543 | | BuildsystemFileScope& operator=(BuildsystemFileScope const&) = delete; |
1544 | | |
1545 | | private: |
1546 | | cmGlobalGenerator* GG; |
1547 | | cmMakefile* CurrentMakefile; |
1548 | | cmStateSnapshot Snapshot; |
1549 | | bool ReportError = true; |
1550 | | }; |
1551 | | |
1552 | | void cmMakefile::Configure() |
1553 | 0 | { |
1554 | 0 | std::string currentStart = this->GetCMakeInstance()->GetCMakeListFile( |
1555 | 0 | this->StateSnapshot.GetDirectory().GetCurrentSource()); |
1556 | | |
1557 | | // Add the bottom of all backtraces within this directory. |
1558 | | // We will never pop this scope because it should be available |
1559 | | // for messages during the generate step too. |
1560 | 0 | this->Backtrace = |
1561 | 0 | this->Backtrace.Push(cmListFileContext::FromListFilePath(currentStart)); |
1562 | |
|
1563 | 0 | BuildsystemFileScope scope(this); |
1564 | | |
1565 | | // make sure the CMakeFiles dir is there |
1566 | 0 | std::string filesDir = cmStrCat( |
1567 | 0 | this->StateSnapshot.GetDirectory().GetCurrentBinary(), "/CMakeFiles"); |
1568 | 0 | cmSystemTools::MakeDirectory(filesDir); |
1569 | |
|
1570 | 0 | assert(cmSystemTools::FileExists(currentStart, true)); |
1571 | | |
1572 | | // In the top-most directory, cmake_minimum_required() may not have been |
1573 | | // called yet, so ApplyPolicyVersion() may not have handled the default |
1574 | | // policy value. Check them here. |
1575 | 0 | if (this->GetPolicyStatus(cmPolicies::CMP0198) == cmPolicies::WARN) { |
1576 | 0 | if (cmValue defaultValue = |
1577 | 0 | this->GetDefinition("CMAKE_POLICY_DEFAULT_CMP0198")) { |
1578 | 0 | if (*defaultValue == "NEW") { |
1579 | 0 | this->SetPolicy(cmPolicies::CMP0198, cmPolicies::NEW); |
1580 | 0 | } else if (*defaultValue == "OLD") { |
1581 | 0 | this->SetPolicy(cmPolicies::CMP0198, cmPolicies::OLD); |
1582 | 0 | } |
1583 | 0 | } |
1584 | 0 | } |
1585 | | |
1586 | | // Set CMAKE_PARENT_LIST_FILE for CMakeLists.txt based on CMP0198 policy |
1587 | 0 | this->UpdateParentListFileVariable(); |
1588 | |
|
1589 | 0 | #ifdef CMake_ENABLE_DEBUGGER |
1590 | 0 | if (this->GetCMakeInstance()->GetDebugAdapter()) { |
1591 | 0 | this->GetCMakeInstance()->GetDebugAdapter()->OnBeginFileParse( |
1592 | 0 | this, currentStart); |
1593 | 0 | } |
1594 | 0 | #endif |
1595 | |
|
1596 | 0 | cmListFile listFile; |
1597 | 0 | if (!listFile.ParseFile(currentStart, this->GetMessenger(), |
1598 | 0 | this->Backtrace)) { |
1599 | 0 | #ifdef CMake_ENABLE_DEBUGGER |
1600 | 0 | if (this->GetCMakeInstance()->GetDebugAdapter()) { |
1601 | 0 | this->GetCMakeInstance()->GetDebugAdapter()->OnEndFileParse(); |
1602 | 0 | } |
1603 | 0 | #endif |
1604 | |
|
1605 | 0 | return; |
1606 | 0 | } |
1607 | | |
1608 | 0 | #ifdef CMake_ENABLE_DEBUGGER |
1609 | 0 | if (this->GetCMakeInstance()->GetDebugAdapter()) { |
1610 | 0 | this->GetCMakeInstance()->GetDebugAdapter()->OnEndFileParse(); |
1611 | 0 | this->GetCMakeInstance()->GetDebugAdapter()->OnFileParsedSuccessfully( |
1612 | 0 | currentStart, listFile.Functions); |
1613 | 0 | } |
1614 | 0 | #endif |
1615 | |
|
1616 | 0 | if (this->IsRootMakefile()) { |
1617 | 0 | bool hasVersion = false; |
1618 | | // search for the right policy command |
1619 | 0 | for (cmListFileFunction const& func : listFile.Functions) { |
1620 | 0 | if (func.LowerCaseName() == "cmake_minimum_required"_s) { |
1621 | 0 | hasVersion = true; |
1622 | 0 | break; |
1623 | 0 | } |
1624 | 0 | } |
1625 | | // if no policy command is found this is an error if they use any |
1626 | | // non advanced functions or a lot of functions |
1627 | 0 | if (!hasVersion) { |
1628 | 0 | bool isProblem = true; |
1629 | 0 | if (listFile.Functions.size() < 30) { |
1630 | | // the list of simple commands DO NOT ADD TO THIS LIST!!!!! |
1631 | | // these commands must have backwards compatibility forever and |
1632 | | // and that is a lot longer than your tiny mind can comprehend mortal |
1633 | 0 | std::set<std::string> allowedCommands; |
1634 | 0 | allowedCommands.insert("project"); |
1635 | 0 | allowedCommands.insert("set"); |
1636 | 0 | allowedCommands.insert("if"); |
1637 | 0 | allowedCommands.insert("endif"); |
1638 | 0 | allowedCommands.insert("else"); |
1639 | 0 | allowedCommands.insert("elseif"); |
1640 | 0 | allowedCommands.insert("add_executable"); |
1641 | 0 | allowedCommands.insert("add_library"); |
1642 | 0 | allowedCommands.insert("target_link_libraries"); |
1643 | 0 | allowedCommands.insert("option"); |
1644 | 0 | allowedCommands.insert("message"); |
1645 | 0 | isProblem = false; |
1646 | 0 | for (cmListFileFunction const& func : listFile.Functions) { |
1647 | 0 | if (!cm::contains(allowedCommands, func.LowerCaseName())) { |
1648 | 0 | isProblem = true; |
1649 | 0 | break; |
1650 | 0 | } |
1651 | 0 | } |
1652 | 0 | } |
1653 | |
|
1654 | 0 | if (isProblem) { |
1655 | | // Tell the top level cmMakefile to diagnose |
1656 | | // this violation of CMP0000. |
1657 | 0 | this->SetCheckCMP0000(true); |
1658 | | |
1659 | | // Implicitly set the version for the user. |
1660 | 0 | cmPolicies::ApplyPolicyVersion(this, 3, 5, 0, |
1661 | 0 | cmPolicies::WarnCompat::Off); |
1662 | 0 | } |
1663 | 0 | } |
1664 | 0 | bool hasProject = false; |
1665 | | // search for a project command |
1666 | 0 | for (cmListFileFunction const& func : listFile.Functions) { |
1667 | 0 | if (func.LowerCaseName() == "project"_s) { |
1668 | 0 | hasProject = true; |
1669 | 0 | break; |
1670 | 0 | } |
1671 | 0 | } |
1672 | | // if no project command is found, add one |
1673 | 0 | if (!hasProject) { |
1674 | 0 | this->GetCMakeInstance()->IssueMessage( |
1675 | 0 | MessageType::AUTHOR_WARNING, |
1676 | 0 | "No project() command is present. The top-level CMakeLists.txt " |
1677 | 0 | "file must contain a literal, direct call to the project() command. " |
1678 | 0 | "Add a line of code such as\n" |
1679 | 0 | " project(ProjectName)\n" |
1680 | 0 | "near the top of the file, but after cmake_minimum_required().\n" |
1681 | 0 | "CMake is pretending there is a \"project(Project)\" command on " |
1682 | 0 | "the first line.", |
1683 | 0 | this->Backtrace); |
1684 | 0 | cmListFileFunction project{ |
1685 | 0 | "project", 0, 0, { { "Project", cmListFileArgument::Unquoted, 0 } } |
1686 | 0 | }; |
1687 | 0 | listFile.Functions.insert(listFile.Functions.begin(), project); |
1688 | 0 | } |
1689 | 0 | } |
1690 | |
|
1691 | 0 | this->Defer = cm::make_unique<DeferCommands>(); |
1692 | 0 | this->RunListFile(listFile, currentStart, this->Defer.get()); |
1693 | 0 | this->Defer.reset(); |
1694 | 0 | if (cmSystemTools::GetFatalErrorOccurred()) { |
1695 | 0 | scope.Quiet(); |
1696 | 0 | } |
1697 | | |
1698 | | // at the end handle any old style subdirs |
1699 | 0 | std::vector<cmMakefile*> subdirs = this->UnConfiguredDirectories; |
1700 | | |
1701 | | // for each subdir recurse |
1702 | 0 | auto sdi = subdirs.begin(); |
1703 | 0 | for (; sdi != subdirs.end(); ++sdi) { |
1704 | 0 | (*sdi)->StateSnapshot.InitializeFromParent_ForSubdirsCommand(); |
1705 | 0 | this->ConfigureSubDirectory(*sdi); |
1706 | 0 | } |
1707 | |
|
1708 | 0 | this->AddCMakeDependFilesFromUser(); |
1709 | 0 | } |
1710 | | |
1711 | | void cmMakefile::ConfigureSubDirectory(cmMakefile* mf) |
1712 | 0 | { |
1713 | 0 | mf->InitializeFromParent(this); |
1714 | 0 | std::string currentStart = mf->GetCurrentSourceDirectory(); |
1715 | 0 | if (this->GetCMakeInstance()->GetDebugOutput()) { |
1716 | 0 | std::string msg = cmStrCat(" Entering ", currentStart); |
1717 | 0 | cmSystemTools::Message(msg); |
1718 | 0 | } |
1719 | |
|
1720 | 0 | std::string currentStartFile = |
1721 | 0 | this->GetCMakeInstance()->GetCMakeListFile(currentStart); |
1722 | 0 | if (!cmSystemTools::FileExists(currentStartFile, true)) { |
1723 | 0 | this->IssueMessage(MessageType::FATAL_ERROR, |
1724 | 0 | cmStrCat("The source directory\n ", currentStart, |
1725 | 0 | "\n" |
1726 | 0 | "does not contain a CMakeLists.txt file.")); |
1727 | 0 | return; |
1728 | 0 | } |
1729 | | // finally configure the subdir |
1730 | 0 | mf->Configure(); |
1731 | |
|
1732 | 0 | if (this->GetCMakeInstance()->GetDebugOutput()) { |
1733 | 0 | auto msg = |
1734 | 0 | cmStrCat(" Returning to ", this->GetCurrentSourceDirectory()); |
1735 | 0 | cmSystemTools::Message(msg); |
1736 | 0 | } |
1737 | 0 | } |
1738 | | |
1739 | | void cmMakefile::AddSubDirectory(std::string const& srcPath, |
1740 | | std::string const& binPath, |
1741 | | bool excludeFromAll, bool immediate, |
1742 | | bool system) |
1743 | 0 | { |
1744 | 0 | if (this->DeferRunning) { |
1745 | 0 | this->IssueMessage( |
1746 | 0 | MessageType::FATAL_ERROR, |
1747 | 0 | "Subdirectories may not be created during deferred execution."); |
1748 | 0 | return; |
1749 | 0 | } |
1750 | | |
1751 | | // Make sure the binary directory is unique. |
1752 | 0 | if (!this->EnforceUniqueDir(srcPath, binPath)) { |
1753 | 0 | return; |
1754 | 0 | } |
1755 | | |
1756 | 0 | cmStateSnapshot newSnapshot = |
1757 | 0 | this->GetState()->CreateBuildsystemDirectorySnapshot(this->StateSnapshot); |
1758 | |
|
1759 | 0 | newSnapshot.GetDirectory().SetCurrentSource(srcPath); |
1760 | 0 | newSnapshot.GetDirectory().SetCurrentBinary(binPath); |
1761 | |
|
1762 | 0 | cmSystemTools::MakeDirectory(binPath); |
1763 | |
|
1764 | 0 | auto subMfu = |
1765 | 0 | cm::make_unique<cmMakefile>(this->GlobalGenerator, newSnapshot); |
1766 | 0 | auto* subMf = subMfu.get(); |
1767 | 0 | this->GetGlobalGenerator()->AddMakefile(std::move(subMfu)); |
1768 | |
|
1769 | 0 | if (excludeFromAll) { |
1770 | 0 | subMf->SetProperty("EXCLUDE_FROM_ALL", "TRUE"); |
1771 | 0 | } |
1772 | 0 | if (system) { |
1773 | 0 | subMf->SetProperty("SYSTEM", "TRUE"); |
1774 | 0 | } |
1775 | |
|
1776 | 0 | if (immediate) { |
1777 | 0 | this->ConfigureSubDirectory(subMf); |
1778 | 0 | } else { |
1779 | 0 | this->UnConfiguredDirectories.push_back(subMf); |
1780 | 0 | } |
1781 | |
|
1782 | 0 | this->AddInstallGenerator(cm::make_unique<cmInstallSubdirectoryGenerator>( |
1783 | 0 | subMf, binPath, this->GetBacktrace())); |
1784 | 0 | } |
1785 | | |
1786 | | std::string const& cmMakefile::GetCurrentSourceDirectory() const |
1787 | 1 | { |
1788 | 1 | return this->StateSnapshot.GetDirectory().GetCurrentSource(); |
1789 | 1 | } |
1790 | | |
1791 | | std::string const& cmMakefile::GetCurrentBinaryDirectory() const |
1792 | 0 | { |
1793 | 0 | return this->StateSnapshot.GetDirectory().GetCurrentBinary(); |
1794 | 0 | } |
1795 | | |
1796 | | cmTarget* cmMakefile::FindImportedTarget(std::string const& name) const |
1797 | 0 | { |
1798 | 0 | auto const i = this->ImportedTargets.find(name); |
1799 | 0 | if (i != this->ImportedTargets.end()) { |
1800 | 0 | return i->second; |
1801 | 0 | } |
1802 | 0 | return nullptr; |
1803 | 0 | } |
1804 | | |
1805 | | std::vector<cmTarget*> cmMakefile::GetImportedTargets() const |
1806 | 0 | { |
1807 | 0 | std::vector<cmTarget*> tgts; |
1808 | 0 | tgts.reserve(this->ImportedTargets.size()); |
1809 | 0 | for (auto const& impTarget : this->ImportedTargets) { |
1810 | 0 | tgts.push_back(impTarget.second); |
1811 | 0 | } |
1812 | 0 | return tgts; |
1813 | 0 | } |
1814 | | |
1815 | | void cmMakefile::AddIncludeDirectories(std::vector<std::string> const& incs, |
1816 | | bool before) |
1817 | 0 | { |
1818 | 0 | if (incs.empty()) { |
1819 | 0 | return; |
1820 | 0 | } |
1821 | | |
1822 | 0 | std::string entryString = cmList::to_string(incs); |
1823 | 0 | if (before) { |
1824 | 0 | this->StateSnapshot.GetDirectory().PrependIncludeDirectoriesEntry( |
1825 | 0 | BT<std::string>(entryString, this->Backtrace)); |
1826 | 0 | } else { |
1827 | 0 | this->StateSnapshot.GetDirectory().AppendIncludeDirectoriesEntry( |
1828 | 0 | BT<std::string>(entryString, this->Backtrace)); |
1829 | 0 | } |
1830 | | |
1831 | | // Property on each target: |
1832 | 0 | for (auto& target : this->Targets) { |
1833 | 0 | cmTarget& t = target.second; |
1834 | 0 | t.InsertInclude(BT<std::string>(entryString, this->Backtrace), before); |
1835 | 0 | } |
1836 | 0 | } |
1837 | | |
1838 | | void cmMakefile::AddSystemIncludeDirectories(std::set<std::string> const& incs) |
1839 | 0 | { |
1840 | 0 | if (incs.empty()) { |
1841 | 0 | return; |
1842 | 0 | } |
1843 | | |
1844 | 0 | this->SystemIncludeDirectories.insert(incs.begin(), incs.end()); |
1845 | |
|
1846 | 0 | for (auto& target : this->Targets) { |
1847 | 0 | cmTarget& t = target.second; |
1848 | 0 | t.AddSystemIncludeDirectories(incs); |
1849 | 0 | } |
1850 | 0 | } |
1851 | | |
1852 | | void cmMakefile::AddDefinition(std::string const& name, cm::string_view value) |
1853 | 9 | { |
1854 | 9 | this->StateSnapshot.SetDefinition(name, value); |
1855 | | |
1856 | 9 | #ifndef CMAKE_BOOTSTRAP |
1857 | 9 | cmVariableWatch* vv = this->GetVariableWatch(); |
1858 | 9 | if (vv) { |
1859 | 9 | vv->VariableAccessed(name, cmVariableWatch::VARIABLE_MODIFIED_ACCESS, |
1860 | 9 | value.data(), this); |
1861 | 9 | } |
1862 | 9 | #endif |
1863 | 9 | } |
1864 | | |
1865 | | void cmMakefile::AddDefinitionBool(std::string const& name, bool value) |
1866 | 0 | { |
1867 | 0 | this->AddDefinition(name, value ? "ON" : "OFF"); |
1868 | 0 | } |
1869 | | |
1870 | | void cmMakefile::AddCacheDefinition(std::string const& name, cmValue value, |
1871 | | cmValue doc, |
1872 | | cmStateEnums::CacheEntryType type, |
1873 | | bool force) |
1874 | 0 | { |
1875 | 0 | cmValue existingValue = this->GetState()->GetInitializedCacheValue(name); |
1876 | | // must be outside the following if() to keep it alive long enough |
1877 | 0 | std::string nvalue; |
1878 | |
|
1879 | 0 | if (existingValue && |
1880 | 0 | (this->GetState()->GetCacheEntryType(name) == |
1881 | 0 | cmStateEnums::UNINITIALIZED)) { |
1882 | | // if this is not a force, then use the value from the cache |
1883 | | // if it is a force, then use the value being passed in |
1884 | 0 | if (!force) { |
1885 | 0 | value = existingValue; |
1886 | 0 | } |
1887 | 0 | if (type == cmStateEnums::PATH || type == cmStateEnums::FILEPATH) { |
1888 | 0 | cmList files(value); |
1889 | 0 | for (auto& file : files) { |
1890 | 0 | if (!cmIsOff(file)) { |
1891 | 0 | file = cmSystemTools::ToNormalizedPathOnDisk(file); |
1892 | 0 | } |
1893 | 0 | } |
1894 | 0 | nvalue = files.to_string(); |
1895 | 0 | value = cmValue{ nvalue }; |
1896 | |
|
1897 | 0 | this->GetCMakeInstance()->AddCacheEntry(name, value, doc, type); |
1898 | 0 | value = this->GetState()->GetInitializedCacheValue(name); |
1899 | 0 | } |
1900 | 0 | } |
1901 | 0 | this->GetCMakeInstance()->AddCacheEntry(name, value, doc, type); |
1902 | 0 | switch (this->GetPolicyStatus(cmPolicies::CMP0126)) { |
1903 | 0 | case cmPolicies::WARN: |
1904 | 0 | if (this->PolicyOptionalWarningEnabled("CMAKE_POLICY_WARNING_CMP0126") && |
1905 | 0 | this->IsNormalDefinitionSet(name)) { |
1906 | 0 | this->IssueMessage( |
1907 | 0 | MessageType::AUTHOR_WARNING, |
1908 | 0 | cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0126), |
1909 | 0 | "\nFor compatibility with older versions of CMake, normal " |
1910 | 0 | "variable \"", |
1911 | 0 | name, "\" will be removed from the current scope.")); |
1912 | 0 | } |
1913 | 0 | CM_FALLTHROUGH; |
1914 | 0 | case cmPolicies::OLD: |
1915 | | // if there was a definition then remove it |
1916 | 0 | this->StateSnapshot.RemoveDefinition(name); |
1917 | 0 | break; |
1918 | 0 | case cmPolicies::NEW: |
1919 | 0 | break; |
1920 | 0 | } |
1921 | 0 | } |
1922 | | |
1923 | | void cmMakefile::MarkVariableAsUsed(std::string const& var) |
1924 | 4 | { |
1925 | 4 | this->StateSnapshot.GetDefinition(var); |
1926 | 4 | } |
1927 | | |
1928 | | bool cmMakefile::VariableInitialized(std::string const& var) const |
1929 | 0 | { |
1930 | 0 | return this->StateSnapshot.IsInitialized(var); |
1931 | 0 | } |
1932 | | |
1933 | | void cmMakefile::MaybeWarnUninitialized(std::string const& variable, |
1934 | | char const* sourceFilename) const |
1935 | 0 | { |
1936 | | // check to see if we need to print a warning |
1937 | | // if strict mode is on and the variable has |
1938 | | // not been "cleared"/initialized with a set(foo ) call |
1939 | 0 | if (this->GetCMakeInstance()->GetWarnUninitialized() && |
1940 | 0 | !this->VariableInitialized(variable)) { |
1941 | 0 | if (this->CheckSystemVars || |
1942 | 0 | (sourceFilename && this->IsProjectFile(sourceFilename))) { |
1943 | 0 | this->IssueMessage(MessageType::AUTHOR_WARNING, |
1944 | 0 | cmStrCat("uninitialized variable '", variable, '\'')); |
1945 | 0 | } |
1946 | 0 | } |
1947 | 0 | } |
1948 | | |
1949 | | void cmMakefile::RemoveDefinition(std::string const& name) |
1950 | 1 | { |
1951 | 1 | this->StateSnapshot.RemoveDefinition(name); |
1952 | 1 | #ifndef CMAKE_BOOTSTRAP |
1953 | 1 | cmVariableWatch* vv = this->GetVariableWatch(); |
1954 | 1 | if (vv) { |
1955 | 1 | vv->VariableAccessed(name, cmVariableWatch::VARIABLE_REMOVED_ACCESS, |
1956 | 1 | nullptr, this); |
1957 | 1 | } |
1958 | 1 | #endif |
1959 | 1 | } |
1960 | | |
1961 | | void cmMakefile::RemoveCacheDefinition(std::string const& name) const |
1962 | 0 | { |
1963 | 0 | this->GetState()->RemoveCacheEntry(name); |
1964 | 0 | } |
1965 | | |
1966 | | void cmMakefile::SetProjectName(std::string const& p) |
1967 | 0 | { |
1968 | 0 | this->StateSnapshot.SetProjectName(p); |
1969 | 0 | } |
1970 | | |
1971 | | void cmMakefile::AddGlobalLinkInformation(cmTarget& target) |
1972 | 0 | { |
1973 | | // for these targets do not add anything |
1974 | 0 | switch (target.GetType()) { |
1975 | 0 | case cmStateEnums::UTILITY: |
1976 | 0 | case cmStateEnums::GLOBAL_TARGET: |
1977 | 0 | case cmStateEnums::INTERFACE_LIBRARY: |
1978 | 0 | return; |
1979 | 0 | default:; |
1980 | 0 | } |
1981 | | |
1982 | 0 | if (cmValue linkLibsProp = this->GetProperty("LINK_LIBRARIES")) { |
1983 | 0 | cmList linkLibs{ *linkLibsProp }; |
1984 | |
|
1985 | 0 | for (auto j = linkLibs.begin(); j != linkLibs.end(); ++j) { |
1986 | 0 | std::string libraryName = *j; |
1987 | 0 | cmTargetLinkLibraryType libType = GENERAL_LibraryType; |
1988 | 0 | if (libraryName == "optimized"_s) { |
1989 | 0 | libType = OPTIMIZED_LibraryType; |
1990 | 0 | ++j; |
1991 | 0 | libraryName = *j; |
1992 | 0 | } else if (libraryName == "debug"_s) { |
1993 | 0 | libType = DEBUG_LibraryType; |
1994 | 0 | ++j; |
1995 | 0 | libraryName = *j; |
1996 | 0 | } |
1997 | | // This is equivalent to the target_link_libraries plain signature. |
1998 | 0 | target.AddLinkLibrary(*this, libraryName, libType); |
1999 | 0 | target.AppendProperty( |
2000 | 0 | "INTERFACE_LINK_LIBRARIES", |
2001 | 0 | target.GetDebugGeneratorExpressions(libraryName, libType)); |
2002 | 0 | } |
2003 | 0 | } |
2004 | 0 | } |
2005 | | |
2006 | | void cmMakefile::AddAlias(std::string const& lname, std::string const& tgtName, |
2007 | | bool globallyVisible) |
2008 | 0 | { |
2009 | 0 | this->AliasTargets[lname] = tgtName; |
2010 | 0 | if (globallyVisible) { |
2011 | 0 | this->GetGlobalGenerator()->AddAlias(lname, tgtName); |
2012 | 0 | } |
2013 | 0 | } |
2014 | | |
2015 | | cmTarget* cmMakefile::AddLibrary(std::string const& lname, |
2016 | | cmStateEnums::TargetType type, |
2017 | | std::vector<std::string> const& srcs, |
2018 | | bool excludeFromAll) |
2019 | 0 | { |
2020 | 0 | assert(type == cmStateEnums::STATIC_LIBRARY || |
2021 | 0 | type == cmStateEnums::SHARED_LIBRARY || |
2022 | 0 | type == cmStateEnums::MODULE_LIBRARY || |
2023 | 0 | type == cmStateEnums::OBJECT_LIBRARY || |
2024 | 0 | type == cmStateEnums::INTERFACE_LIBRARY); |
2025 | |
|
2026 | 0 | cmTarget* target = this->AddNewTarget(type, lname); |
2027 | | // Clear its dependencies. Otherwise, dependencies might persist |
2028 | | // over changes in CMakeLists.txt, making the information stale and |
2029 | | // hence useless. |
2030 | 0 | target->ClearDependencyInformation(*this); |
2031 | 0 | if (excludeFromAll) { |
2032 | 0 | target->SetProperty("EXCLUDE_FROM_ALL", "TRUE"); |
2033 | 0 | } |
2034 | 0 | target->AddSources(srcs); |
2035 | 0 | this->AddGlobalLinkInformation(*target); |
2036 | 0 | return target; |
2037 | 0 | } |
2038 | | |
2039 | | cmTarget* cmMakefile::AddExecutable(std::string const& exeName, |
2040 | | std::vector<std::string> const& srcs, |
2041 | | bool excludeFromAll) |
2042 | 0 | { |
2043 | 0 | cmTarget* target = this->AddNewTarget(cmStateEnums::EXECUTABLE, exeName); |
2044 | 0 | if (excludeFromAll) { |
2045 | 0 | target->SetProperty("EXCLUDE_FROM_ALL", "TRUE"); |
2046 | 0 | } |
2047 | 0 | target->AddSources(srcs); |
2048 | 0 | this->AddGlobalLinkInformation(*target); |
2049 | 0 | return target; |
2050 | 0 | } |
2051 | | |
2052 | | cmTarget* cmMakefile::AddNewTarget(cmStateEnums::TargetType type, |
2053 | | std::string const& name) |
2054 | 0 | { |
2055 | 0 | return &this->CreateNewTarget(name, type).first; |
2056 | 0 | } |
2057 | | |
2058 | | cmTarget* cmMakefile::AddSynthesizedTarget(cmStateEnums::TargetType type, |
2059 | | std::string const& name) |
2060 | 0 | { |
2061 | 0 | return &this |
2062 | 0 | ->CreateNewTarget(name, type, cmTarget::PerConfig::Yes, |
2063 | 0 | cmTarget::Visibility::Generated) |
2064 | 0 | .first; |
2065 | 0 | } |
2066 | | |
2067 | | std::pair<cmTarget&, bool> cmMakefile::CreateNewTarget( |
2068 | | std::string const& name, cmStateEnums::TargetType type, |
2069 | | cmTarget::PerConfig perConfig, cmTarget::Visibility vis) |
2070 | 0 | { |
2071 | 0 | auto ib = |
2072 | 0 | this->Targets.emplace(name, cmTarget(name, type, vis, this, perConfig)); |
2073 | 0 | auto it = ib.first; |
2074 | 0 | if (!ib.second) { |
2075 | 0 | return std::make_pair(std::ref(it->second), false); |
2076 | 0 | } |
2077 | 0 | this->OrderedTargets.push_back(&it->second); |
2078 | 0 | this->GetGlobalGenerator()->IndexTarget(&it->second); |
2079 | 0 | this->GetStateSnapshot().GetDirectory().AddNormalTargetName(name); |
2080 | 0 | return std::make_pair(std::ref(it->second), true); |
2081 | 0 | } |
2082 | | |
2083 | | cmTarget* cmMakefile::AddNewUtilityTarget(std::string const& utilityName, |
2084 | | bool excludeFromAll) |
2085 | 0 | { |
2086 | 0 | cmTarget* target = this->AddNewTarget(cmStateEnums::UTILITY, utilityName); |
2087 | 0 | if (excludeFromAll) { |
2088 | 0 | target->SetProperty("EXCLUDE_FROM_ALL", "TRUE"); |
2089 | 0 | } |
2090 | 0 | return target; |
2091 | 0 | } |
2092 | | |
2093 | | namespace { |
2094 | | } |
2095 | | |
2096 | | #if !defined(CMAKE_BOOTSTRAP) |
2097 | | |
2098 | | void cmMakefile::ResolveSourceGroupGenex(cmLocalGenerator* lg) |
2099 | 0 | { |
2100 | 0 | for (auto const& sourceGroup : this->SourceGroups) { |
2101 | 0 | sourceGroup->ResolveGenex(lg, {}); |
2102 | 0 | } |
2103 | 0 | } |
2104 | | |
2105 | | cmSourceGroup* cmMakefile::GetSourceGroup( |
2106 | | std::vector<std::string> const& name) const |
2107 | 14 | { |
2108 | 14 | cmSourceGroup* sg = nullptr; |
2109 | | |
2110 | | // first look for source group starting with the same as the one we want |
2111 | 49 | for (auto const& srcGroup : this->SourceGroups) { |
2112 | 49 | std::string const& sgName = srcGroup->GetName(); |
2113 | 49 | if (sgName == name[0]) { |
2114 | 7 | sg = srcGroup.get(); |
2115 | 7 | break; |
2116 | 7 | } |
2117 | 49 | } |
2118 | | |
2119 | 14 | if (sg) { |
2120 | | // iterate through its children to find match source group |
2121 | 7 | for (unsigned int i = 1; i < name.size(); ++i) { |
2122 | 0 | sg = sg->LookupChild(name[i]); |
2123 | 0 | if (!sg) { |
2124 | 0 | break; |
2125 | 0 | } |
2126 | 0 | } |
2127 | 7 | } |
2128 | 14 | return sg; |
2129 | 14 | } |
2130 | | |
2131 | | void cmMakefile::AddSourceGroup(std::string const& name, char const* regex) |
2132 | 7 | { |
2133 | 7 | std::vector<std::string> nameVector; |
2134 | 7 | nameVector.push_back(name); |
2135 | 7 | this->AddSourceGroup(nameVector, regex); |
2136 | 7 | } |
2137 | | |
2138 | | void cmMakefile::AddSourceGroup(std::vector<std::string> const& name, |
2139 | | char const* regex) |
2140 | 7 | { |
2141 | 7 | cmSourceGroup* sg = nullptr; |
2142 | 7 | std::vector<std::string> currentName; |
2143 | 7 | int i = 0; |
2144 | 7 | int const lastElement = static_cast<int>(name.size() - 1); |
2145 | 14 | for (i = lastElement; i >= 0; --i) { |
2146 | 7 | currentName.assign(name.begin(), name.begin() + i + 1); |
2147 | 7 | sg = this->GetSourceGroup(currentName); |
2148 | 7 | if (sg) { |
2149 | 0 | break; |
2150 | 0 | } |
2151 | 7 | } |
2152 | | |
2153 | | // i now contains the index of the last found component |
2154 | 7 | if (i == lastElement) { |
2155 | | // group already exists, replace its regular expression |
2156 | 0 | if (regex && sg) { |
2157 | | // We only want to set the regular expression. If there are already |
2158 | | // source files in the group, we don't want to remove them. |
2159 | 0 | sg->SetGroupRegex(regex); |
2160 | 0 | } |
2161 | 0 | return; |
2162 | 0 | } |
2163 | 7 | if (i == -1) { |
2164 | | // group does not exist nor belong to any existing group |
2165 | | // add its first component |
2166 | 7 | this->SourceGroups.emplace_back( |
2167 | 7 | cm::make_unique<cmSourceGroup>(name[0], regex)); |
2168 | 7 | sg = this->GetSourceGroup(currentName); |
2169 | 7 | i = 0; // last component found |
2170 | 7 | } |
2171 | 7 | if (!sg) { |
2172 | 0 | cmSystemTools::Error("Could not create source group "); |
2173 | 0 | return; |
2174 | 0 | } |
2175 | | // build the whole source group path |
2176 | 7 | for (++i; i <= lastElement; ++i) { |
2177 | 0 | sg->AddChild(cm::make_unique<cmSourceGroup>(name[i], nullptr, |
2178 | 0 | sg->GetFullName().c_str())); |
2179 | 0 | sg = sg->LookupChild(name[i]); |
2180 | 0 | } |
2181 | | |
2182 | 7 | sg->SetGroupRegex(regex); |
2183 | 7 | } |
2184 | | |
2185 | | cmSourceGroup* cmMakefile::GetOrCreateSourceGroup( |
2186 | | std::vector<std::string> const& folders) |
2187 | 0 | { |
2188 | 0 | cmSourceGroup* sg = this->GetSourceGroup(folders); |
2189 | 0 | if (!sg) { |
2190 | 0 | this->AddSourceGroup(folders); |
2191 | 0 | sg = this->GetSourceGroup(folders); |
2192 | 0 | } |
2193 | 0 | return sg; |
2194 | 0 | } |
2195 | | |
2196 | | cmSourceGroup* cmMakefile::GetOrCreateSourceGroup(std::string const& name) |
2197 | 0 | { |
2198 | 0 | auto p = this->GetDefinition("SOURCE_GROUP_DELIMITER"); |
2199 | 0 | return this->GetOrCreateSourceGroup( |
2200 | 0 | cmTokenize(name, p ? cm::string_view(*p) : R"(\/)"_s)); |
2201 | 0 | } |
2202 | | #endif |
2203 | | |
2204 | | bool cmMakefile::IsOn(std::string const& name) const |
2205 | 0 | { |
2206 | 0 | return this->GetDefinition(name).IsOn(); |
2207 | 0 | } |
2208 | | |
2209 | | bool cmMakefile::IsSet(std::string const& name) const |
2210 | 0 | { |
2211 | 0 | cmValue value = this->GetDefinition(name); |
2212 | 0 | if (!value) { |
2213 | 0 | return false; |
2214 | 0 | } |
2215 | | |
2216 | 0 | if (value->empty()) { |
2217 | 0 | return false; |
2218 | 0 | } |
2219 | | |
2220 | 0 | if (cmIsNOTFOUND(*value)) { |
2221 | 0 | return false; |
2222 | 0 | } |
2223 | | |
2224 | 0 | return true; |
2225 | 0 | } |
2226 | | |
2227 | | bool cmMakefile::PlatformIs32Bit() const |
2228 | 0 | { |
2229 | 0 | if (cmValue plat_abi = this->GetDefinition("CMAKE_INTERNAL_PLATFORM_ABI")) { |
2230 | 0 | if (*plat_abi == "ELF X32"_s) { |
2231 | 0 | return false; |
2232 | 0 | } |
2233 | 0 | } |
2234 | 0 | if (cmValue sizeof_dptr = this->GetDefinition("CMAKE_SIZEOF_VOID_P")) { |
2235 | 0 | return atoi(sizeof_dptr->c_str()) == 4; |
2236 | 0 | } |
2237 | 0 | return false; |
2238 | 0 | } |
2239 | | |
2240 | | bool cmMakefile::PlatformIs64Bit() const |
2241 | 0 | { |
2242 | 0 | if (cmValue sizeof_dptr = this->GetDefinition("CMAKE_SIZEOF_VOID_P")) { |
2243 | 0 | return atoi(sizeof_dptr->c_str()) == 8; |
2244 | 0 | } |
2245 | 0 | return false; |
2246 | 0 | } |
2247 | | |
2248 | | bool cmMakefile::PlatformIsx32() const |
2249 | 0 | { |
2250 | 0 | if (cmValue plat_abi = this->GetDefinition("CMAKE_INTERNAL_PLATFORM_ABI")) { |
2251 | 0 | if (*plat_abi == "ELF X32"_s) { |
2252 | 0 | return true; |
2253 | 0 | } |
2254 | 0 | } |
2255 | 0 | return false; |
2256 | 0 | } |
2257 | | |
2258 | | cmMakefile::AppleSDK cmMakefile::GetAppleSDKType() const |
2259 | 0 | { |
2260 | 0 | std::string sdkRoot; |
2261 | 0 | sdkRoot = this->GetSafeDefinition("CMAKE_OSX_SYSROOT"); |
2262 | 0 | sdkRoot = cmSystemTools::LowerCase(sdkRoot); |
2263 | |
|
2264 | 0 | struct |
2265 | 0 | { |
2266 | 0 | std::string name; |
2267 | 0 | AppleSDK sdk; |
2268 | 0 | } const sdkDatabase[]{ |
2269 | 0 | { "appletvos", AppleSDK::AppleTVOS }, |
2270 | 0 | { "appletvsimulator", AppleSDK::AppleTVSimulator }, |
2271 | 0 | { "iphoneos", AppleSDK::IPhoneOS }, |
2272 | 0 | { "iphonesimulator", AppleSDK::IPhoneSimulator }, |
2273 | 0 | { "watchos", AppleSDK::WatchOS }, |
2274 | 0 | { "watchsimulator", AppleSDK::WatchSimulator }, |
2275 | 0 | { "xros", AppleSDK::XROS }, |
2276 | 0 | { "xrsimulator", AppleSDK::XRSimulator }, |
2277 | 0 | }; |
2278 | |
|
2279 | 0 | for (auto const& entry : sdkDatabase) { |
2280 | 0 | if (cmHasPrefix(sdkRoot, entry.name) || |
2281 | 0 | sdkRoot.find(cmStrCat('/', entry.name)) != std::string::npos) { |
2282 | 0 | return entry.sdk; |
2283 | 0 | } |
2284 | 0 | } |
2285 | | |
2286 | 0 | return AppleSDK::MacOS; |
2287 | 0 | } |
2288 | | |
2289 | | bool cmMakefile::PlatformIsAppleEmbedded() const |
2290 | 0 | { |
2291 | 0 | return this->GetAppleSDKType() != AppleSDK::MacOS; |
2292 | 0 | } |
2293 | | |
2294 | | bool cmMakefile::PlatformIsAppleSimulator() const |
2295 | 0 | { |
2296 | 0 | return std::set<AppleSDK>{ |
2297 | 0 | AppleSDK::AppleTVSimulator, |
2298 | 0 | AppleSDK::IPhoneSimulator, |
2299 | 0 | AppleSDK::WatchSimulator, |
2300 | 0 | AppleSDK::XRSimulator, |
2301 | 0 | } |
2302 | 0 | .count(this->GetAppleSDKType()); |
2303 | 0 | } |
2304 | | |
2305 | | bool cmMakefile::PlatformIsAppleCatalyst() const |
2306 | 0 | { |
2307 | 0 | std::string systemName; |
2308 | 0 | systemName = this->GetSafeDefinition("CMAKE_SYSTEM_NAME"); |
2309 | 0 | systemName = cmSystemTools::LowerCase(systemName); |
2310 | 0 | return systemName == "ios" && this->GetAppleSDKType() == AppleSDK::MacOS; |
2311 | 0 | } |
2312 | | |
2313 | | bool cmMakefile::PlatformSupportsAppleTextStubs() const |
2314 | 0 | { |
2315 | 0 | return this->IsOn("APPLE") && this->IsSet("CMAKE_TAPI"); |
2316 | 0 | } |
2317 | | |
2318 | | char const* cmMakefile::GetSONameFlag(std::string const& language) const |
2319 | 0 | { |
2320 | 0 | std::string name = "CMAKE_SHARED_LIBRARY_SONAME"; |
2321 | 0 | if (!language.empty()) { |
2322 | 0 | name += "_"; |
2323 | 0 | name += language; |
2324 | 0 | } |
2325 | 0 | name += "_FLAG"; |
2326 | 0 | return this->GetDefinition(name).GetCStr(); |
2327 | 0 | } |
2328 | | |
2329 | | bool cmMakefile::CanIWriteThisFile(std::string const& fileName) const |
2330 | 0 | { |
2331 | 0 | if (!this->IsOn("CMAKE_DISABLE_SOURCE_CHANGES")) { |
2332 | 0 | return true; |
2333 | 0 | } |
2334 | | // If we are doing an in-source build, then the test will always fail |
2335 | 0 | if (cmSystemTools::SameFile(this->GetHomeDirectory(), |
2336 | 0 | this->GetHomeOutputDirectory())) { |
2337 | 0 | return !this->IsOn("CMAKE_DISABLE_IN_SOURCE_BUILD"); |
2338 | 0 | } |
2339 | | |
2340 | 0 | return !cmSystemTools::IsSubDirectory(fileName, this->GetHomeDirectory()) || |
2341 | 0 | cmSystemTools::IsSubDirectory(fileName, this->GetHomeOutputDirectory()) || |
2342 | 0 | cmSystemTools::SameFile(fileName, this->GetHomeOutputDirectory()); |
2343 | 0 | } |
2344 | | |
2345 | | std::string const& cmMakefile::GetRequiredDefinition( |
2346 | | std::string const& name) const |
2347 | 0 | { |
2348 | 0 | static std::string const empty; |
2349 | 0 | cmValue def = this->GetDefinition(name); |
2350 | 0 | if (!def) { |
2351 | 0 | cmSystemTools::Error("Error required internal CMake variable not " |
2352 | 0 | "set, cmake may not be built correctly.\n" |
2353 | 0 | "Missing variable is:\n" + |
2354 | 0 | name); |
2355 | 0 | return empty; |
2356 | 0 | } |
2357 | 0 | return *def; |
2358 | 0 | } |
2359 | | |
2360 | | bool cmMakefile::IsDefinitionSet(std::string const& name) const |
2361 | 0 | { |
2362 | 0 | cmValue def = this->StateSnapshot.GetDefinition(name); |
2363 | 0 | if (!def) { |
2364 | 0 | def = this->GetState()->GetInitializedCacheValue(name); |
2365 | 0 | } |
2366 | 0 | #ifndef CMAKE_BOOTSTRAP |
2367 | 0 | if (cmVariableWatch* vv = this->GetVariableWatch()) { |
2368 | 0 | if (!def) { |
2369 | 0 | vv->VariableAccessed( |
2370 | 0 | name, cmVariableWatch::UNKNOWN_VARIABLE_DEFINED_ACCESS, nullptr, this); |
2371 | 0 | } |
2372 | 0 | } |
2373 | 0 | #endif |
2374 | 0 | return def != nullptr; |
2375 | 0 | } |
2376 | | |
2377 | | bool cmMakefile::IsNormalDefinitionSet(std::string const& name) const |
2378 | 0 | { |
2379 | 0 | cmValue def = this->StateSnapshot.GetDefinition(name); |
2380 | 0 | #ifndef CMAKE_BOOTSTRAP |
2381 | 0 | if (cmVariableWatch* vv = this->GetVariableWatch()) { |
2382 | 0 | if (!def) { |
2383 | 0 | vv->VariableAccessed( |
2384 | 0 | name, cmVariableWatch::UNKNOWN_VARIABLE_DEFINED_ACCESS, nullptr, this); |
2385 | 0 | } |
2386 | 0 | } |
2387 | 0 | #endif |
2388 | 0 | return def != nullptr; |
2389 | 0 | } |
2390 | | |
2391 | | cmValue cmMakefile::GetDefinition(std::string const& name) const |
2392 | 2 | { |
2393 | 2 | cmValue def = this->StateSnapshot.GetDefinition(name); |
2394 | 2 | if (!def) { |
2395 | 2 | def = this->GetState()->GetInitializedCacheValue(name); |
2396 | 2 | } |
2397 | 2 | #ifndef CMAKE_BOOTSTRAP |
2398 | 2 | cmVariableWatch* vv = this->GetVariableWatch(); |
2399 | 2 | if (vv) { |
2400 | 2 | bool const watch_function_executed = |
2401 | 2 | vv->VariableAccessed(name, |
2402 | 2 | def ? cmVariableWatch::VARIABLE_READ_ACCESS |
2403 | 2 | : cmVariableWatch::UNKNOWN_VARIABLE_READ_ACCESS, |
2404 | 2 | def.GetCStr(), this); |
2405 | | |
2406 | 2 | if (watch_function_executed) { |
2407 | | // A callback was executed and may have caused re-allocation of the |
2408 | | // variable storage. Look it up again for now. |
2409 | | // FIXME: Refactor variable storage to avoid this problem. |
2410 | 0 | def = this->StateSnapshot.GetDefinition(name); |
2411 | 0 | if (!def) { |
2412 | 0 | def = this->GetState()->GetInitializedCacheValue(name); |
2413 | 0 | } |
2414 | 0 | } |
2415 | 2 | } |
2416 | 2 | #endif |
2417 | 2 | return def; |
2418 | 2 | } |
2419 | | |
2420 | | std::string const& cmMakefile::GetSafeDefinition(std::string const& name) const |
2421 | 0 | { |
2422 | 0 | return this->GetDefinition(name); |
2423 | 0 | } |
2424 | | |
2425 | | std::vector<std::string> cmMakefile::GetDefinitions() const |
2426 | 0 | { |
2427 | 0 | std::vector<std::string> res = this->StateSnapshot.ClosureKeys(); |
2428 | 0 | cm::append(res, this->GetState()->GetCacheEntryKeys()); |
2429 | 0 | std::sort(res.begin(), res.end()); |
2430 | 0 | return res; |
2431 | 0 | } |
2432 | | |
2433 | | std::string const& cmMakefile::ExpandVariablesInString( |
2434 | | std::string& source) const |
2435 | 0 | { |
2436 | 0 | return this->ExpandVariablesInString(source, false, false); |
2437 | 0 | } |
2438 | | |
2439 | | std::string const& cmMakefile::ExpandVariablesInString( |
2440 | | std::string& source, bool escapeQuotes, bool noEscapes, bool atOnly, |
2441 | | char const* filename, long line, bool removeEmpty, bool replaceAt) const |
2442 | 0 | { |
2443 | | // Sanity check the @ONLY mode. |
2444 | 0 | if (atOnly && (!noEscapes || !removeEmpty)) { |
2445 | | // This case should never be called. At-only is for |
2446 | | // configure-file/string which always does no escapes. |
2447 | 0 | this->IssueMessage(MessageType::INTERNAL_ERROR, |
2448 | 0 | "ExpandVariablesInString @ONLY called " |
2449 | 0 | "on something with escapes."); |
2450 | 0 | return source; |
2451 | 0 | } |
2452 | | |
2453 | 0 | std::string errorstr; |
2454 | 0 | MessageType mtype = this->ExpandVariablesInStringImpl( |
2455 | 0 | errorstr, source, escapeQuotes, noEscapes, atOnly, filename, line, |
2456 | 0 | replaceAt); |
2457 | 0 | if (mtype != MessageType::LOG) { |
2458 | 0 | if (mtype == MessageType::FATAL_ERROR) { |
2459 | 0 | cmSystemTools::SetFatalErrorOccurred(); |
2460 | 0 | } |
2461 | 0 | this->IssueMessage(mtype, errorstr); |
2462 | 0 | } |
2463 | |
|
2464 | 0 | return source; |
2465 | 0 | } |
2466 | | |
2467 | | enum t_domain |
2468 | | { |
2469 | | NORMAL, |
2470 | | ENVIRONMENT, |
2471 | | CACHE |
2472 | | }; |
2473 | | |
2474 | | struct t_lookup |
2475 | | { |
2476 | | t_domain domain = NORMAL; |
2477 | | size_t loc = 0; |
2478 | | }; |
2479 | | |
2480 | | bool cmMakefile::IsProjectFile(char const* filename) const |
2481 | 0 | { |
2482 | 0 | return cmSystemTools::IsSubDirectory(filename, this->GetHomeDirectory()) || |
2483 | 0 | (cmSystemTools::IsSubDirectory(filename, this->GetHomeOutputDirectory()) && |
2484 | 0 | !cmSystemTools::IsSubDirectory(filename, "/CMakeFiles")); |
2485 | 0 | } |
2486 | | |
2487 | | size_t cmMakefile::GetRecursionDepthLimit() const |
2488 | 0 | { |
2489 | 0 | size_t depth = CMake_DEFAULT_RECURSION_LIMIT; |
2490 | 0 | if (cmValue depthStr = |
2491 | 0 | this->GetDefinition("CMAKE_MAXIMUM_RECURSION_DEPTH")) { |
2492 | 0 | unsigned long depthUL; |
2493 | 0 | if (cmStrToULong(depthStr.GetCStr(), &depthUL)) { |
2494 | 0 | depth = depthUL; |
2495 | 0 | } |
2496 | 0 | } else if (cm::optional<std::string> depthEnv = |
2497 | 0 | cmSystemTools::GetEnvVar("CMAKE_MAXIMUM_RECURSION_DEPTH")) { |
2498 | 0 | unsigned long depthUL; |
2499 | 0 | if (cmStrToULong(*depthEnv, &depthUL)) { |
2500 | 0 | depth = depthUL; |
2501 | 0 | } |
2502 | 0 | } |
2503 | 0 | return depth; |
2504 | 0 | } |
2505 | | |
2506 | | size_t cmMakefile::GetRecursionDepth() const |
2507 | 0 | { |
2508 | 0 | return this->RecursionDepth; |
2509 | 0 | } |
2510 | | |
2511 | | void cmMakefile::SetRecursionDepth(size_t recursionDepth) |
2512 | 0 | { |
2513 | 0 | this->RecursionDepth = recursionDepth; |
2514 | 0 | } |
2515 | | |
2516 | | std::string cmMakefile::NewDeferId() const |
2517 | 0 | { |
2518 | 0 | return this->GetGlobalGenerator()->NewDeferId(); |
2519 | 0 | } |
2520 | | |
2521 | | bool cmMakefile::DeferCall(std::string id, std::string file, |
2522 | | cmListFileFunction lff) |
2523 | 0 | { |
2524 | 0 | if (!this->Defer) { |
2525 | 0 | return false; |
2526 | 0 | } |
2527 | 0 | this->Defer->Commands.emplace_back( |
2528 | 0 | DeferCommand{ std::move(id), std::move(file), std::move(lff) }); |
2529 | 0 | return true; |
2530 | 0 | } |
2531 | | |
2532 | | bool cmMakefile::DeferCancelCall(std::string const& id) |
2533 | 0 | { |
2534 | 0 | if (!this->Defer) { |
2535 | 0 | return false; |
2536 | 0 | } |
2537 | 0 | for (DeferCommand& dc : this->Defer->Commands) { |
2538 | 0 | if (dc.Id == id) { |
2539 | 0 | dc.Id.clear(); |
2540 | 0 | } |
2541 | 0 | } |
2542 | 0 | return true; |
2543 | 0 | } |
2544 | | |
2545 | | cm::optional<std::string> cmMakefile::DeferGetCallIds() const |
2546 | 0 | { |
2547 | 0 | cm::optional<std::string> ids; |
2548 | 0 | if (this->Defer) { |
2549 | 0 | ids = cmList::to_string( |
2550 | 0 | cmMakeRange(this->Defer->Commands) |
2551 | 0 | .filter([](DeferCommand const& dc) -> bool { return !dc.Id.empty(); }) |
2552 | 0 | .transform( |
2553 | 0 | [](DeferCommand const& dc) -> std::string const& { return dc.Id; })); |
2554 | 0 | } |
2555 | 0 | return ids; |
2556 | 0 | } |
2557 | | |
2558 | | cm::optional<std::string> cmMakefile::DeferGetCall(std::string const& id) const |
2559 | 0 | { |
2560 | 0 | cm::optional<std::string> call; |
2561 | 0 | if (this->Defer) { |
2562 | 0 | std::string tmp; |
2563 | 0 | for (DeferCommand const& dc : this->Defer->Commands) { |
2564 | 0 | if (dc.Id == id) { |
2565 | 0 | tmp = dc.Command.OriginalName(); |
2566 | 0 | for (cmListFileArgument const& arg : dc.Command.Arguments()) { |
2567 | 0 | tmp = cmStrCat(tmp, ';', arg.Value); |
2568 | 0 | } |
2569 | 0 | break; |
2570 | 0 | } |
2571 | 0 | } |
2572 | 0 | call = std::move(tmp); |
2573 | 0 | } |
2574 | 0 | return call; |
2575 | 0 | } |
2576 | | |
2577 | | MessageType cmMakefile::ExpandVariablesInStringImpl( |
2578 | | std::string& errorstr, std::string& source, bool escapeQuotes, |
2579 | | bool noEscapes, bool atOnly, char const* filename, long line, |
2580 | | bool replaceAt) const |
2581 | 0 | { |
2582 | | // This method replaces ${VAR} and @VAR@ where VAR is looked up |
2583 | | // with GetDefinition(), if not found in the map, nothing is expanded. |
2584 | | // It also supports the $ENV{VAR} syntax where VAR is looked up in |
2585 | | // the current environment variables. |
2586 | |
|
2587 | 0 | char const* in = source.c_str(); |
2588 | 0 | char const* last = in; |
2589 | 0 | std::string result; |
2590 | 0 | result.reserve(source.size()); |
2591 | 0 | std::vector<t_lookup> openstack; |
2592 | 0 | bool error = false; |
2593 | 0 | bool done = false; |
2594 | 0 | MessageType mtype = MessageType::LOG; |
2595 | |
|
2596 | 0 | cmState* state = this->GetCMakeInstance()->GetState(); |
2597 | |
|
2598 | 0 | static std::string const lineVar = "CMAKE_CURRENT_LIST_LINE"; |
2599 | 0 | do { |
2600 | 0 | char inc = *in; |
2601 | 0 | switch (inc) { |
2602 | 0 | case '}': |
2603 | 0 | if (!openstack.empty()) { |
2604 | 0 | t_lookup var = openstack.back(); |
2605 | 0 | openstack.pop_back(); |
2606 | 0 | result.append(last, in - last); |
2607 | 0 | std::string const& lookup = result.substr(var.loc); |
2608 | 0 | cmValue value = nullptr; |
2609 | 0 | std::string varresult; |
2610 | 0 | std::string svalue; |
2611 | 0 | switch (var.domain) { |
2612 | 0 | case NORMAL: |
2613 | 0 | if (filename && lookup == lineVar) { |
2614 | 0 | cmListFileContext const& top = this->Backtrace.Top(); |
2615 | 0 | if (top.DeferId) { |
2616 | 0 | varresult = cmStrCat("DEFERRED:"_s, *top.DeferId); |
2617 | 0 | } else { |
2618 | 0 | varresult = std::to_string(line); |
2619 | 0 | } |
2620 | 0 | } else { |
2621 | 0 | value = this->GetDefinition(lookup); |
2622 | 0 | } |
2623 | 0 | break; |
2624 | 0 | case ENVIRONMENT: |
2625 | 0 | if (cmSystemTools::GetEnv(lookup, svalue)) { |
2626 | 0 | value = cmValue(svalue); |
2627 | 0 | } |
2628 | 0 | break; |
2629 | 0 | case CACHE: |
2630 | 0 | value = state->GetCacheEntryValue(lookup); |
2631 | 0 | break; |
2632 | 0 | } |
2633 | | // Get the string we're meant to append to. |
2634 | 0 | if (value) { |
2635 | 0 | if (escapeQuotes) { |
2636 | 0 | varresult = cmEscapeQuotes(*value); |
2637 | 0 | } else { |
2638 | 0 | varresult = *value; |
2639 | 0 | } |
2640 | 0 | } else { |
2641 | 0 | this->MaybeWarnUninitialized(lookup, filename); |
2642 | 0 | } |
2643 | 0 | result.replace(var.loc, result.size() - var.loc, varresult); |
2644 | | // Start looking from here on out. |
2645 | 0 | last = in + 1; |
2646 | 0 | } |
2647 | 0 | break; |
2648 | 0 | case '$': |
2649 | 0 | if (!atOnly) { |
2650 | 0 | t_lookup lookup; |
2651 | 0 | char const* next = in + 1; |
2652 | 0 | char const* start = nullptr; |
2653 | 0 | char nextc = *next; |
2654 | 0 | if (nextc == '{') { |
2655 | | // Looking for a variable. |
2656 | 0 | start = in + 2; |
2657 | 0 | lookup.domain = NORMAL; |
2658 | 0 | } else if (nextc == '<') { |
2659 | 0 | } else if (!nextc) { |
2660 | 0 | result.append(last, next - last); |
2661 | 0 | last = next; |
2662 | 0 | } else if (cmHasLiteralPrefix(next, "ENV{")) { |
2663 | | // Looking for an environment variable. |
2664 | 0 | start = in + 5; |
2665 | 0 | lookup.domain = ENVIRONMENT; |
2666 | 0 | } else if (cmHasLiteralPrefix(next, "CACHE{")) { |
2667 | | // Looking for a cache variable. |
2668 | 0 | start = in + 7; |
2669 | 0 | lookup.domain = CACHE; |
2670 | 0 | } else { |
2671 | 0 | if (this->cmNamedCurly.find(next)) { |
2672 | 0 | errorstr = "Syntax $" + |
2673 | 0 | std::string(next, this->cmNamedCurly.end()) + |
2674 | 0 | "{} is not supported. Only ${}, $ENV{}, " |
2675 | 0 | "and $CACHE{} are allowed."; |
2676 | 0 | mtype = MessageType::FATAL_ERROR; |
2677 | 0 | error = true; |
2678 | 0 | } |
2679 | 0 | } |
2680 | 0 | if (start) { |
2681 | 0 | result.append(last, in - last); |
2682 | 0 | last = start; |
2683 | 0 | in = start - 1; |
2684 | 0 | lookup.loc = result.size(); |
2685 | 0 | openstack.push_back(lookup); |
2686 | 0 | } |
2687 | 0 | break; |
2688 | 0 | } |
2689 | 0 | CM_FALLTHROUGH; |
2690 | 0 | case '\\': |
2691 | 0 | if (!noEscapes) { |
2692 | 0 | char const* next = in + 1; |
2693 | 0 | char nextc = *next; |
2694 | 0 | if (nextc == 't') { |
2695 | 0 | result.append(last, in - last); |
2696 | 0 | result.push_back('\t'); |
2697 | 0 | last = next + 1; |
2698 | 0 | } else if (nextc == 'n') { |
2699 | 0 | result.append(last, in - last); |
2700 | 0 | result.push_back('\n'); |
2701 | 0 | last = next + 1; |
2702 | 0 | } else if (nextc == 'r') { |
2703 | 0 | result.append(last, in - last); |
2704 | 0 | result.push_back('\r'); |
2705 | 0 | last = next + 1; |
2706 | 0 | } else if (nextc == ';' && openstack.empty()) { |
2707 | | // Handled in ExpandListArgument; pass the backslash literally. |
2708 | 0 | } else if (cmsysString_isalnum(nextc) || nextc == '\0') { |
2709 | 0 | errorstr += "Invalid character escape '\\"; |
2710 | 0 | if (nextc) { |
2711 | 0 | errorstr += nextc; |
2712 | 0 | errorstr += "'."; |
2713 | 0 | } else { |
2714 | 0 | errorstr += "' (at end of input)."; |
2715 | 0 | } |
2716 | 0 | error = true; |
2717 | 0 | } else { |
2718 | | // Take what we've found so far, skipping the escape character. |
2719 | 0 | result.append(last, in - last); |
2720 | | // Start tracking from the next character. |
2721 | 0 | last = in + 1; |
2722 | 0 | } |
2723 | | // Skip the next character since it was escaped, but don't read past |
2724 | | // the end of the string. |
2725 | 0 | if (*last) { |
2726 | 0 | ++in; |
2727 | 0 | } |
2728 | 0 | } |
2729 | 0 | break; |
2730 | 0 | case '\n': |
2731 | | // Onto the next line. |
2732 | 0 | ++line; |
2733 | 0 | break; |
2734 | 0 | case '\0': |
2735 | 0 | done = true; |
2736 | 0 | break; |
2737 | 0 | case '@': |
2738 | 0 | if (replaceAt) { |
2739 | 0 | char const* nextAt = strchr(in + 1, '@'); |
2740 | 0 | if (nextAt && nextAt != in + 1 && |
2741 | 0 | nextAt == |
2742 | 0 | in + 1 + |
2743 | 0 | std::strspn(in + 1, |
2744 | 0 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
2745 | 0 | "abcdefghijklmnopqrstuvwxyz" |
2746 | 0 | "0123456789/_.+-")) { |
2747 | 0 | std::string variable(in + 1, nextAt - in - 1); |
2748 | |
|
2749 | 0 | std::string varresult; |
2750 | 0 | if (filename && variable == lineVar) { |
2751 | 0 | varresult = std::to_string(line); |
2752 | 0 | } else { |
2753 | 0 | cmValue def = this->GetDefinition(variable); |
2754 | 0 | if (def) { |
2755 | 0 | varresult = *def; |
2756 | 0 | } else { |
2757 | 0 | this->MaybeWarnUninitialized(variable, filename); |
2758 | 0 | } |
2759 | 0 | } |
2760 | |
|
2761 | 0 | if (escapeQuotes) { |
2762 | 0 | varresult = cmEscapeQuotes(varresult); |
2763 | 0 | } |
2764 | | // Skip over the variable. |
2765 | 0 | result.append(last, in - last); |
2766 | 0 | result.append(varresult); |
2767 | 0 | in = nextAt; |
2768 | 0 | last = in + 1; |
2769 | 0 | break; |
2770 | 0 | } |
2771 | 0 | } |
2772 | | // Failed to find a valid @ expansion; treat it as literal. |
2773 | 0 | CM_FALLTHROUGH; |
2774 | 0 | default: { |
2775 | 0 | if (!openstack.empty() && |
2776 | 0 | !(cmsysString_isalnum(inc) || inc == '_' || inc == '/' || |
2777 | 0 | inc == '.' || inc == '+' || inc == '-')) { |
2778 | 0 | errorstr += cmStrCat("Invalid character ('", inc); |
2779 | 0 | result.append(last, in - last); |
2780 | 0 | errorstr += cmStrCat("') in a variable name: '", |
2781 | 0 | result.substr(openstack.back().loc), '\''); |
2782 | 0 | mtype = MessageType::FATAL_ERROR; |
2783 | 0 | error = true; |
2784 | 0 | } |
2785 | 0 | break; |
2786 | 0 | } |
2787 | 0 | } |
2788 | | // Look at the next character. |
2789 | 0 | } while (!error && !done && *++in); |
2790 | | |
2791 | | // Check for open variable references yet. |
2792 | 0 | if (!error && !openstack.empty()) { |
2793 | 0 | errorstr += "There is an unterminated variable reference."; |
2794 | 0 | error = true; |
2795 | 0 | } |
2796 | |
|
2797 | 0 | if (error) { |
2798 | 0 | std::string e = "Syntax error in cmake code "; |
2799 | 0 | if (filename) { |
2800 | | // This filename and line number may be more specific than the |
2801 | | // command context because one command invocation can have |
2802 | | // arguments on multiple lines. |
2803 | 0 | e += cmStrCat("at\n ", filename, ':', line, '\n'); |
2804 | 0 | } |
2805 | 0 | errorstr = cmStrCat(e, "when parsing string\n ", source, '\n', errorstr); |
2806 | 0 | mtype = MessageType::FATAL_ERROR; |
2807 | 0 | } else { |
2808 | | // Append the rest of the unchanged part of the string. |
2809 | 0 | result.append(last); |
2810 | |
|
2811 | 0 | source = result; |
2812 | 0 | } |
2813 | |
|
2814 | 0 | return mtype; |
2815 | 0 | } |
2816 | | |
2817 | | void cmMakefile::RemoveVariablesInString(std::string& source, |
2818 | | bool atOnly) const |
2819 | 0 | { |
2820 | 0 | if (!atOnly) { |
2821 | 0 | cmsys::RegularExpression var("(\\${[A-Za-z_0-9]*})"); |
2822 | 0 | while (var.find(source)) { |
2823 | 0 | source.erase(var.start(), var.end() - var.start()); |
2824 | 0 | } |
2825 | 0 | } |
2826 | |
|
2827 | 0 | if (!atOnly) { |
2828 | 0 | cmsys::RegularExpression varb("(\\$ENV{[A-Za-z_0-9]*})"); |
2829 | 0 | while (varb.find(source)) { |
2830 | 0 | source.erase(varb.start(), varb.end() - varb.start()); |
2831 | 0 | } |
2832 | 0 | } |
2833 | 0 | cmsys::RegularExpression var2("(@[A-Za-z_0-9]*@)"); |
2834 | 0 | while (var2.find(source)) { |
2835 | 0 | source.erase(var2.start(), var2.end() - var2.start()); |
2836 | 0 | } |
2837 | 0 | } |
2838 | | |
2839 | | void cmMakefile::InitCMAKE_CONFIGURATION_TYPES(std::string const& genDefault) |
2840 | 0 | { |
2841 | 0 | if (this->GetDefinition("CMAKE_CONFIGURATION_TYPES")) { |
2842 | 0 | return; |
2843 | 0 | } |
2844 | 0 | std::string initConfigs; |
2845 | 0 | if (this->GetCMakeInstance()->GetIsInTryCompile() || |
2846 | 0 | !cmSystemTools::GetEnv("CMAKE_CONFIGURATION_TYPES", initConfigs)) { |
2847 | 0 | initConfigs = genDefault; |
2848 | 0 | } |
2849 | 0 | this->AddCacheDefinition( |
2850 | 0 | "CMAKE_CONFIGURATION_TYPES", initConfigs, |
2851 | 0 | "Semicolon separated list of supported configuration types, " |
2852 | 0 | "only supports Debug, Release, MinSizeRel, and RelWithDebInfo, " |
2853 | 0 | "anything else will be ignored.", |
2854 | 0 | cmStateEnums::STRING); |
2855 | 0 | } |
2856 | | |
2857 | | std::string cmMakefile::GetDefaultConfiguration() const |
2858 | 0 | { |
2859 | 0 | if (this->GetGlobalGenerator()->IsMultiConfig()) { |
2860 | 0 | return std::string{}; |
2861 | 0 | } |
2862 | 0 | return this->GetSafeDefinition("CMAKE_BUILD_TYPE"); |
2863 | 0 | } |
2864 | | |
2865 | | std::vector<std::string> cmMakefile::GetGeneratorConfigs( |
2866 | | GeneratorConfigQuery mode) const |
2867 | 0 | { |
2868 | 0 | cmList configs; |
2869 | 0 | if (this->GetGlobalGenerator()->IsMultiConfig()) { |
2870 | 0 | configs.assign(this->GetDefinition("CMAKE_CONFIGURATION_TYPES")); |
2871 | 0 | } else if (mode != cmMakefile::OnlyMultiConfig) { |
2872 | 0 | std::string const& buildType = this->GetSafeDefinition("CMAKE_BUILD_TYPE"); |
2873 | 0 | if (!buildType.empty()) { |
2874 | 0 | configs.emplace_back(buildType); |
2875 | 0 | } |
2876 | 0 | } |
2877 | 0 | if (mode == cmMakefile::IncludeEmptyConfig && configs.empty()) { |
2878 | 0 | configs.emplace_back(); |
2879 | 0 | } |
2880 | 0 | return std::move(configs.data()); |
2881 | 0 | } |
2882 | | |
2883 | | bool cmMakefile::IsFunctionBlocked(cmListFileFunction const& lff, |
2884 | | cmExecutionStatus& status) |
2885 | 0 | { |
2886 | | // if there are no blockers get out of here |
2887 | 0 | if (this->FunctionBlockers.empty()) { |
2888 | 0 | return false; |
2889 | 0 | } |
2890 | | |
2891 | 0 | return this->FunctionBlockers.top()->IsFunctionBlocked(lff, status); |
2892 | 0 | } |
2893 | | |
2894 | | void cmMakefile::PushFunctionBlockerBarrier() |
2895 | 1 | { |
2896 | 1 | this->FunctionBlockerBarriers.push_back(this->FunctionBlockers.size()); |
2897 | 1 | } |
2898 | | |
2899 | | void cmMakefile::PopFunctionBlockerBarrier(bool reportError) |
2900 | 1 | { |
2901 | | // Remove any extra entries pushed on the barrier. |
2902 | 1 | FunctionBlockersType::size_type barrier = |
2903 | 1 | this->FunctionBlockerBarriers.back(); |
2904 | 1 | while (this->FunctionBlockers.size() > barrier) { |
2905 | 0 | std::unique_ptr<cmFunctionBlocker> fb( |
2906 | 0 | std::move(this->FunctionBlockers.top())); |
2907 | 0 | this->FunctionBlockers.pop(); |
2908 | 0 | if (reportError) { |
2909 | | // Report the context in which the unclosed block was opened. |
2910 | 0 | cmListFileContext const& lfc = fb->GetStartingContext(); |
2911 | 0 | std::ostringstream e; |
2912 | | /* clang-format off */ |
2913 | 0 | e << "A logical block opening on the line\n" |
2914 | 0 | " " << lfc << "\n" |
2915 | 0 | "is not closed."; |
2916 | | /* clang-format on */ |
2917 | 0 | this->IssueMessage(MessageType::FATAL_ERROR, e.str()); |
2918 | 0 | reportError = false; |
2919 | 0 | } |
2920 | 0 | } |
2921 | | |
2922 | | // Remove the barrier. |
2923 | 1 | this->FunctionBlockerBarriers.pop_back(); |
2924 | 1 | } |
2925 | | |
2926 | | void cmMakefile::PushLoopBlock() |
2927 | 0 | { |
2928 | 0 | assert(!this->LoopBlockCounter.empty()); |
2929 | 0 | this->LoopBlockCounter.top()++; |
2930 | 0 | } |
2931 | | |
2932 | | void cmMakefile::PopLoopBlock() |
2933 | 0 | { |
2934 | 0 | assert(!this->LoopBlockCounter.empty()); |
2935 | 0 | assert(this->LoopBlockCounter.top() > 0); |
2936 | 0 | this->LoopBlockCounter.top()--; |
2937 | 0 | } |
2938 | | |
2939 | | void cmMakefile::PushLoopBlockBarrier() |
2940 | 1 | { |
2941 | 1 | this->LoopBlockCounter.push(0); |
2942 | 1 | } |
2943 | | |
2944 | | void cmMakefile::PopLoopBlockBarrier() |
2945 | 0 | { |
2946 | 0 | assert(!this->LoopBlockCounter.empty()); |
2947 | 0 | assert(this->LoopBlockCounter.top() == 0); |
2948 | 0 | this->LoopBlockCounter.pop(); |
2949 | 0 | } |
2950 | | |
2951 | | bool cmMakefile::IsLoopBlock() const |
2952 | 0 | { |
2953 | 0 | assert(!this->LoopBlockCounter.empty()); |
2954 | 0 | return !this->LoopBlockCounter.empty() && this->LoopBlockCounter.top() > 0; |
2955 | 0 | } |
2956 | | |
2957 | | bool cmMakefile::ExpandArguments(std::vector<cmListFileArgument> const& inArgs, |
2958 | | std::vector<std::string>& outArgs) const |
2959 | 0 | { |
2960 | 0 | std::string const& filename = this->GetBacktrace().Top().FilePath; |
2961 | 0 | std::string value; |
2962 | 0 | outArgs.reserve(inArgs.size()); |
2963 | 0 | for (cmListFileArgument const& i : inArgs) { |
2964 | | // No expansion in a bracket argument. |
2965 | 0 | if (i.Delim == cmListFileArgument::Bracket) { |
2966 | 0 | outArgs.push_back(i.Value); |
2967 | 0 | continue; |
2968 | 0 | } |
2969 | | // Expand the variables in the argument. |
2970 | 0 | value = i.Value; |
2971 | 0 | this->ExpandVariablesInString(value, false, false, false, filename.c_str(), |
2972 | 0 | i.Line, false, false); |
2973 | | |
2974 | | // If the argument is quoted, it should be one argument. |
2975 | | // Otherwise, it may be a list of arguments. |
2976 | 0 | if (i.Delim == cmListFileArgument::Quoted) { |
2977 | 0 | outArgs.push_back(value); |
2978 | 0 | } else { |
2979 | 0 | cmExpandList(value, outArgs); |
2980 | 0 | } |
2981 | 0 | } |
2982 | 0 | return !cmSystemTools::GetFatalErrorOccurred(); |
2983 | 0 | } |
2984 | | |
2985 | | bool cmMakefile::ExpandArguments( |
2986 | | std::vector<cmListFileArgument> const& inArgs, |
2987 | | std::vector<cmExpandedCommandArgument>& outArgs) const |
2988 | 0 | { |
2989 | 0 | std::string const& filename = this->GetBacktrace().Top().FilePath; |
2990 | 0 | std::string value; |
2991 | 0 | outArgs.reserve(inArgs.size()); |
2992 | 0 | for (cmListFileArgument const& i : inArgs) { |
2993 | | // No expansion in a bracket argument. |
2994 | 0 | if (i.Delim == cmListFileArgument::Bracket) { |
2995 | 0 | outArgs.emplace_back(i.Value, true); |
2996 | 0 | continue; |
2997 | 0 | } |
2998 | | // Expand the variables in the argument. |
2999 | 0 | value = i.Value; |
3000 | 0 | this->ExpandVariablesInString(value, false, false, false, filename.c_str(), |
3001 | 0 | i.Line, false, false); |
3002 | | |
3003 | | // If the argument is quoted, it should be one argument. |
3004 | | // Otherwise, it may be a list of arguments. |
3005 | 0 | if (i.Delim == cmListFileArgument::Quoted) { |
3006 | 0 | outArgs.emplace_back(value, true); |
3007 | 0 | } else { |
3008 | 0 | cmList stringArgs{ value }; |
3009 | 0 | for (std::string const& stringArg : stringArgs) { |
3010 | 0 | outArgs.emplace_back(stringArg, false); |
3011 | 0 | } |
3012 | 0 | } |
3013 | 0 | } |
3014 | 0 | return !cmSystemTools::GetFatalErrorOccurred(); |
3015 | 0 | } |
3016 | | |
3017 | | void cmMakefile::AddFunctionBlocker(std::unique_ptr<cmFunctionBlocker> fb) |
3018 | 0 | { |
3019 | 0 | if (!this->ExecutionStatusStack.empty()) { |
3020 | | // Record the context in which the blocker is created. |
3021 | 0 | fb->SetStartingContext(this->Backtrace.Top()); |
3022 | 0 | } |
3023 | |
|
3024 | 0 | this->FunctionBlockers.push(std::move(fb)); |
3025 | 0 | } |
3026 | | |
3027 | | std::unique_ptr<cmFunctionBlocker> cmMakefile::RemoveFunctionBlocker() |
3028 | 0 | { |
3029 | 0 | assert(!this->FunctionBlockers.empty()); |
3030 | 0 | assert(this->FunctionBlockerBarriers.empty() || |
3031 | 0 | this->FunctionBlockers.size() > this->FunctionBlockerBarriers.back()); |
3032 | |
|
3033 | 0 | auto b = std::move(this->FunctionBlockers.top()); |
3034 | 0 | this->FunctionBlockers.pop(); |
3035 | 0 | return b; |
3036 | 0 | } |
3037 | | |
3038 | | std::string const& cmMakefile::GetHomeDirectory() const |
3039 | 0 | { |
3040 | 0 | return this->GetCMakeInstance()->GetHomeDirectory(); |
3041 | 0 | } |
3042 | | |
3043 | | std::string const& cmMakefile::GetHomeOutputDirectory() const |
3044 | 0 | { |
3045 | 0 | return this->GetCMakeInstance()->GetHomeOutputDirectory(); |
3046 | 0 | } |
3047 | | |
3048 | | void cmMakefile::SetScriptModeFile(std::string const& scriptfile) |
3049 | 1 | { |
3050 | 1 | this->AddDefinition("CMAKE_SCRIPT_MODE_FILE", scriptfile); |
3051 | 1 | } |
3052 | | |
3053 | | void cmMakefile::SetArgcArgv(std::vector<std::string> const& args) |
3054 | 1 | { |
3055 | 1 | this->AddDefinition("CMAKE_ARGC", std::to_string(args.size())); |
3056 | | |
3057 | 4 | for (auto i = 0u; i < args.size(); ++i) { |
3058 | 3 | this->AddDefinition(cmStrCat("CMAKE_ARGV", i), args[i]); |
3059 | 3 | } |
3060 | 1 | } |
3061 | | |
3062 | | cmSourceFile* cmMakefile::GetSource(std::string const& sourceName, |
3063 | | cmSourceFileLocationKind kind) const |
3064 | 0 | { |
3065 | | // First check "Known" paths (avoids the creation of cmSourceFileLocation) |
3066 | 0 | if (kind == cmSourceFileLocationKind::Known) { |
3067 | 0 | auto sfsi = this->KnownFileSearchIndex.find(sourceName); |
3068 | 0 | if (sfsi != this->KnownFileSearchIndex.end()) { |
3069 | 0 | return sfsi->second; |
3070 | 0 | } |
3071 | 0 | } |
3072 | | |
3073 | 0 | cmSourceFileLocation sfl(this, sourceName, kind); |
3074 | 0 | auto name = this->GetCMakeInstance()->StripExtension(sfl.GetName()); |
3075 | | #if defined(_WIN32) || defined(__APPLE__) |
3076 | | name = cmSystemTools::LowerCase(name); |
3077 | | #endif |
3078 | 0 | auto sfsi = this->SourceFileSearchIndex.find(name); |
3079 | 0 | if (sfsi != this->SourceFileSearchIndex.end()) { |
3080 | 0 | for (auto* sf : sfsi->second) { |
3081 | 0 | if (sf->Matches(sfl)) { |
3082 | 0 | return sf; |
3083 | 0 | } |
3084 | 0 | } |
3085 | 0 | } |
3086 | 0 | return nullptr; |
3087 | 0 | } |
3088 | | |
3089 | | cmSourceFile* cmMakefile::CreateSource(std::string const& sourceName, |
3090 | | bool generated, |
3091 | | cmSourceFileLocationKind kind) |
3092 | 0 | { |
3093 | 0 | auto sf = cm::make_unique<cmSourceFile>(this, sourceName, generated, kind); |
3094 | 0 | auto name = |
3095 | 0 | this->GetCMakeInstance()->StripExtension(sf->GetLocation().GetName()); |
3096 | | #if defined(_WIN32) || defined(__APPLE__) |
3097 | | name = cmSystemTools::LowerCase(name); |
3098 | | #endif |
3099 | 0 | this->SourceFileSearchIndex[name].push_back(sf.get()); |
3100 | | // for "Known" paths add direct lookup (used for faster lookup in GetSource) |
3101 | 0 | if (kind == cmSourceFileLocationKind::Known) { |
3102 | 0 | this->KnownFileSearchIndex[sourceName] = sf.get(); |
3103 | 0 | } |
3104 | |
|
3105 | 0 | this->SourceFiles.push_back(std::move(sf)); |
3106 | |
|
3107 | 0 | return this->SourceFiles.back().get(); |
3108 | 0 | } |
3109 | | |
3110 | | cmSourceFile* cmMakefile::GetOrCreateSource(std::string const& sourceName, |
3111 | | bool generated, |
3112 | | cmSourceFileLocationKind kind) |
3113 | 0 | { |
3114 | 0 | if (cmSourceFile* esf = this->GetSource(sourceName, kind)) { |
3115 | 0 | return esf; |
3116 | 0 | } |
3117 | 0 | return this->CreateSource(sourceName, generated, kind); |
3118 | 0 | } |
3119 | | |
3120 | | cmSourceFile* cmMakefile::GetOrCreateGeneratedSource( |
3121 | | std::string const& sourceName) |
3122 | 0 | { |
3123 | 0 | cmSourceFile* sf = |
3124 | 0 | this->GetOrCreateSource(sourceName, true, cmSourceFileLocationKind::Known); |
3125 | 0 | sf->MarkAsGenerated(); // In case we did not create the source file. |
3126 | 0 | return sf; |
3127 | 0 | } |
3128 | | |
3129 | | void cmMakefile::CreateGeneratedOutputs( |
3130 | | std::vector<std::string> const& outputs) |
3131 | 0 | { |
3132 | 0 | for (std::string const& o : outputs) { |
3133 | 0 | if (cmGeneratorExpression::Find(o) == std::string::npos) { |
3134 | 0 | this->GetOrCreateGeneratedSource(o); |
3135 | 0 | } |
3136 | 0 | } |
3137 | 0 | } |
3138 | | |
3139 | | void cmMakefile::AddTargetObject(std::string const& tgtName, |
3140 | | std::string const& objFile) |
3141 | 0 | { |
3142 | 0 | cmSourceFile* sf = |
3143 | 0 | this->GetOrCreateSource(objFile, true, cmSourceFileLocationKind::Known); |
3144 | 0 | sf->SetSpecialSourceType(cmSourceFile::SpecialSourceType::Object); |
3145 | 0 | sf->SetObjectLibrary(tgtName); |
3146 | 0 | sf->SetProperty("EXTERNAL_OBJECT", "1"); |
3147 | | // TODO: Compute a language for this object based on the associated source |
3148 | | // file that compiles to it. Needs a policy as it likely affects link |
3149 | | // language selection if done unconditionally. |
3150 | 0 | #if !defined(CMAKE_BOOTSTRAP) |
3151 | 0 | this->SourceGroups[this->ObjectLibrariesSourceGroupIndex]->AddGroupFile( |
3152 | 0 | sf->ResolveFullPath()); |
3153 | 0 | #endif |
3154 | 0 | } |
3155 | | |
3156 | | void cmMakefile::EnableLanguage(std::vector<std::string> const& languages, |
3157 | | bool optional) |
3158 | 0 | { |
3159 | 0 | if (this->DeferRunning) { |
3160 | 0 | this->IssueMessage( |
3161 | 0 | MessageType::FATAL_ERROR, |
3162 | 0 | "Languages may not be enabled during deferred execution."); |
3163 | 0 | return; |
3164 | 0 | } |
3165 | 0 | if (char const* def = this->GetGlobalGenerator()->GetCMakeCFGIntDir()) { |
3166 | 0 | this->AddDefinition("CMAKE_CFG_INTDIR", def); |
3167 | 0 | } |
3168 | |
|
3169 | 0 | std::vector<std::string> unique_languages; |
3170 | 0 | { |
3171 | 0 | std::vector<std::string> duplicate_languages; |
3172 | 0 | for (std::string const& language : languages) { |
3173 | 0 | if (!cm::contains(unique_languages, language)) { |
3174 | 0 | unique_languages.push_back(language); |
3175 | 0 | } else if (!cm::contains(duplicate_languages, language)) { |
3176 | 0 | duplicate_languages.push_back(language); |
3177 | 0 | } |
3178 | 0 | } |
3179 | 0 | if (!duplicate_languages.empty()) { |
3180 | 0 | auto quantity = duplicate_languages.size() == 1 ? " has"_s : "s have"_s; |
3181 | 0 | this->IssueMessage( |
3182 | 0 | MessageType::AUTHOR_WARNING, |
3183 | 0 | cmStrCat("Languages to be enabled may not be specified more " |
3184 | 0 | "than once at the same time. The following language", |
3185 | 0 | quantity, " been specified multiple times: ", |
3186 | 0 | cmJoin(duplicate_languages, ", "))); |
3187 | 0 | } |
3188 | 0 | } |
3189 | | |
3190 | | // If RC is explicitly listed we need to do it after other languages. |
3191 | | // On some platforms we enable RC implicitly while enabling others. |
3192 | | // Do not let that look like recursive enable_language(RC). |
3193 | 0 | std::vector<std::string> languages_without_RC; |
3194 | 0 | std::vector<std::string> languages_for_RC; |
3195 | 0 | languages_without_RC.reserve(unique_languages.size()); |
3196 | 0 | for (std::string const& language : unique_languages) { |
3197 | 0 | if (language == "RC"_s) { |
3198 | 0 | languages_for_RC.push_back(language); |
3199 | 0 | } else { |
3200 | 0 | languages_without_RC.push_back(language); |
3201 | 0 | } |
3202 | 0 | } |
3203 | 0 | if (!languages_without_RC.empty()) { |
3204 | 0 | this->GetGlobalGenerator()->EnableLanguage(languages_without_RC, this, |
3205 | 0 | optional); |
3206 | 0 | } |
3207 | 0 | if (!languages_for_RC.empty()) { |
3208 | 0 | this->GetGlobalGenerator()->EnableLanguage(languages_for_RC, this, |
3209 | 0 | optional); |
3210 | 0 | } |
3211 | 0 | } |
3212 | | |
3213 | | int cmMakefile::TryCompile(std::string const& srcdir, |
3214 | | std::string const& bindir, |
3215 | | std::string const& projectName, |
3216 | | std::string const& targetName, bool fast, int jobs, |
3217 | | std::vector<std::string> const* cmakeArgs, |
3218 | | std::string& output) |
3219 | 0 | { |
3220 | 0 | this->IsSourceFileTryCompile = fast; |
3221 | | // does the binary directory exist ? If not create it... |
3222 | 0 | if (!cmSystemTools::FileIsDirectory(bindir)) { |
3223 | 0 | cmSystemTools::MakeDirectory(bindir); |
3224 | 0 | } |
3225 | | |
3226 | | // change to the tests directory and run cmake |
3227 | | // use the cmake object instead of calling cmake |
3228 | 0 | cmWorkingDirectory workdir(bindir); |
3229 | 0 | if (workdir.Failed()) { |
3230 | 0 | this->IssueMessage(MessageType::FATAL_ERROR, workdir.GetError()); |
3231 | 0 | cmSystemTools::SetFatalErrorOccurred(); |
3232 | 0 | this->IsSourceFileTryCompile = false; |
3233 | 0 | return 1; |
3234 | 0 | } |
3235 | | |
3236 | | // unset the NINJA_STATUS environment variable while running try compile. |
3237 | | // since we parse the output, we need to ensure there aren't any unexpected |
3238 | | // characters that will cause issues, such as ANSI color escape codes. |
3239 | 0 | cm::optional<cmSystemTools::ScopedEnv> maybeNinjaStatus; |
3240 | 0 | if (this->GetGlobalGenerator()->IsNinja()) { |
3241 | 0 | maybeNinjaStatus.emplace("NINJA_STATUS="); |
3242 | 0 | } |
3243 | | |
3244 | | // make sure the same generator is used |
3245 | | // use this program as the cmake to be run, it should not |
3246 | | // be run that way but the cmake object requires a valid path |
3247 | 0 | cmake cm(cmState::Role::Project, cmState::TryCompile::Yes); |
3248 | 0 | auto gg = cm.CreateGlobalGenerator(this->GetGlobalGenerator()->GetName()); |
3249 | 0 | if (!gg) { |
3250 | 0 | this->IssueMessage(MessageType::INTERNAL_ERROR, |
3251 | 0 | "Global generator '" + |
3252 | 0 | this->GetGlobalGenerator()->GetName() + |
3253 | 0 | "' could not be created."); |
3254 | 0 | cmSystemTools::SetFatalErrorOccurred(); |
3255 | 0 | this->IsSourceFileTryCompile = false; |
3256 | 0 | return 1; |
3257 | 0 | } |
3258 | 0 | gg->RecursionDepth = this->RecursionDepth; |
3259 | 0 | cm.SetGlobalGenerator(std::move(gg)); |
3260 | | |
3261 | | // copy trace state |
3262 | 0 | cm.SetTraceRedirect(this->GetCMakeInstance()); |
3263 | | |
3264 | | // do a configure |
3265 | 0 | cm.SetHomeDirectory(srcdir); |
3266 | 0 | cm.SetHomeOutputDirectory(bindir); |
3267 | 0 | cm.SetGeneratorInstance(this->GetSafeDefinition("CMAKE_GENERATOR_INSTANCE")); |
3268 | 0 | cm.SetGeneratorPlatform(this->GetSafeDefinition("CMAKE_GENERATOR_PLATFORM")); |
3269 | 0 | cm.SetGeneratorToolset(this->GetSafeDefinition("CMAKE_GENERATOR_TOOLSET")); |
3270 | 0 | cm.LoadCache(); |
3271 | 0 | if (!cm.GetGlobalGenerator()->IsMultiConfig()) { |
3272 | 0 | if (cmValue config = |
3273 | 0 | this->GetDefinition("CMAKE_TRY_COMPILE_CONFIGURATION")) { |
3274 | | // Tell the single-configuration generator which one to use. |
3275 | | // Add this before the user-provided CMake arguments in case |
3276 | | // one of the arguments is -DCMAKE_BUILD_TYPE=... |
3277 | 0 | cm.AddCacheEntry("CMAKE_BUILD_TYPE", config, "Build configuration", |
3278 | 0 | cmStateEnums::STRING); |
3279 | 0 | } |
3280 | 0 | } |
3281 | 0 | cmValue recursionDepth = |
3282 | 0 | this->GetDefinition("CMAKE_MAXIMUM_RECURSION_DEPTH"); |
3283 | 0 | if (recursionDepth) { |
3284 | 0 | cm.AddCacheEntry("CMAKE_MAXIMUM_RECURSION_DEPTH", recursionDepth, |
3285 | 0 | "Maximum recursion depth", cmStateEnums::STRING); |
3286 | 0 | } |
3287 | | // if cmake args were provided then pass them in |
3288 | 0 | if (cmakeArgs) { |
3289 | | // FIXME: Workaround to ignore unused CLI variables in try-compile. |
3290 | | // |
3291 | | // Ideally we should use SetArgs for options like --no-warn-unused-cli. |
3292 | | // However, there is a subtle problem when certain arguments are passed to |
3293 | | // a macro wrapping around try_compile or try_run that does not escape |
3294 | | // semicolons in its parameters but just passes ${ARGV} or ${ARGN}. In |
3295 | | // this case a list argument like "-DVAR=a;b" gets split into multiple |
3296 | | // cmake arguments "-DVAR=a" and "b". Currently SetCacheArgs ignores |
3297 | | // argument "b" and uses just "-DVAR=a", leading to a subtle bug in that |
3298 | | // the try_compile or try_run does not get the proper value of VAR. If we |
3299 | | // call SetArgs here then it would treat "b" as the source directory and |
3300 | | // cause an error such as "The source directory .../CMakeFiles/CMakeTmp/b |
3301 | | // does not exist", thus breaking the try_compile or try_run completely. |
3302 | | // |
3303 | | // Strictly speaking the bug is in the wrapper macro because the CMake |
3304 | | // language has always flattened nested lists and the macro should escape |
3305 | | // the semicolons in its arguments before forwarding them. However, this |
3306 | | // bug is so subtle that projects typically work anyway, usually because |
3307 | | // the value VAR=a is sufficient for the try_compile or try_run to get the |
3308 | | // correct result. Calling SetArgs here would break such projects that |
3309 | | // previously built. Instead we work around the issue by never reporting |
3310 | | // unused arguments and ignoring options such as --no-warn-unused-cli. |
3311 | 0 | cm.SetWarnUnusedCli(false); |
3312 | | // cm.SetArgs(*cmakeArgs, true); |
3313 | |
|
3314 | 0 | cm.SetCacheArgs(*cmakeArgs); |
3315 | 0 | } |
3316 | | // to save time we pass the EnableLanguage info directly |
3317 | 0 | cm.GetGlobalGenerator()->EnableLanguagesFromGenerator( |
3318 | 0 | this->GetGlobalGenerator(), this); |
3319 | 0 | if (this->IsOn("CMAKE_SUPPRESS_DEVELOPER_WARNINGS")) { |
3320 | 0 | cm.AddCacheEntry("CMAKE_SUPPRESS_DEVELOPER_WARNINGS", "TRUE", "", |
3321 | 0 | cmStateEnums::INTERNAL); |
3322 | 0 | } else { |
3323 | 0 | cm.AddCacheEntry("CMAKE_SUPPRESS_DEVELOPER_WARNINGS", "FALSE", "", |
3324 | 0 | cmStateEnums::INTERNAL); |
3325 | 0 | } |
3326 | 0 | if (cm.Configure() != 0) { |
3327 | 0 | this->IssueMessage(MessageType::FATAL_ERROR, |
3328 | 0 | "Failed to configure test project build system."); |
3329 | 0 | cmSystemTools::SetFatalErrorOccurred(); |
3330 | 0 | this->IsSourceFileTryCompile = false; |
3331 | 0 | return 1; |
3332 | 0 | } |
3333 | | |
3334 | 0 | if (cm.Generate() != 0) { |
3335 | 0 | this->IssueMessage(MessageType::FATAL_ERROR, |
3336 | 0 | "Failed to generate test project build system."); |
3337 | 0 | cmSystemTools::SetFatalErrorOccurred(); |
3338 | 0 | this->IsSourceFileTryCompile = false; |
3339 | 0 | return 1; |
3340 | 0 | } |
3341 | | |
3342 | | // finally call the generator to actually build the resulting project |
3343 | 0 | int ret = this->GetGlobalGenerator()->TryCompile( |
3344 | 0 | jobs, bindir, projectName, targetName, fast, output, this); |
3345 | |
|
3346 | 0 | this->IsSourceFileTryCompile = false; |
3347 | 0 | return ret; |
3348 | 0 | } |
3349 | | |
3350 | | bool cmMakefile::GetIsSourceFileTryCompile() const |
3351 | 0 | { |
3352 | 0 | return this->IsSourceFileTryCompile; |
3353 | 0 | } |
3354 | | |
3355 | | cmake* cmMakefile::GetCMakeInstance() const |
3356 | 32 | { |
3357 | 32 | return this->GlobalGenerator->GetCMakeInstance(); |
3358 | 32 | } |
3359 | | |
3360 | | cmMessenger* cmMakefile::GetMessenger() const |
3361 | 1 | { |
3362 | 1 | return this->GetCMakeInstance()->GetMessenger(); |
3363 | 1 | } |
3364 | | |
3365 | | cmGlobalGenerator* cmMakefile::GetGlobalGenerator() const |
3366 | 0 | { |
3367 | 0 | return this->GlobalGenerator; |
3368 | 0 | } |
3369 | | |
3370 | | #ifndef CMAKE_BOOTSTRAP |
3371 | | cmVariableWatch* cmMakefile::GetVariableWatch() const |
3372 | 12 | { |
3373 | 12 | if (this->GetCMakeInstance()) { |
3374 | 12 | return this->GetCMakeInstance()->GetVariableWatch(); |
3375 | 12 | } |
3376 | 0 | return nullptr; |
3377 | 12 | } |
3378 | | #endif |
3379 | | |
3380 | | cmState* cmMakefile::GetState() const |
3381 | 4 | { |
3382 | 4 | return this->GetCMakeInstance()->GetState(); |
3383 | 4 | } |
3384 | | |
3385 | | void cmMakefile::DisplayStatus(std::string const& message, float s) const |
3386 | 0 | { |
3387 | 0 | cmake* cm = this->GetCMakeInstance(); |
3388 | 0 | if (cm->GetState()->GetRole() == cmState::Role::FindPackage) { |
3389 | | // don't output any STATUS message in --find-package mode, since they will |
3390 | | // directly be fed to the compiler, which will be confused. |
3391 | 0 | return; |
3392 | 0 | } |
3393 | 0 | cm->UpdateProgress(message, s); |
3394 | |
|
3395 | 0 | #ifdef CMake_ENABLE_DEBUGGER |
3396 | 0 | if (cm->GetDebugAdapter()) { |
3397 | 0 | cm->GetDebugAdapter()->OnMessageOutput(MessageType::MESSAGE, message); |
3398 | 0 | } |
3399 | 0 | #endif |
3400 | 0 | } |
3401 | | |
3402 | | std::string cmMakefile::GetModulesFile(cm::string_view filename, bool& system, |
3403 | | bool debug, |
3404 | | std::string& debugBuffer) const |
3405 | 0 | { |
3406 | 0 | std::string result; |
3407 | |
|
3408 | 0 | std::string moduleInCMakeRoot; |
3409 | 0 | std::string moduleInCMakeModulePath; |
3410 | | |
3411 | | // Always search in CMAKE_MODULE_PATH: |
3412 | 0 | cmValue cmakeModulePath = this->GetDefinition("CMAKE_MODULE_PATH"); |
3413 | 0 | if (cmakeModulePath) { |
3414 | 0 | cmList modulePath{ *cmakeModulePath }; |
3415 | | |
3416 | | // Look through the possible module directories. |
3417 | 0 | for (std::string itempl : modulePath) { |
3418 | 0 | cmSystemTools::ConvertToUnixSlashes(itempl); |
3419 | 0 | itempl += "/"; |
3420 | 0 | itempl += filename; |
3421 | 0 | if (cmSystemTools::FileExists(itempl)) { |
3422 | 0 | moduleInCMakeModulePath = itempl; |
3423 | 0 | break; |
3424 | 0 | } |
3425 | 0 | if (debug) { |
3426 | 0 | debugBuffer = cmStrCat(debugBuffer, " ", itempl, '\n'); |
3427 | 0 | } |
3428 | 0 | } |
3429 | 0 | } |
3430 | | |
3431 | | // Always search in the standard modules location. |
3432 | 0 | moduleInCMakeRoot = |
3433 | 0 | cmStrCat(cmSystemTools::GetCMakeRoot(), "/Modules/", filename); |
3434 | 0 | cmSystemTools::ConvertToUnixSlashes(moduleInCMakeRoot); |
3435 | 0 | if (!cmSystemTools::FileExists(moduleInCMakeRoot)) { |
3436 | 0 | if (debug) { |
3437 | 0 | debugBuffer = cmStrCat(debugBuffer, " ", moduleInCMakeRoot, '\n'); |
3438 | 0 | } |
3439 | 0 | moduleInCMakeRoot.clear(); |
3440 | 0 | } |
3441 | | |
3442 | | // Normally, prefer the files found in CMAKE_MODULE_PATH. Only when the file |
3443 | | // from which we are being called is located itself in CMAKE_ROOT, then |
3444 | | // prefer results from CMAKE_ROOT depending on the policy setting. |
3445 | 0 | if (!moduleInCMakeModulePath.empty() && !moduleInCMakeRoot.empty()) { |
3446 | 0 | cmValue currentFile = this->GetDefinition(kCMAKE_CURRENT_LIST_FILE); |
3447 | 0 | std::string mods = cmStrCat(cmSystemTools::GetCMakeRoot(), "/Modules/"); |
3448 | 0 | if (currentFile && cmSystemTools::IsSubDirectory(*currentFile, mods)) { |
3449 | 0 | system = true; |
3450 | 0 | result = moduleInCMakeRoot; |
3451 | 0 | } else { |
3452 | 0 | system = false; |
3453 | 0 | result = moduleInCMakeModulePath; |
3454 | 0 | } |
3455 | 0 | } else if (!moduleInCMakeModulePath.empty()) { |
3456 | 0 | system = false; |
3457 | 0 | result = moduleInCMakeModulePath; |
3458 | 0 | } else { |
3459 | 0 | system = true; |
3460 | 0 | result = moduleInCMakeRoot; |
3461 | 0 | } |
3462 | |
|
3463 | 0 | return result; |
3464 | 0 | } |
3465 | | |
3466 | | void cmMakefile::ConfigureString(std::string const& input, std::string& output, |
3467 | | bool atOnly, bool escapeQuotes) const |
3468 | 0 | { |
3469 | | // Split input to handle one line at a time. |
3470 | 0 | std::string::const_iterator lineStart = input.begin(); |
3471 | 0 | while (lineStart != input.end()) { |
3472 | | // Find the end of this line. |
3473 | 0 | std::string::const_iterator lineEnd = lineStart; |
3474 | 0 | while (lineEnd != input.end() && *lineEnd != '\n') { |
3475 | 0 | ++lineEnd; |
3476 | 0 | } |
3477 | | |
3478 | | // Copy the line. |
3479 | 0 | std::string line(lineStart, lineEnd); |
3480 | | |
3481 | | // Skip the newline character. |
3482 | 0 | bool haveNewline = (lineEnd != input.end()); |
3483 | 0 | if (haveNewline) { |
3484 | 0 | ++lineEnd; |
3485 | 0 | } |
3486 | | |
3487 | | // Replace #cmakedefine instances. |
3488 | 0 | if (this->cmDefineRegex.find(line)) { |
3489 | 0 | cmValue def = this->GetDefinition(this->cmDefineRegex.match(2)); |
3490 | 0 | if (!def.IsOff()) { |
3491 | 0 | std::string const indentation = this->cmDefineRegex.match(1); |
3492 | 0 | cmSystemTools::ReplaceString(line, |
3493 | 0 | cmStrCat('#', indentation, "cmakedefine"), |
3494 | 0 | cmStrCat('#', indentation, "define")); |
3495 | 0 | output += line; |
3496 | 0 | } else { |
3497 | 0 | output += "/* #undef "; |
3498 | 0 | output += this->cmDefineRegex.match(2); |
3499 | 0 | output += " */"; |
3500 | 0 | } |
3501 | 0 | } else if (this->cmDefine01Regex.find(line)) { |
3502 | 0 | std::string const indentation = this->cmDefine01Regex.match(1); |
3503 | 0 | cmValue def = this->GetDefinition(this->cmDefine01Regex.match(2)); |
3504 | 0 | cmSystemTools::ReplaceString(line, |
3505 | 0 | cmStrCat('#', indentation, "cmakedefine01"), |
3506 | 0 | cmStrCat('#', indentation, "define")); |
3507 | 0 | output += line; |
3508 | 0 | if (!def.IsOff()) { |
3509 | 0 | output += " 1"; |
3510 | 0 | } else { |
3511 | 0 | output += " 0"; |
3512 | 0 | } |
3513 | 0 | } else { |
3514 | 0 | output += line; |
3515 | 0 | } |
3516 | |
|
3517 | 0 | if (haveNewline) { |
3518 | 0 | output += '\n'; |
3519 | 0 | } |
3520 | | |
3521 | | // Move to the next line. |
3522 | 0 | lineStart = lineEnd; |
3523 | 0 | } |
3524 | | |
3525 | | // Perform variable replacements. |
3526 | 0 | char const* filename = nullptr; |
3527 | 0 | long lineNumber = -1; |
3528 | 0 | if (!this->Backtrace.Empty()) { |
3529 | 0 | auto const& currentTrace = this->Backtrace.Top(); |
3530 | 0 | filename = currentTrace.FilePath.c_str(); |
3531 | 0 | lineNumber = currentTrace.Line; |
3532 | 0 | } |
3533 | 0 | this->ExpandVariablesInString(output, escapeQuotes, true, atOnly, filename, |
3534 | 0 | lineNumber, true, true); |
3535 | 0 | } |
3536 | | |
3537 | | int cmMakefile::ConfigureFile(std::string const& infile, |
3538 | | std::string const& outfile, bool copyonly, |
3539 | | bool atOnly, bool escapeQuotes, |
3540 | | mode_t permissions, cmNewLineStyle newLine) |
3541 | 0 | { |
3542 | 0 | int res = 1; |
3543 | 0 | if (!this->CanIWriteThisFile(outfile)) { |
3544 | 0 | cmSystemTools::Error(cmStrCat("Attempt to write file: ", outfile, |
3545 | 0 | " into a source directory.")); |
3546 | 0 | return 0; |
3547 | 0 | } |
3548 | 0 | if (!cmSystemTools::FileExists(infile)) { |
3549 | 0 | cmSystemTools::Error(cmStrCat("File ", infile, " does not exist.")); |
3550 | 0 | return 0; |
3551 | 0 | } |
3552 | 0 | std::string soutfile = outfile; |
3553 | 0 | std::string const& sinfile = infile; |
3554 | 0 | this->AddCMakeDependFile(sinfile); |
3555 | 0 | cmSystemTools::ConvertToUnixSlashes(soutfile); |
3556 | | |
3557 | | // Re-generate if non-temporary outputs are missing. |
3558 | | // when we finalize the configuration we will remove all |
3559 | | // output files that now don't exist. |
3560 | 0 | this->AddCMakeOutputFile(soutfile); |
3561 | |
|
3562 | 0 | if (permissions == 0) { |
3563 | 0 | cmSystemTools::GetPermissions(sinfile, permissions); |
3564 | 0 | } |
3565 | |
|
3566 | 0 | std::string::size_type pos = soutfile.rfind('/'); |
3567 | 0 | if (pos != std::string::npos) { |
3568 | 0 | std::string path = soutfile.substr(0, pos); |
3569 | 0 | cmSystemTools::MakeDirectory(path); |
3570 | 0 | } |
3571 | |
|
3572 | 0 | if (copyonly) { |
3573 | 0 | auto const copy_status = |
3574 | 0 | cmSystemTools::CopyFileIfDifferent(sinfile, soutfile); |
3575 | 0 | if (!copy_status) { |
3576 | 0 | this->IssueMessage( |
3577 | 0 | MessageType::FATAL_ERROR, |
3578 | 0 | cmStrCat("Fail to copy ", |
3579 | 0 | copy_status.Path == cmsys::SystemTools::CopyStatus::SourcePath |
3580 | 0 | ? "source" |
3581 | 0 | : "destination", |
3582 | 0 | "file: ", copy_status.GetString())); |
3583 | 0 | res = 0; |
3584 | 0 | } else { |
3585 | 0 | auto const status = cmSystemTools::SetPermissions(soutfile, permissions); |
3586 | 0 | if (!status) { |
3587 | 0 | this->IssueMessage(MessageType::FATAL_ERROR, status.GetString()); |
3588 | 0 | res = 0; |
3589 | 0 | } |
3590 | 0 | } |
3591 | 0 | return res; |
3592 | 0 | } |
3593 | | |
3594 | 0 | std::string newLineCharacters; |
3595 | 0 | std::ios::openmode omode = std::ios::out | std::ios::trunc; |
3596 | 0 | if (newLine.IsValid()) { |
3597 | 0 | newLineCharacters = newLine.GetCharacters(); |
3598 | 0 | omode |= std::ios::binary; |
3599 | 0 | } else { |
3600 | 0 | newLineCharacters = "\n"; |
3601 | 0 | } |
3602 | 0 | std::string tempOutputFile = cmStrCat(soutfile, ".tmp"); |
3603 | 0 | cmsys::ofstream fout(tempOutputFile.c_str(), omode); |
3604 | 0 | if (!fout) { |
3605 | 0 | cmSystemTools::Error("Could not open file for write in copy operation " + |
3606 | 0 | tempOutputFile); |
3607 | 0 | cmSystemTools::ReportLastSystemError(""); |
3608 | 0 | return 0; |
3609 | 0 | } |
3610 | 0 | cmsys::ifstream fin(sinfile.c_str()); |
3611 | 0 | if (!fin) { |
3612 | 0 | cmSystemTools::Error("Could not open file for read in copy operation " + |
3613 | 0 | sinfile); |
3614 | 0 | return 0; |
3615 | 0 | } |
3616 | | |
3617 | 0 | cmsys::FStream::BOM bom = cmsys::FStream::ReadBOM(fin); |
3618 | 0 | if (bom != cmsys::FStream::BOM_None && bom != cmsys::FStream::BOM_UTF8) { |
3619 | 0 | this->IssueMessage( |
3620 | 0 | MessageType::FATAL_ERROR, |
3621 | 0 | cmStrCat("File starts with a Byte-Order-Mark that is not UTF-8:\n ", |
3622 | 0 | sinfile)); |
3623 | 0 | return 0; |
3624 | 0 | } |
3625 | | // rewind to copy BOM to output file |
3626 | 0 | fin.seekg(0); |
3627 | | |
3628 | | // now copy input to output and expand variables in the |
3629 | | // input file at the same time |
3630 | 0 | std::string inLine; |
3631 | 0 | std::string outLine; |
3632 | 0 | while (cmSystemTools::GetLineFromStream(fin, inLine)) { |
3633 | 0 | outLine.clear(); |
3634 | 0 | this->ConfigureString(inLine, outLine, atOnly, escapeQuotes); |
3635 | 0 | fout << outLine << newLineCharacters; |
3636 | 0 | } |
3637 | | // close the files before attempting to copy |
3638 | 0 | fin.close(); |
3639 | 0 | fout.close(); |
3640 | |
|
3641 | 0 | auto status = cmSystemTools::MoveFileIfDifferent(tempOutputFile, soutfile); |
3642 | 0 | if (!status) { |
3643 | 0 | this->IssueMessage(MessageType::FATAL_ERROR, status.GetString()); |
3644 | 0 | res = 0; |
3645 | 0 | } else { |
3646 | 0 | status = cmSystemTools::SetPermissions(soutfile, permissions); |
3647 | 0 | if (!status) { |
3648 | 0 | this->IssueMessage(MessageType::FATAL_ERROR, status.GetString()); |
3649 | 0 | res = 0; |
3650 | 0 | } |
3651 | 0 | } |
3652 | |
|
3653 | 0 | return res; |
3654 | 0 | } |
3655 | | |
3656 | | void cmMakefile::SetProperty(std::string const& prop, cmValue value) |
3657 | 0 | { |
3658 | 0 | this->StateSnapshot.GetDirectory().SetProperty(prop, value, this->Backtrace); |
3659 | 0 | } |
3660 | | |
3661 | | void cmMakefile::AppendProperty(std::string const& prop, |
3662 | | std::string const& value, bool asString) |
3663 | 0 | { |
3664 | 0 | this->StateSnapshot.GetDirectory().AppendProperty(prop, value, asString, |
3665 | 0 | this->Backtrace); |
3666 | 0 | } |
3667 | | |
3668 | | cmValue cmMakefile::GetProperty(std::string const& prop) const |
3669 | 0 | { |
3670 | | // Check for computed properties. |
3671 | 0 | static std::string output; |
3672 | 0 | if (prop == "TESTS"_s) { |
3673 | 0 | std::vector<std::string> keys; |
3674 | | // get list of keys |
3675 | 0 | auto const* t = this; |
3676 | 0 | std::transform( |
3677 | 0 | t->Tests.begin(), t->Tests.end(), std::back_inserter(keys), |
3678 | 0 | [](decltype(t->Tests)::value_type const& pair) { return pair.first; }); |
3679 | 0 | output = cmList::to_string(keys); |
3680 | 0 | return cmValue(output); |
3681 | 0 | } |
3682 | | |
3683 | 0 | return this->StateSnapshot.GetDirectory().GetProperty(prop); |
3684 | 0 | } |
3685 | | |
3686 | | cmValue cmMakefile::GetProperty(std::string const& prop, bool chain) const |
3687 | 0 | { |
3688 | 0 | return this->StateSnapshot.GetDirectory().GetProperty(prop, chain); |
3689 | 0 | } |
3690 | | |
3691 | | bool cmMakefile::GetPropertyAsBool(std::string const& prop) const |
3692 | 0 | { |
3693 | 0 | return this->GetProperty(prop).IsOn(); |
3694 | 0 | } |
3695 | | |
3696 | | std::vector<std::string> cmMakefile::GetPropertyKeys() const |
3697 | 0 | { |
3698 | 0 | return this->StateSnapshot.GetDirectory().GetPropertyKeys(); |
3699 | 0 | } |
3700 | | |
3701 | | cmTarget* cmMakefile::FindLocalNonAliasTarget(std::string const& name) const |
3702 | 0 | { |
3703 | 0 | auto i = this->Targets.find(name); |
3704 | 0 | if (i != this->Targets.end()) { |
3705 | 0 | return &i->second; |
3706 | 0 | } |
3707 | 0 | return nullptr; |
3708 | 0 | } |
3709 | | |
3710 | | cmTest* cmMakefile::CreateTest(std::string const& testName) |
3711 | 0 | { |
3712 | 0 | cmTest* test = this->GetTest(testName); |
3713 | 0 | if (test) { |
3714 | 0 | return test; |
3715 | 0 | } |
3716 | 0 | auto newTest = cm::make_unique<cmTest>(this); |
3717 | 0 | test = newTest.get(); |
3718 | 0 | newTest->SetName(testName); |
3719 | 0 | this->Tests[testName] = std::move(newTest); |
3720 | 0 | return test; |
3721 | 0 | } |
3722 | | |
3723 | | cmTest* cmMakefile::GetTest(std::string const& testName) const |
3724 | 0 | { |
3725 | 0 | auto mi = this->Tests.find(testName); |
3726 | 0 | if (mi != this->Tests.end()) { |
3727 | 0 | return mi->second.get(); |
3728 | 0 | } |
3729 | 0 | return nullptr; |
3730 | 0 | } |
3731 | | |
3732 | | void cmMakefile::GetTests(std::string const& config, |
3733 | | std::vector<cmTest*>& tests) const |
3734 | 0 | { |
3735 | 0 | for (auto const& generator : this->GetTestGenerators()) { |
3736 | 0 | if (generator->TestsForConfig(config)) { |
3737 | 0 | tests.push_back(generator->GetTest()); |
3738 | 0 | } |
3739 | 0 | } |
3740 | 0 | } |
3741 | | |
3742 | | void cmMakefile::AddCMakeDependFilesFromUser() |
3743 | 0 | { |
3744 | 0 | cmList deps; |
3745 | 0 | if (cmValue deps_str = this->GetProperty("CMAKE_CONFIGURE_DEPENDS")) { |
3746 | 0 | deps.assign(*deps_str); |
3747 | 0 | } |
3748 | 0 | for (auto const& dep : deps) { |
3749 | 0 | if (cmSystemTools::FileIsFullPath(dep)) { |
3750 | 0 | this->AddCMakeDependFile(dep); |
3751 | 0 | } else { |
3752 | 0 | std::string f = cmStrCat(this->GetCurrentSourceDirectory(), '/', dep); |
3753 | 0 | this->AddCMakeDependFile(f); |
3754 | 0 | } |
3755 | 0 | } |
3756 | 0 | } |
3757 | | |
3758 | | std::string cmMakefile::FormatListFileStack() const |
3759 | 0 | { |
3760 | 0 | std::vector<std::string> listFiles; |
3761 | 0 | for (auto snp = this->StateSnapshot; snp.IsValid(); |
3762 | 0 | snp = snp.GetCallStackParent()) { |
3763 | 0 | listFiles.emplace_back(snp.GetExecutionListFile()); |
3764 | 0 | } |
3765 | |
|
3766 | 0 | if (listFiles.empty()) { |
3767 | 0 | return {}; |
3768 | 0 | } |
3769 | | |
3770 | 0 | auto depth = 1; |
3771 | 0 | std::transform(listFiles.begin(), listFiles.end(), listFiles.begin(), |
3772 | 0 | [&depth](std::string const& file) { |
3773 | 0 | return cmStrCat('[', depth++, "]\t", file); |
3774 | 0 | }); |
3775 | |
|
3776 | 0 | return cmJoinStrings(cmMakeRange(listFiles.rbegin(), listFiles.rend()), |
3777 | 0 | "\n "_s, {}); |
3778 | 0 | } |
3779 | | |
3780 | | void cmMakefile::PushScope() |
3781 | 0 | { |
3782 | 0 | this->StateSnapshot = |
3783 | 0 | this->GetState()->CreateVariableScopeSnapshot(this->StateSnapshot); |
3784 | 0 | this->PushLoopBlockBarrier(); |
3785 | |
|
3786 | 0 | #if !defined(CMAKE_BOOTSTRAP) |
3787 | 0 | this->GetGlobalGenerator()->GetFileLockPool().PushFunctionScope(); |
3788 | 0 | #endif |
3789 | 0 | } |
3790 | | |
3791 | | void cmMakefile::PopScope() |
3792 | 0 | { |
3793 | 0 | #if !defined(CMAKE_BOOTSTRAP) |
3794 | 0 | this->GetGlobalGenerator()->GetFileLockPool().PopFunctionScope(); |
3795 | 0 | #endif |
3796 | |
|
3797 | 0 | this->PopLoopBlockBarrier(); |
3798 | |
|
3799 | 0 | this->PopSnapshot(); |
3800 | 0 | } |
3801 | | |
3802 | | void cmMakefile::RaiseScope(std::string const& var, char const* varDef) |
3803 | 0 | { |
3804 | 0 | if (var.empty()) { |
3805 | 0 | return; |
3806 | 0 | } |
3807 | | |
3808 | 0 | if (!this->StateSnapshot.RaiseScope(var, varDef)) { |
3809 | 0 | this->IssueMessage( |
3810 | 0 | MessageType::AUTHOR_WARNING, |
3811 | 0 | cmStrCat("Cannot set \"", var, "\": current scope has no parent.")); |
3812 | 0 | return; |
3813 | 0 | } |
3814 | | |
3815 | 0 | #ifndef CMAKE_BOOTSTRAP |
3816 | 0 | cmVariableWatch* vv = this->GetVariableWatch(); |
3817 | 0 | if (vv) { |
3818 | 0 | vv->VariableAccessed(var, cmVariableWatch::VARIABLE_MODIFIED_ACCESS, |
3819 | 0 | varDef, this); |
3820 | 0 | } |
3821 | 0 | #endif |
3822 | 0 | } |
3823 | | |
3824 | | void cmMakefile::RaiseScope(std::vector<std::string> const& variables) |
3825 | 0 | { |
3826 | 0 | for (auto const& varName : variables) { |
3827 | 0 | if (this->IsNormalDefinitionSet(varName)) { |
3828 | 0 | this->RaiseScope(varName, this->GetDefinition(varName)); |
3829 | 0 | } else { |
3830 | | // unset variable in parent scope |
3831 | 0 | this->RaiseScope(varName, nullptr); |
3832 | 0 | } |
3833 | 0 | } |
3834 | 0 | } |
3835 | | |
3836 | | cmTarget* cmMakefile::AddImportedTarget(std::string const& name, |
3837 | | cmStateEnums::TargetType type, |
3838 | | bool global) |
3839 | 0 | { |
3840 | | // Create the target. |
3841 | 0 | std::unique_ptr<cmTarget> target( |
3842 | 0 | new cmTarget(name, type, |
3843 | 0 | global ? cmTarget::Visibility::ImportedGlobally |
3844 | 0 | : cmTarget::Visibility::Imported, |
3845 | 0 | this, cmTarget::PerConfig::Yes)); |
3846 | | |
3847 | | // Add to the set of available imported targets. |
3848 | 0 | this->ImportedTargets[name] = target.get(); |
3849 | 0 | this->GetGlobalGenerator()->IndexTarget(target.get()); |
3850 | 0 | this->GetStateSnapshot().GetDirectory().AddImportedTargetName(name); |
3851 | | |
3852 | | // Transfer ownership to this cmMakefile object. |
3853 | 0 | this->ImportedTargetsOwned.push_back(std::move(target)); |
3854 | 0 | return this->ImportedTargetsOwned.back().get(); |
3855 | 0 | } |
3856 | | |
3857 | | cmTarget* cmMakefile::AddForeignTarget(std::string const& origin, |
3858 | | std::string const& name) |
3859 | 0 | { |
3860 | 0 | auto foreign_name = cmStrCat("@foreign_", origin, "::", name); |
3861 | 0 | std::unique_ptr<cmTarget> target(new cmTarget( |
3862 | 0 | foreign_name, cmStateEnums::TargetType::INTERFACE_LIBRARY, |
3863 | 0 | cmTarget::Visibility::Foreign, this, cmTarget::PerConfig::Yes)); |
3864 | |
|
3865 | 0 | this->ImportedTargets[foreign_name] = target.get(); |
3866 | 0 | this->GetGlobalGenerator()->IndexTarget(target.get()); |
3867 | 0 | this->GetStateSnapshot().GetDirectory().AddImportedTargetName(foreign_name); |
3868 | |
|
3869 | 0 | this->ImportedTargetsOwned.push_back(std::move(target)); |
3870 | 0 | return this->ImportedTargetsOwned.back().get(); |
3871 | 0 | } |
3872 | | |
3873 | | cmTarget* cmMakefile::FindTargetToUse( |
3874 | | std::string const& name, cmStateEnums::TargetDomainSet domains) const |
3875 | 0 | { |
3876 | | // Look for an imported target. These take priority because they |
3877 | | // are more local in scope and do not have to be globally unique. |
3878 | 0 | auto targetName = name; |
3879 | 0 | if (domains.contains(cmStateEnums::TargetDomain::ALIAS)) { |
3880 | | // Look for local alias targets. |
3881 | 0 | auto alias = this->AliasTargets.find(name); |
3882 | 0 | if (alias != this->AliasTargets.end()) { |
3883 | 0 | targetName = alias->second; |
3884 | 0 | } |
3885 | 0 | } |
3886 | 0 | auto const imported = this->ImportedTargets.find(targetName); |
3887 | |
|
3888 | 0 | bool const useForeign = |
3889 | 0 | domains.contains(cmStateEnums::TargetDomain::FOREIGN); |
3890 | 0 | bool const useNative = domains.contains(cmStateEnums::TargetDomain::NATIVE); |
3891 | |
|
3892 | 0 | if (imported != this->ImportedTargets.end()) { |
3893 | 0 | if (imported->second->IsForeign() ? useForeign : useNative) { |
3894 | 0 | return imported->second; |
3895 | 0 | } |
3896 | 0 | } |
3897 | | |
3898 | | // Look for a target built in this directory. |
3899 | 0 | if (cmTarget* t = this->FindLocalNonAliasTarget(name)) { |
3900 | 0 | if (t->IsForeign() ? useForeign : useNative) { |
3901 | 0 | return t; |
3902 | 0 | } |
3903 | 0 | } |
3904 | | |
3905 | | // Look for a target built in this project. |
3906 | 0 | return this->GetGlobalGenerator()->FindTarget(name, domains); |
3907 | 0 | } |
3908 | | |
3909 | | bool cmMakefile::IsAlias(std::string const& name) const |
3910 | 0 | { |
3911 | 0 | if (cm::contains(this->AliasTargets, name)) { |
3912 | 0 | return true; |
3913 | 0 | } |
3914 | 0 | return this->GetGlobalGenerator()->IsAlias(name); |
3915 | 0 | } |
3916 | | |
3917 | | bool cmMakefile::EnforceUniqueName(std::string const& name, std::string& msg, |
3918 | | bool isCustom) const |
3919 | 0 | { |
3920 | 0 | if (this->IsAlias(name)) { |
3921 | 0 | msg = cmStrCat("cannot create target \"", name, |
3922 | 0 | "\" because an alias with the same name already exists."); |
3923 | 0 | return false; |
3924 | 0 | } |
3925 | 0 | if (cmTarget* existing = this->FindTargetToUse(name)) { |
3926 | | // The name given conflicts with an existing target. Produce an |
3927 | | // error in a compatible way. |
3928 | 0 | if (existing->IsImported()) { |
3929 | | // Imported targets were not supported in previous versions. |
3930 | | // This is new code, so we can make it an error. |
3931 | 0 | msg = cmStrCat( |
3932 | 0 | "cannot create target \"", name, |
3933 | 0 | "\" because an imported target with the same name already exists."); |
3934 | 0 | return false; |
3935 | 0 | } |
3936 | | |
3937 | | // The conflict is with a non-imported target. |
3938 | | // Allow this if the user has requested support. |
3939 | 0 | cmake* cm = this->GetCMakeInstance(); |
3940 | 0 | if (isCustom && existing->GetType() == cmStateEnums::UTILITY && |
3941 | 0 | this != existing->GetMakefile() && |
3942 | 0 | cm->GetState()->GetGlobalPropertyAsBool( |
3943 | 0 | "ALLOW_DUPLICATE_CUSTOM_TARGETS")) { |
3944 | 0 | return true; |
3945 | 0 | } |
3946 | | |
3947 | | // Produce an error that tells the user how to work around the |
3948 | | // problem. |
3949 | 0 | std::ostringstream e; |
3950 | 0 | e << "cannot create target \"" << name |
3951 | 0 | << "\" because another target with the same name already exists. " |
3952 | 0 | "The existing target is "; |
3953 | 0 | switch (existing->GetType()) { |
3954 | 0 | case cmStateEnums::EXECUTABLE: |
3955 | 0 | e << "an executable "; |
3956 | 0 | break; |
3957 | 0 | case cmStateEnums::STATIC_LIBRARY: |
3958 | 0 | e << "a static library "; |
3959 | 0 | break; |
3960 | 0 | case cmStateEnums::SHARED_LIBRARY: |
3961 | 0 | e << "a shared library "; |
3962 | 0 | break; |
3963 | 0 | case cmStateEnums::MODULE_LIBRARY: |
3964 | 0 | e << "a module library "; |
3965 | 0 | break; |
3966 | 0 | case cmStateEnums::UTILITY: |
3967 | 0 | e << "a custom target "; |
3968 | 0 | break; |
3969 | 0 | case cmStateEnums::INTERFACE_LIBRARY: |
3970 | 0 | e << "an interface library "; |
3971 | 0 | break; |
3972 | 0 | default: |
3973 | 0 | break; |
3974 | 0 | } |
3975 | 0 | e << "created in source directory \"" |
3976 | 0 | << existing->GetMakefile()->GetCurrentSourceDirectory() |
3977 | 0 | << "\". " |
3978 | 0 | "See documentation for policy CMP0002 for more details."; |
3979 | 0 | msg = e.str(); |
3980 | 0 | return false; |
3981 | 0 | } |
3982 | 0 | return true; |
3983 | 0 | } |
3984 | | |
3985 | | bool cmMakefile::EnforceUniqueDir(std::string const& srcPath, |
3986 | | std::string const& binPath) const |
3987 | 0 | { |
3988 | | // Make sure the binary directory is unique. |
3989 | 0 | cmGlobalGenerator* gg = this->GetGlobalGenerator(); |
3990 | 0 | if (gg->BinaryDirectoryIsNew(binPath)) { |
3991 | 0 | return true; |
3992 | 0 | } |
3993 | 0 | this->IssueMessage(MessageType::FATAL_ERROR, |
3994 | 0 | cmStrCat("The binary directory\n" |
3995 | 0 | " ", |
3996 | 0 | binPath, |
3997 | 0 | "\n" |
3998 | 0 | "is already used to build a source directory. " |
3999 | 0 | "It cannot be used to build source directory\n" |
4000 | 0 | " ", |
4001 | 0 | srcPath, |
4002 | 0 | "\n" |
4003 | 0 | "Specify a unique binary directory name.")); |
4004 | |
|
4005 | 0 | return false; |
4006 | 0 | } |
4007 | | |
4008 | | static std::string const matchVariables[] = { |
4009 | | "CMAKE_MATCH_0", "CMAKE_MATCH_1", "CMAKE_MATCH_2", "CMAKE_MATCH_3", |
4010 | | "CMAKE_MATCH_4", "CMAKE_MATCH_5", "CMAKE_MATCH_6", "CMAKE_MATCH_7", |
4011 | | "CMAKE_MATCH_8", "CMAKE_MATCH_9" |
4012 | | }; |
4013 | | |
4014 | | static std::string const nMatchesVariable = "CMAKE_MATCH_COUNT"; |
4015 | | |
4016 | | void cmMakefile::ClearMatches() |
4017 | 0 | { |
4018 | 0 | cmValue nMatchesStr = this->GetDefinition(nMatchesVariable); |
4019 | 0 | if (!nMatchesStr) { |
4020 | 0 | return; |
4021 | 0 | } |
4022 | 0 | int nMatches = atoi(nMatchesStr->c_str()); |
4023 | 0 | for (int i = 0; i <= nMatches; i++) { |
4024 | 0 | std::string const& var = matchVariables[i]; |
4025 | 0 | std::string const& s = this->GetSafeDefinition(var); |
4026 | 0 | if (!s.empty()) { |
4027 | 0 | this->AddDefinition(var, ""); |
4028 | 0 | this->MarkVariableAsUsed(var); |
4029 | 0 | } |
4030 | 0 | } |
4031 | 0 | this->AddDefinition(nMatchesVariable, "0"); |
4032 | 0 | this->MarkVariableAsUsed(nMatchesVariable); |
4033 | 0 | } |
4034 | | |
4035 | | void cmMakefile::StoreMatches(cmsys::RegularExpression& re) |
4036 | 0 | { |
4037 | 0 | char highest = 0; |
4038 | 0 | for (int i = 0; i < 10; i++) { |
4039 | 0 | std::string const& m = re.match(i); |
4040 | 0 | if (!m.empty()) { |
4041 | 0 | std::string const& var = matchVariables[i]; |
4042 | 0 | this->AddDefinition(var, m); |
4043 | 0 | this->MarkVariableAsUsed(var); |
4044 | 0 | highest = static_cast<char>('0' + i); |
4045 | 0 | } |
4046 | 0 | } |
4047 | 0 | char nMatches[] = { highest, '\0' }; |
4048 | 0 | this->AddDefinition(nMatchesVariable, nMatches); |
4049 | 0 | this->MarkVariableAsUsed(nMatchesVariable); |
4050 | 0 | } |
4051 | | |
4052 | | cmStateSnapshot cmMakefile::GetStateSnapshot() const |
4053 | 0 | { |
4054 | 0 | return this->StateSnapshot; |
4055 | 0 | } |
4056 | | |
4057 | | cmPolicies::PolicyStatus cmMakefile::GetPolicyStatus(cmPolicies::PolicyID id, |
4058 | | bool parent_scope) const |
4059 | 0 | { |
4060 | 0 | return this->StateSnapshot.GetPolicy(id, parent_scope); |
4061 | 0 | } |
4062 | | |
4063 | | bool cmMakefile::PolicyOptionalWarningEnabled(std::string const& var) const |
4064 | 0 | { |
4065 | | // Check for an explicit CMAKE_POLICY_WARNING_CMP<NNNN> setting. |
4066 | 0 | if (cmValue val = this->GetDefinition(var)) { |
4067 | 0 | return val.IsOn(); |
4068 | 0 | } |
4069 | | // Enable optional policy warnings with --debug-output, --trace, |
4070 | | // or --trace-expand. |
4071 | 0 | cmake* cm = this->GetCMakeInstance(); |
4072 | 0 | return cm->GetDebugOutput() || cm->GetTrace(); |
4073 | 0 | } |
4074 | | |
4075 | | bool cmMakefile::SetPolicy(char const* id, cmPolicies::PolicyStatus status) |
4076 | 0 | { |
4077 | 0 | cmPolicies::PolicyID pid; |
4078 | 0 | if (!cmPolicies::GetPolicyID(id, /* out */ pid)) { |
4079 | 0 | this->IssueMessage( |
4080 | 0 | MessageType::FATAL_ERROR, |
4081 | 0 | cmStrCat("Policy \"", id, "\" is not known to this version of CMake.")); |
4082 | 0 | return false; |
4083 | 0 | } |
4084 | 0 | return this->SetPolicy(pid, status); |
4085 | 0 | } |
4086 | | |
4087 | | bool cmMakefile::SetPolicy(cmPolicies::PolicyID id, |
4088 | | cmPolicies::PolicyStatus status) |
4089 | 0 | { |
4090 | | // A removed policy may be set only to NEW. |
4091 | 0 | if (cmPolicies::IsRemoved(id) && status != cmPolicies::NEW) { |
4092 | 0 | std::string msg = cmPolicies::GetRemovedPolicyError(id); |
4093 | 0 | this->IssueMessage(MessageType::FATAL_ERROR, msg); |
4094 | 0 | return false; |
4095 | 0 | } |
4096 | | |
4097 | | // Deprecate old policies. |
4098 | 0 | if (status == cmPolicies::OLD && id <= cmPolicies::CMP0155 && |
4099 | 0 | !(this->GetCMakeInstance()->GetIsInTryCompile() && |
4100 | 0 | ( |
4101 | | // Policies set by cmCoreTryCompile::TryCompileCode. |
4102 | 0 | id == cmPolicies::CMP0083 || id == cmPolicies::CMP0091 || |
4103 | 0 | id == cmPolicies::CMP0104 || id == cmPolicies::CMP0123 || |
4104 | 0 | id == cmPolicies::CMP0126 || id == cmPolicies::CMP0128 || |
4105 | 0 | id == cmPolicies::CMP0136 || id == cmPolicies::CMP0141 || |
4106 | 0 | id == cmPolicies::CMP0155)) && |
4107 | 0 | (!this->IsSet("CMAKE_WARN_DEPRECATED") || |
4108 | 0 | this->IsOn("CMAKE_WARN_DEPRECATED"))) { |
4109 | 0 | this->IssueMessage(MessageType::DEPRECATION_WARNING, |
4110 | 0 | cmPolicies::GetPolicyDeprecatedWarning(id)); |
4111 | 0 | } |
4112 | |
|
4113 | 0 | this->StateSnapshot.SetPolicy(id, status); |
4114 | | |
4115 | | // Handle CMAKE_PARENT_LIST_FILE for CMP0198 policy changes |
4116 | 0 | if (id == cmPolicies::CMP0198 && |
4117 | 0 | this->GetCMakeInstance()->GetState()->GetRole() == |
4118 | 0 | cmState::Role::Project) { |
4119 | 0 | this->UpdateParentListFileVariable(); |
4120 | 0 | } |
4121 | |
|
4122 | 0 | return true; |
4123 | 0 | } |
4124 | | |
4125 | | cmMakefile::PolicyPushPop::PolicyPushPop(cmMakefile* m) |
4126 | 0 | : Makefile(m) |
4127 | 0 | { |
4128 | 0 | this->Makefile->PushPolicy(); |
4129 | 0 | } |
4130 | | |
4131 | | cmMakefile::PolicyPushPop::~PolicyPushPop() |
4132 | 0 | { |
4133 | 0 | this->Makefile->PopPolicy(); |
4134 | 0 | } |
4135 | | |
4136 | | void cmMakefile::PushPolicy(bool weak, cmPolicies::PolicyMap const& pm) |
4137 | 1 | { |
4138 | 1 | this->StateSnapshot.PushPolicy(pm, weak); |
4139 | 1 | } |
4140 | | |
4141 | | void cmMakefile::PopPolicy() |
4142 | 0 | { |
4143 | 0 | if (!this->StateSnapshot.PopPolicy()) { |
4144 | 0 | this->IssueMessage(MessageType::FATAL_ERROR, |
4145 | 0 | "cmake_policy POP without matching PUSH"); |
4146 | 0 | } |
4147 | 0 | } |
4148 | | |
4149 | | void cmMakefile::PopSnapshot(bool reportError) |
4150 | 1 | { |
4151 | | // cmStateSnapshot manages nested policy scopes within it. |
4152 | | // Since the scope corresponding to the snapshot is closing, |
4153 | | // reject any still-open nested policy scopes with an error. |
4154 | 1 | while (this->StateSnapshot.CanPopPolicyScope()) { |
4155 | 0 | if (reportError) { |
4156 | 0 | this->IssueMessage(MessageType::FATAL_ERROR, |
4157 | 0 | "cmake_policy PUSH without matching POP"); |
4158 | 0 | reportError = false; |
4159 | 0 | } |
4160 | 0 | this->PopPolicy(); |
4161 | 0 | } |
4162 | | |
4163 | 1 | this->StateSnapshot = this->GetState()->Pop(this->StateSnapshot); |
4164 | 1 | assert(this->StateSnapshot.IsValid()); |
4165 | 1 | } |
4166 | | |
4167 | | bool cmMakefile::SetPolicyVersion(std::string const& version_min, |
4168 | | std::string const& version_max) |
4169 | 0 | { |
4170 | 0 | return cmPolicies::ApplyPolicyVersion(this, version_min, version_max, |
4171 | 0 | cmPolicies::WarnCompat::On); |
4172 | 0 | } |
4173 | | |
4174 | | void cmMakefile::UpdateParentListFileVariable() |
4175 | 0 | { |
4176 | | // CMP0198 determines CMAKE_PARENT_LIST_FILE behavior in CMakeLists.txt |
4177 | 0 | if (this->GetPolicyStatus(cmPolicies::CMP0198) == cmPolicies::NEW) { |
4178 | 0 | this->RemoveDefinition(kCMAKE_PARENT_LIST_FILE); |
4179 | 0 | } else { |
4180 | 0 | std::string currentSourceDir = |
4181 | 0 | this->StateSnapshot.GetDirectory().GetCurrentSource(); |
4182 | 0 | std::string currentStart = |
4183 | 0 | this->GetCMakeInstance()->GetCMakeListFile(currentSourceDir); |
4184 | |
|
4185 | 0 | this->AddDefinition(kCMAKE_PARENT_LIST_FILE, currentStart); |
4186 | 0 | } |
4187 | 0 | } |
4188 | | |
4189 | | cmMakefile::VariablePushPop::VariablePushPop(cmMakefile* m) |
4190 | 0 | : Makefile(m) |
4191 | 0 | { |
4192 | 0 | this->Makefile->StateSnapshot = |
4193 | 0 | this->Makefile->GetState()->CreateVariableScopeSnapshot( |
4194 | 0 | this->Makefile->StateSnapshot); |
4195 | 0 | } |
4196 | | |
4197 | | cmMakefile::VariablePushPop::~VariablePushPop() |
4198 | 0 | { |
4199 | 0 | this->Makefile->PopSnapshot(); |
4200 | 0 | } |
4201 | | |
4202 | | void cmMakefile::RecordPolicies(cmPolicies::PolicyMap& pm) const |
4203 | 0 | { |
4204 | | /* Record the setting of every policy. */ |
4205 | 0 | using PolicyID = cmPolicies::PolicyID; |
4206 | 0 | for (PolicyID pid = cmPolicies::CMP0000; pid != cmPolicies::CMPCOUNT; |
4207 | 0 | pid = static_cast<PolicyID>(pid + 1)) { |
4208 | 0 | pm.Set(pid, this->GetPolicyStatus(pid)); |
4209 | 0 | } |
4210 | 0 | } |
4211 | | |
4212 | | cmMakefile::FunctionPushPop::FunctionPushPop(cmMakefile* mf, |
4213 | | std::string const& fileName, |
4214 | | cmPolicies::PolicyMap const& pm) |
4215 | 0 | : Makefile(mf) |
4216 | 0 | { |
4217 | 0 | this->Makefile->PushFunctionScope(fileName, pm); |
4218 | 0 | } |
4219 | | |
4220 | | cmMakefile::FunctionPushPop::~FunctionPushPop() |
4221 | 0 | { |
4222 | 0 | this->Makefile->PopFunctionScope(this->ReportError); |
4223 | 0 | } |
4224 | | |
4225 | | cmMakefile::MacroPushPop::MacroPushPop(cmMakefile* mf, |
4226 | | std::string const& fileName, |
4227 | | cmPolicies::PolicyMap const& pm) |
4228 | 0 | : Makefile(mf) |
4229 | 0 | { |
4230 | 0 | this->Makefile->PushMacroScope(fileName, pm); |
4231 | 0 | } |
4232 | | |
4233 | | cmMakefile::MacroPushPop::~MacroPushPop() |
4234 | 0 | { |
4235 | 0 | this->Makefile->PopMacroScope(this->ReportError); |
4236 | 0 | } |
4237 | | |
4238 | | cmFindPackageStackRAII::cmFindPackageStackRAII(cmMakefile* mf, |
4239 | | std::string const& name) |
4240 | 0 | : Makefile(mf) |
4241 | 0 | { |
4242 | 0 | this->Makefile->FindPackageStack = |
4243 | 0 | this->Makefile->FindPackageStack.Push(cmFindPackageCall{ |
4244 | 0 | name, |
4245 | 0 | cmPackageInformation(), |
4246 | 0 | this->Makefile->FindPackageStackNextIndex, |
4247 | 0 | }); |
4248 | 0 | this->Makefile->FindPackageStackNextIndex++; |
4249 | 0 | } |
4250 | | |
4251 | | void cmFindPackageStackRAII::BindTop(cmPackageInformation*& value) |
4252 | 0 | { |
4253 | 0 | if (this->Value) { |
4254 | 0 | *this->Value = nullptr; |
4255 | 0 | } |
4256 | 0 | this->Value = &value; |
4257 | 0 | value = &this->Makefile->FindPackageStack.cmStack::Top().PackageInfo; |
4258 | 0 | } |
4259 | | |
4260 | | cmFindPackageStackRAII::~cmFindPackageStackRAII() |
4261 | 0 | { |
4262 | 0 | if (this->Value) { |
4263 | 0 | *this->Value = nullptr; |
4264 | 0 | } |
4265 | |
|
4266 | 0 | this->Makefile->FindPackageStackNextIndex = |
4267 | 0 | this->Makefile->FindPackageStack.Top().Index + 1; |
4268 | 0 | this->Makefile->FindPackageStack = this->Makefile->FindPackageStack.Pop(); |
4269 | |
|
4270 | 0 | if (!this->Makefile->FindPackageStack.Empty()) { |
4271 | 0 | auto top = this->Makefile->FindPackageStack.Top(); |
4272 | 0 | this->Makefile->FindPackageStack = this->Makefile->FindPackageStack.Pop(); |
4273 | |
|
4274 | 0 | top.Index = this->Makefile->FindPackageStackNextIndex; |
4275 | 0 | this->Makefile->FindPackageStackNextIndex++; |
4276 | |
|
4277 | 0 | this->Makefile->FindPackageStack = |
4278 | 0 | this->Makefile->FindPackageStack.Push(top); |
4279 | 0 | } |
4280 | 0 | } |
4281 | | |
4282 | | cmMakefile::DebugFindPkgRAII::DebugFindPkgRAII(cmMakefile* mf, |
4283 | | std::string const& pkg) |
4284 | 0 | : Makefile(mf) |
4285 | 0 | , OldValue(this->Makefile->DebugFindPkg) |
4286 | 0 | { |
4287 | 0 | this->Makefile->DebugFindPkg = |
4288 | 0 | this->Makefile->GetCMakeInstance()->GetDebugFindPkgOutput(pkg); |
4289 | 0 | } |
4290 | | |
4291 | | cmMakefile::DebugFindPkgRAII::~DebugFindPkgRAII() |
4292 | 0 | { |
4293 | 0 | this->Makefile->DebugFindPkg = this->OldValue; |
4294 | 0 | } |
4295 | | |
4296 | | bool cmMakefile::GetDebugFindPkgMode() const |
4297 | 0 | { |
4298 | 0 | return this->DebugFindPkg; |
4299 | 0 | } |