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 "cmGeneratedFileStream.h"
25 #include "cmGeneratorExpression.h"
26 #include "cmGeneratorTarget.h"
27 #include "cmGlobalUnixMakefileGenerator3.h"
28 #include "cmLinkLineComputer.h" // IWYU pragma: keep
29 #include "cmLocalCommonGenerator.h"
30 #include "cmLocalGenerator.h"
31 #include "cmLocalUnixMakefileGenerator3.h"
32 #include "cmMakefile.h"
33 #include "cmMakefileExecutableTargetGenerator.h"
34 #include "cmMakefileLibraryTargetGenerator.h"
35 #include "cmMakefileUtilityTargetGenerator.h"
36 #include "cmMessageType.h"
37 #include "cmOutputConverter.h"
38 #include "cmPolicies.h"
40 #include "cmRulePlaceholderExpander.h"
41 #include "cmSourceFile.h"
42 #include "cmSourceFileLocationKind.h"
44 #include "cmStateDirectory.h"
45 #include "cmStateSnapshot.h"
46 #include "cmStateTypes.h"
47 #include "cmStringAlgorithms.h"
48 #include "cmSystemTools.h"
52 cmMakefileTargetGenerator::cmMakefileTargetGenerator(cmGeneratorTarget* target)
53 : cmCommonTargetGenerator(target)
55 this->CustomCommandDriver = OnBuild;
56 this->LocalGenerator =
57 static_cast<cmLocalUnixMakefileGenerator3*>(target->GetLocalGenerator());
58 this->GlobalGenerator = static_cast<cmGlobalUnixMakefileGenerator3*>(
59 this->LocalGenerator->GetGlobalGenerator());
60 cmake* cm = this->GlobalGenerator->GetCMakeInstance();
61 this->NoRuleMessages = false;
62 if (cmValue ruleStatus =
63 cm->GetState()->GetGlobalProperty("RULE_MESSAGES")) {
64 this->NoRuleMessages = cmIsOff(*ruleStatus);
66 switch (this->GeneratorTarget->GetPolicyStatusCMP0113()) {
67 case cmPolicies::WARN:
70 this->CMP0113New = false;
73 case cmPolicies::REQUIRED_IF_USED:
74 case cmPolicies::REQUIRED_ALWAYS:
75 this->CMP0113New = true;
78 this->MacOSXContentGenerator =
79 cm::make_unique<MacOSXContentGeneratorType>(this);
82 cmMakefileTargetGenerator::~cmMakefileTargetGenerator() = default;
84 std::unique_ptr<cmMakefileTargetGenerator> cmMakefileTargetGenerator::New(
85 cmGeneratorTarget* tgt)
87 std::unique_ptr<cmMakefileTargetGenerator> result;
89 switch (tgt->GetType()) {
90 case cmStateEnums::EXECUTABLE:
91 result = cm::make_unique<cmMakefileExecutableTargetGenerator>(tgt);
93 case cmStateEnums::STATIC_LIBRARY:
94 case cmStateEnums::SHARED_LIBRARY:
95 case cmStateEnums::MODULE_LIBRARY:
96 case cmStateEnums::OBJECT_LIBRARY:
97 result = cm::make_unique<cmMakefileLibraryTargetGenerator>(tgt);
99 case cmStateEnums::INTERFACE_LIBRARY:
100 case cmStateEnums::UTILITY:
101 result = cm::make_unique<cmMakefileUtilityTargetGenerator>(tgt);
105 // break; /* unreachable */
110 std::string cmMakefileTargetGenerator::GetConfigName()
112 auto const& configNames = this->LocalGenerator->GetConfigNames();
113 assert(configNames.size() == 1);
114 return configNames.front();
117 void cmMakefileTargetGenerator::GetDeviceLinkFlags(
118 std::string& linkFlags, const std::string& linkLanguage)
120 cmGeneratorTarget::DeviceLinkSetter setter(*this->GetGeneratorTarget());
122 std::vector<std::string> linkOpts;
123 this->GeneratorTarget->GetLinkOptions(linkOpts, this->GetConfigName(),
125 // LINK_OPTIONS are escaped.
126 this->LocalGenerator->AppendCompileOptions(linkFlags, linkOpts);
129 void cmMakefileTargetGenerator::GetTargetLinkFlags(
130 std::string& flags, const std::string& linkLanguage)
132 this->LocalGenerator->AppendFlags(
133 flags, this->GeneratorTarget->GetSafeProperty("LINK_FLAGS"));
135 std::string linkFlagsConfig =
136 cmStrCat("LINK_FLAGS_", cmSystemTools::UpperCase(this->GetConfigName()));
137 this->LocalGenerator->AppendFlags(
138 flags, this->GeneratorTarget->GetSafeProperty(linkFlagsConfig));
140 std::vector<std::string> opts;
141 this->GeneratorTarget->GetLinkOptions(opts, this->GetConfigName(),
143 // LINK_OPTIONS are escaped.
144 this->LocalGenerator->AppendCompileOptions(flags, opts);
146 this->LocalGenerator->AppendPositionIndependentLinkerFlags(
147 flags, this->GeneratorTarget, this->GetConfigName(), linkLanguage);
150 void cmMakefileTargetGenerator::CreateRuleFile()
152 // Create a directory for this target.
153 this->TargetBuildDirectory =
154 this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
155 this->TargetBuildDirectoryFull =
156 this->LocalGenerator->ConvertToFullPath(this->TargetBuildDirectory);
157 cmSystemTools::MakeDirectory(this->TargetBuildDirectoryFull);
159 // Construct the rule file name.
160 this->BuildFileName = cmStrCat(this->TargetBuildDirectory, "/build.make");
161 this->BuildFileNameFull =
162 cmStrCat(this->TargetBuildDirectoryFull, "/build.make");
164 // Construct the rule file name.
165 this->ProgressFileNameFull =
166 cmStrCat(this->TargetBuildDirectoryFull, "/progress.make");
168 // reset the progress count
169 this->NumberOfProgressActions = 0;
171 // Open the rule file. This should be copy-if-different because the
172 // rules may depend on this file itself.
173 this->BuildFileStream = cm::make_unique<cmGeneratedFileStream>(
174 this->BuildFileNameFull, false,
175 this->GlobalGenerator->GetMakefileEncoding());
176 if (!this->BuildFileStream) {
179 this->BuildFileStream->SetCopyIfDifferent(true);
180 this->LocalGenerator->WriteDisclaimer(*this->BuildFileStream);
181 if (this->GlobalGenerator->AllowDeleteOnError()) {
182 std::vector<std::string> no_depends;
183 std::vector<std::string> no_commands;
184 this->LocalGenerator->WriteMakeRule(
185 *this->BuildFileStream, "Delete rule output on recipe failure.",
186 ".DELETE_ON_ERROR", no_depends, no_commands, false);
188 this->LocalGenerator->WriteSpecialTargetsTop(*this->BuildFileStream);
191 void cmMakefileTargetGenerator::WriteTargetBuildRules()
193 // -- Write the custom commands for this target
195 // Evaluates generator expressions and expands prop_value
196 auto evaluatedFiles =
197 [this](const std::string& prop_value) -> std::vector<std::string> {
198 std::vector<std::string> files;
199 cmExpandList(cmGeneratorExpression::Evaluate(
200 prop_value, this->LocalGenerator, this->GetConfigName(),
201 this->GeneratorTarget),
206 // Look for additional files registered for cleaning in this directory.
207 if (cmValue prop_value =
208 this->Makefile->GetProperty("ADDITIONAL_MAKE_CLEAN_FILES")) {
209 std::vector<std::string> const files = evaluatedFiles(*prop_value);
210 this->CleanFiles.insert(files.begin(), files.end());
213 // Look for additional files registered for cleaning in this target.
214 if (cmValue prop_value =
215 this->GeneratorTarget->GetProperty("ADDITIONAL_CLEAN_FILES")) {
216 std::vector<std::string> const files = evaluatedFiles(*prop_value);
217 // For relative path support
218 std::string const& binaryDir =
219 this->LocalGenerator->GetCurrentBinaryDirectory();
220 for (std::string const& cfl : files) {
221 this->CleanFiles.insert(cmSystemTools::CollapseFullPath(cfl, binaryDir));
225 // Look for ISPC extra object files generated by this target
226 auto ispcAdditionalObjs =
227 this->GeneratorTarget->GetGeneratedISPCObjects(this->GetConfigName());
228 for (std::string const& ispcObj : ispcAdditionalObjs) {
229 this->CleanFiles.insert(
230 this->LocalGenerator->MaybeRelativeToCurBinDir(ispcObj));
233 // add custom commands to the clean rules?
234 bool clean = cmIsOff(this->Makefile->GetProperty("CLEAN_NO_CUSTOM"));
236 // First generate the object rule files. Save a list of all object
237 // files for this target.
238 std::vector<cmSourceFile const*> customCommands;
239 this->GeneratorTarget->GetCustomCommands(customCommands,
240 this->GetConfigName());
241 for (cmSourceFile const* sf : customCommands) {
242 if (this->CMP0113New &&
243 !this->LocalGenerator->GetCommandsVisited(this->GeneratorTarget)
248 cmCustomCommandGenerator ccg(*sf->GetCustomCommand(),
249 this->GetConfigName(), this->LocalGenerator);
250 this->GenerateCustomRuleFile(ccg);
252 const std::vector<std::string>& outputs = ccg.GetOutputs();
253 for (std::string const& output : outputs) {
254 this->CleanFiles.insert(
255 this->LocalGenerator->MaybeRelativeToCurBinDir(output));
257 const std::vector<std::string>& byproducts = ccg.GetByproducts();
258 for (std::string const& byproduct : byproducts) {
259 this->CleanFiles.insert(
260 this->LocalGenerator->MaybeRelativeToCurBinDir(byproduct));
265 // Add byproducts from build events to the clean rules
267 std::vector<cmCustomCommand> buildEventCommands =
268 this->GeneratorTarget->GetPreBuildCommands();
270 cm::append(buildEventCommands,
271 this->GeneratorTarget->GetPreLinkCommands());
272 cm::append(buildEventCommands,
273 this->GeneratorTarget->GetPostBuildCommands());
275 for (const auto& be : buildEventCommands) {
276 cmCustomCommandGenerator beg(be, this->GetConfigName(),
277 this->LocalGenerator);
278 const std::vector<std::string>& byproducts = beg.GetByproducts();
279 for (std::string const& byproduct : byproducts) {
280 this->CleanFiles.insert(
281 this->LocalGenerator->MaybeRelativeToCurBinDir(byproduct));
285 std::vector<cmSourceFile const*> headerSources;
286 this->GeneratorTarget->GetHeaderSources(headerSources,
287 this->GetConfigName());
288 this->OSXBundleGenerator->GenerateMacOSXContentStatements(
289 headerSources, this->MacOSXContentGenerator.get(), this->GetConfigName());
290 std::vector<cmSourceFile const*> extraSources;
291 this->GeneratorTarget->GetExtraSources(extraSources, this->GetConfigName());
292 this->OSXBundleGenerator->GenerateMacOSXContentStatements(
293 extraSources, this->MacOSXContentGenerator.get(), this->GetConfigName());
294 cmValue pchExtension = this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION");
295 std::vector<cmSourceFile const*> externalObjects;
296 this->GeneratorTarget->GetExternalObjects(externalObjects,
297 this->GetConfigName());
298 for (cmSourceFile const* sf : externalObjects) {
299 auto const& objectFileName = sf->GetFullPath();
300 if (!cmHasSuffix(objectFileName, pchExtension)) {
301 this->ExternalObjects.push_back(objectFileName);
305 std::vector<cmSourceFile const*> objectSources;
306 this->GeneratorTarget->GetObjectSources(objectSources,
307 this->GetConfigName());
309 // validate that all languages requested are enabled.
310 std::set<std::string> requiredLangs;
311 if (this->HaveRequiredLanguages(objectSources, requiredLangs)) {
312 for (cmSourceFile const* sf : objectSources) {
313 // Generate this object file's rule file.
314 this->WriteObjectRuleFiles(*sf);
319 void cmMakefileTargetGenerator::WriteCommonCodeRules()
321 const char* root = (this->Makefile->IsOn("CMAKE_MAKE_INCLUDE_FROM_ROOT")
322 ? "$(CMAKE_BINARY_DIR)/"
325 // Include the dependencies for the target.
326 std::string dependFileNameFull =
327 cmStrCat(this->TargetBuildDirectoryFull, "/depend.make");
328 *this->BuildFileStream
329 << "# Include any dependencies generated for this target.\n"
330 << this->GlobalGenerator->IncludeDirective << " " << root
331 << cmSystemTools::ConvertToOutputPath(
332 this->LocalGenerator->MaybeRelativeToTopBinDir(dependFileNameFull))
335 // Scan any custom commands to check if DEPFILE option is specified
336 bool ccGenerateDeps = false;
337 std::vector<cmSourceFile const*> customCommands;
338 this->GeneratorTarget->GetCustomCommands(customCommands,
339 this->GetConfigName());
340 for (cmSourceFile const* sf : customCommands) {
341 if (!sf->GetCustomCommand()->GetDepfile().empty()) {
342 ccGenerateDeps = true;
347 std::string depsUseCompiler = "CMAKE_DEPENDS_USE_COMPILER";
348 bool compilerGenerateDeps =
349 this->GlobalGenerator->SupportsCompilerDependencies() &&
350 (!this->Makefile->IsDefinitionSet(depsUseCompiler) ||
351 this->Makefile->IsOn(depsUseCompiler));
353 if (compilerGenerateDeps || ccGenerateDeps) {
354 std::string compilerDependFile =
355 cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.make");
356 *this->BuildFileStream << "# Include any dependencies generated by the "
357 "compiler for this target.\n"
358 << this->GlobalGenerator->IncludeDirective << " "
360 << cmSystemTools::ConvertToOutputPath(
361 this->LocalGenerator->MaybeRelativeToTopBinDir(
365 // Write an empty dependency file.
366 cmGeneratedFileStream depFileStream(
367 compilerDependFile, false, this->GlobalGenerator->GetMakefileEncoding());
368 depFileStream << "# Empty compiler generated dependencies file for "
369 << this->GeneratorTarget->GetName() << ".\n"
370 << "# This may be replaced when dependencies are built.\n";
371 // remove internal dependency file
372 cmSystemTools::RemoveFile(
373 cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.internal"));
375 std::string compilerDependTimestamp =
376 cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.ts");
377 if (!cmSystemTools::FileExists(compilerDependTimestamp)) {
378 // Write a dependency timestamp file.
379 cmGeneratedFileStream timestampFileStream(
380 compilerDependTimestamp, false,
381 this->GlobalGenerator->GetMakefileEncoding());
383 << "# CMAKE generated file: DO NOT EDIT!\n"
384 << "# Timestamp file for compiler generated dependencies "
386 << this->GeneratorTarget->GetName() << ".\n";
390 if (compilerGenerateDeps) {
391 // deactivate no longer needed legacy dependency files
392 // Write an empty dependency file.
393 cmGeneratedFileStream legacyDepFileStream(
394 dependFileNameFull, false, this->GlobalGenerator->GetMakefileEncoding());
396 << "# Empty dependencies file for " << this->GeneratorTarget->GetName()
398 << "# This may be replaced when dependencies are built.\n";
399 // remove internal dependency file
400 cmSystemTools::RemoveFile(
401 cmStrCat(this->TargetBuildDirectoryFull, "/depend.internal"));
403 // make sure the depend file exists
404 if (!cmSystemTools::FileExists(dependFileNameFull)) {
405 // Write an empty dependency file.
406 cmGeneratedFileStream depFileStream(
407 dependFileNameFull, false,
408 this->GlobalGenerator->GetMakefileEncoding());
409 depFileStream << "# Empty dependencies file for "
410 << this->GeneratorTarget->GetName() << ".\n"
411 << "# This may be replaced when dependencies are built.\n";
415 if (!this->NoRuleMessages) {
416 // Include the progress variables for the target.
417 *this->BuildFileStream
418 << "# Include the progress variables for this target.\n"
419 << this->GlobalGenerator->IncludeDirective << " " << root
420 << cmSystemTools::ConvertToOutputPath(
421 this->LocalGenerator->MaybeRelativeToTopBinDir(
422 this->ProgressFileNameFull))
426 // Open the flags file. This should be copy-if-different because the
427 // rules may depend on this file itself.
428 this->FlagFileNameFull =
429 cmStrCat(this->TargetBuildDirectoryFull, "/flags.make");
430 this->FlagFileStream = cm::make_unique<cmGeneratedFileStream>(
431 this->FlagFileNameFull, false,
432 this->GlobalGenerator->GetMakefileEncoding());
433 if (!this->FlagFileStream) {
436 this->FlagFileStream->SetCopyIfDifferent(true);
437 this->LocalGenerator->WriteDisclaimer(*this->FlagFileStream);
439 // Include the flags for the target.
440 *this->BuildFileStream
441 << "# Include the compile flags for this target's objects.\n"
442 << this->GlobalGenerator->IncludeDirective << " " << root
443 << cmSystemTools::ConvertToOutputPath(
444 this->LocalGenerator->MaybeRelativeToTopBinDir(
445 this->FlagFileNameFull))
449 void cmMakefileTargetGenerator::WriteTargetLanguageFlags()
451 // write language flags for target
452 std::set<std::string> languages;
453 this->GeneratorTarget->GetLanguages(
454 languages, this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"));
455 // put the compiler in the rules.make file so that if it changes
457 for (std::string const& language : languages) {
458 std::string compiler = cmStrCat("CMAKE_", language, "_COMPILER");
459 *this->FlagFileStream << "# compile " << language << " with "
460 << this->Makefile->GetSafeDefinition(compiler)
464 bool const escapeOctothorpe = this->GlobalGenerator->CanEscapeOctothorpe();
466 for (std::string const& language : languages) {
467 std::string defines = this->GetDefines(language, this->GetConfigName());
468 std::string includes = this->GetIncludes(language, this->GetConfigName());
469 if (escapeOctothorpe) {
470 // Escape comment characters so they do not terminate assignment.
471 cmSystemTools::ReplaceString(defines, "#", "\\#");
472 cmSystemTools::ReplaceString(includes, "#", "\\#");
474 *this->FlagFileStream << language << "_DEFINES = " << defines << "\n\n";
475 *this->FlagFileStream << language << "_INCLUDES = " << includes << "\n\n";
477 std::vector<std::string> architectures;
478 this->GeneratorTarget->GetAppleArchs(this->GetConfigName(), architectures);
479 architectures.emplace_back();
481 for (const std::string& arch : architectures) {
483 this->GetFlags(language, this->GetConfigName(), arch);
484 if (escapeOctothorpe) {
485 cmSystemTools::ReplaceString(flags, "#", "\\#");
487 *this->FlagFileStream << language << "_FLAGS" << arch << " = " << flags
493 void cmMakefileTargetGenerator::MacOSXContentGeneratorType::operator()(
494 cmSourceFile const& source, const char* pkgloc, const std::string& config)
496 // Skip OS X content when not building a Framework or Bundle.
497 if (!this->Generator->GetGeneratorTarget()->IsBundleOnApple()) {
502 this->Generator->OSXBundleGenerator->InitMacOSXContentDirectory(pkgloc,
505 // Get the input file location.
506 std::string const& input = source.GetFullPath();
508 // Get the output file location.
510 cmStrCat(macdir, '/', cmSystemTools::GetFilenameName(input));
511 this->Generator->CleanFiles.insert(
512 this->Generator->LocalGenerator->MaybeRelativeToCurBinDir(output));
513 output = this->Generator->LocalGenerator->MaybeRelativeToTopBinDir(output);
515 // Create a rule to copy the content into the bundle.
516 std::vector<std::string> depends;
517 std::vector<std::string> commands;
518 depends.push_back(input);
519 std::string copyEcho = cmStrCat("Copying OS X content ", output);
520 this->Generator->LocalGenerator->AppendEcho(
521 commands, copyEcho, cmLocalUnixMakefileGenerator3::EchoBuild);
522 std::string copyCommand =
523 cmStrCat("$(CMAKE_COMMAND) -E copy ",
524 this->Generator->LocalGenerator->ConvertToOutputFormat(
525 input, cmOutputConverter::SHELL),
527 this->Generator->LocalGenerator->ConvertToOutputFormat(
528 output, cmOutputConverter::SHELL));
529 commands.push_back(std::move(copyCommand));
530 this->Generator->LocalGenerator->WriteMakeRule(
531 *this->Generator->BuildFileStream, nullptr, output, depends, commands,
533 this->Generator->ExtraFiles.insert(output);
536 void cmMakefileTargetGenerator::WriteObjectRuleFiles(
537 cmSourceFile const& source)
539 // Identify the language of the source file.
540 const std::string& lang = source.GetLanguage();
542 // don't know anything about this file so skip it
546 // Use compiler to generate dependencies, if supported.
547 bool compilerGenerateDeps =
548 this->GlobalGenerator->SupportsCompilerDependencies() &&
549 cmIsOn(this->Makefile->GetDefinition(
550 cmStrCat("CMAKE_", lang, "_DEPENDS_USE_COMPILER")));
551 auto scanner = compilerGenerateDeps ? cmDependencyScannerKind::Compiler
552 : cmDependencyScannerKind::CMake;
554 // Get the full path name of the object file.
555 std::string const& objectName =
556 this->GeneratorTarget->GetObjectName(&source);
558 cmStrCat(this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget),
561 // Avoid generating duplicate rules.
562 if (this->ObjectFiles.find(obj) == this->ObjectFiles.end()) {
563 this->ObjectFiles.insert(obj);
565 std::ostringstream err;
566 err << "Warning: Source file \"" << source.GetFullPath()
567 << "\" is listed multiple times for target \""
568 << this->GeneratorTarget->GetName() << "\".";
569 cmSystemTools::Message(err.str(), "Warning");
573 // Create the directory containing the object file. This may be a
574 // subdirectory under the target's directory.
576 std::string dir = cmSystemTools::GetFilenamePath(obj);
577 cmSystemTools::MakeDirectory(this->LocalGenerator->ConvertToFullPath(dir));
580 // Save this in the target's list of object files.
581 this->Objects.push_back(obj);
582 this->CleanFiles.insert(obj);
584 std::vector<std::string> depends;
586 // The object file should be checked for dependency integrity.
587 std::string objFullPath =
588 cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), '/', obj);
589 objFullPath = cmSystemTools::CollapseFullPath(objFullPath);
590 std::string srcFullPath =
591 cmSystemTools::CollapseFullPath(source.GetFullPath());
592 this->LocalGenerator->AddImplicitDepends(this->GeneratorTarget, lang,
593 objFullPath, srcFullPath, scanner);
595 this->LocalGenerator->AppendRuleDepend(depends,
596 this->FlagFileNameFull.c_str());
597 this->LocalGenerator->AppendRuleDepends(depends,
598 this->FlagFileDepends[lang]);
600 // generate the depend scanning rule
601 this->WriteObjectDependRules(source, depends);
603 std::string config = this->GetConfigName();
604 std::string configUpper = cmSystemTools::UpperCase(config);
606 // Add precompile headers dependencies
607 std::vector<std::string> architectures;
608 this->GeneratorTarget->GetAppleArchs(config, architectures);
609 if (architectures.empty()) {
610 architectures.emplace_back();
613 std::string filterArch;
614 std::unordered_map<std::string, std::string> pchSources;
615 for (const std::string& arch : architectures) {
616 const std::string pchSource =
617 this->GeneratorTarget->GetPchSource(config, lang, arch);
618 if (pchSource == source.GetFullPath()) {
621 if (!pchSource.empty()) {
622 pchSources.insert(std::make_pair(pchSource, arch));
626 if (!pchSources.empty() && !source.GetProperty("SKIP_PRECOMPILE_HEADERS")) {
627 for (const std::string& arch : architectures) {
628 std::string const& pchHeader =
629 this->GeneratorTarget->GetPchHeader(config, lang, arch);
630 depends.push_back(pchHeader);
631 if (pchSources.find(source.GetFullPath()) == pchSources.end()) {
633 this->GeneratorTarget->GetPchFile(config, lang, arch));
635 this->LocalGenerator->AddImplicitDepends(
636 this->GeneratorTarget, lang, objFullPath, pchHeader, scanner);
640 if (lang != "ISPC") {
641 auto const& headers =
642 this->GeneratorTarget->GetGeneratedISPCHeaders(config);
643 if (!headers.empty()) {
644 depends.insert(depends.end(), headers.begin(), headers.end());
648 std::string relativeObj =
649 cmStrCat(this->LocalGenerator->GetHomeRelativeOutputPath(), obj);
650 // Write the build rule.
652 // Build the set of compiler flags.
655 // Explicitly add the explicit language flag before any other flag
656 // so user flags can override it.
657 this->GeneratorTarget->AddExplicitLanguageFlags(flags, source);
659 // Add language-specific flags.
660 std::string langFlags = cmStrCat("$(", lang, "_FLAGS", filterArch, ")");
661 this->LocalGenerator->AppendFlags(flags, langFlags);
663 cmGeneratorExpressionInterpreter genexInterpreter(
664 this->LocalGenerator, config, this->GeneratorTarget, lang);
666 // Add Fortran format flags.
667 if (lang == "Fortran") {
668 this->AppendFortranFormatFlags(flags, source);
669 this->AppendFortranPreprocessFlags(flags, source);
672 std::string ispcHeaderRelative;
673 std::string ispcHeaderForShell;
674 if (lang == "ISPC") {
675 std::string ispcSource =
676 cmSystemTools::GetFilenameWithoutLastExtension(objectName);
677 ispcSource = cmSystemTools::GetFilenameWithoutLastExtension(ispcSource);
679 cmValue ispcSuffixProp =
680 this->GeneratorTarget->GetProperty("ISPC_HEADER_SUFFIX");
681 assert(ispcSuffixProp);
683 std::string directory = this->GeneratorTarget->GetObjectDirectory(config);
685 this->GeneratorTarget->GetProperty("ISPC_HEADER_DIRECTORY")) {
687 cmStrCat(this->LocalGenerator->GetBinaryDirectory(), '/', *prop);
689 ispcHeaderRelative = cmStrCat(directory, '/', ispcSource, *ispcSuffixProp);
690 ispcHeaderForShell = this->LocalGenerator->ConvertToOutputFormat(
691 ispcHeaderRelative, cmOutputConverter::SHELL);
694 // Add flags from source file properties.
695 const std::string COMPILE_FLAGS("COMPILE_FLAGS");
696 if (cmValue cflags = source.GetProperty(COMPILE_FLAGS)) {
697 const std::string& evaluatedFlags =
698 genexInterpreter.Evaluate(*cflags, COMPILE_FLAGS);
699 this->LocalGenerator->AppendFlags(flags, evaluatedFlags);
700 *this->FlagFileStream << "# Custom flags: " << relativeObj
701 << "_FLAGS = " << evaluatedFlags << "\n"
705 const std::string COMPILE_OPTIONS("COMPILE_OPTIONS");
706 if (cmValue coptions = source.GetProperty(COMPILE_OPTIONS)) {
707 const std::string& evaluatedOptions =
708 genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS);
709 this->LocalGenerator->AppendCompileOptions(flags, evaluatedOptions);
710 *this->FlagFileStream << "# Custom options: " << relativeObj
711 << "_OPTIONS = " << evaluatedOptions << "\n"
715 // Add precompile headers compile options.
716 if (!pchSources.empty() && !source.GetProperty("SKIP_PRECOMPILE_HEADERS")) {
717 std::string pchOptions;
718 auto pchIt = pchSources.find(source.GetFullPath());
719 if (pchIt != pchSources.end()) {
720 pchOptions = this->GeneratorTarget->GetPchCreateCompileOptions(
721 config, lang, pchIt->second);
724 this->GeneratorTarget->GetPchUseCompileOptions(config, lang);
727 const std::string& evaluatedFlags =
728 genexInterpreter.Evaluate(pchOptions, COMPILE_OPTIONS);
730 this->LocalGenerator->AppendCompileOptions(flags, evaluatedFlags);
731 *this->FlagFileStream << "# PCH options: " << relativeObj
732 << "_OPTIONS = " << evaluatedFlags << "\n"
736 // Add include directories from source file properties.
737 std::vector<std::string> includes;
739 const std::string INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES");
740 if (cmValue cincludes = source.GetProperty(INCLUDE_DIRECTORIES)) {
741 const std::string& evaluatedIncludes =
742 genexInterpreter.Evaluate(*cincludes, INCLUDE_DIRECTORIES);
743 this->LocalGenerator->AppendIncludeDirectories(includes, evaluatedIncludes,
745 *this->FlagFileStream << "# Custom include directories: " << relativeObj
746 << "_INCLUDE_DIRECTORIES = " << evaluatedIncludes
751 // Add language-specific defines.
752 std::set<std::string> defines;
754 // Add source-specific preprocessor definitions.
755 const std::string COMPILE_DEFINITIONS("COMPILE_DEFINITIONS");
756 if (cmValue compile_defs = source.GetProperty(COMPILE_DEFINITIONS)) {
757 const std::string& evaluatedDefs =
758 genexInterpreter.Evaluate(*compile_defs, COMPILE_DEFINITIONS);
759 this->LocalGenerator->AppendDefines(defines, evaluatedDefs);
760 *this->FlagFileStream << "# Custom defines: " << relativeObj
761 << "_DEFINES = " << evaluatedDefs << "\n"
764 std::string defPropName = cmStrCat("COMPILE_DEFINITIONS_", configUpper);
765 if (cmValue config_compile_defs = source.GetProperty(defPropName)) {
766 const std::string& evaluatedDefs =
767 genexInterpreter.Evaluate(*config_compile_defs, COMPILE_DEFINITIONS);
768 this->LocalGenerator->AppendDefines(defines, evaluatedDefs);
769 *this->FlagFileStream << "# Custom defines: " << relativeObj << "_DEFINES_"
770 << configUpper << " = " << evaluatedDefs << "\n"
774 // Get the output paths for source and object files.
775 std::string sourceFile = this->LocalGenerator->ConvertToOutputFormat(
776 source.GetFullPath(), cmOutputConverter::SHELL);
778 // Construct the build message.
779 std::vector<std::string> no_depends;
780 std::vector<std::string> commands;
782 // add in a progress call if needed
783 this->NumberOfProgressActions++;
785 if (!this->NoRuleMessages) {
786 cmLocalUnixMakefileGenerator3::EchoProgress progress;
787 this->MakeEchoProgress(progress);
788 std::string buildEcho =
789 cmStrCat("Building ", lang, " object ", relativeObj);
790 this->LocalGenerator->AppendEcho(commands, buildEcho,
791 cmLocalUnixMakefileGenerator3::EchoBuild,
795 std::string targetOutPathReal;
796 std::string targetOutPathPDB;
797 std::string targetOutPathCompilePDB;
799 std::string targetFullPathReal;
800 std::string targetFullPathPDB;
801 std::string targetFullPathCompilePDB =
802 this->ComputeTargetCompilePDB(this->GetConfigName());
803 if (this->GeneratorTarget->GetType() == cmStateEnums::EXECUTABLE ||
804 this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY ||
805 this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY ||
806 this->GeneratorTarget->GetType() == cmStateEnums::MODULE_LIBRARY) {
807 targetFullPathReal = this->GeneratorTarget->GetFullPath(
808 this->GetConfigName(), cmStateEnums::RuntimeBinaryArtifact, true);
809 targetFullPathPDB = cmStrCat(
810 this->GeneratorTarget->GetPDBDirectory(this->GetConfigName()), '/',
811 this->GeneratorTarget->GetPDBName(this->GetConfigName()));
814 targetOutPathReal = this->LocalGenerator->ConvertToOutputFormat(
815 this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPathReal),
816 cmOutputConverter::SHELL);
817 targetOutPathPDB = this->LocalGenerator->ConvertToOutputFormat(
818 targetFullPathPDB, cmOutputConverter::SHELL);
819 targetOutPathCompilePDB = this->LocalGenerator->ConvertToOutputFormat(
820 this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPathCompilePDB),
821 cmOutputConverter::SHELL);
823 if (this->LocalGenerator->IsMinGWMake() &&
824 cmHasLiteralSuffix(targetOutPathCompilePDB, "\\")) {
825 // mingw32-make incorrectly interprets 'a\ b c' as 'a b' and 'c'
826 // (but 'a\ b "c"' as 'a\', 'b', and 'c'!). Workaround this by
827 // avoiding a trailing backslash in the argument.
828 targetOutPathCompilePDB.back() = '/';
831 std::string compilePdbOutputPath =
832 this->GeneratorTarget->GetCompilePDBDirectory(this->GetConfigName());
833 cmSystemTools::MakeDirectory(compilePdbOutputPath);
835 cmRulePlaceholderExpander::RuleVariables vars;
836 vars.CMTargetName = this->GeneratorTarget->GetName().c_str();
838 cmState::GetTargetTypeName(this->GeneratorTarget->GetType()).c_str();
839 vars.Language = lang.c_str();
840 vars.Target = targetOutPathReal.c_str();
841 vars.TargetPDB = targetOutPathPDB.c_str();
842 vars.TargetCompilePDB = targetOutPathCompilePDB.c_str();
843 vars.Source = sourceFile.c_str();
844 std::string shellObj =
845 this->LocalGenerator->ConvertToOutputFormat(obj, cmOutputConverter::SHELL);
846 vars.Object = shellObj.c_str();
847 std::string objectDir = this->GeneratorTarget->GetSupportDirectory();
848 objectDir = this->LocalGenerator->ConvertToOutputFormat(
849 this->LocalGenerator->MaybeRelativeToCurBinDir(objectDir),
850 cmOutputConverter::SHELL);
851 vars.ObjectDir = objectDir.c_str();
852 std::string objectFileDir = cmSystemTools::GetFilenamePath(obj);
853 objectFileDir = this->LocalGenerator->ConvertToOutputFormat(
854 this->LocalGenerator->MaybeRelativeToCurBinDir(objectFileDir),
855 cmOutputConverter::SHELL);
856 vars.ObjectFileDir = objectFileDir.c_str();
857 vars.Flags = flags.c_str();
858 vars.ISPCHeader = ispcHeaderForShell.c_str();
860 std::string definesString = cmStrCat("$(", lang, "_DEFINES)");
862 this->LocalGenerator->JoinDefines(defines, definesString, lang);
864 vars.Defines = definesString.c_str();
866 std::string includesString = this->LocalGenerator->GetIncludeFlags(
867 includes, this->GeneratorTarget, lang, config);
868 this->LocalGenerator->AppendFlags(includesString,
869 "$(" + lang + "_INCLUDES)");
870 vars.Includes = includesString.c_str();
872 std::string dependencyTarget;
873 std::string shellDependencyFile;
874 std::string dependencyTimestamp;
875 if (compilerGenerateDeps) {
876 dependencyTarget = this->LocalGenerator->EscapeForShell(
877 this->LocalGenerator->ConvertToMakefilePath(
878 this->LocalGenerator->MaybeRelativeToTopBinDir(relativeObj)));
879 vars.DependencyTarget = dependencyTarget.c_str();
881 auto depFile = cmStrCat(obj, ".d");
882 shellDependencyFile = this->LocalGenerator->ConvertToOutputFormat(
883 depFile, cmOutputConverter::SHELL);
884 vars.DependencyFile = shellDependencyFile.c_str();
885 this->CleanFiles.insert(depFile);
887 dependencyTimestamp = this->LocalGenerator->MaybeRelativeToTopBinDir(
888 cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.ts"));
891 // At the moment, it is assumed that C, C++, Fortran, and CUDA have both
892 // assembly and preprocessor capabilities. The same is true for the
893 // ability to export compile commands
894 bool lang_has_preprocessor =
895 ((lang == "C") || (lang == "CXX") || (lang == "OBJC") ||
896 (lang == "OBJCXX") || (lang == "Fortran") || (lang == "CUDA") ||
897 lang == "ISPC" || lang == "HIP" || lang == "ASM");
898 bool const lang_has_assembly = lang_has_preprocessor;
899 bool const lang_can_export_cmds = lang_has_preprocessor;
901 std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
902 this->LocalGenerator->CreateRulePlaceholderExpander());
904 // Construct the compile rules.
906 std::string cudaCompileMode;
907 if (lang == "CUDA") {
908 if (this->GeneratorTarget->GetPropertyAsBool(
909 "CUDA_SEPARABLE_COMPILATION")) {
910 const std::string& rdcFlag =
911 this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_RDC_FLAG");
912 cudaCompileMode = cmStrCat(cudaCompileMode, rdcFlag, " ");
914 if (this->GeneratorTarget->GetPropertyAsBool("CUDA_PTX_COMPILATION")) {
915 const std::string& ptxFlag =
916 this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_PTX_FLAG");
917 cudaCompileMode = cmStrCat(cudaCompileMode, ptxFlag);
919 const std::string& wholeFlag =
920 this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_WHOLE_FLAG");
921 cudaCompileMode = cmStrCat(cudaCompileMode, wholeFlag);
923 vars.CudaCompileMode = cudaCompileMode.c_str();
926 std::vector<std::string> compileCommands;
927 const std::string& compileRule = this->Makefile->GetRequiredDefinition(
928 "CMAKE_" + lang + "_COMPILE_OBJECT");
929 cmExpandList(compileRule, compileCommands);
931 if (this->GeneratorTarget->GetPropertyAsBool("EXPORT_COMPILE_COMMANDS") &&
932 lang_can_export_cmds && compileCommands.size() == 1) {
933 std::string compileCommand = compileCommands[0];
935 // no launcher for CMAKE_EXPORT_COMPILE_COMMANDS
936 rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator,
937 compileCommand, vars);
938 std::string workingDirectory =
939 this->LocalGenerator->GetCurrentBinaryDirectory();
940 std::string::size_type lfPos = compileCommand.find(langFlags);
941 if (lfPos != std::string::npos) {
942 compileCommand.replace(lfPos, langFlags.size(),
943 this->GetFlags(lang, this->GetConfigName()));
945 std::string langDefines = std::string("$(") + lang + "_DEFINES)";
946 std::string::size_type ldPos = compileCommand.find(langDefines);
947 if (ldPos != std::string::npos) {
948 compileCommand.replace(ldPos, langDefines.size(),
949 this->GetDefines(lang, this->GetConfigName()));
951 std::string langIncludes = std::string("$(") + lang + "_INCLUDES)";
952 std::string::size_type liPos = compileCommand.find(langIncludes);
953 if (liPos != std::string::npos) {
954 compileCommand.replace(liPos, langIncludes.size(),
955 this->GetIncludes(lang, this->GetConfigName()));
958 cmValue eliminate[] = {
959 this->Makefile->GetDefinition("CMAKE_START_TEMP_FILE"),
960 this->Makefile->GetDefinition("CMAKE_END_TEMP_FILE")
962 for (cmValue el : eliminate) {
964 cmSystemTools::ReplaceString(compileCommand, *el, "");
968 this->GlobalGenerator->AddCXXCompileCommand(
969 source.GetFullPath(), workingDirectory, compileCommand);
972 // See if we need to use a compiler launcher like ccache or distcc
973 std::string compilerLauncher;
974 if (!compileCommands.empty() &&
975 (lang == "C" || lang == "CXX" || lang == "Fortran" || lang == "CUDA" ||
976 lang == "HIP" || lang == "ISPC" || lang == "OBJC" ||
978 std::string const clauncher_prop = lang + "_COMPILER_LAUNCHER";
979 cmValue clauncher = this->GeneratorTarget->GetProperty(clauncher_prop);
980 if (cmNonempty(clauncher)) {
981 compilerLauncher = *clauncher;
985 // Maybe insert an include-what-you-use runner.
986 if (!compileCommands.empty() &&
987 (lang == "C" || lang == "CXX" || lang == "OBJC" || lang == "OBJCXX")) {
988 std::string const tidy_prop = lang + "_CLANG_TIDY";
989 cmValue tidy = this->GeneratorTarget->GetProperty(tidy_prop);
990 cmValue iwyu = nullptr;
991 cmValue cpplint = nullptr;
992 cmValue cppcheck = nullptr;
993 if (lang == "C" || lang == "CXX") {
994 std::string const iwyu_prop = lang + "_INCLUDE_WHAT_YOU_USE";
995 iwyu = this->GeneratorTarget->GetProperty(iwyu_prop);
996 std::string const cpplint_prop = lang + "_CPPLINT";
997 cpplint = this->GeneratorTarget->GetProperty(cpplint_prop);
998 std::string const cppcheck_prop = lang + "_CPPCHECK";
999 cppcheck = this->GeneratorTarget->GetProperty(cppcheck_prop);
1001 if (cmNonempty(iwyu) || cmNonempty(tidy) || cmNonempty(cpplint) ||
1002 cmNonempty(cppcheck)) {
1003 std::string run_iwyu = "$(CMAKE_COMMAND) -E __run_co_compile";
1004 if (!compilerLauncher.empty()) {
1005 // In __run_co_compile case the launcher command is supplied
1006 // via --launcher=<maybe-list> and consumed
1007 run_iwyu += " --launcher=";
1008 run_iwyu += this->LocalGenerator->EscapeForShell(compilerLauncher);
1009 compilerLauncher.clear();
1011 if (cmNonempty(iwyu)) {
1012 run_iwyu += " --iwyu=";
1014 // Only add --driver-mode if it is not already specified, as adding
1015 // it unconditionally might override a user-specified driver-mode
1016 if (iwyu.Get()->find("--driver-mode=") == std::string::npos) {
1017 cmValue p = this->Makefile->GetDefinition(
1018 cmStrCat("CMAKE_", lang, "_INCLUDE_WHAT_YOU_USE_DRIVER_MODE"));
1019 std::string driverMode;
1021 if (cmNonempty(p)) {
1024 driverMode = lang == "C" ? "gcc" : "g++";
1027 run_iwyu += this->LocalGenerator->EscapeForShell(
1028 cmStrCat(*iwyu, ";--driver-mode=", driverMode));
1030 run_iwyu += this->LocalGenerator->EscapeForShell(*iwyu);
1033 if (cmNonempty(tidy)) {
1034 run_iwyu += " --tidy=";
1035 cmValue p = this->Makefile->GetDefinition("CMAKE_" + lang +
1036 "_CLANG_TIDY_DRIVER_MODE");
1037 std::string driverMode;
1038 if (cmNonempty(p)) {
1041 driverMode = lang == "C" ? "gcc" : "g++";
1043 run_iwyu += this->LocalGenerator->EscapeForShell(
1044 cmStrCat(*tidy, ";--extra-arg-before=--driver-mode=", driverMode));
1046 if (cmNonempty(cpplint)) {
1047 run_iwyu += " --cpplint=";
1048 run_iwyu += this->LocalGenerator->EscapeForShell(*cpplint);
1050 if (cmNonempty(cppcheck)) {
1051 run_iwyu += " --cppcheck=";
1052 run_iwyu += this->LocalGenerator->EscapeForShell(*cppcheck);
1054 if (cmNonempty(tidy) || (cmNonempty(cpplint)) ||
1055 (cmNonempty(cppcheck))) {
1056 run_iwyu += " --source=";
1057 run_iwyu += sourceFile;
1060 compileCommands.front().insert(0, run_iwyu);
1064 // If compiler launcher was specified and not consumed above, it
1065 // goes to the beginning of the command line.
1066 if (!compileCommands.empty() && !compilerLauncher.empty()) {
1067 std::vector<std::string> args = cmExpandedList(compilerLauncher, true);
1068 if (!args.empty()) {
1069 args[0] = this->LocalGenerator->ConvertToOutputFormat(
1070 args[0], cmOutputConverter::SHELL);
1071 for (std::string& i : cmMakeRange(args.begin() + 1, args.end())) {
1072 i = this->LocalGenerator->EscapeForShell(i);
1075 compileCommands.front().insert(0, cmJoin(args, " ") + " ");
1078 std::string launcher;
1080 cmValue val = this->LocalGenerator->GetRuleLauncher(
1081 this->GeneratorTarget, "RULE_LAUNCH_COMPILE");
1082 if (cmNonempty(val)) {
1083 launcher = cmStrCat(*val, ' ');
1087 std::string flagsWithDeps(flags);
1089 if (compilerGenerateDeps) {
1090 // Injects dependency computation
1091 auto depFlags = this->Makefile->GetSafeDefinition(
1092 cmStrCat("CMAKE_DEPFILE_FLAGS_", lang));
1094 if (!depFlags.empty()) {
1095 // Add dependency flags
1096 rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator,
1098 flagsWithDeps.append(1, ' ');
1099 flagsWithDeps.append(depFlags);
1101 vars.Flags = flagsWithDeps.c_str();
1103 const auto& extraCommands = this->Makefile->GetSafeDefinition(
1104 cmStrCat("CMAKE_", lang, "_DEPENDS_EXTRA_COMMANDS"));
1105 if (!extraCommands.empty()) {
1106 auto commandList = cmExpandedList(extraCommands);
1107 compileCommands.insert(compileCommands.end(), commandList.cbegin(),
1108 commandList.cend());
1111 const auto& depFormat = this->Makefile->GetRequiredDefinition(
1112 cmStrCat("CMAKE_", lang, "_DEPFILE_FORMAT"));
1114 if (depFormat == "msvc"_s) {
1115 // compiler must be launched through a wrapper to pick-up dependencies
1116 std::string depFilter =
1117 "$(CMAKE_COMMAND) -E cmake_cl_compile_depends ";
1118 depFilter += cmStrCat("--dep-file=", shellDependencyFile);
1120 cmStrCat(" --working-dir=",
1121 this->LocalGenerator->ConvertToOutputFormat(
1122 this->LocalGenerator->GetCurrentBinaryDirectory(),
1123 cmOutputConverter::SHELL));
1124 const auto& prefix = this->Makefile->GetSafeDefinition(
1125 cmStrCat("CMAKE_", lang, "_CL_SHOWINCLUDES_PREFIX"));
1126 depFilter += cmStrCat(" --filter-prefix=",
1127 this->LocalGenerator->ConvertToOutputFormat(
1128 prefix, cmOutputConverter::SHELL));
1129 depFilter += " -- ";
1130 compileCommands.front().insert(0, depFilter);
1134 // Expand placeholders in the commands.
1135 for (std::string& compileCommand : compileCommands) {
1136 compileCommand = cmStrCat(launcher, compileCommand);
1137 rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator,
1138 compileCommand, vars);
1141 // Change the command working directory to the local build tree.
1142 this->LocalGenerator->CreateCDCommand(
1143 compileCommands, this->LocalGenerator->GetCurrentBinaryDirectory(),
1144 this->LocalGenerator->GetBinaryDirectory());
1145 cm::append(commands, compileCommands);
1148 // Check for extra outputs created by the compilation.
1149 std::vector<std::string> outputs(1, relativeObj);
1150 if (cmValue extra_outputs_str = source.GetProperty("OBJECT_OUTPUTS")) {
1151 std::string evaluated_outputs = cmGeneratorExpression::Evaluate(
1152 *extra_outputs_str, this->LocalGenerator, config);
1154 if (!evaluated_outputs.empty()) {
1155 // Register these as extra files to clean.
1156 cmExpandList(evaluated_outputs, outputs);
1159 if (!ispcHeaderRelative.empty()) {
1160 // can't move ispcHeader as vars is using it
1161 outputs.emplace_back(ispcHeaderRelative);
1164 if (outputs.size() > 1) {
1165 this->CleanFiles.insert(outputs.begin() + 1, outputs.end());
1168 if (compilerGenerateDeps) {
1169 depends.push_back(dependencyTimestamp);
1173 this->WriteMakeRule(*this->BuildFileStream, nullptr, outputs, depends,
1176 if (compilerGenerateDeps) {
1177 // set back flags without dependency generation
1178 vars.Flags = flags.c_str();
1181 bool do_preprocess_rules = lang_has_preprocessor &&
1182 this->LocalGenerator->GetCreatePreprocessedSourceRules();
1183 bool do_assembly_rules =
1184 lang_has_assembly && this->LocalGenerator->GetCreateAssemblySourceRules();
1185 if (do_preprocess_rules || do_assembly_rules) {
1186 std::vector<std::string> force_depends;
1187 force_depends.emplace_back("cmake_force");
1188 std::string::size_type dot_pos = relativeObj.rfind('.');
1189 std::string relativeObjBase = relativeObj.substr(0, dot_pos);
1190 dot_pos = obj.rfind('.');
1191 std::string objBase = obj.substr(0, dot_pos);
1193 if (do_preprocess_rules) {
1195 std::string relativeObjI = relativeObjBase + ".i";
1196 std::string objI = objBase + ".i";
1198 std::string preprocessEcho =
1199 cmStrCat("Preprocessing ", lang, " source to ", objI);
1200 this->LocalGenerator->AppendEcho(
1201 commands, preprocessEcho, cmLocalUnixMakefileGenerator3::EchoBuild);
1203 std::string preprocessRuleVar =
1204 cmStrCat("CMAKE_", lang, "_CREATE_PREPROCESSED_SOURCE");
1205 if (cmValue preprocessRule =
1206 this->Makefile->GetDefinition(preprocessRuleVar)) {
1207 std::vector<std::string> preprocessCommands =
1208 cmExpandedList(*preprocessRule);
1210 std::string shellObjI = this->LocalGenerator->ConvertToOutputFormat(
1211 objI, cmOutputConverter::SHELL);
1212 vars.PreprocessedSource = shellObjI.c_str();
1214 // Expand placeholders in the commands.
1215 for (std::string& preprocessCommand : preprocessCommands) {
1216 // no launcher for preprocessor commands
1217 rulePlaceholderExpander->ExpandRuleVariables(
1218 this->LocalGenerator, preprocessCommand, vars);
1221 this->LocalGenerator->CreateCDCommand(
1223 this->LocalGenerator->GetCurrentBinaryDirectory(),
1224 this->LocalGenerator->GetBinaryDirectory());
1225 cm::append(commands, preprocessCommands);
1228 cmStrCat("$(CMAKE_COMMAND) -E cmake_unimplemented_variable ",
1230 commands.push_back(std::move(cmd));
1233 this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr,
1234 relativeObjI, force_depends,
1238 if (do_assembly_rules) {
1240 std::string relativeObjS = relativeObjBase + ".s";
1241 std::string objS = objBase + ".s";
1243 std::string assemblyEcho =
1244 cmStrCat("Compiling ", lang, " source to assembly ", objS);
1245 this->LocalGenerator->AppendEcho(
1246 commands, assemblyEcho, cmLocalUnixMakefileGenerator3::EchoBuild);
1248 std::string assemblyRuleVar =
1249 cmStrCat("CMAKE_", lang, "_CREATE_ASSEMBLY_SOURCE");
1250 if (cmValue assemblyRule =
1251 this->Makefile->GetDefinition(assemblyRuleVar)) {
1252 std::vector<std::string> assemblyCommands =
1253 cmExpandedList(*assemblyRule);
1255 std::string shellObjS = this->LocalGenerator->ConvertToOutputFormat(
1256 objS, cmOutputConverter::SHELL);
1257 vars.AssemblySource = shellObjS.c_str();
1259 // Expand placeholders in the commands.
1260 for (std::string& assemblyCommand : assemblyCommands) {
1261 // no launcher for assembly commands
1262 rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator,
1263 assemblyCommand, vars);
1266 this->LocalGenerator->CreateCDCommand(
1267 assemblyCommands, this->LocalGenerator->GetCurrentBinaryDirectory(),
1268 this->LocalGenerator->GetBinaryDirectory());
1269 cm::append(commands, assemblyCommands);
1272 cmStrCat("$(CMAKE_COMMAND) -E cmake_unimplemented_variable ",
1274 commands.push_back(std::move(cmd));
1277 this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr,
1278 relativeObjS, force_depends,
1284 void cmMakefileTargetGenerator::WriteTargetCleanRules()
1286 std::vector<std::string> depends;
1287 std::vector<std::string> commands;
1289 // Construct the clean target name.
1290 std::string cleanTarget = cmStrCat(
1291 this->LocalGenerator->GetRelativeTargetDirectory(this->GeneratorTarget),
1294 // Construct the clean command.
1295 this->LocalGenerator->AppendCleanCommand(commands, this->CleanFiles,
1296 this->GeneratorTarget);
1297 this->LocalGenerator->CreateCDCommand(
1298 commands, this->LocalGenerator->GetCurrentBinaryDirectory(),
1299 this->LocalGenerator->GetBinaryDirectory());
1302 this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr,
1303 cleanTarget, depends, commands, true);
1306 bool cmMakefileTargetGenerator::WriteMakeRule(
1307 std::ostream& os, const char* comment,
1308 const std::vector<std::string>& outputs,
1309 const std::vector<std::string>& depends,
1310 const std::vector<std::string>& commands, bool in_help)
1312 bool symbolic = false;
1313 if (outputs.empty()) {
1317 // Check whether we need to bother checking for a symbolic output.
1318 bool need_symbolic = this->GlobalGenerator->GetNeedSymbolicMark();
1320 // Check whether the first output is marked as symbolic.
1321 if (need_symbolic) {
1322 if (cmSourceFile* sf = this->Makefile->GetSource(outputs[0])) {
1323 symbolic = sf->GetPropertyAsBool("SYMBOLIC");
1327 // We always attach the actual commands to the first output.
1328 this->LocalGenerator->WriteMakeRule(os, comment, outputs[0], depends,
1329 commands, symbolic, in_help);
1331 // For single outputs, we are done.
1332 if (outputs.size() == 1) {
1336 // For multiple outputs, make the extra ones depend on the first one.
1337 std::vector<std::string> const output_depends(1, outputs[0]);
1338 for (std::string const& output : cmMakeRange(outputs).advance(1)) {
1339 // Touch the extra output so "make" knows that it was updated,
1340 // but only if the output was actually created.
1341 std::string const out = this->LocalGenerator->ConvertToOutputFormat(
1342 this->LocalGenerator->MaybeRelativeToTopBinDir(output),
1343 cmOutputConverter::SHELL);
1344 std::vector<std::string> output_commands;
1346 bool o_symbolic = false;
1347 if (need_symbolic) {
1348 if (cmSourceFile* sf = this->Makefile->GetSource(output)) {
1349 o_symbolic = sf->GetPropertyAsBool("SYMBOLIC");
1352 symbolic = symbolic && o_symbolic;
1355 output_commands.push_back("@$(CMAKE_COMMAND) -E touch_nocreate " + out);
1357 this->LocalGenerator->WriteMakeRule(os, nullptr, output, output_depends,
1358 output_commands, o_symbolic, in_help);
1361 // At build time, remove the first output if this one does not exist
1362 // so that "make" will rerun the real commands that create this one.
1363 MultipleOutputPairsType::value_type p(output, outputs[0]);
1364 this->MultipleOutputPairs.insert(p);
1370 void cmMakefileTargetGenerator::WriteTargetDependRules()
1372 // must write the targets depend info file
1374 this->LocalGenerator->GetTargetDirectory(this->GeneratorTarget);
1375 this->InfoFileNameFull = cmStrCat(dir, "/DependInfo.cmake");
1376 this->InfoFileNameFull =
1377 this->LocalGenerator->ConvertToFullPath(this->InfoFileNameFull);
1378 this->InfoFileStream =
1379 cm::make_unique<cmGeneratedFileStream>(this->InfoFileNameFull);
1380 if (!this->InfoFileStream) {
1383 this->InfoFileStream->SetCopyIfDifferent(true);
1384 this->LocalGenerator->WriteDependLanguageInfo(*this->InfoFileStream,
1385 this->GeneratorTarget);
1387 // Store multiple output pairs in the depend info file.
1388 if (!this->MultipleOutputPairs.empty()) {
1389 /* clang-format off */
1390 *this->InfoFileStream
1392 << "# Pairs of files generated by the same build rule.\n"
1393 << "set(CMAKE_MULTIPLE_OUTPUT_PAIRS\n";
1394 /* clang-format on */
1395 for (auto const& pi : this->MultipleOutputPairs) {
1396 *this->InfoFileStream
1397 << " " << cmOutputConverter::EscapeForCMake(pi.first) << " "
1398 << cmOutputConverter::EscapeForCMake(pi.second) << "\n";
1400 *this->InfoFileStream << " )\n\n";
1403 // Store list of targets linked directly or transitively.
1405 /* clang-format off */
1406 *this->InfoFileStream
1408 << "# Targets to which this target links.\n"
1409 << "set(CMAKE_TARGET_LINKED_INFO_FILES\n";
1410 /* clang-format on */
1411 std::vector<std::string> dirs =
1412 this->GetLinkedTargetDirectories(this->GetConfigName());
1413 for (std::string const& d : dirs) {
1414 *this->InfoFileStream << " \"" << d << "/DependInfo.cmake\"\n";
1416 *this->InfoFileStream << " )\n";
1419 std::string const& working_dir =
1420 this->LocalGenerator->GetCurrentBinaryDirectory();
1422 /* clang-format off */
1423 *this->InfoFileStream
1425 << "# Fortran module output directory.\n"
1426 << "set(CMAKE_Fortran_TARGET_MODULE_DIR \""
1427 << this->GeneratorTarget->GetFortranModuleDirectory(working_dir)
1430 if (this->GeneratorTarget->IsFortranBuildingInstrinsicModules()) {
1431 *this->InfoFileStream
1433 << "# Fortran compiler is building intrinsic modules.\n"
1434 << "set(CMAKE_Fortran_TARGET_BUILDING_INSTRINSIC_MODULES ON) \n";
1436 /* clang-format on */
1438 // and now write the rule to use it
1439 std::vector<std::string> depends;
1440 std::vector<std::string> commands;
1442 // Construct the name of the dependency generation target.
1443 std::string depTarget = cmStrCat(
1444 this->LocalGenerator->GetRelativeTargetDirectory(this->GeneratorTarget),
1447 // Add a command to call CMake to scan dependencies. CMake will
1448 // touch the corresponding depends file after scanning dependencies.
1449 std::ostringstream depCmd;
1450 // TODO: Account for source file properties and directory-level
1451 // definitions when scanning for dependencies.
1452 #if !defined(_WIN32) || defined(__CYGWIN__)
1453 // This platform supports symlinks, so cmSystemTools will translate
1454 // paths. Make sure PWD is set to the original name of the home
1455 // output directory to help cmSystemTools to create the same
1456 // translation table for the dependency scanning process.
1458 << (this->LocalGenerator->ConvertToOutputFormat(
1459 this->LocalGenerator->GetBinaryDirectory(),
1460 cmOutputConverter::SHELL))
1463 // Generate a call this signature:
1465 // cmake -E cmake_depends <generator>
1466 // <home-src-dir> <start-src-dir>
1467 // <home-out-dir> <start-out-dir>
1468 // <dep-info> --color=$(COLOR)
1470 // This gives the dependency scanner enough information to recreate
1471 // the state of our local generator sufficiently for its needs.
1472 depCmd << "$(CMAKE_COMMAND) -E cmake_depends \""
1473 << this->GlobalGenerator->GetName() << "\" "
1474 << this->LocalGenerator->ConvertToOutputFormat(
1475 this->LocalGenerator->GetSourceDirectory(),
1476 cmOutputConverter::SHELL)
1478 << this->LocalGenerator->ConvertToOutputFormat(
1479 this->LocalGenerator->GetCurrentSourceDirectory(),
1480 cmOutputConverter::SHELL)
1482 << this->LocalGenerator->ConvertToOutputFormat(
1483 this->LocalGenerator->GetBinaryDirectory(),
1484 cmOutputConverter::SHELL)
1486 << this->LocalGenerator->ConvertToOutputFormat(
1487 this->LocalGenerator->GetCurrentBinaryDirectory(),
1488 cmOutputConverter::SHELL)
1490 << this->LocalGenerator->ConvertToOutputFormat(
1491 cmSystemTools::CollapseFullPath(this->InfoFileNameFull),
1492 cmOutputConverter::SHELL);
1493 if (this->LocalGenerator->GetColorMakefile()) {
1494 depCmd << " --color=$(COLOR)";
1496 commands.push_back(depCmd.str());
1498 // Make sure all custom command outputs in this target are built.
1499 if (this->CustomCommandDriver == OnDepends) {
1500 this->DriveCustomCommands(depends);
1504 this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr,
1505 depTarget, depends, commands, true);
1508 void cmMakefileTargetGenerator::DriveCustomCommands(
1509 std::vector<std::string>& depends)
1511 // Depend on all custom command outputs.
1512 cm::append(depends, this->CustomCommandOutputs);
1515 void cmMakefileTargetGenerator::WriteObjectDependRules(
1516 cmSourceFile const& source, std::vector<std::string>& depends)
1518 // Create the list of dependencies known at cmake time. These are
1519 // shared between the object file and dependency scanning rule.
1520 depends.push_back(source.GetFullPath());
1521 if (cmValue objectDeps = source.GetProperty("OBJECT_DEPENDS")) {
1522 cmExpandList(*objectDeps, depends);
1526 void cmMakefileTargetGenerator::WriteDeviceLinkRule(
1527 std::vector<std::string>& commands, const std::string& output)
1529 std::string architecturesStr =
1530 this->GeneratorTarget->GetSafeProperty("CUDA_ARCHITECTURES");
1532 if (cmIsOff(architecturesStr)) {
1533 this->Makefile->IssueMessage(MessageType::FATAL_ERROR,
1534 "CUDA_SEPARABLE_COMPILATION on Clang "
1535 "requires CUDA_ARCHITECTURES to be set.");
1539 cmLocalUnixMakefileGenerator3* localGen{ this->LocalGenerator };
1540 std::vector<std::string> architectures = cmExpandedList(architecturesStr);
1541 std::string const& relPath = localGen->GetHomeRelativeOutputPath();
1543 // Ensure there are no duplicates.
1544 const std::vector<std::string> linkDeps = [&]() -> std::vector<std::string> {
1545 std::vector<std::string> deps;
1546 this->AppendTargetDepends(deps, true);
1547 this->GeneratorTarget->GetLinkDepends(deps, this->GetConfigName(), "CUDA");
1549 for (std::string const& obj : this->Objects) {
1550 deps.emplace_back(cmStrCat(relPath, obj));
1553 std::unordered_set<std::string> depsSet(deps.begin(), deps.end());
1555 std::copy(depsSet.begin(), depsSet.end(), std::back_inserter(deps));
1559 const std::string objectDir = this->GeneratorTarget->ObjectDirectory;
1560 const std::string relObjectDir =
1561 localGen->MaybeRelativeToCurBinDir(objectDir);
1563 // Construct a list of files associated with this executable that
1564 // may need to be cleaned.
1565 std::vector<std::string> cleanFiles;
1566 cleanFiles.push_back(localGen->MaybeRelativeToCurBinDir(output));
1568 std::string profiles;
1569 std::vector<std::string> fatbinaryDepends;
1570 std::string const registerFile =
1571 cmStrCat(objectDir, "cmake_cuda_register.h");
1573 // Link device code for each architecture.
1574 for (const std::string& architectureKind : architectures) {
1575 std::string registerFileCmd;
1577 // The generated register file contains macros that when expanded
1578 // register the device routines. Because the routines are the same for
1579 // all architectures the register file will be the same too. Thus
1580 // generate it only on the first invocation to reduce overhead.
1581 if (fatbinaryDepends.empty()) {
1582 std::string const registerFileRel =
1583 cmStrCat(relPath, relObjectDir, "cmake_cuda_register.h");
1585 cmStrCat(" --register-link-binaries=", registerFileRel);
1586 cleanFiles.push_back(registerFileRel);
1589 // Clang always generates real code, so strip the specifier.
1590 const std::string architecture =
1591 architectureKind.substr(0, architectureKind.find('-'));
1592 const std::string cubin =
1593 cmStrCat(objectDir, "sm_", architecture, ".cubin");
1595 profiles += cmStrCat(" -im=profile=sm_", architecture, ",file=", cubin);
1596 fatbinaryDepends.emplace_back(cubin);
1598 std::string command = cmStrCat(
1599 this->Makefile->GetRequiredDefinition("CMAKE_CUDA_DEVICE_LINKER"),
1600 " -arch=sm_", architecture, registerFileCmd, " -o=$@ ",
1601 cmJoin(linkDeps, " "));
1603 localGen->WriteMakeRule(*this->BuildFileStream, nullptr, cubin, linkDeps,
1604 { command }, false);
1607 // Combine all architectures into a single fatbinary.
1608 const std::string fatbinaryCommand =
1609 cmStrCat(this->Makefile->GetRequiredDefinition("CMAKE_CUDA_FATBINARY"),
1610 " -64 -cmdline=--compile-only -compress-all -link "
1611 "--embedded-fatbin=$@",
1613 const std::string fatbinaryOutput =
1614 cmStrCat(objectDir, "cmake_cuda_fatbin.h");
1615 const std::string fatbinaryOutputRel =
1616 cmStrCat(relPath, relObjectDir, "cmake_cuda_fatbin.h");
1618 localGen->WriteMakeRule(*this->BuildFileStream, nullptr, fatbinaryOutputRel,
1619 fatbinaryDepends, { fatbinaryCommand }, false);
1621 // Compile the stub that registers the kernels and contains the
1623 cmRulePlaceholderExpander::RuleVariables vars;
1624 vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str();
1626 cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()).c_str();
1628 vars.Language = "CUDA";
1629 vars.Object = output.c_str();
1630 vars.Fatbinary = fatbinaryOutput.c_str();
1631 vars.RegisterFile = registerFile.c_str();
1633 std::string linkFlags;
1634 this->GetDeviceLinkFlags(linkFlags, "CUDA");
1635 vars.LinkFlags = linkFlags.c_str();
1637 std::string flags = this->GetFlags("CUDA", this->GetConfigName());
1638 vars.Flags = flags.c_str();
1640 std::string compileCmd = this->GetLinkRule("CMAKE_CUDA_DEVICE_LINK_COMPILE");
1641 std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
1642 localGen->CreateRulePlaceholderExpander());
1643 rulePlaceholderExpander->ExpandRuleVariables(localGen, compileCmd, vars);
1645 commands.emplace_back(compileCmd);
1646 localGen->WriteMakeRule(*this->BuildFileStream, nullptr, output,
1647 { fatbinaryOutputRel }, commands, false);
1649 // Clean all the possible executable names and symlinks.
1650 this->CleanFiles.insert(cleanFiles.begin(), cleanFiles.end());
1653 void cmMakefileTargetGenerator::GenerateCustomRuleFile(
1654 cmCustomCommandGenerator const& ccg)
1656 // Collect the commands.
1657 std::vector<std::string> commands;
1658 std::string comment = this->LocalGenerator->ConstructComment(ccg);
1659 if (!comment.empty()) {
1660 // add in a progress call if needed
1661 this->NumberOfProgressActions++;
1662 if (!this->NoRuleMessages) {
1663 cmLocalUnixMakefileGenerator3::EchoProgress progress;
1664 this->MakeEchoProgress(progress);
1665 this->LocalGenerator->AppendEcho(
1666 commands, comment, cmLocalUnixMakefileGenerator3::EchoGenerate,
1671 // Now append the actual user-specified commands.
1672 std::ostringstream content;
1673 this->LocalGenerator->AppendCustomCommand(
1674 commands, ccg, this->GeneratorTarget,
1675 this->LocalGenerator->GetBinaryDirectory(), false, &content);
1677 // Collect the dependencies.
1678 std::vector<std::string> depends;
1679 this->LocalGenerator->AppendCustomDepend(depends, ccg);
1681 if (!ccg.GetCC().GetDepfile().empty()) {
1682 // Add dependency over timestamp file for dependencies management
1683 auto dependTimestamp = cmSystemTools::ConvertToOutputPath(
1684 this->LocalGenerator->MaybeRelativeToTopBinDir(
1685 cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.ts")));
1687 depends.push_back(dependTimestamp);
1691 const std::vector<std::string>& outputs = ccg.GetOutputs();
1692 bool symbolic = this->WriteMakeRule(*this->BuildFileStream, nullptr, outputs,
1695 // Symbolic inputs are not expected to exist, so add dummy rules.
1696 if (this->CMP0113New && !depends.empty()) {
1697 std::vector<std::string> no_depends;
1698 std::vector<std::string> no_commands;
1699 for (std::string const& dep : depends) {
1700 if (cmSourceFile* dsf =
1701 this->Makefile->GetSource(dep, cmSourceFileLocationKind::Known)) {
1702 if (dsf->GetPropertyAsBool("SYMBOLIC")) {
1703 this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr,
1704 dep, no_depends, no_commands,
1711 // If the rule has changed make sure the output is rebuilt.
1713 this->GlobalGenerator->AddRuleHash(ccg.GetOutputs(), content.str());
1716 // Setup implicit dependency scanning.
1717 for (auto const& idi : ccg.GetCC().GetImplicitDepends()) {
1718 std::string objFullPath = cmSystemTools::CollapseFullPath(
1719 outputs[0], this->LocalGenerator->GetCurrentBinaryDirectory());
1720 std::string srcFullPath = cmSystemTools::CollapseFullPath(
1721 idi.second, this->LocalGenerator->GetCurrentBinaryDirectory());
1722 this->LocalGenerator->AddImplicitDepends(this->GeneratorTarget, idi.first,
1723 objFullPath, srcFullPath);
1726 // Setup implicit depend for depfile if any
1727 if (!ccg.GetCC().GetDepfile().empty()) {
1728 std::string objFullPath = cmSystemTools::CollapseFullPath(
1729 outputs[0], this->LocalGenerator->GetCurrentBinaryDirectory());
1730 this->LocalGenerator->AddImplicitDepends(
1731 this->GeneratorTarget, "CUSTOM", objFullPath, ccg.GetFullDepfile(),
1732 cmDependencyScannerKind::Compiler);
1735 this->CustomCommandOutputs.insert(outputs.begin(), outputs.end());
1738 void cmMakefileTargetGenerator::MakeEchoProgress(
1739 cmLocalUnixMakefileGenerator3::EchoProgress& progress) const
1742 cmStrCat(this->LocalGenerator->GetBinaryDirectory(), "/CMakeFiles");
1743 std::ostringstream progressArg;
1744 progressArg << "$(CMAKE_PROGRESS_" << this->NumberOfProgressActions << ")";
1745 progress.Arg = progressArg.str();
1748 void cmMakefileTargetGenerator::WriteObjectsVariable(
1749 std::string& variableName, std::string& variableNameExternal,
1750 bool useWatcomQuote)
1752 // Write a make variable assignment that lists all objects for the
1754 variableName = this->LocalGenerator->CreateMakeVariable(
1755 this->GeneratorTarget->GetName(), "_OBJECTS");
1756 *this->BuildFileStream << "# Object files for target "
1757 << this->GeneratorTarget->GetName() << "\n"
1758 << variableName << " =";
1760 const auto& lineContinue = this->GlobalGenerator->LineContinueDirective;
1762 cmValue pchExtension = this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION");
1764 for (std::string const& obj : this->Objects) {
1765 if (cmHasSuffix(obj, pchExtension)) {
1768 *this->BuildFileStream << " " << lineContinue;
1769 *this->BuildFileStream
1770 << cmLocalUnixMakefileGenerator3::ConvertToQuotedOutputPath(
1771 obj, useWatcomQuote);
1773 *this->BuildFileStream << "\n";
1775 // Write a make variable assignment that lists all external objects
1777 variableNameExternal = this->LocalGenerator->CreateMakeVariable(
1778 this->GeneratorTarget->GetName(), "_EXTERNAL_OBJECTS");
1779 /* clang-format off */
1780 *this->BuildFileStream
1782 << "# External object files for target "
1783 << this->GeneratorTarget->GetName() << "\n"
1784 << variableNameExternal << " =";
1785 /* clang-format on */
1786 for (std::string const& obj : this->ExternalObjects) {
1787 object = this->LocalGenerator->MaybeRelativeToCurBinDir(obj);
1788 *this->BuildFileStream << " " << lineContinue;
1789 *this->BuildFileStream
1790 << cmLocalUnixMakefileGenerator3::ConvertToQuotedOutputPath(
1791 obj, useWatcomQuote);
1793 *this->BuildFileStream << "\n"
1797 class cmMakefileTargetGeneratorObjectStrings
1800 cmMakefileTargetGeneratorObjectStrings(std::vector<std::string>& strings,
1801 cmOutputConverter* outputConverter,
1802 cmStateDirectory const& stateDir,
1803 std::string::size_type limit)
1805 , OutputConverter(outputConverter)
1806 , StateDir(stateDir)
1807 , LengthLimit(limit)
1811 void Feed(std::string const& obj)
1813 // Construct the name of the next object.
1814 this->NextObject = this->OutputConverter->ConvertToOutputFormat(
1815 this->OutputConverter->MaybeRelativeToCurBinDir(obj),
1816 cmOutputConverter::RESPONSE);
1818 // Roll over to next string if the limit will be exceeded.
1819 if (this->LengthLimit != std::string::npos &&
1820 (this->CurrentString.length() + 1 + this->NextObject.length() >
1821 this->LengthLimit)) {
1822 this->Strings.push_back(this->CurrentString);
1823 this->CurrentString.clear();
1827 // Separate from previous object.
1828 this->CurrentString += this->Space;
1831 // Append this object.
1832 this->CurrentString += this->NextObject;
1834 void Done() { this->Strings.push_back(this->CurrentString); }
1837 std::vector<std::string>& Strings;
1838 cmOutputConverter* OutputConverter;
1839 cmStateDirectory StateDir;
1840 std::string::size_type LengthLimit;
1841 std::string CurrentString;
1842 std::string NextObject;
1846 void cmMakefileTargetGenerator::WriteObjectsStrings(
1847 std::vector<std::string>& objStrings, std::string::size_type limit)
1849 cmValue pchExtension = this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION");
1851 cmMakefileTargetGeneratorObjectStrings helper(
1852 objStrings, this->LocalGenerator,
1853 this->LocalGenerator->GetStateSnapshot().GetDirectory(), limit);
1854 for (std::string const& obj : this->Objects) {
1855 if (cmHasSuffix(obj, pchExtension)) {
1860 for (std::string const& obj : this->ExternalObjects) {
1863 auto ispcAdditionalObjs =
1864 this->GeneratorTarget->GetGeneratedISPCObjects(this->GetConfigName());
1865 for (std::string const& obj : ispcAdditionalObjs) {
1871 void cmMakefileTargetGenerator::WriteTargetDriverRule(
1872 const std::string& main_output, bool relink)
1874 // Compute the name of the driver target.
1876 this->LocalGenerator->GetRelativeTargetDirectory(this->GeneratorTarget);
1877 std::string buildTargetRuleName =
1878 cmStrCat(dir, relink ? "/preinstall" : "/build");
1879 buildTargetRuleName =
1880 this->LocalGenerator->MaybeRelativeToTopBinDir(buildTargetRuleName);
1882 // Build the list of target outputs to drive.
1883 std::vector<std::string> depends;
1884 depends.push_back(main_output);
1886 const char* comment = nullptr;
1888 // Setup the comment for the preinstall driver.
1889 comment = "Rule to relink during preinstall.";
1891 // Setup the comment for the main build driver.
1892 comment = "Rule to build all files generated by this target.";
1894 // Make sure all custom command outputs in this target are built.
1895 if (this->CustomCommandDriver == OnBuild) {
1896 this->DriveCustomCommands(depends);
1899 // Make sure the extra files are built.
1900 cm::append(depends, this->ExtraFiles);
1903 // Write the driver rule.
1904 std::vector<std::string> no_commands;
1905 this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, comment,
1906 buildTargetRuleName, depends,
1910 void cmMakefileTargetGenerator::AppendTargetDepends(
1911 std::vector<std::string>& depends, bool ignoreType)
1913 // Static libraries never depend on anything for linking.
1914 if (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY &&
1919 // Loop over all library dependencies.
1920 const std::string& cfg = this->GetConfigName();
1921 if (cmComputeLinkInformation* cli =
1922 this->GeneratorTarget->GetLinkInformation(cfg)) {
1923 cm::append(depends, cli->GetDepends());
1927 void cmMakefileTargetGenerator::AppendObjectDepends(
1928 std::vector<std::string>& depends)
1930 // Add dependencies on the compiled object files.
1931 std::string const& relPath =
1932 this->LocalGenerator->GetHomeRelativeOutputPath();
1933 for (std::string const& obj : this->Objects) {
1934 std::string objTarget = cmStrCat(relPath, obj);
1935 depends.push_back(std::move(objTarget));
1938 // Add dependencies on the external object files.
1939 cm::append(depends, this->ExternalObjects);
1941 // Add a dependency on the rule file itself.
1942 this->LocalGenerator->AppendRuleDepend(depends,
1943 this->BuildFileNameFull.c_str());
1946 void cmMakefileTargetGenerator::AppendLinkDepends(
1947 std::vector<std::string>& depends, const std::string& linkLanguage)
1949 this->AppendObjectDepends(depends);
1951 // Add dependencies on targets that must be built first.
1952 this->AppendTargetDepends(depends);
1954 // Add a dependency on the link definitions file, if any.
1955 if (cmGeneratorTarget::ModuleDefinitionInfo const* mdi =
1956 this->GeneratorTarget->GetModuleDefinitionInfo(
1957 this->GetConfigName())) {
1958 for (cmSourceFile const* src : mdi->Sources) {
1959 depends.push_back(src->GetFullPath());
1963 // Add a dependency on user-specified manifest files, if any.
1964 std::vector<cmSourceFile const*> manifest_srcs;
1965 this->GeneratorTarget->GetManifests(manifest_srcs, this->GetConfigName());
1966 for (cmSourceFile const* manifest_src : manifest_srcs) {
1967 depends.push_back(manifest_src->GetFullPath());
1970 // Add user-specified dependencies.
1971 this->GeneratorTarget->GetLinkDepends(depends, this->GetConfigName(),
1975 std::string cmMakefileTargetGenerator::GetLinkRule(
1976 const std::string& linkRuleVar)
1978 std::string linkRule = this->Makefile->GetRequiredDefinition(linkRuleVar);
1979 if (this->GeneratorTarget->HasImplibGNUtoMS(this->GetConfigName())) {
1980 std::string ruleVar =
1982 this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName()),
1984 if (cmValue rule = this->Makefile->GetDefinition(ruleVar)) {
1991 void cmMakefileTargetGenerator::CloseFileStreams()
1993 this->BuildFileStream.reset();
1994 this->InfoFileStream.reset();
1995 this->FlagFileStream.reset();
1998 void cmMakefileTargetGenerator::CreateLinkScript(
1999 const char* name, std::vector<std::string> const& link_commands,
2000 std::vector<std::string>& makefile_commands,
2001 std::vector<std::string>& makefile_depends)
2003 // Create the link script file.
2004 std::string linkScriptName =
2005 cmStrCat(this->TargetBuildDirectoryFull, '/', name);
2006 cmGeneratedFileStream linkScriptStream(linkScriptName);
2007 linkScriptStream.SetCopyIfDifferent(true);
2008 for (std::string const& link_command : link_commands) {
2009 // Do not write out empty commands or commands beginning in the
2011 if (!link_command.empty() && link_command[0] != ':') {
2012 linkScriptStream << link_command << "\n";
2016 // Create the makefile command to invoke the link script.
2017 std::string link_command =
2018 cmStrCat("$(CMAKE_COMMAND) -E cmake_link_script ",
2019 this->LocalGenerator->ConvertToOutputFormat(
2020 this->LocalGenerator->MaybeRelativeToCurBinDir(linkScriptName),
2021 cmOutputConverter::SHELL),
2022 " --verbose=$(VERBOSE)");
2023 makefile_commands.push_back(std::move(link_command));
2024 makefile_depends.push_back(std::move(linkScriptName));
2027 bool cmMakefileTargetGenerator::CheckUseResponseFileForObjects(
2028 std::string const& l) const
2030 // Check for an explicit setting one way or the other.
2031 std::string const responseVar =
2032 "CMAKE_" + l + "_USE_RESPONSE_FILE_FOR_OBJECTS";
2033 if (cmValue val = this->Makefile->GetDefinition(responseVar)) {
2034 if (!val->empty()) {
2039 // Check for a system limit.
2040 if (size_t const limit = cmSystemTools::CalculateCommandLineLengthLimit()) {
2041 // Compute the total length of our list of object files with room
2042 // for argument separation and quoting. This does not convert paths
2043 // relative to CMAKE_CURRENT_BINARY_DIR like the final list will be, so
2044 // the actual list will likely be much shorter than this. However, in
2045 // the worst case all objects will remain as absolute paths.
2047 for (std::string const& obj : this->Objects) {
2048 length += obj.size() + 3;
2050 for (std::string const& ext_obj : this->ExternalObjects) {
2051 length += ext_obj.size() + 3;
2054 // We need to guarantee room for both objects and libraries, so
2055 // if the objects take up more than half then use a response file
2057 if (length > (limit / 2)) {
2062 // We do not need a response file for objects.
2066 bool cmMakefileTargetGenerator::CheckUseResponseFileForLibraries(
2067 std::string const& l) const
2069 // Check for an explicit setting one way or the other.
2070 std::string const responseVar =
2071 "CMAKE_" + l + "_USE_RESPONSE_FILE_FOR_LIBRARIES";
2072 if (cmValue val = this->Makefile->GetDefinition(responseVar)) {
2073 if (!val->empty()) {
2078 // We do not need a response file for libraries.
2082 std::string cmMakefileTargetGenerator::CreateResponseFile(
2083 const char* name, std::string const& options,
2084 std::vector<std::string>& makefile_depends)
2086 // FIXME: Find a better way to determine the response file encoding,
2087 // perhaps using tool-specific platform information variables.
2088 // For now, use the makefile encoding as a heuristic.
2089 codecvt::Encoding responseEncoding =
2090 this->GlobalGenerator->GetMakefileEncoding();
2091 // Non-MSVC tooling may not understand a BOM.
2092 if (responseEncoding == codecvt::UTF8_WITH_BOM &&
2093 !this->Makefile->IsOn("MSVC")) {
2094 responseEncoding = codecvt::UTF8;
2097 // Create the response file.
2098 std::string responseFileNameFull =
2099 cmStrCat(this->TargetBuildDirectoryFull, '/', name);
2100 cmGeneratedFileStream responseStream(responseFileNameFull, false,
2102 responseStream.SetCopyIfDifferent(true);
2103 responseStream << options << "\n";
2105 // Add a dependency so the target will rebuild when the set of
2107 makefile_depends.push_back(std::move(responseFileNameFull));
2109 // Construct the name to be used on the command line.
2110 std::string responseFileName =
2111 cmStrCat(this->TargetBuildDirectory, '/', name);
2112 return responseFileName;
2115 std::unique_ptr<cmLinkLineComputer>
2116 cmMakefileTargetGenerator::CreateLinkLineComputer(
2117 cmOutputConverter* outputConverter, cmStateDirectory const& stateDir)
2119 if (this->Makefile->IsOn("MSVC60")) {
2120 return this->GlobalGenerator->CreateMSVC60LinkLineComputer(outputConverter,
2123 return this->GlobalGenerator->CreateLinkLineComputer(outputConverter,
2127 void cmMakefileTargetGenerator::CreateLinkLibs(
2128 cmLinkLineComputer* linkLineComputer, std::string& linkLibs,
2129 bool useResponseFile, std::vector<std::string>& makefile_depends)
2131 std::string frameworkPath;
2132 std::string linkPath;
2133 cmComputeLinkInformation* pcli =
2134 this->GeneratorTarget->GetLinkInformation(this->GetConfigName());
2135 this->LocalGenerator->OutputLinkLibraries(pcli, linkLineComputer, linkLibs,
2136 frameworkPath, linkPath);
2137 linkLibs = frameworkPath + linkPath + linkLibs;
2139 if (useResponseFile &&
2140 linkLibs.find_first_not_of(' ') != std::string::npos) {
2141 // Lookup the response file reference flag.
2142 std::string responseFlagVar =
2144 this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName()),
2145 "_RESPONSE_FILE_LINK_FLAG");
2146 std::string responseFlag;
2147 if (cmValue p = this->Makefile->GetDefinition(responseFlagVar)) {
2153 // Create this response file.
2154 std::string link_rsp =
2155 this->CreateResponseFile("linklibs.rsp", linkLibs, makefile_depends);
2157 // Reference the response file.
2158 linkLibs = cmStrCat(responseFlag,
2159 this->LocalGenerator->ConvertToOutputFormat(
2160 link_rsp, cmOutputConverter::SHELL));
2164 void cmMakefileTargetGenerator::CreateObjectLists(
2165 bool useLinkScript, bool useArchiveRules, bool useResponseFile,
2166 std::string& buildObjs, std::vector<std::string>& makefile_depends,
2167 bool useWatcomQuote)
2169 std::string variableName;
2170 std::string variableNameExternal;
2171 this->WriteObjectsVariable(variableName, variableNameExternal,
2173 if (useResponseFile) {
2174 // MSVC response files cannot exceed 128K.
2175 std::string::size_type const responseFileLimit = 131000;
2177 // Construct the individual object list strings.
2178 std::vector<std::string> object_strings;
2179 this->WriteObjectsStrings(object_strings, responseFileLimit);
2181 // Lookup the response file reference flag.
2182 std::string responseFlagVar =
2184 this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName()),
2185 "_RESPONSE_FILE_LINK_FLAG");
2186 std::string responseFlag;
2187 if (cmValue p = this->Makefile->GetDefinition(responseFlagVar)) {
2193 // Write a response file for each string.
2194 const char* sep = "";
2195 for (unsigned int i = 0; i < object_strings.size(); ++i) {
2196 // Number the response files.
2198 snprintf(rsp, sizeof(rsp), "objects%u.rsp", i + 1);
2200 // Create this response file.
2201 std::string objects_rsp =
2202 this->CreateResponseFile(rsp, object_strings[i], makefile_depends);
2204 // Separate from previous response file references.
2208 // Reference the response file.
2209 buildObjs += responseFlag;
2210 buildObjs += this->LocalGenerator->ConvertToOutputFormat(
2211 objects_rsp, cmOutputConverter::SHELL);
2213 } else if (useLinkScript) {
2214 if (!useArchiveRules) {
2215 std::vector<std::string> objStrings;
2216 this->WriteObjectsStrings(objStrings);
2217 buildObjs = objStrings[0];
2221 cmStrCat("$(", variableName, ") $(", variableNameExternal, ')');
2225 void cmMakefileTargetGenerator::AddIncludeFlags(std::string& flags,
2226 const std::string& lang,
2227 const std::string& /*config*/)
2229 std::string responseVar =
2230 cmStrCat("CMAKE_", lang, "_USE_RESPONSE_FILE_FOR_INCLUDES");
2231 bool useResponseFile = this->Makefile->IsOn(responseVar);
2233 std::vector<std::string> includes;
2234 this->LocalGenerator->GetIncludeDirectories(includes, this->GeneratorTarget,
2235 lang, this->GetConfigName());
2237 std::string includeFlags = this->LocalGenerator->GetIncludeFlags(
2238 includes, this->GeneratorTarget, lang, this->GetConfigName(),
2240 if (includeFlags.empty()) {
2244 if (useResponseFile) {
2245 std::string const responseFlagVar =
2246 "CMAKE_" + lang + "_RESPONSE_FILE_FLAG";
2247 std::string responseFlag =
2248 this->Makefile->GetSafeDefinition(responseFlagVar);
2249 if (responseFlag.empty()) {
2252 std::string name = cmStrCat("includes_", lang, ".rsp");
2253 std::string arg = std::move(responseFlag) +
2254 this->CreateResponseFile(name.c_str(), includeFlags,
2255 this->FlagFileDepends[lang]);
2256 this->LocalGenerator->AppendFlags(flags, arg);
2258 this->LocalGenerator->AppendFlags(flags, includeFlags);
2262 void cmMakefileTargetGenerator::GenDefFile(
2263 std::vector<std::string>& real_link_commands)
2265 cmGeneratorTarget::ModuleDefinitionInfo const* mdi =
2266 this->GeneratorTarget->GetModuleDefinitionInfo(this->GetConfigName());
2267 if (!mdi || !mdi->DefFileGenerated) {
2270 std::string cmd = cmSystemTools::GetCMakeCommand();
2272 this->LocalGenerator->ConvertToOutputFormat(cmd, cmOutputConverter::SHELL),
2273 " -E __create_def ",
2274 this->LocalGenerator->ConvertToOutputFormat(
2275 this->LocalGenerator->MaybeRelativeToCurBinDir(mdi->DefFile),
2276 cmOutputConverter::SHELL),
2278 std::string objlist_file = mdi->DefFile + ".objs";
2279 cmd += this->LocalGenerator->ConvertToOutputFormat(
2280 this->LocalGenerator->MaybeRelativeToCurBinDir(objlist_file),
2281 cmOutputConverter::SHELL);
2282 cmValue nm_executable = this->Makefile->GetDefinition("CMAKE_NM");
2283 if (cmNonempty(nm_executable)) {
2285 cmd += this->LocalCommonGenerator->ConvertToOutputFormat(
2286 *nm_executable, cmOutputConverter::SHELL);
2288 real_link_commands.insert(real_link_commands.begin(), cmd);
2289 // create a list of obj files for the -E __create_def to read
2290 cmGeneratedFileStream fout(objlist_file);
2292 if (mdi->WindowsExportAllSymbols) {
2293 for (std::string const& obj : this->Objects) {
2294 if (cmHasLiteralSuffix(obj, ".obj")) {
2295 fout << obj << "\n";
2298 for (std::string const& obj : this->ExternalObjects) {
2299 fout << obj << "\n";
2303 for (cmSourceFile const* src : mdi->Sources) {
2304 fout << src->GetFullPath() << "\n";