1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
3 #include "cmMakefileTargetGenerator.h"
10 #include <unordered_map>
11 #include <unordered_set>
15 #include <cm/string_view>
16 #include <cmext/algorithm>
17 #include <cmext/string_view>
19 #include "cm_codecvt.hxx"
21 #include "cmComputeLinkInformation.h"
22 #include "cmCustomCommand.h"
23 #include "cmCustomCommandGenerator.h"
24 #include "cmFileSet.h"
25 #include "cmGeneratedFileStream.h"
26 #include "cmGeneratorExpression.h"
27 #include "cmGeneratorTarget.h"
28 #include "cmGlobalUnixMakefileGenerator3.h"
29 #include "cmLinkLineComputer.h" // IWYU pragma: keep
30 #include "cmLocalCommonGenerator.h"
31 #include "cmLocalGenerator.h"
32 #include "cmLocalUnixMakefileGenerator3.h"
33 #include "cmMakefile.h"
34 #include "cmMakefileExecutableTargetGenerator.h"
35 #include "cmMakefileLibraryTargetGenerator.h"
36 #include "cmMakefileUtilityTargetGenerator.h"
37 #include "cmMessageType.h"
38 #include "cmOutputConverter.h"
39 #include "cmPolicies.h"
41 #include "cmRulePlaceholderExpander.h"
42 #include "cmSourceFile.h"
43 #include "cmSourceFileLocationKind.h"
45 #include "cmStateDirectory.h"
46 #include "cmStateSnapshot.h"
47 #include "cmStateTypes.h"
48 #include "cmStringAlgorithms.h"
49 #include "cmSystemTools.h"
54 cmMakefileTargetGenerator::cmMakefileTargetGenerator(cmGeneratorTarget* target)
55 : cmCommonTargetGenerator(target)
57 this->CustomCommandDriver = OnBuild;
58 this->LocalGenerator =
59 static_cast<cmLocalUnixMakefileGenerator3*>(target->GetLocalGenerator());
60 this->GlobalGenerator = static_cast<cmGlobalUnixMakefileGenerator3*>(
61 this->LocalGenerator->GetGlobalGenerator());
62 cmake* cm = this->GlobalGenerator->GetCMakeInstance();
63 this->NoRuleMessages = false;
64 if (cmValue ruleStatus =
65 cm->GetState()->GetGlobalProperty("RULE_MESSAGES")) {
66 this->NoRuleMessages = cmIsOff(*ruleStatus);
68 switch (this->GeneratorTarget->GetPolicyStatusCMP0113()) {
69 case cmPolicies::WARN:
72 this->CMP0113New = false;
75 case cmPolicies::REQUIRED_IF_USED:
76 case cmPolicies::REQUIRED_ALWAYS:
77 this->CMP0113New = true;
80 this->MacOSXContentGenerator =
81 cm::make_unique<MacOSXContentGeneratorType>(this);
84 cmMakefileTargetGenerator::~cmMakefileTargetGenerator() = default;
86 std::unique_ptr<cmMakefileTargetGenerator> cmMakefileTargetGenerator::New(
87 cmGeneratorTarget* tgt)
89 std::unique_ptr<cmMakefileTargetGenerator> result;
91 switch (tgt->GetType()) {
92 case cmStateEnums::EXECUTABLE:
93 result = cm::make_unique<cmMakefileExecutableTargetGenerator>(tgt);
95 case cmStateEnums::STATIC_LIBRARY:
96 case cmStateEnums::SHARED_LIBRARY:
97 case cmStateEnums::MODULE_LIBRARY:
98 case cmStateEnums::OBJECT_LIBRARY:
99 result = cm::make_unique<cmMakefileLibraryTargetGenerator>(tgt);
101 case cmStateEnums::INTERFACE_LIBRARY:
102 case cmStateEnums::UTILITY:
103 result = cm::make_unique<cmMakefileUtilityTargetGenerator>(tgt);
107 // break; /* unreachable */
112 std::string cmMakefileTargetGenerator::GetConfigName() const
114 auto const& configNames = this->LocalGenerator->GetConfigNames();
115 assert(configNames.size() == 1);
116 return configNames.front();
119 void cmMakefileTargetGenerator::GetDeviceLinkFlags(
120 std::string& linkFlags, const std::string& linkLanguage)
122 cmGeneratorTarget::DeviceLinkSetter setter(*this->GetGeneratorTarget());
124 std::vector<std::string> linkOpts;
125 this->GeneratorTarget->GetLinkOptions(linkOpts, this->GetConfigName(),
127 // LINK_OPTIONS are escaped.
128 this->LocalGenerator->AppendCompileOptions(linkFlags, linkOpts);
131 void cmMakefileTargetGenerator::GetTargetLinkFlags(
132 std::string& flags, const std::string& linkLanguage)
134 this->LocalGenerator->AppendFlags(
135 flags, this->GeneratorTarget->GetSafeProperty("LINK_FLAGS"));
137 std::string linkFlagsConfig =
138 cmStrCat("LINK_FLAGS_", cmSystemTools::UpperCase(this->GetConfigName()));
139 this->LocalGenerator->AppendFlags(
140 flags, this->GeneratorTarget->GetSafeProperty(linkFlagsConfig));
142 std::vector<std::string> opts;
143 this->GeneratorTarget->GetLinkOptions(opts, this->GetConfigName(),
145 // LINK_OPTIONS are escaped.
146 this->LocalGenerator->AppendCompileOptions(flags, opts);
148 this->LocalGenerator->AppendPositionIndependentLinkerFlags(
149 flags, this->GeneratorTarget, this->GetConfigName(), linkLanguage);
152 void cmMakefileTargetGenerator::CreateRuleFile()
154 // Create a directory for this target.
155 this->TargetBuildDirectory =
156 this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
157 this->TargetBuildDirectoryFull =
158 this->LocalGenerator->ConvertToFullPath(this->TargetBuildDirectory);
159 cmSystemTools::MakeDirectory(this->TargetBuildDirectoryFull);
161 // Construct the rule file name.
162 this->BuildFileName = cmStrCat(this->TargetBuildDirectory, "/build.make");
163 this->BuildFileNameFull =
164 cmStrCat(this->TargetBuildDirectoryFull, "/build.make");
166 // Construct the rule file name.
167 this->ProgressFileNameFull =
168 cmStrCat(this->TargetBuildDirectoryFull, "/progress.make");
170 // reset the progress count
171 this->NumberOfProgressActions = 0;
173 // Open the rule file. This should be copy-if-different because the
174 // rules may depend on this file itself.
175 this->BuildFileStream = cm::make_unique<cmGeneratedFileStream>(
176 this->BuildFileNameFull, false,
177 this->GlobalGenerator->GetMakefileEncoding());
178 if (!this->BuildFileStream) {
181 this->BuildFileStream->SetCopyIfDifferent(true);
182 this->LocalGenerator->WriteDisclaimer(*this->BuildFileStream);
183 if (this->GlobalGenerator->AllowDeleteOnError()) {
184 std::vector<std::string> no_depends;
185 std::vector<std::string> no_commands;
186 this->LocalGenerator->WriteMakeRule(
187 *this->BuildFileStream, "Delete rule output on recipe failure.",
188 ".DELETE_ON_ERROR", no_depends, no_commands, false);
190 this->LocalGenerator->WriteSpecialTargetsTop(*this->BuildFileStream);
193 void cmMakefileTargetGenerator::WriteTargetBuildRules()
195 this->GeneratorTarget->CheckCxxModuleStatus(this->GetConfigName());
197 if (this->GeneratorTarget->HaveCxx20ModuleSources()) {
198 this->Makefile->IssueMessage(
199 MessageType::FATAL_ERROR,
200 cmStrCat("The \"", this->GeneratorTarget->GetName(),
201 "\" target contains C++ module sources which are not supported "
202 "by the generator"));
205 // -- Write the custom commands for this target
207 // Evaluates generator expressions and expands prop_value
208 auto evaluatedFiles =
209 [this](const std::string& prop_value) -> std::vector<std::string> {
210 std::vector<std::string> files;
211 cmExpandList(cmGeneratorExpression::Evaluate(
212 prop_value, this->LocalGenerator, this->GetConfigName(),
213 this->GeneratorTarget),
218 // Look for additional files registered for cleaning in this directory.
219 if (cmValue prop_value =
220 this->Makefile->GetProperty("ADDITIONAL_MAKE_CLEAN_FILES")) {
221 std::vector<std::string> const files = evaluatedFiles(*prop_value);
222 this->CleanFiles.insert(files.begin(), files.end());
225 // Look for additional files registered for cleaning in this target.
226 if (cmValue prop_value =
227 this->GeneratorTarget->GetProperty("ADDITIONAL_CLEAN_FILES")) {
228 std::vector<std::string> const files = evaluatedFiles(*prop_value);
229 // For relative path support
230 std::string const& binaryDir =
231 this->LocalGenerator->GetCurrentBinaryDirectory();
232 for (std::string const& cfl : files) {
233 this->CleanFiles.insert(cmSystemTools::CollapseFullPath(cfl, binaryDir));
237 // Look for ISPC extra object files generated by this target
238 auto ispcAdditionalObjs =
239 this->GeneratorTarget->GetGeneratedISPCObjects(this->GetConfigName());
240 for (std::string const& ispcObj : ispcAdditionalObjs) {
241 this->CleanFiles.insert(
242 this->LocalGenerator->MaybeRelativeToCurBinDir(ispcObj));
245 // add custom commands to the clean rules?
246 bool clean = cmIsOff(this->Makefile->GetProperty("CLEAN_NO_CUSTOM"));
248 // First generate the object rule files. Save a list of all object
249 // files for this target.
250 std::vector<cmSourceFile const*> customCommands;
251 this->GeneratorTarget->GetCustomCommands(customCommands,
252 this->GetConfigName());
253 for (cmSourceFile const* sf : customCommands) {
254 if (this->CMP0113New &&
255 !this->LocalGenerator->GetCommandsVisited(this->GeneratorTarget)
260 cmCustomCommandGenerator ccg(*sf->GetCustomCommand(),
261 this->GetConfigName(), this->LocalGenerator);
262 this->GenerateCustomRuleFile(ccg);
264 const std::vector<std::string>& outputs = ccg.GetOutputs();
265 for (std::string const& output : outputs) {
266 this->CleanFiles.insert(
267 this->LocalGenerator->MaybeRelativeToCurBinDir(output));
269 const std::vector<std::string>& byproducts = ccg.GetByproducts();
270 for (std::string const& byproduct : byproducts) {
271 this->CleanFiles.insert(
272 this->LocalGenerator->MaybeRelativeToCurBinDir(byproduct));
277 // Add byproducts from build events to the clean rules
279 std::vector<cmCustomCommand> buildEventCommands =
280 this->GeneratorTarget->GetPreBuildCommands();
282 cm::append(buildEventCommands,
283 this->GeneratorTarget->GetPreLinkCommands());
284 cm::append(buildEventCommands,
285 this->GeneratorTarget->GetPostBuildCommands());
287 for (const auto& be : buildEventCommands) {
288 cmCustomCommandGenerator beg(be, this->GetConfigName(),
289 this->LocalGenerator);
290 const std::vector<std::string>& byproducts = beg.GetByproducts();
291 for (std::string const& byproduct : byproducts) {
292 this->CleanFiles.insert(
293 this->LocalGenerator->MaybeRelativeToCurBinDir(byproduct));
297 std::vector<cmSourceFile const*> headerSources;
298 this->GeneratorTarget->GetHeaderSources(headerSources,
299 this->GetConfigName());
300 this->OSXBundleGenerator->GenerateMacOSXContentStatements(
301 headerSources, this->MacOSXContentGenerator.get(), this->GetConfigName());
302 std::vector<cmSourceFile const*> extraSources;
303 this->GeneratorTarget->GetExtraSources(extraSources, this->GetConfigName());
304 this->OSXBundleGenerator->GenerateMacOSXContentStatements(
305 extraSources, this->MacOSXContentGenerator.get(), this->GetConfigName());
306 cmValue pchExtension = this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION");
307 std::vector<cmSourceFile const*> externalObjects;
308 this->GeneratorTarget->GetExternalObjects(externalObjects,
309 this->GetConfigName());
310 for (cmSourceFile const* sf : externalObjects) {
311 auto const& objectFileName = sf->GetFullPath();
312 if (!cmHasSuffix(objectFileName, pchExtension)) {
313 this->ExternalObjects.push_back(objectFileName);
317 std::map<std::string, std::string> file_set_map;
319 auto const* tgt = this->GeneratorTarget->Target;
320 for (auto const& name : tgt->GetAllFileSetNames()) {
321 auto const* file_set = tgt->GetFileSet(name);
323 this->Makefile->IssueMessage(
324 MessageType::INTERNAL_ERROR,
325 cmStrCat("Target \"", tgt->GetName(),
326 "\" is tracked to have file set \"", name,
327 "\", but it was not found."));
331 auto fileEntries = file_set->CompileFileEntries();
332 auto directoryEntries = file_set->CompileDirectoryEntries();
333 auto directories = file_set->EvaluateDirectoryEntries(
334 directoryEntries, this->LocalGenerator, this->GetConfigName(),
335 this->GeneratorTarget);
337 std::map<std::string, std::vector<std::string>> files;
338 for (auto const& entry : fileEntries) {
339 file_set->EvaluateFileEntry(directories, files, entry,
340 this->LocalGenerator, this->GetConfigName(),
341 this->GeneratorTarget);
344 for (auto const& it : files) {
345 for (auto const& filename : it.second) {
346 file_set_map[filename] = file_set->GetType();
351 std::vector<cmSourceFile const*> objectSources;
352 this->GeneratorTarget->GetObjectSources(objectSources,
353 this->GetConfigName());
355 // validate that all languages requested are enabled.
356 std::set<std::string> requiredLangs;
357 if (this->HaveRequiredLanguages(objectSources, requiredLangs)) {
358 for (cmSourceFile const* sf : objectSources) {
359 // Generate this object file's rule file.
360 this->WriteObjectRuleFiles(*sf);
364 for (cmSourceFile const* sf : objectSources) {
365 auto const& path = sf->GetFullPath();
366 auto const it = file_set_map.find(path);
367 if (it != file_set_map.end()) {
368 auto const& file_set_type = it->second;
369 if (file_set_type == "CXX_MODULES"_s ||
370 file_set_type == "CXX_MODULE_HEADER_UNITS"_s) {
371 if (sf->GetLanguage() != "CXX"_s) {
372 this->Makefile->IssueMessage(
373 MessageType::FATAL_ERROR,
375 "Target \"", tgt->GetName(), "\" contains the source\n ", path,
376 "\nin a file set of type \"", file_set_type,
377 R"(" but the source is not classified as a "CXX" source.)"));
384 void cmMakefileTargetGenerator::WriteCommonCodeRules()
386 const char* root = (this->Makefile->IsOn("CMAKE_MAKE_INCLUDE_FROM_ROOT")
387 ? "$(CMAKE_BINARY_DIR)/"
390 // Include the dependencies for the target.
391 std::string dependFileNameFull =
392 cmStrCat(this->TargetBuildDirectoryFull, "/depend.make");
393 *this->BuildFileStream
394 << "# Include any dependencies generated for this target.\n"
395 << this->GlobalGenerator->IncludeDirective << " " << root
396 << cmSystemTools::ConvertToOutputPath(
397 this->LocalGenerator->MaybeRelativeToTopBinDir(dependFileNameFull))
400 // Scan any custom commands to check if DEPFILE option is specified
401 bool ccGenerateDeps = false;
402 std::vector<cmSourceFile const*> customCommands;
403 this->GeneratorTarget->GetCustomCommands(customCommands,
404 this->GetConfigName());
405 for (cmSourceFile const* sf : customCommands) {
406 if (!sf->GetCustomCommand()->GetDepfile().empty()) {
407 ccGenerateDeps = true;
412 std::string depsUseCompiler = "CMAKE_DEPENDS_USE_COMPILER";
413 bool compilerGenerateDeps =
414 this->GlobalGenerator->SupportsCompilerDependencies() &&
415 (!this->Makefile->IsDefinitionSet(depsUseCompiler) ||
416 this->Makefile->IsOn(depsUseCompiler));
418 if (compilerGenerateDeps || ccGenerateDeps) {
419 std::string compilerDependFile =
420 cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.make");
421 *this->BuildFileStream << "# Include any dependencies generated by the "
422 "compiler for this target.\n"
423 << this->GlobalGenerator->IncludeDirective << " "
425 << cmSystemTools::ConvertToOutputPath(
426 this->LocalGenerator->MaybeRelativeToTopBinDir(
430 // Write an empty dependency file.
431 cmGeneratedFileStream depFileStream(
432 compilerDependFile, false, this->GlobalGenerator->GetMakefileEncoding());
433 depFileStream << "# Empty compiler generated dependencies file for "
434 << this->GeneratorTarget->GetName() << ".\n"
435 << "# This may be replaced when dependencies are built.\n";
436 // remove internal dependency file
437 cmSystemTools::RemoveFile(
438 cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.internal"));
440 std::string compilerDependTimestamp =
441 cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.ts");
442 if (!cmSystemTools::FileExists(compilerDependTimestamp)) {
443 // Write a dependency timestamp file.
444 cmGeneratedFileStream timestampFileStream(
445 compilerDependTimestamp, false,
446 this->GlobalGenerator->GetMakefileEncoding());
448 << "# CMAKE generated file: DO NOT EDIT!\n"
449 << "# Timestamp file for compiler generated dependencies "
451 << this->GeneratorTarget->GetName() << ".\n";
455 if (compilerGenerateDeps) {
456 // deactivate no longer needed legacy dependency files
457 // Write an empty dependency file.
458 cmGeneratedFileStream legacyDepFileStream(
459 dependFileNameFull, false, this->GlobalGenerator->GetMakefileEncoding());
461 << "# Empty dependencies file for " << this->GeneratorTarget->GetName()
463 << "# This may be replaced when dependencies are built.\n";
464 // remove internal dependency file
465 cmSystemTools::RemoveFile(
466 cmStrCat(this->TargetBuildDirectoryFull, "/depend.internal"));
468 // make sure the depend file exists
469 if (!cmSystemTools::FileExists(dependFileNameFull)) {
470 // Write an empty dependency file.
471 cmGeneratedFileStream depFileStream(
472 dependFileNameFull, false,
473 this->GlobalGenerator->GetMakefileEncoding());
474 depFileStream << "# Empty dependencies file for "
475 << this->GeneratorTarget->GetName() << ".\n"
476 << "# This may be replaced when dependencies are built.\n";
480 if (!this->NoRuleMessages) {
481 // Include the progress variables for the target.
482 *this->BuildFileStream
483 << "# Include the progress variables for this target.\n"
484 << this->GlobalGenerator->IncludeDirective << " " << root
485 << cmSystemTools::ConvertToOutputPath(
486 this->LocalGenerator->MaybeRelativeToTopBinDir(
487 this->ProgressFileNameFull))
491 // Open the flags file. This should be copy-if-different because the
492 // rules may depend on this file itself.
493 this->FlagFileNameFull =
494 cmStrCat(this->TargetBuildDirectoryFull, "/flags.make");
495 this->FlagFileStream = cm::make_unique<cmGeneratedFileStream>(
496 this->FlagFileNameFull, false,
497 this->GlobalGenerator->GetMakefileEncoding());
498 if (!this->FlagFileStream) {
501 this->FlagFileStream->SetCopyIfDifferent(true);
502 this->LocalGenerator->WriteDisclaimer(*this->FlagFileStream);
504 // Include the flags for the target.
505 *this->BuildFileStream
506 << "# Include the compile flags for this target's objects.\n"
507 << this->GlobalGenerator->IncludeDirective << " " << root
508 << cmSystemTools::ConvertToOutputPath(
509 this->LocalGenerator->MaybeRelativeToTopBinDir(
510 this->FlagFileNameFull))
514 void cmMakefileTargetGenerator::WriteTargetLanguageFlags()
516 // write language flags for target
517 std::set<std::string> languages;
518 this->GeneratorTarget->GetLanguages(
519 languages, this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
520 // put the compiler in the rules.make file so that if it changes
522 for (std::string const& language : languages) {
523 std::string compiler = cmStrCat("CMAKE_", language, "_COMPILER");
524 *this->FlagFileStream << "# compile " << language << " with "
525 << this->Makefile->GetSafeDefinition(compiler)
529 bool const escapeOctothorpe = this->GlobalGenerator->CanEscapeOctothorpe();
531 for (std::string const& language : languages) {
532 std::string defines = this->GetDefines(language, this->GetConfigName());
533 std::string includes = this->GetIncludes(language, this->GetConfigName());
534 if (escapeOctothorpe) {
535 // Escape comment characters so they do not terminate assignment.
536 cmSystemTools::ReplaceString(defines, "#", "\\#");
537 cmSystemTools::ReplaceString(includes, "#", "\\#");
539 *this->FlagFileStream << language << "_DEFINES = " << defines << "\n\n";
540 *this->FlagFileStream << language << "_INCLUDES = " << includes << "\n\n";
542 std::vector<std::string> architectures;
543 this->GeneratorTarget->GetAppleArchs(this->GetConfigName(), architectures);
544 architectures.emplace_back();
546 for (const std::string& arch : architectures) {
548 this->GetFlags(language, this->GetConfigName(), arch);
549 if (escapeOctothorpe) {
550 cmSystemTools::ReplaceString(flags, "#", "\\#");
552 *this->FlagFileStream << language << "_FLAGS" << arch << " = " << flags
558 void cmMakefileTargetGenerator::MacOSXContentGeneratorType::operator()(
559 cmSourceFile const& source, const char* pkgloc, const std::string& config)
561 // Skip OS X content when not building a Framework or Bundle.
562 if (!this->Generator->GetGeneratorTarget()->IsBundleOnApple()) {
567 this->Generator->OSXBundleGenerator->InitMacOSXContentDirectory(pkgloc,
570 // Get the input file location.
571 std::string const& input = source.GetFullPath();
573 // Get the output file location.
575 cmStrCat(macdir, '/', cmSystemTools::GetFilenameName(input));
576 this->Generator->CleanFiles.insert(
577 this->Generator->LocalGenerator->MaybeRelativeToCurBinDir(output));
578 output = this->Generator->LocalGenerator->MaybeRelativeToTopBinDir(output);
580 // Create a rule to copy the content into the bundle.
581 std::vector<std::string> depends;
582 std::vector<std::string> commands;
583 depends.push_back(input);
584 std::string copyEcho = cmStrCat("Copying OS X content ", output);
585 this->Generator->LocalGenerator->AppendEcho(
586 commands, copyEcho, cmLocalUnixMakefileGenerator3::EchoBuild);
587 std::string copyCommand =
588 cmStrCat("$(CMAKE_COMMAND) -E copy ",
589 this->Generator->LocalGenerator->ConvertToOutputFormat(
590 input, cmOutputConverter::SHELL),
592 this->Generator->LocalGenerator->ConvertToOutputFormat(
593 output, cmOutputConverter::SHELL));
594 commands.push_back(std::move(copyCommand));
595 this->Generator->LocalGenerator->WriteMakeRule(
596 *this->Generator->BuildFileStream, nullptr, output, depends, commands,
598 this->Generator->ExtraFiles.insert(output);
601 void cmMakefileTargetGenerator::WriteObjectRuleFiles(
602 cmSourceFile const& source)
604 // Identify the language of the source file.
605 const std::string& lang = source.GetLanguage();
607 // don't know anything about this file so skip it
611 // Use compiler to generate dependencies, if supported.
612 bool compilerGenerateDeps =
613 this->GlobalGenerator->SupportsCompilerDependencies() &&
614 cmIsOn(this->Makefile->GetDefinition(
615 cmStrCat("CMAKE_", lang, "_DEPENDS_USE_COMPILER")));
616 auto scanner = compilerGenerateDeps ? cmDependencyScannerKind::Compiler
617 : cmDependencyScannerKind::CMake;
619 // Get the full path name of the object file.
620 std::string const& objectName =
621 this->GeneratorTarget->GetObjectName(&source);
623 cmStrCat(this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget),
626 // Avoid generating duplicate rules.
627 if (this->ObjectFiles.find(obj) == this->ObjectFiles.end()) {
628 this->ObjectFiles.insert(obj);
630 std::ostringstream err;
631 err << "Warning: Source file \"" << source.GetFullPath()
632 << "\" is listed multiple times for target \""
633 << this->GeneratorTarget->GetName() << "\".";
634 cmSystemTools::Message(err.str(), "Warning");
638 // Create the directory containing the object file. This may be a
639 // subdirectory under the target's directory.
641 std::string dir = cmSystemTools::GetFilenamePath(obj);
642 cmSystemTools::MakeDirectory(this->LocalGenerator->ConvertToFullPath(dir));
645 // Save this in the target's list of object files.
646 this->Objects.push_back(obj);
647 this->CleanFiles.insert(obj);
649 std::vector<std::string> depends;
651 // The object file should be checked for dependency integrity.
652 std::string objFullPath =
653 cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), '/', obj);
654 objFullPath = cmSystemTools::CollapseFullPath(objFullPath);
655 std::string srcFullPath =
656 cmSystemTools::CollapseFullPath(source.GetFullPath());
657 this->LocalGenerator->AddImplicitDepends(this->GeneratorTarget, lang,
658 objFullPath, srcFullPath, scanner);
660 this->LocalGenerator->AppendRuleDepend(depends,
661 this->FlagFileNameFull.c_str());
662 this->LocalGenerator->AppendRuleDepends(depends,
663 this->FlagFileDepends[lang]);
665 // generate the depend scanning rule
666 this->WriteObjectDependRules(source, depends);
668 std::string config = this->GetConfigName();
669 std::string configUpper = cmSystemTools::UpperCase(config);
671 // Add precompile headers dependencies
672 std::vector<std::string> architectures;
673 this->GeneratorTarget->GetAppleArchs(config, architectures);
674 if (architectures.empty()) {
675 architectures.emplace_back();
678 std::string filterArch;
679 std::unordered_map<std::string, std::string> pchSources;
680 for (const std::string& arch : architectures) {
681 const std::string pchSource =
682 this->GeneratorTarget->GetPchSource(config, lang, arch);
683 if (pchSource == source.GetFullPath()) {
686 if (!pchSource.empty()) {
687 pchSources.insert(std::make_pair(pchSource, arch));
691 if (!pchSources.empty() && !source.GetProperty("SKIP_PRECOMPILE_HEADERS")) {
692 for (const std::string& arch : architectures) {
693 std::string const& pchHeader =
694 this->GeneratorTarget->GetPchHeader(config, lang, arch);
695 depends.push_back(pchHeader);
696 if (pchSources.find(source.GetFullPath()) == pchSources.end()) {
698 this->GeneratorTarget->GetPchFile(config, lang, arch));
700 this->LocalGenerator->AddImplicitDepends(
701 this->GeneratorTarget, lang, objFullPath, pchHeader, scanner);
705 if (lang != "ISPC") {
706 auto const& headers =
707 this->GeneratorTarget->GetGeneratedISPCHeaders(config);
708 if (!headers.empty()) {
709 depends.insert(depends.end(), headers.begin(), headers.end());
713 std::string relativeObj =
714 cmStrCat(this->LocalGenerator->GetHomeRelativeOutputPath(), obj);
715 // Write the build rule.
717 // Build the set of compiler flags.
720 // Explicitly add the explicit language flag before any other flag
721 // so user flags can override it.
722 this->GeneratorTarget->AddExplicitLanguageFlags(flags, source);
724 // Add language-specific flags.
725 std::string langFlags = cmStrCat("$(", lang, "_FLAGS", filterArch, ")");
726 this->LocalGenerator->AppendFlags(flags, langFlags);
728 cmGeneratorExpressionInterpreter genexInterpreter(
729 this->LocalGenerator, config, this->GeneratorTarget, lang);
731 // Add Fortran format flags.
732 if (lang == "Fortran") {
733 this->AppendFortranFormatFlags(flags, source);
734 this->AppendFortranPreprocessFlags(flags, source);
737 std::string ispcHeaderRelative;
738 std::string ispcHeaderForShell;
739 if (lang == "ISPC") {
740 std::string ispcSource =
741 cmSystemTools::GetFilenameWithoutLastExtension(objectName);
742 ispcSource = cmSystemTools::GetFilenameWithoutLastExtension(ispcSource);
744 cmValue ispcSuffixProp =
745 this->GeneratorTarget->GetProperty("ISPC_HEADER_SUFFIX");
746 assert(ispcSuffixProp);
748 std::string directory = this->GeneratorTarget->GetObjectDirectory(config);
750 this->GeneratorTarget->GetProperty("ISPC_HEADER_DIRECTORY")) {
752 cmStrCat(this->LocalGenerator->GetBinaryDirectory(), '/', *prop);
754 ispcHeaderRelative = cmStrCat(directory, '/', ispcSource, *ispcSuffixProp);
755 ispcHeaderForShell = this->LocalGenerator->ConvertToOutputFormat(
756 ispcHeaderRelative, cmOutputConverter::SHELL);
759 // Add flags from source file properties.
760 const std::string COMPILE_FLAGS("COMPILE_FLAGS");
761 if (cmValue cflags = source.GetProperty(COMPILE_FLAGS)) {
762 const std::string& evaluatedFlags =
763 genexInterpreter.Evaluate(*cflags, COMPILE_FLAGS);
764 this->LocalGenerator->AppendFlags(flags, evaluatedFlags);
765 *this->FlagFileStream << "# Custom flags: " << relativeObj
766 << "_FLAGS = " << evaluatedFlags << "\n"
770 const std::string COMPILE_OPTIONS("COMPILE_OPTIONS");
771 if (cmValue coptions = source.GetProperty(COMPILE_OPTIONS)) {
772 const std::string& evaluatedOptions =
773 genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS);
774 this->LocalGenerator->AppendCompileOptions(flags, evaluatedOptions);
775 *this->FlagFileStream << "# Custom options: " << relativeObj
776 << "_OPTIONS = " << evaluatedOptions << "\n"
780 // Add precompile headers compile options.
781 if (!pchSources.empty() && !source.GetProperty("SKIP_PRECOMPILE_HEADERS")) {
782 std::string pchOptions;
783 auto pchIt = pchSources.find(source.GetFullPath());
784 if (pchIt != pchSources.end()) {
785 pchOptions = this->GeneratorTarget->GetPchCreateCompileOptions(
786 config, lang, pchIt->second);
789 this->GeneratorTarget->GetPchUseCompileOptions(config, lang);
792 const std::string& evaluatedFlags =
793 genexInterpreter.Evaluate(pchOptions, COMPILE_OPTIONS);
795 this->LocalGenerator->AppendCompileOptions(flags, evaluatedFlags);
796 *this->FlagFileStream << "# PCH options: " << relativeObj
797 << "_OPTIONS = " << evaluatedFlags << "\n"
801 // Add include directories from source file properties.
802 std::vector<std::string> includes;
804 const std::string INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES");
805 if (cmValue cincludes = source.GetProperty(INCLUDE_DIRECTORIES)) {
806 const std::string& evaluatedIncludes =
807 genexInterpreter.Evaluate(*cincludes, INCLUDE_DIRECTORIES);
808 this->LocalGenerator->AppendIncludeDirectories(includes, evaluatedIncludes,
810 *this->FlagFileStream << "# Custom include directories: " << relativeObj
811 << "_INCLUDE_DIRECTORIES = " << evaluatedIncludes
816 // Add language-specific defines.
817 std::set<std::string> defines;
819 // Add source-specific preprocessor definitions.
820 const std::string COMPILE_DEFINITIONS("COMPILE_DEFINITIONS");
821 if (cmValue compile_defs = source.GetProperty(COMPILE_DEFINITIONS)) {
822 const std::string& evaluatedDefs =
823 genexInterpreter.Evaluate(*compile_defs, COMPILE_DEFINITIONS);
824 this->LocalGenerator->AppendDefines(defines, evaluatedDefs);
825 *this->FlagFileStream << "# Custom defines: " << relativeObj
826 << "_DEFINES = " << evaluatedDefs << "\n"
829 std::string defPropName = cmStrCat("COMPILE_DEFINITIONS_", configUpper);
830 if (cmValue config_compile_defs = source.GetProperty(defPropName)) {
831 const std::string& evaluatedDefs =
832 genexInterpreter.Evaluate(*config_compile_defs, COMPILE_DEFINITIONS);
833 this->LocalGenerator->AppendDefines(defines, evaluatedDefs);
834 *this->FlagFileStream << "# Custom defines: " << relativeObj << "_DEFINES_"
835 << configUpper << " = " << evaluatedDefs << "\n"
839 // Get the output paths for source and object files.
840 std::string sourceFile = this->LocalGenerator->ConvertToOutputFormat(
841 source.GetFullPath(), cmOutputConverter::SHELL);
843 // Construct the build message.
844 std::vector<std::string> no_depends;
845 std::vector<std::string> commands;
847 // add in a progress call if needed
848 this->NumberOfProgressActions++;
850 if (!this->NoRuleMessages) {
851 cmLocalUnixMakefileGenerator3::EchoProgress progress;
852 this->MakeEchoProgress(progress);
853 std::string buildEcho =
854 cmStrCat("Building ", lang, " object ", relativeObj);
855 this->LocalGenerator->AppendEcho(commands, buildEcho,
856 cmLocalUnixMakefileGenerator3::EchoBuild,
860 std::string targetOutPathReal;
861 std::string targetOutPathPDB;
862 std::string targetOutPathCompilePDB;
864 std::string targetFullPathReal;
865 std::string targetFullPathPDB;
866 std::string targetFullPathCompilePDB =
867 this->ComputeTargetCompilePDB(this->GetConfigName());
868 if (this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE ||
869 this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY ||
870 this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY ||
871 this->GeneratorTarget->GetType() == cmStateEnums::MODULE_LIBRARY) {
872 targetFullPathReal = this->GeneratorTarget->GetFullPath(
873 this->GetConfigName(), cmStateEnums::RuntimeBinaryArtifact, true);
874 targetFullPathPDB = cmStrCat(
875 this->GeneratorTarget->GetPDBDirectory(this->GetConfigName()), '/',
876 this->GeneratorTarget->GetPDBName(this->GetConfigName()));
879 targetOutPathReal = this->LocalGenerator->ConvertToOutputFormat(
880 this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPathReal),
881 cmOutputConverter::SHELL);
882 targetOutPathPDB = this->LocalGenerator->ConvertToOutputFormat(
883 targetFullPathPDB, cmOutputConverter::SHELL);
884 targetOutPathCompilePDB = this->LocalGenerator->ConvertToOutputFormat(
885 this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPathCompilePDB),
886 cmOutputConverter::SHELL);
888 if (this->LocalGenerator->IsMinGWMake() &&
889 cmHasLiteralSuffix(targetOutPathCompilePDB, "\\")) {
890 // mingw32-make incorrectly interprets 'a\ b c' as 'a b' and 'c'
891 // (but 'a\ b "c"' as 'a\', 'b', and 'c'!). Workaround this by
892 // avoiding a trailing backslash in the argument.
893 targetOutPathCompilePDB.back() = '/';
896 std::string compilePdbOutputPath =
897 this->GeneratorTarget->GetCompilePDBDirectory(this->GetConfigName());
898 cmSystemTools::MakeDirectory(compilePdbOutputPath);
900 cmRulePlaceholderExpander::RuleVariables vars;
901 vars.CMTargetName = this->GeneratorTarget->GetName().c_str();
903 cmState::GetTargetTypeName(this->GeneratorTarget->GetType()).c_str();
904 vars.Language = lang.c_str();
905 vars.Target = targetOutPathReal.c_str();
906 vars.TargetPDB = targetOutPathPDB.c_str();
907 vars.TargetCompilePDB = targetOutPathCompilePDB.c_str();
908 vars.Source = sourceFile.c_str();
909 std::string shellObj =
910 this->LocalGenerator->ConvertToOutputFormat(obj, cmOutputConverter::SHELL);
911 vars.Object = shellObj.c_str();
912 std::string objectDir = this->GeneratorTarget->GetSupportDirectory();
913 objectDir = this->LocalGenerator->ConvertToOutputFormat(
914 this->LocalGenerator->MaybeRelativeToCurBinDir(objectDir),
915 cmOutputConverter::SHELL);
916 vars.ObjectDir = objectDir.c_str();
917 std::string objectFileDir = cmSystemTools::GetFilenamePath(obj);
918 objectFileDir = this->LocalGenerator->ConvertToOutputFormat(
919 this->LocalGenerator->MaybeRelativeToCurBinDir(objectFileDir),
920 cmOutputConverter::SHELL);
921 vars.ObjectFileDir = objectFileDir.c_str();
922 vars.Flags = flags.c_str();
923 vars.ISPCHeader = ispcHeaderForShell.c_str();
925 std::string definesString = cmStrCat("$(", lang, "_DEFINES)");
927 this->LocalGenerator->JoinDefines(defines, definesString, lang);
929 vars.Defines = definesString.c_str();
931 std::string includesString = this->LocalGenerator->GetIncludeFlags(
932 includes, this->GeneratorTarget, lang, config);
933 this->LocalGenerator->AppendFlags(includesString,
934 "$(" + lang + "_INCLUDES)");
935 vars.Includes = includesString.c_str();
937 std::string dependencyTarget;
938 std::string shellDependencyFile;
939 std::string dependencyTimestamp;
940 if (compilerGenerateDeps) {
941 dependencyTarget = this->LocalGenerator->EscapeForShell(
942 this->LocalGenerator->ConvertToMakefilePath(
943 this->LocalGenerator->MaybeRelativeToTopBinDir(relativeObj)));
944 vars.DependencyTarget = dependencyTarget.c_str();
946 auto depFile = cmStrCat(obj, ".d");
947 shellDependencyFile = this->LocalGenerator->ConvertToOutputFormat(
948 depFile, cmOutputConverter::SHELL);
949 vars.DependencyFile = shellDependencyFile.c_str();
950 this->CleanFiles.insert(depFile);
952 dependencyTimestamp = this->LocalGenerator->MaybeRelativeToTopBinDir(
953 cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.ts"));
956 // At the moment, it is assumed that C, C++, Fortran, and CUDA have both
957 // assembly and preprocessor capabilities. The same is true for the
958 // ability to export compile commands
959 bool lang_has_preprocessor =
960 ((lang == "C") || (lang == "CXX") || (lang == "OBJC") ||
961 (lang == "OBJCXX") || (lang == "Fortran") || (lang == "CUDA") ||
962 lang == "ISPC" || lang == "HIP" || lang == "ASM");
963 bool const lang_has_assembly = lang_has_preprocessor;
964 bool const lang_can_export_cmds = lang_has_preprocessor;
966 std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
967 this->LocalGenerator->CreateRulePlaceholderExpander());
969 // Construct the compile rules.
971 std::string cudaCompileMode;
972 if (lang == "CUDA") {
973 if (this->GeneratorTarget->GetPropertyAsBool(
974 "CUDA_SEPARABLE_COMPILATION")) {
975 const std::string& rdcFlag =
976 this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_RDC_FLAG");
977 cudaCompileMode = cmStrCat(cudaCompileMode, rdcFlag, " ");
979 if (this->GeneratorTarget->GetPropertyAsBool("CUDA_PTX_COMPILATION")) {
980 const std::string& ptxFlag =
981 this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_PTX_FLAG");
982 cudaCompileMode = cmStrCat(cudaCompileMode, ptxFlag);
984 const std::string& wholeFlag =
985 this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_WHOLE_FLAG");
986 cudaCompileMode = cmStrCat(cudaCompileMode, wholeFlag);
988 vars.CudaCompileMode = cudaCompileMode.c_str();
991 std::vector<std::string> compileCommands;
992 const std::string& compileRule = this->Makefile->GetRequiredDefinition(
993 "CMAKE_" + lang + "_COMPILE_OBJECT");
994 cmExpandList(compileRule, compileCommands);
996 if (this->GeneratorTarget->GetPropertyAsBool("EXPORT_COMPILE_COMMANDS") &&
997 lang_can_export_cmds && compileCommands.size() == 1) {
998 std::string compileCommand = compileCommands[0];
1000 // no launcher for CMAKE_EXPORT_COMPILE_COMMANDS
1001 rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator,
1002 compileCommand, vars);
1003 std::string workingDirectory =
1004 this->LocalGenerator->GetCurrentBinaryDirectory();
1005 std::string::size_type lfPos = compileCommand.find(langFlags);
1006 if (lfPos != std::string::npos) {
1007 compileCommand.replace(lfPos, langFlags.size(),
1008 this->GetFlags(lang, this->GetConfigName()));
1010 std::string langDefines = std::string("$(") + lang + "_DEFINES)";
1011 std::string::size_type ldPos = compileCommand.find(langDefines);
1012 if (ldPos != std::string::npos) {
1013 compileCommand.replace(ldPos, langDefines.size(),
1014 this->GetDefines(lang, this->GetConfigName()));
1016 std::string langIncludes = std::string("$(") + lang + "_INCLUDES)";
1017 std::string::size_type liPos = compileCommand.find(langIncludes);
1018 if (liPos != std::string::npos) {
1019 compileCommand.replace(liPos, langIncludes.size(),
1020 this->GetIncludes(lang, this->GetConfigName()));
1023 cmValue eliminate[] = {
1024 this->Makefile->GetDefinition("CMAKE_START_TEMP_FILE"),
1025 this->Makefile->GetDefinition("CMAKE_END_TEMP_FILE")
1027 for (cmValue el : eliminate) {
1029 cmSystemTools::ReplaceString(compileCommand, *el, "");
1033 this->GlobalGenerator->AddCXXCompileCommand(
1034 source.GetFullPath(), workingDirectory, compileCommand);
1037 // See if we need to use a compiler launcher like ccache or distcc
1038 std::string compilerLauncher;
1039 if (!compileCommands.empty() &&
1040 (lang == "C" || lang == "CXX" || lang == "Fortran" || lang == "CUDA" ||
1041 lang == "HIP" || lang == "ISPC" || lang == "OBJC" ||
1042 lang == "OBJCXX")) {
1043 std::string const clauncher_prop = lang + "_COMPILER_LAUNCHER";
1044 cmValue clauncher = this->GeneratorTarget->GetProperty(clauncher_prop);
1045 std::string evaluatedClauncher = cmGeneratorExpression::Evaluate(
1046 *clauncher, this->LocalGenerator, config);
1047 if (!evaluatedClauncher.empty()) {
1048 compilerLauncher = evaluatedClauncher;
1052 // Maybe insert an include-what-you-use runner.
1053 if (!compileCommands.empty() &&
1054 (lang == "C" || lang == "CXX" || lang == "OBJC" || lang == "OBJCXX")) {
1055 std::string const tidy_prop = lang + "_CLANG_TIDY";
1056 cmValue tidy = this->GeneratorTarget->GetProperty(tidy_prop);
1057 cmValue iwyu = nullptr;
1058 cmValue cpplint = nullptr;
1059 cmValue cppcheck = nullptr;
1060 if (lang == "C" || lang == "CXX") {
1061 std::string const iwyu_prop = lang + "_INCLUDE_WHAT_YOU_USE";
1062 iwyu = this->GeneratorTarget->GetProperty(iwyu_prop);
1063 std::string const cpplint_prop = lang + "_CPPLINT";
1064 cpplint = this->GeneratorTarget->GetProperty(cpplint_prop);
1065 std::string const cppcheck_prop = lang + "_CPPCHECK";
1066 cppcheck = this->GeneratorTarget->GetProperty(cppcheck_prop);
1068 if (cmNonempty(iwyu) || cmNonempty(tidy) || cmNonempty(cpplint) ||
1069 cmNonempty(cppcheck)) {
1070 std::string run_iwyu = "$(CMAKE_COMMAND) -E __run_co_compile";
1071 if (!compilerLauncher.empty()) {
1072 // In __run_co_compile case the launcher command is supplied
1073 // via --launcher=<maybe-list> and consumed
1074 run_iwyu += " --launcher=";
1075 run_iwyu += this->LocalGenerator->EscapeForShell(compilerLauncher);
1076 compilerLauncher.clear();
1078 if (cmNonempty(iwyu)) {
1079 run_iwyu += " --iwyu=";
1081 // Only add --driver-mode if it is not already specified, as adding
1082 // it unconditionally might override a user-specified driver-mode
1083 if (iwyu.Get()->find("--driver-mode=") == std::string::npos) {
1084 cmValue p = this->Makefile->GetDefinition(
1085 cmStrCat("CMAKE_", lang, "_INCLUDE_WHAT_YOU_USE_DRIVER_MODE"));
1086 std::string driverMode;
1088 if (cmNonempty(p)) {
1091 driverMode = lang == "C" ? "gcc" : "g++";
1094 run_iwyu += this->LocalGenerator->EscapeForShell(
1095 cmStrCat(*iwyu, ";--driver-mode=", driverMode));
1097 run_iwyu += this->LocalGenerator->EscapeForShell(*iwyu);
1100 if (cmNonempty(tidy)) {
1101 run_iwyu += " --tidy=";
1102 cmValue p = this->Makefile->GetDefinition("CMAKE_" + lang +
1103 "_CLANG_TIDY_DRIVER_MODE");
1104 std::string driverMode;
1105 if (cmNonempty(p)) {
1108 driverMode = lang == "C" ? "gcc" : "g++";
1110 run_iwyu += this->LocalGenerator->EscapeForShell(
1111 cmStrCat(*tidy, ";--extra-arg-before=--driver-mode=", driverMode));
1113 if (cmNonempty(cpplint)) {
1114 run_iwyu += " --cpplint=";
1115 run_iwyu += this->LocalGenerator->EscapeForShell(*cpplint);
1117 if (cmNonempty(cppcheck)) {
1118 run_iwyu += " --cppcheck=";
1119 run_iwyu += this->LocalGenerator->EscapeForShell(*cppcheck);
1121 if (cmNonempty(tidy) || (cmNonempty(cpplint)) ||
1122 (cmNonempty(cppcheck))) {
1123 run_iwyu += " --source=";
1124 run_iwyu += sourceFile;
1127 compileCommands.front().insert(0, run_iwyu);
1131 // If compiler launcher was specified and not consumed above, it
1132 // goes to the beginning of the command line.
1133 if (!compileCommands.empty() && !compilerLauncher.empty()) {
1134 std::vector<std::string> args = cmExpandedList(compilerLauncher, true);
1135 if (!args.empty()) {
1136 args[0] = this->LocalGenerator->ConvertToOutputFormat(
1137 args[0], cmOutputConverter::SHELL);
1138 for (std::string& i : cmMakeRange(args.begin() + 1, args.end())) {
1139 i = this->LocalGenerator->EscapeForShell(i);
1142 compileCommands.front().insert(0, cmJoin(args, " ") + " ");
1145 std::string launcher;
1147 cmValue val = this->LocalGenerator->GetRuleLauncher(
1148 this->GeneratorTarget, "RULE_LAUNCH_COMPILE");
1149 if (cmNonempty(val)) {
1150 launcher = cmStrCat(*val, ' ');
1154 std::string flagsWithDeps(flags);
1156 if (compilerGenerateDeps) {
1157 // Injects dependency computation
1158 auto depFlags = this->Makefile->GetSafeDefinition(
1159 cmStrCat("CMAKE_DEPFILE_FLAGS_", lang));
1161 if (!depFlags.empty()) {
1162 // Add dependency flags
1163 rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator,
1165 flagsWithDeps.append(1, ' ');
1166 flagsWithDeps.append(depFlags);
1168 vars.Flags = flagsWithDeps.c_str();
1170 const auto& extraCommands = this->Makefile->GetSafeDefinition(
1171 cmStrCat("CMAKE_", lang, "_DEPENDS_EXTRA_COMMANDS"));
1172 if (!extraCommands.empty()) {
1173 auto commandList = cmExpandedList(extraCommands);
1174 compileCommands.insert(compileCommands.end(), commandList.cbegin(),
1175 commandList.cend());
1178 const auto& depFormat = this->Makefile->GetRequiredDefinition(
1179 cmStrCat("CMAKE_", lang, "_DEPFILE_FORMAT"));
1181 if (depFormat == "msvc"_s) {
1182 // compiler must be launched through a wrapper to pick-up dependencies
1183 std::string depFilter =
1184 "$(CMAKE_COMMAND) -E cmake_cl_compile_depends ";
1185 depFilter += cmStrCat("--dep-file=", shellDependencyFile);
1187 cmStrCat(" --working-dir=",
1188 this->LocalGenerator->ConvertToOutputFormat(
1189 this->LocalGenerator->GetCurrentBinaryDirectory(),
1190 cmOutputConverter::SHELL));
1191 const auto& prefix = this->Makefile->GetSafeDefinition(
1192 cmStrCat("CMAKE_", lang, "_CL_SHOWINCLUDES_PREFIX"));
1193 depFilter += cmStrCat(" --filter-prefix=",
1194 this->LocalGenerator->ConvertToOutputFormat(
1195 prefix, cmOutputConverter::SHELL));
1196 depFilter += " -- ";
1197 compileCommands.front().insert(0, depFilter);
1201 // Expand placeholders in the commands.
1202 for (std::string& compileCommand : compileCommands) {
1203 compileCommand = cmStrCat(launcher, compileCommand);
1204 rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator,
1205 compileCommand, vars);
1208 // Change the command working directory to the local build tree.
1209 this->LocalGenerator->CreateCDCommand(
1210 compileCommands, this->LocalGenerator->GetCurrentBinaryDirectory(),
1211 this->LocalGenerator->GetBinaryDirectory());
1212 cm::append(commands, compileCommands);
1215 // Check for extra outputs created by the compilation.
1216 std::vector<std::string> outputs(1, relativeObj);
1217 if (cmValue extra_outputs_str = source.GetProperty("OBJECT_OUTPUTS")) {
1218 std::string evaluated_outputs = cmGeneratorExpression::Evaluate(
1219 *extra_outputs_str, this->LocalGenerator, config);
1221 if (!evaluated_outputs.empty()) {
1222 // Register these as extra files to clean.
1223 cmExpandList(evaluated_outputs, outputs);
1226 if (!ispcHeaderRelative.empty()) {
1227 // can't move ispcHeader as vars is using it
1228 outputs.emplace_back(ispcHeaderRelative);
1231 if (outputs.size() > 1) {
1232 this->CleanFiles.insert(outputs.begin() + 1, outputs.end());
1235 if (compilerGenerateDeps) {
1236 depends.push_back(dependencyTimestamp);
1240 this->WriteMakeRule(*this->BuildFileStream, nullptr, outputs, depends,
1243 if (compilerGenerateDeps) {
1244 // set back flags without dependency generation
1245 vars.Flags = flags.c_str();
1248 bool do_preprocess_rules = lang_has_preprocessor &&
1249 this->LocalGenerator->GetCreatePreprocessedSourceRules();
1250 bool do_assembly_rules =
1251 lang_has_assembly && this->LocalGenerator->GetCreateAssemblySourceRules();
1252 if (do_preprocess_rules || do_assembly_rules) {
1253 std::vector<std::string> force_depends;
1254 force_depends.emplace_back("cmake_force");
1255 std::string::size_type dot_pos = relativeObj.rfind('.');
1256 std::string relativeObjBase = relativeObj.substr(0, dot_pos);
1257 dot_pos = obj.rfind('.');
1258 std::string objBase = obj.substr(0, dot_pos);
1260 if (do_preprocess_rules) {
1262 std::string relativeObjI = relativeObjBase + ".i";
1263 std::string objI = objBase + ".i";
1265 std::string preprocessEcho =
1266 cmStrCat("Preprocessing ", lang, " source to ", objI);
1267 this->LocalGenerator->AppendEcho(
1268 commands, preprocessEcho, cmLocalUnixMakefileGenerator3::EchoBuild);
1270 std::string preprocessRuleVar =
1271 cmStrCat("CMAKE_", lang, "_CREATE_PREPROCESSED_SOURCE");
1272 if (cmValue preprocessRule =
1273 this->Makefile->GetDefinition(preprocessRuleVar)) {
1274 std::vector<std::string> preprocessCommands =
1275 cmExpandedList(*preprocessRule);
1277 std::string shellObjI = this->LocalGenerator->ConvertToOutputFormat(
1278 objI, cmOutputConverter::SHELL);
1279 vars.PreprocessedSource = shellObjI.c_str();
1281 // Expand placeholders in the commands.
1282 for (std::string& preprocessCommand : preprocessCommands) {
1283 // no launcher for preprocessor commands
1284 rulePlaceholderExpander->ExpandRuleVariables(
1285 this->LocalGenerator, preprocessCommand, vars);
1288 this->LocalGenerator->CreateCDCommand(
1290 this->LocalGenerator->GetCurrentBinaryDirectory(),
1291 this->LocalGenerator->GetBinaryDirectory());
1292 cm::append(commands, preprocessCommands);
1295 cmStrCat("$(CMAKE_COMMAND) -E cmake_unimplemented_variable ",
1297 commands.push_back(std::move(cmd));
1300 this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr,
1301 relativeObjI, force_depends,
1305 if (do_assembly_rules) {
1307 std::string relativeObjS = relativeObjBase + ".s";
1308 std::string objS = objBase + ".s";
1310 std::string assemblyEcho =
1311 cmStrCat("Compiling ", lang, " source to assembly ", objS);
1312 this->LocalGenerator->AppendEcho(
1313 commands, assemblyEcho, cmLocalUnixMakefileGenerator3::EchoBuild);
1315 std::string assemblyRuleVar =
1316 cmStrCat("CMAKE_", lang, "_CREATE_ASSEMBLY_SOURCE");
1317 if (cmValue assemblyRule =
1318 this->Makefile->GetDefinition(assemblyRuleVar)) {
1319 std::vector<std::string> assemblyCommands =
1320 cmExpandedList(*assemblyRule);
1322 std::string shellObjS = this->LocalGenerator->ConvertToOutputFormat(
1323 objS, cmOutputConverter::SHELL);
1324 vars.AssemblySource = shellObjS.c_str();
1326 // Expand placeholders in the commands.
1327 for (std::string& assemblyCommand : assemblyCommands) {
1328 // no launcher for assembly commands
1329 rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator,
1330 assemblyCommand, vars);
1333 this->LocalGenerator->CreateCDCommand(
1334 assemblyCommands, this->LocalGenerator->GetCurrentBinaryDirectory(),
1335 this->LocalGenerator->GetBinaryDirectory());
1336 cm::append(commands, assemblyCommands);
1339 cmStrCat("$(CMAKE_COMMAND) -E cmake_unimplemented_variable ",
1341 commands.push_back(std::move(cmd));
1344 this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr,
1345 relativeObjS, force_depends,
1351 void cmMakefileTargetGenerator::WriteTargetCleanRules()
1353 std::vector<std::string> depends;
1354 std::vector<std::string> commands;
1356 // Construct the clean target name.
1357 std::string cleanTarget = cmStrCat(
1358 this->LocalGenerator->GetRelativeTargetDirectory(this->GeneratorTarget),
1361 // Construct the clean command.
1362 this->LocalGenerator->AppendCleanCommand(commands, this->CleanFiles,
1363 this->GeneratorTarget);
1364 this->LocalGenerator->CreateCDCommand(
1365 commands, this->LocalGenerator->GetCurrentBinaryDirectory(),
1366 this->LocalGenerator->GetBinaryDirectory());
1369 this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr,
1370 cleanTarget, depends, commands, true);
1373 bool cmMakefileTargetGenerator::WriteMakeRule(
1374 std::ostream& os, const char* comment,
1375 const std::vector<std::string>& outputs,
1376 const std::vector<std::string>& depends,
1377 const std::vector<std::string>& commands, bool in_help)
1379 bool symbolic = false;
1380 if (outputs.empty()) {
1384 // Check whether we need to bother checking for a symbolic output.
1385 bool need_symbolic = this->GlobalGenerator->GetNeedSymbolicMark();
1387 // Check whether the first output is marked as symbolic.
1388 if (need_symbolic) {
1389 if (cmSourceFile* sf = this->Makefile->GetSource(outputs[0])) {
1390 symbolic = sf->GetPropertyAsBool("SYMBOLIC");
1394 // We always attach the actual commands to the first output.
1395 this->LocalGenerator->WriteMakeRule(os, comment, outputs[0], depends,
1396 commands, symbolic, in_help);
1398 // For single outputs, we are done.
1399 if (outputs.size() == 1) {
1403 // For multiple outputs, make the extra ones depend on the first one.
1404 std::vector<std::string> const output_depends(1, outputs[0]);
1405 for (std::string const& output : cmMakeRange(outputs).advance(1)) {
1406 // Touch the extra output so "make" knows that it was updated,
1407 // but only if the output was actually created.
1408 std::string const out = this->LocalGenerator->ConvertToOutputFormat(
1409 this->LocalGenerator->MaybeRelativeToTopBinDir(output),
1410 cmOutputConverter::SHELL);
1411 std::vector<std::string> output_commands;
1413 bool o_symbolic = false;
1414 if (need_symbolic) {
1415 if (cmSourceFile* sf = this->Makefile->GetSource(output)) {
1416 o_symbolic = sf->GetPropertyAsBool("SYMBOLIC");
1419 symbolic = symbolic && o_symbolic;
1422 output_commands.push_back("@$(CMAKE_COMMAND) -E touch_nocreate " + out);
1424 this->LocalGenerator->WriteMakeRule(os, nullptr, output, output_depends,
1425 output_commands, o_symbolic, in_help);
1428 // At build time, remove the first output if this one does not exist
1429 // so that "make" will rerun the real commands that create this one.
1430 MultipleOutputPairsType::value_type p(output, outputs[0]);
1431 this->MultipleOutputPairs.insert(p);
1437 void cmMakefileTargetGenerator::WriteTargetDependRules()
1439 // must write the targets depend info file
1441 this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
1442 this->InfoFileNameFull = cmStrCat(dir, "/DependInfo.cmake");
1443 this->InfoFileNameFull =
1444 this->LocalGenerator->ConvertToFullPath(this->InfoFileNameFull);
1445 this->InfoFileStream =
1446 cm::make_unique<cmGeneratedFileStream>(this->InfoFileNameFull);
1447 if (!this->InfoFileStream) {
1450 this->InfoFileStream->SetCopyIfDifferent(true);
1451 this->LocalGenerator->WriteDependLanguageInfo(*this->InfoFileStream,
1452 this->GeneratorTarget);
1454 // Store multiple output pairs in the depend info file.
1455 if (!this->MultipleOutputPairs.empty()) {
1456 /* clang-format off */
1457 *this->InfoFileStream
1459 << "# Pairs of files generated by the same build rule.\n"
1460 << "set(CMAKE_MULTIPLE_OUTPUT_PAIRS\n";
1461 /* clang-format on */
1462 for (auto const& pi : this->MultipleOutputPairs) {
1463 *this->InfoFileStream
1464 << " " << cmOutputConverter::EscapeForCMake(pi.first) << " "
1465 << cmOutputConverter::EscapeForCMake(pi.second) << "\n";
1467 *this->InfoFileStream << " )\n\n";
1470 // Store list of targets linked directly or transitively.
1472 /* clang-format off */
1473 *this->InfoFileStream
1475 << "# Targets to which this target links.\n"
1476 << "set(CMAKE_TARGET_LINKED_INFO_FILES\n";
1477 /* clang-format on */
1478 std::vector<std::string> dirs =
1479 this->GetLinkedTargetDirectories(this->GetConfigName());
1480 for (std::string const& d : dirs) {
1481 *this->InfoFileStream << " \"" << d << "/DependInfo.cmake\"\n";
1483 *this->InfoFileStream << " )\n";
1486 std::string const& working_dir =
1487 this->LocalGenerator->GetCurrentBinaryDirectory();
1489 /* clang-format off */
1490 *this->InfoFileStream
1492 << "# Fortran module output directory.\n"
1493 << "set(CMAKE_Fortran_TARGET_MODULE_DIR \""
1494 << this->GeneratorTarget->GetFortranModuleDirectory(working_dir)
1497 if (this->GeneratorTarget->IsFortranBuildingInstrinsicModules()) {
1498 *this->InfoFileStream
1500 << "# Fortran compiler is building intrinsic modules.\n"
1501 << "set(CMAKE_Fortran_TARGET_BUILDING_INSTRINSIC_MODULES ON) \n";
1503 /* clang-format on */
1505 // and now write the rule to use it
1506 std::vector<std::string> depends;
1507 std::vector<std::string> commands;
1509 // Construct the name of the dependency generation target.
1510 std::string depTarget = cmStrCat(
1511 this->LocalGenerator->GetRelativeTargetDirectory(this->GeneratorTarget),
1514 // Add a command to call CMake to scan dependencies. CMake will
1515 // touch the corresponding depends file after scanning dependencies.
1516 std::ostringstream depCmd;
1517 // TODO: Account for source file properties and directory-level
1518 // definitions when scanning for dependencies.
1519 #if !defined(_WIN32) || defined(__CYGWIN__)
1520 // This platform supports symlinks, so cmSystemTools will translate
1521 // paths. Make sure PWD is set to the original name of the home
1522 // output directory to help cmSystemTools to create the same
1523 // translation table for the dependency scanning process.
1525 << (this->LocalGenerator->ConvertToOutputFormat(
1526 this->LocalGenerator->GetBinaryDirectory(),
1527 cmOutputConverter::SHELL))
1530 // Generate a call this signature:
1532 // cmake -E cmake_depends <generator>
1533 // <home-src-dir> <start-src-dir>
1534 // <home-out-dir> <start-out-dir>
1535 // <dep-info> --color=$(COLOR)
1537 // This gives the dependency scanner enough information to recreate
1538 // the state of our local generator sufficiently for its needs.
1539 depCmd << "$(CMAKE_COMMAND) -E cmake_depends \""
1540 << this->GlobalGenerator->GetName() << "\" "
1541 << this->LocalGenerator->ConvertToOutputFormat(
1542 this->LocalGenerator->GetSourceDirectory(),
1543 cmOutputConverter::SHELL)
1545 << this->LocalGenerator->ConvertToOutputFormat(
1546 this->LocalGenerator->GetCurrentSourceDirectory(),
1547 cmOutputConverter::SHELL)
1549 << this->LocalGenerator->ConvertToOutputFormat(
1550 this->LocalGenerator->GetBinaryDirectory(),
1551 cmOutputConverter::SHELL)
1553 << this->LocalGenerator->ConvertToOutputFormat(
1554 this->LocalGenerator->GetCurrentBinaryDirectory(),
1555 cmOutputConverter::SHELL)
1557 << this->LocalGenerator->ConvertToOutputFormat(
1558 cmSystemTools::CollapseFullPath(this->InfoFileNameFull),
1559 cmOutputConverter::SHELL);
1560 if (this->LocalGenerator->GetColorMakefile()) {
1561 depCmd << " --color=$(COLOR)";
1563 commands.push_back(depCmd.str());
1565 // Make sure all custom command outputs in this target are built.
1566 if (this->CustomCommandDriver == OnDepends) {
1567 this->DriveCustomCommands(depends);
1571 this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr,
1572 depTarget, depends, commands, true);
1575 void cmMakefileTargetGenerator::DriveCustomCommands(
1576 std::vector<std::string>& depends)
1578 // Depend on all custom command outputs.
1579 cm::append(depends, this->CustomCommandOutputs);
1582 void cmMakefileTargetGenerator::WriteObjectDependRules(
1583 cmSourceFile const& source, std::vector<std::string>& depends)
1585 // Create the list of dependencies known at cmake time. These are
1586 // shared between the object file and dependency scanning rule.
1587 depends.push_back(source.GetFullPath());
1588 if (cmValue objectDeps = source.GetProperty("OBJECT_DEPENDS")) {
1589 cmExpandList(*objectDeps, depends);
1593 void cmMakefileTargetGenerator::WriteDeviceLinkRule(
1594 std::vector<std::string>& commands, const std::string& output)
1596 std::string architecturesStr =
1597 this->GeneratorTarget->GetSafeProperty("CUDA_ARCHITECTURES");
1599 if (cmIsOff(architecturesStr)) {
1600 this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
1601 "CUDA_SEPARABLE_COMPILATION on Clang "
1602 "requires CUDA_ARCHITECTURES to be set.");
1606 cmLocalUnixMakefileGenerator3* localGen{ this->LocalGenerator };
1607 std::vector<std::string> architectures = cmExpandedList(architecturesStr);
1608 std::string const& relPath = localGen->GetHomeRelativeOutputPath();
1610 // Ensure there are no duplicates.
1611 const std::vector<std::string> linkDeps = [&]() -> std::vector<std::string> {
1612 std::vector<std::string> deps;
1613 this->AppendTargetDepends(deps, true);
1614 this->GeneratorTarget->GetLinkDepends(deps, this->GetConfigName(), "CUDA");
1616 for (std::string const& obj : this->Objects) {
1617 deps.emplace_back(cmStrCat(relPath, obj));
1620 std::unordered_set<std::string> depsSet(deps.begin(), deps.end());
1622 std::copy(depsSet.begin(), depsSet.end(), std::back_inserter(deps));
1626 const std::string objectDir = this->GeneratorTarget->ObjectDirectory;
1627 const std::string relObjectDir =
1628 localGen->MaybeRelativeToCurBinDir(objectDir);
1630 // Construct a list of files associated with this executable that
1631 // may need to be cleaned.
1632 std::vector<std::string> cleanFiles;
1633 cleanFiles.push_back(localGen->MaybeRelativeToCurBinDir(output));
1635 std::string profiles;
1636 std::vector<std::string> fatbinaryDepends;
1637 std::string const registerFile =
1638 cmStrCat(objectDir, "cmake_cuda_register.h");
1640 // Link device code for each architecture.
1641 for (const std::string& architectureKind : architectures) {
1642 std::string registerFileCmd;
1644 // The generated register file contains macros that when expanded
1645 // register the device routines. Because the routines are the same for
1646 // all architectures the register file will be the same too. Thus
1647 // generate it only on the first invocation to reduce overhead.
1648 if (fatbinaryDepends.empty()) {
1649 std::string const registerFileRel =
1650 cmStrCat(relPath, relObjectDir, "cmake_cuda_register.h");
1652 cmStrCat(" --register-link-binaries=", registerFileRel);
1653 cleanFiles.push_back(registerFileRel);
1656 // Clang always generates real code, so strip the specifier.
1657 const std::string architecture =
1658 architectureKind.substr(0, architectureKind.find('-'));
1659 const std::string cubin =
1660 cmStrCat(objectDir, "sm_", architecture, ".cubin");
1662 profiles += cmStrCat(" -im=profile=sm_", architecture, ",file=", cubin);
1663 fatbinaryDepends.emplace_back(cubin);
1665 std::string command = cmStrCat(
1666 this->Makefile->GetRequiredDefinition("CMAKE_CUDA_DEVICE_LINKER"),
1667 " -arch=sm_", architecture, registerFileCmd, " -o=$@ ",
1668 cmJoin(linkDeps, " "));
1670 localGen->WriteMakeRule(*this->BuildFileStream, nullptr, cubin, linkDeps,
1671 { command }, false);
1674 // Combine all architectures into a single fatbinary.
1675 const std::string fatbinaryCommand =
1676 cmStrCat(this->Makefile->GetRequiredDefinition("CMAKE_CUDA_FATBINARY"),
1677 " -64 -cmdline=--compile-only -compress-all -link "
1678 "--embedded-fatbin=$@",
1680 const std::string fatbinaryOutput =
1681 cmStrCat(objectDir, "cmake_cuda_fatbin.h");
1682 const std::string fatbinaryOutputRel =
1683 cmStrCat(relPath, relObjectDir, "cmake_cuda_fatbin.h");
1685 localGen->WriteMakeRule(*this->BuildFileStream, nullptr, fatbinaryOutputRel,
1686 fatbinaryDepends, { fatbinaryCommand }, false);
1688 // Compile the stub that registers the kernels and contains the
1690 cmRulePlaceholderExpander::RuleVariables vars;
1691 vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str();
1693 cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()).c_str();
1695 vars.Language = "CUDA";
1696 vars.Object = output.c_str();
1697 vars.Fatbinary = fatbinaryOutput.c_str();
1698 vars.RegisterFile = registerFile.c_str();
1700 std::string linkFlags;
1701 this->GetDeviceLinkFlags(linkFlags, "CUDA");
1702 vars.LinkFlags = linkFlags.c_str();
1704 std::string flags = this->GetFlags("CUDA", this->GetConfigName());
1705 vars.Flags = flags.c_str();
1707 std::string compileCmd = this->GetLinkRule("CMAKE_CUDA_DEVICE_LINK_COMPILE");
1708 std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
1709 localGen->CreateRulePlaceholderExpander());
1710 rulePlaceholderExpander->ExpandRuleVariables(localGen, compileCmd, vars);
1712 commands.emplace_back(compileCmd);
1713 localGen->WriteMakeRule(*this->BuildFileStream, nullptr, output,
1714 { fatbinaryOutputRel }, commands, false);
1716 // Clean all the possible executable names and symlinks.
1717 this->CleanFiles.insert(cleanFiles.begin(), cleanFiles.end());
1720 void cmMakefileTargetGenerator::GenerateCustomRuleFile(
1721 cmCustomCommandGenerator const& ccg)
1723 // Collect the commands.
1724 std::vector<std::string> commands;
1725 std::string comment = this->LocalGenerator->ConstructComment(ccg);
1726 if (!comment.empty()) {
1727 // add in a progress call if needed
1728 this->NumberOfProgressActions++;
1729 if (!this->NoRuleMessages) {
1730 cmLocalUnixMakefileGenerator3::EchoProgress progress;
1731 this->MakeEchoProgress(progress);
1732 this->LocalGenerator->AppendEcho(
1733 commands, comment, cmLocalUnixMakefileGenerator3::EchoGenerate,
1738 // Now append the actual user-specified commands.
1739 std::ostringstream content;
1740 this->LocalGenerator->AppendCustomCommand(
1741 commands, ccg, this->GeneratorTarget,
1742 this->LocalGenerator->GetBinaryDirectory(), false, &content);
1744 // Collect the dependencies.
1745 std::vector<std::string> depends;
1746 this->LocalGenerator->AppendCustomDepend(depends, ccg);
1748 if (!ccg.GetCC().GetDepfile().empty()) {
1749 // Add dependency over timestamp file for dependencies management
1750 auto dependTimestamp = cmSystemTools::ConvertToOutputPath(
1751 this->LocalGenerator->MaybeRelativeToTopBinDir(
1752 cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.ts")));
1754 depends.push_back(dependTimestamp);
1758 const std::vector<std::string>& outputs = ccg.GetOutputs();
1759 bool symbolic = this->WriteMakeRule(*this->BuildFileStream, nullptr, outputs,
1762 // Symbolic inputs are not expected to exist, so add dummy rules.
1763 if (this->CMP0113New && !depends.empty()) {
1764 std::vector<std::string> no_depends;
1765 std::vector<std::string> no_commands;
1766 for (std::string const& dep : depends) {
1767 if (cmSourceFile* dsf =
1768 this->Makefile->GetSource(dep, cmSourceFileLocationKind::Known)) {
1769 if (dsf->GetPropertyAsBool("SYMBOLIC")) {
1770 this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr,
1771 dep, no_depends, no_commands,
1778 // If the rule has changed make sure the output is rebuilt.
1780 this->GlobalGenerator->AddRuleHash(ccg.GetOutputs(), content.str());
1783 // Setup implicit dependency scanning.
1784 for (auto const& idi : ccg.GetCC().GetImplicitDepends()) {
1785 std::string objFullPath = cmSystemTools::CollapseFullPath(
1786 outputs[0], this->LocalGenerator->GetCurrentBinaryDirectory());
1787 std::string srcFullPath = cmSystemTools::CollapseFullPath(
1788 idi.second, this->LocalGenerator->GetCurrentBinaryDirectory());
1789 this->LocalGenerator->AddImplicitDepends(this->GeneratorTarget, idi.first,
1790 objFullPath, srcFullPath);
1793 // Setup implicit depend for depfile if any
1794 if (!ccg.GetCC().GetDepfile().empty()) {
1795 std::string objFullPath = cmSystemTools::CollapseFullPath(
1796 outputs[0], this->LocalGenerator->GetCurrentBinaryDirectory());
1797 this->LocalGenerator->AddImplicitDepends(
1798 this->GeneratorTarget, "CUSTOM", objFullPath, ccg.GetFullDepfile(),
1799 cmDependencyScannerKind::Compiler);
1802 this->CustomCommandOutputs.insert(outputs.begin(), outputs.end());
1805 void cmMakefileTargetGenerator::MakeEchoProgress(
1806 cmLocalUnixMakefileGenerator3::EchoProgress& progress) const
1809 cmStrCat(this->LocalGenerator->GetBinaryDirectory(), "/CMakeFiles");
1810 std::ostringstream progressArg;
1811 progressArg << "$(CMAKE_PROGRESS_" << this->NumberOfProgressActions << ")";
1812 progress.Arg = progressArg.str();
1815 void cmMakefileTargetGenerator::WriteObjectsVariable(
1816 std::string& variableName, std::string& variableNameExternal,
1817 bool useWatcomQuote)
1819 // Write a make variable assignment that lists all objects for the
1821 variableName = this->LocalGenerator->CreateMakeVariable(
1822 this->GeneratorTarget->GetName(), "_OBJECTS");
1823 *this->BuildFileStream << "# Object files for target "
1824 << this->GeneratorTarget->GetName() << "\n"
1825 << variableName << " =";
1827 const auto& lineContinue = this->GlobalGenerator->LineContinueDirective;
1829 cmValue pchExtension = this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION");
1831 for (std::string const& obj : this->Objects) {
1832 if (cmHasSuffix(obj, pchExtension)) {
1835 *this->BuildFileStream << " " << lineContinue;
1836 *this->BuildFileStream
1837 << cmLocalUnixMakefileGenerator3::ConvertToQuotedOutputPath(
1838 obj, useWatcomQuote);
1840 *this->BuildFileStream << "\n";
1842 // Write a make variable assignment that lists all external objects
1844 variableNameExternal = this->LocalGenerator->CreateMakeVariable(
1845 this->GeneratorTarget->GetName(), "_EXTERNAL_OBJECTS");
1846 /* clang-format off */
1847 *this->BuildFileStream
1849 << "# External object files for target "
1850 << this->GeneratorTarget->GetName() << "\n"
1851 << variableNameExternal << " =";
1852 /* clang-format on */
1853 for (std::string const& obj : this->ExternalObjects) {
1854 object = this->LocalGenerator->MaybeRelativeToCurBinDir(obj);
1855 *this->BuildFileStream << " " << lineContinue;
1856 *this->BuildFileStream
1857 << cmLocalUnixMakefileGenerator3::ConvertToQuotedOutputPath(
1858 obj, useWatcomQuote);
1860 *this->BuildFileStream << "\n"
1864 class cmMakefileTargetGeneratorObjectStrings
1867 cmMakefileTargetGeneratorObjectStrings(std::vector<std::string>& strings,
1868 cmOutputConverter* outputConverter,
1869 cmStateDirectory const& stateDir,
1870 std::string::size_type limit)
1872 , OutputConverter(outputConverter)
1873 , StateDir(stateDir)
1874 , LengthLimit(limit)
1878 void Feed(std::string const& obj)
1880 // Construct the name of the next object.
1881 this->NextObject = this->OutputConverter->ConvertToOutputFormat(
1882 this->OutputConverter->MaybeRelativeToCurBinDir(obj),
1883 cmOutputConverter::RESPONSE);
1885 // Roll over to next string if the limit will be exceeded.
1886 if (this->LengthLimit != std::string::npos &&
1887 (this->CurrentString.length() + 1 + this->NextObject.length() >
1888 this->LengthLimit)) {
1889 this->Strings.push_back(this->CurrentString);
1890 this->CurrentString.clear();
1894 // Separate from previous object.
1895 this->CurrentString += this->Space;
1898 // Append this object.
1899 this->CurrentString += this->NextObject;
1901 void Done() { this->Strings.push_back(this->CurrentString); }
1904 std::vector<std::string>& Strings;
1905 cmOutputConverter* OutputConverter;
1906 cmStateDirectory StateDir;
1907 std::string::size_type LengthLimit;
1908 std::string CurrentString;
1909 std::string NextObject;
1913 void cmMakefileTargetGenerator::WriteObjectsStrings(
1914 std::vector<std::string>& objStrings, std::string::size_type limit)
1916 cmValue pchExtension = this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION");
1918 cmMakefileTargetGeneratorObjectStrings helper(
1919 objStrings, this->LocalGenerator,
1920 this->LocalGenerator->GetStateSnapshot().GetDirectory(), limit);
1921 for (std::string const& obj : this->Objects) {
1922 if (cmHasSuffix(obj, pchExtension)) {
1927 for (std::string const& obj : this->ExternalObjects) {
1930 auto ispcAdditionalObjs =
1931 this->GeneratorTarget->GetGeneratedISPCObjects(this->GetConfigName());
1932 for (std::string const& obj : ispcAdditionalObjs) {
1938 void cmMakefileTargetGenerator::WriteTargetDriverRule(
1939 const std::string& main_output, bool relink)
1941 // Compute the name of the driver target.
1943 this->LocalGenerator->GetRelativeTargetDirectory(this->GeneratorTarget);
1944 std::string buildTargetRuleName =
1945 cmStrCat(dir, relink ? "/preinstall" : "/build");
1946 buildTargetRuleName =
1947 this->LocalGenerator->MaybeRelativeToTopBinDir(buildTargetRuleName);
1949 // Build the list of target outputs to drive.
1950 std::vector<std::string> depends;
1951 depends.push_back(main_output);
1953 const char* comment = nullptr;
1955 // Setup the comment for the preinstall driver.
1956 comment = "Rule to relink during preinstall.";
1958 // Setup the comment for the main build driver.
1959 comment = "Rule to build all files generated by this target.";
1961 // Make sure all custom command outputs in this target are built.
1962 if (this->CustomCommandDriver == OnBuild) {
1963 this->DriveCustomCommands(depends);
1966 // Make sure the extra files are built.
1967 cm::append(depends, this->ExtraFiles);
1970 // Write the driver rule.
1971 std::vector<std::string> no_commands;
1972 this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, comment,
1973 buildTargetRuleName, depends,
1977 void cmMakefileTargetGenerator::AppendTargetDepends(
1978 std::vector<std::string>& depends, bool ignoreType)
1980 // Static libraries never depend on anything for linking.
1981 if (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY &&
1986 // Loop over all library dependencies.
1987 const std::string& cfg = this->GetConfigName();
1988 if (cmComputeLinkInformation* cli =
1989 this->GeneratorTarget->GetLinkInformation(cfg)) {
1990 cm::append(depends, cli->GetDepends());
1994 void cmMakefileTargetGenerator::AppendObjectDepends(
1995 std::vector<std::string>& depends)
1997 // Add dependencies on the compiled object files.
1998 std::string const& relPath =
1999 this->LocalGenerator->GetHomeRelativeOutputPath();
2000 for (std::string const& obj : this->Objects) {
2001 std::string objTarget = cmStrCat(relPath, obj);
2002 depends.push_back(std::move(objTarget));
2005 // Add dependencies on the external object files.
2006 cm::append(depends, this->ExternalObjects);
2008 // Add a dependency on the rule file itself.
2009 this->LocalGenerator->AppendRuleDepend(depends,
2010 this->BuildFileNameFull.c_str());
2013 void cmMakefileTargetGenerator::AppendLinkDepends(
2014 std::vector<std::string>& depends, const std::string& linkLanguage)
2016 this->AppendObjectDepends(depends);
2018 // Add dependencies on targets that must be built first.
2019 this->AppendTargetDepends(depends);
2021 // Add a dependency on the link definitions file, if any.
2022 if (cmGeneratorTarget::ModuleDefinitionInfo const* mdi =
2023 this->GeneratorTarget->GetModuleDefinitionInfo(
2024 this->GetConfigName())) {
2025 for (cmSourceFile const* src : mdi->Sources) {
2026 depends.push_back(src->GetFullPath());
2030 // Add a dependency on user-specified manifest files, if any.
2031 std::vector<cmSourceFile const*> manifest_srcs;
2032 this->GeneratorTarget->GetManifests(manifest_srcs, this->GetConfigName());
2033 for (cmSourceFile const* manifest_src : manifest_srcs) {
2034 depends.push_back(manifest_src->GetFullPath());
2037 // Add user-specified dependencies.
2038 this->GeneratorTarget->GetLinkDepends(depends, this->GetConfigName(),
2042 std::string cmMakefileTargetGenerator::GetLinkRule(
2043 const std::string& linkRuleVar)
2045 std::string linkRule = this->Makefile->GetRequiredDefinition(linkRuleVar);
2046 if (this->GeneratorTarget->HasImplibGNUtoMS(this->GetConfigName())) {
2047 std::string ruleVar =
2049 this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName()),
2051 if (cmValue rule = this->Makefile->GetDefinition(ruleVar)) {
2058 void cmMakefileTargetGenerator::CloseFileStreams()
2060 this->BuildFileStream.reset();
2061 this->InfoFileStream.reset();
2062 this->FlagFileStream.reset();
2065 void cmMakefileTargetGenerator::CreateLinkScript(
2066 const char* name, std::vector<std::string> const& link_commands,
2067 std::vector<std::string>& makefile_commands,
2068 std::vector<std::string>& makefile_depends)
2070 // Create the link script file.
2071 std::string linkScriptName =
2072 cmStrCat(this->TargetBuildDirectoryFull, '/', name);
2073 cmGeneratedFileStream linkScriptStream(linkScriptName);
2074 linkScriptStream.SetCopyIfDifferent(true);
2075 for (std::string const& link_command : link_commands) {
2076 // Do not write out empty commands or commands beginning in the
2078 if (!link_command.empty() && link_command[0] != ':') {
2079 linkScriptStream << link_command << "\n";
2083 // Create the makefile command to invoke the link script.
2084 std::string link_command =
2085 cmStrCat("$(CMAKE_COMMAND) -E cmake_link_script ",
2086 this->LocalGenerator->ConvertToOutputFormat(
2087 this->LocalGenerator->MaybeRelativeToCurBinDir(linkScriptName),
2088 cmOutputConverter::SHELL),
2089 " --verbose=$(VERBOSE)");
2090 makefile_commands.push_back(std::move(link_command));
2091 makefile_depends.push_back(std::move(linkScriptName));
2094 bool cmMakefileTargetGenerator::CheckUseResponseFileForObjects(
2095 std::string const& l) const
2097 // Check for an explicit setting one way or the other.
2098 std::string const responseVar =
2099 "CMAKE_" + l + "_USE_RESPONSE_FILE_FOR_OBJECTS";
2100 if (cmValue val = this->Makefile->GetDefinition(responseVar)) {
2101 if (!val->empty()) {
2106 // Check for a system limit.
2107 if (size_t const limit = cmSystemTools::CalculateCommandLineLengthLimit()) {
2108 // Compute the total length of our list of object files with room
2109 // for argument separation and quoting. This does not convert paths
2110 // relative to CMAKE_CURRENT_BINARY_DIR like the final list will be, so
2111 // the actual list will likely be much shorter than this. However, in
2112 // the worst case all objects will remain as absolute paths.
2114 for (std::string const& obj : this->Objects) {
2115 length += obj.size() + 3;
2117 for (std::string const& ext_obj : this->ExternalObjects) {
2118 length += ext_obj.size() + 3;
2121 // We need to guarantee room for both objects and libraries, so
2122 // if the objects take up more than half then use a response file
2124 if (length > (limit / 2)) {
2129 // We do not need a response file for objects.
2133 bool cmMakefileTargetGenerator::CheckUseResponseFileForLibraries(
2134 std::string const& l) const
2136 // Check for an explicit setting one way or the other.
2137 std::string const responseVar =
2138 "CMAKE_" + l + "_USE_RESPONSE_FILE_FOR_LIBRARIES";
2139 if (cmValue val = this->Makefile->GetDefinition(responseVar)) {
2140 if (!val->empty()) {
2145 // We do not need a response file for libraries.
2149 std::string cmMakefileTargetGenerator::CreateResponseFile(
2150 const std::string& name, std::string const& options,
2151 std::vector<std::string>& makefile_depends)
2153 // FIXME: Find a better way to determine the response file encoding,
2154 // perhaps using tool-specific platform information variables.
2155 // For now, use the makefile encoding as a heuristic.
2156 codecvt::Encoding responseEncoding =
2157 this->GlobalGenerator->GetMakefileEncoding();
2158 // Non-MSVC tooling may not understand a BOM.
2159 if (responseEncoding == codecvt::UTF8_WITH_BOM &&
2160 !this->Makefile->IsOn("MSVC")) {
2161 responseEncoding = codecvt::UTF8;
2164 // Create the response file.
2165 std::string responseFileNameFull =
2166 cmStrCat(this->TargetBuildDirectoryFull, '/', name);
2167 cmGeneratedFileStream responseStream(responseFileNameFull, false,
2169 responseStream.SetCopyIfDifferent(true);
2170 responseStream << options << "\n";
2172 // Add a dependency so the target will rebuild when the set of
2174 makefile_depends.push_back(std::move(responseFileNameFull));
2176 // Construct the name to be used on the command line.
2177 std::string responseFileName =
2178 cmStrCat(this->TargetBuildDirectory, '/', name);
2179 return responseFileName;
2182 std::unique_ptr<cmLinkLineComputer>
2183 cmMakefileTargetGenerator::CreateLinkLineComputer(
2184 cmOutputConverter* outputConverter, cmStateDirectory const& stateDir)
2186 if (this->Makefile->IsOn("MSVC60")) {
2187 return this->GlobalGenerator->CreateMSVC60LinkLineComputer(outputConverter,
2190 return this->GlobalGenerator->CreateLinkLineComputer(outputConverter,
2194 void cmMakefileTargetGenerator::CreateLinkLibs(
2195 cmLinkLineComputer* linkLineComputer, std::string& linkLibs,
2196 bool useResponseFile, std::vector<std::string>& makefile_depends,
2197 ResponseFlagFor responseMode)
2199 std::string frameworkPath;
2200 std::string linkPath;
2201 cmComputeLinkInformation* pcli =
2202 this->GeneratorTarget->GetLinkInformation(this->GetConfigName());
2203 this->LocalGenerator->OutputLinkLibraries(pcli, linkLineComputer, linkLibs,
2204 frameworkPath, linkPath);
2205 linkLibs = frameworkPath + linkPath + linkLibs;
2207 if (useResponseFile &&
2208 linkLibs.find_first_not_of(' ') != std::string::npos) {
2209 // Lookup the response file reference flag.
2210 std::string responseFlag = this->GetResponseFlag(responseMode);
2212 // Create this response file.
2213 std::string responseFileName =
2214 (responseMode == Link) ? "linkLibs.rsp" : "deviceLinkLibs.rsp";
2215 std::string link_rsp =
2216 this->CreateResponseFile(responseFileName, linkLibs, makefile_depends);
2218 // Reference the response file.
2219 linkLibs = cmStrCat(responseFlag,
2220 this->LocalGenerator->ConvertToOutputFormat(
2221 link_rsp, cmOutputConverter::SHELL));
2225 void cmMakefileTargetGenerator::CreateObjectLists(
2226 bool useLinkScript, bool useArchiveRules, bool useResponseFile,
2227 std::string& buildObjs, std::vector<std::string>& makefile_depends,
2228 bool useWatcomQuote, ResponseFlagFor responseMode)
2230 std::string variableName;
2231 std::string variableNameExternal;
2232 this->WriteObjectsVariable(variableName, variableNameExternal,
2234 if (useResponseFile) {
2235 // MSVC response files cannot exceed 128K.
2236 std::string::size_type const responseFileLimit = 131000;
2238 // Construct the individual object list strings.
2239 std::vector<std::string> object_strings;
2240 this->WriteObjectsStrings(object_strings, responseFileLimit);
2242 // Lookup the response file reference flag.
2243 std::string responseFlag = this->GetResponseFlag(responseMode);
2245 // Write a response file for each string.
2246 const char* sep = "";
2247 for (unsigned int i = 0; i < object_strings.size(); ++i) {
2248 // Number the response files.
2249 std::string responseFileName =
2250 (responseMode == Link) ? "objects" : "deviceObjects";
2251 responseFileName += std::to_string(i + 1);
2253 // Create this response file.
2254 std::string objects_rsp = this->CreateResponseFile(
2255 responseFileName, object_strings[i], makefile_depends);
2257 // Separate from previous response file references.
2261 // Reference the response file.
2262 buildObjs += responseFlag;
2263 buildObjs += this->LocalGenerator->ConvertToOutputFormat(
2264 objects_rsp, cmOutputConverter::SHELL);
2266 } else if (useLinkScript) {
2267 if (!useArchiveRules) {
2268 std::vector<std::string> objStrings;
2269 this->WriteObjectsStrings(objStrings);
2270 buildObjs = objStrings[0];
2274 cmStrCat("$(", variableName, ") $(", variableNameExternal, ')');
2278 void cmMakefileTargetGenerator::AddIncludeFlags(std::string& flags,
2279 const std::string& lang,
2280 const std::string& /*config*/)
2282 std::string responseVar =
2283 cmStrCat("CMAKE_", lang, "_USE_RESPONSE_FILE_FOR_INCLUDES");
2284 bool useResponseFile = this->Makefile->IsOn(responseVar);
2286 std::vector<std::string> includes;
2287 this->LocalGenerator->GetIncludeDirectories(includes, this->GeneratorTarget,
2288 lang, this->GetConfigName());
2290 std::string includeFlags = this->LocalGenerator->GetIncludeFlags(
2291 includes, this->GeneratorTarget, lang, this->GetConfigName(),
2293 if (includeFlags.empty()) {
2297 if (useResponseFile) {
2298 std::string const responseFlagVar =
2299 "CMAKE_" + lang + "_RESPONSE_FILE_FLAG";
2300 std::string responseFlag =
2301 this->Makefile->GetSafeDefinition(responseFlagVar);
2302 if (responseFlag.empty()) {
2305 std::string name = cmStrCat("includes_", lang, ".rsp");
2306 std::string arg = std::move(responseFlag) +
2307 this->CreateResponseFile(name, includeFlags,
2308 this->FlagFileDepends[lang]);
2309 this->LocalGenerator->AppendFlags(flags, arg);
2311 this->LocalGenerator->AppendFlags(flags, includeFlags);
2315 void cmMakefileTargetGenerator::GenDefFile(
2316 std::vector<std::string>& real_link_commands)
2318 cmGeneratorTarget::ModuleDefinitionInfo const* mdi =
2319 this->GeneratorTarget->GetModuleDefinitionInfo(this->GetConfigName());
2320 if (!mdi || !mdi->DefFileGenerated) {
2323 std::string cmd = cmSystemTools::GetCMakeCommand();
2325 this->LocalGenerator->ConvertToOutputFormat(cmd, cmOutputConverter::SHELL),
2326 " -E __create_def ",
2327 this->LocalGenerator->ConvertToOutputFormat(
2328 this->LocalGenerator->MaybeRelativeToCurBinDir(mdi->DefFile),
2329 cmOutputConverter::SHELL),
2331 std::string objlist_file = mdi->DefFile + ".objs";
2332 cmd += this->LocalGenerator->ConvertToOutputFormat(
2333 this->LocalGenerator->MaybeRelativeToCurBinDir(objlist_file),
2334 cmOutputConverter::SHELL);
2335 cmValue nm_executable = this->Makefile->GetDefinition("CMAKE_NM");
2336 if (cmNonempty(nm_executable)) {
2338 cmd += this->LocalCommonGenerator->ConvertToOutputFormat(
2339 *nm_executable, cmOutputConverter::SHELL);
2341 real_link_commands.insert(real_link_commands.begin(), cmd);
2342 // create a list of obj files for the -E __create_def to read
2343 cmGeneratedFileStream fout(objlist_file);
2345 if (mdi->WindowsExportAllSymbols) {
2346 for (std::string const& obj : this->Objects) {
2347 if (cmHasLiteralSuffix(obj, ".obj")) {
2348 fout << obj << "\n";
2351 for (std::string const& obj : this->ExternalObjects) {
2352 fout << obj << "\n";
2356 for (cmSourceFile const* src : mdi->Sources) {
2357 fout << src->GetFullPath() << "\n";
2361 std::string cmMakefileTargetGenerator::GetResponseFlag(
2362 ResponseFlagFor mode) const
2364 std::string responseFlag = "@";
2365 std::string responseFlagVar;
2367 auto lang = this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName());
2368 if (mode == cmMakefileTargetGenerator::ResponseFlagFor::Link) {
2369 responseFlagVar = cmStrCat("CMAKE_", lang, "_RESPONSE_FILE_LINK_FLAG");
2370 } else if (mode == cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink) {
2371 responseFlagVar = "CMAKE_CUDA_RESPONSE_FILE_DEVICE_LINK_FLAG";
2374 if (cmValue p = this->Makefile->GetDefinition(responseFlagVar)) {
2377 return responseFlag;