/src/CMake/Source/cmLocalGenerator.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 "cmLocalGenerator.h" |
4 | | |
5 | | #include <algorithm> |
6 | | #include <array> |
7 | | #include <cassert> |
8 | | #include <cctype> |
9 | | #include <cstdio> |
10 | | #include <cstdlib> |
11 | | #include <initializer_list> |
12 | | #include <iterator> |
13 | | #include <queue> |
14 | | #include <sstream> |
15 | | #include <unordered_set> |
16 | | #include <utility> |
17 | | #include <vector> |
18 | | |
19 | | #include <cm/memory> |
20 | | #include <cm/optional> |
21 | | #include <cm/string_view> |
22 | | #include <cmext/algorithm> |
23 | | #include <cmext/string_view> |
24 | | |
25 | | #include "cmsys/RegularExpression.hxx" |
26 | | |
27 | | #include "cmAlgorithms.h" |
28 | | #include "cmCMakePath.h" |
29 | | #include "cmComputeLinkInformation.h" |
30 | | #include "cmCryptoHash.h" |
31 | | #include "cmCustomCommand.h" |
32 | | #include "cmCustomCommandGenerator.h" |
33 | | #include "cmCustomCommandLines.h" |
34 | | #include "cmCustomCommandTypes.h" |
35 | | #include "cmGeneratedFileStream.h" |
36 | | #include "cmGeneratorExpression.h" |
37 | | #include "cmGeneratorExpressionEvaluationFile.h" |
38 | | #include "cmGeneratorTarget.h" |
39 | | #include "cmGlobalGenerator.h" |
40 | | #include "cmInstallGenerator.h" |
41 | | #include "cmInstallScriptGenerator.h" |
42 | | #include "cmInstallTargetGenerator.h" |
43 | | #include "cmLinkLineComputer.h" |
44 | | #include "cmLinkLineDeviceComputer.h" |
45 | | #include "cmList.h" |
46 | | #include "cmMakefile.h" |
47 | | #include "cmMessageType.h" |
48 | | #include "cmObjectLocation.h" |
49 | | #include "cmRange.h" |
50 | | #include "cmRulePlaceholderExpander.h" |
51 | | #include "cmSourceFile.h" |
52 | | #include "cmSourceFileLocation.h" |
53 | | #include "cmSourceFileLocationKind.h" |
54 | | #include "cmSourceGroup.h" |
55 | | #include "cmStandardLevelResolver.h" |
56 | | #include "cmState.h" |
57 | | #include "cmStateDirectory.h" |
58 | | #include "cmStateTypes.h" |
59 | | #include "cmStringAlgorithms.h" |
60 | | #include "cmSystemTools.h" |
61 | | #include "cmTarget.h" |
62 | | #include "cmTestGenerator.h" |
63 | | #include "cmValue.h" |
64 | | #include "cmake.h" |
65 | | |
66 | | #if defined(__HAIKU__) |
67 | | # include <FindDirectory.h> |
68 | | # include <StorageDefs.h> |
69 | | #endif |
70 | | |
71 | | namespace { |
72 | | // List of variables that are replaced when |
73 | | // rules are expanded. These variables are |
74 | | // replaced in the form <var> with GetSafeDefinition(var). |
75 | | // ${LANG} is replaced in the variable first with all enabled |
76 | | // languages. |
77 | | auto ruleReplaceVars = { |
78 | | "CMAKE_${LANG}_COMPILER", |
79 | | "CMAKE_SHARED_MODULE_${LANG}_FLAGS", |
80 | | "CMAKE_SHARED_LIBRARY_${LANG}_FLAGS", |
81 | | "CMAKE_SHARED_LIBRARY_SONAME_${LANG}_FLAG", |
82 | | "CMAKE_${LANG}_ARCHIVE", |
83 | | "CMAKE_AR", |
84 | | "CMAKE_SOURCE_DIR", |
85 | | "CMAKE_BINARY_DIR", |
86 | | "CMAKE_CURRENT_SOURCE_DIR", |
87 | | "CMAKE_CURRENT_BINARY_DIR", |
88 | | "CMAKE_RANLIB", |
89 | | "CMAKE_MT", |
90 | | "CMAKE_TAPI", |
91 | | "CMAKE_CUDA_HOST_COMPILER", |
92 | | "CMAKE_CUDA_HOST_LINK_LAUNCHER", |
93 | | "CMAKE_HIP_HOST_COMPILER", |
94 | | "CMAKE_HIP_HOST_LINK_LAUNCHER", |
95 | | "CMAKE_CL_SHOWINCLUDES_PREFIX", |
96 | | }; |
97 | | |
98 | | // Variables whose placeholders now map to an empty string. |
99 | | // Our platform modules no longer use these, but third-party code might. |
100 | | auto ruleReplaceEmptyVars = { |
101 | | "CMAKE_SHARED_LIBRARY_CREATE_${LANG}_FLAGS", |
102 | | "CMAKE_SHARED_MODULE_CREATE_${LANG}_FLAGS", |
103 | | "CMAKE_${LANG}_LINK_FLAGS", |
104 | | }; |
105 | | } |
106 | | |
107 | | cmLocalGenerator::cmLocalGenerator(cmGlobalGenerator* gg, cmMakefile* makefile) |
108 | 0 | : cmOutputConverter(makefile->GetStateSnapshot()) |
109 | 0 | , DirectoryBacktrace(makefile->GetBacktrace()) |
110 | 0 | { |
111 | 0 | this->GlobalGenerator = gg; |
112 | |
|
113 | 0 | this->Makefile = makefile; |
114 | |
|
115 | 0 | this->AliasTargets = makefile->GetAliasTargets(); |
116 | |
|
117 | 0 | this->EmitUniversalBinaryFlags = true; |
118 | |
|
119 | 0 | this->ComputeObjectMaxPath(); |
120 | | |
121 | | // Canonicalize entries of the CPATH environment variable the same |
122 | | // way detection of CMAKE_<LANG>_IMPLICIT_INCLUDE_DIRECTORIES does. |
123 | 0 | { |
124 | 0 | std::vector<std::string> cpath; |
125 | 0 | cmSystemTools::GetPath(cpath, "CPATH"); |
126 | 0 | for (std::string const& cp : cpath) { |
127 | 0 | if (cmSystemTools::FileIsFullPath(cp)) { |
128 | 0 | this->EnvCPATH.emplace_back(cmSystemTools::CollapseFullPath(cp)); |
129 | 0 | } |
130 | 0 | } |
131 | 0 | } |
132 | |
|
133 | 0 | std::vector<std::string> enabledLanguages = |
134 | 0 | this->GetState()->GetEnabledLanguages(); |
135 | |
|
136 | 0 | if (cmValue sysrootCompile = |
137 | 0 | this->Makefile->GetDefinition("CMAKE_SYSROOT_COMPILE")) { |
138 | 0 | this->CompilerSysroot = *sysrootCompile; |
139 | 0 | } else { |
140 | 0 | this->CompilerSysroot = this->Makefile->GetSafeDefinition("CMAKE_SYSROOT"); |
141 | 0 | } |
142 | |
|
143 | 0 | if (cmValue sysrootLink = |
144 | 0 | this->Makefile->GetDefinition("CMAKE_SYSROOT_LINK")) { |
145 | 0 | this->LinkerSysroot = *sysrootLink; |
146 | 0 | } else { |
147 | 0 | this->LinkerSysroot = this->Makefile->GetSafeDefinition("CMAKE_SYSROOT"); |
148 | 0 | } |
149 | | |
150 | | // OSX SYSROOT can be required by some tools, like tapi |
151 | 0 | { |
152 | 0 | cmValue osxSysroot = this->Makefile->GetDefinition("CMAKE_OSX_SYSROOT"); |
153 | 0 | this->VariableMappings["CMAKE_OSX_SYSROOT"] = |
154 | 0 | osxSysroot.IsEmpty() ? "/" : this->EscapeForShell(*osxSysroot, true); |
155 | 0 | } |
156 | |
|
157 | 0 | if (cmValue appleArchSysroots = |
158 | 0 | this->Makefile->GetDefinition("CMAKE_APPLE_ARCH_SYSROOTS")) { |
159 | 0 | std::string const& appleArchs = |
160 | 0 | this->Makefile->GetSafeDefinition("CMAKE_OSX_ARCHITECTURES"); |
161 | 0 | cmList archs(appleArchs); |
162 | 0 | cmList sysroots{ appleArchSysroots, cmList::EmptyElements::Yes }; |
163 | 0 | if (archs.size() == sysroots.size()) { |
164 | 0 | for (cmList::size_type i = 0; i < archs.size(); ++i) { |
165 | 0 | this->AppleArchSysroots[archs[i]] = sysroots[i]; |
166 | 0 | } |
167 | 0 | } else { |
168 | 0 | std::string const e = |
169 | 0 | cmStrCat("CMAKE_APPLE_ARCH_SYSROOTS:\n ", *appleArchSysroots, |
170 | 0 | "\n" |
171 | 0 | "is not the same length as CMAKE_OSX_ARCHITECTURES:\n ", |
172 | 0 | appleArchs); |
173 | 0 | this->IssueMessage(MessageType::FATAL_ERROR, e); |
174 | 0 | } |
175 | 0 | } |
176 | |
|
177 | 0 | for (std::string const& lang : enabledLanguages) { |
178 | 0 | if (lang == "NONE") { |
179 | 0 | continue; |
180 | 0 | } |
181 | 0 | this->Compilers["CMAKE_" + lang + "_COMPILER"] = lang; |
182 | |
|
183 | 0 | this->VariableMappings["CMAKE_" + lang + "_COMPILER"] = |
184 | 0 | this->Makefile->GetSafeDefinition("CMAKE_" + lang + "_COMPILER"); |
185 | |
|
186 | 0 | std::string const& compilerArg1 = "CMAKE_" + lang + "_COMPILER_ARG1"; |
187 | 0 | std::string const& compilerTarget = "CMAKE_" + lang + "_COMPILER_TARGET"; |
188 | 0 | std::string const& compilerOptionTarget = |
189 | 0 | "CMAKE_" + lang + "_COMPILE_OPTIONS_TARGET"; |
190 | 0 | std::string const& compilerExternalToolchain = |
191 | 0 | "CMAKE_" + lang + "_COMPILER_EXTERNAL_TOOLCHAIN"; |
192 | 0 | std::string const& compilerOptionExternalToolchain = |
193 | 0 | "CMAKE_" + lang + "_COMPILE_OPTIONS_EXTERNAL_TOOLCHAIN"; |
194 | 0 | std::string const& compilerOptionSysroot = |
195 | 0 | "CMAKE_" + lang + "_COMPILE_OPTIONS_SYSROOT"; |
196 | |
|
197 | 0 | this->VariableMappings[compilerArg1] = |
198 | 0 | this->Makefile->GetSafeDefinition(compilerArg1); |
199 | 0 | this->VariableMappings[compilerTarget] = |
200 | 0 | this->Makefile->GetSafeDefinition(compilerTarget); |
201 | 0 | this->VariableMappings[compilerOptionTarget] = |
202 | 0 | this->Makefile->GetSafeDefinition(compilerOptionTarget); |
203 | 0 | this->VariableMappings[compilerExternalToolchain] = |
204 | 0 | this->Makefile->GetSafeDefinition(compilerExternalToolchain); |
205 | 0 | this->VariableMappings[compilerOptionExternalToolchain] = |
206 | 0 | this->Makefile->GetSafeDefinition(compilerOptionExternalToolchain); |
207 | 0 | this->VariableMappings[compilerOptionSysroot] = |
208 | 0 | this->Makefile->GetSafeDefinition(compilerOptionSysroot); |
209 | |
|
210 | 0 | for (std::string replaceVar : ruleReplaceVars) { |
211 | 0 | if (replaceVar.find("${LANG}") != std::string::npos) { |
212 | 0 | cmSystemTools::ReplaceString(replaceVar, "${LANG}", lang); |
213 | 0 | } |
214 | |
|
215 | 0 | this->VariableMappings[replaceVar] = |
216 | 0 | this->Makefile->GetSafeDefinition(replaceVar); |
217 | 0 | } |
218 | |
|
219 | 0 | for (std::string replaceVar : ruleReplaceEmptyVars) { |
220 | 0 | if (replaceVar.find("${LANG}") != std::string::npos) { |
221 | 0 | cmSystemTools::ReplaceString(replaceVar, "${LANG}", lang); |
222 | 0 | } |
223 | |
|
224 | 0 | this->VariableMappings[replaceVar] = std::string(); |
225 | 0 | } |
226 | 0 | } |
227 | 0 | } |
228 | | |
229 | | std::unique_ptr<cmRulePlaceholderExpander> |
230 | | cmLocalGenerator::CreateRulePlaceholderExpander(cmBuildStep buildStep) const |
231 | 0 | { |
232 | 0 | return cm::make_unique<cmRulePlaceholderExpander>( |
233 | 0 | buildStep, this->Compilers, this->VariableMappings, this->CompilerSysroot, |
234 | 0 | this->LinkerSysroot); |
235 | 0 | } |
236 | | |
237 | 0 | cmLocalGenerator::~cmLocalGenerator() = default; |
238 | | |
239 | | void cmLocalGenerator::IssueMessage(MessageType t, |
240 | | std::string const& text) const |
241 | 0 | { |
242 | 0 | this->GetCMakeInstance()->IssueMessage(t, text, this->DirectoryBacktrace); |
243 | 0 | } |
244 | | |
245 | | void cmLocalGenerator::ComputeObjectMaxPath() |
246 | 0 | { |
247 | | // Choose a maximum object file name length. |
248 | | #if defined(_WIN32) || defined(__CYGWIN__) |
249 | | this->ObjectPathMax = 250; |
250 | | #else |
251 | 0 | this->ObjectPathMax = 1000; |
252 | 0 | #endif |
253 | 0 | cmValue plen = this->Makefile->GetDefinition("CMAKE_OBJECT_PATH_MAX"); |
254 | 0 | if (cmNonempty(plen)) { |
255 | 0 | unsigned int pmax; |
256 | 0 | if (sscanf(plen->c_str(), "%u", &pmax) == 1) { |
257 | 0 | if (pmax >= 128) { |
258 | 0 | this->ObjectPathMax = pmax; |
259 | 0 | } else { |
260 | 0 | std::ostringstream w; |
261 | 0 | w << "CMAKE_OBJECT_PATH_MAX is set to " << pmax |
262 | 0 | << ", which is less than the minimum of 128. " |
263 | 0 | "The value will be ignored."; |
264 | 0 | this->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); |
265 | 0 | } |
266 | 0 | } else { |
267 | 0 | std::ostringstream w; |
268 | 0 | w << "CMAKE_OBJECT_PATH_MAX is set to \"" << *plen |
269 | 0 | << "\", which fails to parse as a positive integer. " |
270 | 0 | "The value will be ignored."; |
271 | 0 | this->IssueMessage(MessageType::AUTHOR_WARNING, w.str()); |
272 | 0 | } |
273 | 0 | } |
274 | 0 | this->ObjectMaxPathViolations.clear(); |
275 | 0 | } |
276 | | |
277 | | static void MoveSystemIncludesToEnd(std::vector<std::string>& includeDirs, |
278 | | std::string const& config, |
279 | | std::string const& lang, |
280 | | cmGeneratorTarget const* target) |
281 | 0 | { |
282 | 0 | if (!target) { |
283 | 0 | return; |
284 | 0 | } |
285 | | |
286 | 0 | std::stable_sort( |
287 | 0 | includeDirs.begin(), includeDirs.end(), |
288 | 0 | [&target, &config, &lang](std::string const& a, std::string const& b) { |
289 | 0 | return !target->IsSystemIncludeDirectory(a, config, lang) && |
290 | 0 | target->IsSystemIncludeDirectory(b, config, lang); |
291 | 0 | }); |
292 | 0 | } |
293 | | |
294 | | static void MoveSystemIncludesToEnd(std::vector<BT<std::string>>& includeDirs, |
295 | | std::string const& config, |
296 | | std::string const& lang, |
297 | | cmGeneratorTarget const* target) |
298 | 0 | { |
299 | 0 | if (!target) { |
300 | 0 | return; |
301 | 0 | } |
302 | | |
303 | 0 | std::stable_sort(includeDirs.begin(), includeDirs.end(), |
304 | 0 | [target, &config, &lang](BT<std::string> const& a, |
305 | 0 | BT<std::string> const& b) { |
306 | 0 | return !target->IsSystemIncludeDirectory(a.Value, config, |
307 | 0 | lang) && |
308 | 0 | target->IsSystemIncludeDirectory(b.Value, config, lang); |
309 | 0 | }); |
310 | 0 | } |
311 | | |
312 | | void cmLocalGenerator::TraceDependencies() const |
313 | 0 | { |
314 | | // Generate the rule files for each target. |
315 | 0 | auto const& targets = this->GetGeneratorTargets(); |
316 | 0 | for (auto const& target : targets) { |
317 | 0 | if (!target->IsInBuildSystem()) { |
318 | 0 | continue; |
319 | 0 | } |
320 | 0 | target->TraceDependencies(); |
321 | 0 | } |
322 | 0 | } |
323 | | |
324 | | #ifndef CMAKE_BOOTSTRAP |
325 | | void cmLocalGenerator::ResolveSourceGroupGenex() |
326 | 0 | { |
327 | 0 | this->Makefile->ResolveSourceGroupGenex(this); |
328 | 0 | } |
329 | | #endif |
330 | | |
331 | | void cmLocalGenerator::GenerateTestFiles() |
332 | 0 | { |
333 | 0 | if (!this->Makefile->IsOn("CMAKE_TESTING_ENABLED")) { |
334 | 0 | return; |
335 | 0 | } |
336 | | |
337 | | // Compute the set of configurations. |
338 | 0 | std::vector<std::string> configurationTypes = |
339 | 0 | this->Makefile->GetGeneratorConfigs(cmMakefile::OnlyMultiConfig); |
340 | 0 | std::string config = this->Makefile->GetDefaultConfiguration(); |
341 | |
|
342 | 0 | std::string file = |
343 | 0 | cmStrCat(this->StateSnapshot.GetDirectory().GetCurrentBinary(), |
344 | 0 | "/CTestTestfile.cmake"); |
345 | 0 | this->GlobalGenerator->AddTestFile(file); |
346 | |
|
347 | 0 | cmGeneratedFileStream fout(file); |
348 | |
|
349 | 0 | fout << "# CMake generated Testfile for \n" |
350 | 0 | "# Source directory: " |
351 | 0 | << this->StateSnapshot.GetDirectory().GetCurrentSource() |
352 | 0 | << "\n" |
353 | 0 | "# Build directory: " |
354 | 0 | << this->StateSnapshot.GetDirectory().GetCurrentBinary() |
355 | 0 | << "\n" |
356 | 0 | "# \n" |
357 | 0 | "# This file includes the relevant testing commands " |
358 | 0 | "required for \n" |
359 | 0 | "# testing this directory and lists subdirectories to " |
360 | 0 | "be tested as well.\n"; |
361 | |
|
362 | 0 | std::string resourceSpecFile = |
363 | 0 | this->Makefile->GetSafeDefinition("CTEST_RESOURCE_SPEC_FILE"); |
364 | 0 | if (!resourceSpecFile.empty()) { |
365 | 0 | fout << "set(CTEST_RESOURCE_SPEC_FILE \"" << resourceSpecFile << "\")\n"; |
366 | 0 | } |
367 | |
|
368 | 0 | cmValue testIncludeFile = this->Makefile->GetProperty("TEST_INCLUDE_FILE"); |
369 | 0 | if (testIncludeFile) { |
370 | 0 | fout << "include(\"" << *testIncludeFile << "\")\n"; |
371 | 0 | } |
372 | |
|
373 | 0 | cmValue testIncludeFiles = this->Makefile->GetProperty("TEST_INCLUDE_FILES"); |
374 | 0 | if (testIncludeFiles) { |
375 | 0 | cmList includesList{ *testIncludeFiles }; |
376 | 0 | for (std::string const& i : includesList) { |
377 | 0 | fout << "include(\"" << i << "\")\n"; |
378 | 0 | } |
379 | 0 | } |
380 | | |
381 | | // Ask each test generator to write its code. |
382 | 0 | for (auto const& tester : this->Makefile->GetTestGenerators()) { |
383 | 0 | tester->Compute(this); |
384 | 0 | tester->Generate(fout, config, configurationTypes); |
385 | 0 | } |
386 | 0 | using vec_t = std::vector<cmStateSnapshot>; |
387 | 0 | vec_t const& children = this->Makefile->GetStateSnapshot().GetChildren(); |
388 | 0 | for (cmStateSnapshot const& i : children) { |
389 | | // TODO: Use add_subdirectory instead? |
390 | 0 | std::string outP = i.GetDirectory().GetCurrentBinary(); |
391 | 0 | outP = this->MaybeRelativeToCurBinDir(outP); |
392 | 0 | outP = cmOutputConverter::EscapeForCMake(outP); |
393 | 0 | fout << "subdirs(" << outP << ")\n"; |
394 | 0 | } |
395 | | |
396 | | // Add directory labels property |
397 | 0 | cmValue directoryLabels = |
398 | 0 | this->Makefile->GetDefinition("CMAKE_DIRECTORY_LABELS"); |
399 | 0 | cmValue labels = this->Makefile->GetProperty("LABELS"); |
400 | |
|
401 | 0 | if (labels || directoryLabels) { |
402 | 0 | fout << "set_directory_properties(PROPERTIES LABELS "; |
403 | 0 | if (labels) { |
404 | 0 | fout << cmOutputConverter::EscapeForCMake(*labels); |
405 | 0 | } |
406 | 0 | if (labels && directoryLabels) { |
407 | 0 | fout << ";"; |
408 | 0 | } |
409 | 0 | if (directoryLabels) { |
410 | 0 | fout << cmOutputConverter::EscapeForCMake(*directoryLabels); |
411 | 0 | } |
412 | 0 | fout << ")\n"; |
413 | 0 | } |
414 | 0 | } |
415 | | |
416 | | void cmLocalGenerator::CreateEvaluationFileOutputs() |
417 | 0 | { |
418 | 0 | std::vector<std::string> const& configs = |
419 | 0 | this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); |
420 | 0 | for (std::string const& c : configs) { |
421 | 0 | this->CreateEvaluationFileOutputs(c); |
422 | 0 | } |
423 | 0 | } |
424 | | |
425 | | void cmLocalGenerator::CreateEvaluationFileOutputs(std::string const& config) |
426 | 0 | { |
427 | 0 | for (auto const& geef : this->Makefile->GetEvaluationFiles()) { |
428 | 0 | geef->CreateOutputFile(this, config); |
429 | 0 | } |
430 | 0 | } |
431 | | |
432 | | void cmLocalGenerator::ProcessEvaluationFiles( |
433 | | std::vector<std::string>& generatedFiles) |
434 | 0 | { |
435 | 0 | for (auto const& geef : this->Makefile->GetEvaluationFiles()) { |
436 | 0 | geef->Generate(this); |
437 | 0 | if (cmSystemTools::GetFatalErrorOccurred()) { |
438 | 0 | return; |
439 | 0 | } |
440 | 0 | std::vector<std::string> files = geef->GetFiles(); |
441 | 0 | std::sort(files.begin(), files.end()); |
442 | |
|
443 | 0 | std::vector<std::string> intersection; |
444 | 0 | std::set_intersection(files.begin(), files.end(), generatedFiles.begin(), |
445 | 0 | generatedFiles.end(), |
446 | 0 | std::back_inserter(intersection)); |
447 | 0 | if (!intersection.empty()) { |
448 | 0 | cmSystemTools::Error("Files to be generated by multiple different " |
449 | 0 | "commands: " + |
450 | 0 | cmWrap('"', intersection, '"', " ")); |
451 | 0 | return; |
452 | 0 | } |
453 | | |
454 | 0 | cm::append(generatedFiles, files); |
455 | 0 | std::inplace_merge(generatedFiles.begin(), |
456 | 0 | generatedFiles.end() - files.size(), |
457 | 0 | generatedFiles.end()); |
458 | 0 | } |
459 | 0 | } |
460 | | |
461 | | void cmLocalGenerator::GenerateInstallRules() |
462 | 0 | { |
463 | | // Compute the install prefix. |
464 | 0 | cmValue installPrefix = |
465 | 0 | this->Makefile->GetDefinition("CMAKE_INSTALL_PREFIX"); |
466 | 0 | std::string prefix = *installPrefix; |
467 | |
|
468 | | #if defined(_WIN32) && !defined(__CYGWIN__) |
469 | | if (!installPrefix) { |
470 | | if (!cmSystemTools::GetEnv("SystemDrive", prefix)) { |
471 | | prefix = "C:"; |
472 | | } |
473 | | cmValue project_name = this->Makefile->GetDefinition("PROJECT_NAME"); |
474 | | if (cmNonempty(project_name)) { |
475 | | prefix += "/Program Files/"; |
476 | | prefix += *project_name; |
477 | | } else { |
478 | | prefix += "/InstalledCMakeProject"; |
479 | | } |
480 | | } |
481 | | #elif defined(__HAIKU__) |
482 | | char dir[B_PATH_NAME_LENGTH]; |
483 | | if (!installPrefix) { |
484 | | if (find_directory(B_SYSTEM_DIRECTORY, -1, false, dir, sizeof(dir)) == |
485 | | B_OK) { |
486 | | prefix = dir; |
487 | | } else { |
488 | | prefix = "/boot/system"; |
489 | | } |
490 | | } |
491 | | #else |
492 | 0 | if (!installPrefix) { |
493 | 0 | prefix = "/usr/local"; |
494 | 0 | } |
495 | 0 | #endif |
496 | 0 | if (cmValue stagingPrefix = |
497 | 0 | this->Makefile->GetDefinition("CMAKE_STAGING_PREFIX")) { |
498 | 0 | prefix = *stagingPrefix; |
499 | 0 | } |
500 | | |
501 | | // Compute the set of configurations. |
502 | 0 | std::vector<std::string> configurationTypes = |
503 | 0 | this->Makefile->GetGeneratorConfigs(cmMakefile::OnlyMultiConfig); |
504 | 0 | std::string config = this->Makefile->GetDefaultConfiguration(); |
505 | | |
506 | | // Choose a default install configuration. |
507 | 0 | std::string default_config = config; |
508 | 0 | char const* default_order[] = { "RELEASE", "MINSIZEREL", "RELWITHDEBINFO", |
509 | 0 | "DEBUG", nullptr }; |
510 | 0 | for (char const** c = default_order; *c && default_config.empty(); ++c) { |
511 | 0 | for (std::string const& configurationType : configurationTypes) { |
512 | 0 | if (cmSystemTools::UpperCase(configurationType) == *c) { |
513 | 0 | default_config = configurationType; |
514 | 0 | } |
515 | 0 | } |
516 | 0 | } |
517 | 0 | if (default_config.empty() && !configurationTypes.empty()) { |
518 | 0 | default_config = configurationTypes[0]; |
519 | 0 | } |
520 | | |
521 | | // Create the install script file. |
522 | 0 | std::string file = this->StateSnapshot.GetDirectory().GetCurrentBinary(); |
523 | 0 | std::string homedir = this->GetState()->GetBinaryDirectory(); |
524 | 0 | int toplevel_install = 0; |
525 | 0 | if (file == homedir) { |
526 | 0 | toplevel_install = 1; |
527 | 0 | } |
528 | 0 | file += "/cmake_install.cmake"; |
529 | 0 | this->GetGlobalGenerator()->AddInstallScript(file); |
530 | 0 | cmGeneratedFileStream fout(file); |
531 | | |
532 | | // Write the header. |
533 | | /* clang-format off */ |
534 | 0 | fout << "# Install script for directory: " |
535 | 0 | << this->StateSnapshot.GetDirectory().GetCurrentSource() |
536 | 0 | << "\n\n" |
537 | 0 | "# Set the install prefix\n" |
538 | 0 | "if(NOT DEFINED CMAKE_INSTALL_PREFIX)\n" |
539 | 0 | " set(CMAKE_INSTALL_PREFIX \"" << prefix << "\")\n" |
540 | 0 | "endif()\n" |
541 | 0 | << R"(string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX )" |
542 | 0 | << "\"${CMAKE_INSTALL_PREFIX}\")\n\n"; |
543 | | /* clang-format on */ |
544 | | |
545 | | // Write support code for generating per-configuration install rules. |
546 | | /* clang-format off */ |
547 | 0 | fout << |
548 | 0 | "# Set the install configuration name.\n" |
549 | 0 | "if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME)\n" |
550 | 0 | " if(BUILD_TYPE)\n" |
551 | 0 | " string(REGEX REPLACE \"^[^A-Za-z0-9_]+\" \"\"\n" |
552 | 0 | " CMAKE_INSTALL_CONFIG_NAME \"${BUILD_TYPE}\")\n" |
553 | 0 | " else()\n" |
554 | 0 | " set(CMAKE_INSTALL_CONFIG_NAME \"" << default_config << "\")\n" |
555 | 0 | " endif()\n" |
556 | 0 | " message(STATUS \"Install configuration: " |
557 | 0 | "\\\"${CMAKE_INSTALL_CONFIG_NAME}\\\"\")\n" |
558 | 0 | "endif()\n" |
559 | 0 | "\n"; |
560 | | /* clang-format on */ |
561 | | |
562 | | // Write support code for dealing with component-specific installs. |
563 | | /* clang-format off */ |
564 | 0 | fout << |
565 | 0 | "# Set the component getting installed.\n" |
566 | 0 | "if(NOT CMAKE_INSTALL_COMPONENT)\n" |
567 | 0 | " if(COMPONENT)\n" |
568 | 0 | " message(STATUS \"Install component: \\\"${COMPONENT}\\\"\")\n" |
569 | 0 | " set(CMAKE_INSTALL_COMPONENT \"${COMPONENT}\")\n" |
570 | 0 | " else()\n" |
571 | 0 | " set(CMAKE_INSTALL_COMPONENT)\n" |
572 | 0 | " endif()\n" |
573 | 0 | "endif()\n" |
574 | 0 | "\n"; |
575 | | /* clang-format on */ |
576 | | |
577 | | // Copy user-specified install options to the install code. |
578 | 0 | if (cmValue so_no_exe = |
579 | 0 | this->Makefile->GetDefinition("CMAKE_INSTALL_SO_NO_EXE")) { |
580 | | /* clang-format off */ |
581 | 0 | fout << |
582 | 0 | "# Install shared libraries without execute permission?\n" |
583 | 0 | "if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE)\n" |
584 | 0 | " set(CMAKE_INSTALL_SO_NO_EXE \"" << *so_no_exe << "\")\n" |
585 | 0 | "endif()\n" |
586 | 0 | "\n"; |
587 | | /* clang-format on */ |
588 | 0 | } |
589 | | |
590 | | // Copy cmake cross compile state to install code. |
591 | 0 | if (cmValue crosscompiling = |
592 | 0 | this->Makefile->GetDefinition("CMAKE_CROSSCOMPILING")) { |
593 | | /* clang-format off */ |
594 | 0 | fout << |
595 | 0 | "# Is this installation the result of a crosscompile?\n" |
596 | 0 | "if(NOT DEFINED CMAKE_CROSSCOMPILING)\n" |
597 | 0 | " set(CMAKE_CROSSCOMPILING \"" << *crosscompiling << "\")\n" |
598 | 0 | "endif()\n" |
599 | 0 | "\n"; |
600 | | /* clang-format on */ |
601 | 0 | } |
602 | | |
603 | | // Write default directory permissions. |
604 | 0 | if (cmValue defaultDirPermissions = this->Makefile->GetDefinition( |
605 | 0 | "CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS")) { |
606 | | /* clang-format off */ |
607 | 0 | fout << |
608 | 0 | "# Set default install directory permissions.\n" |
609 | 0 | "if(NOT DEFINED CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS)\n" |
610 | 0 | " set(CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS \"" |
611 | 0 | << *defaultDirPermissions << "\")\n" |
612 | 0 | "endif()\n" |
613 | 0 | "\n"; |
614 | | /* clang-format on */ |
615 | 0 | } |
616 | | |
617 | | // Write out CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM so that |
618 | | // installed code that uses `file(GET_RUNTIME_DEPENDENCIES)` |
619 | | // has same platform variable as when running cmake |
620 | 0 | if (cmValue platform = this->Makefile->GetDefinition( |
621 | 0 | "CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM")) { |
622 | | /* clang-format off */ |
623 | 0 | fout << |
624 | 0 | "# Set OS and executable format for runtime-dependencies.\n" |
625 | 0 | "if(NOT DEFINED CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM)\n" |
626 | 0 | " set(CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM \"" |
627 | 0 | << *platform << "\")\n" |
628 | 0 | "endif()\n" |
629 | 0 | "\n"; |
630 | | /* clang-format on */ |
631 | 0 | } |
632 | | |
633 | | // Write out CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL so that |
634 | | // installed code that uses `file(GET_RUNTIME_DEPENDENCIES)` |
635 | | // has same tool selected as when running cmake |
636 | 0 | if (cmValue command = |
637 | 0 | this->Makefile->GetDefinition("CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL")) { |
638 | | /* clang-format off */ |
639 | 0 | fout << |
640 | 0 | "# Set tool for dependency-resolution of runtime-dependencies.\n" |
641 | 0 | "if(NOT DEFINED CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL)\n" |
642 | 0 | " set(CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL \"" |
643 | 0 | << *command << "\")\n" |
644 | 0 | "endif()\n" |
645 | 0 | "\n"; |
646 | | /* clang-format on */ |
647 | 0 | } |
648 | | |
649 | | // Write out CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND so that |
650 | | // installed code that uses `file(GET_RUNTIME_DEPENDENCIES)` |
651 | | // has same path to the tool as when running cmake |
652 | 0 | if (cmValue command = this->Makefile->GetDefinition( |
653 | 0 | "CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND")) { |
654 | | /* clang-format off */ |
655 | 0 | fout << |
656 | 0 | "# Set path to tool for dependency-resolution of runtime-dependencies.\n" |
657 | 0 | "if(NOT DEFINED CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND)\n" |
658 | 0 | " set(CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND \"" |
659 | 0 | << *command << "\")\n" |
660 | 0 | "endif()\n" |
661 | 0 | "\n"; |
662 | | /* clang-format on */ |
663 | 0 | } |
664 | | |
665 | | // Write out CMAKE_OBJDUMP so that installed code that uses |
666 | | // `file(GET_RUNTIME_DEPENDENCIES)` and hasn't specified |
667 | | // CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND has consistent |
668 | | // logic to fallback to CMAKE_OBJDUMP when `objdump` is |
669 | | // not on the path |
670 | 0 | if (cmValue command = this->Makefile->GetDefinition("CMAKE_OBJDUMP")) { |
671 | | /* clang-format off */ |
672 | 0 | fout << |
673 | 0 | "# Set path to fallback-tool for dependency-resolution.\n" |
674 | 0 | "if(NOT DEFINED CMAKE_OBJDUMP)\n" |
675 | 0 | " set(CMAKE_OBJDUMP \"" |
676 | 0 | << *command << "\")\n" |
677 | 0 | "endif()\n" |
678 | 0 | "\n"; |
679 | | /* clang-format on */ |
680 | 0 | } |
681 | |
|
682 | 0 | this->AddGeneratorSpecificInstallSetup(fout); |
683 | | |
684 | | // Ask each install generator to write its code. |
685 | 0 | cmPolicies::PolicyStatus status = this->GetPolicyStatus(cmPolicies::CMP0082); |
686 | 0 | auto const& installers = this->Makefile->GetInstallGenerators(); |
687 | 0 | bool haveSubdirectoryInstall = false; |
688 | 0 | bool haveInstallAfterSubdirectory = false; |
689 | 0 | if (status == cmPolicies::WARN) { |
690 | 0 | for (auto const& installer : installers) { |
691 | 0 | installer->CheckCMP0082(haveSubdirectoryInstall, |
692 | 0 | haveInstallAfterSubdirectory); |
693 | 0 | installer->Generate(fout, config, configurationTypes); |
694 | 0 | } |
695 | 0 | } else { |
696 | 0 | for (auto const& installer : installers) { |
697 | 0 | installer->Generate(fout, config, configurationTypes); |
698 | 0 | } |
699 | 0 | } |
700 | | |
701 | | // Write rules from old-style specification stored in targets. |
702 | 0 | this->GenerateTargetInstallRules(fout, config, configurationTypes); |
703 | | |
704 | | // Include install scripts from subdirectories. |
705 | 0 | switch (status) { |
706 | 0 | case cmPolicies::WARN: |
707 | 0 | if (haveInstallAfterSubdirectory && |
708 | 0 | this->Makefile->PolicyOptionalWarningEnabled( |
709 | 0 | "CMAKE_POLICY_WARNING_CMP0082")) { |
710 | 0 | std::ostringstream e; |
711 | 0 | e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0082) << "\n"; |
712 | 0 | this->IssueMessage(MessageType::AUTHOR_WARNING, e.str()); |
713 | 0 | } |
714 | 0 | CM_FALLTHROUGH; |
715 | 0 | case cmPolicies::OLD: { |
716 | 0 | std::vector<cmStateSnapshot> children = |
717 | 0 | this->Makefile->GetStateSnapshot().GetChildren(); |
718 | 0 | if (!children.empty()) { |
719 | 0 | fout << "if(NOT CMAKE_INSTALL_LOCAL_ONLY)\n"; |
720 | 0 | fout << " # Include the install script for each subdirectory.\n"; |
721 | 0 | for (cmStateSnapshot const& c : children) { |
722 | 0 | if (!c.GetDirectory().GetPropertyAsBool("EXCLUDE_FROM_ALL")) { |
723 | 0 | std::string odir = c.GetDirectory().GetCurrentBinary(); |
724 | 0 | cmSystemTools::ConvertToUnixSlashes(odir); |
725 | 0 | fout << " include(\"" << odir << "/cmake_install.cmake\")\n"; |
726 | 0 | } |
727 | 0 | } |
728 | 0 | fout << "\n"; |
729 | 0 | fout << "endif()\n\n"; |
730 | 0 | } |
731 | 0 | } break; |
732 | | |
733 | 0 | case cmPolicies::NEW: |
734 | | // NEW behavior is handled in |
735 | | // cmInstallSubdirectoryGenerator::GenerateScript() |
736 | 0 | break; |
737 | 0 | } |
738 | | |
739 | | /* clang-format off */ |
740 | | |
741 | 0 | fout << |
742 | 0 | "string(REPLACE \";\" \"\\n\" CMAKE_INSTALL_MANIFEST_CONTENT\n" |
743 | 0 | " \"${CMAKE_INSTALL_MANIFEST_FILES}\")\n" |
744 | 0 | "if(CMAKE_INSTALL_LOCAL_ONLY)\n" |
745 | 0 | " file(WRITE \"" << |
746 | 0 | this->StateSnapshot.GetDirectory().GetCurrentBinary() << |
747 | 0 | "/install_local_manifest.txt\"\n" |
748 | 0 | " \"${CMAKE_INSTALL_MANIFEST_CONTENT}\")\n" |
749 | 0 | "endif()\n"; |
750 | |
|
751 | 0 | if (toplevel_install) { |
752 | 0 | fout << |
753 | 0 | "if(CMAKE_INSTALL_COMPONENT)\n" |
754 | 0 | " if(CMAKE_INSTALL_COMPONENT MATCHES \"^[a-zA-Z0-9_.+-]+$\")\n" |
755 | 0 | " set(CMAKE_INSTALL_MANIFEST \"install_manifest_" |
756 | 0 | "${CMAKE_INSTALL_COMPONENT}.txt\")\n" |
757 | 0 | " else()\n" |
758 | 0 | " string(MD5 CMAKE_INST_COMP_HASH \"${CMAKE_INSTALL_COMPONENT}\")\n" |
759 | 0 | " set(CMAKE_INSTALL_MANIFEST \"install_manifest_" |
760 | 0 | "${CMAKE_INST_COMP_HASH}.txt\")\n" |
761 | 0 | " unset(CMAKE_INST_COMP_HASH)\n" |
762 | 0 | " endif()\n" |
763 | 0 | "else()\n" |
764 | 0 | " set(CMAKE_INSTALL_MANIFEST \"install_manifest.txt\")\n" |
765 | 0 | "endif()\n" |
766 | 0 | "\n" |
767 | 0 | "if(NOT CMAKE_INSTALL_LOCAL_ONLY)\n" |
768 | 0 | " file(WRITE \"" << homedir << "/${CMAKE_INSTALL_MANIFEST}\"\n" |
769 | 0 | " \"${CMAKE_INSTALL_MANIFEST_CONTENT}\")\n" |
770 | 0 | "endif()\n"; |
771 | 0 | } |
772 | | /* clang-format on */ |
773 | 0 | } |
774 | | |
775 | | void cmLocalGenerator::AddGeneratorTarget( |
776 | | std::unique_ptr<cmGeneratorTarget> gt) |
777 | 0 | { |
778 | 0 | cmGeneratorTarget* gt_ptr = gt.get(); |
779 | |
|
780 | 0 | this->GeneratorTargets.push_back(std::move(gt)); |
781 | 0 | this->GeneratorTargetSearchIndex.emplace(gt_ptr->GetName(), gt_ptr); |
782 | 0 | this->GlobalGenerator->IndexGeneratorTarget(gt_ptr); |
783 | 0 | } |
784 | | |
785 | | void cmLocalGenerator::AddImportedGeneratorTarget(cmGeneratorTarget* gt) |
786 | 0 | { |
787 | 0 | this->ImportedGeneratorTargets.emplace(gt->GetName(), gt); |
788 | 0 | this->GlobalGenerator->IndexGeneratorTarget(gt); |
789 | 0 | } |
790 | | |
791 | | void cmLocalGenerator::AddOwnedImportedGeneratorTarget( |
792 | | std::unique_ptr<cmGeneratorTarget> gt) |
793 | 0 | { |
794 | 0 | this->OwnedImportedGeneratorTargets.push_back(std::move(gt)); |
795 | 0 | } |
796 | | |
797 | | cmGeneratorTarget* cmLocalGenerator::FindLocalNonAliasGeneratorTarget( |
798 | | std::string const& name) const |
799 | 0 | { |
800 | 0 | auto ti = this->GeneratorTargetSearchIndex.find(name); |
801 | 0 | if (ti != this->GeneratorTargetSearchIndex.end()) { |
802 | 0 | return ti->second; |
803 | 0 | } |
804 | 0 | return nullptr; |
805 | 0 | } |
806 | | |
807 | | void cmLocalGenerator::ComputeTargetManifest() |
808 | 0 | { |
809 | | // Collect the set of configuration types. |
810 | 0 | std::vector<std::string> configNames = |
811 | 0 | this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); |
812 | | |
813 | | // Add our targets to the manifest for each configuration. |
814 | 0 | auto const& targets = this->GetGeneratorTargets(); |
815 | 0 | for (auto const& target : targets) { |
816 | 0 | if (!target->IsInBuildSystem()) { |
817 | 0 | continue; |
818 | 0 | } |
819 | 0 | for (std::string const& c : configNames) { |
820 | 0 | target->ComputeTargetManifest(c); |
821 | 0 | } |
822 | 0 | } |
823 | 0 | } |
824 | | |
825 | | bool cmLocalGenerator::ComputeTargetCompileFeatures() |
826 | 0 | { |
827 | | // Collect the set of configuration types. |
828 | 0 | std::vector<std::string> configNames = |
829 | 0 | this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); |
830 | |
|
831 | 0 | using LanguagePair = std::pair<std::string, std::string>; |
832 | 0 | std::vector<LanguagePair> pairedLanguages{ |
833 | 0 | { "OBJC", "C" }, { "OBJCXX", "CXX" }, { "CUDA", "CXX" }, { "HIP", "CXX" } |
834 | 0 | }; |
835 | 0 | std::set<LanguagePair> inferredEnabledLanguages; |
836 | 0 | for (auto const& lang : pairedLanguages) { |
837 | 0 | if (this->Makefile->GetState()->GetLanguageEnabled(lang.first)) { |
838 | 0 | inferredEnabledLanguages.insert(lang); |
839 | 0 | } |
840 | 0 | } |
841 | | |
842 | | // Process compile features of all targets. |
843 | 0 | auto const& targets = this->GetGeneratorTargets(); |
844 | 0 | for (auto const& target : targets) { |
845 | 0 | for (std::string const& c : configNames) { |
846 | 0 | if (!target->ComputeCompileFeatures(c)) { |
847 | 0 | return false; |
848 | 0 | } |
849 | 0 | } |
850 | | |
851 | | // Now that C/C++ _STANDARD values have been computed |
852 | | // set the values to ObjC/ObjCXX _STANDARD variables |
853 | 0 | if (target->CanCompileSources()) { |
854 | 0 | for (std::string const& c : configNames) { |
855 | 0 | target->ComputeCompileFeatures(c, inferredEnabledLanguages); |
856 | 0 | } |
857 | 0 | } |
858 | 0 | } |
859 | | |
860 | 0 | return true; |
861 | 0 | } |
862 | | |
863 | | bool cmLocalGenerator::IsRootMakefile() const |
864 | 0 | { |
865 | 0 | return !this->StateSnapshot.GetBuildsystemDirectoryParent().IsValid(); |
866 | 0 | } |
867 | | |
868 | | cmState* cmLocalGenerator::GetState() const |
869 | 0 | { |
870 | 0 | return this->GlobalGenerator->GetCMakeInstance()->GetState(); |
871 | 0 | } |
872 | | |
873 | | cmStateSnapshot cmLocalGenerator::GetStateSnapshot() const |
874 | 0 | { |
875 | 0 | return this->Makefile->GetStateSnapshot(); |
876 | 0 | } |
877 | | |
878 | | std::string cmLocalGenerator::GetRuleLauncher(cmGeneratorTarget* target, |
879 | | std::string const& prop, |
880 | | std::string const& config) |
881 | 0 | { |
882 | 0 | cmValue value = this->Makefile->GetProperty(prop); |
883 | 0 | if (target) { |
884 | 0 | value = target->GetProperty(prop); |
885 | 0 | } |
886 | 0 | if (value) { |
887 | 0 | return cmGeneratorExpression::Evaluate(*value, this, config, target); |
888 | 0 | } |
889 | 0 | return ""; |
890 | 0 | } |
891 | | |
892 | | std::string cmLocalGenerator::ConvertToIncludeReference( |
893 | | std::string const& path, OutputFormat format) |
894 | 0 | { |
895 | 0 | return this->ConvertToOutputForExisting(path, format); |
896 | 0 | } |
897 | | |
898 | | std::string cmLocalGenerator::GetIncludeFlags( |
899 | | std::vector<std::string> const& includeDirs, cmGeneratorTarget* target, |
900 | | std::string const& lang, std::string const& config, bool forResponseFile) |
901 | 0 | { |
902 | 0 | if (lang.empty()) { |
903 | 0 | return ""; |
904 | 0 | } |
905 | | |
906 | 0 | std::vector<std::string> includes = includeDirs; |
907 | 0 | MoveSystemIncludesToEnd(includes, config, lang, target); |
908 | |
|
909 | 0 | OutputFormat shellFormat = forResponseFile ? RESPONSE : SHELL; |
910 | 0 | std::ostringstream includeFlags; |
911 | |
|
912 | 0 | std::string const& includeFlag = |
913 | 0 | this->Makefile->GetSafeDefinition(cmStrCat("CMAKE_INCLUDE_FLAG_", lang)); |
914 | 0 | bool quotePaths = false; |
915 | 0 | if (this->Makefile->GetDefinition("CMAKE_QUOTE_INCLUDE_PATHS")) { |
916 | 0 | quotePaths = true; |
917 | 0 | } |
918 | 0 | std::string sep = " "; |
919 | 0 | bool repeatFlag = true; |
920 | | // should the include flag be repeated like ie. -IA -IB |
921 | 0 | if (cmValue incSep = this->Makefile->GetDefinition( |
922 | 0 | cmStrCat("CMAKE_INCLUDE_FLAG_SEP_", lang))) { |
923 | | // if there is a separator then the flag is not repeated but is only |
924 | | // given once i.e. -classpath a:b:c |
925 | 0 | sep = *incSep; |
926 | 0 | repeatFlag = false; |
927 | 0 | } |
928 | | |
929 | | // Support special system include flag if it is available and the |
930 | | // normal flag is repeated for each directory. |
931 | 0 | cmValue sysIncludeFlag = nullptr; |
932 | 0 | cmValue sysIncludeFlagWarning = nullptr; |
933 | 0 | if (repeatFlag) { |
934 | 0 | sysIncludeFlag = this->Makefile->GetDefinition( |
935 | 0 | cmStrCat("CMAKE_INCLUDE_SYSTEM_FLAG_", lang)); |
936 | 0 | sysIncludeFlagWarning = this->Makefile->GetDefinition( |
937 | 0 | cmStrCat("CMAKE_INCLUDE_SYSTEM_FLAG_", lang, "_WARNING")); |
938 | 0 | } |
939 | |
|
940 | 0 | cmValue fwSearchFlag = this->Makefile->GetDefinition( |
941 | 0 | cmStrCat("CMAKE_", lang, "_FRAMEWORK_SEARCH_FLAG")); |
942 | 0 | cmValue sysFwSearchFlag = this->Makefile->GetDefinition( |
943 | 0 | cmStrCat("CMAKE_", lang, "_SYSTEM_FRAMEWORK_SEARCH_FLAG")); |
944 | |
|
945 | 0 | bool flagUsed = false; |
946 | 0 | bool sysIncludeFlagUsed = false; |
947 | 0 | std::set<std::string> emitted; |
948 | | #ifdef __APPLE__ |
949 | | emitted.insert("/System/Library/Frameworks"); |
950 | | #endif |
951 | 0 | for (std::string const& i : includes) { |
952 | 0 | if (cmNonempty(fwSearchFlag) && this->Makefile->IsOn("APPLE") && |
953 | 0 | cmSystemTools::IsPathToFramework(i)) { |
954 | 0 | std::string const frameworkDir = cmSystemTools::GetFilenamePath(i); |
955 | 0 | if (emitted.insert(frameworkDir).second) { |
956 | 0 | if (sysFwSearchFlag && target && |
957 | 0 | target->IsSystemIncludeDirectory(frameworkDir, config, lang)) { |
958 | 0 | includeFlags << *sysFwSearchFlag; |
959 | 0 | } else { |
960 | 0 | includeFlags << *fwSearchFlag; |
961 | 0 | } |
962 | 0 | includeFlags << this->ConvertToOutputFormat(frameworkDir, shellFormat) |
963 | 0 | << " "; |
964 | 0 | } |
965 | 0 | continue; |
966 | 0 | } |
967 | | |
968 | 0 | if (!flagUsed || repeatFlag) { |
969 | 0 | if (sysIncludeFlag && target && |
970 | 0 | target->IsSystemIncludeDirectory(i, config, lang)) { |
971 | 0 | includeFlags << *sysIncludeFlag; |
972 | 0 | sysIncludeFlagUsed = true; |
973 | 0 | } else { |
974 | 0 | includeFlags << includeFlag; |
975 | 0 | } |
976 | 0 | flagUsed = true; |
977 | 0 | } |
978 | 0 | std::string includePath = this->ConvertToIncludeReference(i, shellFormat); |
979 | 0 | if (quotePaths && !includePath.empty() && includePath.front() != '\"') { |
980 | 0 | includeFlags << "\""; |
981 | 0 | } |
982 | 0 | includeFlags << includePath; |
983 | 0 | if (quotePaths && !includePath.empty() && includePath.front() != '\"') { |
984 | 0 | includeFlags << "\""; |
985 | 0 | } |
986 | 0 | includeFlags << sep; |
987 | 0 | } |
988 | 0 | if (sysIncludeFlagUsed && sysIncludeFlagWarning) { |
989 | 0 | includeFlags << *sysIncludeFlagWarning; |
990 | 0 | } |
991 | 0 | std::string flags = includeFlags.str(); |
992 | | // remove trailing separators |
993 | 0 | if ((sep[0] != ' ') && !flags.empty() && flags.back() == sep[0]) { |
994 | 0 | flags.back() = ' '; |
995 | 0 | } |
996 | 0 | return cmTrimWhitespace(flags); |
997 | 0 | } |
998 | | |
999 | | void cmLocalGenerator::AddCompileOptions(std::string& flags, |
1000 | | cmGeneratorTarget* target, |
1001 | | std::string const& lang, |
1002 | | std::string const& config) |
1003 | 0 | { |
1004 | 0 | std::vector<BT<std::string>> tmpFlags; |
1005 | 0 | this->AddCompileOptions(tmpFlags, target, lang, config); |
1006 | 0 | this->AppendFlags(flags, tmpFlags); |
1007 | 0 | } |
1008 | | |
1009 | | void cmLocalGenerator::AddCompileOptions(std::vector<BT<std::string>>& flags, |
1010 | | cmGeneratorTarget* target, |
1011 | | std::string const& lang, |
1012 | | std::string const& config) |
1013 | 0 | { |
1014 | 0 | std::string langFlagRegexVar = cmStrCat("CMAKE_", lang, "_FLAG_REGEX"); |
1015 | |
|
1016 | 0 | if (cmValue langFlagRegexStr = |
1017 | 0 | this->Makefile->GetDefinition(langFlagRegexVar)) { |
1018 | | // Filter flags acceptable to this language. |
1019 | 0 | if (cmValue targetFlags = target->GetProperty("COMPILE_FLAGS")) { |
1020 | 0 | std::vector<std::string> opts; |
1021 | 0 | cmSystemTools::ParseWindowsCommandLine(targetFlags->c_str(), opts); |
1022 | | // Re-escape these flags since COMPILE_FLAGS were already parsed |
1023 | | // as a command line above. |
1024 | 0 | std::string compileOpts; |
1025 | 0 | this->AppendCompileOptions(compileOpts, opts, langFlagRegexStr->c_str()); |
1026 | 0 | if (!compileOpts.empty()) { |
1027 | 0 | flags.emplace_back(std::move(compileOpts)); |
1028 | 0 | } |
1029 | 0 | } |
1030 | 0 | std::vector<BT<std::string>> targetCompileOpts = |
1031 | 0 | target->GetCompileOptions(config, lang); |
1032 | | // COMPILE_OPTIONS are escaped. |
1033 | 0 | this->AppendCompileOptions(flags, targetCompileOpts, |
1034 | 0 | langFlagRegexStr->c_str()); |
1035 | 0 | } else { |
1036 | | // Use all flags. |
1037 | 0 | if (cmValue targetFlags = target->GetProperty("COMPILE_FLAGS")) { |
1038 | | // COMPILE_FLAGS are not escaped for historical reasons. |
1039 | 0 | std::string compileFlags; |
1040 | 0 | this->AppendFlags(compileFlags, *targetFlags); |
1041 | 0 | if (!compileFlags.empty()) { |
1042 | 0 | flags.emplace_back(std::move(compileFlags)); |
1043 | 0 | } |
1044 | 0 | } |
1045 | 0 | std::vector<BT<std::string>> targetCompileOpts = |
1046 | 0 | target->GetCompileOptions(config, lang); |
1047 | | // COMPILE_OPTIONS are escaped. |
1048 | 0 | this->AppendCompileOptions(flags, targetCompileOpts); |
1049 | 0 | } |
1050 | |
|
1051 | 0 | cmStandardLevelResolver standardResolver(this->Makefile); |
1052 | 0 | for (auto const& it : target->GetMaxLanguageStandards()) { |
1053 | 0 | cmValue standard = target->GetLanguageStandard(it.first, config); |
1054 | 0 | if (!standard) { |
1055 | 0 | continue; |
1056 | 0 | } |
1057 | 0 | if (standardResolver.IsLaterStandard(it.first, *standard, it.second)) { |
1058 | 0 | std::ostringstream e; |
1059 | 0 | e << "The COMPILE_FEATURES property of target \"" << target->GetName() |
1060 | 0 | << "\" was evaluated when computing the link " |
1061 | 0 | "implementation, and the \"" |
1062 | 0 | << it.first << "_STANDARD\" was \"" << it.second |
1063 | 0 | << "\" for that computation. Computing the " |
1064 | 0 | "COMPILE_FEATURES based on the link implementation resulted in a " |
1065 | 0 | "higher \"" |
1066 | 0 | << it.first << "_STANDARD\" \"" << *standard |
1067 | 0 | << "\". " |
1068 | 0 | "This is not permitted. The COMPILE_FEATURES may not both depend " |
1069 | 0 | "on " |
1070 | 0 | "and be depended on by the link implementation.\n"; |
1071 | 0 | this->IssueMessage(MessageType::FATAL_ERROR, e.str()); |
1072 | 0 | return; |
1073 | 0 | } |
1074 | 0 | } |
1075 | | |
1076 | | // Add Warning as errors flags |
1077 | 0 | if (!this->GetCMakeInstance()->GetIgnoreCompileWarningAsError()) { |
1078 | 0 | cmValue const wError = target->GetProperty("COMPILE_WARNING_AS_ERROR"); |
1079 | 0 | cmValue const wErrorOpts = this->Makefile->GetDefinition( |
1080 | 0 | cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_WARNING_AS_ERROR")); |
1081 | 0 | if (wError.IsOn() && wErrorOpts.IsSet()) { |
1082 | 0 | std::string wErrorFlags; |
1083 | 0 | this->AppendCompileOptions(wErrorFlags, *wErrorOpts); |
1084 | 0 | if (!wErrorFlags.empty()) { |
1085 | 0 | flags.emplace_back(std::move(wErrorFlags)); |
1086 | 0 | } |
1087 | 0 | } |
1088 | 0 | } |
1089 | | |
1090 | | // Add compile flag for the MSVC compiler only. |
1091 | 0 | cmMakefile* mf = this->GetMakefile(); |
1092 | 0 | if (cmValue jmc = |
1093 | 0 | mf->GetDefinition("CMAKE_" + lang + "_COMPILE_OPTIONS_JMC")) { |
1094 | | |
1095 | | // Handle Just My Code debugging flags, /JMC. |
1096 | | // If the target is a Managed C++ one, /JMC is not compatible. |
1097 | 0 | if (target->GetManagedType(config) != |
1098 | 0 | cmGeneratorTarget::ManagedType::Managed) { |
1099 | | // add /JMC flags if target property VS_JUST_MY_CODE_DEBUGGING is set |
1100 | | // to ON |
1101 | 0 | if (cmValue jmcExprGen = |
1102 | 0 | target->GetProperty("VS_JUST_MY_CODE_DEBUGGING")) { |
1103 | 0 | std::string isJMCEnabled = |
1104 | 0 | cmGeneratorExpression::Evaluate(*jmcExprGen, this, config); |
1105 | 0 | if (cmIsOn(isJMCEnabled)) { |
1106 | 0 | cmList optList{ *jmc }; |
1107 | 0 | std::string jmcFlags; |
1108 | 0 | this->AppendCompileOptions(jmcFlags, optList); |
1109 | 0 | if (!jmcFlags.empty()) { |
1110 | 0 | flags.emplace_back(std::move(jmcFlags)); |
1111 | 0 | } |
1112 | 0 | } |
1113 | 0 | } |
1114 | 0 | } |
1115 | 0 | } |
1116 | 0 | } |
1117 | | |
1118 | | cmTarget* cmLocalGenerator::AddCustomCommandToTarget( |
1119 | | std::string const& target, cmCustomCommandType type, |
1120 | | std::unique_ptr<cmCustomCommand> cc, cmObjectLibraryCommands objLibCommands) |
1121 | 0 | { |
1122 | 0 | cmTarget* t = this->Makefile->GetCustomCommandTarget( |
1123 | 0 | target, objLibCommands, this->DirectoryBacktrace); |
1124 | 0 | if (!t) { |
1125 | 0 | return nullptr; |
1126 | 0 | } |
1127 | | |
1128 | 0 | cc->SetBacktrace(this->DirectoryBacktrace); |
1129 | |
|
1130 | 0 | detail::AddCustomCommandToTarget(*this, cmCommandOrigin::Generator, t, type, |
1131 | 0 | std::move(cc)); |
1132 | |
|
1133 | 0 | return t; |
1134 | 0 | } |
1135 | | |
1136 | | cmSourceFile* cmLocalGenerator::AddCustomCommandToOutput( |
1137 | | std::unique_ptr<cmCustomCommand> cc, bool replace) |
1138 | 0 | { |
1139 | | // Make sure there is at least one output. |
1140 | 0 | if (cc->GetOutputs().empty()) { |
1141 | 0 | cmSystemTools::Error("Attempt to add a custom rule with no output!"); |
1142 | 0 | return nullptr; |
1143 | 0 | } |
1144 | | |
1145 | 0 | cc->SetBacktrace(this->DirectoryBacktrace); |
1146 | 0 | return detail::AddCustomCommandToOutput(*this, cmCommandOrigin::Generator, |
1147 | 0 | std::move(cc), replace); |
1148 | 0 | } |
1149 | | |
1150 | | cmTarget* cmLocalGenerator::AddUtilityCommand( |
1151 | | std::string const& utilityName, bool excludeFromAll, |
1152 | | std::unique_ptr<cmCustomCommand> cc) |
1153 | 0 | { |
1154 | 0 | cmTarget* target = |
1155 | 0 | this->Makefile->AddNewUtilityTarget(utilityName, excludeFromAll); |
1156 | 0 | target->SetIsGeneratorProvided(true); |
1157 | |
|
1158 | 0 | if (cc->GetCommandLines().empty() && cc->GetDepends().empty()) { |
1159 | 0 | return target; |
1160 | 0 | } |
1161 | | |
1162 | 0 | cc->SetBacktrace(this->DirectoryBacktrace); |
1163 | 0 | detail::AddUtilityCommand(*this, cmCommandOrigin::Generator, target, |
1164 | 0 | std::move(cc)); |
1165 | |
|
1166 | 0 | return target; |
1167 | 0 | } |
1168 | | |
1169 | | std::vector<BT<std::string>> cmLocalGenerator::GetIncludeDirectoriesImplicit( |
1170 | | cmGeneratorTarget const* target, std::string const& lang, |
1171 | | std::string const& config, bool stripImplicitDirs, |
1172 | | bool appendAllImplicitDirs) const |
1173 | 0 | { |
1174 | 0 | std::vector<BT<std::string>> result; |
1175 | | // Do not repeat an include path. |
1176 | 0 | std::set<std::string> emitted; |
1177 | |
|
1178 | 0 | auto emitDir = [&result, &emitted](std::string const& dir) { |
1179 | 0 | if (emitted.insert(dir).second) { |
1180 | 0 | result.emplace_back(dir); |
1181 | 0 | } |
1182 | 0 | }; |
1183 | |
|
1184 | 0 | auto emitBT = [&result, &emitted](BT<std::string> const& dir) { |
1185 | 0 | if (emitted.insert(dir.Value).second) { |
1186 | 0 | result.emplace_back(dir); |
1187 | 0 | } |
1188 | 0 | }; |
1189 | | |
1190 | | // When automatic include directories are requested for a build then |
1191 | | // include the source and binary directories at the beginning of the |
1192 | | // include path to approximate include file behavior for an |
1193 | | // in-source build. This does not account for the case of a source |
1194 | | // file in a subdirectory of the current source directory but we |
1195 | | // cannot fix this because not all native build tools support |
1196 | | // per-source-file include paths. |
1197 | 0 | if (this->Makefile->IsOn("CMAKE_INCLUDE_CURRENT_DIR")) { |
1198 | | // Current binary directory |
1199 | 0 | emitDir(this->StateSnapshot.GetDirectory().GetCurrentBinary()); |
1200 | | // Current source directory |
1201 | 0 | emitDir(this->StateSnapshot.GetDirectory().GetCurrentSource()); |
1202 | 0 | } |
1203 | |
|
1204 | 0 | if (!target) { |
1205 | 0 | return result; |
1206 | 0 | } |
1207 | | |
1208 | | // Standard include directories to be added unconditionally at the end. |
1209 | | // These are intended to simulate additional implicit include directories. |
1210 | 0 | cmList userStandardDirs; |
1211 | 0 | { |
1212 | 0 | std::string const value = this->Makefile->GetSafeDefinition( |
1213 | 0 | cmStrCat("CMAKE_", lang, "_STANDARD_INCLUDE_DIRECTORIES")); |
1214 | 0 | userStandardDirs.assign(value); |
1215 | 0 | for (std::string& usd : userStandardDirs) { |
1216 | 0 | cmSystemTools::ConvertToUnixSlashes(usd); |
1217 | 0 | } |
1218 | 0 | } |
1219 | | |
1220 | | // Implicit include directories |
1221 | 0 | std::vector<std::string> implicitDirs; |
1222 | 0 | std::set<std::string> implicitSet; |
1223 | | // Include directories to be excluded as if they were implicit. |
1224 | 0 | std::set<std::string> implicitExclude; |
1225 | 0 | { |
1226 | | // Raw list of implicit include directories |
1227 | | // Start with "standard" directories that we unconditionally add below. |
1228 | 0 | std::vector<std::string> impDirVec = userStandardDirs; |
1229 | | |
1230 | | // Load implicit include directories for this language. |
1231 | | // We ignore this for Fortran because: |
1232 | | // * There are no standard library headers to avoid overriding. |
1233 | | // * Compilers like gfortran do not search their own implicit include |
1234 | | // directories for modules ('.mod' files). |
1235 | 0 | if (lang != "Fortran") { |
1236 | 0 | size_t const impDirVecOldSize = impDirVec.size(); |
1237 | 0 | cmList::append(impDirVec, |
1238 | 0 | this->Makefile->GetDefinition(cmStrCat( |
1239 | 0 | "CMAKE_", lang, "_IMPLICIT_INCLUDE_DIRECTORIES"))); |
1240 | | // FIXME: Use cmRange with 'advance()' when it supports non-const. |
1241 | 0 | for (size_t i = impDirVecOldSize; i < impDirVec.size(); ++i) { |
1242 | 0 | cmSystemTools::ConvertToUnixSlashes(impDirVec[i]); |
1243 | 0 | } |
1244 | | |
1245 | | // The CMAKE_<LANG>_IMPLICIT_INCLUDE_DIRECTORIES are computed using |
1246 | | // try_compile in CMAKE_DETERMINE_COMPILER_ABI, but the implicit include |
1247 | | // directories are not known during that try_compile. This can be a |
1248 | | // problem when the HIP runtime include path is /usr/include because the |
1249 | | // runtime include path is always added to the userDirs and the compiler |
1250 | | // includes standard library headers via "__clang_hip_runtime_wrapper.h". |
1251 | 0 | if (lang == "HIP" && impDirVec.size() == impDirVecOldSize && |
1252 | 0 | !cm::contains(impDirVec, "/usr/include")) { |
1253 | 0 | implicitExclude.emplace("/usr/include"); |
1254 | 0 | } |
1255 | 0 | } |
1256 | | |
1257 | | // The Platform/UnixPaths module used to hard-code /usr/include for C, CXX, |
1258 | | // and CUDA in CMAKE_<LANG>_IMPLICIT_INCLUDE_DIRECTORIES, but those |
1259 | | // variables are now computed. On macOS the /usr/include directory is |
1260 | | // inside the platform SDK so the computed value does not contain it |
1261 | | // directly. In this case adding -I/usr/include can hide SDK headers so we |
1262 | | // must still exclude it. |
1263 | 0 | if ((lang == "C" || lang == "CXX" || lang == "CUDA") && |
1264 | 0 | !cm::contains(impDirVec, "/usr/include") && |
1265 | 0 | std::find_if(impDirVec.begin(), impDirVec.end(), |
1266 | 0 | [](std::string const& d) { |
1267 | 0 | return cmHasLiteralSuffix(d, "/usr/include"); |
1268 | 0 | }) != impDirVec.end()) { |
1269 | | // Only exclude this hard coded path for backwards compatibility. |
1270 | 0 | implicitExclude.emplace("/usr/include"); |
1271 | 0 | } |
1272 | |
|
1273 | 0 | for (std::string const& i : impDirVec) { |
1274 | 0 | if (implicitSet.insert(this->GlobalGenerator->GetRealPath(i)).second) { |
1275 | 0 | implicitDirs.emplace_back(i); |
1276 | 0 | } |
1277 | 0 | } |
1278 | 0 | } |
1279 | |
|
1280 | 0 | bool const isCorCxx = (lang == "C" || lang == "CXX"); |
1281 | | |
1282 | | // Resolve symlinks in CPATH for comparison with resolved include paths. |
1283 | | // We do this here instead of when EnvCPATH is populated in case symlinks |
1284 | | // on disk have changed in the meantime. |
1285 | 0 | std::set<std::string> resolvedEnvCPATH; |
1286 | 0 | if (isCorCxx) { |
1287 | 0 | for (std::string const& i : this->EnvCPATH) { |
1288 | 0 | resolvedEnvCPATH.emplace(this->GlobalGenerator->GetRealPath(i)); |
1289 | 0 | } |
1290 | 0 | } |
1291 | | |
1292 | | // Checks if this is not an excluded (implicit) include directory. |
1293 | 0 | auto notExcluded = [this, &implicitSet, &implicitExclude, &resolvedEnvCPATH, |
1294 | 0 | isCorCxx](std::string const& dir) -> bool { |
1295 | 0 | std::string const& real_dir = this->GlobalGenerator->GetRealPath(dir); |
1296 | 0 | return |
1297 | | // Do not exclude directories that are not in any excluded set. |
1298 | 0 | !(cm::contains(implicitSet, real_dir) || |
1299 | 0 | cm::contains(implicitExclude, dir)) |
1300 | | // Do not exclude entries of the CPATH environment variable even though |
1301 | | // they are implicitly searched by the compiler. They are meant to be |
1302 | | // user-specified directories that can be re-ordered or converted to |
1303 | | // -isystem without breaking real compiler builtin headers. |
1304 | 0 | || (isCorCxx && cm::contains(resolvedEnvCPATH, real_dir)); |
1305 | 0 | }; |
1306 | | |
1307 | | // Get the target-specific include directories. |
1308 | 0 | std::vector<BT<std::string>> userDirs = |
1309 | 0 | target->GetIncludeDirectories(config, lang); |
1310 | | |
1311 | | // Support putting all the in-project include directories first if |
1312 | | // it is requested by the project. |
1313 | 0 | if (this->Makefile->IsOn("CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE")) { |
1314 | 0 | std::string const& topSourceDir = this->GetState()->GetSourceDirectory(); |
1315 | 0 | std::string const& topBinaryDir = this->GetState()->GetBinaryDirectory(); |
1316 | 0 | for (BT<std::string> const& udr : userDirs) { |
1317 | | // Emit this directory only if it is a subdirectory of the |
1318 | | // top-level source or binary tree. |
1319 | 0 | if (cmSystemTools::ComparePath(udr.Value, topSourceDir) || |
1320 | 0 | cmSystemTools::ComparePath(udr.Value, topBinaryDir) || |
1321 | 0 | cmSystemTools::IsSubDirectory(udr.Value, topSourceDir) || |
1322 | 0 | cmSystemTools::IsSubDirectory(udr.Value, topBinaryDir)) { |
1323 | 0 | if (notExcluded(udr.Value)) { |
1324 | 0 | emitBT(udr); |
1325 | 0 | } |
1326 | 0 | } |
1327 | 0 | } |
1328 | 0 | } |
1329 | | |
1330 | | // Emit remaining non implicit user directories. |
1331 | 0 | for (BT<std::string> const& udr : userDirs) { |
1332 | 0 | if (notExcluded(udr.Value)) { |
1333 | 0 | emitBT(udr); |
1334 | 0 | } |
1335 | 0 | } |
1336 | | |
1337 | | // Sort result |
1338 | 0 | MoveSystemIncludesToEnd(result, config, lang, target); |
1339 | | |
1340 | | // Append standard include directories for this language. |
1341 | 0 | userDirs.reserve(userDirs.size() + userStandardDirs.size()); |
1342 | 0 | for (std::string& usd : userStandardDirs) { |
1343 | 0 | emitDir(usd); |
1344 | 0 | userDirs.emplace_back(std::move(usd)); |
1345 | 0 | } |
1346 | | |
1347 | | // Append compiler implicit include directories |
1348 | 0 | if (!stripImplicitDirs) { |
1349 | | // Append implicit directories that were requested by the user only |
1350 | 0 | for (BT<std::string> const& udr : userDirs) { |
1351 | 0 | if (cm::contains(implicitSet, cmSystemTools::GetRealPath(udr.Value))) { |
1352 | 0 | emitBT(udr); |
1353 | 0 | } |
1354 | 0 | } |
1355 | | // Append remaining implicit directories (on demand) |
1356 | 0 | if (appendAllImplicitDirs) { |
1357 | 0 | for (std::string& imd : implicitDirs) { |
1358 | 0 | emitDir(imd); |
1359 | 0 | } |
1360 | 0 | } |
1361 | 0 | } |
1362 | |
|
1363 | 0 | return result; |
1364 | 0 | } |
1365 | | |
1366 | | void cmLocalGenerator::GetIncludeDirectoriesImplicit( |
1367 | | std::vector<std::string>& dirs, cmGeneratorTarget const* target, |
1368 | | std::string const& lang, std::string const& config, bool stripImplicitDirs, |
1369 | | bool appendAllImplicitDirs) const |
1370 | 0 | { |
1371 | 0 | std::vector<BT<std::string>> tmp = this->GetIncludeDirectoriesImplicit( |
1372 | 0 | target, lang, config, stripImplicitDirs, appendAllImplicitDirs); |
1373 | 0 | dirs.reserve(dirs.size() + tmp.size()); |
1374 | 0 | for (BT<std::string>& v : tmp) { |
1375 | 0 | dirs.emplace_back(std::move(v.Value)); |
1376 | 0 | } |
1377 | 0 | } |
1378 | | |
1379 | | std::vector<BT<std::string>> cmLocalGenerator::GetIncludeDirectories( |
1380 | | cmGeneratorTarget const* target, std::string const& lang, |
1381 | | std::string const& config) const |
1382 | 0 | { |
1383 | 0 | return this->GetIncludeDirectoriesImplicit(target, lang, config); |
1384 | 0 | } |
1385 | | |
1386 | | void cmLocalGenerator::GetIncludeDirectories(std::vector<std::string>& dirs, |
1387 | | cmGeneratorTarget const* target, |
1388 | | std::string const& lang, |
1389 | | std::string const& config) const |
1390 | 0 | { |
1391 | 0 | this->GetIncludeDirectoriesImplicit(dirs, target, lang, config); |
1392 | 0 | } |
1393 | | |
1394 | | void cmLocalGenerator::GetStaticLibraryFlags(std::string& flags, |
1395 | | std::string const& config, |
1396 | | std::string const& linkLanguage, |
1397 | | cmGeneratorTarget* target) |
1398 | 0 | { |
1399 | 0 | std::vector<BT<std::string>> tmpFlags = |
1400 | 0 | this->GetStaticLibraryFlags(config, linkLanguage, target); |
1401 | 0 | this->AppendFlags(flags, tmpFlags); |
1402 | 0 | } |
1403 | | |
1404 | | std::vector<BT<std::string>> cmLocalGenerator::GetStaticLibraryFlags( |
1405 | | std::string const& config, std::string const& linkLanguage, |
1406 | | cmGeneratorTarget* target) |
1407 | 0 | { |
1408 | 0 | std::vector<BT<std::string>> flags; |
1409 | 0 | if (linkLanguage != "Swift" && !this->IsSplitSwiftBuild()) { |
1410 | 0 | std::string staticLibFlags; |
1411 | 0 | this->AddConfigVariableFlags(staticLibFlags, "CMAKE_STATIC_LINKER_FLAGS", |
1412 | 0 | config); |
1413 | 0 | if (!staticLibFlags.empty()) { |
1414 | 0 | flags.emplace_back(std::move(staticLibFlags)); |
1415 | 0 | } |
1416 | 0 | } |
1417 | |
|
1418 | 0 | std::string staticLibFlags; |
1419 | 0 | std::string const configUpper = cmSystemTools::UpperCase(config); |
1420 | 0 | this->AppendFlags(staticLibFlags, |
1421 | 0 | target->GetSafeProperty("STATIC_LIBRARY_FLAGS")); |
1422 | 0 | if (!configUpper.empty()) { |
1423 | 0 | std::string name = "STATIC_LIBRARY_FLAGS_" + configUpper; |
1424 | 0 | this->AppendFlags(staticLibFlags, target->GetSafeProperty(name)); |
1425 | 0 | } |
1426 | |
|
1427 | 0 | if (!staticLibFlags.empty()) { |
1428 | 0 | flags.emplace_back(std::move(staticLibFlags)); |
1429 | 0 | } |
1430 | |
|
1431 | 0 | std::vector<BT<std::string>> staticLibOpts = |
1432 | 0 | target->GetStaticLibraryLinkOptions(config, linkLanguage); |
1433 | | // STATIC_LIBRARY_OPTIONS are escaped. |
1434 | 0 | this->AppendCompileOptions(flags, staticLibOpts); |
1435 | |
|
1436 | 0 | return flags; |
1437 | 0 | } |
1438 | | |
1439 | | void cmLocalGenerator::GetDeviceLinkFlags( |
1440 | | cmLinkLineDeviceComputer& linkLineComputer, std::string const& config, |
1441 | | std::string& linkLibs, std::string& linkFlags, std::string& frameworkPath, |
1442 | | std::string& linkPath, cmGeneratorTarget* target) |
1443 | 0 | { |
1444 | 0 | cmGeneratorTarget::DeviceLinkSetter setter(*target); |
1445 | |
|
1446 | 0 | cmComputeLinkInformation* pcli = target->GetLinkInformation(config); |
1447 | |
|
1448 | 0 | auto linklang = linkLineComputer.GetLinkerLanguage(target, config); |
1449 | 0 | auto ipoEnabled = target->IsIPOEnabled(linklang, config); |
1450 | 0 | if (!ipoEnabled && pcli) { |
1451 | 0 | ipoEnabled = linkLineComputer.ComputeRequiresDeviceLinkingIPOFlag(*pcli); |
1452 | 0 | } |
1453 | 0 | if (ipoEnabled) { |
1454 | 0 | if (cmValue cudaIPOFlags = this->Makefile->GetDefinition( |
1455 | 0 | "CMAKE_CUDA_DEVICE_LINK_OPTIONS_IPO")) { |
1456 | 0 | linkFlags += *cudaIPOFlags; |
1457 | 0 | } |
1458 | 0 | } |
1459 | |
|
1460 | 0 | if (pcli) { |
1461 | | // Compute the required device link libraries when |
1462 | | // resolving gpu lang device symbols |
1463 | 0 | this->OutputLinkLibraries(pcli, &linkLineComputer, linkLibs, frameworkPath, |
1464 | 0 | linkPath); |
1465 | 0 | } |
1466 | |
|
1467 | 0 | this->AddVisibilityPresetFlags(linkFlags, target, "CUDA"); |
1468 | 0 | this->GetGlobalGenerator()->EncodeLiteral(linkFlags); |
1469 | |
|
1470 | 0 | std::vector<std::string> linkOpts; |
1471 | 0 | target->GetLinkOptions(linkOpts, config, "CUDA"); |
1472 | 0 | this->SetLinkScriptShell(this->GetGlobalGenerator()->GetUseLinkScript()); |
1473 | | // LINK_OPTIONS are escaped. |
1474 | 0 | this->AppendCompileOptions(linkFlags, linkOpts); |
1475 | 0 | this->SetLinkScriptShell(false); |
1476 | 0 | } |
1477 | | |
1478 | | void cmLocalGenerator::GetTargetFlags( |
1479 | | cmLinkLineComputer* linkLineComputer, std::string const& config, |
1480 | | std::string& linkLibs, std::string& flags, std::string& linkFlags, |
1481 | | std::string& frameworkPath, std::string& linkPath, cmGeneratorTarget* target) |
1482 | 0 | { |
1483 | 0 | std::vector<BT<std::string>> linkFlagsList; |
1484 | 0 | std::vector<BT<std::string>> linkPathList; |
1485 | 0 | std::vector<BT<std::string>> linkLibsList; |
1486 | 0 | this->GetTargetFlags(linkLineComputer, config, linkLibsList, flags, |
1487 | 0 | linkFlagsList, frameworkPath, linkPathList, target); |
1488 | 0 | this->AppendFlags(linkFlags, linkFlagsList); |
1489 | 0 | this->AppendFlags(linkPath, linkPathList); |
1490 | 0 | this->AppendFlags(linkLibs, linkLibsList); |
1491 | 0 | } |
1492 | | |
1493 | | void cmLocalGenerator::GetTargetFlags( |
1494 | | cmLinkLineComputer* linkLineComputer, std::string const& config, |
1495 | | std::vector<BT<std::string>>& linkLibs, std::string& flags, |
1496 | | std::vector<BT<std::string>>& linkFlags, std::string& frameworkPath, |
1497 | | std::vector<BT<std::string>>& linkPath, cmGeneratorTarget* target) |
1498 | 0 | { |
1499 | 0 | std::string const configUpper = cmSystemTools::UpperCase(config); |
1500 | 0 | cmComputeLinkInformation* pcli = target->GetLinkInformation(config); |
1501 | |
|
1502 | 0 | std::string const linkLanguage = |
1503 | 0 | linkLineComputer->GetLinkerLanguage(target, config); |
1504 | |
|
1505 | 0 | { |
1506 | 0 | std::string createFlags; |
1507 | 0 | this->AppendTargetCreationLinkFlags(createFlags, target, linkLanguage); |
1508 | 0 | if (!createFlags.empty()) { |
1509 | 0 | linkFlags.emplace_back(std::move(createFlags)); |
1510 | 0 | } |
1511 | 0 | } |
1512 | |
|
1513 | 0 | switch (target->GetType()) { |
1514 | 0 | case cmStateEnums::STATIC_LIBRARY: |
1515 | 0 | linkFlags = this->GetStaticLibraryFlags(config, linkLanguage, target); |
1516 | 0 | break; |
1517 | 0 | case cmStateEnums::MODULE_LIBRARY: |
1518 | 0 | CM_FALLTHROUGH; |
1519 | 0 | case cmStateEnums::SHARED_LIBRARY: { |
1520 | 0 | if (this->IsSplitSwiftBuild() || linkLanguage != "Swift") { |
1521 | 0 | std::string libFlags; |
1522 | 0 | this->AddTargetTypeLinkerFlags(libFlags, target, linkLanguage, config); |
1523 | 0 | if (!libFlags.empty()) { |
1524 | 0 | linkFlags.emplace_back(std::move(libFlags)); |
1525 | 0 | } |
1526 | 0 | } |
1527 | |
|
1528 | 0 | std::string langLinkFlags; |
1529 | 0 | this->AddPerLanguageLinkFlags(langLinkFlags, target, linkLanguage, |
1530 | 0 | config); |
1531 | 0 | if (!langLinkFlags.empty()) { |
1532 | 0 | linkFlags.emplace_back(std::move(langLinkFlags)); |
1533 | 0 | } |
1534 | |
|
1535 | 0 | std::string sharedLibFlags; |
1536 | 0 | this->AddTargetPropertyLinkFlags(sharedLibFlags, target, config); |
1537 | 0 | if (!sharedLibFlags.empty()) { |
1538 | 0 | this->GetGlobalGenerator()->EncodeLiteral(sharedLibFlags); |
1539 | 0 | linkFlags.emplace_back(std::move(sharedLibFlags)); |
1540 | 0 | } |
1541 | |
|
1542 | 0 | std::vector<BT<std::string>> linkOpts = |
1543 | 0 | target->GetLinkOptions(config, linkLanguage); |
1544 | 0 | this->SetLinkScriptShell(this->GetGlobalGenerator()->GetUseLinkScript()); |
1545 | | // LINK_OPTIONS are escaped. |
1546 | 0 | this->AppendCompileOptions(linkFlags, linkOpts); |
1547 | 0 | this->SetLinkScriptShell(false); |
1548 | |
|
1549 | 0 | if (pcli) { |
1550 | 0 | this->OutputLinkLibraries(pcli, linkLineComputer, linkLibs, |
1551 | 0 | frameworkPath, linkPath); |
1552 | 0 | } |
1553 | 0 | } break; |
1554 | 0 | case cmStateEnums::EXECUTABLE: { |
1555 | 0 | if (linkLanguage != "Swift") { |
1556 | 0 | std::string exeFlags; |
1557 | 0 | this->AddTargetTypeLinkerFlags(exeFlags, target, linkLanguage, config); |
1558 | 0 | if (!exeFlags.empty()) { |
1559 | 0 | linkFlags.emplace_back(std::move(exeFlags)); |
1560 | 0 | } |
1561 | 0 | } |
1562 | |
|
1563 | 0 | std::string langLinkFlags; |
1564 | 0 | this->AddPerLanguageLinkFlags(langLinkFlags, target, linkLanguage, |
1565 | 0 | config); |
1566 | 0 | if (!langLinkFlags.empty()) { |
1567 | 0 | linkFlags.emplace_back(std::move(langLinkFlags)); |
1568 | 0 | } |
1569 | |
|
1570 | 0 | { |
1571 | 0 | auto exeType = cmStrCat( |
1572 | 0 | "CMAKE_", linkLanguage, "_CREATE_", |
1573 | 0 | (target->IsWin32Executable(config) ? "WIN32" : "CONSOLE"), "_EXE"); |
1574 | 0 | std::string exeFlags; |
1575 | 0 | this->AppendFlags(exeFlags, this->Makefile->GetDefinition(exeType), |
1576 | 0 | exeType, target, cmBuildStep::Link, linkLanguage); |
1577 | 0 | if (!exeFlags.empty()) { |
1578 | 0 | linkFlags.emplace_back(std::move(exeFlags)); |
1579 | 0 | } |
1580 | 0 | } |
1581 | |
|
1582 | 0 | std::string exeFlags; |
1583 | 0 | if (target->IsExecutableWithExports()) { |
1584 | 0 | exeFlags += this->Makefile->GetSafeDefinition( |
1585 | 0 | cmStrCat("CMAKE_EXE_EXPORTS_", linkLanguage, "_FLAG")); |
1586 | 0 | exeFlags += " "; |
1587 | 0 | } |
1588 | |
|
1589 | 0 | this->AddLanguageFlagsForLinking(flags, target, linkLanguage, config); |
1590 | 0 | if (pcli) { |
1591 | 0 | this->OutputLinkLibraries(pcli, linkLineComputer, linkLibs, |
1592 | 0 | frameworkPath, linkPath); |
1593 | 0 | } |
1594 | |
|
1595 | 0 | if (this->Makefile->IsOn("BUILD_SHARED_LIBS")) { |
1596 | 0 | std::string sFlagVar = "CMAKE_SHARED_BUILD_" + linkLanguage + "_FLAGS"; |
1597 | 0 | exeFlags += this->Makefile->GetSafeDefinition(sFlagVar); |
1598 | 0 | exeFlags += " "; |
1599 | 0 | } |
1600 | |
|
1601 | 0 | std::string exeExportFlags = |
1602 | 0 | this->GetExeExportFlags(linkLanguage, *target); |
1603 | 0 | if (!exeExportFlags.empty()) { |
1604 | 0 | exeFlags += exeExportFlags; |
1605 | 0 | exeFlags += " "; |
1606 | 0 | } |
1607 | |
|
1608 | 0 | this->AddTargetPropertyLinkFlags(exeFlags, target, config); |
1609 | |
|
1610 | 0 | if (!exeFlags.empty()) { |
1611 | 0 | this->GetGlobalGenerator()->EncodeLiteral(exeFlags); |
1612 | 0 | linkFlags.emplace_back(std::move(exeFlags)); |
1613 | 0 | } |
1614 | |
|
1615 | 0 | std::vector<BT<std::string>> linkOpts = |
1616 | 0 | target->GetLinkOptions(config, linkLanguage); |
1617 | 0 | this->SetLinkScriptShell(this->GetGlobalGenerator()->GetUseLinkScript()); |
1618 | | // LINK_OPTIONS are escaped. |
1619 | 0 | this->AppendCompileOptions(linkFlags, linkOpts); |
1620 | 0 | this->SetLinkScriptShell(false); |
1621 | 0 | } break; |
1622 | 0 | default: |
1623 | 0 | break; |
1624 | 0 | } |
1625 | | |
1626 | 0 | std::string extraLinkFlags; |
1627 | 0 | this->AppendLinkerTypeFlags(extraLinkFlags, target, config, linkLanguage); |
1628 | 0 | this->AppendPositionIndependentLinkerFlags(extraLinkFlags, target, config, |
1629 | 0 | linkLanguage); |
1630 | 0 | this->AppendWarningAsErrorLinkerFlags(extraLinkFlags, target, linkLanguage); |
1631 | 0 | this->AppendIPOLinkerFlags(extraLinkFlags, target, config, linkLanguage); |
1632 | 0 | this->AppendModuleDefinitionFlag(extraLinkFlags, target, linkLineComputer, |
1633 | 0 | config, linkLanguage); |
1634 | |
|
1635 | 0 | if (!extraLinkFlags.empty()) { |
1636 | 0 | this->GetGlobalGenerator()->EncodeLiteral(extraLinkFlags); |
1637 | 0 | linkFlags.emplace_back(std::move(extraLinkFlags)); |
1638 | 0 | } |
1639 | 0 | } |
1640 | | |
1641 | | void cmLocalGenerator::GetTargetCompileFlags(cmGeneratorTarget* target, |
1642 | | std::string const& config, |
1643 | | std::string const& lang, |
1644 | | std::string& flags, |
1645 | | std::string const& arch) |
1646 | 0 | { |
1647 | 0 | std::vector<BT<std::string>> tmpFlags = |
1648 | 0 | this->GetTargetCompileFlags(target, config, lang, arch); |
1649 | 0 | this->AppendFlags(flags, tmpFlags); |
1650 | 0 | } |
1651 | | |
1652 | | std::vector<BT<std::string>> cmLocalGenerator::GetTargetCompileFlags( |
1653 | | cmGeneratorTarget* target, std::string const& config, |
1654 | | std::string const& lang, std::string const& arch) |
1655 | 0 | { |
1656 | 0 | std::vector<BT<std::string>> flags; |
1657 | 0 | std::string compileFlags; |
1658 | |
|
1659 | 0 | cmMakefile* mf = this->GetMakefile(); |
1660 | | |
1661 | | // Add language-specific flags. |
1662 | 0 | this->AddLanguageFlags(compileFlags, target, cmBuildStep::Compile, lang, |
1663 | 0 | config); |
1664 | |
|
1665 | 0 | if (target->IsIPOEnabled(lang, config)) { |
1666 | 0 | this->AppendFeatureOptions(compileFlags, lang, "IPO"); |
1667 | 0 | } |
1668 | |
|
1669 | 0 | this->AddArchitectureFlags(compileFlags, target, lang, config, arch); |
1670 | |
|
1671 | 0 | if (lang == "Fortran") { |
1672 | 0 | this->AppendFlags(compileFlags, |
1673 | 0 | this->GetTargetFortranFlags(target, config)); |
1674 | 0 | } else if (lang == "Swift") { |
1675 | | // Only set the compile mode if CMP0157 is set |
1676 | 0 | if (cm::optional<cmSwiftCompileMode> swiftCompileMode = |
1677 | 0 | this->GetSwiftCompileMode(target, config)) { |
1678 | 0 | std::string swiftCompileModeFlag; |
1679 | 0 | switch (*swiftCompileMode) { |
1680 | 0 | case cmSwiftCompileMode::Incremental: { |
1681 | 0 | swiftCompileModeFlag = "-incremental"; |
1682 | 0 | if (cmValue flag = |
1683 | 0 | mf->GetDefinition("CMAKE_Swift_COMPILE_OPTIONS_INCREMENTAL")) { |
1684 | 0 | swiftCompileModeFlag = *flag; |
1685 | 0 | } |
1686 | 0 | break; |
1687 | 0 | } |
1688 | 0 | case cmSwiftCompileMode::Wholemodule: { |
1689 | 0 | swiftCompileModeFlag = "-wmo"; |
1690 | 0 | if (cmValue flag = |
1691 | 0 | mf->GetDefinition("CMAKE_Swift_COMPILE_OPTIONS_WMO")) { |
1692 | 0 | swiftCompileModeFlag = *flag; |
1693 | 0 | } |
1694 | 0 | break; |
1695 | 0 | } |
1696 | 0 | case cmSwiftCompileMode::Singlefile: |
1697 | 0 | break; |
1698 | 0 | case cmSwiftCompileMode::Unknown: { |
1699 | 0 | this->IssueMessage( |
1700 | 0 | MessageType::AUTHOR_WARNING, |
1701 | 0 | cmStrCat("Unknown Swift_COMPILATION_MODE on target '", |
1702 | 0 | target->GetName(), '\'')); |
1703 | 0 | } |
1704 | 0 | } |
1705 | 0 | this->AppendFlags(compileFlags, swiftCompileModeFlag); |
1706 | 0 | } |
1707 | 0 | } |
1708 | | |
1709 | 0 | this->AddFeatureFlags(compileFlags, target, lang, config); |
1710 | 0 | this->AddVisibilityPresetFlags(compileFlags, target, lang); |
1711 | 0 | this->AddColorDiagnosticsFlags(compileFlags, lang); |
1712 | 0 | this->AppendFlags(compileFlags, mf->GetDefineFlags()); |
1713 | 0 | this->AppendFlags(compileFlags, |
1714 | 0 | this->GetFrameworkFlags(lang, config, target)); |
1715 | 0 | this->AppendFlags(compileFlags, |
1716 | 0 | this->GetXcFrameworkFlags(lang, config, target)); |
1717 | |
|
1718 | 0 | if (!compileFlags.empty()) { |
1719 | 0 | flags.emplace_back(std::move(compileFlags)); |
1720 | 0 | } |
1721 | 0 | this->AddCompileOptions(flags, target, lang, config); |
1722 | 0 | return flags; |
1723 | 0 | } |
1724 | | |
1725 | | std::string cmLocalGenerator::GetFrameworkFlags(std::string const& lang, |
1726 | | std::string const& config, |
1727 | | cmGeneratorTarget* target) |
1728 | 0 | { |
1729 | 0 | cmLocalGenerator* lg = target->GetLocalGenerator(); |
1730 | 0 | cmMakefile* mf = lg->GetMakefile(); |
1731 | |
|
1732 | 0 | if (!target->IsApple()) { |
1733 | 0 | return std::string(); |
1734 | 0 | } |
1735 | | |
1736 | 0 | cmValue fwSearchFlag = |
1737 | 0 | mf->GetDefinition(cmStrCat("CMAKE_", lang, "_FRAMEWORK_SEARCH_FLAG")); |
1738 | 0 | cmValue sysFwSearchFlag = mf->GetDefinition( |
1739 | 0 | cmStrCat("CMAKE_", lang, "_SYSTEM_FRAMEWORK_SEARCH_FLAG")); |
1740 | |
|
1741 | 0 | if (!fwSearchFlag && !sysFwSearchFlag) { |
1742 | 0 | return std::string{}; |
1743 | 0 | } |
1744 | | |
1745 | 0 | std::set<std::string> emitted; |
1746 | | #ifdef __APPLE__ /* don't insert this when crosscompiling e.g. to iphone */ |
1747 | | emitted.insert("/System/Library/Frameworks"); |
1748 | | #endif |
1749 | 0 | std::vector<std::string> includes; |
1750 | |
|
1751 | 0 | lg->GetIncludeDirectories(includes, target, "C", config); |
1752 | | // check all include directories for frameworks as this |
1753 | | // will already have added a -F for the framework |
1754 | 0 | for (std::string const& include : includes) { |
1755 | 0 | if (lg->GetGlobalGenerator()->NameResolvesToFramework(include)) { |
1756 | 0 | std::string frameworkDir = cmStrCat(include, "/../"); |
1757 | 0 | frameworkDir = cmSystemTools::CollapseFullPath(frameworkDir); |
1758 | 0 | emitted.insert(frameworkDir); |
1759 | 0 | } |
1760 | 0 | } |
1761 | |
|
1762 | 0 | std::string flags; |
1763 | 0 | if (cmComputeLinkInformation* cli = target->GetLinkInformation(config)) { |
1764 | 0 | std::vector<std::string> const& frameworks = cli->GetFrameworkPaths(); |
1765 | 0 | for (std::string const& framework : frameworks) { |
1766 | 0 | if (emitted.insert(framework).second) { |
1767 | 0 | if (sysFwSearchFlag && |
1768 | 0 | target->IsSystemIncludeDirectory(framework, config, lang)) { |
1769 | 0 | flags += *sysFwSearchFlag; |
1770 | 0 | } else { |
1771 | 0 | flags += *fwSearchFlag; |
1772 | 0 | } |
1773 | 0 | flags += |
1774 | 0 | lg->ConvertToOutputFormat(framework, cmOutputConverter::SHELL); |
1775 | 0 | flags += " "; |
1776 | 0 | } |
1777 | 0 | } |
1778 | 0 | } |
1779 | 0 | return flags; |
1780 | 0 | } |
1781 | | |
1782 | | std::string cmLocalGenerator::GetXcFrameworkFlags(std::string const& lang, |
1783 | | std::string const& config, |
1784 | | cmGeneratorTarget* target) |
1785 | 0 | { |
1786 | 0 | cmLocalGenerator* lg = target->GetLocalGenerator(); |
1787 | 0 | cmMakefile* mf = lg->GetMakefile(); |
1788 | |
|
1789 | 0 | if (!target->IsApple()) { |
1790 | 0 | return std::string(); |
1791 | 0 | } |
1792 | | |
1793 | 0 | cmValue includeSearchFlag = |
1794 | 0 | mf->GetDefinition(cmStrCat("CMAKE_INCLUDE_FLAG_", lang)); |
1795 | 0 | cmValue sysIncludeSearchFlag = |
1796 | 0 | mf->GetDefinition(cmStrCat("CMAKE_INCLUDE_SYSTEM_FLAG_", lang)); |
1797 | |
|
1798 | 0 | if (!includeSearchFlag && !sysIncludeSearchFlag) { |
1799 | 0 | return std::string{}; |
1800 | 0 | } |
1801 | | |
1802 | 0 | std::string flags; |
1803 | 0 | if (cmComputeLinkInformation* cli = target->GetLinkInformation(config)) { |
1804 | 0 | std::vector<std::string> const& paths = cli->GetXcFrameworkHeaderPaths(); |
1805 | 0 | for (std::string const& path : paths) { |
1806 | 0 | if (sysIncludeSearchFlag && |
1807 | 0 | target->IsSystemIncludeDirectory(path, config, lang)) { |
1808 | 0 | flags += *sysIncludeSearchFlag; |
1809 | 0 | } else { |
1810 | 0 | flags += *includeSearchFlag; |
1811 | 0 | } |
1812 | 0 | flags += lg->ConvertToOutputFormat(path, cmOutputConverter::SHELL); |
1813 | 0 | flags += " "; |
1814 | 0 | } |
1815 | 0 | } |
1816 | 0 | return flags; |
1817 | 0 | } |
1818 | | |
1819 | | void cmLocalGenerator::GetTargetDefines(cmGeneratorTarget const* target, |
1820 | | std::string const& config, |
1821 | | std::string const& lang, |
1822 | | std::set<std::string>& defines) const |
1823 | 0 | { |
1824 | 0 | std::set<BT<std::string>> tmp = this->GetTargetDefines(target, config, lang); |
1825 | 0 | for (BT<std::string> const& v : tmp) { |
1826 | 0 | defines.emplace(v.Value); |
1827 | 0 | } |
1828 | 0 | } |
1829 | | |
1830 | | std::set<BT<std::string>> cmLocalGenerator::GetTargetDefines( |
1831 | | cmGeneratorTarget const* target, std::string const& config, |
1832 | | std::string const& lang) const |
1833 | 0 | { |
1834 | 0 | std::set<BT<std::string>> defines; |
1835 | | |
1836 | | // Add the export symbol definition for shared library objects. |
1837 | 0 | if (std::string const* exportMacro = target->GetExportMacro()) { |
1838 | 0 | this->AppendDefines(defines, *exportMacro); |
1839 | 0 | } |
1840 | 0 | for (auto const& sharedLibCompileDef : |
1841 | 0 | target->GetSharedLibraryCompileDefs(config)) { |
1842 | 0 | this->AppendDefines(defines, sharedLibCompileDef); |
1843 | 0 | } |
1844 | | |
1845 | | // Add preprocessor definitions for this target and configuration. |
1846 | 0 | std::vector<BT<std::string>> targetDefines = |
1847 | 0 | target->GetCompileDefinitions(config, lang); |
1848 | 0 | this->AppendDefines(defines, targetDefines); |
1849 | |
|
1850 | 0 | return defines; |
1851 | 0 | } |
1852 | | |
1853 | | std::string cmLocalGenerator::GetTargetFortranFlags( |
1854 | | cmGeneratorTarget const* /*unused*/, std::string const& /*unused*/) |
1855 | 0 | { |
1856 | | // Implemented by specific generators that override this. |
1857 | 0 | return std::string(); |
1858 | 0 | } |
1859 | | |
1860 | | /** |
1861 | | * Output the linking rules on a command line. For executables, |
1862 | | * targetLibrary should be a NULL pointer. For libraries, it should point |
1863 | | * to the name of the library. This will not link a library against itself. |
1864 | | */ |
1865 | | void cmLocalGenerator::OutputLinkLibraries( |
1866 | | cmComputeLinkInformation* pcli, cmLinkLineComputer* linkLineComputer, |
1867 | | std::string& linkLibraries, std::string& frameworkPath, |
1868 | | std::string& linkPath) |
1869 | 0 | { |
1870 | 0 | std::vector<BT<std::string>> linkLibrariesList; |
1871 | 0 | std::vector<BT<std::string>> linkPathList; |
1872 | 0 | this->OutputLinkLibraries(pcli, linkLineComputer, linkLibrariesList, |
1873 | 0 | frameworkPath, linkPathList); |
1874 | 0 | pcli->AppendValues(linkLibraries, linkLibrariesList); |
1875 | 0 | pcli->AppendValues(linkPath, linkPathList); |
1876 | 0 | } |
1877 | | |
1878 | | void cmLocalGenerator::OutputLinkLibraries( |
1879 | | cmComputeLinkInformation* pcli, cmLinkLineComputer* linkLineComputer, |
1880 | | std::vector<BT<std::string>>& linkLibraries, std::string& frameworkPath, |
1881 | | std::vector<BT<std::string>>& linkPath) |
1882 | 0 | { |
1883 | 0 | cmComputeLinkInformation& cli = *pcli; |
1884 | |
|
1885 | 0 | std::string linkLanguage = cli.GetLinkLanguage(); |
1886 | |
|
1887 | 0 | std::string libPathFlag; |
1888 | 0 | if (cmValue value = this->Makefile->GetDefinition( |
1889 | 0 | "CMAKE_" + cli.GetLinkLanguage() + "_LIBRARY_PATH_FLAG")) { |
1890 | 0 | libPathFlag = *value; |
1891 | 0 | } else { |
1892 | 0 | libPathFlag = |
1893 | 0 | this->Makefile->GetRequiredDefinition("CMAKE_LIBRARY_PATH_FLAG"); |
1894 | 0 | } |
1895 | |
|
1896 | 0 | std::string libPathTerminator; |
1897 | 0 | if (cmValue value = this->Makefile->GetDefinition( |
1898 | 0 | "CMAKE_" + cli.GetLinkLanguage() + "_LIBRARY_PATH_TERMINATOR")) { |
1899 | 0 | libPathTerminator = *value; |
1900 | 0 | } else { |
1901 | 0 | libPathTerminator = |
1902 | 0 | this->Makefile->GetRequiredDefinition("CMAKE_LIBRARY_PATH_TERMINATOR"); |
1903 | 0 | } |
1904 | | |
1905 | | // Add standard link directories for this language |
1906 | 0 | std::string stdLinkDirString = this->Makefile->GetSafeDefinition( |
1907 | 0 | cmStrCat("CMAKE_", cli.GetLinkLanguage(), "_STANDARD_LINK_DIRECTORIES")); |
1908 | | |
1909 | | // Add standard libraries for this language. |
1910 | 0 | std::string stdLibString = this->Makefile->GetSafeDefinition( |
1911 | 0 | cmStrCat("CMAKE_", cli.GetLinkLanguage(), "_STANDARD_LIBRARIES")); |
1912 | | |
1913 | | // Append the framework search path flags. |
1914 | 0 | cmValue fwSearchFlag = this->Makefile->GetDefinition( |
1915 | 0 | cmStrCat("CMAKE_", linkLanguage, "_FRAMEWORK_SEARCH_FLAG")); |
1916 | |
|
1917 | 0 | frameworkPath = linkLineComputer->ComputeFrameworkPath(cli, fwSearchFlag); |
1918 | 0 | linkLineComputer->ComputeLinkPath(cli, libPathFlag, libPathTerminator, |
1919 | 0 | stdLinkDirString, linkPath); |
1920 | 0 | linkLineComputer->ComputeLinkLibraries(cli, stdLibString, linkLibraries); |
1921 | 0 | } |
1922 | | |
1923 | | std::string cmLocalGenerator::GetExeExportFlags( |
1924 | | std::string const& linkLanguage, cmGeneratorTarget& tgt) const |
1925 | 0 | { |
1926 | 0 | std::string linkFlags; |
1927 | | |
1928 | | // Flags to export symbols from an executable. |
1929 | 0 | if (tgt.GetType() == cmStateEnums::EXECUTABLE && |
1930 | 0 | this->StateSnapshot.GetState()->GetGlobalPropertyAsBool( |
1931 | 0 | "TARGET_SUPPORTS_SHARED_LIBS")) { |
1932 | | // Only add the flags if ENABLE_EXPORTS is on, |
1933 | | // except on AIX where we compute symbol exports. |
1934 | 0 | if (!tgt.IsAIX() && tgt.GetPropertyAsBool("ENABLE_EXPORTS")) { |
1935 | 0 | linkFlags = this->Makefile->GetSafeDefinition( |
1936 | 0 | cmStrCat("CMAKE_SHARED_LIBRARY_LINK_", linkLanguage, "_FLAGS")); |
1937 | 0 | } |
1938 | 0 | } |
1939 | 0 | return linkFlags; |
1940 | 0 | } |
1941 | | |
1942 | | bool cmLocalGenerator::AllAppleArchSysrootsAreTheSame( |
1943 | | std::vector<std::string> const& archs, cmValue sysroot) |
1944 | 0 | { |
1945 | 0 | if (!sysroot) { |
1946 | 0 | return false; |
1947 | 0 | } |
1948 | | |
1949 | 0 | return std::all_of(archs.begin(), archs.end(), |
1950 | 0 | [this, sysroot](std::string const& arch) -> bool { |
1951 | 0 | std::string const& archSysroot = |
1952 | 0 | this->AppleArchSysroots[arch]; |
1953 | 0 | return cmIsOff(archSysroot) || *sysroot == archSysroot; |
1954 | 0 | }); |
1955 | 0 | } |
1956 | | |
1957 | | void cmLocalGenerator::AddArchitectureFlags(std::string& flags, |
1958 | | cmGeneratorTarget const* target, |
1959 | | std::string const& lang, |
1960 | | std::string const& config, |
1961 | | std::string const& filterArch) |
1962 | 0 | { |
1963 | | // Only add Apple specific flags on Apple platforms |
1964 | 0 | if (target->IsApple() && this->EmitUniversalBinaryFlags) { |
1965 | 0 | std::vector<std::string> archs = target->GetAppleArchs(config, lang); |
1966 | 0 | if (!archs.empty() && |
1967 | 0 | (lang == "C" || lang == "CXX" || lang == "OBJC" || lang == "OBJCXX" || |
1968 | 0 | lang == "ASM")) { |
1969 | 0 | for (std::string const& arch : archs) { |
1970 | 0 | if (filterArch.empty() || filterArch == arch) { |
1971 | 0 | flags += " -arch "; |
1972 | 0 | flags += arch; |
1973 | 0 | } |
1974 | 0 | } |
1975 | 0 | } |
1976 | |
|
1977 | 0 | cmValue sysroot = this->Makefile->GetDefinition("CMAKE_OSX_SYSROOT"); |
1978 | 0 | if (sysroot.IsEmpty() && |
1979 | 0 | this->Makefile->IsOn( |
1980 | 0 | cmStrCat("CMAKE_", lang, "_COMPILER_APPLE_SYSROOT_REQUIRED"))) { |
1981 | 0 | sysroot = this->Makefile->GetDefinition("_CMAKE_OSX_SYSROOT_PATH"); |
1982 | 0 | } |
1983 | 0 | if (sysroot && *sysroot == "/") { |
1984 | 0 | sysroot = nullptr; |
1985 | 0 | } |
1986 | 0 | std::string sysrootFlagVar = "CMAKE_" + lang + "_SYSROOT_FLAG"; |
1987 | 0 | cmValue sysrootFlag = this->Makefile->GetDefinition(sysrootFlagVar); |
1988 | 0 | if (cmNonempty(sysrootFlag)) { |
1989 | 0 | if (!this->AppleArchSysroots.empty() && |
1990 | 0 | !this->AllAppleArchSysrootsAreTheSame(archs, sysroot)) { |
1991 | 0 | for (std::string const& arch : archs) { |
1992 | 0 | std::string const& archSysroot = this->AppleArchSysroots[arch]; |
1993 | 0 | if (cmIsOff(archSysroot)) { |
1994 | 0 | continue; |
1995 | 0 | } |
1996 | 0 | if (filterArch.empty() || filterArch == arch) { |
1997 | 0 | flags += " -Xarch_" + arch + " "; |
1998 | | // Combine sysroot flag and path to work with -Xarch |
1999 | 0 | std::string arch_sysroot = *sysrootFlag + archSysroot; |
2000 | 0 | flags += this->ConvertToOutputFormat(arch_sysroot, SHELL); |
2001 | 0 | } |
2002 | 0 | } |
2003 | 0 | } else if (cmNonempty(sysroot)) { |
2004 | 0 | flags += " "; |
2005 | 0 | flags += *sysrootFlag; |
2006 | 0 | flags += " "; |
2007 | 0 | flags += this->ConvertToOutputFormat(*sysroot, SHELL); |
2008 | 0 | } |
2009 | 0 | } |
2010 | |
|
2011 | 0 | cmValue deploymentTarget = |
2012 | 0 | this->Makefile->GetDefinition("CMAKE_OSX_DEPLOYMENT_TARGET"); |
2013 | 0 | if (cmNonempty(deploymentTarget)) { |
2014 | 0 | std::string deploymentTargetFlagVar = |
2015 | 0 | "CMAKE_" + lang + "_OSX_DEPLOYMENT_TARGET_FLAG"; |
2016 | 0 | cmValue deploymentTargetFlag = |
2017 | 0 | this->Makefile->GetDefinition(deploymentTargetFlagVar); |
2018 | 0 | if (cmNonempty(deploymentTargetFlag) && |
2019 | | // CMAKE_<LANG>_COMPILER_TARGET overrides a --target= for |
2020 | | // CMAKE_OSX_DEPLOYMENT_TARGET, e.g., for visionOS. |
2021 | 0 | (!cmHasLiteralPrefix(*deploymentTarget, "--target=") || |
2022 | 0 | this->Makefile |
2023 | 0 | ->GetDefinition(cmStrCat("CMAKE_", lang, "_COMPILER_TARGET")) |
2024 | 0 | .IsEmpty())) { |
2025 | 0 | std::string flag = *deploymentTargetFlag; |
2026 | | |
2027 | | // Add the deployment target architecture to the flag, if needed. |
2028 | 0 | static std::string const kARCH = "<ARCH>"; |
2029 | 0 | std::string::size_type archPos = flag.find(kARCH); |
2030 | 0 | if (archPos != std::string::npos) { |
2031 | | // This placeholder is meant for visionOS, so default to arm64 |
2032 | | // unless only non-arm64 archs are given. |
2033 | 0 | std::string const arch = |
2034 | 0 | (archs.empty() || cm::contains(archs, "arm64")) ? "arm64" |
2035 | 0 | : archs[0]; |
2036 | | // Replace the placeholder with its value. |
2037 | 0 | flag = cmStrCat(flag.substr(0, archPos), arch, |
2038 | 0 | flag.substr(archPos + kARCH.size())); |
2039 | 0 | } |
2040 | | |
2041 | | // Add the deployment target version to the flag. |
2042 | 0 | static std::string const kVERSION_MIN = "<VERSION_MIN>"; |
2043 | 0 | std::string::size_type verPos = flag.find(kVERSION_MIN); |
2044 | 0 | if (verPos != std::string::npos) { |
2045 | | // Replace the placeholder with its value. |
2046 | 0 | flag = cmStrCat(flag.substr(0, verPos), *deploymentTarget, |
2047 | 0 | flag.substr(verPos + kVERSION_MIN.size())); |
2048 | 0 | } else { |
2049 | | // There is no placeholder, so append the value. |
2050 | 0 | flag = cmStrCat(flag, *deploymentTarget); |
2051 | 0 | } |
2052 | |
|
2053 | 0 | flags += " "; |
2054 | 0 | flags += flag; |
2055 | 0 | } |
2056 | 0 | } |
2057 | 0 | } |
2058 | 0 | } |
2059 | | |
2060 | | void cmLocalGenerator::AddLanguageFlags(std::string& flags, |
2061 | | cmGeneratorTarget const* target, |
2062 | | cmBuildStep compileOrLink, |
2063 | | std::string const& lang, |
2064 | | std::string const& config) |
2065 | 0 | { |
2066 | | // Add language-specific flags. |
2067 | 0 | this->AddConfigVariableFlags(flags, cmStrCat("CMAKE_", lang, "_FLAGS"), |
2068 | 0 | config); |
2069 | | |
2070 | | // Add the language standard flag for compiling, and sometimes linking. |
2071 | 0 | if (compileOrLink == cmBuildStep::Compile || |
2072 | 0 | (compileOrLink == cmBuildStep::Link && |
2073 | | // Some toolchains require use of the language standard flag |
2074 | | // when linking in order to use the matching standard library. |
2075 | | // FIXME: If CMake gains an abstraction for standard library |
2076 | | // selection, this will have to be reconciled with it. |
2077 | 0 | this->Makefile->IsOn( |
2078 | 0 | cmStrCat("CMAKE_", lang, "_LINK_WITH_STANDARD_COMPILE_OPTION")))) { |
2079 | 0 | cmStandardLevelResolver standardResolver(this->Makefile); |
2080 | 0 | std::string const& optionFlagDef = |
2081 | 0 | standardResolver.GetCompileOptionDef(target, lang, config); |
2082 | 0 | if (!optionFlagDef.empty()) { |
2083 | 0 | cmValue opt = |
2084 | 0 | target->Target->GetMakefile()->GetDefinition(optionFlagDef); |
2085 | 0 | if (opt) { |
2086 | 0 | cmList optList{ *opt }; |
2087 | 0 | for (std::string const& i : optList) { |
2088 | 0 | this->AppendFlagEscape(flags, i); |
2089 | 0 | } |
2090 | 0 | } |
2091 | 0 | } |
2092 | 0 | } |
2093 | |
|
2094 | 0 | std::string compilerId = this->Makefile->GetSafeDefinition( |
2095 | 0 | cmStrCat("CMAKE_", lang, "_COMPILER_ID")); |
2096 | |
|
2097 | 0 | std::string compilerSimulateId = this->Makefile->GetSafeDefinition( |
2098 | 0 | cmStrCat("CMAKE_", lang, "_SIMULATE_ID")); |
2099 | |
|
2100 | 0 | bool const compilerTargetsMsvcABI = |
2101 | 0 | (compilerId == "MSVC" || compilerSimulateId == "MSVC"); |
2102 | 0 | bool const compilerTargetsWatcomABI = |
2103 | 0 | (compilerId == "OpenWatcom" || compilerSimulateId == "OpenWatcom"); |
2104 | |
|
2105 | 0 | if (lang == "Swift") { |
2106 | 0 | if (cmValue v = target->GetProperty("Swift_LANGUAGE_VERSION")) { |
2107 | 0 | if (cmSystemTools::VersionCompare( |
2108 | 0 | cmSystemTools::OP_GREATER_EQUAL, |
2109 | 0 | this->Makefile->GetDefinition("CMAKE_Swift_COMPILER_VERSION"), |
2110 | 0 | "4.2")) { |
2111 | 0 | this->AppendFlags(flags, "-swift-version " + *v); |
2112 | 0 | } |
2113 | 0 | } |
2114 | 0 | } else if (lang == "CUDA") { |
2115 | 0 | target->AddCUDAArchitectureFlags(compileOrLink, config, flags); |
2116 | 0 | target->AddCUDAToolkitFlags(flags); |
2117 | 0 | } else if (lang == "ISPC") { |
2118 | 0 | target->AddISPCTargetFlags(flags); |
2119 | 0 | } else if (lang == "RC" && |
2120 | 0 | this->Makefile->GetSafeDefinition("CMAKE_RC_COMPILER") |
2121 | 0 | .find("llvm-rc") != std::string::npos) { |
2122 | 0 | compilerId = this->Makefile->GetSafeDefinition("CMAKE_C_COMPILER_ID"); |
2123 | 0 | if (!compilerId.empty()) { |
2124 | 0 | compilerSimulateId = |
2125 | 0 | this->Makefile->GetSafeDefinition("CMAKE_C_SIMULATE_ID"); |
2126 | 0 | } else { |
2127 | 0 | compilerId = this->Makefile->GetSafeDefinition("CMAKE_CXX_COMPILER_ID"); |
2128 | 0 | compilerSimulateId = |
2129 | 0 | this->Makefile->GetSafeDefinition("CMAKE_CXX_SIMULATE_ID"); |
2130 | 0 | } |
2131 | 0 | } else if (lang == "HIP") { |
2132 | 0 | target->AddHIPArchitectureFlags(compileOrLink, config, flags); |
2133 | 0 | } |
2134 | | |
2135 | | // Add VFS Overlay for Clang compilers |
2136 | 0 | if (compilerId == "Clang") { |
2137 | 0 | if (cmValue vfsOverlay = |
2138 | 0 | this->Makefile->GetDefinition("CMAKE_CLANG_VFS_OVERLAY")) { |
2139 | 0 | if (compilerSimulateId == "MSVC") { |
2140 | 0 | this->AppendCompileOptions( |
2141 | 0 | flags, |
2142 | 0 | std::vector<std::string>{ "-Xclang", "-ivfsoverlay", "-Xclang", |
2143 | 0 | *vfsOverlay }); |
2144 | 0 | } else { |
2145 | 0 | this->AppendCompileOptions( |
2146 | 0 | flags, std::vector<std::string>{ "-ivfsoverlay", *vfsOverlay }); |
2147 | 0 | } |
2148 | 0 | } |
2149 | 0 | } |
2150 | | // Add MSVC runtime library flags. This is activated by the presence |
2151 | | // of a default selection whether or not it is overridden by a property. |
2152 | 0 | cmValue msvcRuntimeLibraryDefault = |
2153 | 0 | this->Makefile->GetDefinition("CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT"); |
2154 | 0 | if (cmNonempty(msvcRuntimeLibraryDefault)) { |
2155 | 0 | cmValue msvcRuntimeLibraryValue = |
2156 | 0 | target->GetProperty("MSVC_RUNTIME_LIBRARY"); |
2157 | 0 | if (!msvcRuntimeLibraryValue) { |
2158 | 0 | msvcRuntimeLibraryValue = msvcRuntimeLibraryDefault; |
2159 | 0 | } |
2160 | 0 | std::string const msvcRuntimeLibrary = cmGeneratorExpression::Evaluate( |
2161 | 0 | *msvcRuntimeLibraryValue, this, config, target); |
2162 | 0 | if (!msvcRuntimeLibrary.empty()) { |
2163 | 0 | if (cmValue msvcRuntimeLibraryOptions = this->Makefile->GetDefinition( |
2164 | 0 | "CMAKE_" + lang + "_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_" + |
2165 | 0 | msvcRuntimeLibrary)) { |
2166 | 0 | this->AppendCompileOptions(flags, *msvcRuntimeLibraryOptions); |
2167 | 0 | } else if (compilerTargetsMsvcABI && |
2168 | 0 | !cmSystemTools::GetErrorOccurredFlag()) { |
2169 | | // The compiler uses the MSVC ABI so it needs a known runtime library. |
2170 | 0 | this->IssueMessage(MessageType::FATAL_ERROR, |
2171 | 0 | "MSVC_RUNTIME_LIBRARY value '" + |
2172 | 0 | msvcRuntimeLibrary + "' not known for this " + |
2173 | 0 | lang + " compiler."); |
2174 | 0 | } |
2175 | 0 | } |
2176 | 0 | } |
2177 | | |
2178 | | // Add Watcom runtime library flags. This is activated by the presence |
2179 | | // of a default selection whether or not it is overridden by a property. |
2180 | 0 | cmValue watcomRuntimeLibraryDefault = |
2181 | 0 | this->Makefile->GetDefinition("CMAKE_WATCOM_RUNTIME_LIBRARY_DEFAULT"); |
2182 | 0 | if (cmNonempty(watcomRuntimeLibraryDefault)) { |
2183 | 0 | cmValue watcomRuntimeLibraryValue = |
2184 | 0 | target->GetProperty("WATCOM_RUNTIME_LIBRARY"); |
2185 | 0 | if (!watcomRuntimeLibraryValue) { |
2186 | 0 | watcomRuntimeLibraryValue = watcomRuntimeLibraryDefault; |
2187 | 0 | } |
2188 | 0 | std::string const watcomRuntimeLibrary = cmGeneratorExpression::Evaluate( |
2189 | 0 | *watcomRuntimeLibraryValue, this, config, target); |
2190 | 0 | if (!watcomRuntimeLibrary.empty()) { |
2191 | 0 | if (cmValue watcomRuntimeLibraryOptions = this->Makefile->GetDefinition( |
2192 | 0 | "CMAKE_" + lang + "_COMPILE_OPTIONS_WATCOM_RUNTIME_LIBRARY_" + |
2193 | 0 | watcomRuntimeLibrary)) { |
2194 | 0 | this->AppendCompileOptions(flags, *watcomRuntimeLibraryOptions); |
2195 | 0 | } else if (compilerTargetsWatcomABI && |
2196 | 0 | !cmSystemTools::GetErrorOccurredFlag()) { |
2197 | | // The compiler uses the Watcom ABI so it needs a known runtime |
2198 | | // library. |
2199 | 0 | this->IssueMessage(MessageType::FATAL_ERROR, |
2200 | 0 | "WATCOM_RUNTIME_LIBRARY value '" + |
2201 | 0 | watcomRuntimeLibrary + "' not known for this " + |
2202 | 0 | lang + " compiler."); |
2203 | 0 | } |
2204 | 0 | } |
2205 | 0 | } |
2206 | | |
2207 | | // Add MSVC runtime checks flags. This is activated by the presence |
2208 | | // of a default selection whether or not it is overridden by a property. |
2209 | 0 | cmValue msvcRuntimeChecksDefault = |
2210 | 0 | this->Makefile->GetDefinition("CMAKE_MSVC_RUNTIME_CHECKS_DEFAULT"); |
2211 | 0 | if (cmNonempty(msvcRuntimeChecksDefault)) { |
2212 | 0 | cmValue msvcRuntimeChecksValue = |
2213 | 0 | target->GetProperty("MSVC_RUNTIME_CHECKS"); |
2214 | 0 | if (!msvcRuntimeChecksValue) { |
2215 | 0 | msvcRuntimeChecksValue = msvcRuntimeChecksDefault; |
2216 | 0 | } |
2217 | 0 | cmList msvcRuntimeChecksList = cmGeneratorExpression::Evaluate( |
2218 | 0 | *msvcRuntimeChecksValue, this, config, target); |
2219 | 0 | msvcRuntimeChecksList.remove_duplicates(); |
2220 | | |
2221 | | // RTC1/RTCsu VS GUI workaround |
2222 | 0 | std::string const stackFrameErrorCheck = "StackFrameErrorCheck"; |
2223 | 0 | std::string const unitinitializedVariable = "UninitializedVariable"; |
2224 | 0 | std::string const rtcSU = "RTCsu"; |
2225 | 0 | if ((cm::contains(msvcRuntimeChecksList, stackFrameErrorCheck) && |
2226 | 0 | cm::contains(msvcRuntimeChecksList, unitinitializedVariable)) || |
2227 | 0 | cm::contains(msvcRuntimeChecksList, rtcSU)) { |
2228 | 0 | msvcRuntimeChecksList.remove_items( |
2229 | 0 | { stackFrameErrorCheck, unitinitializedVariable, rtcSU }); |
2230 | 0 | msvcRuntimeChecksList.append(rtcSU); |
2231 | 0 | } |
2232 | |
|
2233 | 0 | for (std::string const& msvcRuntimeChecks : msvcRuntimeChecksList) { |
2234 | 0 | if (cmValue msvcRuntimeChecksOptions = |
2235 | 0 | this->Makefile->GetDefinition(cmStrCat( |
2236 | 0 | "CMAKE_", lang, |
2237 | 0 | "_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_" + msvcRuntimeChecks))) { |
2238 | 0 | this->AppendCompileOptions(flags, *msvcRuntimeChecksOptions); |
2239 | 0 | } else if (compilerTargetsMsvcABI && |
2240 | 0 | !cmSystemTools::GetErrorOccurredFlag()) { |
2241 | | // The compiler uses the MSVC ABI so it needs a known runtime checks. |
2242 | 0 | this->IssueMessage(MessageType::FATAL_ERROR, |
2243 | 0 | cmStrCat("MSVC_RUNTIME_CHECKS value '", |
2244 | 0 | msvcRuntimeChecks, "' not known for this ", |
2245 | 0 | lang, " compiler.")); |
2246 | 0 | } |
2247 | 0 | } |
2248 | 0 | } |
2249 | | |
2250 | | // Add MSVC debug information format flags if CMP0141 is NEW. |
2251 | 0 | if (cm::optional<std::string> msvcDebugInformationFormat = |
2252 | 0 | this->GetMSVCDebugFormatName(config, target)) { |
2253 | 0 | if (!msvcDebugInformationFormat->empty()) { |
2254 | 0 | if (cmValue msvcDebugInformationFormatOptions = |
2255 | 0 | this->Makefile->GetDefinition( |
2256 | 0 | cmStrCat("CMAKE_", lang, |
2257 | 0 | "_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_", |
2258 | 0 | *msvcDebugInformationFormat))) { |
2259 | 0 | this->AppendCompileOptions(flags, *msvcDebugInformationFormatOptions); |
2260 | 0 | } else if (compilerTargetsMsvcABI && |
2261 | 0 | !cmSystemTools::GetErrorOccurredFlag()) { |
2262 | | // The compiler uses the MSVC ABI so it needs a known runtime library. |
2263 | 0 | this->IssueMessage(MessageType::FATAL_ERROR, |
2264 | 0 | cmStrCat("MSVC_DEBUG_INFORMATION_FORMAT value '", |
2265 | 0 | *msvcDebugInformationFormat, |
2266 | 0 | "' not known for this ", lang, |
2267 | 0 | " compiler.")); |
2268 | 0 | } |
2269 | 0 | } |
2270 | 0 | } |
2271 | 0 | } |
2272 | | |
2273 | | void cmLocalGenerator::AddLanguageFlagsForLinking( |
2274 | | std::string& flags, cmGeneratorTarget const* target, std::string const& lang, |
2275 | | std::string const& config) |
2276 | 0 | { |
2277 | 0 | this->AddLanguageFlags(flags, target, cmBuildStep::Link, lang, config); |
2278 | |
|
2279 | 0 | if (target->IsIPOEnabled(lang, config)) { |
2280 | 0 | this->AppendFeatureOptions(flags, lang, "IPO"); |
2281 | 0 | } |
2282 | 0 | } |
2283 | | |
2284 | | cmGeneratorTarget* cmLocalGenerator::FindGeneratorTargetToUse( |
2285 | | std::string const& name) const |
2286 | 0 | { |
2287 | 0 | auto imported = this->ImportedGeneratorTargets.find(name); |
2288 | 0 | if (imported != this->ImportedGeneratorTargets.end()) { |
2289 | 0 | return imported->second; |
2290 | 0 | } |
2291 | | |
2292 | | // find local alias to imported target |
2293 | 0 | auto aliased = this->AliasTargets.find(name); |
2294 | 0 | if (aliased != this->AliasTargets.end()) { |
2295 | 0 | imported = this->ImportedGeneratorTargets.find(aliased->second); |
2296 | 0 | if (imported != this->ImportedGeneratorTargets.end()) { |
2297 | 0 | return imported->second; |
2298 | 0 | } |
2299 | 0 | } |
2300 | | |
2301 | 0 | if (cmGeneratorTarget* t = this->FindLocalNonAliasGeneratorTarget(name)) { |
2302 | 0 | return t; |
2303 | 0 | } |
2304 | | |
2305 | 0 | return this->GetGlobalGenerator()->FindGeneratorTarget(name); |
2306 | 0 | } |
2307 | | |
2308 | | bool cmLocalGenerator::GetRealDependency(std::string const& inName, |
2309 | | std::string const& config, |
2310 | | std::string& dep) |
2311 | 0 | { |
2312 | | // Older CMake code may specify the dependency using the target |
2313 | | // output file rather than the target name. Such code would have |
2314 | | // been written before there was support for target properties that |
2315 | | // modify the name so stripping down to just the file name should |
2316 | | // produce the target name in this case. |
2317 | 0 | std::string name = cmSystemTools::GetFilenameName(inName); |
2318 | | |
2319 | | // If the input name is the empty string, there is no real |
2320 | | // dependency. Short-circuit the other checks: |
2321 | 0 | if (name.empty()) { |
2322 | 0 | return false; |
2323 | 0 | } |
2324 | 0 | if (cmHasSuffix(name, ".exe"_s)) { |
2325 | 0 | name = cmSystemTools::GetFilenameWithoutLastExtension(name); |
2326 | 0 | } |
2327 | | |
2328 | | // Look for a CMake target with the given name. |
2329 | 0 | if (cmGeneratorTarget* target = this->FindGeneratorTargetToUse(name)) { |
2330 | | // make sure it is not just a coincidence that the target name |
2331 | | // found is part of the inName |
2332 | 0 | if (cmSystemTools::FileIsFullPath(inName)) { |
2333 | 0 | std::string tLocation; |
2334 | 0 | if (target->GetType() >= cmStateEnums::EXECUTABLE && |
2335 | 0 | target->GetType() <= cmStateEnums::MODULE_LIBRARY) { |
2336 | 0 | tLocation = target->GetLocation(config); |
2337 | 0 | tLocation = cmSystemTools::GetFilenamePath(tLocation); |
2338 | 0 | tLocation = cmSystemTools::CollapseFullPath(tLocation); |
2339 | 0 | } |
2340 | 0 | std::string depLocation = |
2341 | 0 | cmSystemTools::GetFilenamePath(std::string(inName)); |
2342 | 0 | depLocation = cmSystemTools::CollapseFullPath(depLocation); |
2343 | 0 | if (depLocation != tLocation) { |
2344 | | // it is a full path to a depend that has the same name |
2345 | | // as a target but is in a different location so do not use |
2346 | | // the target as the depend |
2347 | 0 | dep = inName; |
2348 | 0 | return true; |
2349 | 0 | } |
2350 | 0 | } |
2351 | 0 | switch (target->GetType()) { |
2352 | 0 | case cmStateEnums::EXECUTABLE: |
2353 | 0 | case cmStateEnums::STATIC_LIBRARY: |
2354 | 0 | case cmStateEnums::SHARED_LIBRARY: |
2355 | 0 | case cmStateEnums::MODULE_LIBRARY: |
2356 | 0 | case cmStateEnums::UNKNOWN_LIBRARY: |
2357 | 0 | dep = target->GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact, |
2358 | 0 | /*realname=*/true); |
2359 | 0 | return true; |
2360 | 0 | case cmStateEnums::OBJECT_LIBRARY: |
2361 | | // An object library has no single file on which to depend. |
2362 | | // This was listed to get the target-level dependency. |
2363 | 0 | case cmStateEnums::INTERFACE_LIBRARY: |
2364 | | // An interface library has no file on which to depend. |
2365 | | // This was listed to get the target-level dependency. |
2366 | 0 | case cmStateEnums::UTILITY: |
2367 | 0 | case cmStateEnums::GLOBAL_TARGET: |
2368 | | // A utility target has no file on which to depend. This was listed |
2369 | | // only to get the target-level dependency. |
2370 | 0 | return false; |
2371 | 0 | } |
2372 | 0 | } |
2373 | | |
2374 | | // The name was not that of a CMake target. It must name a file. |
2375 | 0 | if (cmSystemTools::FileIsFullPath(inName)) { |
2376 | | // This is a full path. Return it as given. |
2377 | 0 | dep = inName; |
2378 | 0 | return true; |
2379 | 0 | } |
2380 | | |
2381 | | // Check for a source file in this directory that matches the |
2382 | | // dependency. |
2383 | 0 | if (cmSourceFile* sf = this->Makefile->GetSource(inName)) { |
2384 | 0 | dep = sf->ResolveFullPath(); |
2385 | 0 | return true; |
2386 | 0 | } |
2387 | | |
2388 | | // Treat the name as relative to the source directory in which it |
2389 | | // was given. |
2390 | 0 | dep = cmStrCat(this->GetCurrentSourceDirectory(), '/', inName); |
2391 | | |
2392 | | // If the in-source path does not exist, assume it instead lives in the |
2393 | | // binary directory. |
2394 | 0 | if (!cmSystemTools::FileExists(dep)) { |
2395 | 0 | dep = cmStrCat(this->GetCurrentBinaryDirectory(), '/', inName); |
2396 | 0 | } |
2397 | |
|
2398 | 0 | dep = cmSystemTools::CollapseFullPath(dep); |
2399 | |
|
2400 | 0 | return true; |
2401 | 0 | } |
2402 | | |
2403 | | static void AddVisibilityCompileOption(std::string& flags, |
2404 | | cmGeneratorTarget const* target, |
2405 | | cmLocalGenerator* lg, |
2406 | | std::string const& lang) |
2407 | 0 | { |
2408 | 0 | std::string compileOption = "CMAKE_" + lang + "_COMPILE_OPTIONS_VISIBILITY"; |
2409 | 0 | cmValue opt = lg->GetMakefile()->GetDefinition(compileOption); |
2410 | 0 | if (!opt) { |
2411 | 0 | return; |
2412 | 0 | } |
2413 | 0 | std::string flagDefine = lang + "_VISIBILITY_PRESET"; |
2414 | |
|
2415 | 0 | cmValue prop = target->GetProperty(flagDefine); |
2416 | 0 | if (!prop) { |
2417 | 0 | return; |
2418 | 0 | } |
2419 | 0 | if ((*prop != "hidden") && (*prop != "default") && (*prop != "protected") && |
2420 | 0 | (*prop != "internal")) { |
2421 | 0 | std::ostringstream e; |
2422 | 0 | e << "Target " << target->GetName() << " uses unsupported value \"" |
2423 | 0 | << *prop << "\" for " << flagDefine << "." |
2424 | 0 | << " The supported values are: default, hidden, protected, and " |
2425 | 0 | "internal."; |
2426 | 0 | cmSystemTools::Error(e.str()); |
2427 | 0 | return; |
2428 | 0 | } |
2429 | 0 | std::string option = *opt + *prop; |
2430 | 0 | lg->AppendFlags(flags, option); |
2431 | 0 | } |
2432 | | |
2433 | | static void AddInlineVisibilityCompileOption(std::string& flags, |
2434 | | cmGeneratorTarget const* target, |
2435 | | cmLocalGenerator* lg, |
2436 | | std::string const& lang) |
2437 | 0 | { |
2438 | 0 | std::string compileOption = |
2439 | 0 | cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN"); |
2440 | 0 | cmValue opt = lg->GetMakefile()->GetDefinition(compileOption); |
2441 | 0 | if (!opt) { |
2442 | 0 | return; |
2443 | 0 | } |
2444 | | |
2445 | 0 | bool prop = target->GetPropertyAsBool("VISIBILITY_INLINES_HIDDEN"); |
2446 | 0 | if (!prop) { |
2447 | 0 | return; |
2448 | 0 | } |
2449 | 0 | lg->AppendFlags(flags, *opt); |
2450 | 0 | } |
2451 | | |
2452 | | void cmLocalGenerator::AddVisibilityPresetFlags( |
2453 | | std::string& flags, cmGeneratorTarget const* target, std::string const& lang) |
2454 | 0 | { |
2455 | 0 | if (lang.empty()) { |
2456 | 0 | return; |
2457 | 0 | } |
2458 | | |
2459 | 0 | AddVisibilityCompileOption(flags, target, this, lang); |
2460 | |
|
2461 | 0 | if (lang == "CXX" || lang == "OBJCXX") { |
2462 | 0 | AddInlineVisibilityCompileOption(flags, target, this, lang); |
2463 | 0 | } |
2464 | 0 | } |
2465 | | |
2466 | | void cmLocalGenerator::AddFeatureFlags(std::string& flags, |
2467 | | cmGeneratorTarget const* target, |
2468 | | std::string const& lang, |
2469 | | std::string const& config) |
2470 | 0 | { |
2471 | 0 | int targetType = target->GetType(); |
2472 | |
|
2473 | 0 | bool shared = ((targetType == cmStateEnums::SHARED_LIBRARY) || |
2474 | 0 | (targetType == cmStateEnums::MODULE_LIBRARY)); |
2475 | |
|
2476 | 0 | if (target->GetLinkInterfaceDependentBoolProperty( |
2477 | 0 | "POSITION_INDEPENDENT_CODE", config)) { |
2478 | 0 | this->AddPositionIndependentFlags(flags, lang, targetType); |
2479 | 0 | } |
2480 | 0 | if (shared) { |
2481 | 0 | this->AppendFeatureOptions(flags, lang, "DLL"); |
2482 | 0 | } |
2483 | 0 | } |
2484 | | |
2485 | | void cmLocalGenerator::AddPositionIndependentFlags(std::string& flags, |
2486 | | std::string const& lang, |
2487 | | int targetType) |
2488 | 0 | { |
2489 | 0 | std::string picFlags; |
2490 | |
|
2491 | 0 | if (targetType == cmStateEnums::EXECUTABLE) { |
2492 | 0 | picFlags = this->Makefile->GetSafeDefinition( |
2493 | 0 | cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_PIE")); |
2494 | 0 | } |
2495 | 0 | if (picFlags.empty()) { |
2496 | 0 | picFlags = this->Makefile->GetSafeDefinition( |
2497 | 0 | cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_PIC")); |
2498 | 0 | } |
2499 | 0 | if (!picFlags.empty()) { |
2500 | 0 | cmList options{ picFlags }; |
2501 | 0 | for (std::string const& o : options) { |
2502 | 0 | this->AppendFlagEscape(flags, o); |
2503 | 0 | } |
2504 | 0 | } |
2505 | 0 | } |
2506 | | |
2507 | | void cmLocalGenerator::AddColorDiagnosticsFlags(std::string& flags, |
2508 | | std::string const& lang) |
2509 | 0 | { |
2510 | 0 | cmValue diag = this->Makefile->GetDefinition("CMAKE_COLOR_DIAGNOSTICS"); |
2511 | 0 | if (diag.IsSet()) { |
2512 | 0 | std::string colorFlagName; |
2513 | 0 | if (diag.IsOn()) { |
2514 | 0 | colorFlagName = |
2515 | 0 | cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_COLOR_DIAGNOSTICS"); |
2516 | 0 | } else { |
2517 | 0 | colorFlagName = |
2518 | 0 | cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_COLOR_DIAGNOSTICS_OFF"); |
2519 | 0 | } |
2520 | |
|
2521 | 0 | cmList options{ this->Makefile->GetDefinition(colorFlagName) }; |
2522 | |
|
2523 | 0 | for (auto const& option : options) { |
2524 | 0 | this->AppendFlagEscape(flags, option); |
2525 | 0 | } |
2526 | 0 | } |
2527 | 0 | } |
2528 | | |
2529 | | void cmLocalGenerator::AddConfigVariableFlags(std::string& flags, |
2530 | | std::string const& var, |
2531 | | std::string const& config) |
2532 | 0 | { |
2533 | | // Add the flags from the variable itself. |
2534 | 0 | this->AppendFlags(flags, this->Makefile->GetSafeDefinition(var)); |
2535 | | // Add the flags from the build-type specific variable. |
2536 | 0 | if (!config.empty()) { |
2537 | 0 | std::string const flagsVar = |
2538 | 0 | cmStrCat(var, '_', cmSystemTools::UpperCase(config)); |
2539 | 0 | this->AppendFlags(flags, this->Makefile->GetSafeDefinition(flagsVar)); |
2540 | 0 | } |
2541 | 0 | } |
2542 | | void cmLocalGenerator::AddConfigVariableFlags(std::string& flags, |
2543 | | std::string const& var, |
2544 | | cmGeneratorTarget const* target, |
2545 | | cmBuildStep compileOrLink, |
2546 | | std::string const& lang, |
2547 | | std::string const& config) |
2548 | 0 | { |
2549 | 0 | std::string newFlags; |
2550 | 0 | this->AddConfigVariableFlags(newFlags, var, config); |
2551 | 0 | if (!newFlags.empty()) { |
2552 | 0 | this->AppendFlags(flags, newFlags, var, target, compileOrLink, lang); |
2553 | 0 | } |
2554 | 0 | } |
2555 | | |
2556 | | void cmLocalGenerator::AppendFlags(std::string& flags, |
2557 | | std::string const& newFlags) const |
2558 | 0 | { |
2559 | 0 | bool allSpaces = std::all_of(newFlags.begin(), newFlags.end(), cmIsSpace); |
2560 | |
|
2561 | 0 | if (!newFlags.empty() && !allSpaces) { |
2562 | 0 | if (!flags.empty()) { |
2563 | 0 | flags += " "; |
2564 | 0 | } |
2565 | 0 | flags += newFlags; |
2566 | 0 | } |
2567 | 0 | } |
2568 | | |
2569 | | void cmLocalGenerator::AppendFlags( |
2570 | | std::string& flags, std::vector<BT<std::string>> const& newFlags) const |
2571 | 0 | { |
2572 | 0 | for (BT<std::string> const& flag : newFlags) { |
2573 | 0 | this->AppendFlags(flags, flag.Value); |
2574 | 0 | } |
2575 | 0 | } |
2576 | | |
2577 | | void cmLocalGenerator::AppendFlagEscape(std::string& flags, |
2578 | | std::string const& rawFlag) const |
2579 | 0 | { |
2580 | 0 | this->AppendFlags( |
2581 | 0 | flags, |
2582 | 0 | this->EscapeForShell(rawFlag, false, false, false, this->IsNinjaMulti())); |
2583 | 0 | } |
2584 | | |
2585 | | void cmLocalGenerator::AppendLinkFlagsWithParsing( |
2586 | | std::string& flags, std::string const& newFlags, |
2587 | | cmGeneratorTarget const* target, std::string const& language) |
2588 | 0 | { |
2589 | 0 | std::vector<std::string> options; |
2590 | 0 | cmSystemTools::ParseUnixCommandLine(newFlags.c_str(), options); |
2591 | 0 | this->SetLinkScriptShell(this->GlobalGenerator->GetUseLinkScript()); |
2592 | 0 | std::vector<BT<std::string>> optionsWithBT{ options.size() }; |
2593 | 0 | std::transform(options.cbegin(), options.cend(), optionsWithBT.begin(), |
2594 | 0 | [](std::string const& item) -> BT<std::string> { |
2595 | 0 | return BT<std::string>{ item }; |
2596 | 0 | }); |
2597 | 0 | target->ResolveLinkerWrapper(optionsWithBT, language); |
2598 | 0 | for (auto const& item : optionsWithBT) { |
2599 | 0 | this->AppendFlagEscape(flags, item.Value); |
2600 | 0 | } |
2601 | 0 | this->SetLinkScriptShell(false); |
2602 | 0 | } |
2603 | | |
2604 | | void cmLocalGenerator::AppendFlags(std::string& flags, |
2605 | | std::string const& newFlags, |
2606 | | std::string const& name, |
2607 | | cmGeneratorTarget const* target, |
2608 | | cmBuildStep compileOrLink, |
2609 | | std::string const& language) |
2610 | 0 | { |
2611 | 0 | switch (target->GetPolicyStatusCMP0181()) { |
2612 | 0 | case cmPolicies::WARN: |
2613 | 0 | if (!this->Makefile->GetCMakeInstance()->GetIsInTryCompile() && |
2614 | 0 | this->Makefile->PolicyOptionalWarningEnabled( |
2615 | 0 | "CMAKE_POLICY_WARNING_CMP0181")) { |
2616 | 0 | this->Makefile->GetCMakeInstance()->IssueMessage( |
2617 | 0 | MessageType::AUTHOR_WARNING, |
2618 | 0 | cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0181), |
2619 | 0 | "\nSince the policy is not set, the contents of variable '", |
2620 | 0 | name, |
2621 | 0 | "' will " |
2622 | 0 | "be used as is."), |
2623 | 0 | target->GetBacktrace()); |
2624 | 0 | } |
2625 | 0 | CM_FALLTHROUGH; |
2626 | 0 | case cmPolicies::OLD: |
2627 | 0 | this->AppendFlags( |
2628 | 0 | flags, this->GetGlobalGenerator()->GetEncodedLiteral(newFlags)); |
2629 | 0 | break; |
2630 | 0 | case cmPolicies::NEW: |
2631 | 0 | if (compileOrLink == cmBuildStep::Link) { |
2632 | 0 | this->AppendLinkFlagsWithParsing(flags, newFlags, target, language); |
2633 | 0 | } else { |
2634 | 0 | this->AppendFlags(flags, newFlags); |
2635 | 0 | } |
2636 | 0 | } |
2637 | 0 | } |
2638 | | |
2639 | | void cmLocalGenerator::AddISPCDependencies(cmGeneratorTarget* target) |
2640 | 0 | { |
2641 | 0 | std::vector<std::string> enabledLanguages = |
2642 | 0 | this->GetState()->GetEnabledLanguages(); |
2643 | 0 | if (std::find(enabledLanguages.begin(), enabledLanguages.end(), "ISPC") == |
2644 | 0 | enabledLanguages.end()) { |
2645 | 0 | return; |
2646 | 0 | } |
2647 | | |
2648 | 0 | cmValue ispcHeaderSuffixProp = target->GetProperty("ISPC_HEADER_SUFFIX"); |
2649 | 0 | assert(ispcHeaderSuffixProp); |
2650 | |
|
2651 | 0 | std::vector<std::string> ispcArchSuffixes = |
2652 | 0 | detail::ComputeISPCObjectSuffixes(target); |
2653 | 0 | bool const extra_objects = (ispcArchSuffixes.size() > 1); |
2654 | |
|
2655 | 0 | std::vector<std::string> configsList = |
2656 | 0 | this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); |
2657 | 0 | for (std::string const& config : configsList) { |
2658 | |
|
2659 | 0 | std::string rootObjectDir = target->GetObjectDirectory(config); |
2660 | 0 | std::string headerDir = rootObjectDir; |
2661 | 0 | if (cmValue prop = target->GetProperty("ISPC_HEADER_DIRECTORY")) { |
2662 | 0 | headerDir = cmSystemTools::CollapseFullPath( |
2663 | 0 | cmStrCat(this->GetBinaryDirectory(), '/', *prop)); |
2664 | 0 | } |
2665 | |
|
2666 | 0 | std::vector<cmSourceFile*> sources; |
2667 | 0 | target->GetSourceFiles(sources, config); |
2668 | | |
2669 | | // build up the list of ispc headers and extra objects that this target is |
2670 | | // generating |
2671 | 0 | for (cmSourceFile const* sf : sources) { |
2672 | | // Generate this object file's rule file. |
2673 | 0 | std::string const& lang = sf->GetLanguage(); |
2674 | 0 | if (lang == "ISPC") { |
2675 | 0 | std::string const& objectName = target->GetObjectName(sf); |
2676 | | |
2677 | | // Drop both ".obj" and the source file extension |
2678 | 0 | std::string ispcSource = |
2679 | 0 | cmSystemTools::GetFilenameWithoutLastExtension(objectName); |
2680 | 0 | ispcSource = |
2681 | 0 | cmSystemTools::GetFilenameWithoutLastExtension(ispcSource); |
2682 | |
|
2683 | 0 | auto headerPath = |
2684 | 0 | cmStrCat(headerDir, '/', ispcSource, *ispcHeaderSuffixProp); |
2685 | 0 | target->AddISPCGeneratedHeader(headerPath, config); |
2686 | 0 | if (extra_objects) { |
2687 | 0 | std::vector<std::string> objs = detail::ComputeISPCExtraObjects( |
2688 | 0 | objectName, rootObjectDir, ispcArchSuffixes); |
2689 | 0 | target->AddISPCGeneratedObject(std::move(objs), config); |
2690 | 0 | } |
2691 | 0 | } |
2692 | 0 | } |
2693 | 0 | } |
2694 | 0 | } |
2695 | | |
2696 | | void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target) |
2697 | 0 | { |
2698 | 0 | std::vector<std::string> configsList = |
2699 | 0 | this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); |
2700 | |
|
2701 | 0 | for (std::string const& config : configsList) { |
2702 | | // FIXME: Refactor collection of sources to not evaluate object |
2703 | | // libraries. |
2704 | 0 | std::vector<cmSourceFile*> sources; |
2705 | 0 | target->GetSourceFiles(sources, config); |
2706 | |
|
2707 | 0 | std::string const configUpper = cmSystemTools::UpperCase(config); |
2708 | 0 | static std::array<std::string, 4> const langs = { { "C", "CXX", "OBJC", |
2709 | 0 | "OBJCXX" } }; |
2710 | |
|
2711 | 0 | std::set<std::string> pchLangSet; |
2712 | 0 | if (this->GetGlobalGenerator()->IsXcode()) { |
2713 | 0 | for (std::string const& lang : langs) { |
2714 | 0 | std::string const pchHeader = target->GetPchHeader(config, lang, ""); |
2715 | 0 | if (!pchHeader.empty()) { |
2716 | 0 | pchLangSet.emplace(lang); |
2717 | 0 | } |
2718 | 0 | } |
2719 | 0 | } |
2720 | |
|
2721 | 0 | for (std::string const& lang : langs) { |
2722 | 0 | auto langSources = std::count_if( |
2723 | 0 | sources.begin(), sources.end(), [lang](cmSourceFile* sf) { |
2724 | 0 | return lang == sf->GetLanguage() && |
2725 | 0 | !sf->GetProperty("SKIP_PRECOMPILE_HEADERS"); |
2726 | 0 | }); |
2727 | 0 | if (langSources == 0) { |
2728 | 0 | continue; |
2729 | 0 | } |
2730 | | |
2731 | 0 | std::vector<std::string> pchArchs = target->GetPchArchs(config, lang); |
2732 | 0 | if (pchArchs.size() > 1) { |
2733 | 0 | std::string useMultiArchPch; |
2734 | 0 | for (std::string const& arch : pchArchs) { |
2735 | 0 | std::string const pchHeader = |
2736 | 0 | target->GetPchHeader(config, lang, arch); |
2737 | 0 | if (!pchHeader.empty()) { |
2738 | 0 | useMultiArchPch = cmStrCat(useMultiArchPch, ";-Xarch_", arch, |
2739 | 0 | ";-include", pchHeader); |
2740 | 0 | } |
2741 | 0 | } |
2742 | |
|
2743 | 0 | if (!useMultiArchPch.empty()) { |
2744 | |
|
2745 | 0 | target->Target->AppendProperty( |
2746 | 0 | cmStrCat(lang, "_COMPILE_OPTIONS_USE_PCH"), |
2747 | 0 | cmStrCat("$<$<CONFIG:", config, ">:", useMultiArchPch, '>')); |
2748 | 0 | } |
2749 | 0 | } |
2750 | |
|
2751 | 0 | for (std::string const& arch : pchArchs) { |
2752 | 0 | std::string const pchSource = target->GetPchSource(config, lang, arch); |
2753 | 0 | std::string const pchHeader = target->GetPchHeader(config, lang, arch); |
2754 | |
|
2755 | 0 | if (pchSource.empty() || pchHeader.empty()) { |
2756 | 0 | if (this->GetGlobalGenerator()->IsXcode() && !pchLangSet.empty()) { |
2757 | 0 | for (auto* sf : sources) { |
2758 | 0 | auto const sourceLanguage = sf->GetLanguage(); |
2759 | 0 | if (!sourceLanguage.empty() && |
2760 | 0 | pchLangSet.find(sourceLanguage) == pchLangSet.end()) { |
2761 | 0 | sf->SetProperty("SKIP_PRECOMPILE_HEADERS", "ON"); |
2762 | 0 | } |
2763 | 0 | } |
2764 | 0 | } |
2765 | 0 | continue; |
2766 | 0 | } |
2767 | | |
2768 | 0 | cmValue pchExtension = |
2769 | 0 | this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION"); |
2770 | |
|
2771 | 0 | if (pchExtension.IsEmpty()) { |
2772 | 0 | continue; |
2773 | 0 | } |
2774 | | |
2775 | 0 | auto* reuseTarget = target->GetPchReuseTarget(); |
2776 | 0 | bool const haveReuseTarget = reuseTarget && reuseTarget != target; |
2777 | |
|
2778 | 0 | auto* pch_sf = this->Makefile->GetOrCreateSource( |
2779 | 0 | pchSource, false, cmSourceFileLocationKind::Known); |
2780 | 0 | pch_sf->SetSpecialSourceType( |
2781 | 0 | cmSourceFile::SpecialSourceType::PchSource); |
2782 | | // PCH sources should never be scanned as they cannot contain C++ |
2783 | | // module references. |
2784 | 0 | pch_sf->SetProperty("CXX_SCAN_FOR_MODULES", "0"); |
2785 | |
|
2786 | 0 | if (!this->GetGlobalGenerator()->IsXcode()) { |
2787 | 0 | if (!haveReuseTarget) { |
2788 | 0 | target->AddSource(pchSource, true); |
2789 | 0 | } |
2790 | |
|
2791 | 0 | std::string const pchFile = target->GetPchFile(config, lang, arch); |
2792 | | |
2793 | | // Exclude the pch files from linking |
2794 | 0 | if (this->Makefile->IsOn("CMAKE_LINK_PCH")) { |
2795 | 0 | if (!haveReuseTarget) { |
2796 | 0 | pch_sf->AppendProperty( |
2797 | 0 | "OBJECT_OUTPUTS", |
2798 | 0 | cmStrCat("$<$<CONFIG:", config, ">:", pchFile, '>')); |
2799 | 0 | } else { |
2800 | 0 | if (this->Makefile->IsOn("CMAKE_PCH_COPY_COMPILE_PDB")) { |
2801 | |
|
2802 | 0 | std::string const compilerId = |
2803 | 0 | this->Makefile->GetSafeDefinition( |
2804 | 0 | cmStrCat("CMAKE_", lang, "_COMPILER_ID")); |
2805 | |
|
2806 | 0 | std::string const compilerVersion = |
2807 | 0 | this->Makefile->GetSafeDefinition( |
2808 | 0 | cmStrCat("CMAKE_", lang, "_COMPILER_VERSION")); |
2809 | |
|
2810 | 0 | std::string const langFlags = |
2811 | 0 | this->Makefile->GetSafeDefinition( |
2812 | 0 | cmStrCat("CMAKE_", lang, "_FLAGS_", configUpper)); |
2813 | |
|
2814 | 0 | bool editAndContinueDebugInfo = false; |
2815 | 0 | bool programDatabaseDebugInfo = false; |
2816 | 0 | cm::optional<std::string> msvcDebugInformationFormat = |
2817 | 0 | this->GetMSVCDebugFormatName(config, target); |
2818 | 0 | if (msvcDebugInformationFormat && |
2819 | 0 | !msvcDebugInformationFormat->empty()) { |
2820 | 0 | editAndContinueDebugInfo = |
2821 | 0 | *msvcDebugInformationFormat == "EditAndContinue"; |
2822 | 0 | programDatabaseDebugInfo = |
2823 | 0 | *msvcDebugInformationFormat == "ProgramDatabase"; |
2824 | 0 | } else { |
2825 | 0 | editAndContinueDebugInfo = |
2826 | 0 | langFlags.find("/ZI") != std::string::npos || |
2827 | 0 | langFlags.find("-ZI") != std::string::npos; |
2828 | 0 | programDatabaseDebugInfo = |
2829 | 0 | langFlags.find("/Zi") != std::string::npos || |
2830 | 0 | langFlags.find("-Zi") != std::string::npos; |
2831 | 0 | } |
2832 | |
|
2833 | 0 | if (editAndContinueDebugInfo) { |
2834 | 0 | this->CopyPchCompilePdb(config, lang, target, reuseTarget, |
2835 | 0 | { ".pdb", ".idb" }); |
2836 | 0 | } else if (programDatabaseDebugInfo) { |
2837 | 0 | this->CopyPchCompilePdb(config, lang, target, reuseTarget, |
2838 | 0 | { ".pdb" }); |
2839 | 0 | } |
2840 | 0 | } |
2841 | | |
2842 | | // Link to the pch object file |
2843 | 0 | std::string pchSourceObj; |
2844 | | // Fastbuild will propagate pch.obj for us, no need to link to it |
2845 | | // explicitly. |
2846 | 0 | if (!this->GetGlobalGenerator()->IsFastbuild()) { |
2847 | 0 | pchSourceObj = |
2848 | 0 | reuseTarget->GetPchFileObject(config, lang, arch); |
2849 | 0 | } |
2850 | |
|
2851 | 0 | if (target->GetType() != cmStateEnums::OBJECT_LIBRARY) { |
2852 | 0 | std::string linkerProperty = "LINK_FLAGS_"; |
2853 | 0 | if (target->GetType() == cmStateEnums::STATIC_LIBRARY) { |
2854 | 0 | linkerProperty = "STATIC_LIBRARY_FLAGS_"; |
2855 | 0 | } |
2856 | 0 | target->Target->AppendProperty( |
2857 | 0 | cmStrCat(linkerProperty, configUpper), |
2858 | 0 | cmStrCat(' ', |
2859 | 0 | this->ConvertToOutputFormat(pchSourceObj, SHELL)), |
2860 | 0 | cm::nullopt, true); |
2861 | 0 | } else if (reuseTarget->GetType() == |
2862 | 0 | cmStateEnums::OBJECT_LIBRARY) { |
2863 | 0 | target->Target->AppendProperty( |
2864 | 0 | "INTERFACE_LINK_LIBRARIES", |
2865 | 0 | cmStrCat("$<$<CONFIG:", config, |
2866 | 0 | ">:$<LINK_ONLY:", pchSourceObj, ">>")); |
2867 | 0 | } |
2868 | 0 | } |
2869 | 0 | } else { |
2870 | 0 | pch_sf->SetProperty("PCH_EXTENSION", pchExtension); |
2871 | 0 | } |
2872 | | |
2873 | | // Add pchHeader to source files, which will |
2874 | | // be grouped as "Precompile Header File" |
2875 | 0 | auto* pchHeader_sf = this->Makefile->GetOrCreateSource( |
2876 | 0 | pchHeader, false, cmSourceFileLocationKind::Known); |
2877 | 0 | pchHeader_sf->SetSpecialSourceType( |
2878 | 0 | cmSourceFile::SpecialSourceType::PchHeader); |
2879 | 0 | std::string err; |
2880 | 0 | pchHeader_sf->ResolveFullPath(&err); |
2881 | 0 | if (!err.empty()) { |
2882 | 0 | std::ostringstream msg; |
2883 | 0 | msg << "Unable to resolve full path of PCH-header '" << pchHeader |
2884 | 0 | << "' assigned to target " << target->GetName() |
2885 | 0 | << ", although its path is supposed to be known!"; |
2886 | 0 | this->IssueMessage(MessageType::FATAL_ERROR, msg.str()); |
2887 | 0 | } |
2888 | 0 | target->AddSource(pchHeader); |
2889 | 0 | } |
2890 | 0 | } |
2891 | 0 | } |
2892 | 0 | } |
2893 | 0 | } |
2894 | | |
2895 | | void cmLocalGenerator::CopyPchCompilePdb( |
2896 | | std::string const& config, std::string const& language, |
2897 | | cmGeneratorTarget* target, cmGeneratorTarget* reuseTarget, |
2898 | | std::vector<std::string> const& extensions) |
2899 | 0 | { |
2900 | 0 | std::string const copy_script = cmStrCat(target->GetSupportDirectory(), |
2901 | 0 | "/copy_idb_pdb_", config, ".cmake"); |
2902 | 0 | cmGeneratedFileStream file(copy_script); |
2903 | |
|
2904 | 0 | file << "# CMake generated file\n"; |
2905 | |
|
2906 | 0 | file << "# The compiler generated pdb file needs to be written to disk\n" |
2907 | 0 | << "# by mspdbsrv. The foreach retry loop is needed to make sure\n" |
2908 | 0 | << "# the pdb file is ready to be copied.\n\n"; |
2909 | |
|
2910 | 0 | auto configGenex = [&](cm::string_view expr) -> std::string { |
2911 | 0 | if (this->GetGlobalGenerator()->IsMultiConfig()) { |
2912 | 0 | return cmStrCat("$<$<CONFIG:", config, ">:", expr, '>'); |
2913 | 0 | } |
2914 | 0 | return std::string(expr); |
2915 | 0 | }; |
2916 | |
|
2917 | 0 | std::vector<std::string> outputs; |
2918 | 0 | auto replaceExtension = [](std::string const& path, |
2919 | 0 | std::string const& ext) -> std::string { |
2920 | 0 | auto const dir = cmSystemTools::GetFilenamePath(path); |
2921 | 0 | auto const base = cmSystemTools::GetFilenameWithoutLastExtension(path); |
2922 | 0 | if (dir.empty()) { |
2923 | 0 | return cmStrCat(base, ext); |
2924 | 0 | } |
2925 | 0 | return cmStrCat(dir, '/', base, ext); |
2926 | 0 | }; |
2927 | 0 | for (auto const& extension : extensions) { |
2928 | 0 | std::string const from_file = |
2929 | 0 | replaceExtension(reuseTarget->GetCompilePDBPath(config), extension); |
2930 | 0 | std::string const to_dir = target->GetCompilePDBDirectory(config); |
2931 | 0 | std::string const to_file = |
2932 | 0 | replaceExtension(reuseTarget->GetCompilePDBName(config), extension); |
2933 | 0 | std::string const dest_file = cmStrCat(to_dir, '/', to_file); |
2934 | |
|
2935 | 0 | file << "foreach(retry RANGE 1 30)\n"; |
2936 | 0 | file << " if (EXISTS \"" << from_file << "\" AND (NOT EXISTS \"" |
2937 | 0 | << dest_file << "\" OR NOT \"" << dest_file << "\" IS_NEWER_THAN \"" |
2938 | 0 | << from_file << "\"))\n"; |
2939 | 0 | file << " file(MAKE_DIRECTORY \"" << to_dir << "\")\n"; |
2940 | 0 | file << " execute_process(COMMAND ${CMAKE_COMMAND} -E copy"; |
2941 | 0 | file << " \"" << from_file << "\"" << " \"" << to_dir |
2942 | 0 | << "\" RESULT_VARIABLE result " << " ERROR_QUIET)\n"; |
2943 | 0 | file << " if (NOT result EQUAL 0)\n" |
2944 | 0 | << " execute_process(COMMAND ${CMAKE_COMMAND}" |
2945 | 0 | << " -E sleep 1)\n" |
2946 | 0 | << " else()\n"; |
2947 | 0 | file << " break()\n" |
2948 | 0 | << " endif()\n"; |
2949 | 0 | file << " elseif(NOT EXISTS \"" << from_file << "\")\n" |
2950 | 0 | << " execute_process(COMMAND ${CMAKE_COMMAND}" << " -E sleep 1)\n" |
2951 | 0 | << " endif()\n"; |
2952 | 0 | file << "endforeach()\n"; |
2953 | 0 | outputs.push_back(configGenex(dest_file)); |
2954 | 0 | } |
2955 | |
|
2956 | 0 | cmCustomCommandLines commandLines = |
2957 | 0 | cmMakeSingleCommandLine({ configGenex(cmSystemTools::GetCMakeCommand()), |
2958 | 0 | configGenex("-P"), configGenex(copy_script) }); |
2959 | |
|
2960 | 0 | auto const comment = |
2961 | 0 | cmStrCat("Copying PDB for PCH reuse from ", reuseTarget->GetName(), |
2962 | 0 | " for ", target->GetName()); |
2963 | 0 | ; |
2964 | |
|
2965 | 0 | auto cc = cm::make_unique<cmCustomCommand>(); |
2966 | 0 | cc->SetCommandLines(commandLines); |
2967 | 0 | cc->SetComment(comment.c_str()); |
2968 | 0 | cc->SetStdPipesUTF8(true); |
2969 | 0 | cc->AppendDepends( |
2970 | 0 | { reuseTarget->GetPchFile(config, language), copy_script }); |
2971 | | // Fastbuild needs to know that this custom command actually depends on a |
2972 | | // target that produces PCH, so it can sort by dependencies correctly. |
2973 | 0 | if (this->GetGlobalGenerator()->IsFastbuild()) { |
2974 | 0 | cc->AppendDepends({ reuseTarget->GetName() }); |
2975 | 0 | } |
2976 | |
|
2977 | 0 | if (this->GetGlobalGenerator()->IsVisualStudio()) { |
2978 | 0 | cc->SetByproducts(outputs); |
2979 | 0 | this->AddCustomCommandToTarget( |
2980 | 0 | target->GetName(), cmCustomCommandType::PRE_BUILD, std::move(cc), |
2981 | 0 | cmObjectLibraryCommands::Accept); |
2982 | 0 | } else { |
2983 | 0 | cc->SetOutputs(outputs); |
2984 | 0 | cmSourceFile* copy_rule = this->AddCustomCommandToOutput(std::move(cc)); |
2985 | 0 | if (copy_rule) { |
2986 | 0 | copy_rule->SetProperty("CXX_SCAN_FOR_MODULES", "0"); |
2987 | 0 | auto* pch_pdb_sf = target->AddSource(copy_rule->ResolveFullPath()); |
2988 | 0 | pch_pdb_sf->SetSpecialSourceType( |
2989 | 0 | cmSourceFile::SpecialSourceType::PchPdbReuseSource); |
2990 | 0 | } |
2991 | 0 | } |
2992 | 0 | } |
2993 | | |
2994 | | cm::optional<std::string> cmLocalGenerator::GetMSVCDebugFormatName( |
2995 | | std::string const& config, cmGeneratorTarget const* target) |
2996 | 0 | { |
2997 | | // MSVC debug information format selection is activated by the presence |
2998 | | // of a default whether or not it is overridden by a property. |
2999 | 0 | cm::optional<std::string> msvcDebugInformationFormat; |
3000 | 0 | cmValue msvcDebugInformationFormatDefault = this->Makefile->GetDefinition( |
3001 | 0 | "CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT"); |
3002 | 0 | if (cmNonempty(msvcDebugInformationFormatDefault)) { |
3003 | 0 | cmValue msvcDebugInformationFormatValue = |
3004 | 0 | target->GetProperty("MSVC_DEBUG_INFORMATION_FORMAT"); |
3005 | 0 | if (!msvcDebugInformationFormatValue) { |
3006 | 0 | msvcDebugInformationFormatValue = msvcDebugInformationFormatDefault; |
3007 | 0 | } |
3008 | 0 | msvcDebugInformationFormat = cmGeneratorExpression::Evaluate( |
3009 | 0 | *msvcDebugInformationFormatValue, this, config, target); |
3010 | 0 | } |
3011 | 0 | return msvcDebugInformationFormat; |
3012 | 0 | } |
3013 | | |
3014 | | cm::optional<cmSwiftCompileMode> cmLocalGenerator::GetSwiftCompileMode( |
3015 | | cmGeneratorTarget const* target, std::string const& config) |
3016 | 0 | { |
3017 | 0 | cmMakefile const* mf = this->GetMakefile(); |
3018 | 0 | cmValue const swiftCompileModeDefault = |
3019 | 0 | mf->GetDefinition("CMAKE_Swift_COMPILATION_MODE_DEFAULT"); |
3020 | 0 | if (!cmNonempty(swiftCompileModeDefault)) { |
3021 | 0 | return {}; |
3022 | 0 | } |
3023 | 0 | cmValue swiftCompileMode = target->GetProperty("Swift_COMPILATION_MODE"); |
3024 | 0 | if (!swiftCompileMode) { |
3025 | 0 | swiftCompileMode = swiftCompileModeDefault; |
3026 | 0 | } |
3027 | |
|
3028 | 0 | std::string const expandedCompileMode = |
3029 | 0 | cmGeneratorExpression::Evaluate(*swiftCompileMode, this, config, target); |
3030 | 0 | if (expandedCompileMode == "wholemodule") { |
3031 | 0 | return cmSwiftCompileMode::Wholemodule; |
3032 | 0 | } |
3033 | 0 | if (expandedCompileMode == "singlefile") { |
3034 | 0 | return cmSwiftCompileMode::Singlefile; |
3035 | 0 | } |
3036 | 0 | if (expandedCompileMode == "incremental") { |
3037 | 0 | return cmSwiftCompileMode::Incremental; |
3038 | 0 | } |
3039 | 0 | return cmSwiftCompileMode::Unknown; |
3040 | 0 | } |
3041 | | |
3042 | | bool cmLocalGenerator::IsSplitSwiftBuild() const |
3043 | 0 | { |
3044 | 0 | return cmNonempty(this->GetMakefile()->GetDefinition( |
3045 | 0 | "CMAKE_Swift_COMPILATION_MODE_DEFAULT")); |
3046 | 0 | } |
3047 | | |
3048 | | namespace { |
3049 | | |
3050 | | inline void RegisterUnitySources(cmGeneratorTarget* target, cmSourceFile* sf, |
3051 | | std::string const& filename) |
3052 | 0 | { |
3053 | 0 | target->AddSourceFileToUnityBatch(sf->ResolveFullPath()); |
3054 | 0 | sf->SetProperty("UNITY_SOURCE_FILE", filename); |
3055 | 0 | } |
3056 | | } |
3057 | | |
3058 | | cmLocalGenerator::UnitySource cmLocalGenerator::WriteUnitySource( |
3059 | | cmGeneratorTarget* target, std::vector<std::string> const& configs, |
3060 | | cmRange<std::vector<UnityBatchedSource>::const_iterator> sources, |
3061 | | cmValue beforeInclude, cmValue afterInclude, std::string filename, |
3062 | | std::string const& unityFileDirectory, UnityPathMode pathMode) const |
3063 | 0 | { |
3064 | 0 | cmValue uniqueIdName = target->GetProperty("UNITY_BUILD_UNIQUE_ID"); |
3065 | 0 | cmGeneratedFileStream file( |
3066 | 0 | filename, false, target->GetGlobalGenerator()->GetMakefileEncoding()); |
3067 | 0 | file.SetCopyIfDifferent(true); |
3068 | 0 | file << "/* generated by CMake */\n\n"; |
3069 | |
|
3070 | 0 | bool perConfig = false; |
3071 | 0 | for (UnityBatchedSource const& ubs : sources) { |
3072 | 0 | cm::optional<std::string> cond; |
3073 | 0 | if (ubs.Configs.size() != configs.size()) { |
3074 | 0 | perConfig = true; |
3075 | 0 | cond = std::string(); |
3076 | 0 | cm::string_view sep; |
3077 | 0 | for (size_t ci : ubs.Configs) { |
3078 | 0 | cond = cmStrCat(*cond, sep, "defined(CMAKE_UNITY_CONFIG_", |
3079 | 0 | cmSystemTools::UpperCase(configs[ci]), ')'); |
3080 | 0 | sep = " || "_s; |
3081 | 0 | } |
3082 | 0 | } |
3083 | 0 | RegisterUnitySources(target, ubs.Source, filename); |
3084 | 0 | WriteUnitySourceInclude(file, cond, ubs.Source->ResolveFullPath(), |
3085 | 0 | beforeInclude, afterInclude, uniqueIdName, |
3086 | 0 | pathMode, unityFileDirectory); |
3087 | 0 | } |
3088 | |
|
3089 | 0 | return UnitySource(std::move(filename), perConfig); |
3090 | 0 | } |
3091 | | |
3092 | | void cmLocalGenerator::WriteUnitySourceInclude( |
3093 | | std::ostream& unity_file, cm::optional<std::string> const& cond, |
3094 | | std::string const& sf_full_path, cmValue beforeInclude, cmValue afterInclude, |
3095 | | cmValue uniqueIdName, UnityPathMode pathMode, |
3096 | | std::string const& unityFileDirectory) const |
3097 | 0 | { |
3098 | 0 | if (cond) { |
3099 | 0 | unity_file << "#if " << *cond << "\n"; |
3100 | 0 | } |
3101 | |
|
3102 | 0 | std::string pathToHash; |
3103 | 0 | std::string relocatableIncludePath; |
3104 | 0 | auto PathEqOrSubDir = [](std::string const& a, std::string const& b) { |
3105 | 0 | return (cmSystemTools::ComparePath(a, b) || |
3106 | 0 | cmSystemTools::IsSubDirectory(a, b)); |
3107 | 0 | }; |
3108 | 0 | auto const path = cmSystemTools::GetFilenamePath(sf_full_path); |
3109 | 0 | if (PathEqOrSubDir(path, this->GetBinaryDirectory())) { |
3110 | 0 | relocatableIncludePath = |
3111 | 0 | cmSystemTools::RelativePath(unityFileDirectory, sf_full_path); |
3112 | 0 | pathToHash = "BLD_" + |
3113 | 0 | cmSystemTools::RelativePath(this->GetBinaryDirectory(), sf_full_path); |
3114 | 0 | } else if (PathEqOrSubDir(path, this->GetSourceDirectory())) { |
3115 | 0 | relocatableIncludePath = |
3116 | 0 | cmSystemTools::RelativePath(this->GetSourceDirectory(), sf_full_path); |
3117 | 0 | pathToHash = "SRC_" + relocatableIncludePath; |
3118 | 0 | } else { |
3119 | 0 | relocatableIncludePath = sf_full_path; |
3120 | 0 | pathToHash = "ABS_" + sf_full_path; |
3121 | 0 | } |
3122 | |
|
3123 | 0 | if (cmNonempty(uniqueIdName)) { |
3124 | 0 | cmCryptoHash hasher(cmCryptoHash::AlgoMD5); |
3125 | 0 | unity_file << "/* " << pathToHash << " */\n" |
3126 | 0 | << "#undef " << *uniqueIdName << "\n" |
3127 | 0 | << "#define " << *uniqueIdName << " unity_" |
3128 | 0 | << hasher.HashString(pathToHash) << "\n"; |
3129 | 0 | } |
3130 | |
|
3131 | 0 | if (beforeInclude) { |
3132 | 0 | unity_file << *beforeInclude << "\n"; |
3133 | 0 | } |
3134 | | |
3135 | | // clang-tidy-17 has new include checks that needs NOLINT too. |
3136 | 0 | unity_file |
3137 | 0 | << "/* NOLINTNEXTLINE(bugprone-suspicious-include,misc-include-cleaner) " |
3138 | 0 | "*/\n"; |
3139 | 0 | if (pathMode == UnityPathMode::Relative) { |
3140 | 0 | unity_file << "#include \"" << relocatableIncludePath << "\"\n"; |
3141 | 0 | } else { |
3142 | 0 | unity_file << "#include \"" << sf_full_path << "\"\n"; |
3143 | 0 | } |
3144 | |
|
3145 | 0 | if (afterInclude) { |
3146 | 0 | unity_file << *afterInclude << "\n"; |
3147 | 0 | } |
3148 | 0 | if (cond) { |
3149 | 0 | unity_file << "#endif\n"; |
3150 | 0 | } |
3151 | 0 | unity_file << "\n"; |
3152 | 0 | } |
3153 | | |
3154 | | namespace { |
3155 | | std::string unity_file_extension(std::string const& lang) |
3156 | 0 | { |
3157 | 0 | std::string extension; |
3158 | 0 | if (lang == "C") { |
3159 | 0 | extension = "_c.c"; |
3160 | 0 | } else if (lang == "CXX") { |
3161 | 0 | extension = "_cxx.cxx"; |
3162 | 0 | } else if (lang == "CUDA") { |
3163 | 0 | extension = "_cu.cu"; |
3164 | 0 | } else if (lang == "OBJC") { |
3165 | 0 | extension = "_m.m"; |
3166 | 0 | } else if (lang == "OBJCXX") { |
3167 | 0 | extension = "_mm.mm"; |
3168 | 0 | } |
3169 | 0 | return extension; |
3170 | 0 | } |
3171 | | |
3172 | | char const* unity_file_prefix(cmGeneratorTarget* target) |
3173 | 0 | { |
3174 | 0 | if (cmValue val = target->GetProperty("UNITY_BUILD_FILENAME_PREFIX")) { |
3175 | 0 | return val->c_str(); |
3176 | 0 | } |
3177 | 0 | return "unity_"; |
3178 | 0 | } |
3179 | | } |
3180 | | |
3181 | | std::vector<cmLocalGenerator::UnitySource> |
3182 | | cmLocalGenerator::AddUnityFilesModeAuto( |
3183 | | cmGeneratorTarget* target, std::string const& lang, |
3184 | | std::vector<std::string> const& configs, |
3185 | | std::vector<UnityBatchedSource> const& filtered_sources, |
3186 | | cmValue beforeInclude, cmValue afterInclude, |
3187 | | std::string const& filename_base, UnityPathMode pathMode, size_t batchSize) |
3188 | 0 | { |
3189 | 0 | if (batchSize == 0) { |
3190 | 0 | batchSize = filtered_sources.size(); |
3191 | 0 | } |
3192 | 0 | char const* filename_prefix = unity_file_prefix(target); |
3193 | 0 | std::vector<UnitySource> unity_files; |
3194 | 0 | for (size_t itemsLeft = filtered_sources.size(), chunk, batch = 0; |
3195 | 0 | itemsLeft > 0; itemsLeft -= chunk, ++batch) { |
3196 | |
|
3197 | 0 | chunk = std::min(itemsLeft, batchSize); |
3198 | |
|
3199 | 0 | std::string filename = cmStrCat(filename_base, filename_prefix, batch, |
3200 | 0 | unity_file_extension(lang)); |
3201 | 0 | auto const begin = filtered_sources.begin() + batch * batchSize; |
3202 | 0 | auto const end = begin + chunk; |
3203 | 0 | unity_files.emplace_back(this->WriteUnitySource( |
3204 | 0 | target, configs, cmMakeRange(begin, end), beforeInclude, afterInclude, |
3205 | 0 | std::move(filename), filename_base, pathMode)); |
3206 | 0 | } |
3207 | 0 | return unity_files; |
3208 | 0 | } |
3209 | | |
3210 | | std::vector<cmLocalGenerator::UnitySource> |
3211 | | cmLocalGenerator::AddUnityFilesModeGroup( |
3212 | | cmGeneratorTarget* target, std::string const& lang, |
3213 | | std::vector<std::string> const& configs, |
3214 | | std::vector<UnityBatchedSource> const& filtered_sources, |
3215 | | cmValue beforeInclude, cmValue afterInclude, |
3216 | | std::string const& filename_base, UnityPathMode pathMode) |
3217 | 0 | { |
3218 | 0 | std::vector<UnitySource> unity_files; |
3219 | | |
3220 | | // sources organized by group name. Drop any source |
3221 | | // without a group |
3222 | 0 | std::unordered_map<std::string, std::vector<UnityBatchedSource>> |
3223 | 0 | explicit_mapping; |
3224 | 0 | for (UnityBatchedSource const& ubs : filtered_sources) { |
3225 | 0 | if (cmValue value = ubs.Source->GetProperty("UNITY_GROUP")) { |
3226 | 0 | auto i = explicit_mapping.find(*value); |
3227 | 0 | if (i == explicit_mapping.end()) { |
3228 | 0 | std::vector<UnityBatchedSource> sources{ ubs }; |
3229 | 0 | explicit_mapping.emplace(*value, std::move(sources)); |
3230 | 0 | } else { |
3231 | 0 | i->second.emplace_back(ubs); |
3232 | 0 | } |
3233 | 0 | } |
3234 | 0 | } |
3235 | |
|
3236 | 0 | char const* filename_prefix = unity_file_prefix(target); |
3237 | 0 | for (auto const& item : explicit_mapping) { |
3238 | 0 | auto const& name = item.first; |
3239 | 0 | std::string filename = cmStrCat(filename_base, filename_prefix, name, |
3240 | 0 | unity_file_extension(lang)); |
3241 | 0 | unity_files.emplace_back(this->WriteUnitySource( |
3242 | 0 | target, configs, cmMakeRange(item.second), beforeInclude, afterInclude, |
3243 | 0 | std::move(filename), filename_base, pathMode)); |
3244 | 0 | } |
3245 | |
|
3246 | 0 | return unity_files; |
3247 | 0 | } |
3248 | | |
3249 | | void cmLocalGenerator::AddUnityBuild(cmGeneratorTarget* target) |
3250 | 0 | { |
3251 | | // cmFastbuildNormalTargetGenerator handles unity build. |
3252 | 0 | if (this->GetGlobalGenerator()->IsFastbuild() || |
3253 | 0 | !target->GetPropertyAsBool("UNITY_BUILD")) { |
3254 | 0 | return; |
3255 | 0 | } |
3256 | | |
3257 | 0 | std::vector<UnityBatchedSource> unitySources; |
3258 | |
|
3259 | 0 | std::vector<std::string> configs = |
3260 | 0 | this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); |
3261 | |
|
3262 | 0 | std::map<cmSourceFile const*, size_t> index; |
3263 | |
|
3264 | 0 | for (size_t ci = 0; ci < configs.size(); ++ci) { |
3265 | | // FIXME: Refactor collection of sources to not evaluate object libraries. |
3266 | | // Their final set of object files might be transformed by unity builds. |
3267 | 0 | std::vector<cmSourceFile*> sources; |
3268 | 0 | target->GetSourceFiles(sources, configs[ci]); |
3269 | 0 | for (cmSourceFile* sf : sources) { |
3270 | | // Files which need C++ scanning cannot participate in unity builds as |
3271 | | // there is a single place in TUs that may perform module-dependency bits |
3272 | | // and a unity source cannot `#include` them in-order and represent a |
3273 | | // valid TU. |
3274 | 0 | if (sf->GetLanguage() == "CXX"_s && |
3275 | 0 | target->NeedDyndepForSource("CXX", configs[ci], sf)) { |
3276 | 0 | continue; |
3277 | 0 | } |
3278 | | |
3279 | 0 | auto mi = index.find(sf); |
3280 | 0 | if (mi == index.end()) { |
3281 | 0 | unitySources.emplace_back(sf); |
3282 | 0 | std::map<cmSourceFile const*, size_t>::value_type entry( |
3283 | 0 | sf, unitySources.size() - 1); |
3284 | 0 | mi = index.insert(entry).first; |
3285 | 0 | } |
3286 | 0 | unitySources[mi->second].Configs.emplace_back(ci); |
3287 | 0 | } |
3288 | 0 | } |
3289 | |
|
3290 | 0 | std::string filename_base = |
3291 | 0 | cmStrCat(target->GetCMFSupportDirectory(), "/Unity/"); |
3292 | |
|
3293 | 0 | cmValue batchSizeString = target->GetProperty("UNITY_BUILD_BATCH_SIZE"); |
3294 | 0 | size_t const unityBatchSize = batchSizeString |
3295 | 0 | ? static_cast<size_t>(std::atoi(batchSizeString->c_str())) |
3296 | 0 | : 0; |
3297 | |
|
3298 | 0 | cmValue beforeInclude = |
3299 | 0 | target->GetProperty("UNITY_BUILD_CODE_BEFORE_INCLUDE"); |
3300 | 0 | cmValue afterInclude = target->GetProperty("UNITY_BUILD_CODE_AFTER_INCLUDE"); |
3301 | 0 | cmValue unityMode = target->GetProperty("UNITY_BUILD_MODE"); |
3302 | 0 | UnityPathMode pathMode = target->GetPropertyAsBool("UNITY_BUILD_RELOCATABLE") |
3303 | 0 | ? UnityPathMode::Relative |
3304 | 0 | : UnityPathMode::Absolute; |
3305 | |
|
3306 | 0 | for (std::string lang : { "C", "CXX", "OBJC", "OBJCXX", "CUDA" }) { |
3307 | 0 | std::vector<UnityBatchedSource> filtered_sources; |
3308 | 0 | std::copy_if(unitySources.begin(), unitySources.end(), |
3309 | 0 | std::back_inserter(filtered_sources), |
3310 | 0 | [&](UnityBatchedSource const& ubs) -> bool { |
3311 | 0 | cmSourceFile* sf = ubs.Source; |
3312 | 0 | return sf->GetLanguage() == lang && |
3313 | 0 | !sf->GetPropertyAsBool("SKIP_UNITY_BUILD_INCLUSION") && |
3314 | 0 | !sf->GetPropertyAsBool("HEADER_FILE_ONLY") && |
3315 | 0 | !sf->GetProperty("COMPILE_OPTIONS") && |
3316 | 0 | !sf->GetProperty("COMPILE_DEFINITIONS") && |
3317 | 0 | !sf->GetProperty("COMPILE_FLAGS") && |
3318 | 0 | !sf->GetProperty("INCLUDE_DIRECTORIES"); |
3319 | 0 | }); |
3320 | |
|
3321 | 0 | std::vector<UnitySource> unity_files; |
3322 | 0 | if (!unityMode || *unityMode == "BATCH") { |
3323 | 0 | unity_files = AddUnityFilesModeAuto( |
3324 | 0 | target, lang, configs, filtered_sources, beforeInclude, afterInclude, |
3325 | 0 | filename_base, pathMode, unityBatchSize); |
3326 | 0 | } else if (unityMode && *unityMode == "GROUP") { |
3327 | 0 | unity_files = AddUnityFilesModeGroup( |
3328 | 0 | target, lang, configs, filtered_sources, beforeInclude, afterInclude, |
3329 | 0 | filename_base, pathMode); |
3330 | 0 | } else { |
3331 | | // unity mode is set to an unsupported value |
3332 | 0 | std::string e("Invalid UNITY_BUILD_MODE value of " + *unityMode + |
3333 | 0 | " assigned to target " + target->GetName() + |
3334 | 0 | ". Acceptable values are BATCH and GROUP."); |
3335 | 0 | this->IssueMessage(MessageType::FATAL_ERROR, e); |
3336 | 0 | } |
3337 | |
|
3338 | 0 | for (UnitySource const& file : unity_files) { |
3339 | 0 | auto* unity = this->GetMakefile()->GetOrCreateSource(file.Path); |
3340 | 0 | unity->SetSpecialSourceType( |
3341 | 0 | cmSourceFile::SpecialSourceType::UnitySource); |
3342 | 0 | target->AddSource(file.Path, true); |
3343 | 0 | unity->SetProperty("SKIP_UNITY_BUILD_INCLUSION", "ON"); |
3344 | 0 | unity->SetProperty("UNITY_SOURCE_FILE", file.Path); |
3345 | 0 | unity->SetProperty("CXX_SCAN_FOR_MODULES", "0"); |
3346 | 0 | if (file.PerConfig) { |
3347 | 0 | unity->SetProperty("COMPILE_DEFINITIONS", |
3348 | 0 | "CMAKE_UNITY_CONFIG_$<UPPER_CASE:$<CONFIG>>"); |
3349 | 0 | } |
3350 | |
|
3351 | 0 | if (pathMode == UnityPathMode::Relative) { |
3352 | 0 | unity->AppendProperty("INCLUDE_DIRECTORIES", |
3353 | 0 | this->GetSourceDirectory(), false); |
3354 | 0 | } |
3355 | 0 | } |
3356 | 0 | } |
3357 | 0 | } |
3358 | | |
3359 | | void cmLocalGenerator::AddPerLanguageLinkFlags(std::string& flags, |
3360 | | cmGeneratorTarget const* target, |
3361 | | std::string const& lang, |
3362 | | std::string const& config) |
3363 | 0 | { |
3364 | 0 | switch (target->GetType()) { |
3365 | 0 | case cmStateEnums::MODULE_LIBRARY: |
3366 | 0 | case cmStateEnums::SHARED_LIBRARY: |
3367 | 0 | case cmStateEnums::EXECUTABLE: |
3368 | 0 | break; |
3369 | 0 | default: |
3370 | 0 | return; |
3371 | 0 | } |
3372 | | |
3373 | 0 | std::string langLinkFlags = |
3374 | 0 | this->Makefile->GetSafeDefinition(cmStrCat("CMAKE_", lang, "_LINK_FLAGS")); |
3375 | |
|
3376 | 0 | switch (target->GetPolicyStatusCMP0210()) { |
3377 | 0 | case cmPolicies::WARN: |
3378 | | // WARN only when CMAKE_<LANG>_LINK_FLAGS is set, and when the current |
3379 | | // target is not an executable, and CMAKE_<LANG>_LINK_FLAGS is not equal |
3380 | | // to CMAKE_EXECUTABLE_CREATE_<LANG>_FLAGS. This warns users trying to |
3381 | | // use the NEW behavior on old projects (since CMake will be ignoring |
3382 | | // their wishes), while also exempting cases when the latter variable |
3383 | | // (substituted for the former spelling under the NEW behavior) is being |
3384 | | // used legitimately by CMake. |
3385 | 0 | if (!langLinkFlags.empty() && |
3386 | 0 | target->GetType() != cmStateEnums::EXECUTABLE && |
3387 | 0 | langLinkFlags != |
3388 | 0 | this->Makefile->GetSafeDefinition( |
3389 | 0 | cmStrCat("CMAKE_EXECUTABLE_CREATE_", lang, "_FLAGS"))) { |
3390 | 0 | this->IssueMessage( |
3391 | 0 | MessageType::AUTHOR_WARNING, |
3392 | 0 | cmStrCat(cmPolicies::GetPolicyWarning(cmPolicies::CMP0210), "\n", |
3393 | 0 | "For compatibility with older versions of CMake, ", |
3394 | 0 | "CMAKE_", lang, "_LINK_FLAGS will be ignored for target '", |
3395 | 0 | target->GetName(), "'.")); |
3396 | 0 | } |
3397 | 0 | CM_FALLTHROUGH; |
3398 | 0 | case cmPolicies::OLD: |
3399 | | // OLD behavior is to do nothing here, since the use of |
3400 | | // CMAKE_<LANG>_LINK_FLAGS for EXECUTABLEs is handled elsewhere. |
3401 | 0 | break; |
3402 | 0 | case cmPolicies::NEW: |
3403 | | // NEW behavior is to support per-language link flags for all target |
3404 | | // types. |
3405 | 0 | this->AppendLinkFlagsWithParsing(flags, langLinkFlags, target, lang); |
3406 | 0 | if (!config.empty()) { |
3407 | 0 | std::string lankLinkFlagsConfig = |
3408 | 0 | this->Makefile->GetSafeDefinition(cmStrCat( |
3409 | 0 | "CMAKE_", lang, "_LINK_FLAGS_", cmSystemTools::UpperCase(config))); |
3410 | 0 | this->AppendLinkFlagsWithParsing(flags, lankLinkFlagsConfig, target, |
3411 | 0 | lang); |
3412 | 0 | } |
3413 | 0 | break; |
3414 | 0 | } |
3415 | 0 | } |
3416 | | |
3417 | | void cmLocalGenerator::AppendTargetCreationLinkFlags( |
3418 | | std::string& flags, cmGeneratorTarget const* target, |
3419 | | std::string const& linkLanguage) |
3420 | 0 | { |
3421 | 0 | std::string createFlagsVar; |
3422 | 0 | cmValue createFlagsVal; |
3423 | 0 | switch (target->GetType()) { |
3424 | 0 | case cmStateEnums::STATIC_LIBRARY: |
3425 | 0 | break; |
3426 | 0 | case cmStateEnums::MODULE_LIBRARY: |
3427 | 0 | createFlagsVar = |
3428 | 0 | cmStrCat("CMAKE_SHARED_MODULE_CREATE_", linkLanguage, "_FLAGS"); |
3429 | 0 | createFlagsVal = this->Makefile->GetDefinition(createFlagsVar); |
3430 | | // On some platforms we use shared library creation flags for modules. |
3431 | 0 | CM_FALLTHROUGH; |
3432 | 0 | case cmStateEnums::SHARED_LIBRARY: |
3433 | 0 | if (!createFlagsVal) { |
3434 | 0 | createFlagsVar = |
3435 | 0 | cmStrCat("CMAKE_SHARED_LIBRARY_CREATE_", linkLanguage, "_FLAGS"); |
3436 | 0 | createFlagsVal = this->Makefile->GetDefinition(createFlagsVar); |
3437 | 0 | } |
3438 | 0 | break; |
3439 | 0 | case cmStateEnums::EXECUTABLE: |
3440 | 0 | createFlagsVar = target->GetPolicyStatusCMP0210() == cmPolicies::NEW |
3441 | 0 | ? cmStrCat("CMAKE_EXECUTABLE_CREATE_", linkLanguage, "_FLAGS") |
3442 | 0 | : cmStrCat("CMAKE_", linkLanguage, "_LINK_FLAGS"); |
3443 | 0 | createFlagsVal = this->Makefile->GetDefinition(createFlagsVar); |
3444 | 0 | break; |
3445 | 0 | default: |
3446 | 0 | break; |
3447 | 0 | } |
3448 | 0 | if (createFlagsVal) { |
3449 | 0 | this->AppendFlags(flags, *createFlagsVal, createFlagsVar, target, |
3450 | 0 | cmBuildStep::Link, linkLanguage); |
3451 | 0 | } |
3452 | 0 | } |
3453 | | |
3454 | | void cmLocalGenerator::AppendLinkerTypeFlags(std::string& flags, |
3455 | | cmGeneratorTarget* target, |
3456 | | std::string const& config, |
3457 | | std::string const& linkLanguage) |
3458 | 0 | { |
3459 | 0 | switch (target->GetType()) { |
3460 | 0 | case cmStateEnums::EXECUTABLE: |
3461 | 0 | case cmStateEnums::SHARED_LIBRARY: |
3462 | 0 | case cmStateEnums::MODULE_LIBRARY: |
3463 | 0 | break; |
3464 | 0 | default: |
3465 | 0 | return; |
3466 | 0 | } |
3467 | | |
3468 | 0 | auto linkMode = |
3469 | 0 | cmStrCat("CMAKE_", linkLanguage, target->IsDeviceLink() ? "_DEVICE_" : "_", |
3470 | 0 | "LINK_MODE"); |
3471 | 0 | auto mode = this->Makefile->GetDefinition(linkMode); |
3472 | 0 | if (mode && mode != "DRIVER"_s) { |
3473 | 0 | return; |
3474 | 0 | } |
3475 | | |
3476 | 0 | auto linkerType = target->GetLinkerTypeProperty(linkLanguage, config); |
3477 | 0 | if (linkerType.empty()) { |
3478 | 0 | linkerType = "DEFAULT"; |
3479 | 0 | } |
3480 | 0 | auto usingLinker = |
3481 | 0 | cmStrCat("CMAKE_", linkLanguage, "_USING_", |
3482 | 0 | target->IsDeviceLink() ? "DEVICE_" : "", "LINKER_", linkerType); |
3483 | 0 | auto linkerTypeFlags = this->Makefile->GetDefinition(usingLinker); |
3484 | 0 | if (linkerTypeFlags) { |
3485 | 0 | if (!linkerTypeFlags.IsEmpty()) { |
3486 | 0 | auto linkerFlags = cmExpandListWithBacktrace(linkerTypeFlags); |
3487 | 0 | target->ResolveLinkerWrapper(linkerFlags, linkLanguage); |
3488 | 0 | this->AppendFlags(flags, linkerFlags); |
3489 | 0 | } |
3490 | 0 | } else if (linkerType != "DEFAULT"_s) { |
3491 | 0 | auto isCMakeLinkerType = [](std::string const& type) -> bool { |
3492 | 0 | return std::all_of(type.cbegin(), type.cend(), |
3493 | 0 | [](char c) { return std::isupper(c); }); |
3494 | 0 | }; |
3495 | 0 | if (isCMakeLinkerType(linkerType)) { |
3496 | 0 | this->IssueMessage( |
3497 | 0 | MessageType::FATAL_ERROR, |
3498 | 0 | cmStrCat("LINKER_TYPE '", linkerType, |
3499 | 0 | "' is unknown or not supported by this toolchain.")); |
3500 | 0 | } else { |
3501 | 0 | this->IssueMessage( |
3502 | 0 | MessageType::FATAL_ERROR, |
3503 | 0 | cmStrCat("LINKER_TYPE '", linkerType, |
3504 | 0 | "' is unknown. Did you forget to define the '", usingLinker, |
3505 | 0 | "' variable?")); |
3506 | 0 | } |
3507 | 0 | } |
3508 | 0 | } |
3509 | | |
3510 | | void cmLocalGenerator::AddTargetTypeLinkerFlags( |
3511 | | std::string& flags, cmGeneratorTarget const* target, std::string const& lang, |
3512 | | std::string const& config) |
3513 | 0 | { |
3514 | 0 | std::string linkerFlagsVar; |
3515 | 0 | switch (target->GetType()) { |
3516 | 0 | case cmStateEnums::EXECUTABLE: |
3517 | 0 | linkerFlagsVar = "CMAKE_EXE_LINKER_FLAGS"; |
3518 | 0 | break; |
3519 | 0 | case cmStateEnums::SHARED_LIBRARY: |
3520 | 0 | linkerFlagsVar = "CMAKE_SHARED_LINKER_FLAGS"; |
3521 | 0 | break; |
3522 | 0 | case cmStateEnums::MODULE_LIBRARY: |
3523 | 0 | linkerFlagsVar = "CMAKE_MODULE_LINKER_FLAGS"; |
3524 | 0 | break; |
3525 | 0 | default: |
3526 | 0 | return; |
3527 | 0 | } |
3528 | 0 | this->AddConfigVariableFlags(flags, linkerFlagsVar, target, |
3529 | 0 | cmBuildStep::Link, lang, config); |
3530 | 0 | } |
3531 | | |
3532 | | void cmLocalGenerator::AddTargetPropertyLinkFlags( |
3533 | | std::string& flags, cmGeneratorTarget const* target, |
3534 | | std::string const& config) |
3535 | 0 | { |
3536 | 0 | cmValue targetLinkFlags = target->GetProperty("LINK_FLAGS"); |
3537 | 0 | if (targetLinkFlags) { |
3538 | 0 | this->AppendFlags(flags, *targetLinkFlags); |
3539 | 0 | } |
3540 | 0 | if (!config.empty()) { |
3541 | 0 | cmValue targetLinkFlagsConfig = target->GetProperty( |
3542 | 0 | cmStrCat("LINK_FLAGS_", cmSystemTools::UpperCase(config))); |
3543 | 0 | if (targetLinkFlagsConfig) { |
3544 | 0 | this->AppendFlags(flags, *targetLinkFlagsConfig); |
3545 | 0 | } |
3546 | 0 | } |
3547 | 0 | } |
3548 | | |
3549 | | void cmLocalGenerator::AppendIPOLinkerFlags(std::string& flags, |
3550 | | cmGeneratorTarget* target, |
3551 | | std::string const& config, |
3552 | | std::string const& lang) |
3553 | 0 | { |
3554 | 0 | if (!target->IsIPOEnabled(lang, config)) { |
3555 | 0 | return; |
3556 | 0 | } |
3557 | | |
3558 | 0 | switch (target->GetType()) { |
3559 | 0 | case cmStateEnums::EXECUTABLE: |
3560 | 0 | case cmStateEnums::SHARED_LIBRARY: |
3561 | 0 | case cmStateEnums::MODULE_LIBRARY: |
3562 | 0 | break; |
3563 | 0 | default: |
3564 | 0 | return; |
3565 | 0 | } |
3566 | | |
3567 | 0 | std::string const name = "CMAKE_" + lang + "_LINK_OPTIONS_IPO"; |
3568 | 0 | cmValue rawFlagsList = this->Makefile->GetDefinition(name); |
3569 | 0 | if (!rawFlagsList) { |
3570 | 0 | return; |
3571 | 0 | } |
3572 | | |
3573 | 0 | cmList flagsList{ *rawFlagsList }; |
3574 | 0 | for (std::string const& o : flagsList) { |
3575 | 0 | this->AppendFlagEscape(flags, o); |
3576 | 0 | } |
3577 | 0 | } |
3578 | | |
3579 | | void cmLocalGenerator::AppendPositionIndependentLinkerFlags( |
3580 | | std::string& flags, cmGeneratorTarget* target, std::string const& config, |
3581 | | std::string const& lang) |
3582 | 0 | { |
3583 | | // For now, only EXECUTABLE is concerned |
3584 | 0 | if (target->GetType() != cmStateEnums::EXECUTABLE) { |
3585 | 0 | return; |
3586 | 0 | } |
3587 | | |
3588 | 0 | char const* PICValue = target->GetLinkPIEProperty(config); |
3589 | 0 | if (!PICValue) { |
3590 | | // POSITION_INDEPENDENT_CODE is not set |
3591 | 0 | return; |
3592 | 0 | } |
3593 | | |
3594 | 0 | std::string const mode = cmIsOn(PICValue) ? "PIE" : "NO_PIE"; |
3595 | |
|
3596 | 0 | std::string supported = "CMAKE_" + lang + "_LINK_" + mode + "_SUPPORTED"; |
3597 | 0 | if (this->Makefile->GetDefinition(supported).IsOff()) { |
3598 | 0 | return; |
3599 | 0 | } |
3600 | | |
3601 | 0 | std::string name = "CMAKE_" + lang + "_LINK_OPTIONS_" + mode; |
3602 | |
|
3603 | 0 | auto pieFlags = this->Makefile->GetSafeDefinition(name); |
3604 | 0 | if (pieFlags.empty()) { |
3605 | 0 | return; |
3606 | 0 | } |
3607 | | |
3608 | 0 | cmList flagsList{ pieFlags }; |
3609 | 0 | for (auto const& flag : flagsList) { |
3610 | 0 | this->AppendFlagEscape(flags, flag); |
3611 | 0 | } |
3612 | 0 | } |
3613 | | |
3614 | | void cmLocalGenerator::AppendWarningAsErrorLinkerFlags( |
3615 | | std::string& flags, cmGeneratorTarget* target, std::string const& lang) |
3616 | 0 | { |
3617 | 0 | if (this->GetCMakeInstance()->GetIgnoreLinkWarningAsError()) { |
3618 | 0 | return; |
3619 | 0 | } |
3620 | | |
3621 | 0 | switch (target->GetType()) { |
3622 | 0 | case cmStateEnums::EXECUTABLE: |
3623 | 0 | case cmStateEnums::SHARED_LIBRARY: |
3624 | 0 | case cmStateEnums::MODULE_LIBRARY: |
3625 | 0 | break; |
3626 | 0 | default: |
3627 | 0 | return; |
3628 | 0 | } |
3629 | | |
3630 | 0 | auto const wError = target->GetProperty("LINK_WARNING_AS_ERROR"); |
3631 | 0 | if (wError.IsOff()) { |
3632 | 0 | return; |
3633 | 0 | } |
3634 | 0 | cmList wErrorOptions; |
3635 | 0 | if (wError.IsOn()) { |
3636 | 0 | wErrorOptions = { "DRIVER", "LINKER" }; |
3637 | 0 | } else { |
3638 | 0 | wErrorOptions = wError; |
3639 | 0 | std::sort(wErrorOptions.begin(), wErrorOptions.end()); |
3640 | 0 | wErrorOptions.erase( |
3641 | 0 | std::unique(wErrorOptions.begin(), wErrorOptions.end()), |
3642 | 0 | wErrorOptions.end()); |
3643 | 0 | } |
3644 | |
|
3645 | 0 | auto linkModeIsDriver = |
3646 | 0 | this->Makefile->GetDefinition(cmStrCat("CMAKE_", lang, "_LINK_MODE")) == |
3647 | 0 | "DRIVER"_s; |
3648 | 0 | std::string errorMessage; |
3649 | 0 | for (auto const& option : wErrorOptions) { |
3650 | 0 | if (option != "DRIVER"_s && option != "LINKER"_s) { |
3651 | 0 | errorMessage += cmStrCat(" ", option, '\n'); |
3652 | 0 | continue; |
3653 | 0 | } |
3654 | | |
3655 | 0 | if (option == "DRIVER"_s && !linkModeIsDriver) { |
3656 | 0 | continue; |
3657 | 0 | } |
3658 | | |
3659 | 0 | auto const wErrorOpts = this->Makefile->GetDefinition(cmStrCat( |
3660 | 0 | "CMAKE_", lang, '_', (option == "DRIVER"_s ? "COMPILE" : "LINK"), |
3661 | 0 | "_OPTIONS_WARNING_AS_ERROR")); |
3662 | 0 | if (wErrorOpts.IsSet()) { |
3663 | 0 | auto items = |
3664 | 0 | cmExpandListWithBacktrace(wErrorOpts, target->GetBacktrace()); |
3665 | 0 | if (option == "LINKER"_s) { |
3666 | 0 | target->ResolveLinkerWrapper(items, lang); |
3667 | 0 | } |
3668 | 0 | for (auto const& item : items) { |
3669 | 0 | this->AppendFlagEscape(flags, item.Value); |
3670 | 0 | } |
3671 | 0 | } |
3672 | 0 | } |
3673 | 0 | if (!errorMessage.empty()) { |
3674 | 0 | this->Makefile->GetCMakeInstance()->IssueMessage( |
3675 | 0 | MessageType::FATAL_ERROR, |
3676 | 0 | cmStrCat( |
3677 | 0 | "Erroneous value(s) for 'LINK_WARNING_AS_ERROR' property of target '", |
3678 | 0 | target->GetName(), "':\n", errorMessage)); |
3679 | 0 | } |
3680 | 0 | } |
3681 | | |
3682 | | void cmLocalGenerator::AppendDependencyInfoLinkerFlags( |
3683 | | std::string& flags, cmGeneratorTarget* target, std::string const& config, |
3684 | | std::string const& linkLanguage) |
3685 | 0 | { |
3686 | 0 | if (!this->GetGlobalGenerator()->SupportsLinkerDependencyFile() || |
3687 | 0 | !target->HasLinkDependencyFile(config)) { |
3688 | 0 | return; |
3689 | 0 | } |
3690 | | |
3691 | 0 | auto depFlag = *this->Makefile->GetDefinition( |
3692 | 0 | cmStrCat("CMAKE_", linkLanguage, "_LINKER_DEPFILE_FLAGS")); |
3693 | 0 | if (depFlag.empty()) { |
3694 | 0 | return; |
3695 | 0 | } |
3696 | | |
3697 | 0 | auto depFile = this->ConvertToOutputFormat( |
3698 | 0 | this->MaybeRelativeToWorkDir(this->GetLinkDependencyFile(target, config)), |
3699 | 0 | cmOutputConverter::SHELL); |
3700 | 0 | auto rulePlaceholderExpander = this->CreateRulePlaceholderExpander(); |
3701 | 0 | cmRulePlaceholderExpander::RuleVariables linkDepsVariables; |
3702 | 0 | linkDepsVariables.DependencyFile = depFile.c_str(); |
3703 | 0 | rulePlaceholderExpander->ExpandRuleVariables(this, depFlag, |
3704 | 0 | linkDepsVariables); |
3705 | 0 | auto depFlags = cmExpandListWithBacktrace(depFlag); |
3706 | 0 | target->ResolveLinkerWrapper(depFlags, linkLanguage); |
3707 | |
|
3708 | 0 | this->AppendFlags(flags, depFlags); |
3709 | 0 | } |
3710 | | |
3711 | | std::string cmLocalGenerator::GetLinkDependencyFile( |
3712 | | cmGeneratorTarget* /*target*/, std::string const& /*config*/) const |
3713 | 0 | { |
3714 | 0 | return "link.d"; |
3715 | 0 | } |
3716 | | |
3717 | | void cmLocalGenerator::AppendModuleDefinitionFlag( |
3718 | | std::string& flags, cmGeneratorTarget const* target, |
3719 | | cmLinkLineComputer* linkLineComputer, std::string const& config, |
3720 | | std::string const& lang) |
3721 | 0 | { |
3722 | 0 | cmGeneratorTarget::ModuleDefinitionInfo const* mdi = |
3723 | 0 | target->GetModuleDefinitionInfo(config); |
3724 | 0 | if (!mdi || mdi->DefFile.empty()) { |
3725 | 0 | return; |
3726 | 0 | } |
3727 | | |
3728 | 0 | cmValue defFileFlag = this->Makefile->GetDefinition( |
3729 | 0 | cmStrCat("CMAKE_", lang, "_LINK_DEF_FILE_FLAG")); |
3730 | 0 | if (!defFileFlag) { |
3731 | 0 | defFileFlag = this->Makefile->GetDefinition("CMAKE_LINK_DEF_FILE_FLAG"); |
3732 | 0 | } |
3733 | 0 | if (!defFileFlag) { |
3734 | 0 | return; |
3735 | 0 | } |
3736 | | |
3737 | | // Append the flag and value. Use ConvertToLinkReference to help |
3738 | | // vs6's "cl -link" pass it to the linker. |
3739 | 0 | std::string flag = |
3740 | 0 | cmStrCat(*defFileFlag, |
3741 | 0 | this->ConvertToOutputFormat( |
3742 | 0 | linkLineComputer->ConvertToLinkReference(mdi->DefFile), |
3743 | 0 | cmOutputConverter::SHELL)); |
3744 | 0 | this->AppendFlags(flags, flag); |
3745 | 0 | } |
3746 | | |
3747 | | bool cmLocalGenerator::AppendLWYUFlags(std::string& flags, |
3748 | | cmGeneratorTarget const* target, |
3749 | | std::string const& lang) |
3750 | 0 | { |
3751 | 0 | auto useLWYU = target->GetPropertyAsBool("LINK_WHAT_YOU_USE") && |
3752 | 0 | (target->GetType() == cmStateEnums::TargetType::EXECUTABLE || |
3753 | 0 | target->GetType() == cmStateEnums::TargetType::SHARED_LIBRARY || |
3754 | 0 | target->GetType() == cmStateEnums::TargetType::MODULE_LIBRARY); |
3755 | |
|
3756 | 0 | if (useLWYU) { |
3757 | 0 | auto const& lwyuFlag = this->GetMakefile()->GetSafeDefinition( |
3758 | 0 | cmStrCat("CMAKE_", lang, "_LINK_WHAT_YOU_USE_FLAG")); |
3759 | 0 | useLWYU = !lwyuFlag.empty(); |
3760 | |
|
3761 | 0 | if (useLWYU) { |
3762 | 0 | std::vector<BT<std::string>> lwyuOpts; |
3763 | 0 | lwyuOpts.emplace_back(lwyuFlag); |
3764 | 0 | this->AppendFlags(flags, target->ResolveLinkerWrapper(lwyuOpts, lang)); |
3765 | 0 | } |
3766 | 0 | } |
3767 | |
|
3768 | 0 | return useLWYU; |
3769 | 0 | } |
3770 | | |
3771 | | void cmLocalGenerator::AppendCompileOptions(std::string& options, |
3772 | | std::string const& options_list, |
3773 | | char const* regex) const |
3774 | 0 | { |
3775 | | // Short-circuit if there are no options. |
3776 | 0 | if (options_list.empty()) { |
3777 | 0 | return; |
3778 | 0 | } |
3779 | | |
3780 | | // Expand the list of options. |
3781 | 0 | cmList options_vec{ options_list }; |
3782 | 0 | this->AppendCompileOptions(options, options_vec, regex); |
3783 | 0 | } |
3784 | | |
3785 | | void cmLocalGenerator::AppendCompileOptions( |
3786 | | std::string& options, std::vector<std::string> const& options_vec, |
3787 | | char const* regex) const |
3788 | 0 | { |
3789 | 0 | if (regex) { |
3790 | | // Filter flags upon specified reges. |
3791 | 0 | cmsys::RegularExpression r(regex); |
3792 | |
|
3793 | 0 | for (std::string const& opt : options_vec) { |
3794 | 0 | if (r.find(opt)) { |
3795 | 0 | this->AppendFlagEscape(options, opt); |
3796 | 0 | } |
3797 | 0 | } |
3798 | 0 | } else { |
3799 | 0 | for (std::string const& opt : options_vec) { |
3800 | 0 | this->AppendFlagEscape(options, opt); |
3801 | 0 | } |
3802 | 0 | } |
3803 | 0 | } |
3804 | | |
3805 | | void cmLocalGenerator::AppendCompileOptions( |
3806 | | std::vector<BT<std::string>>& options, |
3807 | | std::vector<BT<std::string>> const& options_vec, char const* regex) const |
3808 | 0 | { |
3809 | 0 | if (regex) { |
3810 | | // Filter flags upon specified regular expressions. |
3811 | 0 | cmsys::RegularExpression r(regex); |
3812 | |
|
3813 | 0 | for (BT<std::string> const& opt : options_vec) { |
3814 | 0 | if (r.find(opt.Value)) { |
3815 | 0 | std::string flag; |
3816 | 0 | this->AppendFlagEscape(flag, opt.Value); |
3817 | 0 | options.emplace_back(std::move(flag), opt.Backtrace); |
3818 | 0 | } |
3819 | 0 | } |
3820 | 0 | } else { |
3821 | 0 | for (BT<std::string> const& opt : options_vec) { |
3822 | 0 | std::string flag; |
3823 | 0 | this->AppendFlagEscape(flag, opt.Value); |
3824 | 0 | options.emplace_back(std::move(flag), opt.Backtrace); |
3825 | 0 | } |
3826 | 0 | } |
3827 | 0 | } |
3828 | | |
3829 | | void cmLocalGenerator::AppendIncludeDirectories( |
3830 | | std::vector<std::string>& includes, std::string const& includes_list, |
3831 | | cmSourceFile const& sourceFile) const |
3832 | 0 | { |
3833 | | // Short-circuit if there are no includes. |
3834 | 0 | if (includes_list.empty()) { |
3835 | 0 | return; |
3836 | 0 | } |
3837 | | |
3838 | | // Expand the list of includes. |
3839 | 0 | cmList includes_vec{ includes_list }; |
3840 | 0 | this->AppendIncludeDirectories(includes, includes_vec, sourceFile); |
3841 | 0 | } |
3842 | | |
3843 | | void cmLocalGenerator::AppendIncludeDirectories( |
3844 | | std::vector<std::string>& includes, |
3845 | | std::vector<std::string> const& includes_vec, |
3846 | | cmSourceFile const& sourceFile) const |
3847 | 0 | { |
3848 | 0 | std::unordered_set<std::string> uniqueIncludes; |
3849 | |
|
3850 | 0 | for (std::string const& include : includes_vec) { |
3851 | 0 | if (!cmSystemTools::FileIsFullPath(include)) { |
3852 | 0 | std::ostringstream e; |
3853 | 0 | e << "Found relative path while evaluating include directories of " |
3854 | 0 | "\"" |
3855 | 0 | << sourceFile.GetLocation().GetName() << "\":\n \"" << include |
3856 | 0 | << "\"\n"; |
3857 | |
|
3858 | 0 | this->IssueMessage(MessageType::FATAL_ERROR, e.str()); |
3859 | 0 | return; |
3860 | 0 | } |
3861 | | |
3862 | 0 | std::string inc = include; |
3863 | |
|
3864 | 0 | if (!cmIsOff(inc)) { |
3865 | 0 | cmSystemTools::ConvertToUnixSlashes(inc); |
3866 | 0 | } |
3867 | |
|
3868 | 0 | if (uniqueIncludes.insert(inc).second) { |
3869 | 0 | includes.push_back(std::move(inc)); |
3870 | 0 | } |
3871 | 0 | } |
3872 | 0 | } |
3873 | | |
3874 | | void cmLocalGenerator::AppendDefines(std::set<std::string>& defines, |
3875 | | std::string const& defines_list) const |
3876 | 0 | { |
3877 | 0 | std::set<BT<std::string>> tmp; |
3878 | 0 | this->AppendDefines(tmp, cmExpandListWithBacktrace(defines_list)); |
3879 | 0 | for (BT<std::string> const& i : tmp) { |
3880 | 0 | defines.emplace(i.Value); |
3881 | 0 | } |
3882 | 0 | } |
3883 | | |
3884 | | void cmLocalGenerator::AppendDefines(std::set<BT<std::string>>& defines, |
3885 | | std::string const& defines_list) const |
3886 | 0 | { |
3887 | | // Short-circuit if there are no definitions. |
3888 | 0 | if (defines_list.empty()) { |
3889 | 0 | return; |
3890 | 0 | } |
3891 | | |
3892 | | // Expand the list of definitions. |
3893 | 0 | this->AppendDefines(defines, cmExpandListWithBacktrace(defines_list)); |
3894 | 0 | } |
3895 | | |
3896 | | void cmLocalGenerator::AppendDefines( |
3897 | | std::set<BT<std::string>>& defines, |
3898 | | std::vector<BT<std::string>> const& defines_vec) const |
3899 | 0 | { |
3900 | 0 | for (BT<std::string> const& d : defines_vec) { |
3901 | | // Skip unsupported definitions. |
3902 | 0 | if (!this->CheckDefinition(d.Value)) { |
3903 | 0 | continue; |
3904 | 0 | } |
3905 | | // remove any leading -D |
3906 | 0 | if (cmHasLiteralPrefix(d.Value, "-D")) { |
3907 | 0 | defines.emplace(d.Value.substr(2), d.Backtrace); |
3908 | 0 | } else { |
3909 | 0 | defines.insert(d); |
3910 | 0 | } |
3911 | 0 | } |
3912 | 0 | } |
3913 | | |
3914 | | void cmLocalGenerator::JoinDefines(std::set<std::string> const& defines, |
3915 | | std::string& definesString, |
3916 | | std::string const& lang) |
3917 | 0 | { |
3918 | | // Lookup the define flag for the current language. |
3919 | 0 | std::string dflag = "-D"; |
3920 | 0 | if (!lang.empty()) { |
3921 | 0 | cmValue df = |
3922 | 0 | this->Makefile->GetDefinition(cmStrCat("CMAKE_", lang, "_DEFINE_FLAG")); |
3923 | 0 | if (cmNonempty(df)) { |
3924 | 0 | dflag = *df; |
3925 | 0 | } |
3926 | 0 | } |
3927 | 0 | char const* itemSeparator = definesString.empty() ? "" : " "; |
3928 | 0 | for (std::string const& define : defines) { |
3929 | | // Append the definition with proper escaping. |
3930 | 0 | std::string def = dflag; |
3931 | 0 | if (this->GetState()->UseWatcomWMake()) { |
3932 | | // The Watcom compiler does its own command line parsing instead |
3933 | | // of using the windows shell rules. Definitions are one of |
3934 | | // -DNAME |
3935 | | // -DNAME=<cpp-token> |
3936 | | // -DNAME="c-string with spaces and other characters(?@#$)" |
3937 | | // |
3938 | | // Watcom will properly parse each of these cases from the |
3939 | | // command line without any escapes. However we still have to |
3940 | | // get the '$' and '#' characters through WMake as '$$' and |
3941 | | // '$#'. |
3942 | 0 | for (char c : define) { |
3943 | 0 | if (c == '$' || c == '#') { |
3944 | 0 | def += '$'; |
3945 | 0 | } |
3946 | 0 | def += c; |
3947 | 0 | } |
3948 | 0 | } else { |
3949 | | // Make the definition appear properly on the command line. Use |
3950 | | // -DNAME="value" instead of -D"NAME=value" for historical reasons. |
3951 | 0 | std::string::size_type eq = define.find('='); |
3952 | 0 | def += define.substr(0, eq); |
3953 | 0 | if (eq != std::string::npos) { |
3954 | 0 | def += "="; |
3955 | 0 | def += this->EscapeForShell(define.substr(eq + 1), true); |
3956 | 0 | } |
3957 | 0 | } |
3958 | 0 | definesString += itemSeparator; |
3959 | 0 | itemSeparator = " "; |
3960 | 0 | definesString += def; |
3961 | 0 | } |
3962 | 0 | } |
3963 | | |
3964 | | void cmLocalGenerator::AppendFeatureOptions(std::string& flags, |
3965 | | std::string const& lang, |
3966 | | char const* feature) |
3967 | 0 | { |
3968 | 0 | cmValue optionList = this->Makefile->GetDefinition( |
3969 | 0 | cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_", feature)); |
3970 | 0 | if (optionList) { |
3971 | 0 | cmList options{ *optionList }; |
3972 | 0 | for (std::string const& o : options) { |
3973 | 0 | this->AppendFlagEscape(flags, o); |
3974 | 0 | } |
3975 | 0 | } |
3976 | 0 | } |
3977 | | |
3978 | | cmValue cmLocalGenerator::GetFeature(std::string const& feature, |
3979 | | std::string const& config) |
3980 | 0 | { |
3981 | 0 | std::string featureName = feature; |
3982 | | // TODO: Define accumulation policy for features (prepend, append, |
3983 | | // replace). Currently we always replace. |
3984 | 0 | if (!config.empty()) { |
3985 | 0 | featureName += "_"; |
3986 | 0 | featureName += cmSystemTools::UpperCase(config); |
3987 | 0 | } |
3988 | 0 | cmStateSnapshot snp = this->StateSnapshot; |
3989 | 0 | while (snp.IsValid()) { |
3990 | 0 | if (cmValue value = snp.GetDirectory().GetProperty(featureName)) { |
3991 | 0 | return value; |
3992 | 0 | } |
3993 | 0 | snp = snp.GetBuildsystemDirectoryParent(); |
3994 | 0 | } |
3995 | 0 | return nullptr; |
3996 | 0 | } |
3997 | | |
3998 | | std::string cmLocalGenerator::GetProjectName() const |
3999 | 0 | { |
4000 | 0 | return this->StateSnapshot.GetProjectName(); |
4001 | 0 | } |
4002 | | |
4003 | | std::string cmLocalGenerator::ConstructComment( |
4004 | | cmCustomCommandGenerator const& ccg, char const* default_comment) const |
4005 | 0 | { |
4006 | | // Check for a comment provided with the command. |
4007 | 0 | if (cm::optional<std::string> comment = ccg.GetComment()) { |
4008 | 0 | return *comment; |
4009 | 0 | } |
4010 | | |
4011 | | // Construct a reasonable default comment if possible. |
4012 | 0 | if (!ccg.GetOutputs().empty()) { |
4013 | 0 | std::string comment; |
4014 | 0 | comment = "Generating "; |
4015 | 0 | char const* sep = ""; |
4016 | 0 | for (std::string const& o : ccg.GetOutputs()) { |
4017 | 0 | comment += sep; |
4018 | 0 | comment += this->MaybeRelativeToCurBinDir(o); |
4019 | 0 | sep = ", "; |
4020 | 0 | } |
4021 | 0 | return comment; |
4022 | 0 | } |
4023 | | |
4024 | | // Otherwise use the provided default. |
4025 | 0 | return default_comment; |
4026 | 0 | } |
4027 | | |
4028 | | class cmInstallTargetGeneratorLocal : public cmInstallTargetGenerator |
4029 | | { |
4030 | | public: |
4031 | | cmInstallTargetGeneratorLocal(cmLocalGenerator* lg, std::string const& t, |
4032 | | std::string const& dest, bool implib) |
4033 | 0 | : cmInstallTargetGenerator( |
4034 | 0 | t, dest, implib, "", std::vector<std::string>(), "Unspecified", |
4035 | 0 | cmInstallGenerator::SelectMessageLevel(lg->GetMakefile()), false, |
4036 | 0 | false) |
4037 | 0 | { |
4038 | 0 | this->Compute(lg); |
4039 | 0 | } |
4040 | | }; |
4041 | | |
4042 | | void cmLocalGenerator::GenerateTargetInstallRules( |
4043 | | std::ostream& os, std::string const& config, |
4044 | | std::vector<std::string> const& configurationTypes) |
4045 | 0 | { |
4046 | | // Convert the old-style install specification from each target to |
4047 | | // an install generator and run it. |
4048 | 0 | auto const& tgts = this->GetGeneratorTargets(); |
4049 | 0 | for (auto const& l : tgts) { |
4050 | 0 | if (l->GetType() == cmStateEnums::INTERFACE_LIBRARY) { |
4051 | 0 | continue; |
4052 | 0 | } |
4053 | | |
4054 | | // Include the user-specified pre-install script for this target. |
4055 | 0 | if (cmValue preinstall = l->GetProperty("PRE_INSTALL_SCRIPT")) { |
4056 | 0 | cmInstallScriptGenerator g(*preinstall, false, "", false, false); |
4057 | 0 | g.Generate(os, config, configurationTypes); |
4058 | 0 | } |
4059 | | |
4060 | | // Install this target if a destination is given. |
4061 | 0 | if (!l->Target->GetInstallPath().empty()) { |
4062 | | // Compute the full install destination. Note that converting |
4063 | | // to unix slashes also removes any trailing slash. |
4064 | | // We also skip over the leading slash given by the user. |
4065 | 0 | std::string destination = l->Target->GetInstallPath().substr(1); |
4066 | 0 | cmSystemTools::ConvertToUnixSlashes(destination); |
4067 | 0 | if (destination.empty()) { |
4068 | 0 | destination = "."; |
4069 | 0 | } |
4070 | | |
4071 | | // Generate the proper install generator for this target type. |
4072 | 0 | switch (l->GetType()) { |
4073 | 0 | case cmStateEnums::EXECUTABLE: |
4074 | 0 | case cmStateEnums::STATIC_LIBRARY: |
4075 | 0 | case cmStateEnums::MODULE_LIBRARY: { |
4076 | | // Use a target install generator. |
4077 | 0 | cmInstallTargetGeneratorLocal g(this, l->GetName(), destination, |
4078 | 0 | false); |
4079 | 0 | g.Generate(os, config, configurationTypes); |
4080 | 0 | } break; |
4081 | 0 | case cmStateEnums::SHARED_LIBRARY: { |
4082 | | #if defined(_WIN32) || defined(__CYGWIN__) |
4083 | | // Special code to handle DLL. Install the import library |
4084 | | // to the normal destination and the DLL to the runtime |
4085 | | // destination. |
4086 | | cmInstallTargetGeneratorLocal g1(this, l->GetName(), destination, |
4087 | | true); |
4088 | | g1.Generate(os, config, configurationTypes); |
4089 | | // We also skip over the leading slash given by the user. |
4090 | | destination = l->Target->GetRuntimeInstallPath().substr(1); |
4091 | | cmSystemTools::ConvertToUnixSlashes(destination); |
4092 | | cmInstallTargetGeneratorLocal g2(this, l->GetName(), destination, |
4093 | | false); |
4094 | | g2.Generate(os, config, configurationTypes); |
4095 | | #else |
4096 | | // Use a target install generator. |
4097 | 0 | cmInstallTargetGeneratorLocal g(this, l->GetName(), destination, |
4098 | 0 | false); |
4099 | 0 | g.Generate(os, config, configurationTypes); |
4100 | 0 | #endif |
4101 | 0 | } break; |
4102 | 0 | default: |
4103 | 0 | break; |
4104 | 0 | } |
4105 | 0 | } |
4106 | | |
4107 | | // Include the user-specified post-install script for this target. |
4108 | 0 | if (cmValue postinstall = l->GetProperty("POST_INSTALL_SCRIPT")) { |
4109 | 0 | cmInstallScriptGenerator g(*postinstall, false, "", false, false); |
4110 | 0 | g.Generate(os, config, configurationTypes); |
4111 | 0 | } |
4112 | 0 | } |
4113 | 0 | } |
4114 | | |
4115 | | namespace { |
4116 | | bool cmLocalGeneratorShortenObjectName(std::string& objName, |
4117 | | std::string::size_type max_len) |
4118 | 0 | { |
4119 | | // Check if the path can be shortened using an md5 sum replacement for |
4120 | | // a portion of the path. |
4121 | 0 | std::string::size_type md5Len = 32; |
4122 | 0 | std::string::size_type numExtraChars = objName.size() - max_len + md5Len; |
4123 | 0 | std::string::size_type pos = objName.find('/', numExtraChars); |
4124 | 0 | if (pos == std::string::npos) { |
4125 | 0 | pos = objName.rfind('/', numExtraChars); |
4126 | 0 | if (pos == std::string::npos || pos <= md5Len) { |
4127 | 0 | return false; |
4128 | 0 | } |
4129 | 0 | } |
4130 | | |
4131 | | // Replace the beginning of the path portion of the object name with |
4132 | | // its own md5 sum. |
4133 | 0 | cmCryptoHash md5(cmCryptoHash::AlgoMD5); |
4134 | 0 | std::string md5name = cmStrCat(md5.HashString(objName.substr(0, pos)), |
4135 | 0 | cm::string_view(objName).substr(pos)); |
4136 | 0 | objName = md5name; |
4137 | | |
4138 | | // The object name is now shorter, check if it is short enough. |
4139 | 0 | return pos >= numExtraChars; |
4140 | 0 | } |
4141 | | |
4142 | | bool cmLocalGeneratorCheckObjectName(std::string& objName, |
4143 | | std::string::size_type dir_len, |
4144 | | std::string::size_type max_total_len) |
4145 | 0 | { |
4146 | | // Enforce the maximum file name length if possible. |
4147 | 0 | std::string::size_type max_obj_len = max_total_len; |
4148 | 0 | if (dir_len < max_total_len) { |
4149 | 0 | max_obj_len = max_total_len - dir_len; |
4150 | 0 | if (objName.size() > max_obj_len) { |
4151 | | // The current object file name is too long. Try to shorten it. |
4152 | 0 | return cmLocalGeneratorShortenObjectName(objName, max_obj_len); |
4153 | 0 | } |
4154 | | // The object file name is short enough. |
4155 | 0 | return true; |
4156 | 0 | } |
4157 | | // The build directory in which the object will be stored is |
4158 | | // already too deep. |
4159 | 0 | return false; |
4160 | 0 | } |
4161 | | } |
4162 | | |
4163 | | std::string cmLocalGenerator::CreateSafeObjectFileName( |
4164 | | std::string const& sin) const |
4165 | 0 | { |
4166 | | // Start with the original name. |
4167 | 0 | std::string ssin = sin; |
4168 | | |
4169 | | // Avoid full paths by removing leading slashes. |
4170 | 0 | ssin.erase(0, ssin.find_first_not_of('/')); |
4171 | | |
4172 | | // Avoid full paths by removing colons. |
4173 | 0 | std::replace(ssin.begin(), ssin.end(), ':', '_'); |
4174 | | |
4175 | | // Avoid relative paths that go up the tree. |
4176 | 0 | cmSystemTools::ReplaceString(ssin, "../", "__/"); |
4177 | | |
4178 | | // Avoid spaces. |
4179 | 0 | std::replace(ssin.begin(), ssin.end(), ' ', '_'); |
4180 | |
|
4181 | 0 | return ssin; |
4182 | 0 | } |
4183 | | |
4184 | | void cmLocalGenerator::ComputeSourceGroupSearchIndex() |
4185 | 0 | { |
4186 | 0 | #if !defined(CMAKE_BOOTSTRAP) |
4187 | 0 | SourceGroupVector const& sourceGroups = this->Makefile->GetSourceGroups(); |
4188 | | |
4189 | | // Build lookup index from sources to source groups |
4190 | 0 | std::queue<cmSourceGroup*> sgToVisit; |
4191 | 0 | for (auto const& group : sourceGroups) { |
4192 | 0 | cmSourceGroup* cmSourceGroup = group.get(); |
4193 | 0 | sgToVisit.emplace(cmSourceGroup); |
4194 | 0 | } |
4195 | |
|
4196 | 0 | while (!sgToVisit.empty()) { |
4197 | 0 | cmSourceGroup* sourceGroup = sgToVisit.front(); |
4198 | 0 | sgToVisit.pop(); |
4199 | 0 | for (auto const& sgChild : sourceGroup->GetGroupChildren()) { |
4200 | 0 | sgToVisit.emplace(sgChild.get()); |
4201 | 0 | } |
4202 | 0 | for (std::string const& source : sourceGroup->GetGroupFiles()) { |
4203 | 0 | this->SourceGroupSearchIndex.emplace(source, sourceGroup); |
4204 | 0 | } |
4205 | 0 | } |
4206 | 0 | #endif |
4207 | 0 | } |
4208 | | |
4209 | | cmSourceGroup* cmLocalGenerator::FindSourceGroup(std::string const& source) |
4210 | 0 | { |
4211 | 0 | #if !defined(CMAKE_BOOTSTRAP) |
4212 | 0 | auto const indexIt = SourceGroupSearchIndex.find(source); |
4213 | 0 | if (indexIt != SourceGroupSearchIndex.cend()) { |
4214 | 0 | if (cmSourceGroup* result = indexIt->second) { |
4215 | 0 | return result; |
4216 | 0 | } |
4217 | 0 | } |
4218 | | |
4219 | 0 | cmSourceGroup* sourceGroup = |
4220 | 0 | cmSourceGroup::FindSourceGroup(source, this->Makefile->GetSourceGroups()); |
4221 | 0 | if (sourceGroup) { |
4222 | | // Update index if we have a miss |
4223 | 0 | SourceGroupSearchIndex.emplace(source, sourceGroup); |
4224 | 0 | } |
4225 | 0 | return sourceGroup; |
4226 | | #else |
4227 | | static_cast<void>(source); |
4228 | | return nullptr; |
4229 | | #endif |
4230 | 0 | } |
4231 | | |
4232 | | std::string& cmLocalGenerator::CreateSafeUniqueObjectFileName( |
4233 | | std::string const& sin, std::string const& dir_max) |
4234 | 0 | { |
4235 | | // Look for an existing mapped name for this object file. |
4236 | 0 | auto it = this->UniqueObjectNamesMap.find(sin); |
4237 | | |
4238 | | // If no entry exists create one. |
4239 | 0 | if (it == this->UniqueObjectNamesMap.end()) { |
4240 | 0 | auto ssin = this->CreateSafeObjectFileName(sin); |
4241 | | |
4242 | | // Mangle the name if necessary. |
4243 | 0 | if (this->Makefile->IsOn("CMAKE_MANGLE_OBJECT_FILE_NAMES")) { |
4244 | 0 | bool done; |
4245 | 0 | int cc = 0; |
4246 | 0 | char rpstr[100]; |
4247 | 0 | snprintf(rpstr, sizeof(rpstr), "_p_"); |
4248 | 0 | cmSystemTools::ReplaceString(ssin, "+", rpstr); |
4249 | 0 | std::string sssin = sin; |
4250 | 0 | do { |
4251 | 0 | done = true; |
4252 | 0 | for (it = this->UniqueObjectNamesMap.begin(); |
4253 | 0 | it != this->UniqueObjectNamesMap.end(); ++it) { |
4254 | 0 | if (it->second == ssin) { |
4255 | 0 | done = false; |
4256 | 0 | } |
4257 | 0 | } |
4258 | 0 | if (done) { |
4259 | 0 | break; |
4260 | 0 | } |
4261 | 0 | sssin = ssin; |
4262 | 0 | cmSystemTools::ReplaceString(ssin, "_p_", rpstr); |
4263 | 0 | snprintf(rpstr, sizeof(rpstr), "_p%d_", cc++); |
4264 | 0 | } while (!done); |
4265 | 0 | } |
4266 | |
|
4267 | 0 | if (!cmLocalGeneratorCheckObjectName(ssin, dir_max.size(), |
4268 | 0 | this->ObjectPathMax)) { |
4269 | | // Warn if this is the first time the path has been seen. |
4270 | 0 | if (this->ObjectMaxPathViolations.insert(dir_max).second) { |
4271 | 0 | std::ostringstream m; |
4272 | | /* clang-format off */ |
4273 | 0 | m << "The object file directory\n" |
4274 | 0 | << " " << dir_max << "\n" |
4275 | 0 | << "has " << dir_max.size() << " characters. " |
4276 | 0 | << "The maximum full path to an object file is " |
4277 | 0 | << this->ObjectPathMax << " characters " |
4278 | 0 | << "(see CMAKE_OBJECT_PATH_MAX). " |
4279 | 0 | << "Object file\n" |
4280 | 0 | << " " << ssin << "\n" |
4281 | 0 | << "cannot be safely placed under this directory. " |
4282 | 0 | << "The build may not work correctly."; |
4283 | | /* clang-format on */ |
4284 | 0 | this->IssueMessage(MessageType::WARNING, m.str()); |
4285 | 0 | } |
4286 | 0 | } |
4287 | | |
4288 | | // Insert the newly mapped object file name. |
4289 | 0 | std::map<std::string, std::string>::value_type e(sin, ssin); |
4290 | 0 | it = this->UniqueObjectNamesMap.insert(e).first; |
4291 | 0 | } |
4292 | | |
4293 | | // Return the map entry. |
4294 | 0 | return it->second; |
4295 | 0 | } |
4296 | | |
4297 | | void cmLocalGenerator::ComputeObjectFilenames( |
4298 | | std::map<cmSourceFile const*, cmObjectLocations>& /*unused*/, |
4299 | | std::string const& /*unused*/, cmGeneratorTarget const* /*unused*/) |
4300 | 0 | { |
4301 | 0 | } |
4302 | | |
4303 | | bool cmLocalGenerator::IsWindowsShell() const |
4304 | 0 | { |
4305 | 0 | return this->GetState()->UseWindowsShell(); |
4306 | 0 | } |
4307 | | |
4308 | | bool cmLocalGenerator::IsWatcomWMake() const |
4309 | 0 | { |
4310 | 0 | return this->GetState()->UseWatcomWMake(); |
4311 | 0 | } |
4312 | | |
4313 | | bool cmLocalGenerator::IsMinGWMake() const |
4314 | 0 | { |
4315 | 0 | return this->GetState()->UseMinGWMake(); |
4316 | 0 | } |
4317 | | |
4318 | | bool cmLocalGenerator::IsNMake() const |
4319 | 0 | { |
4320 | 0 | return this->GetState()->UseNMake(); |
4321 | 0 | } |
4322 | | |
4323 | | bool cmLocalGenerator::IsNinjaMulti() const |
4324 | 0 | { |
4325 | 0 | return this->GetState()->UseNinjaMulti(); |
4326 | 0 | } |
4327 | | |
4328 | | bool cmLocalGenerator::IsWindowsVSIDE() const |
4329 | 0 | { |
4330 | 0 | return this->GetState()->UseWindowsVSIDE(); |
4331 | 0 | } |
4332 | | |
4333 | | namespace { |
4334 | | std::string relativeIfUnder(std::string const& top, std::string const& cur, |
4335 | | std::string const& path) |
4336 | 0 | { |
4337 | | // Use a path relative to 'cur' if it can be expressed without |
4338 | | // a `../` sequence that leaves 'top'. |
4339 | 0 | if (cmSystemTools::IsSubDirectory(path, cur) || |
4340 | 0 | (cmSystemTools::IsSubDirectory(cur, top) && |
4341 | 0 | cmSystemTools::IsSubDirectory(path, top))) { |
4342 | 0 | return cmSystemTools::ForceToRelativePath(cur, path); |
4343 | 0 | } |
4344 | 0 | return path; |
4345 | 0 | } |
4346 | | } |
4347 | | |
4348 | | std::string cmLocalGenerator::GetRelativeSourceFileName( |
4349 | | cmSourceFile const& source) const |
4350 | 0 | { |
4351 | | // Construct the object file name using the full path to the source |
4352 | | // file which is its only unique identification. |
4353 | 0 | std::string const& fullPath = source.GetFullPath(); |
4354 | | |
4355 | | // Try referencing the source relative to the source tree. |
4356 | 0 | std::string relFromSource = relativeIfUnder( |
4357 | 0 | this->GetSourceDirectory(), this->GetCurrentSourceDirectory(), fullPath); |
4358 | 0 | assert(!relFromSource.empty()); |
4359 | 0 | bool relSource = !cmSystemTools::FileIsFullPath(relFromSource); |
4360 | 0 | bool subSource = relSource && relFromSource[0] != '.'; |
4361 | | |
4362 | | // Try referencing the source relative to the binary tree. |
4363 | 0 | std::string relFromBinary = relativeIfUnder( |
4364 | 0 | this->GetBinaryDirectory(), this->GetCurrentBinaryDirectory(), fullPath); |
4365 | 0 | assert(!relFromBinary.empty()); |
4366 | 0 | bool relBinary = !cmSystemTools::FileIsFullPath(relFromBinary); |
4367 | 0 | bool subBinary = relBinary && relFromBinary[0] != '.'; |
4368 | | |
4369 | | // Select a nice-looking reference to the source file to construct |
4370 | | // the object file name. |
4371 | 0 | std::string objectName; |
4372 | | // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165 |
4373 | | // NOLINTNEXTLINE(bugprone-branch-clone) |
4374 | 0 | if ((relSource && !relBinary) || (subSource && !subBinary)) { |
4375 | 0 | objectName = relFromSource; |
4376 | 0 | } else if ((relBinary && !relSource) || (subBinary && !subSource) || |
4377 | 0 | relFromBinary.length() < relFromSource.length()) { |
4378 | 0 | objectName = relFromBinary; |
4379 | 0 | } else { |
4380 | 0 | objectName = relFromSource; |
4381 | 0 | } |
4382 | 0 | return objectName; |
4383 | 0 | } |
4384 | | |
4385 | | std::string cmLocalGenerator::GetCustomObjectFileName( |
4386 | | cmSourceFile const& source) const |
4387 | 0 | { |
4388 | 0 | if (!this->GetGlobalGenerator()->SupportsCustomObjectNames()) { |
4389 | 0 | return std::string{}; |
4390 | 0 | } |
4391 | | |
4392 | 0 | if (auto objName = source.GetProperty("OBJECT_NAME")) { |
4393 | 0 | cmGeneratorExpression ge(*this->GetCMakeInstance()); |
4394 | 0 | auto cge = ge.Parse(objName); |
4395 | 0 | static std::string const INVALID_GENEX = |
4396 | 0 | "_cmake_invalid_object_name_genex"; |
4397 | 0 | static std::string const INVALID_VALUE = |
4398 | 0 | "_cmake_invalid_object_name_value"; |
4399 | |
|
4400 | 0 | if (!cge) { |
4401 | 0 | this->Makefile->IssueMessage( |
4402 | 0 | MessageType::FATAL_ERROR, |
4403 | 0 | cmStrCat("The \"OBJECT_NAME\" property for\n ", source.GetFullPath(), |
4404 | 0 | "\nis not a valid generator expression (", objName, ").")); |
4405 | 0 | return INVALID_GENEX; |
4406 | 0 | } |
4407 | 0 | if (cge->GetHadHeadSensitiveCondition()) { |
4408 | | // Not reachable; all target-sensitive genexes actually fail to parse. |
4409 | 0 | this->Makefile->IssueMessage( |
4410 | 0 | MessageType::FATAL_ERROR, |
4411 | 0 | cmStrCat("The \"OBJECT_NAME\" property for\n ", source.GetFullPath(), |
4412 | 0 | "\ncontains a condition that queries the consuming target " |
4413 | 0 | "which is not supported (", |
4414 | 0 | objName, ").")); |
4415 | 0 | return INVALID_GENEX; |
4416 | 0 | } |
4417 | 0 | if (cge->GetHadLinkLanguageSensitiveCondition()) { |
4418 | | // Not reachable; all target-sensitive genexes actually fail to parse. |
4419 | 0 | this->Makefile->IssueMessage( |
4420 | 0 | MessageType::FATAL_ERROR, |
4421 | 0 | cmStrCat("The \"OBJECT_NAME\" property for\n ", source.GetFullPath(), |
4422 | 0 | "\ncontains a condition that queries the link language " |
4423 | 0 | "which is not supported (", |
4424 | 0 | objName, ").")); |
4425 | 0 | return INVALID_GENEX; |
4426 | 0 | } |
4427 | | |
4428 | 0 | auto objNameValue = cge->Evaluate(this, ""); |
4429 | 0 | if (cge->GetHadContextSensitiveCondition()) { |
4430 | 0 | this->Makefile->IssueMessage( |
4431 | 0 | MessageType::FATAL_ERROR, |
4432 | 0 | cmStrCat("The \"OBJECT_NAME\" property for\n ", source.GetFullPath(), |
4433 | 0 | "\ncontains a context-sensitive condition which is not " |
4434 | 0 | "supported (", |
4435 | 0 | objName, ").")); |
4436 | 0 | return INVALID_GENEX; |
4437 | 0 | } |
4438 | | |
4439 | | // Skip if it evaluates to empty. |
4440 | 0 | if (!objNameValue.empty()) { |
4441 | 0 | cmCMakePath objNamePath = objNameValue; |
4442 | | // Verify that it is a relative path. |
4443 | 0 | if (objNamePath.IsAbsolute()) { |
4444 | 0 | this->Makefile->IssueMessage( |
4445 | 0 | MessageType::FATAL_ERROR, |
4446 | 0 | cmStrCat( |
4447 | 0 | "The \"OBJECT_NAME\" property for\n ", source.GetFullPath(), |
4448 | 0 | "\nresolves to an absolute path which is not supported:\n ", |
4449 | 0 | objNameValue)); |
4450 | 0 | return INVALID_VALUE; |
4451 | 0 | } |
4452 | 0 | auto isInvalidComponent = [](cmCMakePath const& component) -> bool { |
4453 | 0 | return component == ".."_s; |
4454 | 0 | }; |
4455 | | // Verify that it contains no `..` components. |
4456 | 0 | if (std::any_of(objNamePath.begin(), objNamePath.end(), |
4457 | 0 | isInvalidComponent)) { |
4458 | 0 | this->Makefile->IssueMessage( |
4459 | 0 | MessageType::FATAL_ERROR, |
4460 | 0 | cmStrCat("The \"OBJECT_NAME\" property for\n ", |
4461 | 0 | source.GetFullPath(), "\ncontains an invalid component (", |
4462 | 0 | objNameValue, ").")); |
4463 | 0 | return INVALID_VALUE; |
4464 | 0 | } |
4465 | | |
4466 | 0 | return objNameValue; |
4467 | 0 | } |
4468 | 0 | } |
4469 | | |
4470 | 0 | return std::string{}; |
4471 | 0 | } |
4472 | | |
4473 | | std::string cmLocalGenerator::GetCustomInstallObjectFileName( |
4474 | | cmSourceFile const& source, std::string const& config, |
4475 | | char const* custom_ext) const |
4476 | 0 | { |
4477 | 0 | if (auto objName = source.GetProperty("INSTALL_OBJECT_NAME")) { |
4478 | 0 | cmGeneratorExpression ge(*this->GetCMakeInstance()); |
4479 | 0 | auto cge = ge.Parse(objName); |
4480 | 0 | static std::string const INVALID_GENEX = |
4481 | 0 | "_cmake_invalid_object_name_genex"; |
4482 | 0 | static std::string const INVALID_VALUE = |
4483 | 0 | "_cmake_invalid_object_name_value"; |
4484 | |
|
4485 | 0 | if (!cge) { |
4486 | 0 | this->Makefile->IssueMessage( |
4487 | 0 | MessageType::FATAL_ERROR, |
4488 | 0 | cmStrCat("The \"INSTALL_OBJECT_NAME\" property for\n ", |
4489 | 0 | source.GetFullPath(), |
4490 | 0 | "\nis not a valid generator expression (", objName, ").")); |
4491 | 0 | return INVALID_GENEX; |
4492 | 0 | } |
4493 | 0 | if (cge->GetHadHeadSensitiveCondition()) { |
4494 | | // Not reachable; all target-sensitive genexes actually fail to parse. |
4495 | 0 | this->Makefile->IssueMessage( |
4496 | 0 | MessageType::FATAL_ERROR, |
4497 | 0 | cmStrCat("The \"INSTALL_OBJECT_NAME\" property for\n ", |
4498 | 0 | source.GetFullPath(), |
4499 | 0 | "\ncontains a condition that queries the consuming target " |
4500 | 0 | "which is not supported (", |
4501 | 0 | objName, ").")); |
4502 | 0 | return INVALID_GENEX; |
4503 | 0 | } |
4504 | 0 | if (cge->GetHadLinkLanguageSensitiveCondition()) { |
4505 | | // Not reachable; all target-sensitive genexes actually fail to parse. |
4506 | 0 | this->Makefile->IssueMessage( |
4507 | 0 | MessageType::FATAL_ERROR, |
4508 | 0 | cmStrCat("The \"INSTALL_OBJECT_NAME\" property for\n ", |
4509 | 0 | source.GetFullPath(), |
4510 | 0 | "\ncontains a condition that queries the link language " |
4511 | 0 | "which is not supported (", |
4512 | 0 | objName, ").")); |
4513 | 0 | return INVALID_GENEX; |
4514 | 0 | } |
4515 | | |
4516 | 0 | auto objNameValue = cge->Evaluate(this, config); |
4517 | | |
4518 | | // Skip if it evaluates to empty. |
4519 | 0 | if (!objNameValue.empty()) { |
4520 | 0 | cmCMakePath objNamePath = objNameValue; |
4521 | | // Verify that it is a relative path. |
4522 | 0 | if (objNamePath.IsAbsolute()) { |
4523 | 0 | this->Makefile->IssueMessage( |
4524 | 0 | MessageType::FATAL_ERROR, |
4525 | 0 | cmStrCat( |
4526 | 0 | "The \"INSTALL_OBJECT_NAME\" property for\n ", |
4527 | 0 | source.GetFullPath(), |
4528 | 0 | "\nresolves to an absolute path which is not supported:\n ", |
4529 | 0 | objNameValue)); |
4530 | 0 | return INVALID_VALUE; |
4531 | 0 | } |
4532 | 0 | auto isInvalidComponent = [](cmCMakePath const& component) -> bool { |
4533 | 0 | return component == ".."_s; |
4534 | 0 | }; |
4535 | | // Verify that it contains no `..` components. |
4536 | 0 | if (std::any_of(objNamePath.begin(), objNamePath.end(), |
4537 | 0 | isInvalidComponent)) { |
4538 | 0 | this->Makefile->IssueMessage( |
4539 | 0 | MessageType::FATAL_ERROR, |
4540 | 0 | cmStrCat("The \"INSTALL_OBJECT_NAME\" property for\n ", |
4541 | 0 | source.GetFullPath(), "\ncontains an invalid component (", |
4542 | 0 | objNameValue, ").")); |
4543 | 0 | return INVALID_VALUE; |
4544 | 0 | } |
4545 | | |
4546 | 0 | if (custom_ext) { |
4547 | 0 | objNameValue += custom_ext; |
4548 | 0 | } else { |
4549 | 0 | objNameValue += |
4550 | 0 | this->GetGlobalGenerator()->GetLanguageOutputExtension(source); |
4551 | 0 | } |
4552 | |
|
4553 | 0 | return objNameValue; |
4554 | 0 | } |
4555 | 0 | } |
4556 | | |
4557 | 0 | return std::string{}; |
4558 | 0 | } |
4559 | | |
4560 | | void cmLocalGenerator::FillCustomInstallObjectLocations( |
4561 | | cmSourceFile const& source, std::string const& config, |
4562 | | char const* custom_ext, |
4563 | | std::map<std::string, cmObjectLocation>& mapping) const |
4564 | 0 | { |
4565 | 0 | auto installLoc = |
4566 | 0 | this->GetCustomInstallObjectFileName(source, config, custom_ext); |
4567 | 0 | if (!installLoc.empty()) { |
4568 | 0 | mapping[config] = installLoc; |
4569 | 0 | } |
4570 | 0 | } |
4571 | | |
4572 | | std::string cmLocalGenerator::GetObjectFileNameWithoutTarget( |
4573 | | cmSourceFile const& source, std::string const& dir_max, |
4574 | | bool* hasSourceExtension, char const* customOutputExtension, |
4575 | | bool const* forceShortObjectName) |
4576 | 0 | { |
4577 | 0 | bool useShortObjectNames = this->UseShortObjectNames(); |
4578 | 0 | if (forceShortObjectName) { |
4579 | 0 | useShortObjectNames = *forceShortObjectName; |
4580 | 0 | } |
4581 | |
|
4582 | 0 | if (!useShortObjectNames && |
4583 | 0 | this->GetGlobalGenerator()->SupportsCustomObjectNames()) { |
4584 | 0 | auto customName = this->GetCustomObjectFileName(source); |
4585 | 0 | if (!customName.empty()) { |
4586 | 0 | auto ext = this->GlobalGenerator->GetLanguageOutputExtension(source); |
4587 | 0 | if (customOutputExtension) { |
4588 | 0 | ext = *customOutputExtension; |
4589 | 0 | } |
4590 | 0 | return cmStrCat(customName, ext); |
4591 | 0 | } |
4592 | 0 | } |
4593 | | |
4594 | | // This can return an absolute path in the case where source is |
4595 | | // not relative to the current source or binary directories |
4596 | 0 | std::string objectName = this->GetRelativeSourceFileName(source); |
4597 | | // if it is still a full path check for the try compile case |
4598 | | // try compile never have in source sources, and should not |
4599 | | // have conflicting source file names in the same target |
4600 | 0 | if (cmSystemTools::FileIsFullPath(objectName)) { |
4601 | 0 | if (this->GetGlobalGenerator()->GetCMakeInstance()->GetIsInTryCompile()) { |
4602 | 0 | objectName = cmSystemTools::GetFilenameName(source.GetFullPath()); |
4603 | 0 | } |
4604 | 0 | } |
4605 | 0 | bool const isPchObject = source.IsPchHeader() || source.IsPchSource(); |
4606 | | |
4607 | | // Short object path policy selected, use as little info as necessary to |
4608 | | // select an object name |
4609 | 0 | bool keptSourceExtension = true; |
4610 | 0 | if (useShortObjectNames) { |
4611 | 0 | objectName = this->GetShortObjectFileName(source); |
4612 | 0 | keptSourceExtension = false; |
4613 | 0 | } |
4614 | | |
4615 | | // Ensure that for the CMakeFiles/<target>.dir/generated_source_file |
4616 | | // we don't end up having: |
4617 | | // CMakeFiles/<target>.dir/CMakeFiles/<target>.dir/generated_source_file.obj |
4618 | 0 | cmValue unitySourceFile = source.GetProperty("UNITY_SOURCE_FILE"); |
4619 | 0 | cmValue pchExtension = source.GetProperty("PCH_EXTENSION"); |
4620 | 0 | if (unitySourceFile || pchExtension || isPchObject) { |
4621 | 0 | if (pchExtension) { |
4622 | 0 | customOutputExtension = pchExtension->c_str(); |
4623 | 0 | } |
4624 | |
|
4625 | 0 | cmsys::RegularExpression var("(CMakeFiles/[^/]+.dir/)"); |
4626 | 0 | if (var.find(objectName)) { |
4627 | 0 | objectName.erase(var.start(), var.end() - var.start()); |
4628 | 0 | } |
4629 | 0 | } |
4630 | | |
4631 | | // Replace the original source file extension with the object file |
4632 | | // extension. |
4633 | 0 | if (!source.GetPropertyAsBool("KEEP_EXTENSION")) { |
4634 | | // Decide whether this language wants to replace the source |
4635 | | // extension with the object extension. |
4636 | 0 | bool replaceExt = false; |
4637 | 0 | std::string lang = source.GetLanguage(); |
4638 | 0 | if (!lang.empty()) { |
4639 | 0 | replaceExt = this->Makefile->IsOn( |
4640 | 0 | cmStrCat("CMAKE_", lang, "_OUTPUT_EXTENSION_REPLACE")); |
4641 | 0 | } |
4642 | | |
4643 | | // Remove the source extension if it is to be replaced. |
4644 | 0 | if (replaceExt || customOutputExtension) { |
4645 | 0 | keptSourceExtension = false; |
4646 | 0 | std::string::size_type dot_pos = objectName.rfind('.'); |
4647 | 0 | if (dot_pos != std::string::npos) { |
4648 | 0 | objectName = objectName.substr(0, dot_pos); |
4649 | 0 | } |
4650 | 0 | } |
4651 | | |
4652 | | // Strip source file extension when shortening object file paths |
4653 | 0 | if (useShortObjectNames) { |
4654 | 0 | objectName = cmSystemTools::GetFilenameWithoutExtension(objectName); |
4655 | 0 | } |
4656 | | // Store the new extension. |
4657 | 0 | if (customOutputExtension) { |
4658 | 0 | objectName += customOutputExtension; |
4659 | 0 | } else { |
4660 | 0 | objectName += this->GlobalGenerator->GetLanguageOutputExtension(source); |
4661 | 0 | } |
4662 | 0 | } |
4663 | 0 | if (hasSourceExtension) { |
4664 | 0 | *hasSourceExtension = keptSourceExtension; |
4665 | 0 | } |
4666 | | |
4667 | | // Convert to a safe name. |
4668 | 0 | return this->CreateSafeUniqueObjectFileName(objectName, dir_max); |
4669 | 0 | } |
4670 | | |
4671 | | bool cmLocalGenerator::UseShortObjectNames( |
4672 | | cmStateEnums::IntermediateDirKind kind) const |
4673 | 0 | { |
4674 | 0 | return this->GlobalGenerator->UseShortObjectNames(kind); |
4675 | 0 | } |
4676 | | |
4677 | | std::string cmLocalGenerator::GetObjectOutputRoot( |
4678 | | cmStateEnums::IntermediateDirKind kind) const |
4679 | 0 | { |
4680 | 0 | if (this->UseShortObjectNames(kind)) { |
4681 | 0 | return cmStrCat(this->GetCurrentBinaryDirectory(), '/', |
4682 | 0 | this->GlobalGenerator->GetShortBinaryOutputDir()); |
4683 | 0 | } |
4684 | 0 | return cmStrCat(this->GetCurrentBinaryDirectory(), "/CMakeFiles"); |
4685 | 0 | } |
4686 | | |
4687 | | bool cmLocalGenerator::AlwaysUsesCMFPaths() const |
4688 | 0 | { |
4689 | 0 | return true; |
4690 | 0 | } |
4691 | | |
4692 | | std::string cmLocalGenerator::GetShortObjectFileName( |
4693 | | cmSourceFile const& source) const |
4694 | 0 | { |
4695 | 0 | std::string objectName = this->GetRelativeSourceFileName(source); |
4696 | 0 | cmCryptoHash objNameHasher(cmCryptoHash::AlgoSHA3_512); |
4697 | 0 | std::string terseObjectName = |
4698 | 0 | objNameHasher.HashString(objectName).substr(0, 8); |
4699 | 0 | return terseObjectName; |
4700 | 0 | } |
4701 | | |
4702 | | std::string cmLocalGenerator::ComputeShortTargetDirectory( |
4703 | | cmGeneratorTarget const* target) const |
4704 | 0 | { |
4705 | 0 | auto const& tgtName = target->GetName(); |
4706 | 0 | return this->GetGlobalGenerator()->ComputeTargetShortName( |
4707 | 0 | this->GetCurrentBinaryDirectory(), tgtName); |
4708 | 0 | } |
4709 | | |
4710 | | std::string cmLocalGenerator::GetSourceFileLanguage(cmSourceFile const& source) |
4711 | 0 | { |
4712 | 0 | return source.GetLanguage(); |
4713 | 0 | } |
4714 | | |
4715 | | cmake* cmLocalGenerator::GetCMakeInstance() const |
4716 | 0 | { |
4717 | 0 | return this->GlobalGenerator->GetCMakeInstance(); |
4718 | 0 | } |
4719 | | |
4720 | | std::string const& cmLocalGenerator::GetSourceDirectory() const |
4721 | 0 | { |
4722 | 0 | return this->GetCMakeInstance()->GetHomeDirectory(); |
4723 | 0 | } |
4724 | | |
4725 | | std::string const& cmLocalGenerator::GetBinaryDirectory() const |
4726 | 0 | { |
4727 | 0 | return this->GetCMakeInstance()->GetHomeOutputDirectory(); |
4728 | 0 | } |
4729 | | |
4730 | | std::string const& cmLocalGenerator::GetCurrentBinaryDirectory() const |
4731 | 0 | { |
4732 | 0 | return this->StateSnapshot.GetDirectory().GetCurrentBinary(); |
4733 | 0 | } |
4734 | | |
4735 | | std::string const& cmLocalGenerator::GetCurrentSourceDirectory() const |
4736 | 0 | { |
4737 | 0 | return this->StateSnapshot.GetDirectory().GetCurrentSource(); |
4738 | 0 | } |
4739 | | |
4740 | | std::string cmLocalGenerator::GetTargetDirectory( |
4741 | | cmGeneratorTarget const* /*unused*/, |
4742 | | cmStateEnums::IntermediateDirKind /*kind*/) const |
4743 | 0 | { |
4744 | 0 | cmSystemTools::Error("GetTargetDirectory" |
4745 | 0 | " called on cmLocalGenerator"); |
4746 | 0 | return ""; |
4747 | 0 | } |
4748 | | |
4749 | | cmPolicies::PolicyStatus cmLocalGenerator::GetPolicyStatus( |
4750 | | cmPolicies::PolicyID id) const |
4751 | 0 | { |
4752 | 0 | return this->Makefile->GetPolicyStatus(id); |
4753 | 0 | } |
4754 | | |
4755 | | bool cmLocalGenerator::CheckDefinition(std::string const& define) const |
4756 | 0 | { |
4757 | | // Many compilers do not support -DNAME(arg)=sdf so we disable it. |
4758 | 0 | std::string::size_type pos = define.find_first_of("(="); |
4759 | 0 | if (pos != std::string::npos) { |
4760 | 0 | if (define[pos] == '(') { |
4761 | 0 | std::ostringstream e; |
4762 | | /* clang-format off */ |
4763 | 0 | e << "WARNING: Function-style preprocessor definitions may not be " |
4764 | 0 | "passed on the compiler command line because many compilers " |
4765 | 0 | "do not support it.\n" |
4766 | 0 | "CMake is dropping a preprocessor definition: " << define << "\n" |
4767 | 0 | "Consider defining the macro in a (configured) header file.\n"; |
4768 | | /* clang-format on */ |
4769 | 0 | cmSystemTools::Message(e.str()); |
4770 | 0 | return false; |
4771 | 0 | } |
4772 | 0 | } |
4773 | | |
4774 | | // Many compilers do not support # in the value so we disable it. |
4775 | 0 | if (define.find_first_of('#') != std::string::npos) { |
4776 | 0 | std::ostringstream e; |
4777 | | /* clang-format off */ |
4778 | 0 | e << "WARNING: Preprocessor definitions containing '#' may not be " |
4779 | 0 | "passed on the compiler command line because many compilers " |
4780 | 0 | "do not support it.\n" |
4781 | 0 | "CMake is dropping a preprocessor definition: " << define << "\n" |
4782 | 0 | "Consider defining the macro in a (configured) header file.\n"; |
4783 | | /* clang-format on */ |
4784 | 0 | cmSystemTools::Message(e.str()); |
4785 | 0 | return false; |
4786 | 0 | } |
4787 | | |
4788 | | // Assume it is supported. |
4789 | 0 | return true; |
4790 | 0 | } |
4791 | | |
4792 | | static void cmLGInfoProp(cmMakefile* mf, cmGeneratorTarget* target, |
4793 | | std::string const& prop) |
4794 | 0 | { |
4795 | 0 | if (cmValue val = target->GetProperty(prop)) { |
4796 | 0 | mf->AddDefinition(prop, *val); |
4797 | 0 | } |
4798 | 0 | } |
4799 | | |
4800 | | void cmLocalGenerator::GenerateAppleInfoPList(cmGeneratorTarget* target, |
4801 | | std::string const& targetName, |
4802 | | std::string const& fname) |
4803 | 0 | { |
4804 | | // Find the Info.plist template. |
4805 | 0 | cmValue in = target->GetProperty("MACOSX_BUNDLE_INFO_PLIST"); |
4806 | 0 | std::string inFile = cmNonempty(in) ? *in : "MacOSXBundleInfo.plist.in"; |
4807 | 0 | if (!cmSystemTools::FileIsFullPath(inFile)) { |
4808 | 0 | std::string inMod = this->Makefile->GetModulesFile(inFile); |
4809 | 0 | if (!inMod.empty()) { |
4810 | 0 | inFile = inMod; |
4811 | 0 | } |
4812 | 0 | } |
4813 | 0 | if (!cmSystemTools::FileExists(inFile, true)) { |
4814 | 0 | std::ostringstream e; |
4815 | 0 | e << "Target " << target->GetName() << " Info.plist template \"" << inFile |
4816 | 0 | << "\" could not be found."; |
4817 | 0 | cmSystemTools::Error(e.str()); |
4818 | 0 | return; |
4819 | 0 | } |
4820 | | |
4821 | | // Convert target properties to variables in an isolated makefile |
4822 | | // scope to configure the file. If properties are set they will |
4823 | | // override user make variables. If not the configuration will fall |
4824 | | // back to the directory-level values set by the user. |
4825 | 0 | cmMakefile* mf = this->Makefile; |
4826 | 0 | cmMakefile::ScopePushPop varScope(mf); |
4827 | 0 | mf->AddDefinition("MACOSX_BUNDLE_EXECUTABLE_NAME", targetName); |
4828 | 0 | cmLGInfoProp(mf, target, "MACOSX_BUNDLE_INFO_STRING"); |
4829 | 0 | cmLGInfoProp(mf, target, "MACOSX_BUNDLE_ICON_FILE"); |
4830 | 0 | cmLGInfoProp(mf, target, "MACOSX_BUNDLE_GUI_IDENTIFIER"); |
4831 | 0 | cmLGInfoProp(mf, target, "MACOSX_BUNDLE_LONG_VERSION_STRING"); |
4832 | 0 | cmLGInfoProp(mf, target, "MACOSX_BUNDLE_BUNDLE_NAME"); |
4833 | 0 | cmLGInfoProp(mf, target, "MACOSX_BUNDLE_SHORT_VERSION_STRING"); |
4834 | 0 | cmLGInfoProp(mf, target, "MACOSX_BUNDLE_BUNDLE_VERSION"); |
4835 | 0 | cmLGInfoProp(mf, target, "MACOSX_BUNDLE_COPYRIGHT"); |
4836 | 0 | mf->ConfigureFile(inFile, fname, false, false, false); |
4837 | 0 | } |
4838 | | |
4839 | | void cmLocalGenerator::GenerateFrameworkInfoPList( |
4840 | | cmGeneratorTarget* target, std::string const& targetName, |
4841 | | std::string const& fname) |
4842 | 0 | { |
4843 | | // Find the Info.plist template. |
4844 | 0 | cmValue in = target->GetProperty("MACOSX_FRAMEWORK_INFO_PLIST"); |
4845 | 0 | std::string inFile = cmNonempty(in) ? *in : "MacOSXFrameworkInfo.plist.in"; |
4846 | 0 | if (!cmSystemTools::FileIsFullPath(inFile)) { |
4847 | 0 | std::string inMod = this->Makefile->GetModulesFile(inFile); |
4848 | 0 | if (!inMod.empty()) { |
4849 | 0 | inFile = inMod; |
4850 | 0 | } |
4851 | 0 | } |
4852 | 0 | if (!cmSystemTools::FileExists(inFile, true)) { |
4853 | 0 | std::ostringstream e; |
4854 | 0 | e << "Target " << target->GetName() << " Info.plist template \"" << inFile |
4855 | 0 | << "\" could not be found."; |
4856 | 0 | cmSystemTools::Error(e.str()); |
4857 | 0 | return; |
4858 | 0 | } |
4859 | | |
4860 | | // Convert target properties to variables in an isolated makefile |
4861 | | // scope to configure the file. If properties are set they will |
4862 | | // override user make variables. If not the configuration will fall |
4863 | | // back to the directory-level values set by the user. |
4864 | 0 | cmMakefile* mf = this->Makefile; |
4865 | 0 | cmMakefile::ScopePushPop varScope(mf); |
4866 | 0 | mf->AddDefinition("MACOSX_FRAMEWORK_NAME", targetName); |
4867 | 0 | cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_ICON_FILE"); |
4868 | 0 | cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_IDENTIFIER"); |
4869 | 0 | cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_SHORT_VERSION_STRING"); |
4870 | 0 | cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_BUNDLE_NAME"); |
4871 | 0 | cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_BUNDLE_VERSION"); |
4872 | 0 | mf->ConfigureFile(inFile, fname, false, false, false); |
4873 | 0 | } |
4874 | | |
4875 | | namespace { |
4876 | | cm::string_view CustomOutputRoleKeyword(cmLocalGenerator::OutputRole role) |
4877 | 0 | { |
4878 | 0 | return (role == cmLocalGenerator::OutputRole::Primary ? "OUTPUT"_s |
4879 | 0 | : "BYPRODUCTS"_s); |
4880 | 0 | } |
4881 | | |
4882 | | void CreateGeneratedSource(cmLocalGenerator& lg, std::string const& output, |
4883 | | cmLocalGenerator::OutputRole role, |
4884 | | cmCommandOrigin origin, |
4885 | | cmListFileBacktrace const& lfbt) |
4886 | 0 | { |
4887 | 0 | if (cmGeneratorExpression::Find(output) != std::string::npos) { |
4888 | 0 | lg.GetCMakeInstance()->IssueMessage( |
4889 | 0 | MessageType::FATAL_ERROR, |
4890 | 0 | "Generator expressions in custom command outputs are not implemented!", |
4891 | 0 | lfbt); |
4892 | 0 | return; |
4893 | 0 | } |
4894 | | |
4895 | | // Make sure the file will not be generated into the source |
4896 | | // directory during an out of source build. |
4897 | 0 | if (!lg.GetMakefile()->CanIWriteThisFile(output)) { |
4898 | 0 | lg.GetCMakeInstance()->IssueMessage( |
4899 | 0 | MessageType::FATAL_ERROR, |
4900 | 0 | cmStrCat(CustomOutputRoleKeyword(role), " path\n ", output, |
4901 | 0 | "\nin a source directory as an output of custom command."), |
4902 | 0 | lfbt); |
4903 | 0 | return; |
4904 | 0 | } |
4905 | | |
4906 | | // Make sure the output file name has no invalid characters. |
4907 | 0 | bool const hashNotAllowed = lg.GetState()->UseBorlandMake(); |
4908 | 0 | std::string::size_type pos = output.find_first_of("<>"); |
4909 | 0 | if (pos == std::string::npos && hashNotAllowed) { |
4910 | 0 | pos = output.find_first_of('#'); |
4911 | 0 | } |
4912 | |
|
4913 | 0 | if (pos != std::string::npos) { |
4914 | 0 | lg.GetCMakeInstance()->IssueMessage( |
4915 | 0 | MessageType::FATAL_ERROR, |
4916 | 0 | cmStrCat(CustomOutputRoleKeyword(role), " containing a \"", output[pos], |
4917 | 0 | "\" is not allowed."), |
4918 | 0 | lfbt); |
4919 | 0 | return; |
4920 | 0 | } |
4921 | | |
4922 | | // Outputs without generator expressions from the project are already |
4923 | | // created and marked as generated. Do not mark them again, because |
4924 | | // other commands might have overwritten the property. |
4925 | 0 | if (origin == cmCommandOrigin::Generator) { |
4926 | 0 | lg.GetMakefile()->GetOrCreateGeneratedSource(output); |
4927 | 0 | } |
4928 | 0 | } |
4929 | | |
4930 | | std::string ComputeCustomCommandRuleFileName(cmLocalGenerator& lg, |
4931 | | cmListFileBacktrace const& bt, |
4932 | | std::string const& output) |
4933 | 0 | { |
4934 | | // If the output path has no generator expressions, use it directly. |
4935 | 0 | if (cmGeneratorExpression::Find(output) == std::string::npos) { |
4936 | 0 | return output; |
4937 | 0 | } |
4938 | | |
4939 | | // The output path contains a generator expression, but we must choose |
4940 | | // a single source file path to which to attach the custom command. |
4941 | | // Use some heuristics to provide a nice-looking name when possible. |
4942 | | |
4943 | | // If the only genex is $<CONFIG>, replace that gracefully. |
4944 | 0 | { |
4945 | 0 | std::string simple = output; |
4946 | 0 | cmSystemTools::ReplaceString(simple, "$<CONFIG>", "(CONFIG)"); |
4947 | 0 | if (cmGeneratorExpression::Find(simple) == std::string::npos) { |
4948 | 0 | return simple; |
4949 | 0 | } |
4950 | 0 | } |
4951 | | |
4952 | | // If the genex evaluates to the same value in all configurations, use that. |
4953 | 0 | { |
4954 | 0 | std::vector<std::string> allConfigOutputs = |
4955 | 0 | lg.ExpandCustomCommandOutputGenex(output, bt); |
4956 | 0 | if (allConfigOutputs.size() == 1) { |
4957 | 0 | return allConfigOutputs.front(); |
4958 | 0 | } |
4959 | 0 | } |
4960 | | |
4961 | | // Fall back to a deterministic unique name. |
4962 | 0 | cmCryptoHash h(cmCryptoHash::AlgoSHA256); |
4963 | 0 | return cmStrCat(lg.GetCurrentBinaryDirectory(), "/CMakeFiles/", |
4964 | 0 | h.HashString(output).substr(0, 16)); |
4965 | 0 | } |
4966 | | |
4967 | | cmSourceFile* AddCustomCommand(cmLocalGenerator& lg, cmCommandOrigin origin, |
4968 | | std::unique_ptr<cmCustomCommand> cc, |
4969 | | bool replace) |
4970 | 0 | { |
4971 | 0 | cmMakefile* mf = lg.GetMakefile(); |
4972 | 0 | auto const& lfbt = cc->GetBacktrace(); |
4973 | 0 | auto const& outputs = cc->GetOutputs(); |
4974 | 0 | auto const& byproducts = cc->GetByproducts(); |
4975 | 0 | auto const& commandLines = cc->GetCommandLines(); |
4976 | | |
4977 | | // Choose a source file on which to store the custom command. |
4978 | 0 | cmSourceFile* file = nullptr; |
4979 | 0 | if (!commandLines.empty() && cc->HasMainDependency()) { |
4980 | 0 | auto const& main_dependency = cc->GetMainDependency(); |
4981 | | // The main dependency was specified. Use it unless a different |
4982 | | // custom command already used it. |
4983 | 0 | file = mf->GetSource(main_dependency); |
4984 | 0 | if (file && file->GetCustomCommand() && !replace) { |
4985 | | // The main dependency already has a custom command. |
4986 | 0 | if (commandLines == file->GetCustomCommand()->GetCommandLines()) { |
4987 | | // The existing custom command is identical. Silently ignore |
4988 | | // the duplicate. |
4989 | 0 | return file; |
4990 | 0 | } |
4991 | | // The existing custom command is different. We need to |
4992 | | // generate a rule file for this new command. |
4993 | 0 | file = nullptr; |
4994 | 0 | } else if (!file) { |
4995 | 0 | file = mf->CreateSource(main_dependency); |
4996 | 0 | } |
4997 | 0 | } |
4998 | | |
4999 | | // Generate a rule file if the main dependency is not available. |
5000 | 0 | if (!file) { |
5001 | 0 | cmGlobalGenerator* gg = lg.GetGlobalGenerator(); |
5002 | | |
5003 | | // Construct a rule file associated with the first output produced. |
5004 | 0 | std::string outName = gg->GenerateRuleFile( |
5005 | 0 | ComputeCustomCommandRuleFileName(lg, lfbt, outputs[0])); |
5006 | | |
5007 | | // Check if the rule file already exists. |
5008 | 0 | file = mf->GetSource(outName, cmSourceFileLocationKind::Known); |
5009 | 0 | if (file && file->GetCustomCommand() && !replace) { |
5010 | | // The rule file already exists. |
5011 | 0 | if (commandLines != file->GetCustomCommand()->GetCommandLines()) { |
5012 | 0 | lg.GetCMakeInstance()->IssueMessage( |
5013 | 0 | MessageType::FATAL_ERROR, |
5014 | 0 | cmStrCat("Attempt to add a custom rule to output\n ", outName, |
5015 | 0 | "\nwhich already has a custom rule."), |
5016 | 0 | lfbt); |
5017 | 0 | } |
5018 | 0 | return file; |
5019 | 0 | } |
5020 | | |
5021 | | // Create a cmSourceFile for the rule file. |
5022 | 0 | if (!file) { |
5023 | 0 | file = mf->CreateSource(outName, true, cmSourceFileLocationKind::Known); |
5024 | 0 | } |
5025 | 0 | file->SetProperty("__CMAKE_RULE", "1"); |
5026 | 0 | } |
5027 | | |
5028 | | // Attach the custom command to the file. |
5029 | 0 | if (file) { |
5030 | 0 | cc->SetEscapeAllowMakeVars(true); |
5031 | |
|
5032 | 0 | lg.AddSourceOutputs(file, outputs, cmLocalGenerator::OutputRole::Primary, |
5033 | 0 | lfbt, origin); |
5034 | 0 | lg.AddSourceOutputs(file, byproducts, |
5035 | 0 | cmLocalGenerator::OutputRole::Byproduct, lfbt, origin); |
5036 | |
|
5037 | 0 | file->SetCustomCommand(std::move(cc)); |
5038 | 0 | } |
5039 | 0 | return file; |
5040 | 0 | } |
5041 | | |
5042 | | bool AnyOutputMatches(std::string const& name, |
5043 | | std::vector<std::string> const& outputs) |
5044 | 0 | { |
5045 | 0 | return std::any_of(outputs.begin(), outputs.end(), |
5046 | 0 | [&name](std::string const& output) -> bool { |
5047 | 0 | std::string::size_type pos = output.rfind(name); |
5048 | | // If the output matches exactly |
5049 | 0 | return (pos != std::string::npos && |
5050 | 0 | pos == output.size() - name.size() && |
5051 | 0 | (pos == 0 || output[pos - 1] == '/')); |
5052 | 0 | }); |
5053 | 0 | } |
5054 | | |
5055 | | bool AnyTargetCommandOutputMatches( |
5056 | | std::string const& name, std::vector<cmCustomCommand> const& commands) |
5057 | 0 | { |
5058 | 0 | return std::any_of(commands.begin(), commands.end(), |
5059 | 0 | [&name](cmCustomCommand const& command) -> bool { |
5060 | 0 | return AnyOutputMatches(name, command.GetByproducts()); |
5061 | 0 | }); |
5062 | 0 | } |
5063 | | } |
5064 | | |
5065 | | namespace detail { |
5066 | | void AddCustomCommandToTarget(cmLocalGenerator& lg, cmCommandOrigin origin, |
5067 | | cmTarget* target, cmCustomCommandType type, |
5068 | | std::unique_ptr<cmCustomCommand> cc) |
5069 | 0 | { |
5070 | | // Add the command to the appropriate build step for the target. |
5071 | 0 | cc->SetEscapeAllowMakeVars(true); |
5072 | 0 | cc->SetTarget(target->GetName()); |
5073 | |
|
5074 | 0 | lg.AddTargetByproducts(target, cc->GetByproducts(), cc->GetBacktrace(), |
5075 | 0 | origin); |
5076 | |
|
5077 | 0 | switch (type) { |
5078 | 0 | case cmCustomCommandType::PRE_BUILD: |
5079 | 0 | target->AddPreBuildCommand(std::move(*cc)); |
5080 | 0 | break; |
5081 | 0 | case cmCustomCommandType::PRE_LINK: |
5082 | 0 | target->AddPreLinkCommand(std::move(*cc)); |
5083 | 0 | break; |
5084 | 0 | case cmCustomCommandType::POST_BUILD: |
5085 | 0 | target->AddPostBuildCommand(std::move(*cc)); |
5086 | 0 | break; |
5087 | 0 | } |
5088 | | |
5089 | 0 | cc.reset(); |
5090 | 0 | } |
5091 | | |
5092 | | cmSourceFile* AddCustomCommandToOutput(cmLocalGenerator& lg, |
5093 | | cmCommandOrigin origin, |
5094 | | std::unique_ptr<cmCustomCommand> cc, |
5095 | | bool replace) |
5096 | 0 | { |
5097 | 0 | return AddCustomCommand(lg, origin, std::move(cc), replace); |
5098 | 0 | } |
5099 | | |
5100 | | void AppendCustomCommandToOutput(cmLocalGenerator& lg, |
5101 | | cmListFileBacktrace const& lfbt, |
5102 | | std::string const& output, |
5103 | | std::vector<std::string> const& depends, |
5104 | | cmImplicitDependsList const& implicit_depends, |
5105 | | cmCustomCommandLines const& commandLines) |
5106 | 0 | { |
5107 | | // Lookup an existing command. |
5108 | 0 | cmSourceFile* sf = nullptr; |
5109 | 0 | if (cmGeneratorExpression::Find(output) == std::string::npos) { |
5110 | 0 | sf = lg.GetSourceFileWithOutput(output); |
5111 | 0 | } else { |
5112 | | // This output path has a generator expression. Evaluate it to |
5113 | | // find the output for any configurations. |
5114 | 0 | for (std::string const& out : |
5115 | 0 | lg.ExpandCustomCommandOutputGenex(output, lfbt)) { |
5116 | 0 | sf = lg.GetSourceFileWithOutput(out); |
5117 | 0 | if (sf) { |
5118 | 0 | break; |
5119 | 0 | } |
5120 | 0 | } |
5121 | 0 | } |
5122 | |
|
5123 | 0 | if (sf) { |
5124 | 0 | if (cmCustomCommand* cc = sf->GetCustomCommand()) { |
5125 | 0 | cc->AppendCommands(commandLines); |
5126 | 0 | cc->AppendDepends(depends); |
5127 | 0 | if (cc->GetCodegen() && !implicit_depends.empty()) { |
5128 | 0 | lg.GetCMakeInstance()->IssueMessage( |
5129 | 0 | MessageType::FATAL_ERROR, |
5130 | 0 | "Cannot append IMPLICIT_DEPENDS to existing CODEGEN custom " |
5131 | 0 | "command."); |
5132 | 0 | } |
5133 | 0 | cc->AppendImplicitDepends(implicit_depends); |
5134 | 0 | return; |
5135 | 0 | } |
5136 | 0 | } |
5137 | | |
5138 | | // No existing command found. |
5139 | 0 | lg.GetCMakeInstance()->IssueMessage( |
5140 | 0 | MessageType::FATAL_ERROR, |
5141 | 0 | cmStrCat("Attempt to APPEND to custom command with output\n ", output, |
5142 | 0 | "\nwhich is not already a custom command output."), |
5143 | 0 | lfbt); |
5144 | 0 | } |
5145 | | |
5146 | | void AddUtilityCommand(cmLocalGenerator& lg, cmCommandOrigin origin, |
5147 | | cmTarget* target, std::unique_ptr<cmCustomCommand> cc) |
5148 | 0 | { |
5149 | | // They might be moved away |
5150 | 0 | auto byproducts = cc->GetByproducts(); |
5151 | 0 | auto lfbt = cc->GetBacktrace(); |
5152 | | |
5153 | | // Use an empty comment to avoid generation of default comment. |
5154 | 0 | if (!cc->GetComment()) { |
5155 | 0 | cc->SetComment(""); |
5156 | 0 | } |
5157 | | |
5158 | | // Create the generated symbolic output name of the utility target. |
5159 | 0 | std::string output = |
5160 | 0 | lg.CreateUtilityOutput(target->GetName(), byproducts, lfbt); |
5161 | 0 | cc->SetOutputs(output); |
5162 | |
|
5163 | 0 | cmSourceFile* rule = AddCustomCommand(lg, origin, std::move(cc), |
5164 | 0 | /*replace=*/false); |
5165 | 0 | if (rule) { |
5166 | 0 | lg.AddTargetByproducts(target, byproducts, lfbt, origin); |
5167 | 0 | } |
5168 | |
|
5169 | 0 | target->AddSource(output); |
5170 | 0 | } |
5171 | | |
5172 | | std::vector<std::string> ComputeISPCObjectSuffixes(cmGeneratorTarget* target) |
5173 | 0 | { |
5174 | 0 | cmValue const targetProperty = target->GetProperty("ISPC_INSTRUCTION_SETS"); |
5175 | 0 | cmList ispcTargets; |
5176 | |
|
5177 | 0 | if (!targetProperty.IsOff()) { |
5178 | 0 | ispcTargets.assign(targetProperty); |
5179 | 0 | for (auto& ispcTarget : ispcTargets) { |
5180 | | // transform targets into the suffixes |
5181 | 0 | auto pos = ispcTarget.find('-'); |
5182 | 0 | auto target_suffix = ispcTarget.substr(0, pos); |
5183 | 0 | if (target_suffix == |
5184 | 0 | "avx1") { // when targeting avx1 ISPC uses the 'avx' output string |
5185 | 0 | target_suffix = "avx"; |
5186 | 0 | } |
5187 | 0 | ispcTarget = target_suffix; |
5188 | 0 | } |
5189 | 0 | } |
5190 | 0 | return std::move(ispcTargets.data()); |
5191 | 0 | } |
5192 | | |
5193 | | std::vector<std::string> ComputeISPCExtraObjects( |
5194 | | std::string const& objectName, std::string const& buildDirectory, |
5195 | | std::vector<std::string> const& ispcSuffixes) |
5196 | 0 | { |
5197 | 0 | auto normalizedDir = cmSystemTools::CollapseFullPath(buildDirectory); |
5198 | 0 | std::vector<std::string> computedObjects; |
5199 | 0 | computedObjects.reserve(ispcSuffixes.size()); |
5200 | |
|
5201 | 0 | auto extension = cmSystemTools::GetFilenameLastExtensionView(objectName); |
5202 | | |
5203 | | // We can't use cmSystemTools::GetFilenameWithoutLastExtension as it |
5204 | | // drops any directories in objectName |
5205 | 0 | auto objNameNoExt = objectName; |
5206 | 0 | std::string::size_type dot_pos = objectName.rfind('.'); |
5207 | 0 | if (dot_pos != std::string::npos) { |
5208 | 0 | objNameNoExt.resize(dot_pos); |
5209 | 0 | } |
5210 | |
|
5211 | 0 | for (auto const& ispcTarget : ispcSuffixes) { |
5212 | 0 | computedObjects.emplace_back( |
5213 | 0 | cmStrCat(normalizedDir, '/', objNameNoExt, '_', ispcTarget, extension)); |
5214 | 0 | } |
5215 | |
|
5216 | 0 | return computedObjects; |
5217 | 0 | } |
5218 | | } |
5219 | | |
5220 | | cmSourcesWithOutput cmLocalGenerator::GetSourcesWithOutput( |
5221 | | std::string const& name) const |
5222 | 0 | { |
5223 | | // Linear search? Also see GetSourceFileWithOutput for detail. |
5224 | 0 | if (!cmSystemTools::FileIsFullPath(name)) { |
5225 | 0 | cmSourcesWithOutput sources; |
5226 | 0 | sources.Target = this->LinearGetTargetWithOutput(name); |
5227 | 0 | sources.Source = this->LinearGetSourceFileWithOutput( |
5228 | 0 | name, cmSourceOutputKind::OutputOrByproduct, sources.SourceIsByproduct); |
5229 | 0 | return sources; |
5230 | 0 | } |
5231 | | // Otherwise we use an efficient lookup map. |
5232 | 0 | auto o = this->OutputToSource.find(name); |
5233 | 0 | if (o != this->OutputToSource.end()) { |
5234 | 0 | return o->second.Sources; |
5235 | 0 | } |
5236 | 0 | return {}; |
5237 | 0 | } |
5238 | | |
5239 | | cmSourceFile* cmLocalGenerator::GetSourceFileWithOutput( |
5240 | | std::string const& name, cmSourceOutputKind kind) const |
5241 | 0 | { |
5242 | | // If the queried path is not absolute we use the backward compatible |
5243 | | // linear-time search for an output with a matching suffix. |
5244 | 0 | if (!cmSystemTools::FileIsFullPath(name)) { |
5245 | 0 | bool byproduct = false; |
5246 | 0 | return this->LinearGetSourceFileWithOutput(name, kind, byproduct); |
5247 | 0 | } |
5248 | | // Otherwise we use an efficient lookup map. |
5249 | 0 | auto o = this->OutputToSource.find(name); |
5250 | 0 | if (o != this->OutputToSource.end() && |
5251 | 0 | (!o->second.Sources.SourceIsByproduct || |
5252 | 0 | kind == cmSourceOutputKind::OutputOrByproduct)) { |
5253 | | // Source file could also be null pointer for example if we found the |
5254 | | // byproduct of a utility target, a PRE_BUILD, PRE_LINK, or POST_BUILD |
5255 | | // command of a target, or a not yet created custom command. |
5256 | 0 | return o->second.Sources.Source; |
5257 | 0 | } |
5258 | 0 | return nullptr; |
5259 | 0 | } |
5260 | | |
5261 | | std::string cmLocalGenerator::CreateUtilityOutput( |
5262 | | std::string const& targetName, std::vector<std::string> const&, |
5263 | | cmListFileBacktrace const&) |
5264 | 0 | { |
5265 | 0 | std::string force = |
5266 | 0 | cmStrCat(this->GetCurrentBinaryDirectory(), "/CMakeFiles/", targetName); |
5267 | | // The output is not actually created so mark it symbolic. |
5268 | 0 | if (cmSourceFile* sf = this->Makefile->GetOrCreateGeneratedSource(force)) { |
5269 | 0 | sf->SetProperty("SYMBOLIC", "1"); |
5270 | 0 | } else { |
5271 | 0 | cmSystemTools::Error("Could not get source file entry for " + force); |
5272 | 0 | } |
5273 | 0 | return force; |
5274 | 0 | } |
5275 | | |
5276 | | std::vector<cmCustomCommandGenerator> |
5277 | | cmLocalGenerator::MakeCustomCommandGenerators(cmCustomCommand const& cc, |
5278 | | std::string const& config) |
5279 | 0 | { |
5280 | 0 | std::vector<cmCustomCommandGenerator> ccgs; |
5281 | 0 | ccgs.emplace_back(cc, config, this); |
5282 | 0 | return ccgs; |
5283 | 0 | } |
5284 | | |
5285 | | std::vector<std::string> cmLocalGenerator::ExpandCustomCommandOutputPaths( |
5286 | | cmCompiledGeneratorExpression const& cge, std::string const& config) |
5287 | 0 | { |
5288 | 0 | cmList paths{ cge.Evaluate(this, config) }; |
5289 | 0 | for (std::string& p : paths) { |
5290 | 0 | p = cmSystemTools::CollapseFullPath(p, this->GetCurrentBinaryDirectory()); |
5291 | 0 | } |
5292 | 0 | return std::move(paths.data()); |
5293 | 0 | } |
5294 | | |
5295 | | std::vector<std::string> cmLocalGenerator::ExpandCustomCommandOutputGenex( |
5296 | | std::string const& o, cmListFileBacktrace const& bt) |
5297 | 0 | { |
5298 | 0 | std::vector<std::string> allConfigOutputs; |
5299 | 0 | cmGeneratorExpression ge(*this->GetCMakeInstance(), bt); |
5300 | 0 | std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(o); |
5301 | 0 | std::vector<std::string> configs = |
5302 | 0 | this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); |
5303 | 0 | for (std::string const& config : configs) { |
5304 | 0 | std::vector<std::string> configOutputs = |
5305 | 0 | this->ExpandCustomCommandOutputPaths(*cge, config); |
5306 | 0 | allConfigOutputs.reserve(allConfigOutputs.size() + configOutputs.size()); |
5307 | 0 | std::move(configOutputs.begin(), configOutputs.end(), |
5308 | 0 | std::back_inserter(allConfigOutputs)); |
5309 | 0 | } |
5310 | 0 | auto endUnique = |
5311 | 0 | cmRemoveDuplicates(allConfigOutputs.begin(), allConfigOutputs.end()); |
5312 | 0 | allConfigOutputs.erase(endUnique, allConfigOutputs.end()); |
5313 | 0 | return allConfigOutputs; |
5314 | 0 | } |
5315 | | |
5316 | | void cmLocalGenerator::AddTargetByproducts( |
5317 | | cmTarget* target, std::vector<std::string> const& byproducts, |
5318 | | cmListFileBacktrace const& bt, cmCommandOrigin origin) |
5319 | 0 | { |
5320 | 0 | for (std::string const& o : byproducts) { |
5321 | 0 | if (cmGeneratorExpression::Find(o) == std::string::npos) { |
5322 | 0 | this->UpdateOutputToSourceMap(o, target, bt, origin); |
5323 | 0 | continue; |
5324 | 0 | } |
5325 | | |
5326 | | // This byproduct path has a generator expression. Evaluate it to |
5327 | | // register the byproducts for all configurations. |
5328 | 0 | for (std::string const& b : this->ExpandCustomCommandOutputGenex(o, bt)) { |
5329 | 0 | this->UpdateOutputToSourceMap(b, target, bt, cmCommandOrigin::Generator); |
5330 | 0 | } |
5331 | 0 | } |
5332 | 0 | } |
5333 | | |
5334 | | void cmLocalGenerator::AddSourceOutputs( |
5335 | | cmSourceFile* source, std::vector<std::string> const& outputs, |
5336 | | OutputRole role, cmListFileBacktrace const& bt, cmCommandOrigin origin) |
5337 | 0 | { |
5338 | 0 | for (std::string const& o : outputs) { |
5339 | 0 | if (cmGeneratorExpression::Find(o) == std::string::npos) { |
5340 | 0 | this->UpdateOutputToSourceMap(o, source, role, bt, origin); |
5341 | 0 | continue; |
5342 | 0 | } |
5343 | | |
5344 | | // This output path has a generator expression. Evaluate it to |
5345 | | // register the outputs for all configurations. |
5346 | 0 | for (std::string const& out : |
5347 | 0 | this->ExpandCustomCommandOutputGenex(o, bt)) { |
5348 | 0 | this->UpdateOutputToSourceMap(out, source, role, bt, |
5349 | 0 | cmCommandOrigin::Generator); |
5350 | 0 | } |
5351 | 0 | } |
5352 | 0 | } |
5353 | | |
5354 | | void cmLocalGenerator::UpdateOutputToSourceMap(std::string const& byproduct, |
5355 | | cmTarget* target, |
5356 | | cmListFileBacktrace const& bt, |
5357 | | cmCommandOrigin origin) |
5358 | 0 | { |
5359 | 0 | SourceEntry entry; |
5360 | 0 | entry.Sources.Target = target; |
5361 | |
|
5362 | 0 | auto pr = this->OutputToSource.emplace(byproduct, entry); |
5363 | 0 | if (pr.second) { |
5364 | 0 | CreateGeneratedSource(*this, byproduct, OutputRole::Byproduct, origin, bt); |
5365 | 0 | } else { |
5366 | 0 | SourceEntry& current = pr.first->second; |
5367 | | // Has the target already been set? |
5368 | 0 | if (!current.Sources.Target) { |
5369 | 0 | current.Sources.Target = target; |
5370 | 0 | } else { |
5371 | | // Multiple custom commands/targets produce the same output (source file |
5372 | | // or target). See also comment in other UpdateOutputToSourceMap |
5373 | | // overload. |
5374 | | // |
5375 | | // TODO: Warn the user about this case. |
5376 | 0 | } |
5377 | 0 | } |
5378 | 0 | } |
5379 | | |
5380 | | void cmLocalGenerator::UpdateOutputToSourceMap(std::string const& output, |
5381 | | cmSourceFile* source, |
5382 | | OutputRole role, |
5383 | | cmListFileBacktrace const& bt, |
5384 | | cmCommandOrigin origin) |
5385 | 0 | { |
5386 | 0 | SourceEntry entry; |
5387 | 0 | entry.Sources.Source = source; |
5388 | 0 | entry.Sources.SourceIsByproduct = role == OutputRole::Byproduct; |
5389 | |
|
5390 | 0 | auto pr = this->OutputToSource.emplace(output, entry); |
5391 | 0 | if (pr.second) { |
5392 | 0 | CreateGeneratedSource(*this, output, role, origin, bt); |
5393 | 0 | } else { |
5394 | 0 | SourceEntry& current = pr.first->second; |
5395 | | // Outputs take precedence over byproducts |
5396 | 0 | if (!current.Sources.Source || |
5397 | 0 | (current.Sources.SourceIsByproduct && role == OutputRole::Primary)) { |
5398 | 0 | current.Sources.Source = source; |
5399 | 0 | current.Sources.SourceIsByproduct = false; |
5400 | 0 | } else { |
5401 | | // Multiple custom commands produce the same output but may |
5402 | | // be attached to a different source file (MAIN_DEPENDENCY). |
5403 | | // LinearGetSourceFileWithOutput would return the first one, |
5404 | | // so keep the mapping for the first one. |
5405 | | // |
5406 | | // TODO: Warn the user about this case. However, the VS 8 generator |
5407 | | // triggers it for separate generate.stamp rules in ZERO_CHECK and |
5408 | | // individual targets. |
5409 | 0 | } |
5410 | 0 | } |
5411 | 0 | } |
5412 | | |
5413 | | cmTarget* cmLocalGenerator::LinearGetTargetWithOutput( |
5414 | | std::string const& name) const |
5415 | 0 | { |
5416 | | // We go through the ordered vector of targets to get reproducible results |
5417 | | // should multiple names match. |
5418 | 0 | for (cmTarget* t : this->Makefile->GetOrderedTargets()) { |
5419 | | // Does the output of any command match the source file name? |
5420 | 0 | if (AnyTargetCommandOutputMatches(name, t->GetPreBuildCommands())) { |
5421 | 0 | return t; |
5422 | 0 | } |
5423 | 0 | if (AnyTargetCommandOutputMatches(name, t->GetPreLinkCommands())) { |
5424 | 0 | return t; |
5425 | 0 | } |
5426 | 0 | if (AnyTargetCommandOutputMatches(name, t->GetPostBuildCommands())) { |
5427 | 0 | return t; |
5428 | 0 | } |
5429 | 0 | } |
5430 | 0 | return nullptr; |
5431 | 0 | } |
5432 | | |
5433 | | cmSourceFile* cmLocalGenerator::LinearGetSourceFileWithOutput( |
5434 | | std::string const& name, cmSourceOutputKind kind, bool& byproduct) const |
5435 | 0 | { |
5436 | | // Outputs take precedence over byproducts. |
5437 | 0 | byproduct = false; |
5438 | 0 | cmSourceFile* fallback = nullptr; |
5439 | | |
5440 | | // Look through all the source files that have custom commands and see if the |
5441 | | // custom command has the passed source file as an output. |
5442 | 0 | for (auto const& src : this->Makefile->GetSourceFiles()) { |
5443 | | // Does this source file have a custom command? |
5444 | 0 | if (src->GetCustomCommand()) { |
5445 | | // Does the output of the custom command match the source file name? |
5446 | 0 | if (AnyOutputMatches(name, src->GetCustomCommand()->GetOutputs())) { |
5447 | | // Return the first matching output. |
5448 | 0 | return src.get(); |
5449 | 0 | } |
5450 | 0 | if (kind == cmSourceOutputKind::OutputOrByproduct) { |
5451 | 0 | if (AnyOutputMatches(name, src->GetCustomCommand()->GetByproducts())) { |
5452 | | // Do not return the source yet as there might be a matching output. |
5453 | 0 | fallback = src.get(); |
5454 | 0 | } |
5455 | 0 | } |
5456 | 0 | } |
5457 | 0 | } |
5458 | | |
5459 | | // Did we find a byproduct? |
5460 | 0 | byproduct = fallback != nullptr; |
5461 | 0 | return fallback; |
5462 | 0 | } |