/src/CMake/Source/cmMakefileTargetGenerator.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 "cmMakefileTargetGenerator.h" |
4 | | |
5 | | #include <algorithm> |
6 | | #include <array> |
7 | | #include <cassert> |
8 | | #include <cstdio> |
9 | | #include <functional> |
10 | | #include <iterator> |
11 | | #include <sstream> |
12 | | #include <unordered_map> |
13 | | #include <unordered_set> |
14 | | #include <utility> |
15 | | |
16 | | #include <cm/memory> |
17 | | #include <cm/optional> |
18 | | #include <cm/string_view> |
19 | | #include <cmext/algorithm> |
20 | | #include <cmext/string_view> |
21 | | |
22 | | #include "cm_codecvt_Encoding.hxx" |
23 | | |
24 | | #include "cmComputeLinkInformation.h" |
25 | | #include "cmCustomCommand.h" |
26 | | #include "cmCustomCommandGenerator.h" |
27 | | #include "cmFileSetMetadata.h" |
28 | | #include "cmGenExContext.h" |
29 | | #include "cmGeneratedFileStream.h" |
30 | | #include "cmGeneratorExpression.h" |
31 | | #include "cmGeneratorFileSet.h" |
32 | | #include "cmGeneratorOptions.h" |
33 | | #include "cmGeneratorTarget.h" |
34 | | #include "cmGlobalUnixMakefileGenerator3.h" |
35 | | #include "cmLinkLineComputer.h" // IWYU pragma: keep |
36 | | #include "cmList.h" |
37 | | #include "cmListFileCache.h" |
38 | | #include "cmLocalCommonGenerator.h" |
39 | | #include "cmLocalGenerator.h" |
40 | | #include "cmLocalUnixMakefileGenerator3.h" |
41 | | #include "cmMakefile.h" |
42 | | #include "cmMakefileExecutableTargetGenerator.h" |
43 | | #include "cmMakefileLibraryTargetGenerator.h" |
44 | | #include "cmMakefileUtilityTargetGenerator.h" |
45 | | #include "cmMessageType.h" |
46 | | #include "cmOutputConverter.h" |
47 | | #include "cmPolicies.h" |
48 | | #include "cmRange.h" |
49 | | #include "cmRulePlaceholderExpander.h" |
50 | | #include "cmScriptGenerator.h" |
51 | | #include "cmSourceFile.h" |
52 | | #include "cmSourceFileLocationKind.h" |
53 | | #include "cmState.h" |
54 | | #include "cmStateDirectory.h" |
55 | | #include "cmStateSnapshot.h" |
56 | | #include "cmStateTypes.h" |
57 | | #include "cmStringAlgorithms.h" |
58 | | #include "cmSystemTools.h" |
59 | | #include "cmValue.h" |
60 | | #include "cmake.h" |
61 | | |
62 | | cmMakefileTargetGenerator::cmMakefileTargetGenerator(cmGeneratorTarget* target) |
63 | 0 | : cmCommonTargetGenerator(target) |
64 | 0 | { |
65 | 0 | this->CustomCommandDriver = OnBuild; |
66 | 0 | this->LocalGenerator = |
67 | 0 | static_cast<cmLocalUnixMakefileGenerator3*>(target->GetLocalGenerator()); |
68 | 0 | this->GlobalGenerator = static_cast<cmGlobalUnixMakefileGenerator3*>( |
69 | 0 | this->LocalGenerator->GetGlobalGenerator()); |
70 | 0 | cmake* cm = this->GlobalGenerator->GetCMakeInstance(); |
71 | 0 | this->NoRuleMessages = false; |
72 | 0 | if (cmValue ruleStatus = |
73 | 0 | cm->GetState()->GetGlobalProperty("RULE_MESSAGES")) { |
74 | 0 | this->NoRuleMessages = ruleStatus.IsOff(); |
75 | 0 | } |
76 | 0 | switch (this->GeneratorTarget->GetPolicyStatusCMP0113()) { |
77 | 0 | case cmPolicies::WARN: |
78 | 0 | CM_FALLTHROUGH; |
79 | 0 | case cmPolicies::OLD: |
80 | 0 | this->CMP0113New = false; |
81 | 0 | break; |
82 | 0 | case cmPolicies::NEW: |
83 | 0 | this->CMP0113New = true; |
84 | 0 | break; |
85 | 0 | } |
86 | 0 | this->MacOSXContentGenerator = |
87 | 0 | cm::make_unique<MacOSXContentGeneratorType>(this); |
88 | 0 | } |
89 | | |
90 | 0 | cmMakefileTargetGenerator::~cmMakefileTargetGenerator() = default; |
91 | | |
92 | | std::unique_ptr<cmMakefileTargetGenerator> cmMakefileTargetGenerator::New( |
93 | | cmGeneratorTarget* tgt) |
94 | 0 | { |
95 | 0 | std::unique_ptr<cmMakefileTargetGenerator> result; |
96 | |
|
97 | 0 | switch (tgt->GetType()) { |
98 | 0 | case cmStateEnums::EXECUTABLE: |
99 | 0 | result = cm::make_unique<cmMakefileExecutableTargetGenerator>(tgt); |
100 | 0 | break; |
101 | 0 | case cmStateEnums::STATIC_LIBRARY: |
102 | 0 | case cmStateEnums::SHARED_LIBRARY: |
103 | 0 | case cmStateEnums::MODULE_LIBRARY: |
104 | 0 | case cmStateEnums::OBJECT_LIBRARY: |
105 | 0 | result = cm::make_unique<cmMakefileLibraryTargetGenerator>(tgt); |
106 | 0 | break; |
107 | 0 | case cmStateEnums::INTERFACE_LIBRARY: |
108 | 0 | case cmStateEnums::UTILITY: |
109 | 0 | result = cm::make_unique<cmMakefileUtilityTargetGenerator>(tgt); |
110 | 0 | break; |
111 | 0 | default: |
112 | 0 | return result; |
113 | | // break; /* unreachable */ |
114 | 0 | } |
115 | 0 | return result; |
116 | 0 | } |
117 | | |
118 | | std::string const& cmMakefileTargetGenerator::GetConfigName() const |
119 | 0 | { |
120 | 0 | auto const& configNames = this->LocalGenerator->GetConfigNames(); |
121 | 0 | assert(configNames.size() == 1); |
122 | 0 | return configNames.front(); |
123 | 0 | } |
124 | | |
125 | | void cmMakefileTargetGenerator::GetDeviceLinkFlags( |
126 | | std::string& linkFlags, std::string const& linkLanguage) |
127 | 0 | { |
128 | 0 | cmGeneratorTarget::DeviceLinkSetter setter(*this->GetGeneratorTarget()); |
129 | |
|
130 | 0 | std::vector<std::string> linkOpts; |
131 | 0 | this->GeneratorTarget->GetLinkOptions(linkOpts, this->GetConfigName(), |
132 | 0 | linkLanguage); |
133 | 0 | this->LocalGenerator->SetLinkScriptShell( |
134 | 0 | this->GlobalGenerator->GetUseLinkScript()); |
135 | | // LINK_OPTIONS are escaped. |
136 | 0 | this->LocalGenerator->AppendCompileOptions(linkFlags, linkOpts); |
137 | 0 | this->LocalGenerator->SetLinkScriptShell(false); |
138 | 0 | } |
139 | | |
140 | | void cmMakefileTargetGenerator::GetTargetLinkFlags( |
141 | | std::string& flags, std::string const& linkLanguage) |
142 | 0 | { |
143 | 0 | this->LocalGenerator->AddTargetPropertyLinkFlags( |
144 | 0 | flags, this->GeneratorTarget, this->GetConfigName()); |
145 | |
|
146 | 0 | std::vector<std::string> opts; |
147 | 0 | this->GeneratorTarget->GetLinkOptions(opts, this->GetConfigName(), |
148 | 0 | linkLanguage); |
149 | 0 | this->LocalGenerator->SetLinkScriptShell( |
150 | 0 | this->GlobalGenerator->GetUseLinkScript()); |
151 | | // LINK_OPTIONS are escaped. |
152 | 0 | this->LocalGenerator->AppendCompileOptions(flags, opts); |
153 | 0 | this->LocalGenerator->SetLinkScriptShell(false); |
154 | |
|
155 | 0 | this->LocalGenerator->AppendLinkerTypeFlags( |
156 | 0 | flags, this->GeneratorTarget, this->GetConfigName(), linkLanguage); |
157 | 0 | this->LocalGenerator->AppendPositionIndependentLinkerFlags( |
158 | 0 | flags, this->GeneratorTarget, this->GetConfigName(), linkLanguage); |
159 | 0 | this->LocalGenerator->AppendWarningAsErrorLinkerFlags( |
160 | 0 | flags, this->GeneratorTarget, linkLanguage); |
161 | 0 | this->LocalGenerator->AppendDependencyInfoLinkerFlags( |
162 | 0 | flags, this->GeneratorTarget, this->GetConfigName(), linkLanguage); |
163 | 0 | } |
164 | | |
165 | | void cmMakefileTargetGenerator::CreateRuleFile() |
166 | 0 | { |
167 | | // Create a directory for this target. |
168 | 0 | this->TargetBuildDirectoryFull = |
169 | 0 | this->GeneratorTarget->GetSupportDirectory(); |
170 | 0 | this->TargetBuildDirectory = this->LocalGenerator->MaybeRelativeToCurBinDir( |
171 | 0 | this->TargetBuildDirectoryFull); |
172 | 0 | cmSystemTools::MakeDirectory(this->TargetBuildDirectoryFull); |
173 | | |
174 | | // Construct the rule file name. |
175 | 0 | this->BuildFileName = cmStrCat(this->TargetBuildDirectory, "/build.make"); |
176 | 0 | this->BuildFileNameFull = |
177 | 0 | cmStrCat(this->TargetBuildDirectoryFull, "/build.make"); |
178 | | |
179 | | // Construct the rule file name. |
180 | 0 | this->ProgressFileNameFull = |
181 | 0 | cmStrCat(this->TargetBuildDirectoryFull, "/progress.make"); |
182 | | |
183 | | // reset the progress count |
184 | 0 | this->NumberOfProgressActions = 0; |
185 | | |
186 | | // Open the rule file. This should be copy-if-different because the |
187 | | // rules may depend on this file itself. |
188 | 0 | this->BuildFileStream = cm::make_unique<cmGeneratedFileStream>( |
189 | 0 | this->BuildFileNameFull, false, |
190 | 0 | this->GlobalGenerator->GetMakefileEncoding()); |
191 | 0 | if (!this->BuildFileStream) { |
192 | 0 | return; |
193 | 0 | } |
194 | 0 | this->BuildFileStream->SetCopyIfDifferent(true); |
195 | 0 | this->LocalGenerator->WriteDisclaimer(*this->BuildFileStream); |
196 | 0 | if (this->GlobalGenerator->AllowDeleteOnError()) { |
197 | 0 | std::vector<std::string> no_depends; |
198 | 0 | std::vector<std::string> no_commands; |
199 | 0 | this->LocalGenerator->WriteMakeRule( |
200 | 0 | *this->BuildFileStream, "Delete rule output on recipe failure.", |
201 | 0 | ".DELETE_ON_ERROR", no_depends, no_commands, false); |
202 | 0 | } |
203 | 0 | this->LocalGenerator->WriteSpecialTargetsTop(*this->BuildFileStream); |
204 | 0 | } |
205 | | |
206 | | void cmMakefileTargetGenerator::WriteTargetBuildRules() |
207 | 0 | { |
208 | 0 | cm::GenEx::Context context(this->LocalGenerator, this->GetConfigName()); |
209 | 0 | this->GeneratorTarget->CheckCxxModuleStatus(this->GetConfigName()); |
210 | | |
211 | | // -- Write the custom commands for this target |
212 | | |
213 | | // Evaluates generator expressions and expands prop_value |
214 | 0 | auto evaluatedFiles = [this](std::string const& prop_value) -> cmList { |
215 | 0 | cmList files{ cmGeneratorExpression::Evaluate( |
216 | 0 | prop_value, this->LocalGenerator, this->GetConfigName(), |
217 | 0 | this->GeneratorTarget) }; |
218 | 0 | return files; |
219 | 0 | }; |
220 | | |
221 | | // Look for additional files registered for cleaning in this directory. |
222 | 0 | if (cmValue prop_value = |
223 | 0 | this->Makefile->GetProperty("ADDITIONAL_MAKE_CLEAN_FILES")) { |
224 | 0 | auto const files = evaluatedFiles(*prop_value); |
225 | 0 | this->CleanFiles.insert(files.begin(), files.end()); |
226 | 0 | } |
227 | | |
228 | | // Look for additional files registered for cleaning in this target. |
229 | 0 | if (cmValue prop_value = |
230 | 0 | this->GeneratorTarget->GetProperty("ADDITIONAL_CLEAN_FILES")) { |
231 | 0 | auto const files = evaluatedFiles(*prop_value); |
232 | | // For relative path support |
233 | 0 | std::string const& binaryDir = |
234 | 0 | this->LocalGenerator->GetCurrentBinaryDirectory(); |
235 | 0 | for (std::string const& cfl : files) { |
236 | 0 | this->CleanFiles.insert(cmSystemTools::CollapseFullPath(cfl, binaryDir)); |
237 | 0 | } |
238 | 0 | } |
239 | | |
240 | | // Look for ISPC extra object files generated by this target |
241 | 0 | auto const ispcAdditionalObjs = |
242 | 0 | this->GeneratorTarget->GetGeneratedISPCObjects(this->GetConfigName()); |
243 | 0 | for (auto const& ispcObj : ispcAdditionalObjs) { |
244 | 0 | this->CleanFiles.insert( |
245 | 0 | this->LocalGenerator->MaybeRelativeToCurBinDir(ispcObj.second)); |
246 | 0 | } |
247 | | |
248 | | // add custom commands to the clean rules? |
249 | 0 | bool const clean = this->Makefile->GetProperty("CLEAN_NO_CUSTOM").IsOff(); |
250 | | |
251 | | // First generate the object rule files. Save a list of all object |
252 | | // files for this target. |
253 | 0 | std::vector<cmSourceFile const*> customCommands; |
254 | 0 | this->GeneratorTarget->GetCustomCommands(customCommands, |
255 | 0 | this->GetConfigName()); |
256 | 0 | std::vector<std::string> codegen_depends; |
257 | 0 | codegen_depends.reserve(customCommands.size()); |
258 | 0 | for (cmSourceFile const* sf : customCommands) { |
259 | 0 | if (this->CMP0113New && |
260 | 0 | !this->LocalGenerator->GetCommandsVisited(this->GeneratorTarget) |
261 | 0 | .insert(sf) |
262 | 0 | .second) { |
263 | 0 | continue; |
264 | 0 | } |
265 | 0 | cmCustomCommandGenerator ccg(*sf->GetCustomCommand(), |
266 | 0 | this->GetConfigName(), this->LocalGenerator); |
267 | 0 | this->GenerateCustomRuleFile(ccg); |
268 | 0 | if (clean) { |
269 | 0 | std::vector<std::string> const& outputs = ccg.GetOutputs(); |
270 | 0 | for (std::string const& output : outputs) { |
271 | 0 | this->CleanFiles.insert( |
272 | 0 | this->LocalGenerator->MaybeRelativeToCurBinDir(output)); |
273 | 0 | } |
274 | 0 | std::vector<std::string> const& byproducts = ccg.GetByproducts(); |
275 | 0 | for (std::string const& byproduct : byproducts) { |
276 | 0 | this->CleanFiles.insert( |
277 | 0 | this->LocalGenerator->MaybeRelativeToCurBinDir(byproduct)); |
278 | 0 | } |
279 | 0 | } |
280 | |
|
281 | 0 | if (ccg.GetCC().GetCodegen()) { |
282 | 0 | std::string const& output = ccg.GetOutputs().front(); |
283 | | |
284 | | // We always attach the actual commands to the first output. |
285 | 0 | codegen_depends.emplace_back(output); |
286 | 0 | } |
287 | 0 | } |
288 | | |
289 | | // Some make tools need a special dependency for an empty rule. |
290 | 0 | if (codegen_depends.empty()) { |
291 | 0 | std::string hack = this->GlobalGenerator->GetEmptyRuleHackDepends(); |
292 | 0 | if (!hack.empty()) { |
293 | 0 | codegen_depends.emplace_back(std::move(hack)); |
294 | 0 | } |
295 | 0 | } |
296 | | |
297 | | // Construct the codegen target. |
298 | 0 | { |
299 | 0 | std::string const codegenTarget = cmStrCat( |
300 | 0 | this->LocalGenerator->GetRelativeTargetDirectory(this->GeneratorTarget), |
301 | 0 | "/codegen"); |
302 | | |
303 | | // Write the rule. |
304 | 0 | this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr, |
305 | 0 | codegenTarget, codegen_depends, {}, |
306 | 0 | true); |
307 | 0 | } |
308 | | |
309 | | // Add byproducts from build events to the clean rules |
310 | 0 | if (clean) { |
311 | 0 | std::vector<cmCustomCommand> buildEventCommands = |
312 | 0 | this->GeneratorTarget->GetPreBuildCommands(); |
313 | |
|
314 | 0 | cm::append(buildEventCommands, |
315 | 0 | this->GeneratorTarget->GetPreLinkCommands()); |
316 | 0 | cm::append(buildEventCommands, |
317 | 0 | this->GeneratorTarget->GetPostBuildCommands()); |
318 | |
|
319 | 0 | for (auto const& be : buildEventCommands) { |
320 | 0 | cmCustomCommandGenerator beg(be, this->GetConfigName(), |
321 | 0 | this->LocalGenerator); |
322 | 0 | std::vector<std::string> const& byproducts = beg.GetByproducts(); |
323 | 0 | for (std::string const& byproduct : byproducts) { |
324 | 0 | this->CleanFiles.insert( |
325 | 0 | this->LocalGenerator->MaybeRelativeToCurBinDir(byproduct)); |
326 | 0 | } |
327 | 0 | } |
328 | 0 | } |
329 | 0 | std::vector<cmSourceFile const*> headerSources; |
330 | 0 | this->GeneratorTarget->GetHeaderSources(headerSources, |
331 | 0 | this->GetConfigName()); |
332 | 0 | this->OSXBundleGenerator->GenerateMacOSXContentStatements( |
333 | 0 | headerSources, this->MacOSXContentGenerator.get(), this->GetConfigName()); |
334 | 0 | std::vector<cmSourceFile const*> extraSources; |
335 | 0 | this->GeneratorTarget->GetExtraSources(extraSources, this->GetConfigName()); |
336 | 0 | this->OSXBundleGenerator->GenerateMacOSXContentStatements( |
337 | 0 | extraSources, this->MacOSXContentGenerator.get(), this->GetConfigName()); |
338 | 0 | cmValue pchExtension = this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION"); |
339 | 0 | std::vector<cmSourceFile const*> externalObjects; |
340 | 0 | this->GeneratorTarget->GetExternalObjects(externalObjects, |
341 | 0 | this->GetConfigName()); |
342 | 0 | for (cmSourceFile const* sf : externalObjects) { |
343 | 0 | auto const& objectFileName = sf->GetFullPath(); |
344 | 0 | if (!cmHasSuffix(objectFileName, pchExtension)) { |
345 | 0 | this->ExternalObjects.push_back(objectFileName); |
346 | 0 | } |
347 | 0 | } |
348 | |
|
349 | 0 | std::vector<cmSourceFile const*> objectSources; |
350 | 0 | this->GeneratorTarget->GetObjectSources(objectSources, |
351 | 0 | this->GetConfigName()); |
352 | | |
353 | | // validate that all languages requested are enabled. |
354 | 0 | std::set<std::string> requiredLangs; |
355 | 0 | if (this->HaveRequiredLanguages(objectSources, requiredLangs)) { |
356 | 0 | for (cmSourceFile const* sf : objectSources) { |
357 | | // Generate this object file's rule file. |
358 | 0 | this->WriteObjectRuleFiles(*sf); |
359 | 0 | } |
360 | 0 | } |
361 | |
|
362 | 0 | for (cmSourceFile const* sf : objectSources) { |
363 | 0 | cmGeneratorFileSet const* fileSet = |
364 | 0 | this->GeneratorTarget->GetFileSetForSource(this->GetConfigName(), sf); |
365 | 0 | if (fileSet && fileSet->GetType() == cm::FileSetMetadata::CXX_MODULES && |
366 | 0 | sf->GetLanguage() != "CXX"_s) { |
367 | 0 | this->Makefile->IssueMessage( |
368 | 0 | MessageType::FATAL_ERROR, |
369 | 0 | cmStrCat("Target \"", this->GeneratorTarget->GetName(), |
370 | 0 | "\" contains the source\n ", sf->GetFullPath(), |
371 | 0 | "\nin a file set of type \"", |
372 | 0 | cm::FileSetMetadata::CXX_MODULES, |
373 | 0 | R"(" but the source is not classified as a "CXX" source.)")); |
374 | 0 | } |
375 | 0 | } |
376 | 0 | } |
377 | | |
378 | | void cmMakefileTargetGenerator::WriteCommonCodeRules() |
379 | 0 | { |
380 | 0 | char const* root = (this->Makefile->IsOn("CMAKE_MAKE_INCLUDE_FROM_ROOT") |
381 | 0 | ? "$(CMAKE_BINARY_DIR)/" |
382 | 0 | : ""); |
383 | | |
384 | | // Include the dependencies for the target. |
385 | 0 | std::string dependFileNameFull = |
386 | 0 | cmStrCat(this->TargetBuildDirectoryFull, "/depend.make"); |
387 | 0 | *this->BuildFileStream |
388 | 0 | << "# Include any dependencies generated for this target.\n" |
389 | 0 | << this->GlobalGenerator->IncludeDirective << " " << root |
390 | 0 | << cmSystemTools::ConvertToOutputPath( |
391 | 0 | this->LocalGenerator->MaybeRelativeToTopBinDir(dependFileNameFull)) |
392 | 0 | << "\n"; |
393 | | |
394 | | // Scan any custom commands to check if DEPFILE option is specified |
395 | 0 | bool ccGenerateDeps = false; |
396 | 0 | std::vector<cmSourceFile const*> customCommands; |
397 | 0 | this->GeneratorTarget->GetCustomCommands(customCommands, |
398 | 0 | this->GetConfigName()); |
399 | 0 | for (cmSourceFile const* sf : customCommands) { |
400 | 0 | if (!sf->GetCustomCommand()->GetDepfile().empty()) { |
401 | 0 | ccGenerateDeps = true; |
402 | 0 | break; |
403 | 0 | } |
404 | 0 | } |
405 | |
|
406 | 0 | std::string depsUseCompiler = "CMAKE_DEPENDS_USE_COMPILER"; |
407 | 0 | bool compilerGenerateDeps = |
408 | 0 | this->GlobalGenerator->SupportsCompilerDependencies() && |
409 | 0 | (!this->Makefile->IsDefinitionSet(depsUseCompiler) || |
410 | 0 | this->Makefile->IsOn(depsUseCompiler)); |
411 | 0 | bool linkerGenerateDeps = |
412 | 0 | this->GeneratorTarget->HasLinkDependencyFile(this->GetConfigName()); |
413 | |
|
414 | 0 | if (compilerGenerateDeps || linkerGenerateDeps || ccGenerateDeps) { |
415 | 0 | std::string const compilerDependFile = |
416 | 0 | cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.make"); |
417 | 0 | *this->BuildFileStream << "# Include any dependencies generated by the " |
418 | 0 | "compiler for this target.\n" |
419 | 0 | << this->GlobalGenerator->IncludeDirective << " " |
420 | 0 | << root |
421 | 0 | << cmSystemTools::ConvertToOutputPath( |
422 | 0 | this->LocalGenerator->MaybeRelativeToTopBinDir( |
423 | 0 | compilerDependFile)) |
424 | 0 | << "\n\n"; |
425 | | |
426 | | // Write an empty dependency file. |
427 | 0 | cmGeneratedFileStream depFileStream( |
428 | 0 | compilerDependFile, false, this->GlobalGenerator->GetMakefileEncoding()); |
429 | 0 | depFileStream << "# Empty compiler generated dependencies file for " |
430 | 0 | << this->GeneratorTarget->GetName() << ".\n" |
431 | 0 | << "# This may be replaced when dependencies are built.\n"; |
432 | | // remove internal dependency file |
433 | 0 | cmSystemTools::RemoveFile( |
434 | 0 | cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.internal")); |
435 | |
|
436 | 0 | std::string compilerDependTimestamp = |
437 | 0 | cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.ts"); |
438 | 0 | if (!cmSystemTools::FileExists(compilerDependTimestamp)) { |
439 | | // Write a dependency timestamp file. |
440 | 0 | cmGeneratedFileStream timestampFileStream( |
441 | 0 | compilerDependTimestamp, false, |
442 | 0 | this->GlobalGenerator->GetMakefileEncoding()); |
443 | 0 | timestampFileStream |
444 | 0 | << "# CMAKE generated file: DO NOT EDIT!\n" |
445 | 0 | << "# Timestamp file for compiler generated dependencies " |
446 | 0 | "management for " |
447 | 0 | << this->GeneratorTarget->GetName() << ".\n"; |
448 | 0 | } |
449 | 0 | } |
450 | |
|
451 | 0 | if (compilerGenerateDeps) { |
452 | | // deactivate no longer needed legacy dependency files |
453 | | // Write an empty dependency file. |
454 | 0 | cmGeneratedFileStream legacyDepFileStream( |
455 | 0 | dependFileNameFull, false, this->GlobalGenerator->GetMakefileEncoding()); |
456 | 0 | legacyDepFileStream |
457 | 0 | << "# Empty dependencies file for " << this->GeneratorTarget->GetName() |
458 | 0 | << ".\n" |
459 | 0 | << "# This may be replaced when dependencies are built.\n"; |
460 | | // remove internal dependency file |
461 | 0 | cmSystemTools::RemoveFile( |
462 | 0 | cmStrCat(this->TargetBuildDirectoryFull, "/depend.internal")); |
463 | 0 | } else { |
464 | | // make sure the depend file exists |
465 | 0 | if (!cmSystemTools::FileExists(dependFileNameFull)) { |
466 | | // Write an empty dependency file. |
467 | 0 | cmGeneratedFileStream depFileStream( |
468 | 0 | dependFileNameFull, false, |
469 | 0 | this->GlobalGenerator->GetMakefileEncoding()); |
470 | 0 | depFileStream << "# Empty dependencies file for " |
471 | 0 | << this->GeneratorTarget->GetName() << ".\n" |
472 | 0 | << "# This may be replaced when dependencies are built.\n"; |
473 | 0 | } |
474 | 0 | } |
475 | |
|
476 | 0 | if (!this->NoRuleMessages) { |
477 | | // Include the progress variables for the target. |
478 | 0 | *this->BuildFileStream |
479 | 0 | << "# Include the progress variables for this target.\n" |
480 | 0 | << this->GlobalGenerator->IncludeDirective << " " << root |
481 | 0 | << cmSystemTools::ConvertToOutputPath( |
482 | 0 | this->LocalGenerator->MaybeRelativeToTopBinDir( |
483 | 0 | this->ProgressFileNameFull)) |
484 | 0 | << "\n\n"; |
485 | 0 | } |
486 | | |
487 | | // Open the flags file. This should be copy-if-different because the |
488 | | // rules may depend on this file itself. |
489 | 0 | this->FlagFileNameFull = |
490 | 0 | cmStrCat(this->TargetBuildDirectoryFull, "/flags.make"); |
491 | 0 | this->FlagFileStream = cm::make_unique<cmGeneratedFileStream>( |
492 | 0 | this->FlagFileNameFull, false, |
493 | 0 | this->GlobalGenerator->GetMakefileEncoding()); |
494 | 0 | if (!this->FlagFileStream) { |
495 | 0 | return; |
496 | 0 | } |
497 | 0 | this->FlagFileStream->SetCopyIfDifferent(true); |
498 | 0 | this->LocalGenerator->WriteDisclaimer(*this->FlagFileStream); |
499 | | |
500 | | // Include the flags for the target. |
501 | 0 | *this->BuildFileStream |
502 | 0 | << "# Include the compile flags for this target's objects.\n" |
503 | 0 | << this->GlobalGenerator->IncludeDirective << " " << root |
504 | 0 | << cmSystemTools::ConvertToOutputPath( |
505 | 0 | this->LocalGenerator->MaybeRelativeToTopBinDir( |
506 | 0 | this->FlagFileNameFull)) |
507 | 0 | << "\n\n"; |
508 | 0 | } |
509 | | |
510 | | void cmMakefileTargetGenerator::WriteTargetLanguageFlags() |
511 | 0 | { |
512 | | // write language flags for target |
513 | 0 | std::set<std::string> languages; |
514 | 0 | this->GeneratorTarget->GetLanguages( |
515 | 0 | languages, this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE")); |
516 | | // put the compiler in the rules.make file so that if it changes |
517 | | // things rebuild |
518 | 0 | for (std::string const& language : languages) { |
519 | 0 | std::string compiler = cmStrCat("CMAKE_", language, "_COMPILER"); |
520 | 0 | *this->FlagFileStream << "# compile " << language << " with " |
521 | 0 | << this->Makefile->GetSafeDefinition(compiler) |
522 | 0 | << "\n"; |
523 | 0 | } |
524 | |
|
525 | 0 | bool const escapeOctothorpe = this->GlobalGenerator->CanEscapeOctothorpe(); |
526 | |
|
527 | 0 | for (std::string const& language : languages) { |
528 | 0 | std::string defines = this->GetDefines(language, this->GetConfigName()); |
529 | 0 | std::string includes = this->GetIncludes(language, this->GetConfigName()); |
530 | 0 | if (escapeOctothorpe) { |
531 | | // Escape comment characters so they do not terminate assignment. |
532 | 0 | cmSystemTools::ReplaceString(defines, "#", "\\#"); |
533 | 0 | cmSystemTools::ReplaceString(includes, "#", "\\#"); |
534 | 0 | } |
535 | 0 | *this->FlagFileStream << language << "_DEFINES = " << defines << "\n\n"; |
536 | 0 | *this->FlagFileStream << language << "_INCLUDES = " << includes << "\n\n"; |
537 | |
|
538 | 0 | std::vector<std::string> architectures = |
539 | 0 | this->GeneratorTarget->GetAppleArchs(this->GetConfigName(), language); |
540 | 0 | architectures.emplace_back(); |
541 | |
|
542 | 0 | for (std::string const& arch : architectures) { |
543 | 0 | std::string flags = |
544 | 0 | this->GetFlags(language, this->GetConfigName(), arch); |
545 | 0 | if (escapeOctothorpe) { |
546 | 0 | cmSystemTools::ReplaceString(flags, "#", "\\#"); |
547 | 0 | } |
548 | 0 | *this->FlagFileStream << language << "_FLAGS" << arch << " = " << flags |
549 | 0 | << "\n\n"; |
550 | 0 | } |
551 | 0 | } |
552 | 0 | } |
553 | | |
554 | | void cmMakefileTargetGenerator::MacOSXContentGeneratorType::operator()( |
555 | | cmSourceFile const& source, char const* pkgloc, std::string const& config) |
556 | 0 | { |
557 | | // Skip OS X content when not building a Framework or Bundle. |
558 | 0 | if (!this->Generator->GetGeneratorTarget()->IsBundleOnApple()) { |
559 | 0 | return; |
560 | 0 | } |
561 | | |
562 | 0 | std::string macdir = |
563 | 0 | this->Generator->OSXBundleGenerator->InitMacOSXContentDirectory(pkgloc, |
564 | 0 | config); |
565 | | |
566 | | // Get the input file location. |
567 | 0 | std::string const& input = source.GetFullPath(); |
568 | | |
569 | | // Get the output file location. |
570 | 0 | std::string output = |
571 | 0 | cmStrCat(macdir, '/', cmSystemTools::GetFilenameName(input)); |
572 | 0 | this->Generator->CleanFiles.insert( |
573 | 0 | this->Generator->LocalGenerator->MaybeRelativeToCurBinDir(output)); |
574 | 0 | output = this->Generator->LocalGenerator->MaybeRelativeToTopBinDir(output); |
575 | | |
576 | | // Create a rule to copy the content into the bundle. |
577 | 0 | std::vector<std::string> depends; |
578 | 0 | std::vector<std::string> commands; |
579 | 0 | depends.push_back(input); |
580 | 0 | std::string copyEcho = cmStrCat("Copying OS X content ", output); |
581 | 0 | this->Generator->LocalGenerator->AppendEcho( |
582 | 0 | commands, copyEcho, cmLocalUnixMakefileGenerator3::EchoBuild); |
583 | 0 | std::string copyCommand = |
584 | 0 | cmStrCat(cmSystemTools::FileIsDirectory(input) |
585 | 0 | ? "$(CMAKE_COMMAND) -E copy_directory " |
586 | 0 | : "$(CMAKE_COMMAND) -E copy ", |
587 | 0 | this->Generator->LocalGenerator->ConvertToOutputFormat( |
588 | 0 | input, cmOutputConverter::SHELL), |
589 | 0 | ' ', |
590 | 0 | this->Generator->LocalGenerator->ConvertToOutputFormat( |
591 | 0 | output, cmOutputConverter::SHELL)); |
592 | 0 | commands.push_back(std::move(copyCommand)); |
593 | 0 | this->Generator->LocalGenerator->WriteMakeRule( |
594 | 0 | *this->Generator->BuildFileStream, nullptr, output, depends, commands, |
595 | 0 | false); |
596 | 0 | this->Generator->ExtraFiles.insert(output); |
597 | 0 | } |
598 | | |
599 | | void cmMakefileTargetGenerator::WriteObjectRuleFiles( |
600 | | cmSourceFile const& source) |
601 | 0 | { |
602 | | // Identify the language of the source file. |
603 | 0 | std::string const& lang = source.GetLanguage(); |
604 | 0 | if (lang.empty()) { |
605 | | // don't know anything about this file so skip it |
606 | 0 | return; |
607 | 0 | } |
608 | | |
609 | | // Use compiler to generate dependencies, if supported. |
610 | 0 | bool const compilerGenerateDeps = |
611 | 0 | this->GlobalGenerator->SupportsCompilerDependencies() && |
612 | 0 | this->Makefile |
613 | 0 | ->GetDefinition(cmStrCat("CMAKE_", lang, "_DEPENDS_USE_COMPILER")) |
614 | 0 | .IsOn(); |
615 | 0 | auto const scanner = compilerGenerateDeps ? cmDependencyScannerKind::Compiler |
616 | 0 | : cmDependencyScannerKind::CMake; |
617 | | |
618 | | // Get the full path name of the object file. |
619 | 0 | std::string const& objectName = |
620 | 0 | this->GeneratorTarget->GetObjectName(&source); |
621 | 0 | std::string const obj = |
622 | 0 | cmStrCat(this->TargetBuildDirectory, '/', objectName); |
623 | | |
624 | | // Avoid generating duplicate rules. |
625 | 0 | if (this->ObjectFiles.find(obj) == this->ObjectFiles.end()) { |
626 | 0 | this->ObjectFiles.insert(obj); |
627 | 0 | } else { |
628 | 0 | std::ostringstream err; |
629 | 0 | err << "Warning: Source file \"" << source.GetFullPath() |
630 | 0 | << "\" is listed multiple times for target \"" |
631 | 0 | << this->GeneratorTarget->GetName() << "\"."; |
632 | 0 | cmSystemTools::Message(err.str(), "Warning"); |
633 | 0 | return; |
634 | 0 | } |
635 | | |
636 | | // Create the directory containing the object file. This may be a |
637 | | // subdirectory under the target's directory. |
638 | 0 | { |
639 | 0 | std::string const dir = cmSystemTools::GetFilenamePath(obj); |
640 | 0 | cmSystemTools::MakeDirectory(this->LocalGenerator->ConvertToFullPath(dir)); |
641 | 0 | } |
642 | | |
643 | | // Save this in the target's list of object files. |
644 | 0 | this->Objects.push_back(obj); |
645 | 0 | this->CleanFiles.insert(obj); |
646 | |
|
647 | 0 | std::vector<std::string> depends; |
648 | | |
649 | | // The object file should be checked for dependency integrity. |
650 | 0 | std::string objFullPath = |
651 | 0 | cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), '/', obj); |
652 | 0 | std::string const srcFullPath = source.GetFullPath(); |
653 | 0 | this->LocalGenerator->AddImplicitDepends(this->GeneratorTarget, lang, |
654 | 0 | objFullPath, srcFullPath, scanner); |
655 | |
|
656 | 0 | this->LocalGenerator->AppendRuleDepend(depends, |
657 | 0 | this->FlagFileNameFull.c_str()); |
658 | 0 | this->LocalGenerator->AppendRuleDepends(depends, |
659 | 0 | this->FlagFileDepends[lang]); |
660 | | |
661 | | // generate the depend scanning rule |
662 | 0 | this->WriteObjectDependRules(source, depends); |
663 | |
|
664 | 0 | std::string const config = this->GetConfigName(); |
665 | 0 | std::string const configUpper = cmSystemTools::UpperCase(config); |
666 | | |
667 | | // lookup for the associated file set, if any. |
668 | 0 | auto const* fileSet = |
669 | 0 | this->GeneratorTarget->GetFileSetForSource(config, &source); |
670 | | |
671 | | // Add precompile headers dependencies |
672 | 0 | std::vector<std::string> pchArchs = |
673 | 0 | this->GeneratorTarget->GetPchArchs(config, lang); |
674 | |
|
675 | 0 | std::string filterArch; |
676 | 0 | std::unordered_map<std::string, std::string> pchSources; |
677 | 0 | for (std::string const& arch : pchArchs) { |
678 | 0 | std::string const pchSource = |
679 | 0 | this->GeneratorTarget->GetPchSource(config, lang, arch); |
680 | 0 | if (pchSource == source.GetFullPath()) { |
681 | 0 | filterArch = arch; |
682 | 0 | } |
683 | 0 | if (!pchSource.empty()) { |
684 | 0 | pchSources.insert(std::make_pair(pchSource, arch)); |
685 | 0 | } |
686 | 0 | } |
687 | |
|
688 | 0 | if (!pchSources.empty() && |
689 | 0 | !((fileSet && fileSet->GetProperty("SKIP_PRECOMPILE_HEADERS")) || |
690 | 0 | source.GetProperty("SKIP_PRECOMPILE_HEADERS"))) { |
691 | 0 | for (std::string const& arch : pchArchs) { |
692 | 0 | std::string const& pchHeader = |
693 | 0 | this->GeneratorTarget->GetPchHeader(config, lang, arch); |
694 | 0 | depends.push_back(pchHeader); |
695 | 0 | if (pchSources.find(source.GetFullPath()) == pchSources.end()) { |
696 | 0 | depends.push_back( |
697 | 0 | this->GeneratorTarget->GetPchFile(config, lang, arch)); |
698 | 0 | } |
699 | 0 | this->LocalGenerator->AddImplicitDepends( |
700 | 0 | this->GeneratorTarget, lang, objFullPath, pchHeader, scanner); |
701 | 0 | } |
702 | 0 | } |
703 | |
|
704 | 0 | if (lang != "ISPC") { |
705 | 0 | auto const& headers = |
706 | 0 | this->GeneratorTarget->GetGeneratedISPCHeaders(config); |
707 | 0 | if (!headers.empty()) { |
708 | 0 | depends.insert(depends.end(), headers.begin(), headers.end()); |
709 | 0 | } |
710 | 0 | } |
711 | |
|
712 | 0 | std::string relativeObj = |
713 | 0 | cmStrCat(this->LocalGenerator->GetHomeRelativeOutputPath(), obj); |
714 | | // Write the build rule. |
715 | | |
716 | | // Build the set of compiler flags. |
717 | 0 | std::string flags; |
718 | | |
719 | | // Explicitly add the explicit language flag before any other flag |
720 | | // so user flags can override it. |
721 | 0 | this->GeneratorTarget->AddExplicitLanguageFlags(flags, source); |
722 | | |
723 | | // Add language-specific flags. |
724 | 0 | std::string const langFlags = |
725 | 0 | cmStrCat("$(", lang, "_FLAGS", filterArch, ')'); |
726 | 0 | this->LocalGenerator->AppendFlags(flags, langFlags); |
727 | |
|
728 | 0 | cmGeneratorExpressionInterpreter genexInterpreter( |
729 | 0 | this->LocalGenerator, config, this->GeneratorTarget, lang); |
730 | | |
731 | | // Add Fortran format flags. |
732 | 0 | if (lang == "Fortran") { |
733 | 0 | this->AppendFortranFormatFlags(flags, source); |
734 | 0 | this->AppendFortranPreprocessFlags(flags, source); |
735 | 0 | } |
736 | |
|
737 | 0 | std::string ispcHeaderRelative; |
738 | 0 | std::string ispcHeaderForShell; |
739 | 0 | if (lang == "ISPC") { |
740 | 0 | std::string ispcSource = |
741 | 0 | cmSystemTools::GetFilenameWithoutLastExtension(objectName); |
742 | 0 | ispcSource = cmSystemTools::GetFilenameWithoutLastExtension(ispcSource); |
743 | |
|
744 | 0 | cmValue const ispcSuffixProp = |
745 | 0 | this->GeneratorTarget->GetProperty("ISPC_HEADER_SUFFIX"); |
746 | 0 | assert(ispcSuffixProp); |
747 | |
|
748 | 0 | std::string directory = this->GeneratorTarget->GetObjectDirectory(config); |
749 | 0 | if (cmValue prop = |
750 | 0 | this->GeneratorTarget->GetProperty("ISPC_HEADER_DIRECTORY")) { |
751 | 0 | directory = |
752 | 0 | cmStrCat(this->LocalGenerator->GetBinaryDirectory(), '/', *prop); |
753 | 0 | } |
754 | 0 | ispcHeaderRelative = cmStrCat(directory, '/', ispcSource, *ispcSuffixProp); |
755 | 0 | ispcHeaderForShell = this->LocalGenerator->ConvertToOutputFormat( |
756 | 0 | ispcHeaderRelative, cmOutputConverter::SHELL); |
757 | 0 | } |
758 | | |
759 | | // Add flags from source file properties. |
760 | 0 | std::string const COMPILE_FLAGS("COMPILE_FLAGS"); |
761 | 0 | if (cmValue cflags = source.GetProperty(COMPILE_FLAGS)) { |
762 | 0 | std::string const& evaluatedFlags = |
763 | 0 | genexInterpreter.Evaluate(*cflags, COMPILE_FLAGS); |
764 | 0 | this->LocalGenerator->AppendFlags(flags, evaluatedFlags); |
765 | 0 | *this->FlagFileStream << "# Custom flags: " << relativeObj |
766 | 0 | << "_FLAGS = " << evaluatedFlags << "\n" |
767 | 0 | << "\n"; |
768 | 0 | } |
769 | |
|
770 | 0 | std::string const COMPILE_OPTIONS("COMPILE_OPTIONS"); |
771 | 0 | if (cmValue coptions = source.GetProperty(COMPILE_OPTIONS)) { |
772 | 0 | std::string const& evaluatedOptions = |
773 | 0 | genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS); |
774 | 0 | this->LocalGenerator->AppendCompileOptions(flags, evaluatedOptions); |
775 | 0 | *this->FlagFileStream << "# Custom options: " << relativeObj |
776 | 0 | << "_OPTIONS = " << evaluatedOptions << "\n" |
777 | 0 | << "\n"; |
778 | 0 | } |
779 | | |
780 | | // Add flags from file set properties. |
781 | 0 | if (fileSet) { |
782 | 0 | auto options = fileSet->BelongsTo(this->GeneratorTarget) |
783 | 0 | ? fileSet->GetCompileOptions(config, lang) |
784 | 0 | : fileSet->GetInterfaceCompileOptions(config, lang); |
785 | 0 | if (!options.empty()) { |
786 | 0 | this->LocalGenerator->AppendCompileOptions(flags, |
787 | 0 | cm::remove_BT(options)); |
788 | 0 | *this->FlagFileStream << "# Custom options (File Set '" |
789 | 0 | << fileSet->GetName() << "'): " << relativeObj |
790 | 0 | << "_OPTIONS = " << cmList::to_string(options) |
791 | 0 | << "\n" |
792 | 0 | << "\n"; |
793 | 0 | } |
794 | 0 | } |
795 | | |
796 | | // Add precompile headers compile options. |
797 | 0 | if (!pchSources.empty() && |
798 | 0 | !((fileSet && fileSet->GetProperty("SKIP_PRECOMPILE_HEADERS")) || |
799 | 0 | source.GetProperty("SKIP_PRECOMPILE_HEADERS"))) { |
800 | 0 | std::string pchOptions; |
801 | 0 | auto const pchIt = pchSources.find(source.GetFullPath()); |
802 | 0 | if (pchIt != pchSources.end()) { |
803 | 0 | pchOptions = this->GeneratorTarget->GetPchCreateCompileOptions( |
804 | 0 | config, lang, pchIt->second); |
805 | 0 | } else { |
806 | 0 | pchOptions = |
807 | 0 | this->GeneratorTarget->GetPchUseCompileOptions(config, lang); |
808 | 0 | } |
809 | |
|
810 | 0 | std::string const& evaluatedFlags = |
811 | 0 | genexInterpreter.Evaluate(pchOptions, COMPILE_OPTIONS); |
812 | |
|
813 | 0 | this->LocalGenerator->AppendCompileOptions(flags, evaluatedFlags); |
814 | 0 | *this->FlagFileStream << "# PCH options: " << relativeObj |
815 | 0 | << "_OPTIONS = " << evaluatedFlags << "\n" |
816 | 0 | << "\n"; |
817 | 0 | } |
818 | | |
819 | | // Add include directories from file set properties. |
820 | 0 | std::vector<std::string> includes; |
821 | |
|
822 | 0 | if (fileSet) { |
823 | 0 | auto fsIncludes = fileSet->BelongsTo(this->GeneratorTarget) |
824 | 0 | ? fileSet->GetIncludeDirectories(config, lang) |
825 | 0 | : fileSet->GetInterfaceIncludeDirectories(config, lang); |
826 | 0 | if (!fsIncludes.empty()) { |
827 | 0 | this->LocalGenerator->AppendIncludeDirectories( |
828 | 0 | includes, cm::remove_BT(fsIncludes), source); |
829 | 0 | *this->FlagFileStream |
830 | 0 | << "# Custom include directories (File Set '" << fileSet->GetName() |
831 | 0 | << "'): " << relativeObj |
832 | 0 | << "_INCLUDE_DIRECTORIES = " << cmList::to_string(fsIncludes) << "\n" |
833 | 0 | << "\n"; |
834 | 0 | } |
835 | 0 | } |
836 | | |
837 | | // Add include directories from source file properties. |
838 | 0 | std::string const INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES"); |
839 | 0 | if (cmValue cincludes = source.GetProperty(INCLUDE_DIRECTORIES)) { |
840 | 0 | std::string const& evaluatedIncludes = |
841 | 0 | genexInterpreter.Evaluate(*cincludes, INCLUDE_DIRECTORIES); |
842 | 0 | this->LocalGenerator->AppendIncludeDirectories(includes, evaluatedIncludes, |
843 | 0 | source); |
844 | 0 | *this->FlagFileStream << "# Custom include directories: " << relativeObj |
845 | 0 | << "_INCLUDE_DIRECTORIES = " << evaluatedIncludes |
846 | 0 | << "\n" |
847 | 0 | << "\n"; |
848 | 0 | } |
849 | | |
850 | | // Add language-specific defines. |
851 | 0 | std::set<std::string> defines; |
852 | | |
853 | | // Add source-specific preprocessor definitions. |
854 | 0 | std::string const COMPILE_DEFINITIONS("COMPILE_DEFINITIONS"); |
855 | 0 | if (cmValue compile_defs = source.GetProperty(COMPILE_DEFINITIONS)) { |
856 | 0 | std::string const& evaluatedDefs = |
857 | 0 | genexInterpreter.Evaluate(*compile_defs, COMPILE_DEFINITIONS); |
858 | 0 | this->LocalGenerator->AppendDefines(defines, evaluatedDefs); |
859 | 0 | *this->FlagFileStream << "# Custom defines: " << relativeObj |
860 | 0 | << "_DEFINES = " << evaluatedDefs << "\n" |
861 | 0 | << "\n"; |
862 | 0 | } |
863 | 0 | std::string const defPropName = |
864 | 0 | cmStrCat("COMPILE_DEFINITIONS_", configUpper); |
865 | 0 | if (cmValue config_compile_defs = source.GetProperty(defPropName)) { |
866 | 0 | std::string const& evaluatedDefs = |
867 | 0 | genexInterpreter.Evaluate(*config_compile_defs, COMPILE_DEFINITIONS); |
868 | 0 | this->LocalGenerator->AppendDefines(defines, evaluatedDefs); |
869 | 0 | *this->FlagFileStream << "# Custom defines: " << relativeObj << "_DEFINES_" |
870 | 0 | << configUpper << " = " << evaluatedDefs << "\n" |
871 | 0 | << "\n"; |
872 | 0 | } |
873 | | |
874 | | // Add file set preprocessor definitions |
875 | 0 | if (fileSet) { |
876 | 0 | auto fsDefines = fileSet->BelongsTo(this->GeneratorTarget) |
877 | 0 | ? fileSet->GetCompileDefinitions(config, lang) |
878 | 0 | : fileSet->GetInterfaceCompileDefinitions(config, lang); |
879 | 0 | if (!fsDefines.empty()) { |
880 | 0 | this->LocalGenerator->AppendDefines(defines, fsDefines); |
881 | 0 | *this->FlagFileStream << "# Custom defines (File Set '" |
882 | 0 | << fileSet->GetName() << "'): " << relativeObj |
883 | 0 | << "_DEFINES = " << cmList::to_string(fsDefines) |
884 | 0 | << "\n" |
885 | 0 | << "\n"; |
886 | 0 | } |
887 | 0 | } |
888 | | |
889 | | // Get the output paths for source and object files. |
890 | 0 | std::string const sourceFile = this->LocalGenerator->ConvertToOutputFormat( |
891 | 0 | source.GetFullPath(), cmOutputConverter::SHELL); |
892 | | |
893 | | // Construct the build message. |
894 | 0 | std::vector<std::string> no_depends; |
895 | 0 | std::vector<std::string> commands; |
896 | | |
897 | | // add in a progress call if needed |
898 | 0 | this->NumberOfProgressActions++; |
899 | |
|
900 | 0 | if (!this->NoRuleMessages) { |
901 | 0 | cmLocalUnixMakefileGenerator3::EchoProgress progress; |
902 | 0 | this->MakeEchoProgress(progress); |
903 | 0 | std::string buildEcho = |
904 | 0 | cmStrCat("Building ", lang, " object ", relativeObj); |
905 | 0 | this->LocalGenerator->AppendEcho(commands, buildEcho, |
906 | 0 | cmLocalUnixMakefileGenerator3::EchoBuild, |
907 | 0 | &progress); |
908 | 0 | } |
909 | |
|
910 | 0 | std::string targetOutPathReal; |
911 | 0 | std::string targetOutPathPDB; |
912 | 0 | std::string targetOutPathCompilePDB; |
913 | 0 | { |
914 | 0 | std::string targetFullPathReal; |
915 | 0 | std::string targetFullPathPDB; |
916 | 0 | std::string targetFullPathCompilePDB = |
917 | 0 | this->ComputeTargetCompilePDB(this->GetConfigName()); |
918 | 0 | if (this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE || |
919 | 0 | this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY || |
920 | 0 | this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY || |
921 | 0 | this->GeneratorTarget->GetType() == cmStateEnums::MODULE_LIBRARY) { |
922 | 0 | targetFullPathReal = this->GeneratorTarget->GetFullPath( |
923 | 0 | this->GetConfigName(), cmStateEnums::RuntimeBinaryArtifact, true); |
924 | 0 | targetFullPathPDB = cmStrCat( |
925 | 0 | this->GeneratorTarget->GetPDBDirectory(this->GetConfigName()), '/', |
926 | 0 | this->GeneratorTarget->GetPDBName(this->GetConfigName())); |
927 | 0 | } |
928 | |
|
929 | 0 | targetOutPathReal = this->LocalGenerator->ConvertToOutputFormat( |
930 | 0 | this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPathReal), |
931 | 0 | cmOutputConverter::SHELL); |
932 | 0 | targetOutPathPDB = this->LocalGenerator->ConvertToOutputFormat( |
933 | 0 | targetFullPathPDB, cmOutputConverter::SHELL); |
934 | 0 | targetOutPathCompilePDB = this->LocalGenerator->ConvertToOutputFormat( |
935 | 0 | this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPathCompilePDB), |
936 | 0 | cmOutputConverter::SHELL); |
937 | |
|
938 | 0 | if (this->LocalGenerator->IsMinGWMake() && |
939 | 0 | cmHasSuffix(targetOutPathCompilePDB, '\\')) { |
940 | | // mingw32-make incorrectly interprets 'a\ b c' as 'a b' and 'c' |
941 | | // (but 'a\ b "c"' as 'a\', 'b', and 'c'!). Workaround this by |
942 | | // avoiding a trailing backslash in the argument. |
943 | 0 | targetOutPathCompilePDB.back() = '/'; |
944 | 0 | } |
945 | |
|
946 | 0 | std::string const compilePdbOutputPath = |
947 | 0 | this->GeneratorTarget->GetCompilePDBDirectory(this->GetConfigName()); |
948 | 0 | cmSystemTools::MakeDirectory(compilePdbOutputPath); |
949 | 0 | } |
950 | 0 | cmRulePlaceholderExpander::RuleVariables vars; |
951 | 0 | vars.CMTargetName = this->GeneratorTarget->GetName().c_str(); |
952 | 0 | vars.CMTargetType = |
953 | 0 | cmState::GetTargetTypeName(this->GeneratorTarget->GetType()).c_str(); |
954 | 0 | vars.Language = lang.c_str(); |
955 | 0 | vars.Target = targetOutPathReal.c_str(); |
956 | 0 | vars.TargetPDB = targetOutPathPDB.c_str(); |
957 | 0 | vars.TargetCompilePDB = targetOutPathCompilePDB.c_str(); |
958 | 0 | vars.Source = sourceFile.c_str(); |
959 | 0 | std::string const shellObj = |
960 | 0 | this->LocalGenerator->ConvertToOutputFormat(obj, cmOutputConverter::SHELL); |
961 | 0 | vars.Object = shellObj.c_str(); |
962 | 0 | std::string objectDir = this->GeneratorTarget->GetSupportDirectory(); |
963 | 0 | objectDir = this->LocalGenerator->ConvertToOutputFormat( |
964 | 0 | this->LocalGenerator->MaybeRelativeToCurBinDir(objectDir), |
965 | 0 | cmOutputConverter::SHELL); |
966 | 0 | vars.ObjectDir = objectDir.c_str(); |
967 | 0 | std::string targetSupportDir = |
968 | 0 | this->GeneratorTarget->GetCMFSupportDirectory(); |
969 | 0 | targetSupportDir = this->LocalGenerator->ConvertToOutputFormat( |
970 | 0 | this->LocalGenerator->MaybeRelativeToTopBinDir(targetSupportDir), |
971 | 0 | cmOutputConverter::SHELL); |
972 | 0 | vars.TargetSupportDir = targetSupportDir.c_str(); |
973 | 0 | std::string objectFileDir = cmSystemTools::GetFilenamePath(obj); |
974 | 0 | objectFileDir = this->LocalGenerator->ConvertToOutputFormat( |
975 | 0 | this->LocalGenerator->MaybeRelativeToCurBinDir(objectFileDir), |
976 | 0 | cmOutputConverter::SHELL); |
977 | 0 | vars.ObjectFileDir = objectFileDir.c_str(); |
978 | 0 | vars.Flags = flags.c_str(); |
979 | 0 | vars.ISPCHeader = ispcHeaderForShell.c_str(); |
980 | 0 | vars.Config = this->GetConfigName().c_str(); |
981 | 0 | vars.RustEmit = source.GetRustEmitProperty()->c_str(); |
982 | |
|
983 | 0 | std::string definesString = cmStrCat("$(", lang, "_DEFINES)"); |
984 | |
|
985 | 0 | this->LocalGenerator->JoinDefines(defines, definesString, lang); |
986 | |
|
987 | 0 | vars.Defines = definesString.c_str(); |
988 | |
|
989 | 0 | std::string includesString = this->LocalGenerator->GetIncludeFlags( |
990 | 0 | includes, this->GeneratorTarget, lang, config); |
991 | 0 | this->LocalGenerator->AppendFlags(includesString, |
992 | 0 | "$(" + lang + "_INCLUDES)"); |
993 | 0 | vars.Includes = includesString.c_str(); |
994 | |
|
995 | 0 | std::string dependencyTarget; |
996 | 0 | std::string shellDependencyFile; |
997 | 0 | std::string dependencyTimestamp; |
998 | 0 | if (compilerGenerateDeps) { |
999 | 0 | dependencyTarget = this->LocalGenerator->EscapeForShell( |
1000 | 0 | this->LocalGenerator->ConvertToMakefilePath( |
1001 | 0 | this->LocalGenerator->MaybeRelativeToTopBinDir(relativeObj))); |
1002 | 0 | vars.DependencyTarget = dependencyTarget.c_str(); |
1003 | |
|
1004 | 0 | auto depFile = cmStrCat(obj, ".d"); |
1005 | 0 | shellDependencyFile = this->LocalGenerator->ConvertToOutputFormat( |
1006 | 0 | depFile, cmOutputConverter::SHELL); |
1007 | 0 | vars.DependencyFile = shellDependencyFile.c_str(); |
1008 | 0 | this->CleanFiles.insert(depFile); |
1009 | |
|
1010 | 0 | dependencyTimestamp = this->LocalGenerator->MaybeRelativeToTopBinDir( |
1011 | 0 | cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.ts")); |
1012 | 0 | } |
1013 | | |
1014 | | // At the moment, it is assumed that C, C++, Fortran, and CUDA have both |
1015 | | // assembly and preprocessor capabilities. The same is true for the |
1016 | | // ability to export compile commands |
1017 | 0 | bool const lang_has_preprocessor = |
1018 | 0 | ((lang == "C") || (lang == "CXX") || (lang == "OBJC") || |
1019 | 0 | (lang == "OBJCXX") || (lang == "Fortran") || (lang == "CUDA") || |
1020 | 0 | lang == "ISPC" || lang == "HIP" || lang == "ASM"); |
1021 | 0 | bool const lang_has_assembly = lang_has_preprocessor; |
1022 | 0 | bool const lang_can_export_cmds = lang_has_preprocessor; |
1023 | |
|
1024 | 0 | auto rulePlaceholderExpander = |
1025 | 0 | this->LocalGenerator->CreateRulePlaceholderExpander(); |
1026 | | |
1027 | | // Construct the compile rules. |
1028 | 0 | { |
1029 | 0 | std::string cudaCompileMode; |
1030 | 0 | if (lang == "CUDA") { |
1031 | 0 | if (this->GeneratorTarget->GetPropertyAsBool( |
1032 | 0 | "CUDA_SEPARABLE_COMPILATION")) { |
1033 | 0 | std::string const& rdcFlag = |
1034 | 0 | this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_RDC_FLAG"); |
1035 | 0 | cudaCompileMode = cmStrCat(cudaCompileMode, rdcFlag, ' '); |
1036 | 0 | } |
1037 | |
|
1038 | 0 | static std::array<cm::string_view, 4> const compileModes{ |
1039 | 0 | { "PTX"_s, "CUBIN"_s, "FATBIN"_s, "OPTIX"_s } |
1040 | 0 | }; |
1041 | 0 | bool useNormalCompileMode = true; |
1042 | 0 | for (cm::string_view mode : compileModes) { |
1043 | 0 | auto propName = cmStrCat("CUDA_", mode, "_COMPILATION"); |
1044 | 0 | auto defName = cmStrCat("_CMAKE_CUDA_", mode, "_FLAG"); |
1045 | 0 | if (this->GeneratorTarget->GetPropertyAsBool(propName)) { |
1046 | 0 | std::string const& flag = |
1047 | 0 | this->Makefile->GetRequiredDefinition(defName); |
1048 | 0 | cudaCompileMode = cmStrCat(cudaCompileMode, flag); |
1049 | 0 | useNormalCompileMode = false; |
1050 | 0 | break; |
1051 | 0 | } |
1052 | 0 | } |
1053 | 0 | if (useNormalCompileMode) { |
1054 | 0 | std::string const& wholeFlag = |
1055 | 0 | this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_WHOLE_FLAG"); |
1056 | 0 | cudaCompileMode = cmStrCat(cudaCompileMode, wholeFlag); |
1057 | 0 | } |
1058 | 0 | vars.CudaCompileMode = cudaCompileMode.c_str(); |
1059 | 0 | } |
1060 | |
|
1061 | 0 | cmList compileCommands; |
1062 | 0 | std::string const& compileRule = this->Makefile->GetRequiredDefinition( |
1063 | 0 | "CMAKE_" + lang + "_COMPILE_OBJECT"); |
1064 | 0 | compileCommands.assign(compileRule); |
1065 | |
|
1066 | 0 | if (this->GeneratorTarget->GetPropertyAsBool("EXPORT_COMPILE_COMMANDS") && |
1067 | 0 | lang_can_export_cmds && compileCommands.size() == 1) { |
1068 | 0 | std::string compileCommand = compileCommands[0]; |
1069 | | |
1070 | | // no launcher for CMAKE_EXPORT_COMPILE_COMMANDS |
1071 | 0 | rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator, |
1072 | 0 | compileCommand, vars); |
1073 | 0 | std::string const workingDirectory = |
1074 | 0 | this->LocalGenerator->GetCurrentBinaryDirectory(); |
1075 | 0 | std::string::size_type lfPos = compileCommand.find(langFlags); |
1076 | 0 | if (lfPos != std::string::npos) { |
1077 | 0 | compileCommand.replace(lfPos, langFlags.size(), |
1078 | 0 | this->GetFlags(lang, this->GetConfigName())); |
1079 | 0 | } |
1080 | 0 | std::string const langDefines = std::string("$(") + lang + "_DEFINES)"; |
1081 | 0 | std::string::size_type const ldPos = compileCommand.find(langDefines); |
1082 | 0 | if (ldPos != std::string::npos) { |
1083 | 0 | compileCommand.replace(ldPos, langDefines.size(), |
1084 | 0 | this->GetDefines(lang, this->GetConfigName())); |
1085 | 0 | } |
1086 | 0 | std::string const langIncludes = std::string("$(") + lang + "_INCLUDES)"; |
1087 | 0 | std::string::size_type const liPos = compileCommand.find(langIncludes); |
1088 | 0 | if (liPos != std::string::npos) { |
1089 | 0 | compileCommand.replace(liPos, langIncludes.size(), |
1090 | 0 | this->GetIncludes(lang, this->GetConfigName())); |
1091 | 0 | } |
1092 | |
|
1093 | 0 | cmValue const eliminate[] = { |
1094 | 0 | this->Makefile->GetDefinition("CMAKE_START_TEMP_FILE"), |
1095 | 0 | this->Makefile->GetDefinition("CMAKE_END_TEMP_FILE") |
1096 | 0 | }; |
1097 | 0 | for (cmValue const& el : eliminate) { |
1098 | 0 | if (el) { |
1099 | 0 | cmSystemTools::ReplaceString(compileCommand, *el, ""); |
1100 | 0 | } |
1101 | 0 | } |
1102 | |
|
1103 | 0 | this->GlobalGenerator->AddCXXCompileCommand( |
1104 | 0 | source.GetFullPath(), workingDirectory, compileCommand, relativeObj); |
1105 | 0 | } |
1106 | | |
1107 | | // See if we need to use a compiler launcher like ccache or distcc |
1108 | 0 | std::string compilerLauncher; |
1109 | 0 | if (!compileCommands.empty()) { |
1110 | 0 | compilerLauncher = GetCompilerLauncher(lang, config); |
1111 | 0 | } |
1112 | |
|
1113 | 0 | cmValue const fsSkipCodeCheckVal = |
1114 | 0 | fileSet ? fileSet->GetProperty("SKIP_LINTING") : nullptr; |
1115 | 0 | cmValue const srcSkipCodeCheckVal = source.GetProperty("SKIP_LINTING"); |
1116 | 0 | bool const skipCodeCheck = fsSkipCodeCheckVal.IsSet() |
1117 | 0 | ? fsSkipCodeCheckVal.IsOn() |
1118 | 0 | : (srcSkipCodeCheckVal.IsSet() |
1119 | 0 | ? srcSkipCodeCheckVal.IsOn() |
1120 | 0 | : this->GetGeneratorTarget()->GetPropertyAsBool("SKIP_LINTING")); |
1121 | |
|
1122 | 0 | if (!skipCodeCheck) { |
1123 | 0 | std::string const codeCheck = this->GenerateCodeCheckRules( |
1124 | 0 | source, compilerLauncher, "$(CMAKE_COMMAND)", config, nullptr); |
1125 | 0 | if (!codeCheck.empty()) { |
1126 | 0 | compileCommands.front().insert(0, codeCheck); |
1127 | 0 | } |
1128 | 0 | } |
1129 | | |
1130 | | // If compiler launcher was specified and not consumed above, it |
1131 | | // goes to the beginning of the command line. |
1132 | 0 | if (!compileCommands.empty() && !compilerLauncher.empty()) { |
1133 | 0 | cmList args{ compilerLauncher, cmList::EmptyElements::Yes }; |
1134 | 0 | if (!args.empty()) { |
1135 | 0 | args[0] = this->LocalGenerator->ConvertToOutputFormat( |
1136 | 0 | args[0], cmOutputConverter::SHELL); |
1137 | 0 | for (std::string& i : cmMakeRange(args.begin() + 1, args.end())) { |
1138 | 0 | i = this->LocalGenerator->EscapeForShell(i); |
1139 | 0 | } |
1140 | 0 | } |
1141 | 0 | compileCommands.front().insert(0, args.join(" ") + " "); |
1142 | 0 | } |
1143 | |
|
1144 | 0 | std::string launcher; |
1145 | 0 | { |
1146 | 0 | std::string val = this->LocalGenerator->GetRuleLauncher( |
1147 | 0 | this->GeneratorTarget, "RULE_LAUNCH_COMPILE", |
1148 | 0 | this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE")); |
1149 | 0 | if (cmNonempty(val)) { |
1150 | 0 | launcher = cmStrCat(val, ' '); |
1151 | 0 | } |
1152 | 0 | } |
1153 | |
|
1154 | 0 | std::string flagsWithDeps(flags); |
1155 | |
|
1156 | 0 | if (compilerGenerateDeps) { |
1157 | | // Injects dependency computation |
1158 | 0 | auto depFlags = this->Makefile->GetSafeDefinition( |
1159 | 0 | cmStrCat("CMAKE_DEPFILE_FLAGS_", lang)); |
1160 | |
|
1161 | 0 | if (!depFlags.empty()) { |
1162 | | // Add dependency flags |
1163 | 0 | rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator, |
1164 | 0 | depFlags, vars); |
1165 | 0 | flagsWithDeps.append(1, ' '); |
1166 | 0 | flagsWithDeps.append(depFlags); |
1167 | 0 | } |
1168 | 0 | vars.Flags = flagsWithDeps.c_str(); |
1169 | |
|
1170 | 0 | auto const& extraCommands = this->Makefile->GetSafeDefinition( |
1171 | 0 | cmStrCat("CMAKE_", lang, "_DEPENDS_EXTRA_COMMANDS")); |
1172 | 0 | if (!extraCommands.empty()) { |
1173 | 0 | compileCommands.append(extraCommands); |
1174 | 0 | } |
1175 | |
|
1176 | 0 | auto const& depFormat = this->Makefile->GetRequiredDefinition( |
1177 | 0 | cmStrCat("CMAKE_", lang, "_DEPFILE_FORMAT")); |
1178 | |
|
1179 | 0 | if (depFormat == "msvc"_s) { |
1180 | | // compiler must be launched through a wrapper to pick-up dependencies |
1181 | 0 | std::string depFilter = |
1182 | 0 | "$(CMAKE_COMMAND) -E cmake_cl_compile_depends "; |
1183 | 0 | depFilter += cmStrCat("--dep-file=", shellDependencyFile); |
1184 | 0 | depFilter += |
1185 | 0 | cmStrCat(" --working-dir=", |
1186 | 0 | this->LocalGenerator->ConvertToOutputFormat( |
1187 | 0 | this->LocalGenerator->GetCurrentBinaryDirectory(), |
1188 | 0 | cmOutputConverter::SHELL)); |
1189 | 0 | auto const& prefix = this->Makefile->GetSafeDefinition( |
1190 | 0 | cmStrCat("CMAKE_", lang, "_CL_SHOWINCLUDES_PREFIX")); |
1191 | 0 | depFilter += cmStrCat(" --filter-prefix=", |
1192 | 0 | this->LocalGenerator->ConvertToOutputFormat( |
1193 | 0 | prefix, cmOutputConverter::SHELL)); |
1194 | 0 | depFilter += " -- "; |
1195 | 0 | compileCommands.front().insert(0, depFilter); |
1196 | 0 | } |
1197 | 0 | } |
1198 | | |
1199 | | // Expand placeholders in the commands. |
1200 | 0 | for (std::string& compileCommand : compileCommands) { |
1201 | 0 | compileCommand = cmStrCat(launcher, compileCommand); |
1202 | 0 | rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator, |
1203 | 0 | compileCommand, vars); |
1204 | 0 | } |
1205 | | |
1206 | | // Change the command working directory to the local build tree. |
1207 | 0 | this->LocalGenerator->CreateCDCommand( |
1208 | 0 | compileCommands, this->LocalGenerator->GetCurrentBinaryDirectory(), |
1209 | 0 | this->LocalGenerator->GetBinaryDirectory()); |
1210 | 0 | cm::append(commands, compileCommands); |
1211 | 0 | } |
1212 | | |
1213 | | // Check for extra outputs created by the compilation. |
1214 | 0 | cmList outputs; |
1215 | 0 | outputs.emplace_back(relativeObj); |
1216 | 0 | if (cmValue extra_outputs_str = source.GetProperty("OBJECT_OUTPUTS")) { |
1217 | 0 | std::string evaluated_outputs = cmGeneratorExpression::Evaluate( |
1218 | 0 | *extra_outputs_str, this->LocalGenerator, config); |
1219 | |
|
1220 | 0 | if (!evaluated_outputs.empty()) { |
1221 | | // Register these as extra files to clean. |
1222 | 0 | outputs.append(evaluated_outputs); |
1223 | 0 | } |
1224 | 0 | } |
1225 | 0 | if (!ispcHeaderRelative.empty()) { |
1226 | | // can't move ispcHeader as vars is using it |
1227 | 0 | outputs.emplace_back(ispcHeaderRelative); |
1228 | 0 | } |
1229 | |
|
1230 | 0 | if (outputs.size() > 1) { |
1231 | 0 | this->CleanFiles.insert(outputs.begin() + 1, outputs.end()); |
1232 | 0 | } |
1233 | |
|
1234 | 0 | if (compilerGenerateDeps) { |
1235 | 0 | depends.push_back(dependencyTimestamp); |
1236 | 0 | } |
1237 | | |
1238 | | // Write the rule. |
1239 | 0 | this->WriteMakeRule(*this->BuildFileStream, nullptr, outputs, depends, |
1240 | 0 | commands); |
1241 | |
|
1242 | 0 | if (compilerGenerateDeps) { |
1243 | | // set back flags without dependency generation |
1244 | 0 | vars.Flags = flags.c_str(); |
1245 | 0 | } |
1246 | |
|
1247 | 0 | bool do_preprocess_rules = lang_has_preprocessor && |
1248 | 0 | this->LocalGenerator->GetCreatePreprocessedSourceRules(); |
1249 | 0 | bool do_assembly_rules = |
1250 | 0 | lang_has_assembly && this->LocalGenerator->GetCreateAssemblySourceRules(); |
1251 | 0 | if (do_preprocess_rules || do_assembly_rules) { |
1252 | 0 | std::vector<std::string> force_depends; |
1253 | 0 | force_depends.emplace_back("cmake_force"); |
1254 | 0 | std::string::size_type dot_pos = relativeObj.rfind('.'); |
1255 | 0 | std::string relativeObjBase = relativeObj.substr(0, dot_pos); |
1256 | 0 | dot_pos = obj.rfind('.'); |
1257 | 0 | std::string objBase = obj.substr(0, dot_pos); |
1258 | |
|
1259 | 0 | if (do_preprocess_rules) { |
1260 | 0 | commands.clear(); |
1261 | 0 | std::string const relativeObjI = relativeObjBase + ".i"; |
1262 | 0 | std::string const objI = objBase + ".i"; |
1263 | |
|
1264 | 0 | std::string preprocessEcho = |
1265 | 0 | cmStrCat("Preprocessing ", lang, " source to ", objI); |
1266 | 0 | this->LocalGenerator->AppendEcho( |
1267 | 0 | commands, preprocessEcho, cmLocalUnixMakefileGenerator3::EchoBuild); |
1268 | |
|
1269 | 0 | std::string preprocessRuleVar = |
1270 | 0 | cmStrCat("CMAKE_", lang, "_CREATE_PREPROCESSED_SOURCE"); |
1271 | 0 | if (cmValue preprocessRule = |
1272 | 0 | this->Makefile->GetDefinition(preprocessRuleVar)) { |
1273 | 0 | cmList preprocessCommands{ *preprocessRule }; |
1274 | |
|
1275 | 0 | std::string shellObjI = this->LocalGenerator->ConvertToOutputFormat( |
1276 | 0 | objI, cmOutputConverter::SHELL); |
1277 | 0 | vars.PreprocessedSource = shellObjI.c_str(); |
1278 | | |
1279 | | // Expand placeholders in the commands. |
1280 | 0 | for (std::string& preprocessCommand : preprocessCommands) { |
1281 | | // no launcher for preprocessor commands |
1282 | 0 | rulePlaceholderExpander->ExpandRuleVariables( |
1283 | 0 | this->LocalGenerator, preprocessCommand, vars); |
1284 | 0 | } |
1285 | |
|
1286 | 0 | this->LocalGenerator->CreateCDCommand( |
1287 | 0 | preprocessCommands, |
1288 | 0 | this->LocalGenerator->GetCurrentBinaryDirectory(), |
1289 | 0 | this->LocalGenerator->GetBinaryDirectory()); |
1290 | 0 | cm::append(commands, preprocessCommands); |
1291 | 0 | } else { |
1292 | 0 | std::string cmd = |
1293 | 0 | cmStrCat("$(CMAKE_COMMAND) -E cmake_unimplemented_variable ", |
1294 | 0 | preprocessRuleVar); |
1295 | 0 | commands.push_back(std::move(cmd)); |
1296 | 0 | } |
1297 | |
|
1298 | 0 | this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr, |
1299 | 0 | relativeObjI, force_depends, |
1300 | 0 | commands, false); |
1301 | 0 | } |
1302 | |
|
1303 | 0 | if (do_assembly_rules) { |
1304 | 0 | commands.clear(); |
1305 | 0 | std::string relativeObjS = relativeObjBase + ".s"; |
1306 | 0 | std::string objS = objBase + ".s"; |
1307 | |
|
1308 | 0 | std::string assemblyEcho = |
1309 | 0 | cmStrCat("Compiling ", lang, " source to assembly ", objS); |
1310 | 0 | this->LocalGenerator->AppendEcho( |
1311 | 0 | commands, assemblyEcho, cmLocalUnixMakefileGenerator3::EchoBuild); |
1312 | |
|
1313 | 0 | std::string assemblyRuleVar = |
1314 | 0 | cmStrCat("CMAKE_", lang, "_CREATE_ASSEMBLY_SOURCE"); |
1315 | 0 | if (cmValue assemblyRule = |
1316 | 0 | this->Makefile->GetDefinition(assemblyRuleVar)) { |
1317 | 0 | cmList assemblyCommands{ *assemblyRule }; |
1318 | |
|
1319 | 0 | std::string shellObjS = this->LocalGenerator->ConvertToOutputFormat( |
1320 | 0 | objS, cmOutputConverter::SHELL); |
1321 | 0 | vars.AssemblySource = shellObjS.c_str(); |
1322 | | |
1323 | | // Expand placeholders in the commands. |
1324 | 0 | for (std::string& assemblyCommand : assemblyCommands) { |
1325 | | // no launcher for assembly commands |
1326 | 0 | rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator, |
1327 | 0 | assemblyCommand, vars); |
1328 | 0 | } |
1329 | |
|
1330 | 0 | this->LocalGenerator->CreateCDCommand( |
1331 | 0 | assemblyCommands, this->LocalGenerator->GetCurrentBinaryDirectory(), |
1332 | 0 | this->LocalGenerator->GetBinaryDirectory()); |
1333 | 0 | cm::append(commands, assemblyCommands); |
1334 | 0 | } else { |
1335 | 0 | std::string cmd = |
1336 | 0 | cmStrCat("$(CMAKE_COMMAND) -E cmake_unimplemented_variable ", |
1337 | 0 | assemblyRuleVar); |
1338 | 0 | commands.push_back(std::move(cmd)); |
1339 | 0 | } |
1340 | |
|
1341 | 0 | this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr, |
1342 | 0 | relativeObjS, force_depends, |
1343 | 0 | commands, false); |
1344 | 0 | } |
1345 | 0 | } |
1346 | 0 | } |
1347 | | |
1348 | | void cmMakefileTargetGenerator::WriteTargetCleanRules() |
1349 | 0 | { |
1350 | 0 | std::vector<std::string> depends; |
1351 | 0 | std::vector<std::string> commands; |
1352 | | |
1353 | | // Construct the clean target name. |
1354 | 0 | std::string const cleanTarget = cmStrCat( |
1355 | 0 | this->LocalGenerator->GetRelativeTargetDirectory(this->GeneratorTarget), |
1356 | 0 | "/clean"); |
1357 | | |
1358 | | // Construct the clean command. |
1359 | 0 | this->LocalGenerator->AppendCleanCommand(commands, this->CleanFiles, |
1360 | 0 | this->GeneratorTarget); |
1361 | 0 | this->LocalGenerator->CreateCDCommand( |
1362 | 0 | commands, this->LocalGenerator->GetCurrentBinaryDirectory(), |
1363 | 0 | this->LocalGenerator->GetBinaryDirectory()); |
1364 | | |
1365 | | // Write the rule. |
1366 | 0 | this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr, |
1367 | 0 | cleanTarget, depends, commands, true); |
1368 | 0 | } |
1369 | | |
1370 | | bool cmMakefileTargetGenerator::WriteMakeRule( |
1371 | | std::ostream& os, char const* comment, |
1372 | | std::vector<std::string> const& outputs, |
1373 | | std::vector<std::string> const& depends, |
1374 | | std::vector<std::string> const& commands, bool in_help) |
1375 | 0 | { |
1376 | 0 | bool symbolic = false; |
1377 | 0 | if (outputs.empty()) { |
1378 | 0 | return symbolic; |
1379 | 0 | } |
1380 | | |
1381 | | // Check whether we need to bother checking for a symbolic output. |
1382 | 0 | bool const need_symbolic = this->GlobalGenerator->GetNeedSymbolicMark(); |
1383 | | |
1384 | | // Check whether the first output is marked as symbolic. |
1385 | 0 | if (need_symbolic) { |
1386 | 0 | if (cmSourceFile* sf = this->Makefile->GetSource(outputs[0])) { |
1387 | 0 | symbolic = sf->GetPropertyAsBool("SYMBOLIC"); |
1388 | 0 | } |
1389 | 0 | } |
1390 | | |
1391 | | // We always attach the actual commands to the first output. |
1392 | 0 | this->LocalGenerator->WriteMakeRule(os, comment, outputs[0], depends, |
1393 | 0 | commands, symbolic, in_help); |
1394 | | |
1395 | | // For single outputs, we are done. |
1396 | 0 | if (outputs.size() == 1) { |
1397 | 0 | return symbolic; |
1398 | 0 | } |
1399 | | |
1400 | | // For multiple outputs, make the extra ones depend on the first one. |
1401 | 0 | std::vector<std::string> const output_depends(1, outputs[0]); |
1402 | 0 | for (std::string const& output : cmMakeRange(outputs).advance(1)) { |
1403 | | // Touch the extra output so "make" knows that it was updated, |
1404 | | // but only if the output was actually created. |
1405 | 0 | std::string const out = this->LocalGenerator->ConvertToOutputFormat( |
1406 | 0 | this->LocalGenerator->MaybeRelativeToTopBinDir(output), |
1407 | 0 | cmOutputConverter::SHELL); |
1408 | 0 | std::vector<std::string> output_commands; |
1409 | |
|
1410 | 0 | bool o_symbolic = false; |
1411 | 0 | if (need_symbolic) { |
1412 | 0 | if (cmSourceFile const* sf = this->Makefile->GetSource(output)) { |
1413 | 0 | o_symbolic = sf->GetPropertyAsBool("SYMBOLIC"); |
1414 | 0 | } |
1415 | 0 | } |
1416 | 0 | symbolic = symbolic && o_symbolic; |
1417 | |
|
1418 | 0 | if (!o_symbolic) { |
1419 | 0 | output_commands.push_back("@$(CMAKE_COMMAND) -E touch_nocreate " + out); |
1420 | 0 | } |
1421 | 0 | this->LocalGenerator->WriteMakeRule(os, nullptr, output, output_depends, |
1422 | 0 | output_commands, o_symbolic, in_help); |
1423 | |
|
1424 | 0 | if (!o_symbolic) { |
1425 | | // At build time, remove the first output if this one does not exist |
1426 | | // so that "make" will rerun the real commands that create this one. |
1427 | 0 | MultipleOutputPairsType::value_type p(output, outputs[0]); |
1428 | 0 | this->MultipleOutputPairs.insert(p); |
1429 | 0 | } |
1430 | 0 | } |
1431 | 0 | return symbolic; |
1432 | 0 | } |
1433 | | |
1434 | | void cmMakefileTargetGenerator::WriteTargetLinkDependRules() |
1435 | 0 | { |
1436 | 0 | if (!this->GeneratorTarget->HasLinkDependencyFile(this->GetConfigName())) { |
1437 | 0 | return; |
1438 | 0 | } |
1439 | | |
1440 | 0 | auto depFile = this->LocalGenerator->GetLinkDependencyFile( |
1441 | 0 | this->GeneratorTarget, this->GetConfigName()); |
1442 | 0 | this->CleanFiles.insert(depFile); |
1443 | 0 | this->LocalGenerator->AddImplicitDepends( |
1444 | 0 | this->GeneratorTarget, "LINK", |
1445 | 0 | this->GeneratorTarget->GetFullPath(this->GetConfigName()), depFile, |
1446 | 0 | cmDependencyScannerKind::Compiler); |
1447 | 0 | } |
1448 | | std::string cmMakefileTargetGenerator::GetClangTidyReplacementsFilePath( |
1449 | | std::string const& directory, cmSourceFile const& source, |
1450 | | std::string const& config) const |
1451 | 0 | { |
1452 | 0 | (void)config; |
1453 | 0 | auto const& objectName = this->GeneratorTarget->GetObjectName(&source); |
1454 | | // NOTE: This may be better to use `this->TargetBuildDirectory` instead of |
1455 | | // `MaybeRelativeToTopBinDir(this->TargetBuildDirectoryFull)` here. The main |
1456 | | // difference is that the current behavior looks odd to relative |
1457 | | // `<LANG>_CLANG_TIDY_EXPORT_FIXES_DIR` settings. Each subdirectory has its |
1458 | | // own export fixes directory *and* adds its relative-from-root path |
1459 | | // underneath it. However, when using an absolute export fixes directory, the |
1460 | | // source directory structure is preserved. The main benefit of the former is |
1461 | | // shorter paths everywhere versus the status quo of the existing code. |
1462 | 0 | cmLocalGenerator* lg = this->GeneratorTarget->GetLocalGenerator(); |
1463 | 0 | auto fixesFile = cmSystemTools::CollapseFullPath( |
1464 | 0 | cmStrCat(directory, '/', |
1465 | 0 | lg->CreateSafeObjectFileName( |
1466 | 0 | lg->MaybeRelativeToTopBinDir(this->TargetBuildDirectoryFull)), |
1467 | 0 | '/', objectName, ".yaml")); |
1468 | 0 | return fixesFile; |
1469 | 0 | } |
1470 | | |
1471 | | void cmMakefileTargetGenerator::WriteTargetDependRules() |
1472 | 0 | { |
1473 | | // must write the targets depend info file |
1474 | 0 | this->InfoFileNameFull = |
1475 | 0 | cmStrCat(this->TargetBuildDirectoryFull, "/DependInfo.cmake"); |
1476 | 0 | this->InfoFileStream = |
1477 | 0 | cm::make_unique<cmGeneratedFileStream>(this->InfoFileNameFull); |
1478 | 0 | if (!this->InfoFileStream) { |
1479 | 0 | return; |
1480 | 0 | } |
1481 | 0 | this->InfoFileStream->SetCopyIfDifferent(true); |
1482 | 0 | this->LocalGenerator->WriteDependLanguageInfo(*this->InfoFileStream, |
1483 | 0 | this->GeneratorTarget); |
1484 | | |
1485 | | // Store multiple output pairs in the depend info file. |
1486 | 0 | if (!this->MultipleOutputPairs.empty()) { |
1487 | | /* clang-format off */ |
1488 | 0 | *this->InfoFileStream |
1489 | 0 | << "\n" |
1490 | 0 | << "# Pairs of files generated by the same build rule.\n" |
1491 | 0 | << "set(CMAKE_MULTIPLE_OUTPUT_PAIRS\n"; |
1492 | | /* clang-format on */ |
1493 | 0 | for (auto const& pi : this->MultipleOutputPairs) { |
1494 | 0 | *this->InfoFileStream << " " << cmScriptGenerator::Quote(pi.first) |
1495 | 0 | << ' ' << cmScriptGenerator::Quote(pi.second) |
1496 | 0 | << '\n'; |
1497 | 0 | } |
1498 | 0 | *this->InfoFileStream << " )\n\n"; |
1499 | 0 | } |
1500 | | |
1501 | | // Store list of targets linked directly or transitively. |
1502 | 0 | { |
1503 | | /* clang-format off */ |
1504 | 0 | *this->InfoFileStream |
1505 | 0 | << "\n" |
1506 | 0 | "# Targets to which this target links which contain Fortran sources.\n" |
1507 | 0 | "set(CMAKE_Fortran_TARGET_LINKED_INFO_FILES\n"; |
1508 | | /* clang-format on */ |
1509 | 0 | auto const dirs = |
1510 | 0 | this->GetLinkedTargetDirectories("Fortran", this->GetConfigName()); |
1511 | 0 | for (std::string const& d : dirs.Direct) { |
1512 | 0 | *this->InfoFileStream << " \"" << d << "/DependInfo.cmake\"\n"; |
1513 | 0 | } |
1514 | 0 | *this->InfoFileStream << " )\n"; |
1515 | | |
1516 | | /* clang-format off */ |
1517 | 0 | *this->InfoFileStream |
1518 | 0 | << "\n" |
1519 | 0 | "# Targets to which this target links which contain Fortran sources.\n" |
1520 | 0 | "set(CMAKE_Fortran_TARGET_FORWARD_LINKED_INFO_FILES\n"; |
1521 | | /* clang-format on */ |
1522 | 0 | for (std::string const& d : dirs.Forward) { |
1523 | 0 | *this->InfoFileStream << " \"" << d << "/DependInfo.cmake\"\n"; |
1524 | 0 | } |
1525 | 0 | *this->InfoFileStream << " )\n"; |
1526 | 0 | } |
1527 | |
|
1528 | 0 | std::string const& working_dir = |
1529 | 0 | this->LocalGenerator->GetCurrentBinaryDirectory(); |
1530 | | |
1531 | | /* clang-format off */ |
1532 | 0 | *this->InfoFileStream |
1533 | 0 | << "\n" |
1534 | 0 | << "# Fortran module output directory.\n" |
1535 | 0 | << "set(CMAKE_Fortran_TARGET_MODULE_DIR \"" |
1536 | 0 | << this->GeneratorTarget->GetFortranModuleDirectory(working_dir) |
1537 | 0 | << "\")\n"; |
1538 | |
|
1539 | 0 | if (this->GeneratorTarget->IsFortranBuildingIntrinsicModules()) { |
1540 | 0 | *this->InfoFileStream |
1541 | 0 | << "\n" |
1542 | 0 | << "# Fortran compiler is building intrinsic modules.\n" |
1543 | 0 | << "set(CMAKE_Fortran_TARGET_BUILDING_INTRINSIC_MODULES ON) \n"; |
1544 | 0 | } |
1545 | | /* clang-format on */ |
1546 | | |
1547 | | // and now write the rule to use it |
1548 | 0 | std::vector<std::string> depends; |
1549 | 0 | std::vector<std::string> commands; |
1550 | | |
1551 | | // Construct the name of the dependency generation target. |
1552 | 0 | std::string const depTarget = cmStrCat( |
1553 | 0 | this->LocalGenerator->GetRelativeTargetDirectory(this->GeneratorTarget), |
1554 | 0 | "/depend"); |
1555 | | |
1556 | | // Add a command to call CMake to scan dependencies. CMake will |
1557 | | // touch the corresponding depends file after scanning dependencies. |
1558 | 0 | std::ostringstream depCmd; |
1559 | | // TODO: Account for source file properties and directory-level |
1560 | | // definitions when scanning for dependencies. |
1561 | 0 | #if !defined(_WIN32) || defined(__CYGWIN__) |
1562 | | // This platform supports symlinks, so cmSystemTools will translate |
1563 | | // paths. Make sure PWD is set to the original name of the home |
1564 | | // output directory to help cmSystemTools to create the same |
1565 | | // translation table for the dependency scanning process. |
1566 | 0 | depCmd << "cd " |
1567 | 0 | << (this->LocalGenerator->ConvertToOutputFormat( |
1568 | 0 | this->LocalGenerator->GetBinaryDirectory(), |
1569 | 0 | cmOutputConverter::SHELL)) |
1570 | 0 | << " && "; |
1571 | 0 | #endif |
1572 | | // Generate a call this signature: |
1573 | | // |
1574 | | // cmake -E cmake_depends <generator> |
1575 | | // <home-src-dir> <start-src-dir> |
1576 | | // <home-out-dir> <start-out-dir> |
1577 | | // <dep-info> --color=$(COLOR) |
1578 | | // <target-name> |
1579 | | // |
1580 | | // This gives the dependency scanner enough information to recreate |
1581 | | // the state of our local generator sufficiently for its needs. |
1582 | 0 | depCmd << "$(CMAKE_COMMAND) -E cmake_depends \"" |
1583 | 0 | << this->GlobalGenerator->GetName() << "\" " |
1584 | 0 | << this->LocalGenerator->ConvertToOutputFormat( |
1585 | 0 | this->LocalGenerator->GetSourceDirectory(), |
1586 | 0 | cmOutputConverter::SHELL) |
1587 | 0 | << " " |
1588 | 0 | << this->LocalGenerator->ConvertToOutputFormat( |
1589 | 0 | this->LocalGenerator->GetCurrentSourceDirectory(), |
1590 | 0 | cmOutputConverter::SHELL) |
1591 | 0 | << " " |
1592 | 0 | << this->LocalGenerator->ConvertToOutputFormat( |
1593 | 0 | this->LocalGenerator->GetBinaryDirectory(), |
1594 | 0 | cmOutputConverter::SHELL) |
1595 | 0 | << " " |
1596 | 0 | << this->LocalGenerator->ConvertToOutputFormat( |
1597 | 0 | this->LocalGenerator->GetCurrentBinaryDirectory(), |
1598 | 0 | cmOutputConverter::SHELL) |
1599 | 0 | << " " |
1600 | 0 | << this->LocalGenerator->ConvertToOutputFormat( |
1601 | 0 | this->InfoFileNameFull, cmOutputConverter::SHELL); |
1602 | 0 | if (this->LocalGenerator->GetColorMakefile()) { |
1603 | 0 | depCmd << " \"--color=$(COLOR)\""; |
1604 | 0 | } |
1605 | 0 | depCmd << ' ' |
1606 | 0 | << this->LocalGenerator->ConvertToOutputFormat( |
1607 | 0 | this->GeneratorTarget->GetName(), cmOutputConverter::SHELL); |
1608 | 0 | commands.push_back(depCmd.str()); |
1609 | | |
1610 | | // Make sure all custom command outputs in this target are built. |
1611 | 0 | if (this->CustomCommandDriver == OnDepends) { |
1612 | 0 | this->DriveCustomCommands(depends); |
1613 | 0 | } |
1614 | | |
1615 | | // Write the rule. |
1616 | 0 | this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr, |
1617 | 0 | depTarget, depends, commands, true); |
1618 | 0 | } |
1619 | | |
1620 | | void cmMakefileTargetGenerator::DriveCustomCommands( |
1621 | | std::vector<std::string>& depends) |
1622 | 0 | { |
1623 | | // Depend on all custom command outputs. |
1624 | 0 | cm::append(depends, this->CustomCommandOutputs); |
1625 | 0 | } |
1626 | | |
1627 | | void cmMakefileTargetGenerator::WriteObjectDependRules( |
1628 | | cmSourceFile const& source, std::vector<std::string>& depends) |
1629 | 0 | { |
1630 | | // Create the list of dependencies known at cmake time. These are |
1631 | | // shared between the object file and dependency scanning rule. |
1632 | 0 | depends.push_back(source.GetFullPath()); |
1633 | 0 | if (cmValue objectDeps = source.GetProperty("OBJECT_DEPENDS")) { |
1634 | 0 | cmExpandList(*objectDeps, depends); |
1635 | 0 | } |
1636 | 0 | } |
1637 | | |
1638 | | void cmMakefileTargetGenerator::WriteDeviceLinkRule( |
1639 | | std::vector<std::string>& commands, std::string const& output) |
1640 | 0 | { |
1641 | 0 | std::string architecturesStr = |
1642 | 0 | this->GeneratorTarget->GetSafeProperty("CUDA_ARCHITECTURES"); |
1643 | |
|
1644 | 0 | if (cmIsOff(architecturesStr)) { |
1645 | 0 | this->Makefile->IssueMessage(MessageType::FATAL_ERROR, |
1646 | 0 | "CUDA_SEPARABLE_COMPILATION on Clang " |
1647 | 0 | "requires CUDA_ARCHITECTURES to be set."); |
1648 | 0 | return; |
1649 | 0 | } |
1650 | | |
1651 | 0 | cmLocalUnixMakefileGenerator3* localGen{ this->LocalGenerator }; |
1652 | 0 | cmList architectures{ architecturesStr }; |
1653 | 0 | std::string const& relPath = localGen->GetHomeRelativeOutputPath(); |
1654 | | |
1655 | | // Ensure there are no duplicates. |
1656 | 0 | std::vector<std::string> const linkDeps = [&]() -> std::vector<std::string> { |
1657 | 0 | std::vector<std::string> deps; |
1658 | 0 | this->AppendTargetDepends(deps, true); |
1659 | 0 | this->GeneratorTarget->GetLinkDepends(deps, this->GetConfigName(), "CUDA"); |
1660 | |
|
1661 | 0 | for (std::string const& obj : this->Objects) { |
1662 | 0 | deps.emplace_back(cmStrCat(relPath, obj)); |
1663 | 0 | } |
1664 | |
|
1665 | 0 | std::unordered_set<std::string> const depsSet(deps.begin(), deps.end()); |
1666 | 0 | deps.clear(); |
1667 | 0 | std::copy(depsSet.begin(), depsSet.end(), std::back_inserter(deps)); |
1668 | 0 | return deps; |
1669 | 0 | }(); |
1670 | |
|
1671 | 0 | std::string const objectDir = this->GeneratorTarget->ObjectDirectory; |
1672 | 0 | std::string const relObjectDir = |
1673 | 0 | localGen->MaybeRelativeToCurBinDir(objectDir); |
1674 | | |
1675 | | // Construct a list of files associated with this executable that |
1676 | | // may need to be cleaned. |
1677 | 0 | std::vector<std::string> cleanFiles; |
1678 | 0 | cleanFiles.push_back(localGen->MaybeRelativeToCurBinDir(output)); |
1679 | |
|
1680 | 0 | std::string profiles; |
1681 | 0 | std::vector<std::string> fatbinaryDepends; |
1682 | 0 | std::string const registerFile = |
1683 | 0 | cmStrCat(objectDir, "cmake_cuda_register.h"); |
1684 | | |
1685 | | // Link device code for each architecture. |
1686 | 0 | for (std::string const& architectureKind : architectures) { |
1687 | 0 | std::string registerFileCmd; |
1688 | | |
1689 | | // The generated register file contains macros that when expanded |
1690 | | // register the device routines. Because the routines are the same for |
1691 | | // all architectures the register file will be the same too. Thus |
1692 | | // generate it only on the first invocation to reduce overhead. |
1693 | 0 | if (fatbinaryDepends.empty()) { |
1694 | 0 | std::string const registerFileRel = |
1695 | 0 | cmStrCat(relPath, relObjectDir, "cmake_cuda_register.h"); |
1696 | 0 | registerFileCmd = |
1697 | 0 | cmStrCat(" --register-link-binaries=", registerFileRel); |
1698 | 0 | cleanFiles.push_back(registerFileRel); |
1699 | 0 | } |
1700 | | |
1701 | | // Clang always generates real code, so strip the specifier. |
1702 | 0 | std::string const architecture = |
1703 | 0 | architectureKind.substr(0, architectureKind.find('-')); |
1704 | 0 | std::string const cubin = |
1705 | 0 | cmStrCat(objectDir, "sm_", architecture, ".cubin"); |
1706 | |
|
1707 | 0 | profiles += cmStrCat(" -im=profile=sm_", architecture, ",file=", cubin); |
1708 | 0 | fatbinaryDepends.emplace_back(cubin); |
1709 | |
|
1710 | 0 | std::string command = cmStrCat( |
1711 | 0 | this->Makefile->GetRequiredDefinition("CMAKE_CUDA_DEVICE_LINKER"), |
1712 | 0 | " -arch=sm_", architecture, registerFileCmd, " -o=$@ ", |
1713 | 0 | cmJoin(linkDeps, " ")); |
1714 | |
|
1715 | 0 | localGen->WriteMakeRule(*this->BuildFileStream, nullptr, cubin, linkDeps, |
1716 | 0 | { command }, false); |
1717 | 0 | } |
1718 | | |
1719 | | // Combine all architectures into a single fatbinary. |
1720 | 0 | std::string const fatbinaryCommand = |
1721 | 0 | cmStrCat(this->Makefile->GetRequiredDefinition("CMAKE_CUDA_FATBINARY"), |
1722 | 0 | " -64 -cmdline=--compile-only -compress-all -link " |
1723 | 0 | "--embedded-fatbin=$@", |
1724 | 0 | profiles); |
1725 | 0 | std::string const fatbinaryOutput = |
1726 | 0 | cmStrCat(objectDir, "cmake_cuda_fatbin.h"); |
1727 | 0 | std::string const fatbinaryOutputRel = |
1728 | 0 | cmStrCat(relPath, relObjectDir, "cmake_cuda_fatbin.h"); |
1729 | |
|
1730 | 0 | localGen->WriteMakeRule(*this->BuildFileStream, nullptr, fatbinaryOutputRel, |
1731 | 0 | fatbinaryDepends, { fatbinaryCommand }, false); |
1732 | | |
1733 | | // Compile the stub that registers the kernels and contains the |
1734 | | // fatbinaries. |
1735 | 0 | cmRulePlaceholderExpander::RuleVariables vars; |
1736 | 0 | vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str(); |
1737 | 0 | vars.CMTargetType = |
1738 | 0 | cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()).c_str(); |
1739 | 0 | vars.Language = "CUDA"; |
1740 | 0 | vars.Object = output.c_str(); |
1741 | 0 | vars.Fatbinary = fatbinaryOutput.c_str(); |
1742 | 0 | vars.RegisterFile = registerFile.c_str(); |
1743 | 0 | vars.Config = this->GetConfigName().c_str(); |
1744 | |
|
1745 | 0 | std::string linkFlags; |
1746 | 0 | this->GetDeviceLinkFlags(linkFlags, "CUDA"); |
1747 | 0 | vars.LinkFlags = linkFlags.c_str(); |
1748 | |
|
1749 | 0 | std::string const flags = this->GetFlags("CUDA", this->GetConfigName()); |
1750 | 0 | vars.Flags = flags.c_str(); |
1751 | |
|
1752 | 0 | std::string compileCmd = this->GetLinkRule("CMAKE_CUDA_DEVICE_LINK_COMPILE"); |
1753 | 0 | auto rulePlaceholderExpander = |
1754 | 0 | localGen->CreateRulePlaceholderExpander(cmBuildStep::Link); |
1755 | 0 | rulePlaceholderExpander->ExpandRuleVariables(localGen, compileCmd, vars); |
1756 | |
|
1757 | 0 | commands.emplace_back(compileCmd); |
1758 | 0 | localGen->WriteMakeRule(*this->BuildFileStream, nullptr, output, |
1759 | 0 | { fatbinaryOutputRel }, commands, false); |
1760 | | |
1761 | | // Clean all the possible executable names and symlinks. |
1762 | 0 | this->CleanFiles.insert(cleanFiles.begin(), cleanFiles.end()); |
1763 | 0 | } |
1764 | | |
1765 | | void cmMakefileTargetGenerator::GenerateCustomRuleFile( |
1766 | | cmCustomCommandGenerator const& ccg) |
1767 | 0 | { |
1768 | | // Collect the commands. |
1769 | 0 | std::vector<std::string> commands; |
1770 | 0 | std::string comment = this->LocalGenerator->ConstructComment(ccg); |
1771 | 0 | if (!comment.empty()) { |
1772 | | // add in a progress call if needed |
1773 | 0 | this->NumberOfProgressActions++; |
1774 | 0 | if (!this->NoRuleMessages) { |
1775 | 0 | cmLocalUnixMakefileGenerator3::EchoProgress progress; |
1776 | 0 | this->MakeEchoProgress(progress); |
1777 | 0 | this->LocalGenerator->AppendEcho( |
1778 | 0 | commands, comment, cmLocalUnixMakefileGenerator3::EchoGenerate, |
1779 | 0 | &progress); |
1780 | 0 | } |
1781 | 0 | } |
1782 | | |
1783 | | // Now append the actual user-specified commands. |
1784 | 0 | std::ostringstream content; |
1785 | 0 | this->LocalGenerator->AppendCustomCommand( |
1786 | 0 | commands, ccg, this->GeneratorTarget, |
1787 | 0 | this->LocalGenerator->GetBinaryDirectory(), false, &content); |
1788 | | |
1789 | | // Collect the dependencies. |
1790 | 0 | std::vector<std::string> depends; |
1791 | 0 | this->LocalGenerator->AppendCustomDepend(depends, ccg); |
1792 | |
|
1793 | 0 | if (!ccg.GetCC().GetDepfile().empty()) { |
1794 | | // Add dependency over timestamp file for dependencies management |
1795 | 0 | auto dependTimestamp = this->LocalGenerator->MaybeRelativeToTopBinDir( |
1796 | 0 | cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.ts")); |
1797 | |
|
1798 | 0 | depends.emplace_back(std::move(dependTimestamp)); |
1799 | 0 | } |
1800 | | |
1801 | | // Write the rule. |
1802 | 0 | std::vector<std::string> const& outputs = ccg.GetOutputs(); |
1803 | 0 | bool const symbolic = this->WriteMakeRule(*this->BuildFileStream, nullptr, |
1804 | 0 | outputs, depends, commands); |
1805 | | |
1806 | | // Symbolic inputs are not expected to exist, so add dummy rules. |
1807 | 0 | if (this->CMP0113New && !depends.empty()) { |
1808 | 0 | std::vector<std::string> no_depends; |
1809 | 0 | std::vector<std::string> no_commands; |
1810 | 0 | for (std::string const& dep : depends) { |
1811 | 0 | if (cmSourceFile* dsf = |
1812 | 0 | this->Makefile->GetSource(dep, cmSourceFileLocationKind::Known)) { |
1813 | 0 | if (dsf->GetPropertyAsBool("SYMBOLIC")) { |
1814 | 0 | this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr, |
1815 | 0 | dep, no_depends, no_commands, |
1816 | 0 | true); |
1817 | 0 | } |
1818 | 0 | } |
1819 | 0 | } |
1820 | 0 | } |
1821 | | |
1822 | | // If the rule has changed make sure the output is rebuilt. |
1823 | 0 | if (!symbolic) { |
1824 | 0 | this->GlobalGenerator->AddRuleHash(ccg.GetOutputs(), content.str()); |
1825 | 0 | } |
1826 | | |
1827 | | // Setup implicit dependency scanning. |
1828 | 0 | for (auto const& idi : ccg.GetCC().GetImplicitDepends()) { |
1829 | 0 | std::string objFullPath = cmSystemTools::CollapseFullPath( |
1830 | 0 | outputs[0], this->LocalGenerator->GetCurrentBinaryDirectory()); |
1831 | 0 | std::string srcFullPath = cmSystemTools::CollapseFullPath( |
1832 | 0 | idi.second, this->LocalGenerator->GetCurrentBinaryDirectory()); |
1833 | 0 | this->LocalGenerator->AddImplicitDepends(this->GeneratorTarget, idi.first, |
1834 | 0 | objFullPath, srcFullPath); |
1835 | 0 | } |
1836 | | |
1837 | | // Setup implicit depend for depfile if any |
1838 | 0 | if (!ccg.GetCC().GetDepfile().empty()) { |
1839 | 0 | std::string objFullPath = cmSystemTools::CollapseFullPath( |
1840 | 0 | outputs[0], this->LocalGenerator->GetCurrentBinaryDirectory()); |
1841 | 0 | this->LocalGenerator->AddImplicitDepends( |
1842 | 0 | this->GeneratorTarget, "CUSTOM", objFullPath, ccg.GetFullDepfile(), |
1843 | 0 | cmDependencyScannerKind::Compiler); |
1844 | 0 | } |
1845 | |
|
1846 | 0 | this->CustomCommandOutputs.insert(outputs.begin(), outputs.end()); |
1847 | 0 | } |
1848 | | |
1849 | | void cmMakefileTargetGenerator::MakeEchoProgress( |
1850 | | cmLocalUnixMakefileGenerator3::EchoProgress& progress) const |
1851 | 0 | { |
1852 | 0 | progress.Dir = |
1853 | 0 | cmStrCat(this->LocalGenerator->GetBinaryDirectory(), "/CMakeFiles"); |
1854 | 0 | std::ostringstream progressArg; |
1855 | 0 | progressArg << "$(CMAKE_PROGRESS_" << this->NumberOfProgressActions << ")"; |
1856 | 0 | progress.Arg = progressArg.str(); |
1857 | 0 | } |
1858 | | |
1859 | | void cmMakefileTargetGenerator::WriteObjectsVariable( |
1860 | | std::string& variableName, std::string& variableNameExternal, |
1861 | | bool useWatcomQuote) |
1862 | 0 | { |
1863 | | // Write a make variable assignment that lists all objects for the |
1864 | | // target. |
1865 | 0 | variableName = this->LocalGenerator->CreateMakeVariable( |
1866 | 0 | this->GeneratorTarget->GetName(), "_OBJECTS"); |
1867 | 0 | *this->BuildFileStream << "# Object files for target " |
1868 | 0 | << this->GeneratorTarget->GetName() << "\n" |
1869 | 0 | << variableName << " ="; |
1870 | 0 | auto const& lineContinue = this->GlobalGenerator->LineContinueDirective; |
1871 | |
|
1872 | 0 | cmValue pchExtension = this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION"); |
1873 | |
|
1874 | 0 | for (std::string const& obj : this->Objects) { |
1875 | 0 | if (cmHasSuffix(obj, pchExtension)) { |
1876 | 0 | continue; |
1877 | 0 | } |
1878 | 0 | *this->BuildFileStream << " " << lineContinue; |
1879 | 0 | *this->BuildFileStream |
1880 | 0 | << cmLocalUnixMakefileGenerator3::ConvertToQuotedOutputPath( |
1881 | 0 | obj, useWatcomQuote); |
1882 | 0 | } |
1883 | 0 | *this->BuildFileStream << "\n"; |
1884 | | |
1885 | | // Write a make variable assignment that lists all external objects |
1886 | | // for the target. |
1887 | 0 | variableNameExternal = this->LocalGenerator->CreateMakeVariable( |
1888 | 0 | this->GeneratorTarget->GetName(), "_EXTERNAL_OBJECTS"); |
1889 | | /* clang-format off */ |
1890 | 0 | *this->BuildFileStream |
1891 | 0 | << "\n" |
1892 | 0 | << "# External object files for target " |
1893 | 0 | << this->GeneratorTarget->GetName() << "\n" |
1894 | 0 | << variableNameExternal << " ="; |
1895 | | /* clang-format on */ |
1896 | 0 | for (std::string const& obj : this->ExternalObjects) { |
1897 | 0 | *this->BuildFileStream << " " << lineContinue; |
1898 | 0 | *this->BuildFileStream |
1899 | 0 | << cmLocalUnixMakefileGenerator3::ConvertToQuotedOutputPath( |
1900 | 0 | obj, useWatcomQuote); |
1901 | 0 | } |
1902 | 0 | *this->BuildFileStream << "\n" |
1903 | 0 | << "\n"; |
1904 | 0 | } |
1905 | | |
1906 | | class cmMakefileTargetGeneratorObjectStrings |
1907 | | { |
1908 | | public: |
1909 | | cmMakefileTargetGeneratorObjectStrings(std::vector<std::string>& strings, |
1910 | | cmOutputConverter* outputConverter, |
1911 | | bool useWatcomQuote, |
1912 | | cmStateDirectory const& stateDir, |
1913 | | std::string::size_type limit) |
1914 | 0 | : Strings(strings) |
1915 | 0 | , OutputConverter(outputConverter) |
1916 | 0 | , UseWatcomQuote(useWatcomQuote) |
1917 | 0 | , StateDir(stateDir) |
1918 | 0 | , LengthLimit(limit) |
1919 | 0 | { |
1920 | 0 | this->Space = ""; |
1921 | 0 | } |
1922 | | void Feed(std::string const& obj) |
1923 | 0 | { |
1924 | | // Construct the name of the next object. |
1925 | 0 | this->NextObject = this->OutputConverter->ConvertToOutputFormat( |
1926 | 0 | this->OutputConverter->MaybeRelativeToCurBinDir(obj), |
1927 | 0 | cmOutputConverter::RESPONSE, this->UseWatcomQuote); |
1928 | | |
1929 | | // Roll over to next string if the limit will be exceeded. |
1930 | 0 | if (this->LengthLimit != std::string::npos && |
1931 | 0 | (this->CurrentString.length() + 1 + this->NextObject.length() > |
1932 | 0 | this->LengthLimit)) { |
1933 | 0 | this->Strings.push_back(this->CurrentString); |
1934 | 0 | this->CurrentString.clear(); |
1935 | 0 | this->Space = ""; |
1936 | 0 | } |
1937 | | |
1938 | | // Separate from previous object. |
1939 | 0 | this->CurrentString += this->Space; |
1940 | 0 | this->Space = " "; |
1941 | | |
1942 | | // Append this object. |
1943 | 0 | this->CurrentString += this->NextObject; |
1944 | 0 | } |
1945 | 0 | void Done() { this->Strings.push_back(this->CurrentString); } |
1946 | | |
1947 | | private: |
1948 | | std::vector<std::string>& Strings; |
1949 | | cmOutputConverter* OutputConverter; |
1950 | | bool UseWatcomQuote; |
1951 | | cmStateDirectory StateDir; |
1952 | | std::string::size_type LengthLimit; |
1953 | | std::string CurrentString; |
1954 | | std::string NextObject; |
1955 | | char const* Space; |
1956 | | }; |
1957 | | |
1958 | | void cmMakefileTargetGenerator::WriteObjectsStrings( |
1959 | | std::vector<std::string>& objStrings, bool useWatcomQuote, |
1960 | | std::string::size_type limit) |
1961 | 0 | { |
1962 | 0 | cmValue pchExtension = this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION"); |
1963 | |
|
1964 | 0 | cmMakefileTargetGeneratorObjectStrings helper( |
1965 | 0 | objStrings, this->LocalGenerator, useWatcomQuote, |
1966 | 0 | this->LocalGenerator->GetStateSnapshot().GetDirectory(), limit); |
1967 | 0 | for (std::string const& obj : this->Objects) { |
1968 | 0 | if (cmHasSuffix(obj, pchExtension)) { |
1969 | 0 | continue; |
1970 | 0 | } |
1971 | 0 | helper.Feed(obj); |
1972 | 0 | } |
1973 | 0 | for (std::string const& obj : this->ExternalObjects) { |
1974 | 0 | helper.Feed(obj); |
1975 | 0 | } |
1976 | 0 | auto const ispcAdditionalObjs = |
1977 | 0 | this->GeneratorTarget->GetGeneratedISPCObjects(this->GetConfigName()); |
1978 | 0 | for (auto const& obj : ispcAdditionalObjs) { |
1979 | 0 | helper.Feed(obj.second); |
1980 | 0 | } |
1981 | 0 | helper.Done(); |
1982 | 0 | } |
1983 | | |
1984 | | void cmMakefileTargetGenerator::WriteTargetDriverRule( |
1985 | | std::string const& main_output, bool relink) |
1986 | 0 | { |
1987 | | // Compute the name of the driver target. |
1988 | 0 | std::string dir = |
1989 | 0 | this->LocalGenerator->GetRelativeTargetDirectory(this->GeneratorTarget); |
1990 | 0 | std::string buildTargetRuleName = |
1991 | 0 | cmStrCat(std::move(dir), relink ? "/preinstall" : "/build"); |
1992 | 0 | buildTargetRuleName = |
1993 | 0 | this->LocalGenerator->MaybeRelativeToTopBinDir(buildTargetRuleName); |
1994 | | |
1995 | | // Build the list of target outputs to drive. |
1996 | 0 | std::vector<std::string> depends{ main_output }; |
1997 | |
|
1998 | 0 | char const* comment = nullptr; |
1999 | 0 | if (relink) { |
2000 | | // Setup the comment for the preinstall driver. |
2001 | 0 | comment = "Rule to relink during preinstall."; |
2002 | 0 | } else { |
2003 | | // Setup the comment for the main build driver. |
2004 | 0 | comment = "Rule to build all files generated by this target."; |
2005 | | |
2006 | | // Make sure all custom command outputs in this target are built. |
2007 | 0 | if (this->CustomCommandDriver == OnBuild) { |
2008 | 0 | this->DriveCustomCommands(depends); |
2009 | 0 | } |
2010 | | |
2011 | | // Make sure the extra files are built. |
2012 | 0 | cm::append(depends, this->ExtraFiles); |
2013 | 0 | } |
2014 | | |
2015 | | // Write the driver rule. |
2016 | 0 | std::vector<std::string> no_commands; |
2017 | 0 | this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, comment, |
2018 | 0 | buildTargetRuleName, depends, |
2019 | 0 | no_commands, true); |
2020 | 0 | } |
2021 | | |
2022 | | void cmMakefileTargetGenerator::AppendTargetDepends( |
2023 | | std::vector<std::string>& depends, bool ignoreType) |
2024 | 0 | { |
2025 | | // Static libraries never depend on anything for linking. |
2026 | 0 | if (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY && |
2027 | 0 | !ignoreType) { |
2028 | 0 | return; |
2029 | 0 | } |
2030 | | |
2031 | 0 | std::string const& cfg = this->GetConfigName(); |
2032 | |
|
2033 | 0 | if (this->GeneratorTarget->HasLinkDependencyFile(cfg)) { |
2034 | 0 | depends.push_back( |
2035 | 0 | cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.ts")); |
2036 | 0 | } |
2037 | | |
2038 | | // Loop over all library dependencies. |
2039 | 0 | if (cmComputeLinkInformation const* cli = |
2040 | 0 | this->GeneratorTarget->GetLinkInformation(cfg)) { |
2041 | 0 | cm::append(depends, cli->GetDepends()); |
2042 | 0 | } |
2043 | 0 | } |
2044 | | |
2045 | | void cmMakefileTargetGenerator::AppendObjectDepends( |
2046 | | std::vector<std::string>& depends) |
2047 | 0 | { |
2048 | | // Add dependencies on the compiled object files. |
2049 | 0 | std::string const& relPath = |
2050 | 0 | this->LocalGenerator->GetHomeRelativeOutputPath(); |
2051 | 0 | for (std::string const& obj : this->Objects) { |
2052 | 0 | std::string objTarget = cmStrCat(relPath, obj); |
2053 | 0 | depends.push_back(std::move(objTarget)); |
2054 | 0 | } |
2055 | | |
2056 | | // Add dependencies on the external object files. |
2057 | 0 | cm::append(depends, this->ExternalObjects); |
2058 | | |
2059 | | // Add dependency on the Rust main crate root file. |
2060 | 0 | if (cmSourceFile const* mainCrateRoot = |
2061 | 0 | this->GeneratorTarget->GetRustMainCrateRoot(this->GetConfigName())) { |
2062 | 0 | depends.push_back(mainCrateRoot->GetFullPath()); |
2063 | 0 | } |
2064 | | |
2065 | | // Add a dependency on the rule file itself. |
2066 | 0 | this->LocalGenerator->AppendRuleDepend(depends, |
2067 | 0 | this->BuildFileNameFull.c_str()); |
2068 | 0 | } |
2069 | | |
2070 | | void cmMakefileTargetGenerator::AppendLinkDepends( |
2071 | | std::vector<std::string>& depends, std::string const& linkLanguage) |
2072 | 0 | { |
2073 | 0 | this->AppendObjectDepends(depends); |
2074 | | |
2075 | | // Add dependencies on targets that must be built first. |
2076 | 0 | this->AppendTargetDepends(depends); |
2077 | | |
2078 | | // Add a dependency on the link definitions file, if any. |
2079 | 0 | if (cmGeneratorTarget::ModuleDefinitionInfo const* mdi = |
2080 | 0 | this->GeneratorTarget->GetModuleDefinitionInfo( |
2081 | 0 | this->GetConfigName())) { |
2082 | 0 | for (cmSourceFile const* src : mdi->Sources) { |
2083 | 0 | depends.push_back(src->GetFullPath()); |
2084 | 0 | } |
2085 | 0 | } |
2086 | | |
2087 | | // Add a dependency on user-specified manifest files, if any. |
2088 | 0 | std::vector<cmSourceFile const*> manifest_srcs; |
2089 | 0 | this->GeneratorTarget->GetManifests(manifest_srcs, this->GetConfigName()); |
2090 | 0 | for (cmSourceFile const* manifest_src : manifest_srcs) { |
2091 | 0 | depends.push_back(manifest_src->GetFullPath()); |
2092 | 0 | } |
2093 | | |
2094 | | // Add user-specified dependencies. |
2095 | 0 | this->GeneratorTarget->GetLinkDepends(depends, this->GetConfigName(), |
2096 | 0 | linkLanguage); |
2097 | 0 | } |
2098 | | |
2099 | | std::string cmMakefileTargetGenerator::GetLinkRule( |
2100 | | std::string const& linkRuleVar) |
2101 | 0 | { |
2102 | 0 | std::string linkRule = this->Makefile->GetRequiredDefinition(linkRuleVar); |
2103 | 0 | if (this->GeneratorTarget->HasImplibGNUtoMS(this->GetConfigName())) { |
2104 | 0 | std::string ruleVar = |
2105 | 0 | cmStrCat("CMAKE_", |
2106 | 0 | this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName()), |
2107 | 0 | "_GNUtoMS_RULE"); |
2108 | 0 | if (cmValue rule = this->Makefile->GetDefinition(ruleVar)) { |
2109 | 0 | linkRule += *rule; |
2110 | 0 | } |
2111 | 0 | } |
2112 | 0 | return linkRule; |
2113 | 0 | } |
2114 | | |
2115 | | void cmMakefileTargetGenerator::CloseFileStreams() |
2116 | 0 | { |
2117 | 0 | this->BuildFileStream.reset(); |
2118 | 0 | this->InfoFileStream.reset(); |
2119 | 0 | this->FlagFileStream.reset(); |
2120 | 0 | } |
2121 | | |
2122 | | void cmMakefileTargetGenerator::CreateLinkScript( |
2123 | | char const* name, std::vector<std::string> const& link_commands, |
2124 | | std::vector<std::string>& makefile_commands, |
2125 | | std::vector<std::string>& makefile_depends) |
2126 | 0 | { |
2127 | | // Create the link script file. |
2128 | 0 | std::string linkScriptName = |
2129 | 0 | cmStrCat(this->TargetBuildDirectoryFull, '/', name); |
2130 | 0 | cmGeneratedFileStream linkScriptStream(linkScriptName); |
2131 | 0 | linkScriptStream.SetCopyIfDifferent(true); |
2132 | 0 | for (std::string const& link_command : link_commands) { |
2133 | | // Do not write out empty commands or commands beginning in the |
2134 | | // shell no-op ":". |
2135 | 0 | if (!link_command.empty() && link_command[0] != ':') { |
2136 | 0 | linkScriptStream << link_command << "\n"; |
2137 | 0 | } |
2138 | 0 | } |
2139 | | |
2140 | | // Create the makefile command to invoke the link script. |
2141 | 0 | std::string link_command = |
2142 | 0 | cmStrCat("$(CMAKE_COMMAND) -E cmake_link_script ", |
2143 | 0 | this->LocalGenerator->ConvertToOutputFormat( |
2144 | 0 | this->LocalGenerator->MaybeRelativeToCurBinDir(linkScriptName), |
2145 | 0 | cmOutputConverter::SHELL), |
2146 | 0 | " --verbose=$(VERBOSE)"); |
2147 | 0 | makefile_commands.push_back(std::move(link_command)); |
2148 | 0 | makefile_depends.push_back(std::move(linkScriptName)); |
2149 | 0 | } |
2150 | | |
2151 | | bool cmMakefileTargetGenerator::CheckUseResponseFileForObjects( |
2152 | | std::string const& l) const |
2153 | 0 | { |
2154 | | // Check for an explicit setting one way or the other. |
2155 | 0 | std::string const responseVar = |
2156 | 0 | "CMAKE_" + l + "_USE_RESPONSE_FILE_FOR_OBJECTS"; |
2157 | 0 | if (cmValue val = this->Makefile->GetDefinition(responseVar)) { |
2158 | 0 | if (!val->empty()) { |
2159 | 0 | return val.IsOn(); |
2160 | 0 | } |
2161 | 0 | } |
2162 | | |
2163 | | // Check for a system limit. |
2164 | 0 | if (size_t const limit = cmSystemTools::CalculateCommandLineLengthLimit()) { |
2165 | | // Compute the total length of our list of object files with room |
2166 | | // for argument separation and quoting. This does not convert paths |
2167 | | // relative to CMAKE_CURRENT_BINARY_DIR like the final list will be, so |
2168 | | // the actual list will likely be much shorter than this. However, in |
2169 | | // the worst case all objects will remain as absolute paths. |
2170 | 0 | size_t length = 0; |
2171 | 0 | for (std::string const& obj : this->Objects) { |
2172 | 0 | length += obj.size() + 3; |
2173 | 0 | } |
2174 | 0 | for (std::string const& ext_obj : this->ExternalObjects) { |
2175 | 0 | length += ext_obj.size() + 3; |
2176 | 0 | } |
2177 | | |
2178 | | // We need to guarantee room for both objects and libraries, so |
2179 | | // if the objects take up more than half then use a response file |
2180 | | // for them. |
2181 | 0 | if (length > (limit / 2)) { |
2182 | 0 | return true; |
2183 | 0 | } |
2184 | 0 | } |
2185 | | |
2186 | | // We do not need a response file for objects. |
2187 | 0 | return false; |
2188 | 0 | } |
2189 | | |
2190 | | bool cmMakefileTargetGenerator::CheckUseResponseFileForLibraries( |
2191 | | std::string const& l) const |
2192 | 0 | { |
2193 | | // Check for an explicit setting one way or the other. |
2194 | 0 | std::string const responseVar = |
2195 | 0 | "CMAKE_" + l + "_USE_RESPONSE_FILE_FOR_LIBRARIES"; |
2196 | 0 | if (cmValue val = this->Makefile->GetDefinition(responseVar)) { |
2197 | 0 | if (!val->empty()) { |
2198 | 0 | return val.IsOn(); |
2199 | 0 | } |
2200 | 0 | } |
2201 | | |
2202 | | // We do not need a response file for libraries. |
2203 | 0 | return false; |
2204 | 0 | } |
2205 | | |
2206 | | std::string cmMakefileTargetGenerator::CreateResponseFile( |
2207 | | std::string const& name, std::string const& options, |
2208 | | std::vector<std::string>& makefile_depends, std::string const& language) |
2209 | 0 | { |
2210 | | // FIXME: Find a better way to determine the response file encoding, |
2211 | | // perhaps using tool-specific platform information variables. |
2212 | | // For now, use the makefile encoding as a heuristic. |
2213 | 0 | codecvt_Encoding responseEncoding = |
2214 | 0 | this->GlobalGenerator->GetMakefileEncoding(); |
2215 | | // Non-MSVC tooling doesn't understand BOM encoded files. |
2216 | 0 | if (responseEncoding == codecvt_Encoding::UTF8_WITH_BOM && |
2217 | 0 | (language == "CUDA" || !this->Makefile->IsOn("MSVC"))) { |
2218 | 0 | responseEncoding = codecvt_Encoding::UTF8; |
2219 | 0 | } |
2220 | | |
2221 | | // Create the response file. |
2222 | 0 | std::string responseFileNameFull = |
2223 | 0 | cmStrCat(this->TargetBuildDirectoryFull, '/', name); |
2224 | 0 | cmGeneratedFileStream responseStream(responseFileNameFull, false, |
2225 | 0 | responseEncoding); |
2226 | 0 | responseStream.SetCopyIfDifferent(true); |
2227 | 0 | responseStream << options << "\n"; |
2228 | | |
2229 | | // Add a dependency so the target will rebuild when the set of |
2230 | | // objects changes. |
2231 | 0 | makefile_depends.push_back(std::move(responseFileNameFull)); |
2232 | | |
2233 | | // Construct the name to be used on the command line. |
2234 | 0 | std::string responseFileName = |
2235 | 0 | cmStrCat(this->TargetBuildDirectory, '/', name); |
2236 | 0 | return responseFileName; |
2237 | 0 | } |
2238 | | |
2239 | | std::unique_ptr<cmLinkLineComputer> |
2240 | | cmMakefileTargetGenerator::CreateLinkLineComputer( |
2241 | | cmOutputConverter* outputConverter, cmStateDirectory const& stateDir) |
2242 | 0 | { |
2243 | 0 | if (this->Makefile->IsOn("MSVC60")) { |
2244 | 0 | return this->GlobalGenerator->CreateMSVC60LinkLineComputer(outputConverter, |
2245 | 0 | stateDir); |
2246 | 0 | } |
2247 | 0 | return this->GlobalGenerator->CreateLinkLineComputer(outputConverter, |
2248 | 0 | stateDir); |
2249 | 0 | } |
2250 | | |
2251 | | void cmMakefileTargetGenerator::CreateLinkLibs( |
2252 | | cmLinkLineComputer* linkLineComputer, std::string& linkLibs, |
2253 | | bool useResponseFile, std::vector<std::string>& makefile_depends, |
2254 | | std::string const& linkLanguage, ResponseFlagFor responseMode) |
2255 | 0 | { |
2256 | 0 | if (cmComputeLinkInformation* pcli = |
2257 | 0 | this->GeneratorTarget->GetLinkInformation(this->GetConfigName())) { |
2258 | 0 | std::string frameworkPath; |
2259 | 0 | std::string linkPath; |
2260 | 0 | this->LocalGenerator->OutputLinkLibraries(pcli, linkLineComputer, linkLibs, |
2261 | 0 | frameworkPath, linkPath); |
2262 | 0 | linkLibs = frameworkPath + linkPath + linkLibs; |
2263 | 0 | } |
2264 | |
|
2265 | 0 | if (useResponseFile && |
2266 | 0 | linkLibs.find_first_not_of(' ') != std::string::npos) { |
2267 | | // Lookup the response file reference flag. |
2268 | 0 | std::string responseFlag = this->GetResponseFlag(responseMode); |
2269 | | |
2270 | | // Create this response file. |
2271 | 0 | std::string const responseFileName = |
2272 | 0 | (responseMode == DeviceLink) ? "deviceLinkLibs.rsp" : "linkLibs.rsp"; |
2273 | 0 | std::string const responseLang = |
2274 | 0 | (responseMode == DeviceLink) ? "CUDA" : linkLanguage; |
2275 | 0 | std::string link_rsp = this->CreateResponseFile( |
2276 | 0 | responseFileName, linkLibs, makefile_depends, responseLang); |
2277 | | |
2278 | | // Reference the response file. |
2279 | 0 | linkLibs = cmStrCat(std::move(responseFlag), |
2280 | 0 | this->LocalGenerator->ConvertToOutputFormat( |
2281 | 0 | std::move(link_rsp), cmOutputConverter::SHELL)); |
2282 | 0 | } |
2283 | 0 | } |
2284 | | |
2285 | | void cmMakefileTargetGenerator::CreateObjectLists( |
2286 | | bool useLinkScript, bool useArchiveRules, bool useResponseFile, |
2287 | | std::string& buildObjs, std::vector<std::string>& makefile_depends, |
2288 | | bool useWatcomQuote, std::string const& linkLanguage, |
2289 | | ResponseFlagFor responseMode) |
2290 | 0 | { |
2291 | 0 | std::string variableName; |
2292 | 0 | std::string variableNameExternal; |
2293 | 0 | this->WriteObjectsVariable(variableName, variableNameExternal, |
2294 | 0 | useWatcomQuote); |
2295 | 0 | if (useResponseFile) { |
2296 | | // MSVC response files cannot exceed 128K. |
2297 | 0 | std::string::size_type constexpr responseFileLimit = 131000; |
2298 | | |
2299 | | // Construct the individual object list strings. |
2300 | 0 | std::vector<std::string> object_strings; |
2301 | 0 | this->WriteObjectsStrings(object_strings, useWatcomQuote, |
2302 | 0 | responseFileLimit); |
2303 | | |
2304 | | // Lookup the response file reference flag. |
2305 | 0 | std::string const responseFlag = this->GetResponseFlag(responseMode); |
2306 | | |
2307 | | // Write a response file for each string. |
2308 | 0 | char const* sep = ""; |
2309 | 0 | for (unsigned int i = 0; i < object_strings.size(); ++i) { |
2310 | | // Number the response files. |
2311 | 0 | std::string responseFileName = |
2312 | 0 | cmStrCat((responseMode == DeviceLink) ? "deviceObjects" : "objects", |
2313 | 0 | i + 1, ".rsp"); |
2314 | | |
2315 | | // Create this response file. |
2316 | 0 | std::string objects_rsp = this->CreateResponseFile( |
2317 | 0 | responseFileName, object_strings[i], makefile_depends, linkLanguage); |
2318 | |
|
2319 | 0 | buildObjs += |
2320 | 0 | cmStrCat(sep, // Separate from previous response file references. |
2321 | 0 | responseFlag, // Reference the response file. |
2322 | 0 | this->LocalGenerator->ConvertToOutputFormat( |
2323 | 0 | objects_rsp, cmOutputConverter::SHELL)); |
2324 | 0 | sep = " "; |
2325 | 0 | } |
2326 | 0 | } else if (useLinkScript) { |
2327 | 0 | if (!useArchiveRules) { |
2328 | 0 | std::vector<std::string> objStrings; |
2329 | 0 | this->WriteObjectsStrings(objStrings, useWatcomQuote); |
2330 | 0 | buildObjs = objStrings[0]; |
2331 | 0 | } |
2332 | 0 | } else { |
2333 | 0 | buildObjs = |
2334 | 0 | cmStrCat("$(", variableName, ") $(", variableNameExternal, ')'); |
2335 | 0 | } |
2336 | 0 | } |
2337 | | |
2338 | | bool cmMakefileTargetGenerator::CreateRustLinkArguments( |
2339 | | std::string const& linkLanguage, std::string& rustMainCrateRootPath, |
2340 | | std::string& rustLinkCrates, std::string& rustNativeObjects) |
2341 | 0 | { |
2342 | 0 | if (linkLanguage == "Rust") { |
2343 | 0 | this->ComputeRustFlagsForObjects(rustLinkCrates, rustNativeObjects, |
2344 | 0 | this->Objects); |
2345 | 0 | this->ComputeRustFlagsForObjects(rustLinkCrates, rustNativeObjects, |
2346 | 0 | this->ExternalObjects); |
2347 | |
|
2348 | 0 | cmSourceFile const* mainCrateRoot = |
2349 | 0 | this->GeneratorTarget->GetRustMainCrateRoot(this->GetConfigName()); |
2350 | 0 | if (!mainCrateRoot) { |
2351 | 0 | this->Makefile->IssueMessage(MessageType::FATAL_ERROR, |
2352 | 0 | "Target " + |
2353 | 0 | this->GeneratorTarget->GetName() + |
2354 | 0 | " has no main crate root."); |
2355 | 0 | return false; |
2356 | 0 | } |
2357 | 0 | rustMainCrateRootPath = mainCrateRoot->GetFullPath(); |
2358 | 0 | rustMainCrateRootPath = this->LocalGenerator->ConvertToOutputFormat( |
2359 | 0 | rustMainCrateRootPath, cmOutputConverter::SHELL); |
2360 | 0 | return true; |
2361 | 0 | } |
2362 | 0 | return false; |
2363 | 0 | } |
2364 | | |
2365 | | void cmMakefileTargetGenerator::AddIncludeFlags(std::string& flags, |
2366 | | std::string const& lang, |
2367 | | std::string const& /*config*/) |
2368 | 0 | { |
2369 | 0 | std::string const responseVar = |
2370 | 0 | cmStrCat("CMAKE_", lang, "_USE_RESPONSE_FILE_FOR_INCLUDES"); |
2371 | 0 | bool const useResponseFile = this->Makefile->IsOn(responseVar); |
2372 | |
|
2373 | 0 | std::vector<std::string> includes; |
2374 | 0 | this->LocalGenerator->GetIncludeDirectories(includes, this->GeneratorTarget, |
2375 | 0 | lang, this->GetConfigName()); |
2376 | |
|
2377 | 0 | std::string const includeFlags = this->LocalGenerator->GetIncludeFlags( |
2378 | 0 | includes, this->GeneratorTarget, lang, this->GetConfigName(), |
2379 | 0 | useResponseFile); |
2380 | 0 | if (includeFlags.empty()) { |
2381 | 0 | return; |
2382 | 0 | } |
2383 | | |
2384 | 0 | if (useResponseFile) { |
2385 | 0 | std::string const responseFlagVar = |
2386 | 0 | "CMAKE_" + lang + "_RESPONSE_FILE_FLAG"; |
2387 | 0 | std::string responseFlag = |
2388 | 0 | this->Makefile->GetSafeDefinition(responseFlagVar); |
2389 | 0 | if (responseFlag.empty()) { |
2390 | 0 | responseFlag = "@"; |
2391 | 0 | } |
2392 | 0 | std::string const name = cmStrCat("includes_", lang, ".rsp"); |
2393 | 0 | std::string const includes_rsp = this->CreateResponseFile( |
2394 | 0 | name, includeFlags, this->FlagFileDepends[lang], lang); |
2395 | 0 | std::string const arg = |
2396 | 0 | cmStrCat(std::move(responseFlag), |
2397 | 0 | this->LocalGenerator->ConvertToOutputFormat( |
2398 | 0 | includes_rsp, cmOutputConverter::SHELL)); |
2399 | 0 | this->LocalGenerator->AppendFlags(flags, arg); |
2400 | 0 | } else { |
2401 | 0 | this->LocalGenerator->AppendFlags(flags, includeFlags); |
2402 | 0 | } |
2403 | 0 | } |
2404 | | |
2405 | | void cmMakefileTargetGenerator::GenDefFile( |
2406 | | std::vector<std::string>& real_link_commands) |
2407 | 0 | { |
2408 | 0 | cmGeneratorTarget::ModuleDefinitionInfo const* mdi = |
2409 | 0 | this->GeneratorTarget->GetModuleDefinitionInfo(this->GetConfigName()); |
2410 | 0 | if (!mdi || !mdi->DefFileGenerated) { |
2411 | 0 | return; |
2412 | 0 | } |
2413 | 0 | std::string cmd = cmSystemTools::GetCMakeCommand(); |
2414 | 0 | cmd = cmStrCat( |
2415 | 0 | this->LocalGenerator->ConvertToOutputFormat(cmd, cmOutputConverter::SHELL), |
2416 | 0 | " -E __create_def ", |
2417 | 0 | this->LocalGenerator->ConvertToOutputFormat( |
2418 | 0 | this->LocalGenerator->MaybeRelativeToCurBinDir(mdi->DefFile), |
2419 | 0 | cmOutputConverter::SHELL), |
2420 | 0 | ' '); |
2421 | 0 | std::string objlist_file = mdi->DefFile + ".objs"; |
2422 | 0 | cmd += this->LocalGenerator->ConvertToOutputFormat( |
2423 | 0 | this->LocalGenerator->MaybeRelativeToCurBinDir(objlist_file), |
2424 | 0 | cmOutputConverter::SHELL); |
2425 | 0 | cmValue const nm_executable = this->Makefile->GetDefinition("CMAKE_NM"); |
2426 | 0 | if (cmNonempty(nm_executable)) { |
2427 | 0 | cmd += " --nm="; |
2428 | 0 | cmd += this->LocalCommonGenerator->ConvertToOutputFormat( |
2429 | 0 | *nm_executable, cmOutputConverter::SHELL); |
2430 | 0 | } |
2431 | 0 | real_link_commands.insert(real_link_commands.begin(), cmd); |
2432 | | // create a list of obj files for the -E __create_def to read |
2433 | 0 | cmGeneratedFileStream fout(objlist_file); |
2434 | |
|
2435 | 0 | if (mdi->WindowsExportAllSymbols) { |
2436 | 0 | for (std::string const& obj : this->Objects) { |
2437 | 0 | if (cmHasLiteralSuffix(obj, ".obj")) { |
2438 | 0 | fout << obj << "\n"; |
2439 | 0 | } |
2440 | 0 | } |
2441 | 0 | for (std::string const& obj : this->ExternalObjects) { |
2442 | 0 | fout << obj << "\n"; |
2443 | 0 | } |
2444 | 0 | } |
2445 | |
|
2446 | 0 | for (cmSourceFile const* src : mdi->Sources) { |
2447 | 0 | fout << src->GetFullPath() << "\n"; |
2448 | 0 | } |
2449 | 0 | } |
2450 | | |
2451 | | std::string cmMakefileTargetGenerator::GetResponseFlag( |
2452 | | ResponseFlagFor mode) const |
2453 | 0 | { |
2454 | 0 | std::string responseFlag = "@"; |
2455 | 0 | std::string responseFlagVar; |
2456 | |
|
2457 | 0 | auto const lang = |
2458 | 0 | this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName()); |
2459 | 0 | if (mode == cmMakefileTargetGenerator::ResponseFlagFor::Link) { |
2460 | 0 | responseFlagVar = cmStrCat("CMAKE_", lang, "_RESPONSE_FILE_LINK_FLAG"); |
2461 | 0 | } else if (mode == cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink) { |
2462 | 0 | responseFlagVar = "CMAKE_CUDA_RESPONSE_FILE_DEVICE_LINK_FLAG"; |
2463 | 0 | } else if (mode == cmMakefileTargetGenerator::ResponseFlagFor::Archive) { |
2464 | 0 | responseFlagVar = cmStrCat("CMAKE_", lang, "_RESPONSE_FILE_ARCHIVE_FLAG"); |
2465 | 0 | } |
2466 | |
|
2467 | 0 | if (cmValue const p = this->Makefile->GetDefinition(responseFlagVar)) { |
2468 | 0 | responseFlag = *p; |
2469 | 0 | } else if (mode == cmMakefileTargetGenerator::ResponseFlagFor::Archive) { |
2470 | 0 | responseFlagVar = cmStrCat("CMAKE_", lang, "_RESPONSE_FILE_LINK_FLAG"); |
2471 | 0 | if (cmValue const q = this->Makefile->GetDefinition(responseFlagVar)) { |
2472 | 0 | responseFlag = *q; |
2473 | 0 | } |
2474 | 0 | } |
2475 | 0 | return responseFlag; |
2476 | 0 | } |