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 "cmLocalGenerator.h"
10 #include <initializer_list>
13 #include <unordered_set>
18 #include <cm/string_view>
19 #include <cmext/algorithm>
21 #include "cmsys/RegularExpression.hxx"
23 #include "cmAlgorithms.h"
24 #include "cmComputeLinkInformation.h"
25 #include "cmCustomCommand.h"
26 #include "cmCustomCommandGenerator.h"
27 #include "cmCustomCommandLines.h"
28 #include "cmCustomCommandTypes.h"
29 #include "cmGeneratedFileStream.h"
30 #include "cmGeneratorExpression.h"
31 #include "cmGeneratorExpressionEvaluationFile.h"
32 #include "cmGeneratorTarget.h"
33 #include "cmGlobalGenerator.h"
34 #include "cmInstallGenerator.h"
35 #include "cmInstallScriptGenerator.h"
36 #include "cmInstallTargetGenerator.h"
37 #include "cmLinkLineComputer.h"
38 #include "cmLinkLineDeviceComputer.h"
39 #include "cmMakefile.h"
40 #include "cmRulePlaceholderExpander.h"
41 #include "cmSourceFile.h"
42 #include "cmSourceFileLocation.h"
43 #include "cmSourceFileLocationKind.h"
45 #include "cmStateDirectory.h"
46 #include "cmStateTypes.h"
47 #include "cmStringAlgorithms.h"
48 #include "cmSystemTools.h"
50 #include "cmTestGenerator.h"
51 #include "cmVersion.h"
54 #if !defined(CMAKE_BOOTSTRAP)
55 # define CM_LG_ENCODE_OBJECT_NAMES
56 # include "cmCryptoHash.h"
59 #if defined(__HAIKU__)
60 # include <FindDirectory.h>
61 # include <StorageDefs.h>
64 // List of variables that are replaced when
65 // rules are expanced. These variables are
66 // replaced in the form <var> with GetSafeDefinition(var).
67 // ${LANG} is replaced in the variable first with all enabled
69 static auto ruleReplaceVars = { "CMAKE_${LANG}_COMPILER",
70 "CMAKE_SHARED_LIBRARY_CREATE_${LANG}_FLAGS",
71 "CMAKE_SHARED_MODULE_CREATE_${LANG}_FLAGS",
72 "CMAKE_SHARED_MODULE_${LANG}_FLAGS",
73 "CMAKE_SHARED_LIBRARY_${LANG}_FLAGS",
74 "CMAKE_${LANG}_LINK_FLAGS",
75 "CMAKE_SHARED_LIBRARY_SONAME_${LANG}_FLAG",
76 "CMAKE_${LANG}_ARCHIVE",
78 "CMAKE_CURRENT_SOURCE_DIR",
79 "CMAKE_CURRENT_BINARY_DIR",
83 "CMAKE_CUDA_HOST_COMPILER",
84 "CMAKE_CUDA_HOST_LINK_LAUNCHER",
85 "CMAKE_CL_SHOWINCLUDES_PREFIX" };
87 cmLocalGenerator::cmLocalGenerator(cmGlobalGenerator* gg, cmMakefile* makefile)
88 : cmOutputConverter(makefile->GetStateSnapshot())
89 , StateSnapshot(makefile->GetStateSnapshot())
90 , DirectoryBacktrace(makefile->GetBacktrace())
92 this->GlobalGenerator = gg;
94 this->Makefile = makefile;
96 this->AliasTargets = makefile->GetAliasTargets();
98 this->EmitUniversalBinaryFlags = true;
99 this->BackwardsCompatibility = 0;
100 this->BackwardsCompatibilityFinal = false;
102 this->ComputeObjectMaxPath();
104 // Canonicalize entries of the CPATH environment variable the same
105 // way detection of CMAKE_<LANG>_IMPLICIT_INCLUDE_DIRECTORIES does.
107 std::vector<std::string> cpath;
108 cmSystemTools::GetPath(cpath, "CPATH");
109 for (std::string& cp : cpath) {
110 if (cmSystemTools::FileIsFullPath(cp)) {
111 cp = cmSystemTools::CollapseFullPath(cp);
112 this->EnvCPATH.emplace(std::move(cp));
117 std::vector<std::string> enabledLanguages =
118 this->GetState()->GetEnabledLanguages();
120 if (const char* sysrootCompile =
121 this->Makefile->GetDefinition("CMAKE_SYSROOT_COMPILE")) {
122 this->CompilerSysroot = sysrootCompile;
124 this->CompilerSysroot = this->Makefile->GetSafeDefinition("CMAKE_SYSROOT");
127 if (const char* sysrootLink =
128 this->Makefile->GetDefinition("CMAKE_SYSROOT_LINK")) {
129 this->LinkerSysroot = sysrootLink;
131 this->LinkerSysroot = this->Makefile->GetSafeDefinition("CMAKE_SYSROOT");
134 for (std::string const& lang : enabledLanguages) {
135 if (lang == "NONE") {
138 this->Compilers["CMAKE_" + lang + "_COMPILER"] = lang;
140 this->VariableMappings["CMAKE_" + lang + "_COMPILER"] =
141 this->Makefile->GetSafeDefinition("CMAKE_" + lang + "_COMPILER");
143 std::string const& compilerArg1 = "CMAKE_" + lang + "_COMPILER_ARG1";
144 std::string const& compilerTarget = "CMAKE_" + lang + "_COMPILER_TARGET";
145 std::string const& compilerOptionTarget =
146 "CMAKE_" + lang + "_COMPILE_OPTIONS_TARGET";
147 std::string const& compilerExternalToolchain =
148 "CMAKE_" + lang + "_COMPILER_EXTERNAL_TOOLCHAIN";
149 std::string const& compilerOptionExternalToolchain =
150 "CMAKE_" + lang + "_COMPILE_OPTIONS_EXTERNAL_TOOLCHAIN";
151 std::string const& compilerOptionSysroot =
152 "CMAKE_" + lang + "_COMPILE_OPTIONS_SYSROOT";
154 this->VariableMappings[compilerArg1] =
155 this->Makefile->GetSafeDefinition(compilerArg1);
156 this->VariableMappings[compilerTarget] =
157 this->Makefile->GetSafeDefinition(compilerTarget);
158 this->VariableMappings[compilerOptionTarget] =
159 this->Makefile->GetSafeDefinition(compilerOptionTarget);
160 this->VariableMappings[compilerExternalToolchain] =
161 this->Makefile->GetSafeDefinition(compilerExternalToolchain);
162 this->VariableMappings[compilerOptionExternalToolchain] =
163 this->Makefile->GetSafeDefinition(compilerOptionExternalToolchain);
164 this->VariableMappings[compilerOptionSysroot] =
165 this->Makefile->GetSafeDefinition(compilerOptionSysroot);
167 for (std::string replaceVar : ruleReplaceVars) {
168 if (replaceVar.find("${LANG}") != std::string::npos) {
169 cmSystemTools::ReplaceString(replaceVar, "${LANG}", lang);
172 this->VariableMappings[replaceVar] =
173 this->Makefile->GetSafeDefinition(replaceVar);
178 cmRulePlaceholderExpander* cmLocalGenerator::CreateRulePlaceholderExpander()
181 return new cmRulePlaceholderExpander(this->Compilers, this->VariableMappings,
182 this->CompilerSysroot,
183 this->LinkerSysroot);
186 cmLocalGenerator::~cmLocalGenerator() = default;
188 void cmLocalGenerator::IssueMessage(MessageType t,
189 std::string const& text) const
191 this->GetCMakeInstance()->IssueMessage(t, text, this->DirectoryBacktrace);
194 void cmLocalGenerator::ComputeObjectMaxPath()
196 // Choose a maximum object file name length.
197 #if defined(_WIN32) || defined(__CYGWIN__)
198 this->ObjectPathMax = 250;
200 this->ObjectPathMax = 1000;
202 const char* plen = this->Makefile->GetDefinition("CMAKE_OBJECT_PATH_MAX");
205 if (sscanf(plen, "%u", &pmax) == 1) {
207 this->ObjectPathMax = pmax;
209 std::ostringstream w;
210 w << "CMAKE_OBJECT_PATH_MAX is set to " << pmax
211 << ", which is less than the minimum of 128. "
212 << "The value will be ignored.";
213 this->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
216 std::ostringstream w;
217 w << "CMAKE_OBJECT_PATH_MAX is set to \"" << plen
218 << "\", which fails to parse as a positive integer. "
219 << "The value will be ignored.";
220 this->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
223 this->ObjectMaxPathViolations.clear();
226 static void MoveSystemIncludesToEnd(std::vector<std::string>& includeDirs,
227 const std::string& config,
228 const std::string& lang,
229 const cmGeneratorTarget* target)
236 includeDirs.begin(), includeDirs.end(),
237 [&target, &config, &lang](std::string const& a, std::string const& b) {
238 return !target->IsSystemIncludeDirectory(a, config, lang) &&
239 target->IsSystemIncludeDirectory(b, config, lang);
243 static void MoveSystemIncludesToEnd(std::vector<BT<std::string>>& includeDirs,
244 const std::string& config,
245 const std::string& lang,
246 const cmGeneratorTarget* target)
252 std::stable_sort(includeDirs.begin(), includeDirs.end(),
253 [target, &config, &lang](BT<std::string> const& a,
254 BT<std::string> const& b) {
255 return !target->IsSystemIncludeDirectory(a.Value, config,
257 target->IsSystemIncludeDirectory(b.Value, config, lang);
261 void cmLocalGenerator::TraceDependencies()
263 // Generate the rule files for each target.
264 const auto& targets = this->GetGeneratorTargets();
265 for (const auto& target : targets) {
266 if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
269 target->TraceDependencies();
273 void cmLocalGenerator::GenerateTestFiles()
275 if (!this->Makefile->IsOn("CMAKE_TESTING_ENABLED")) {
279 // Compute the set of configurations.
280 std::vector<std::string> configurationTypes;
281 const std::string& config =
282 this->Makefile->GetConfigurations(configurationTypes, false);
285 cmStrCat(this->StateSnapshot.GetDirectory().GetCurrentBinary(),
286 "/CTestTestfile.cmake");
288 cmGeneratedFileStream fout(file);
289 fout.SetCopyIfDifferent(true);
291 fout << "# CMake generated Testfile for " << std::endl
292 << "# Source directory: "
293 << this->StateSnapshot.GetDirectory().GetCurrentSource() << std::endl
294 << "# Build directory: "
295 << this->StateSnapshot.GetDirectory().GetCurrentBinary() << std::endl
297 << "# This file includes the relevant testing commands "
298 << "required for " << std::endl
299 << "# testing this directory and lists subdirectories to "
300 << "be tested as well." << std::endl;
302 const char* testIncludeFile =
303 this->Makefile->GetProperty("TEST_INCLUDE_FILE");
304 if (testIncludeFile) {
305 fout << "include(\"" << testIncludeFile << "\")" << std::endl;
308 const char* testIncludeFiles =
309 this->Makefile->GetProperty("TEST_INCLUDE_FILES");
310 if (testIncludeFiles) {
311 std::vector<std::string> includesList = cmExpandedList(testIncludeFiles);
312 for (std::string const& i : includesList) {
313 fout << "include(\"" << i << "\")" << std::endl;
317 // Ask each test generator to write its code.
318 for (const auto& tester : this->Makefile->GetTestGenerators()) {
319 tester->Compute(this);
320 tester->Generate(fout, config, configurationTypes);
322 using vec_t = std::vector<cmStateSnapshot>;
323 vec_t const& children = this->Makefile->GetStateSnapshot().GetChildren();
324 std::string parentBinDir = this->GetCurrentBinaryDirectory();
325 for (cmStateSnapshot const& i : children) {
326 // TODO: Use add_subdirectory instead?
327 std::string outP = i.GetDirectory().GetCurrentBinary();
328 outP = this->MaybeConvertToRelativePath(parentBinDir, outP);
329 outP = cmOutputConverter::EscapeForCMake(outP);
330 fout << "subdirs(" << outP << ")" << std::endl;
333 // Add directory labels property
334 const char* directoryLabels =
335 this->Makefile->GetDefinition("CMAKE_DIRECTORY_LABELS");
336 const char* labels = this->Makefile->GetProperty("LABELS");
338 if (labels || directoryLabels) {
339 fout << "set_directory_properties(PROPERTIES LABELS ";
341 fout << cmOutputConverter::EscapeForCMake(labels);
343 if (labels && directoryLabels) {
346 if (directoryLabels) {
347 fout << cmOutputConverter::EscapeForCMake(directoryLabels);
349 fout << ")" << std::endl;
353 void cmLocalGenerator::CreateEvaluationFileOutputs()
355 std::vector<std::string> const& configs =
356 this->Makefile->GetGeneratorConfigs();
357 for (std::string const& c : configs) {
358 this->CreateEvaluationFileOutputs(c);
362 void cmLocalGenerator::CreateEvaluationFileOutputs(std::string const& config)
364 for (const auto& geef : this->Makefile->GetEvaluationFiles()) {
365 geef->CreateOutputFile(this, config);
369 void cmLocalGenerator::ProcessEvaluationFiles(
370 std::vector<std::string>& generatedFiles)
372 for (const auto& geef : this->Makefile->GetEvaluationFiles()) {
373 geef->Generate(this);
374 if (cmSystemTools::GetFatalErrorOccured()) {
377 std::vector<std::string> files = geef->GetFiles();
378 std::sort(files.begin(), files.end());
380 std::vector<std::string> intersection;
381 std::set_intersection(files.begin(), files.end(), generatedFiles.begin(),
382 generatedFiles.end(),
383 std::back_inserter(intersection));
384 if (!intersection.empty()) {
385 cmSystemTools::Error("Files to be generated by multiple different "
387 cmWrap('"', intersection, '"', " "));
391 cm::append(generatedFiles, files);
392 std::inplace_merge(generatedFiles.begin(),
393 generatedFiles.end() - files.size(),
394 generatedFiles.end());
398 void cmLocalGenerator::GenerateInstallRules()
400 // Compute the install prefix.
401 const char* prefix = this->Makefile->GetDefinition("CMAKE_INSTALL_PREFIX");
403 #if defined(_WIN32) && !defined(__CYGWIN__)
404 std::string prefix_win32;
406 if (!cmSystemTools::GetEnv("SystemDrive", prefix_win32)) {
409 const char* project_name = this->Makefile->GetDefinition("PROJECT_NAME");
410 if (project_name && project_name[0]) {
411 prefix_win32 += "/Program Files/";
412 prefix_win32 += project_name;
414 prefix_win32 += "/InstalledCMakeProject";
416 prefix = prefix_win32.c_str();
418 #elif defined(__HAIKU__)
419 char dir[B_PATH_NAME_LENGTH];
421 if (find_directory(B_SYSTEM_DIRECTORY, -1, false, dir, sizeof(dir)) ==
425 prefix = "/boot/system";
430 prefix = "/usr/local";
433 if (const char* stagingPrefix =
434 this->Makefile->GetDefinition("CMAKE_STAGING_PREFIX")) {
435 prefix = stagingPrefix;
438 // Compute the set of configurations.
439 std::vector<std::string> configurationTypes;
440 const std::string& config =
441 this->Makefile->GetConfigurations(configurationTypes, false);
443 // Choose a default install configuration.
444 std::string default_config = config;
445 const char* default_order[] = { "RELEASE", "MINSIZEREL", "RELWITHDEBINFO",
447 for (const char** c = default_order; *c && default_config.empty(); ++c) {
448 for (std::string const& configurationType : configurationTypes) {
449 if (cmSystemTools::UpperCase(configurationType) == *c) {
450 default_config = configurationType;
454 if (default_config.empty() && !configurationTypes.empty()) {
455 default_config = configurationTypes[0];
458 // Create the install script file.
459 std::string file = this->StateSnapshot.GetDirectory().GetCurrentBinary();
460 std::string homedir = this->GetState()->GetBinaryDirectory();
461 int toplevel_install = 0;
462 if (file == homedir) {
463 toplevel_install = 1;
465 file += "/cmake_install.cmake";
466 cmGeneratedFileStream fout(file);
467 fout.SetCopyIfDifferent(true);
470 fout << "# Install script for directory: "
471 << this->StateSnapshot.GetDirectory().GetCurrentSource() << std::endl
473 fout << "# Set the install prefix" << std::endl
474 << "if(NOT DEFINED CMAKE_INSTALL_PREFIX)" << std::endl
475 << " set(CMAKE_INSTALL_PREFIX \"" << prefix << "\")" << std::endl
476 << "endif()" << std::endl
477 << R"(string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX )"
478 << "\"${CMAKE_INSTALL_PREFIX}\")" << std::endl
481 // Write support code for generating per-configuration install rules.
482 /* clang-format off */
484 "# Set the install configuration name.\n"
485 "if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME)\n"
487 " string(REGEX REPLACE \"^[^A-Za-z0-9_]+\" \"\"\n"
488 " CMAKE_INSTALL_CONFIG_NAME \"${BUILD_TYPE}\")\n"
490 " set(CMAKE_INSTALL_CONFIG_NAME \"" << default_config << "\")\n"
492 " message(STATUS \"Install configuration: "
493 "\\\"${CMAKE_INSTALL_CONFIG_NAME}\\\"\")\n"
496 /* clang-format on */
498 // Write support code for dealing with component-specific installs.
499 /* clang-format off */
501 "# Set the component getting installed.\n"
502 "if(NOT CMAKE_INSTALL_COMPONENT)\n"
504 " message(STATUS \"Install component: \\\"${COMPONENT}\\\"\")\n"
505 " set(CMAKE_INSTALL_COMPONENT \"${COMPONENT}\")\n"
507 " set(CMAKE_INSTALL_COMPONENT)\n"
511 /* clang-format on */
513 // Copy user-specified install options to the install code.
514 if (const char* so_no_exe =
515 this->Makefile->GetDefinition("CMAKE_INSTALL_SO_NO_EXE")) {
516 /* clang-format off */
518 "# Install shared libraries without execute permission?\n"
519 "if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE)\n"
520 " set(CMAKE_INSTALL_SO_NO_EXE \"" << so_no_exe << "\")\n"
523 /* clang-format on */
526 // Copy cmake cross compile state to install code.
527 if (const char* crosscompiling =
528 this->Makefile->GetDefinition("CMAKE_CROSSCOMPILING")) {
529 /* clang-format off */
531 "# Is this installation the result of a crosscompile?\n"
532 "if(NOT DEFINED CMAKE_CROSSCOMPILING)\n"
533 " set(CMAKE_CROSSCOMPILING \"" << crosscompiling << "\")\n"
536 /* clang-format on */
539 // Write default directory permissions.
540 if (const char* defaultDirPermissions = this->Makefile->GetDefinition(
541 "CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS")) {
542 /* clang-format off */
544 "# Set default install directory permissions.\n"
545 "if(NOT DEFINED CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS)\n"
546 " set(CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS \""
547 << defaultDirPermissions << "\")\n"
550 /* clang-format on */
553 // Ask each install generator to write its code.
554 cmPolicies::PolicyStatus status = this->GetPolicyStatus(cmPolicies::CMP0082);
555 auto const& installers = this->Makefile->GetInstallGenerators();
556 bool haveSubdirectoryInstall = false;
557 bool haveInstallAfterSubdirectory = false;
558 if (status == cmPolicies::WARN) {
559 for (const auto& installer : installers) {
560 installer->CheckCMP0082(haveSubdirectoryInstall,
561 haveInstallAfterSubdirectory);
562 installer->Generate(fout, config, configurationTypes);
565 for (const auto& installer : installers) {
566 installer->Generate(fout, config, configurationTypes);
570 // Write rules from old-style specification stored in targets.
571 this->GenerateTargetInstallRules(fout, config, configurationTypes);
573 // Include install scripts from subdirectories.
575 case cmPolicies::WARN:
576 if (haveInstallAfterSubdirectory &&
577 this->Makefile->PolicyOptionalWarningEnabled(
578 "CMAKE_POLICY_WARNING_CMP0082")) {
579 std::ostringstream e;
580 e << cmPolicies::GetPolicyWarning(cmPolicies::CMP0082) << "\n";
581 this->IssueMessage(MessageType::AUTHOR_WARNING, e.str());
584 case cmPolicies::OLD: {
585 std::vector<cmStateSnapshot> children =
586 this->Makefile->GetStateSnapshot().GetChildren();
587 if (!children.empty()) {
588 fout << "if(NOT CMAKE_INSTALL_LOCAL_ONLY)\n";
589 fout << " # Include the install script for each subdirectory.\n";
590 for (cmStateSnapshot const& c : children) {
591 if (!c.GetDirectory().GetPropertyAsBool("EXCLUDE_FROM_ALL")) {
592 std::string odir = c.GetDirectory().GetCurrentBinary();
593 cmSystemTools::ConvertToUnixSlashes(odir);
594 fout << " include(\"" << odir << "/cmake_install.cmake\")"
599 fout << "endif()\n\n";
603 case cmPolicies::REQUIRED_IF_USED:
604 case cmPolicies::REQUIRED_ALWAYS:
605 case cmPolicies::NEW:
606 // NEW behavior is handled in
607 // cmInstallSubdirectoryGenerator::GenerateScript()
611 // Record the install manifest.
612 if (toplevel_install) {
613 /* clang-format off */
615 "if(CMAKE_INSTALL_COMPONENT)\n"
616 " set(CMAKE_INSTALL_MANIFEST \"install_manifest_"
617 "${CMAKE_INSTALL_COMPONENT}.txt\")\n"
619 " set(CMAKE_INSTALL_MANIFEST \"install_manifest.txt\")\n"
622 "string(REPLACE \";\" \"\\n\" CMAKE_INSTALL_MANIFEST_CONTENT\n"
623 " \"${CMAKE_INSTALL_MANIFEST_FILES}\")\n"
624 "file(WRITE \"" << homedir << "/${CMAKE_INSTALL_MANIFEST}\"\n"
625 " \"${CMAKE_INSTALL_MANIFEST_CONTENT}\")\n";
626 /* clang-format on */
630 void cmLocalGenerator::AddGeneratorTarget(
631 std::unique_ptr<cmGeneratorTarget> gt)
633 cmGeneratorTarget* gt_ptr = gt.get();
635 this->GeneratorTargets.push_back(std::move(gt));
636 this->GeneratorTargetSearchIndex.emplace(gt_ptr->GetName(), gt_ptr);
637 this->GlobalGenerator->IndexGeneratorTarget(gt_ptr);
640 void cmLocalGenerator::AddImportedGeneratorTarget(cmGeneratorTarget* gt)
642 this->ImportedGeneratorTargets.emplace(gt->GetName(), gt);
643 this->GlobalGenerator->IndexGeneratorTarget(gt);
646 void cmLocalGenerator::AddOwnedImportedGeneratorTarget(
647 std::unique_ptr<cmGeneratorTarget> gt)
649 this->OwnedImportedGeneratorTargets.push_back(std::move(gt));
652 cmGeneratorTarget* cmLocalGenerator::FindLocalNonAliasGeneratorTarget(
653 const std::string& name) const
655 auto ti = this->GeneratorTargetSearchIndex.find(name);
656 if (ti != this->GeneratorTargetSearchIndex.end()) {
662 void cmLocalGenerator::ComputeTargetManifest()
664 // Collect the set of configuration types.
665 std::vector<std::string> configNames;
666 this->Makefile->GetConfigurations(configNames);
667 if (configNames.empty()) {
668 configNames.emplace_back();
671 // Add our targets to the manifest for each configuration.
672 const auto& targets = this->GetGeneratorTargets();
673 for (const auto& target : targets) {
674 if (target->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
677 for (std::string const& c : configNames) {
678 target->ComputeTargetManifest(c);
683 bool cmLocalGenerator::ComputeTargetCompileFeatures()
685 // Collect the set of configuration types.
686 std::vector<std::string> configNames;
687 this->Makefile->GetConfigurations(configNames);
688 if (configNames.empty()) {
689 configNames.emplace_back();
692 using LanguagePair = std::pair<std::string, std::string>;
693 std::vector<LanguagePair> pairedLanguages{ { "OBJC", "C" },
696 std::set<LanguagePair> inferredEnabledLanguages;
697 for (auto const& lang : pairedLanguages) {
698 if (this->Makefile->GetState()->GetLanguageEnabled(lang.first)) {
699 inferredEnabledLanguages.insert(lang);
703 // Process compile features of all targets.
704 const auto& targets = this->GetGeneratorTargets();
705 for (const auto& target : targets) {
706 for (std::string const& c : configNames) {
707 if (!target->ComputeCompileFeatures(c)) {
712 // Now that C/C++ _STANDARD values have been computed
713 // set the values to ObjC/ObjCXX _STANDARD variables
714 if (target->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
715 auto copyStandardToObjLang = [&](LanguagePair const& lang) -> bool {
716 if (!target->GetProperty(cmStrCat(lang.first, "_STANDARD"))) {
718 target->GetProperty(cmStrCat(lang.second, "_STANDARD"));
720 standard = this->Makefile->GetDefinition(
721 cmStrCat("CMAKE_", lang.second, "_STANDARD_DEFAULT"));
723 target->Target->SetProperty(cmStrCat(lang.first, "_STANDARD"),
729 auto copyPropertyToObjLang = [&](LanguagePair const& lang,
730 const char* property) {
731 if (!target->GetProperty(cmStrCat(lang.first, property)) &&
732 target->GetProperty(cmStrCat(lang.second, property))) {
733 target->Target->SetProperty(
734 cmStrCat(lang.first, property),
735 target->GetProperty(cmStrCat(lang.second, property)));
738 for (auto const& lang : pairedLanguages) {
739 if (copyStandardToObjLang(lang)) {
740 copyPropertyToObjLang(lang, "_STANDARD_REQUIRED");
741 copyPropertyToObjLang(lang, "_EXTENSIONS");
744 if (const char* standard = target->GetProperty("CUDA_STANDARD")) {
745 if (std::string{ standard } == "98") {
746 target->Target->SetProperty("CUDA_STANDARD", "03");
755 bool cmLocalGenerator::IsRootMakefile() const
757 return !this->StateSnapshot.GetBuildsystemDirectoryParent().IsValid();
760 cmState* cmLocalGenerator::GetState() const
762 return this->GlobalGenerator->GetCMakeInstance()->GetState();
765 cmStateSnapshot cmLocalGenerator::GetStateSnapshot() const
767 return this->Makefile->GetStateSnapshot();
770 const char* cmLocalGenerator::GetRuleLauncher(cmGeneratorTarget* target,
771 const std::string& prop)
774 return target->GetProperty(prop);
776 return this->Makefile->GetProperty(prop);
779 std::string cmLocalGenerator::ConvertToIncludeReference(
780 std::string const& path, OutputFormat format, bool forceFullPaths)
782 static_cast<void>(forceFullPaths);
783 return this->ConvertToOutputForExisting(path, format);
786 std::string cmLocalGenerator::GetIncludeFlags(
787 const std::vector<std::string>& includeDirs, cmGeneratorTarget* target,
788 const std::string& lang, bool forceFullPaths, bool forResponseFile,
789 const std::string& config)
795 std::vector<std::string> includes = includeDirs;
796 MoveSystemIncludesToEnd(includes, config, lang, target);
798 OutputFormat shellFormat = forResponseFile ? RESPONSE : SHELL;
799 std::ostringstream includeFlags;
801 std::string const& includeFlag =
802 this->Makefile->GetSafeDefinition(cmStrCat("CMAKE_INCLUDE_FLAG_", lang));
804 this->Makefile->GetDefinition(cmStrCat("CMAKE_INCLUDE_FLAG_SEP_", lang));
805 bool quotePaths = false;
806 if (this->Makefile->GetDefinition("CMAKE_QUOTE_INCLUDE_PATHS")) {
809 bool repeatFlag = true;
810 // should the include flag be repeated like ie. -IA -IB
814 // if there is a separator then the flag is not repeated but is only
815 // given once i.e. -classpath a:b:c
819 // Support special system include flag if it is available and the
820 // normal flag is repeated for each directory.
821 const char* sysIncludeFlag = nullptr;
823 sysIncludeFlag = this->Makefile->GetDefinition(
824 cmStrCat("CMAKE_INCLUDE_SYSTEM_FLAG_", lang));
827 const char* fwSearchFlag = this->Makefile->GetDefinition(
828 cmStrCat("CMAKE_", lang, "_FRAMEWORK_SEARCH_FLAG"));
829 const char* sysFwSearchFlag = this->Makefile->GetDefinition(
830 cmStrCat("CMAKE_", lang, "_SYSTEM_FRAMEWORK_SEARCH_FLAG"));
832 bool flagUsed = false;
833 std::set<std::string> emitted;
835 emitted.insert("/System/Library/Frameworks");
837 for (std::string const& i : includes) {
838 if (fwSearchFlag && *fwSearchFlag && this->Makefile->IsOn("APPLE") &&
839 cmSystemTools::IsPathToFramework(i)) {
840 std::string const frameworkDir =
841 cmSystemTools::CollapseFullPath(cmStrCat(i, "/../"));
842 if (emitted.insert(frameworkDir).second) {
843 if (sysFwSearchFlag && target &&
844 target->IsSystemIncludeDirectory(i, config, lang)) {
845 includeFlags << sysFwSearchFlag;
847 includeFlags << fwSearchFlag;
849 includeFlags << this->ConvertToOutputFormat(frameworkDir, shellFormat)
855 if (!flagUsed || repeatFlag) {
856 if (sysIncludeFlag && target &&
857 target->IsSystemIncludeDirectory(i, config, lang)) {
858 includeFlags << sysIncludeFlag;
860 includeFlags << includeFlag;
864 std::string includePath =
865 this->ConvertToIncludeReference(i, shellFormat, forceFullPaths);
866 if (quotePaths && !includePath.empty() && includePath.front() != '\"') {
867 includeFlags << "\"";
869 includeFlags << includePath;
870 if (quotePaths && !includePath.empty() && includePath.front() != '\"') {
871 includeFlags << "\"";
875 std::string flags = includeFlags.str();
876 // remove trailing separators
877 if ((sep[0] != ' ') && !flags.empty() && flags.back() == sep[0]) {
883 void cmLocalGenerator::AddCompileOptions(std::string& flags,
884 cmGeneratorTarget* target,
885 const std::string& lang,
886 const std::string& config)
888 std::vector<BT<std::string>> tmpFlags;
889 this->AddCompileOptions(tmpFlags, target, lang, config);
890 this->AppendFlags(flags, tmpFlags);
893 void cmLocalGenerator::AddCompileOptions(std::vector<BT<std::string>>& flags,
894 cmGeneratorTarget* target,
895 const std::string& lang,
896 const std::string& config)
898 std::string langFlagRegexVar = std::string("CMAKE_") + lang + "_FLAG_REGEX";
900 if (const char* langFlagRegexStr =
901 this->Makefile->GetDefinition(langFlagRegexVar)) {
902 // Filter flags acceptable to this language.
903 if (const char* targetFlags = target->GetProperty("COMPILE_FLAGS")) {
904 std::vector<std::string> opts;
905 cmSystemTools::ParseWindowsCommandLine(targetFlags, opts);
906 // Re-escape these flags since COMPILE_FLAGS were already parsed
907 // as a command line above.
908 std::string compileOpts;
909 this->AppendCompileOptions(compileOpts, opts, langFlagRegexStr);
910 if (!compileOpts.empty()) {
911 flags.emplace_back(std::move(compileOpts));
914 std::vector<BT<std::string>> targetCompileOpts =
915 target->GetCompileOptions(config, lang);
916 // COMPILE_OPTIONS are escaped.
917 this->AppendCompileOptions(flags, targetCompileOpts, langFlagRegexStr);
920 if (const char* targetFlags = target->GetProperty("COMPILE_FLAGS")) {
921 // COMPILE_FLAGS are not escaped for historical reasons.
922 std::string compileFlags;
923 this->AppendFlags(compileFlags, targetFlags);
924 if (!compileFlags.empty()) {
925 flags.emplace_back(std::move(compileFlags));
928 std::vector<BT<std::string>> targetCompileOpts =
929 target->GetCompileOptions(config, lang);
930 // COMPILE_OPTIONS are escaped.
931 this->AppendCompileOptions(flags, targetCompileOpts);
934 for (auto const& it : target->GetMaxLanguageStandards()) {
935 const char* standard = target->GetProperty(it.first + "_STANDARD");
939 if (this->Makefile->IsLaterStandard(it.first, standard, it.second)) {
940 std::ostringstream e;
941 e << "The COMPILE_FEATURES property of target \"" << target->GetName()
942 << "\" was evaluated when computing the link "
943 "implementation, and the \""
944 << it.first << "_STANDARD\" was \"" << it.second
945 << "\" for that computation. Computing the "
946 "COMPILE_FEATURES based on the link implementation resulted in a "
948 << it.first << "_STANDARD\" \"" << standard
950 "This is not permitted. The COMPILE_FEATURES may not both depend "
952 "and be depended on by the link implementation."
954 this->IssueMessage(MessageType::FATAL_ERROR, e.str());
959 std::string compReqFlag;
960 this->AddCompilerRequirementFlag(compReqFlag, target, lang);
961 if (!compReqFlag.empty()) {
962 flags.emplace_back(std::move(compReqFlag));
965 // Add compile flag for the MSVC compiler only.
966 cmMakefile* mf = this->GetMakefile();
967 if (const char* jmc =
968 mf->GetDefinition("CMAKE_" + lang + "_COMPILE_OPTIONS_JMC")) {
970 // Handle Just My Code debugging flags, /JMC.
971 // If the target is a Managed C++ one, /JMC is not compatible.
972 if (target->GetManagedType(config) !=
973 cmGeneratorTarget::ManagedType::Managed) {
974 // add /JMC flags if target property VS_JUST_MY_CODE_DEBUGGING is set
976 if (char const* jmcExprGen =
977 target->GetProperty("VS_JUST_MY_CODE_DEBUGGING")) {
978 std::string isJMCEnabled =
979 cmGeneratorExpression::Evaluate(jmcExprGen, this, config);
980 if (cmIsOn(isJMCEnabled)) {
981 std::vector<std::string> optVec = cmExpandedList(jmc);
982 std::string jmcFlags;
983 this->AppendCompileOptions(jmcFlags, optVec);
984 if (!jmcFlags.empty()) {
985 flags.emplace_back(std::move(jmcFlags));
993 cmTarget* cmLocalGenerator::AddCustomCommandToTarget(
994 const std::string& target, const std::vector<std::string>& byproducts,
995 const std::vector<std::string>& depends,
996 const cmCustomCommandLines& commandLines, cmCustomCommandType type,
997 const char* comment, const char* workingDir, bool escapeOldStyle,
998 bool uses_terminal, const std::string& depfile, const std::string& job_pool,
999 bool command_expand_lists, cmObjectLibraryCommands objLibCommands)
1001 cmTarget* t = this->Makefile->GetCustomCommandTarget(
1002 target, objLibCommands, this->DirectoryBacktrace);
1007 detail::AddCustomCommandToTarget(
1008 *this, this->DirectoryBacktrace, cmCommandOrigin::Generator, t, byproducts,
1009 depends, commandLines, type, comment, workingDir, escapeOldStyle,
1010 uses_terminal, depfile, job_pool, command_expand_lists);
1015 cmSourceFile* cmLocalGenerator::AddCustomCommandToOutput(
1016 const std::string& output, const std::vector<std::string>& depends,
1017 const std::string& main_dependency, const cmCustomCommandLines& commandLines,
1018 const char* comment, const char* workingDir, bool replace,
1019 bool escapeOldStyle, bool uses_terminal, bool command_expand_lists,
1020 const std::string& depfile, const std::string& job_pool)
1022 std::vector<std::string> no_byproducts;
1023 cmImplicitDependsList no_implicit_depends;
1024 return this->AddCustomCommandToOutput(
1025 { output }, no_byproducts, depends, main_dependency, no_implicit_depends,
1026 commandLines, comment, workingDir, replace, escapeOldStyle, uses_terminal,
1027 command_expand_lists, depfile, job_pool);
1030 cmSourceFile* cmLocalGenerator::AddCustomCommandToOutput(
1031 const std::vector<std::string>& outputs,
1032 const std::vector<std::string>& byproducts,
1033 const std::vector<std::string>& depends, const std::string& main_dependency,
1034 const cmImplicitDependsList& implicit_depends,
1035 const cmCustomCommandLines& commandLines, const char* comment,
1036 const char* workingDir, bool replace, bool escapeOldStyle,
1037 bool uses_terminal, bool command_expand_lists, const std::string& depfile,
1038 const std::string& job_pool)
1040 // Make sure there is at least one output.
1041 if (outputs.empty()) {
1042 cmSystemTools::Error("Attempt to add a custom rule with no output!");
1046 return detail::AddCustomCommandToOutput(
1047 *this, this->DirectoryBacktrace, cmCommandOrigin::Generator, outputs,
1048 byproducts, depends, main_dependency, implicit_depends, commandLines,
1049 comment, workingDir, replace, escapeOldStyle, uses_terminal,
1050 command_expand_lists, depfile, job_pool);
1053 cmTarget* cmLocalGenerator::AddUtilityCommand(
1054 const std::string& utilityName, bool excludeFromAll, const char* workingDir,
1055 const std::vector<std::string>& byproducts,
1056 const std::vector<std::string>& depends,
1057 const cmCustomCommandLines& commandLines, bool escapeOldStyle,
1058 const char* comment, bool uses_terminal, bool command_expand_lists,
1059 const std::string& job_pool)
1062 this->Makefile->AddNewUtilityTarget(utilityName, excludeFromAll);
1063 target->SetIsGeneratorProvided(true);
1065 if (commandLines.empty() && depends.empty()) {
1069 detail::AddUtilityCommand(
1070 *this, this->DirectoryBacktrace, cmCommandOrigin::Generator, target,
1071 this->Makefile->GetUtilityOutput(target), workingDir, byproducts, depends,
1072 commandLines, escapeOldStyle, comment, uses_terminal, command_expand_lists,
1078 std::vector<BT<std::string>> cmLocalGenerator::GetIncludeDirectoriesImplicit(
1079 cmGeneratorTarget const* target, std::string const& lang,
1080 std::string const& config, bool stripImplicitDirs,
1081 bool appendAllImplicitDirs) const
1083 std::vector<BT<std::string>> result;
1084 // Do not repeat an include path.
1085 std::set<std::string> emitted;
1087 auto emitDir = [&result, &emitted](std::string const& dir) {
1088 if (emitted.insert(dir).second) {
1089 result.emplace_back(dir);
1093 auto emitBT = [&result, &emitted](BT<std::string> const& dir) {
1094 if (emitted.insert(dir.Value).second) {
1095 result.emplace_back(dir);
1099 // When automatic include directories are requested for a build then
1100 // include the source and binary directories at the beginning of the
1101 // include path to approximate include file behavior for an
1102 // in-source build. This does not account for the case of a source
1103 // file in a subdirectory of the current source directory but we
1104 // cannot fix this because not all native build tools support
1105 // per-source-file include paths.
1106 if (this->Makefile->IsOn("CMAKE_INCLUDE_CURRENT_DIR")) {
1107 // Current binary directory
1108 emitDir(this->StateSnapshot.GetDirectory().GetCurrentBinary());
1109 // Current source directory
1110 emitDir(this->StateSnapshot.GetDirectory().GetCurrentSource());
1117 // Standard include directories to be added unconditionally at the end.
1118 // These are intended to simulate additional implicit include directories.
1119 std::vector<std::string> userStandardDirs;
1121 std::string const value = this->Makefile->GetSafeDefinition(
1122 cmStrCat("CMAKE_", lang, "_STANDARD_INCLUDE_DIRECTORIES"));
1123 cmExpandList(value, userStandardDirs);
1124 for (std::string& usd : userStandardDirs) {
1125 cmSystemTools::ConvertToUnixSlashes(usd);
1129 // Implicit include directories
1130 std::vector<std::string> implicitDirs;
1131 std::set<std::string> implicitSet;
1132 // Include directories to be excluded as if they were implicit.
1133 std::set<std::string> implicitExclude;
1135 // Raw list of implicit include directories
1136 // Start with "standard" directories that we unconditionally add below.
1137 std::vector<std::string> impDirVec = userStandardDirs;
1139 // Load implicit include directories for this language.
1140 // We ignore this for Fortran because:
1141 // * There are no standard library headers to avoid overriding.
1142 // * Compilers like gfortran do not search their own implicit include
1143 // directories for modules ('.mod' files).
1144 if (lang != "Fortran") {
1145 const char* value = this->Makefile->GetDefinition(
1146 cmStrCat("CMAKE_", lang, "_IMPLICIT_INCLUDE_DIRECTORIES"));
1147 if (value != nullptr) {
1148 size_t const impDirVecOldSize = impDirVec.size();
1149 cmExpandList(value, impDirVec);
1150 // FIXME: Use cmRange with 'advance()' when it supports non-const.
1151 for (size_t i = impDirVecOldSize; i < impDirVec.size(); ++i) {
1152 cmSystemTools::ConvertToUnixSlashes(impDirVec[i]);
1157 // The Platform/UnixPaths module used to hard-code /usr/include for C, CXX,
1158 // and CUDA in CMAKE_<LANG>_IMPLICIT_INCLUDE_DIRECTORIES, but those
1159 // variables are now computed. On macOS the /usr/include directory is
1160 // inside the platform SDK so the computed value does not contain it
1161 // directly. In this case adding -I/usr/include can hide SDK headers so we
1162 // must still exclude it.
1163 if ((lang == "C" || lang == "CXX" || lang == "CUDA") &&
1164 !cmContains(impDirVec, "/usr/include") &&
1165 std::find_if(impDirVec.begin(), impDirVec.end(),
1166 [](std::string const& d) {
1167 return cmHasLiteralSuffix(d, "/usr/include");
1168 }) != impDirVec.end()) {
1169 // Only exclude this hard coded path for backwards compatibility.
1170 implicitExclude.emplace("/usr/include");
1173 for (std::string const& i : impDirVec) {
1174 if (implicitSet.insert(this->GlobalGenerator->GetRealPath(i)).second) {
1175 implicitDirs.emplace_back(i);
1180 // Checks if this is not an excluded (implicit) include directory.
1181 auto notExcluded = [this, &implicitSet, &implicitExclude,
1182 &lang](std::string const& dir) {
1184 // Do not exclude directories that are not in an excluded set.
1185 ((!cmContains(implicitSet, this->GlobalGenerator->GetRealPath(dir))) &&
1186 (!cmContains(implicitExclude, dir)))
1187 // Do not exclude entries of the CPATH environment variable even though
1188 // they are implicitly searched by the compiler. They are meant to be
1189 // user-specified directories that can be re-ordered or converted to
1190 // -isystem without breaking real compiler builtin headers.
1191 || ((lang == "C" || lang == "CXX") && cmContains(this->EnvCPATH, dir)));
1194 // Get the target-specific include directories.
1195 std::vector<BT<std::string>> userDirs =
1196 target->GetIncludeDirectories(config, lang);
1198 // Support putting all the in-project include directories first if
1199 // it is requested by the project.
1200 if (this->Makefile->IsOn("CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE")) {
1201 std::string const& topSourceDir = this->GetState()->GetSourceDirectory();
1202 std::string const& topBinaryDir = this->GetState()->GetBinaryDirectory();
1203 for (BT<std::string> const& udr : userDirs) {
1204 // Emit this directory only if it is a subdirectory of the
1205 // top-level source or binary tree.
1206 if (cmSystemTools::ComparePath(udr.Value, topSourceDir) ||
1207 cmSystemTools::ComparePath(udr.Value, topBinaryDir) ||
1208 cmSystemTools::IsSubDirectory(udr.Value, topSourceDir) ||
1209 cmSystemTools::IsSubDirectory(udr.Value, topBinaryDir)) {
1210 if (notExcluded(udr.Value)) {
1217 // Emit remaining non implicit user direcories.
1218 for (BT<std::string> const& udr : userDirs) {
1219 if (notExcluded(udr.Value)) {
1225 MoveSystemIncludesToEnd(result, config, lang, target);
1227 // Append standard include directories for this language.
1228 userDirs.reserve(userDirs.size() + userStandardDirs.size());
1229 for (std::string& usd : userStandardDirs) {
1231 userDirs.emplace_back(std::move(usd));
1234 // Append compiler implicit include directories
1235 if (!stripImplicitDirs) {
1236 // Append implicit directories that were requested by the user only
1237 for (BT<std::string> const& udr : userDirs) {
1238 if (cmContains(implicitSet, cmSystemTools::GetRealPath(udr.Value))) {
1242 // Append remaining implicit directories (on demand)
1243 if (appendAllImplicitDirs) {
1244 for (std::string& imd : implicitDirs) {
1253 void cmLocalGenerator::GetIncludeDirectoriesImplicit(
1254 std::vector<std::string>& dirs, cmGeneratorTarget const* target,
1255 const std::string& lang, const std::string& config, bool stripImplicitDirs,
1256 bool appendAllImplicitDirs) const
1258 std::vector<BT<std::string>> tmp = this->GetIncludeDirectoriesImplicit(
1259 target, lang, config, stripImplicitDirs, appendAllImplicitDirs);
1260 dirs.reserve(dirs.size() + tmp.size());
1261 for (BT<std::string>& v : tmp) {
1262 dirs.emplace_back(std::move(v.Value));
1266 std::vector<BT<std::string>> cmLocalGenerator::GetIncludeDirectories(
1267 cmGeneratorTarget const* target, std::string const& lang,
1268 std::string const& config) const
1270 return this->GetIncludeDirectoriesImplicit(target, lang, config);
1273 void cmLocalGenerator::GetIncludeDirectories(std::vector<std::string>& dirs,
1274 cmGeneratorTarget const* target,
1275 const std::string& lang,
1276 const std::string& config) const
1278 this->GetIncludeDirectoriesImplicit(dirs, target, lang, config);
1281 void cmLocalGenerator::GetStaticLibraryFlags(std::string& flags,
1282 std::string const& config,
1283 std::string const& linkLanguage,
1284 cmGeneratorTarget* target)
1286 std::vector<BT<std::string>> tmpFlags =
1287 this->GetStaticLibraryFlags(config, linkLanguage, target);
1288 this->AppendFlags(flags, tmpFlags);
1291 std::vector<BT<std::string>> cmLocalGenerator::GetStaticLibraryFlags(
1292 std::string const& config, std::string const& linkLanguage,
1293 cmGeneratorTarget* target)
1295 std::vector<BT<std::string>> flags;
1296 if (linkLanguage != "Swift") {
1297 std::string staticLibFlags;
1300 this->Makefile->GetSafeDefinition("CMAKE_STATIC_LINKER_FLAGS"));
1301 if (!config.empty()) {
1302 std::string name = "CMAKE_STATIC_LINKER_FLAGS_" + config;
1303 this->AppendFlags(staticLibFlags,
1304 this->Makefile->GetSafeDefinition(name));
1306 if (!staticLibFlags.empty()) {
1307 flags.emplace_back(std::move(staticLibFlags));
1311 std::string staticLibFlags;
1312 this->AppendFlags(staticLibFlags,
1313 target->GetSafeProperty("STATIC_LIBRARY_FLAGS"));
1314 if (!config.empty()) {
1315 std::string name = "STATIC_LIBRARY_FLAGS_" + config;
1316 this->AppendFlags(staticLibFlags, target->GetSafeProperty(name));
1319 if (!staticLibFlags.empty()) {
1320 flags.emplace_back(std::move(staticLibFlags));
1323 std::vector<BT<std::string>> staticLibOpts =
1324 target->GetStaticLibraryLinkOptions(config, linkLanguage);
1325 // STATIC_LIBRARY_OPTIONS are escaped.
1326 this->AppendCompileOptions(flags, staticLibOpts);
1331 void cmLocalGenerator::GetTargetFlags(
1332 cmLinkLineComputer* linkLineComputer, const std::string& config,
1333 std::string& linkLibs, std::string& flags, std::string& linkFlags,
1334 std::string& frameworkPath, std::string& linkPath, cmGeneratorTarget* target)
1336 std::vector<BT<std::string>> linkFlagsList;
1337 std::vector<BT<std::string>> linkPathList;
1338 std::vector<BT<std::string>> linkLibsList;
1339 this->GetTargetFlags(linkLineComputer, config, linkLibsList, flags,
1340 linkFlagsList, frameworkPath, linkPathList, target);
1341 this->AppendFlags(linkFlags, linkFlagsList);
1342 this->AppendFlags(linkPath, linkPathList);
1343 this->AppendFlags(linkLibs, linkLibsList);
1346 void cmLocalGenerator::GetTargetFlags(
1347 cmLinkLineComputer* linkLineComputer, const std::string& config,
1348 std::vector<BT<std::string>>& linkLibs, std::string& flags,
1349 std::vector<BT<std::string>>& linkFlags, std::string& frameworkPath,
1350 std::vector<BT<std::string>>& linkPath, cmGeneratorTarget* target)
1352 const std::string buildType = cmSystemTools::UpperCase(config);
1353 cmComputeLinkInformation* pcli = target->GetLinkInformation(config);
1354 const char* libraryLinkVariable =
1355 "CMAKE_SHARED_LINKER_FLAGS"; // default to shared library
1357 const std::string linkLanguage =
1358 linkLineComputer->GetLinkerLanguage(target, buildType);
1360 switch (target->GetType()) {
1361 case cmStateEnums::STATIC_LIBRARY:
1362 linkFlags = this->GetStaticLibraryFlags(buildType, linkLanguage, target);
1363 if (pcli && dynamic_cast<cmLinkLineDeviceComputer*>(linkLineComputer)) {
1364 // Compute the required cuda device link libraries when
1365 // resolving cuda device symbols
1366 this->OutputLinkLibraries(pcli, linkLineComputer, linkLibs,
1367 frameworkPath, linkPath);
1370 case cmStateEnums::MODULE_LIBRARY:
1371 libraryLinkVariable = "CMAKE_MODULE_LINKER_FLAGS";
1373 case cmStateEnums::SHARED_LIBRARY: {
1374 std::string sharedLibFlags;
1375 if (linkLanguage != "Swift") {
1376 sharedLibFlags = cmStrCat(
1377 this->Makefile->GetSafeDefinition(libraryLinkVariable), ' ');
1378 if (!buildType.empty()) {
1379 std::string build = cmStrCat(libraryLinkVariable, '_', buildType);
1380 sharedLibFlags += this->Makefile->GetSafeDefinition(build);
1381 sharedLibFlags += " ";
1383 if (this->Makefile->IsOn("WIN32") &&
1384 !(this->Makefile->IsOn("CYGWIN") ||
1385 this->Makefile->IsOn("MINGW"))) {
1386 std::vector<cmSourceFile*> sources;
1387 target->GetSourceFiles(sources, buildType);
1388 std::string defFlag =
1389 this->Makefile->GetSafeDefinition("CMAKE_LINK_DEF_FILE_FLAG");
1390 for (cmSourceFile* sf : sources) {
1391 if (sf->GetExtension() == "def") {
1392 sharedLibFlags += defFlag;
1393 sharedLibFlags += this->ConvertToOutputFormat(
1394 cmSystemTools::CollapseFullPath(sf->ResolveFullPath()), SHELL);
1395 sharedLibFlags += " ";
1401 const char* targetLinkFlags = target->GetProperty("LINK_FLAGS");
1402 if (targetLinkFlags) {
1403 sharedLibFlags += targetLinkFlags;
1404 sharedLibFlags += " ";
1406 if (!buildType.empty()) {
1408 target->GetProperty(cmStrCat("LINK_FLAGS_", buildType));
1409 if (targetLinkFlags) {
1410 sharedLibFlags += targetLinkFlags;
1411 sharedLibFlags += " ";
1415 if (!sharedLibFlags.empty()) {
1416 linkFlags.emplace_back(std::move(sharedLibFlags));
1419 std::vector<BT<std::string>> linkOpts =
1420 target->GetLinkOptions(config, linkLanguage);
1421 // LINK_OPTIONS are escaped.
1422 this->AppendCompileOptions(linkFlags, linkOpts);
1424 this->OutputLinkLibraries(pcli, linkLineComputer, linkLibs,
1425 frameworkPath, linkPath);
1428 case cmStateEnums::EXECUTABLE: {
1429 std::string exeFlags;
1430 if (linkLanguage != "Swift") {
1431 exeFlags = this->Makefile->GetSafeDefinition("CMAKE_EXE_LINKER_FLAGS");
1433 if (!buildType.empty()) {
1434 exeFlags += this->Makefile->GetSafeDefinition(
1435 cmStrCat("CMAKE_EXE_LINKER_FLAGS_", buildType));
1438 if (linkLanguage.empty()) {
1439 cmSystemTools::Error(
1440 "CMake can not determine linker language for target: " +
1445 if (target->GetPropertyAsBool("WIN32_EXECUTABLE")) {
1447 this->Makefile->GetSafeDefinition("CMAKE_CREATE_WIN32_EXE");
1451 this->Makefile->GetSafeDefinition("CMAKE_CREATE_CONSOLE_EXE");
1455 if (target->IsExecutableWithExports()) {
1456 exeFlags += this->Makefile->GetSafeDefinition(
1457 cmStrCat("CMAKE_EXE_EXPORTS_", linkLanguage, "_FLAG"));
1462 this->AddLanguageFlagsForLinking(flags, target, linkLanguage, buildType);
1464 this->OutputLinkLibraries(pcli, linkLineComputer, linkLibs,
1465 frameworkPath, linkPath);
1468 if (cmIsOn(this->Makefile->GetDefinition("BUILD_SHARED_LIBS"))) {
1469 std::string sFlagVar = std::string("CMAKE_SHARED_BUILD_") +
1470 linkLanguage + std::string("_FLAGS");
1471 exeFlags += this->Makefile->GetSafeDefinition(sFlagVar);
1475 std::string cmp0065Flags =
1476 this->GetLinkLibsCMP0065(linkLanguage, *target);
1477 if (!cmp0065Flags.empty()) {
1478 exeFlags += cmp0065Flags;
1482 const char* targetLinkFlags = target->GetProperty("LINK_FLAGS");
1483 if (targetLinkFlags) {
1484 exeFlags += targetLinkFlags;
1487 if (!buildType.empty()) {
1489 target->GetProperty(cmStrCat("LINK_FLAGS_", buildType));
1490 if (targetLinkFlags) {
1491 exeFlags += targetLinkFlags;
1496 if (!exeFlags.empty()) {
1497 linkFlags.emplace_back(std::move(exeFlags));
1500 std::vector<BT<std::string>> linkOpts =
1501 target->GetLinkOptions(config, linkLanguage);
1502 // LINK_OPTIONS are escaped.
1503 this->AppendCompileOptions(linkFlags, linkOpts);
1509 std::string extraLinkFlags;
1510 this->AppendPositionIndependentLinkerFlags(extraLinkFlags, target, config,
1512 this->AppendIPOLinkerFlags(extraLinkFlags, target, config, linkLanguage);
1514 if (!extraLinkFlags.empty()) {
1515 linkFlags.emplace_back(std::move(extraLinkFlags));
1519 void cmLocalGenerator::GetTargetCompileFlags(cmGeneratorTarget* target,
1520 std::string const& config,
1521 std::string const& lang,
1524 std::vector<BT<std::string>> tmpFlags =
1525 this->GetTargetCompileFlags(target, config, lang);
1526 this->AppendFlags(flags, tmpFlags);
1529 std::vector<BT<std::string>> cmLocalGenerator::GetTargetCompileFlags(
1530 cmGeneratorTarget* target, std::string const& config,
1531 std::string const& lang)
1533 std::vector<BT<std::string>> flags;
1534 std::string compileFlags;
1536 cmMakefile* mf = this->GetMakefile();
1538 // Add language-specific flags.
1539 this->AddLanguageFlags(compileFlags, target, lang, config);
1541 if (target->IsIPOEnabled(lang, config)) {
1542 this->AppendFeatureOptions(compileFlags, lang, "IPO");
1545 this->AddArchitectureFlags(compileFlags, target, lang, config);
1547 if (lang == "Fortran") {
1548 this->AppendFlags(compileFlags,
1549 this->GetTargetFortranFlags(target, config));
1552 this->AddCMP0018Flags(compileFlags, target, lang, config);
1553 this->AddVisibilityPresetFlags(compileFlags, target, lang);
1554 this->AppendFlags(compileFlags, mf->GetDefineFlags());
1555 this->AppendFlags(compileFlags,
1556 this->GetFrameworkFlags(lang, config, target));
1558 if (!compileFlags.empty()) {
1559 flags.emplace_back(std::move(compileFlags));
1561 this->AddCompileOptions(flags, target, lang, config);
1565 static std::string GetFrameworkFlags(const std::string& lang,
1566 const std::string& config,
1567 cmGeneratorTarget* target)
1569 cmLocalGenerator* lg = target->GetLocalGenerator();
1570 cmMakefile* mf = lg->GetMakefile();
1572 if (!mf->IsOn("APPLE")) {
1573 return std::string();
1576 std::string fwSearchFlagVar = "CMAKE_" + lang + "_FRAMEWORK_SEARCH_FLAG";
1577 const char* fwSearchFlag = mf->GetDefinition(fwSearchFlagVar);
1578 if (!(fwSearchFlag && *fwSearchFlag)) {
1579 return std::string();
1582 std::set<std::string> emitted;
1583 #ifdef __APPLE__ /* don't insert this when crosscompiling e.g. to iphone */
1584 emitted.insert("/System/Library/Frameworks");
1586 std::vector<std::string> includes;
1588 lg->GetIncludeDirectories(includes, target, "C", config);
1589 // check all include directories for frameworks as this
1590 // will already have added a -F for the framework
1591 for (std::string const& include : includes) {
1592 if (lg->GetGlobalGenerator()->NameResolvesToFramework(include)) {
1593 std::string frameworkDir = cmStrCat(include, "/../");
1594 frameworkDir = cmSystemTools::CollapseFullPath(frameworkDir);
1595 emitted.insert(frameworkDir);
1600 if (cmComputeLinkInformation* cli = target->GetLinkInformation(config)) {
1601 std::vector<std::string> const& frameworks = cli->GetFrameworkPaths();
1602 for (std::string const& framework : frameworks) {
1603 if (emitted.insert(framework).second) {
1604 flags += fwSearchFlag;
1606 lg->ConvertToOutputFormat(framework, cmOutputConverter::SHELL);
1614 std::string cmLocalGenerator::GetFrameworkFlags(std::string const& l,
1615 std::string const& config,
1616 cmGeneratorTarget* target)
1618 return ::GetFrameworkFlags(l, config, target);
1621 void cmLocalGenerator::GetTargetDefines(cmGeneratorTarget const* target,
1622 std::string const& config,
1623 std::string const& lang,
1624 std::set<std::string>& defines) const
1626 std::set<BT<std::string>> tmp = this->GetTargetDefines(target, config, lang);
1627 for (BT<std::string> const& v : tmp) {
1628 defines.emplace(v.Value);
1632 std::set<BT<std::string>> cmLocalGenerator::GetTargetDefines(
1633 cmGeneratorTarget const* target, std::string const& config,
1634 std::string const& lang) const
1636 std::set<BT<std::string>> defines;
1638 // Add the export symbol definition for shared library objects.
1639 if (const std::string* exportMacro = target->GetExportMacro()) {
1640 this->AppendDefines(defines, *exportMacro);
1643 // Add preprocessor definitions for this target and configuration.
1644 std::vector<BT<std::string>> targetDefines =
1645 target->GetCompileDefinitions(config, lang);
1646 this->AppendDefines(defines, targetDefines);
1651 std::string cmLocalGenerator::GetTargetFortranFlags(
1652 cmGeneratorTarget const* /*unused*/, std::string const& /*unused*/)
1654 // Implemented by specific generators that override this.
1655 return std::string();
1659 * Output the linking rules on a command line. For executables,
1660 * targetLibrary should be a NULL pointer. For libraries, it should point
1661 * to the name of the library. This will not link a library against itself.
1663 void cmLocalGenerator::OutputLinkLibraries(
1664 cmComputeLinkInformation* pcli, cmLinkLineComputer* linkLineComputer,
1665 std::string& linkLibraries, std::string& frameworkPath,
1666 std::string& linkPath)
1668 std::vector<BT<std::string>> linkLibrariesList;
1669 std::vector<BT<std::string>> linkPathList;
1670 this->OutputLinkLibraries(pcli, linkLineComputer, linkLibrariesList,
1671 frameworkPath, linkPathList);
1672 pcli->AppendValues(linkLibraries, linkLibrariesList);
1673 pcli->AppendValues(linkPath, linkPathList);
1676 void cmLocalGenerator::OutputLinkLibraries(
1677 cmComputeLinkInformation* pcli, cmLinkLineComputer* linkLineComputer,
1678 std::vector<BT<std::string>>& linkLibraries, std::string& frameworkPath,
1679 std::vector<BT<std::string>>& linkPath)
1681 cmComputeLinkInformation& cli = *pcli;
1683 std::string linkLanguage = cli.GetLinkLanguage();
1685 std::string libPathFlag;
1686 if (const char* value = this->Makefile->GetDefinition(
1687 "CMAKE_" + cli.GetLinkLanguage() + "_LIBRARY_PATH_FLAG")) {
1688 libPathFlag = value;
1691 this->Makefile->GetRequiredDefinition("CMAKE_LIBRARY_PATH_FLAG");
1694 std::string libPathTerminator;
1695 if (const char* value = this->Makefile->GetDefinition(
1696 "CMAKE_" + cli.GetLinkLanguage() + "_LIBRARY_PATH_TERMINATOR")) {
1697 libPathTerminator = value;
1700 this->Makefile->GetRequiredDefinition("CMAKE_LIBRARY_PATH_TERMINATOR");
1703 // Add standard libraries for this language.
1704 std::string stdLibString = this->Makefile->GetSafeDefinition(
1705 cmStrCat("CMAKE_", cli.GetLinkLanguage(), "_STANDARD_LIBRARIES"));
1707 // Append the framework search path flags.
1708 std::string fwSearchFlag = this->Makefile->GetSafeDefinition(
1709 cmStrCat("CMAKE_", linkLanguage, "_FRAMEWORK_SEARCH_FLAG"));
1711 frameworkPath = linkLineComputer->ComputeFrameworkPath(cli, fwSearchFlag);
1712 linkLineComputer->ComputeLinkPath(cli, libPathFlag, libPathTerminator,
1714 linkLineComputer->ComputeLinkLibraries(cli, stdLibString, linkLibraries);
1717 std::string cmLocalGenerator::GetLinkLibsCMP0065(
1718 std::string const& linkLanguage, cmGeneratorTarget& tgt) const
1720 std::string linkFlags;
1722 // Flags to link an executable to shared libraries.
1723 if (tgt.GetType() == cmStateEnums::EXECUTABLE &&
1724 this->StateSnapshot.GetState()->GetGlobalPropertyAsBool(
1725 "TARGET_SUPPORTS_SHARED_LIBS")) {
1726 bool add_shlib_flags = false;
1727 switch (tgt.GetPolicyStatusCMP0065()) {
1728 case cmPolicies::WARN:
1729 if (!tgt.GetPropertyAsBool("ENABLE_EXPORTS") &&
1730 this->Makefile->PolicyOptionalWarningEnabled(
1731 "CMAKE_POLICY_WARNING_CMP0065")) {
1732 std::ostringstream w;
1733 /* clang-format off */
1734 w << cmPolicies::GetPolicyWarning(cmPolicies::CMP0065) << "\n"
1735 "For compatibility with older versions of CMake, "
1736 "additional flags may be added to export symbols on all "
1737 "executables regardless of their ENABLE_EXPORTS property.";
1738 /* clang-format on */
1739 this->IssueMessage(MessageType::AUTHOR_WARNING, w.str());
1742 case cmPolicies::OLD:
1743 // OLD behavior is to always add the flags, except on AIX where
1744 // we compute symbol exports if ENABLE_EXPORTS is on.
1746 !(tgt.Target->IsAIX() && tgt.GetPropertyAsBool("ENABLE_EXPORTS"));
1748 case cmPolicies::REQUIRED_IF_USED:
1749 case cmPolicies::REQUIRED_ALWAYS:
1751 MessageType::FATAL_ERROR,
1752 cmPolicies::GetRequiredPolicyError(cmPolicies::CMP0065));
1754 case cmPolicies::NEW:
1755 // NEW behavior is to only add the flags if ENABLE_EXPORTS is on,
1756 // except on AIX where we compute symbol exports.
1758 !tgt.Target->IsAIX() && tgt.GetPropertyAsBool("ENABLE_EXPORTS");
1762 if (add_shlib_flags) {
1763 linkFlags = this->Makefile->GetSafeDefinition(
1764 cmStrCat("CMAKE_SHARED_LIBRARY_LINK_", linkLanguage, "_FLAGS"));
1770 void cmLocalGenerator::AddArchitectureFlags(std::string& flags,
1771 cmGeneratorTarget const* target,
1772 const std::string& lang,
1773 const std::string& config)
1775 // Only add Apple specific flags on Apple platforms
1776 if (this->Makefile->IsOn("APPLE") && this->EmitUniversalBinaryFlags) {
1777 std::vector<std::string> archs;
1778 target->GetAppleArchs(config, archs);
1779 if (!archs.empty() && !lang.empty() &&
1780 (lang[0] == 'C' || lang[0] == 'F' || lang[0] == 'O')) {
1781 for (std::string const& arch : archs) {
1787 const char* sysroot = this->Makefile->GetDefinition("CMAKE_OSX_SYSROOT");
1788 if (sysroot && sysroot[0] == '/' && !sysroot[1]) {
1791 std::string sysrootFlagVar =
1792 std::string("CMAKE_") + lang + "_SYSROOT_FLAG";
1793 const char* sysrootFlag = this->Makefile->GetDefinition(sysrootFlagVar);
1794 if (sysrootFlag && *sysrootFlag) {
1795 std::vector<std::string> arch_sysroots;
1796 if (const char* arch_sysroots_str =
1797 this->Makefile->GetDefinition("CMAKE_APPLE_ARCH_SYSROOTS")) {
1798 cmExpandList(std::string(arch_sysroots_str), arch_sysroots, true);
1800 if (!arch_sysroots.empty()) {
1801 assert(arch_sysroots.size() == archs.size());
1802 for (size_t i = 0; i < archs.size(); ++i) {
1803 if (arch_sysroots[i].empty()) {
1806 flags += " -Xarch_" + archs[i] + " ";
1807 // Combine sysroot flag and path to work with -Xarch
1808 std::string arch_sysroot = sysrootFlag + arch_sysroots[i];
1809 flags += this->ConvertToOutputFormat(arch_sysroot, SHELL);
1811 } else if (sysroot && *sysroot) {
1813 flags += sysrootFlag;
1815 flags += this->ConvertToOutputFormat(sysroot, SHELL);
1819 const char* deploymentTarget =
1820 this->Makefile->GetDefinition("CMAKE_OSX_DEPLOYMENT_TARGET");
1821 std::string deploymentTargetFlagVar =
1822 std::string("CMAKE_") + lang + "_OSX_DEPLOYMENT_TARGET_FLAG";
1823 const char* deploymentTargetFlag =
1824 this->Makefile->GetDefinition(deploymentTargetFlagVar);
1825 if (deploymentTargetFlag && *deploymentTargetFlag && deploymentTarget &&
1826 *deploymentTarget) {
1828 flags += deploymentTargetFlag;
1829 flags += deploymentTarget;
1834 void cmLocalGenerator::AddLanguageFlags(std::string& flags,
1835 cmGeneratorTarget const* target,
1836 const std::string& lang,
1837 const std::string& config)
1839 // Add language-specific flags.
1840 this->AddConfigVariableFlags(flags, cmStrCat("CMAKE_", lang, "_FLAGS"),
1843 if (lang == "Swift") {
1844 if (const char* v = target->GetProperty("Swift_LANGUAGE_VERSION")) {
1845 if (cmSystemTools::VersionCompare(
1846 cmSystemTools::OP_GREATER_EQUAL,
1847 this->Makefile->GetDefinition("CMAKE_Swift_COMPILER_VERSION"),
1849 this->AppendFlags(flags, "-swift-version " + std::string(v));
1854 // Add MSVC runtime library flags. This is activated by the presence
1855 // of a default selection whether or not it is overridden by a property.
1856 const char* msvcRuntimeLibraryDefault =
1857 this->Makefile->GetDefinition("CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT");
1858 if (msvcRuntimeLibraryDefault && *msvcRuntimeLibraryDefault) {
1859 const char* msvcRuntimeLibraryValue =
1860 target->GetProperty("MSVC_RUNTIME_LIBRARY");
1861 if (!msvcRuntimeLibraryValue) {
1862 msvcRuntimeLibraryValue = msvcRuntimeLibraryDefault;
1864 std::string const msvcRuntimeLibrary = cmGeneratorExpression::Evaluate(
1865 msvcRuntimeLibraryValue, this, config, target);
1866 if (!msvcRuntimeLibrary.empty()) {
1867 if (const char* msvcRuntimeLibraryOptions =
1868 this->Makefile->GetDefinition(
1869 "CMAKE_" + lang + "_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_" +
1870 msvcRuntimeLibrary)) {
1871 this->AppendCompileOptions(flags, msvcRuntimeLibraryOptions);
1872 } else if ((this->Makefile->GetSafeDefinition(
1873 "CMAKE_" + lang + "_COMPILER_ID") == "MSVC" ||
1874 this->Makefile->GetSafeDefinition(
1875 "CMAKE_" + lang + "_SIMULATE_ID") == "MSVC") &&
1876 !cmSystemTools::GetErrorOccuredFlag()) {
1877 // The compiler uses the MSVC ABI so it needs a known runtime library.
1878 this->IssueMessage(MessageType::FATAL_ERROR,
1879 "MSVC_RUNTIME_LIBRARY value '" +
1880 msvcRuntimeLibrary + "' not known for this " +
1881 lang + " compiler.");
1887 void cmLocalGenerator::AddLanguageFlagsForLinking(
1888 std::string& flags, cmGeneratorTarget const* target, const std::string& lang,
1889 const std::string& config)
1891 if (this->Makefile->IsOn("CMAKE_" + lang +
1892 "_LINK_WITH_STANDARD_COMPILE_OPTION")) {
1893 // This toolchain requires use of the language standard flag
1894 // when linking in order to use the matching standard library.
1895 // FIXME: If CMake gains an abstraction for standard library
1896 // selection, this will have to be reconciled with it.
1897 this->AddCompilerRequirementFlag(flags, target, lang);
1900 this->AddLanguageFlags(flags, target, lang, config);
1902 if (target->IsIPOEnabled(lang, config)) {
1903 this->AppendFeatureOptions(flags, lang, "IPO");
1907 cmGeneratorTarget* cmLocalGenerator::FindGeneratorTargetToUse(
1908 const std::string& name) const
1910 auto imported = this->ImportedGeneratorTargets.find(name);
1911 if (imported != this->ImportedGeneratorTargets.end()) {
1912 return imported->second;
1915 if (cmGeneratorTarget* t = this->FindLocalNonAliasGeneratorTarget(name)) {
1919 return this->GetGlobalGenerator()->FindGeneratorTarget(name);
1922 bool cmLocalGenerator::GetRealDependency(const std::string& inName,
1923 const std::string& config,
1926 // Older CMake code may specify the dependency using the target
1927 // output file rather than the target name. Such code would have
1928 // been written before there was support for target properties that
1929 // modify the name so stripping down to just the file name should
1930 // produce the target name in this case.
1931 std::string name = cmSystemTools::GetFilenameName(inName);
1933 // If the input name is the empty string, there is no real
1934 // dependency. Short-circuit the other checks:
1939 if (cmSystemTools::GetFilenameLastExtension(name) == ".exe") {
1940 name = cmSystemTools::GetFilenameWithoutLastExtension(name);
1943 // Look for a CMake target with the given name.
1944 if (cmGeneratorTarget* target = this->FindGeneratorTargetToUse(name)) {
1945 // make sure it is not just a coincidence that the target name
1946 // found is part of the inName
1947 if (cmSystemTools::FileIsFullPath(inName)) {
1948 std::string tLocation;
1949 if (target->GetType() >= cmStateEnums::EXECUTABLE &&
1950 target->GetType() <= cmStateEnums::MODULE_LIBRARY) {
1951 tLocation = target->GetLocation(config);
1952 tLocation = cmSystemTools::GetFilenamePath(tLocation);
1953 tLocation = cmSystemTools::CollapseFullPath(tLocation);
1955 std::string depLocation =
1956 cmSystemTools::GetFilenamePath(std::string(inName));
1957 depLocation = cmSystemTools::CollapseFullPath(depLocation);
1958 if (depLocation != tLocation) {
1959 // it is a full path to a depend that has the same name
1960 // as a target but is in a different location so do not use
1961 // the target as the depend
1966 switch (target->GetType()) {
1967 case cmStateEnums::EXECUTABLE:
1968 case cmStateEnums::STATIC_LIBRARY:
1969 case cmStateEnums::SHARED_LIBRARY:
1970 case cmStateEnums::MODULE_LIBRARY:
1971 case cmStateEnums::UNKNOWN_LIBRARY:
1972 dep = target->GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact,
1975 case cmStateEnums::OBJECT_LIBRARY:
1976 // An object library has no single file on which to depend.
1977 // This was listed to get the target-level dependency.
1979 case cmStateEnums::INTERFACE_LIBRARY:
1980 // An interface library has no file on which to depend.
1981 // This was listed to get the target-level dependency.
1983 case cmStateEnums::UTILITY:
1984 case cmStateEnums::GLOBAL_TARGET:
1985 // A utility target has no file on which to depend. This was listed
1986 // only to get the target-level dependency.
1991 // The name was not that of a CMake target. It must name a file.
1992 if (cmSystemTools::FileIsFullPath(inName)) {
1993 // This is a full path. Return it as given.
1998 // Check for a source file in this directory that matches the
2000 if (cmSourceFile* sf = this->Makefile->GetSource(inName)) {
2001 dep = sf->ResolveFullPath();
2005 // Treat the name as relative to the source directory in which it
2007 dep = cmStrCat(this->GetCurrentSourceDirectory(), '/', inName);
2009 // If the in-source path does not exist, assume it instead lives in the
2010 // binary directory.
2011 if (!cmSystemTools::FileExists(dep)) {
2012 dep = cmStrCat(this->GetCurrentBinaryDirectory(), '/', inName);
2015 dep = cmSystemTools::CollapseFullPath(dep, this->GetBinaryDirectory());
2020 void cmLocalGenerator::AddSharedFlags(std::string& flags,
2021 const std::string& lang, bool shared)
2023 std::string flagsVar;
2025 // Add flags for dealing with shared libraries for this language.
2027 this->AppendFlags(flags,
2028 this->Makefile->GetSafeDefinition(
2029 cmStrCat("CMAKE_SHARED_LIBRARY_", lang, "_FLAGS")));
2033 void cmLocalGenerator::AddCompilerRequirementFlag(
2034 std::string& flags, cmGeneratorTarget const* target, const std::string& lang)
2039 const char* defaultStd =
2040 this->Makefile->GetDefinition("CMAKE_" + lang + "_STANDARD_DEFAULT");
2041 if (!defaultStd || !*defaultStd) {
2042 // This compiler has no notion of language standard levels.
2045 std::string extProp = lang + "_EXTENSIONS";
2047 if (const char* extPropValue = target->GetProperty(extProp)) {
2048 if (cmIsOff(extPropValue)) {
2052 std::string stdProp = lang + "_STANDARD";
2053 const char* standardProp = target->GetProperty(stdProp);
2054 if (!standardProp) {
2056 // No language standard is specified and extensions are not disabled.
2057 // Check if this compiler needs a flag to enable extensions.
2058 std::string const option_flag =
2059 "CMAKE_" + lang + "_EXTENSION_COMPILE_OPTION";
2060 if (const char* opt =
2061 target->Target->GetMakefile()->GetDefinition(option_flag)) {
2062 std::vector<std::string> optVec = cmExpandedList(opt);
2063 for (std::string const& i : optVec) {
2064 this->AppendFlagEscape(flags, i);
2071 std::string const type = ext ? "EXTENSION" : "STANDARD";
2073 if (target->GetPropertyAsBool(lang + "_STANDARD_REQUIRED")) {
2074 std::string option_flag =
2075 "CMAKE_" + lang + standardProp + "_" + type + "_COMPILE_OPTION";
2078 target->Target->GetMakefile()->GetDefinition(option_flag);
2080 std::ostringstream e;
2081 e << "Target \"" << target->GetName()
2082 << "\" requires the language "
2084 << lang << standardProp << "\" "
2085 << (ext ? "(with compiler extensions)" : "")
2087 "does not know the compile flags to use to enable it.";
2088 this->IssueMessage(MessageType::FATAL_ERROR, e.str());
2090 std::vector<std::string> optVec = cmExpandedList(opt);
2091 for (std::string const& i : optVec) {
2092 this->AppendFlagEscape(flags, i);
2098 static std::map<std::string, std::vector<std::string>> langStdMap;
2099 if (langStdMap.empty()) {
2100 // Maintain sorted order, most recent first.
2101 langStdMap["CXX"].emplace_back("20");
2102 langStdMap["CXX"].emplace_back("17");
2103 langStdMap["CXX"].emplace_back("14");
2104 langStdMap["CXX"].emplace_back("11");
2105 langStdMap["CXX"].emplace_back("98");
2107 langStdMap["OBJCXX"].emplace_back("20");
2108 langStdMap["OBJCXX"].emplace_back("17");
2109 langStdMap["OBJCXX"].emplace_back("14");
2110 langStdMap["OBJCXX"].emplace_back("11");
2111 langStdMap["OBJCXX"].emplace_back("98");
2113 langStdMap["C"].emplace_back("11");
2114 langStdMap["C"].emplace_back("99");
2115 langStdMap["C"].emplace_back("90");
2117 langStdMap["OBJC"].emplace_back("11");
2118 langStdMap["OBJC"].emplace_back("99");
2119 langStdMap["OBJC"].emplace_back("90");
2121 langStdMap["CUDA"].emplace_back("20");
2122 langStdMap["CUDA"].emplace_back("17");
2123 langStdMap["CUDA"].emplace_back("14");
2124 langStdMap["CUDA"].emplace_back("11");
2125 langStdMap["CUDA"].emplace_back("03");
2128 std::string standard(standardProp);
2129 if (lang == "CUDA" && standard == "98") {
2132 std::vector<std::string>& stds = langStdMap[lang];
2134 auto stdIt = std::find(stds.begin(), stds.end(), standard);
2135 if (stdIt == stds.end()) {
2138 lang + "_STANDARD is set to invalid value '" + standard + "'";
2139 this->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
2140 MessageType::FATAL_ERROR, e, target->GetBacktrace());
2144 auto defaultStdIt = std::find(stds.begin(), stds.end(), defaultStd);
2145 if (defaultStdIt == stds.end()) {
2146 std::string e = "CMAKE_" + lang +
2147 "_STANDARD_DEFAULT is set to invalid value '" + std::string(defaultStd) +
2149 this->IssueMessage(MessageType::INTERNAL_ERROR, e);
2153 // If the standard requested is older than the compiler's default
2154 // then we need to use a flag to change it. The comparison is
2155 // greater-or-equal because the standards are stored in backward
2156 // chronological order.
2157 if (stdIt >= defaultStdIt) {
2158 std::string option_flag =
2159 "CMAKE_" + lang + *stdIt + "_" + type + "_COMPILE_OPTION";
2161 std::string const& opt =
2162 target->Target->GetMakefile()->GetRequiredDefinition(option_flag);
2163 std::vector<std::string> optVec = cmExpandedList(opt);
2164 for (std::string const& i : optVec) {
2165 this->AppendFlagEscape(flags, i);
2170 // The standard requested is at least as new as the compiler's default,
2171 // and the standard request is not required. Decay to the newest standard
2172 // for which a flag is defined.
2173 for (; stdIt < defaultStdIt; ++stdIt) {
2174 std::string option_flag =
2175 cmStrCat("CMAKE_", lang, *stdIt, "_", type, "_COMPILE_OPTION");
2177 if (const char* opt =
2178 target->Target->GetMakefile()->GetDefinition(option_flag)) {
2179 std::vector<std::string> optVec = cmExpandedList(opt);
2180 for (std::string const& i : optVec) {
2181 this->AppendFlagEscape(flags, i);
2188 static void AddVisibilityCompileOption(std::string& flags,
2189 cmGeneratorTarget const* target,
2190 cmLocalGenerator* lg,
2191 const std::string& lang,
2192 std::string* warnCMP0063)
2194 std::string compileOption = "CMAKE_" + lang + "_COMPILE_OPTIONS_VISIBILITY";
2195 const char* opt = lg->GetMakefile()->GetDefinition(compileOption);
2199 std::string flagDefine = lang + "_VISIBILITY_PRESET";
2201 const char* prop = target->GetProperty(flagDefine);
2206 *warnCMP0063 += " " + flagDefine + "\n";
2209 if (strcmp(prop, "hidden") != 0 && strcmp(prop, "default") != 0 &&
2210 strcmp(prop, "protected") != 0 && strcmp(prop, "internal") != 0) {
2211 std::ostringstream e;
2212 e << "Target " << target->GetName() << " uses unsupported value \"" << prop
2213 << "\" for " << flagDefine << "."
2214 << " The supported values are: default, hidden, protected, and "
2216 cmSystemTools::Error(e.str());
2219 std::string option = std::string(opt) + prop;
2220 lg->AppendFlags(flags, option);
2223 static void AddInlineVisibilityCompileOption(std::string& flags,
2224 cmGeneratorTarget const* target,
2225 cmLocalGenerator* lg,
2226 std::string* warnCMP0063,
2227 const std::string& lang)
2229 std::string compileOption =
2230 cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN");
2231 const char* opt = lg->GetMakefile()->GetDefinition(compileOption);
2236 bool prop = target->GetPropertyAsBool("VISIBILITY_INLINES_HIDDEN");
2241 *warnCMP0063 += " VISIBILITY_INLINES_HIDDEN\n";
2244 lg->AppendFlags(flags, opt);
2247 void cmLocalGenerator::AddVisibilityPresetFlags(
2248 std::string& flags, cmGeneratorTarget const* target, const std::string& lang)
2254 std::string warnCMP0063;
2255 std::string* pWarnCMP0063 = nullptr;
2256 if (target->GetType() != cmStateEnums::SHARED_LIBRARY &&
2257 target->GetType() != cmStateEnums::MODULE_LIBRARY &&
2258 !target->IsExecutableWithExports()) {
2259 switch (target->GetPolicyStatusCMP0063()) {
2260 case cmPolicies::OLD:
2262 case cmPolicies::WARN:
2263 pWarnCMP0063 = &warnCMP0063;
2270 AddVisibilityCompileOption(flags, target, this, lang, pWarnCMP0063);
2272 if (lang == "CXX" || lang == "OBJCXX") {
2273 AddInlineVisibilityCompileOption(flags, target, this, pWarnCMP0063, lang);
2276 if (!warnCMP0063.empty() && this->WarnCMP0063.insert(target).second) {
2277 std::ostringstream w;
2278 /* clang-format off */
2280 cmPolicies::GetPolicyWarning(cmPolicies::CMP0063) << "\n"
2281 "Target \"" << target->GetName() << "\" of "
2282 "type \"" << cmState::GetTargetTypeName(target->GetType()) << "\" "
2283 "has the following visibility properties set for " << lang << ":\n" <<
2285 "For compatibility CMake is not honoring them for this target.";
2286 /* clang-format on */
2287 target->GetLocalGenerator()->GetCMakeInstance()->IssueMessage(
2288 MessageType::AUTHOR_WARNING, w.str(), target->GetBacktrace());
2292 void cmLocalGenerator::AddCMP0018Flags(std::string& flags,
2293 cmGeneratorTarget const* target,
2294 std::string const& lang,
2295 const std::string& config)
2297 int targetType = target->GetType();
2299 bool shared = ((targetType == cmStateEnums::SHARED_LIBRARY) ||
2300 (targetType == cmStateEnums::MODULE_LIBRARY));
2302 if (this->GetShouldUseOldFlags(shared, lang)) {
2303 this->AddSharedFlags(flags, lang, shared);
2305 if (target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
2306 if (target->GetPropertyAsBool("POSITION_INDEPENDENT_CODE")) {
2307 this->AddPositionIndependentFlags(flags, lang, targetType);
2312 if (target->GetLinkInterfaceDependentBoolProperty(
2313 "POSITION_INDEPENDENT_CODE", config)) {
2314 this->AddPositionIndependentFlags(flags, lang, targetType);
2317 this->AppendFeatureOptions(flags, lang, "DLL");
2322 bool cmLocalGenerator::GetShouldUseOldFlags(bool shared,
2323 const std::string& lang) const
2325 std::string originalFlags =
2326 this->GlobalGenerator->GetSharedLibFlagsForLanguage(lang);
2328 std::string flagsVar = cmStrCat("CMAKE_SHARED_LIBRARY_", lang, "_FLAGS");
2329 std::string const& flags = this->Makefile->GetSafeDefinition(flagsVar);
2331 if (flags != originalFlags) {
2332 switch (this->GetPolicyStatus(cmPolicies::CMP0018)) {
2333 case cmPolicies::WARN: {
2334 std::ostringstream e;
2335 e << "Variable " << flagsVar
2336 << " has been modified. CMake "
2337 "will ignore the POSITION_INDEPENDENT_CODE target property for "
2338 "shared libraries and will use the "
2341 "instead. This may cause errors if the original content of "
2342 << flagsVar << " was removed.\n"
2343 << cmPolicies::GetPolicyWarning(cmPolicies::CMP0018);
2345 this->IssueMessage(MessageType::AUTHOR_WARNING, e.str());
2348 case cmPolicies::OLD:
2350 case cmPolicies::REQUIRED_IF_USED:
2351 case cmPolicies::REQUIRED_ALWAYS:
2352 case cmPolicies::NEW:
2360 void cmLocalGenerator::AddPositionIndependentFlags(std::string& flags,
2361 std::string const& lang,
2364 std::string picFlags;
2366 if (targetType == cmStateEnums::EXECUTABLE) {
2367 picFlags = this->Makefile->GetSafeDefinition(
2368 cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_PIE"));
2370 if (picFlags.empty()) {
2371 picFlags = this->Makefile->GetSafeDefinition(
2372 cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_PIC"));
2374 if (!picFlags.empty()) {
2375 std::vector<std::string> options = cmExpandedList(picFlags);
2376 for (std::string const& o : options) {
2377 this->AppendFlagEscape(flags, o);
2382 void cmLocalGenerator::AddConfigVariableFlags(std::string& flags,
2383 const std::string& var,
2384 const std::string& config)
2386 // Add the flags from the variable itself.
2387 this->AppendFlags(flags, this->Makefile->GetSafeDefinition(var));
2388 // Add the flags from the build-type specific variable.
2389 if (!config.empty()) {
2390 const std::string flagsVar =
2391 cmStrCat(var, '_', cmSystemTools::UpperCase(config));
2392 this->AppendFlags(flags, this->Makefile->GetSafeDefinition(flagsVar));
2396 void cmLocalGenerator::AppendFlags(std::string& flags,
2397 const std::string& newFlags) const
2399 if (!newFlags.empty()) {
2400 if (!flags.empty()) {
2407 void cmLocalGenerator::AppendFlags(
2408 std::string& flags, const std::vector<BT<std::string>>& newFlags) const
2410 for (BT<std::string> const& flag : newFlags) {
2411 this->AppendFlags(flags, flag.Value);
2415 void cmLocalGenerator::AppendFlagEscape(std::string& flags,
2416 const std::string& rawFlag) const
2420 this->EscapeForShell(rawFlag, false, false, false, this->IsNinjaMulti()));
2423 void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target)
2425 std::vector<std::string> configsList;
2426 std::string configDefault = this->Makefile->GetConfigurations(configsList);
2427 if (configsList.empty()) {
2428 configsList.push_back(configDefault);
2431 for (std::string const& config : configsList) {
2432 const std::string buildType = cmSystemTools::UpperCase(config);
2434 // FIXME: Refactor collection of sources to not evaluate object libraries.
2435 std::vector<cmSourceFile*> sources;
2436 target->GetSourceFiles(sources, buildType);
2438 for (const std::string& lang : { "C", "CXX", "OBJC", "OBJCXX" }) {
2439 auto langSources = std::count_if(
2440 sources.begin(), sources.end(), [lang](cmSourceFile* sf) {
2441 return lang == sf->GetLanguage() &&
2442 !sf->GetProperty("SKIP_PRECOMPILE_HEADERS");
2444 if (langSources == 0) {
2448 const std::string pchSource = target->GetPchSource(config, lang);
2449 const std::string pchHeader = target->GetPchHeader(config, lang);
2451 if (pchSource.empty() || pchHeader.empty()) {
2455 const std::string pchExtension =
2456 this->Makefile->GetSafeDefinition("CMAKE_PCH_EXTENSION");
2458 if (pchExtension.empty()) {
2462 const char* pchReuseFrom =
2463 target->GetProperty("PRECOMPILE_HEADERS_REUSE_FROM");
2465 auto pch_sf = this->Makefile->GetOrCreateSource(
2466 pchSource, false, cmSourceFileLocationKind::Known);
2468 if (!this->GetGlobalGenerator()->IsXcode()) {
2469 if (!pchReuseFrom) {
2470 target->AddSource(pchSource, true);
2473 const std::string pchFile = target->GetPchFile(config, lang);
2475 // Exclude the pch files from linking
2476 if (this->Makefile->IsOn("CMAKE_LINK_PCH")) {
2477 if (!pchReuseFrom) {
2478 pch_sf->SetProperty("OBJECT_OUTPUTS", pchFile.c_str());
2481 this->GlobalGenerator->FindGeneratorTarget(pchReuseFrom);
2483 if (this->Makefile->IsOn("CMAKE_PCH_COPY_COMPILE_PDB")) {
2485 const std::string pdb_prefix =
2486 this->GetGlobalGenerator()->IsMultiConfig()
2487 ? cmStrCat(this->GlobalGenerator->GetCMakeCFGIntDir(), "/")
2490 const std::string target_compile_pdb_dir = cmStrCat(
2491 target->GetLocalGenerator()->GetCurrentBinaryDirectory(), "/",
2492 target->GetName(), ".dir/");
2494 const std::string copy_script =
2495 cmStrCat(target_compile_pdb_dir, "copy_idb_pdb.cmake");
2496 cmGeneratedFileStream file(copy_script);
2498 file << "# CMake generated file\n";
2499 for (auto extension : { ".pdb", ".idb" }) {
2500 const std::string from_file =
2501 cmStrCat(reuseTarget->GetLocalGenerator()
2502 ->GetCurrentBinaryDirectory(),
2503 "/", pchReuseFrom, ".dir/${PDB_PREFIX}",
2504 pchReuseFrom, extension);
2506 const std::string to_dir = cmStrCat(
2507 target->GetLocalGenerator()->GetCurrentBinaryDirectory(),
2508 "/", target->GetName(), ".dir/${PDB_PREFIX}");
2510 const std::string to_file =
2511 cmStrCat(to_dir, pchReuseFrom, extension);
2513 std::string dest_file = to_file;
2515 const std::string prefix = target->GetSafeProperty("PREFIX");
2516 if (!prefix.empty()) {
2518 cmStrCat(to_dir, prefix, pchReuseFrom, extension);
2521 file << "if (EXISTS \"" << from_file << "\" AND \""
2522 << from_file << "\" IS_NEWER_THAN \"" << dest_file
2524 file << " file(COPY \"" << from_file << "\""
2525 << " DESTINATION \"" << to_dir << "\")\n";
2526 if (!prefix.empty()) {
2527 file << " file(REMOVE \"" << dest_file << "\")\n";
2528 file << " file(RENAME \"" << to_file << "\" \"" << dest_file
2531 file << "endif()\n";
2534 cmCustomCommandLines commandLines = cmMakeSingleCommandLine(
2535 { cmSystemTools::GetCMakeCommand(),
2536 cmStrCat("-DPDB_PREFIX=", pdb_prefix), "-P", copy_script });
2538 const std::string no_main_dependency;
2539 const std::vector<std::string> no_deps;
2540 const char* no_message = "";
2541 const char* no_current_dir = nullptr;
2542 std::vector<std::string> no_byproducts;
2544 std::vector<std::string> outputs;
2545 outputs.push_back(cmStrCat(target_compile_pdb_dir, pdb_prefix,
2546 pchReuseFrom, ".pdb"));
2548 if (this->GetGlobalGenerator()->IsVisualStudio()) {
2549 this->AddCustomCommandToTarget(
2550 target->GetName(), outputs, no_deps, commandLines,
2551 cmCustomCommandType::PRE_BUILD, no_message, no_current_dir);
2553 cmImplicitDependsList no_implicit_depends;
2554 cmSourceFile* copy_rule = this->AddCustomCommandToOutput(
2555 outputs, no_byproducts, no_deps, no_main_dependency,
2556 no_implicit_depends, commandLines, no_message,
2560 target->AddSource(copy_rule->ResolveFullPath());
2564 target->Target->SetProperty("COMPILE_PDB_OUTPUT_DIRECTORY",
2565 target_compile_pdb_dir);
2568 std::string pchSourceObj =
2569 reuseTarget->GetPchFileObject(config, lang);
2571 // Link to the pch object file
2572 target->Target->AppendProperty(
2574 cmStrCat(" ", this->ConvertToOutputFormat(pchSourceObj, SHELL)),
2578 pch_sf->SetProperty("PCH_EXTENSION", pchExtension.c_str());
2581 // Add pchHeader to source files, which will
2582 // be grouped as "Precompile Header File"
2583 auto pchHeader_sf = this->Makefile->GetOrCreateSource(
2584 pchHeader, false, cmSourceFileLocationKind::Known);
2586 pchHeader_sf->ResolveFullPath(&err);
2587 target->AddSource(pchHeader);
2593 void cmLocalGenerator::AddUnityBuild(cmGeneratorTarget* target)
2595 if (!target->GetPropertyAsBool("UNITY_BUILD")) {
2599 // FIXME: Handle all configurations in multi-config generators.
2601 if (!this->GetGlobalGenerator()->IsMultiConfig()) {
2602 config = this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE");
2605 const std::string buildType = cmSystemTools::UpperCase(config);
2607 std::string filename_base =
2608 cmStrCat(this->GetCurrentBinaryDirectory(), "/CMakeFiles/",
2609 target->GetName(), ".dir/Unity/");
2611 // FIXME: Refactor collection of sources to not evaluate object libraries.
2612 std::vector<cmSourceFile*> sources;
2613 target->GetSourceFiles(sources, buildType);
2615 auto batchSizeString = target->GetProperty("UNITY_BUILD_BATCH_SIZE");
2616 const size_t unityBatchSize =
2617 static_cast<size_t>(std::atoi(batchSizeString));
2619 auto beforeInclude = target->GetProperty("UNITY_BUILD_CODE_BEFORE_INCLUDE");
2620 auto afterInclude = target->GetProperty("UNITY_BUILD_CODE_AFTER_INCLUDE");
2622 for (std::string lang : { "C", "CXX" }) {
2623 std::vector<cmSourceFile*> filtered_sources;
2624 std::copy_if(sources.begin(), sources.end(),
2625 std::back_inserter(filtered_sources), [&](cmSourceFile* sf) {
2626 return sf->GetLanguage() == lang &&
2627 !sf->GetPropertyAsBool("SKIP_UNITY_BUILD_INCLUSION") &&
2628 !sf->GetPropertyAsBool("HEADER_FILE_ONLY") &&
2629 !sf->GetProperty("COMPILE_OPTIONS") &&
2630 !sf->GetProperty("COMPILE_DEFINITIONS") &&
2631 !sf->GetProperty("COMPILE_FLAGS") &&
2632 !sf->GetProperty("INCLUDE_DIRECTORIES");
2635 size_t batchSize = unityBatchSize;
2636 if (unityBatchSize == 0) {
2637 batchSize = filtered_sources.size();
2640 for (size_t itemsLeft = filtered_sources.size(), chunk, batch = 0;
2641 itemsLeft > 0; itemsLeft -= chunk, ++batch) {
2643 chunk = std::min(itemsLeft, batchSize);
2645 std::string filename = cmStrCat(filename_base, "unity_", batch,
2646 (lang == "C") ? "_c.c" : "_cxx.cxx");
2648 const std::string filename_tmp = cmStrCat(filename, ".tmp");
2650 size_t begin = batch * batchSize;
2651 size_t end = begin + chunk;
2653 cmGeneratedFileStream file(
2654 filename_tmp, false,
2655 this->GetGlobalGenerator()->GetMakefileEncoding());
2656 file << "/* generated by CMake */\n\n";
2658 for (; begin != end; ++begin) {
2659 cmSourceFile* sf = filtered_sources[begin];
2661 target->AddSourceFileToUnityBatch(sf->ResolveFullPath());
2662 sf->SetProperty("UNITY_SOURCE_FILE", filename.c_str());
2664 if (beforeInclude) {
2665 file << beforeInclude << "\n";
2668 file << "#include \"" << sf->ResolveFullPath() << "\"\n";
2671 file << afterInclude << "\n";
2675 cmSystemTools::MoveFileIfDifferent(filename_tmp, filename);
2677 target->AddSource(filename, true);
2679 auto unity = this->Makefile->GetOrCreateSource(filename);
2680 unity->SetProperty("SKIP_UNITY_BUILD_INCLUSION", "ON");
2681 unity->SetProperty("UNITY_SOURCE_FILE", filename.c_str());
2686 void cmLocalGenerator::AppendIPOLinkerFlags(std::string& flags,
2687 cmGeneratorTarget* target,
2688 const std::string& config,
2689 const std::string& lang)
2691 if (!target->IsIPOEnabled(lang, config)) {
2695 switch (target->GetType()) {
2696 case cmStateEnums::EXECUTABLE:
2697 case cmStateEnums::SHARED_LIBRARY:
2698 case cmStateEnums::MODULE_LIBRARY:
2704 const std::string name = "CMAKE_" + lang + "_LINK_OPTIONS_IPO";
2705 const char* rawFlagsList = this->Makefile->GetDefinition(name);
2706 if (rawFlagsList == nullptr) {
2710 std::vector<std::string> flagsList = cmExpandedList(rawFlagsList);
2711 for (std::string const& o : flagsList) {
2712 this->AppendFlagEscape(flags, o);
2716 void cmLocalGenerator::AppendPositionIndependentLinkerFlags(
2717 std::string& flags, cmGeneratorTarget* target, const std::string& config,
2718 const std::string& lang)
2720 // For now, only EXECUTABLE is concerned
2721 if (target->GetType() != cmStateEnums::EXECUTABLE) {
2725 const char* PICValue = target->GetLinkPIEProperty(config);
2726 if (PICValue == nullptr) {
2727 // POSITION_INDEPENDENT_CODE is not set
2731 const std::string mode = cmIsOn(PICValue) ? "PIE" : "NO_PIE";
2733 std::string supported = "CMAKE_" + lang + "_LINK_" + mode + "_SUPPORTED";
2734 if (cmIsOff(this->Makefile->GetDefinition(supported))) {
2738 std::string name = "CMAKE_" + lang + "_LINK_OPTIONS_" + mode;
2740 auto pieFlags = this->Makefile->GetSafeDefinition(name);
2741 if (pieFlags.empty()) {
2745 std::vector<std::string> flagsList = cmExpandedList(pieFlags);
2746 for (const auto& flag : flagsList) {
2747 this->AppendFlagEscape(flags, flag);
2751 void cmLocalGenerator::AppendCompileOptions(std::string& options,
2752 std::string const& options_list,
2753 const char* regex) const
2755 // Short-circuit if there are no options.
2756 if (options_list.empty()) {
2760 // Expand the list of options.
2761 std::vector<std::string> options_vec = cmExpandedList(options_list);
2762 this->AppendCompileOptions(options, options_vec, regex);
2765 void cmLocalGenerator::AppendCompileOptions(
2766 std::string& options, const std::vector<std::string>& options_vec,
2767 const char* regex) const
2769 if (regex != nullptr) {
2770 // Filter flags upon specified reges.
2771 cmsys::RegularExpression r(regex);
2773 for (std::string const& opt : options_vec) {
2775 this->AppendFlagEscape(options, opt);
2779 for (std::string const& opt : options_vec) {
2780 this->AppendFlagEscape(options, opt);
2785 void cmLocalGenerator::AppendCompileOptions(
2786 std::vector<BT<std::string>>& options,
2787 const std::vector<BT<std::string>>& options_vec, const char* regex) const
2789 if (regex != nullptr) {
2790 // Filter flags upon specified regular expressions.
2791 cmsys::RegularExpression r(regex);
2793 for (BT<std::string> const& opt : options_vec) {
2794 if (r.find(opt.Value)) {
2796 this->AppendFlagEscape(flag, opt.Value);
2797 options.emplace_back(std::move(flag), opt.Backtrace);
2801 for (BT<std::string> const& opt : options_vec) {
2803 this->AppendFlagEscape(flag, opt.Value);
2804 options.emplace_back(std::move(flag), opt.Backtrace);
2809 void cmLocalGenerator::AppendIncludeDirectories(
2810 std::vector<std::string>& includes, const std::string& includes_list,
2811 const cmSourceFile& sourceFile) const
2813 // Short-circuit if there are no includes.
2814 if (includes_list.empty()) {
2818 // Expand the list of includes.
2819 std::vector<std::string> includes_vec = cmExpandedList(includes_list);
2820 this->AppendIncludeDirectories(includes, includes_vec, sourceFile);
2823 void cmLocalGenerator::AppendIncludeDirectories(
2824 std::vector<std::string>& includes,
2825 const std::vector<std::string>& includes_vec,
2826 const cmSourceFile& sourceFile) const
2828 std::unordered_set<std::string> uniqueIncludes;
2830 for (const std::string& include : includes_vec) {
2831 if (!cmSystemTools::FileIsFullPath(include)) {
2832 std::ostringstream e;
2833 e << "Found relative path while evaluating include directories of "
2835 << sourceFile.GetLocation().GetName() << "\":\n \"" << include
2838 this->IssueMessage(MessageType::FATAL_ERROR, e.str());
2842 std::string inc = include;
2844 if (!cmIsOff(inc)) {
2845 cmSystemTools::ConvertToUnixSlashes(inc);
2848 if (uniqueIncludes.insert(inc).second) {
2849 includes.push_back(std::move(inc));
2854 void cmLocalGenerator::AppendDefines(std::set<std::string>& defines,
2855 std::string const& defines_list) const
2857 std::set<BT<std::string>> tmp;
2858 this->AppendDefines(tmp, ExpandListWithBacktrace(defines_list));
2859 for (BT<std::string> const& i : tmp) {
2860 defines.emplace(i.Value);
2864 void cmLocalGenerator::AppendDefines(std::set<BT<std::string>>& defines,
2865 std::string const& defines_list) const
2867 // Short-circuit if there are no definitions.
2868 if (defines_list.empty()) {
2872 // Expand the list of definitions.
2873 this->AppendDefines(defines, ExpandListWithBacktrace(defines_list));
2876 void cmLocalGenerator::AppendDefines(
2877 std::set<BT<std::string>>& defines,
2878 const std::vector<BT<std::string>>& defines_vec) const
2880 for (BT<std::string> const& d : defines_vec) {
2881 // Skip unsupported definitions.
2882 if (!this->CheckDefinition(d.Value)) {
2889 void cmLocalGenerator::JoinDefines(const std::set<std::string>& defines,
2890 std::string& definesString,
2891 const std::string& lang)
2893 // Lookup the define flag for the current language.
2894 std::string dflag = "-D";
2895 if (!lang.empty()) {
2897 this->Makefile->GetDefinition(cmStrCat("CMAKE_", lang, "_DEFINE_FLAG"));
2902 const char* itemSeparator = definesString.empty() ? "" : " ";
2903 for (std::string const& define : defines) {
2904 // Append the definition with proper escaping.
2905 std::string def = dflag;
2906 if (this->GetState()->UseWatcomWMake()) {
2907 // The Watcom compiler does its own command line parsing instead
2908 // of using the windows shell rules. Definitions are one of
2910 // -DNAME=<cpp-token>
2911 // -DNAME="c-string with spaces and other characters(?@#$)"
2913 // Watcom will properly parse each of these cases from the
2914 // command line without any escapes. However we still have to
2915 // get the '$' and '#' characters through WMake as '$$' and
2917 for (const char* c = define.c_str(); *c; ++c) {
2918 if (*c == '$' || *c == '#') {
2924 // Make the definition appear properly on the command line. Use
2925 // -DNAME="value" instead of -D"NAME=value" for historical reasons.
2926 std::string::size_type eq = define.find('=');
2927 def += define.substr(0, eq);
2928 if (eq != std::string::npos) {
2930 def += this->EscapeForShell(define.substr(eq + 1), true);
2933 definesString += itemSeparator;
2934 itemSeparator = " ";
2935 definesString += def;
2939 void cmLocalGenerator::AppendFeatureOptions(std::string& flags,
2940 const std::string& lang,
2941 const char* feature)
2943 const char* optionList = this->Makefile->GetDefinition(
2944 cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_", feature));
2945 if (optionList != nullptr) {
2946 std::vector<std::string> options = cmExpandedList(optionList);
2947 for (std::string const& o : options) {
2948 this->AppendFlagEscape(flags, o);
2953 const char* cmLocalGenerator::GetFeature(const std::string& feature,
2954 const std::string& config)
2956 std::string featureName = feature;
2957 // TODO: Define accumulation policy for features (prepend, append, replace).
2958 // Currently we always replace.
2959 if (!config.empty()) {
2961 featureName += cmSystemTools::UpperCase(config);
2963 cmStateSnapshot snp = this->StateSnapshot;
2964 while (snp.IsValid()) {
2965 if (const char* value = snp.GetDirectory().GetProperty(featureName)) {
2968 snp = snp.GetBuildsystemDirectoryParent();
2973 std::string cmLocalGenerator::GetProjectName() const
2975 return this->StateSnapshot.GetProjectName();
2978 std::string cmLocalGenerator::ConstructComment(
2979 cmCustomCommandGenerator const& ccg, const char* default_comment)
2981 // Check for a comment provided with the command.
2982 if (ccg.GetComment()) {
2983 return ccg.GetComment();
2986 // Construct a reasonable default comment if possible.
2987 if (!ccg.GetOutputs().empty()) {
2988 std::string comment;
2989 comment = "Generating ";
2990 const char* sep = "";
2991 std::string currentBinaryDir = this->GetCurrentBinaryDirectory();
2992 for (std::string const& o : ccg.GetOutputs()) {
2994 comment += this->MaybeConvertToRelativePath(currentBinaryDir, o);
3000 // Otherwise use the provided default.
3001 return default_comment;
3004 class cmInstallTargetGeneratorLocal : public cmInstallTargetGenerator
3007 cmInstallTargetGeneratorLocal(cmLocalGenerator* lg, std::string const& t,
3008 std::string const& dest, bool implib)
3009 : cmInstallTargetGenerator(
3010 t, dest, implib, "", std::vector<std::string>(), "Unspecified",
3011 cmInstallGenerator::SelectMessageLevel(lg->GetMakefile()), false,
3018 void cmLocalGenerator::GenerateTargetInstallRules(
3019 std::ostream& os, const std::string& config,
3020 std::vector<std::string> const& configurationTypes)
3022 // Convert the old-style install specification from each target to
3023 // an install generator and run it.
3024 const auto& tgts = this->GetGeneratorTargets();
3025 for (const auto& l : tgts) {
3026 if (l->GetType() == cmStateEnums::INTERFACE_LIBRARY) {
3030 // Include the user-specified pre-install script for this target.
3031 if (const char* preinstall = l->GetProperty("PRE_INSTALL_SCRIPT")) {
3032 cmInstallScriptGenerator g(preinstall, false, "", false);
3033 g.Generate(os, config, configurationTypes);
3036 // Install this target if a destination is given.
3037 if (!l->Target->GetInstallPath().empty()) {
3038 // Compute the full install destination. Note that converting
3039 // to unix slashes also removes any trailing slash.
3040 // We also skip over the leading slash given by the user.
3041 std::string destination = l->Target->GetInstallPath().substr(1);
3042 cmSystemTools::ConvertToUnixSlashes(destination);
3043 if (destination.empty()) {
3047 // Generate the proper install generator for this target type.
3048 switch (l->GetType()) {
3049 case cmStateEnums::EXECUTABLE:
3050 case cmStateEnums::STATIC_LIBRARY:
3051 case cmStateEnums::MODULE_LIBRARY: {
3052 // Use a target install generator.
3053 cmInstallTargetGeneratorLocal g(this, l->GetName(), destination,
3055 g.Generate(os, config, configurationTypes);
3057 case cmStateEnums::SHARED_LIBRARY: {
3058 #if defined(_WIN32) || defined(__CYGWIN__)
3059 // Special code to handle DLL. Install the import library
3060 // to the normal destination and the DLL to the runtime
3062 cmInstallTargetGeneratorLocal g1(this, l->GetName(), destination,
3064 g1.Generate(os, config, configurationTypes);
3065 // We also skip over the leading slash given by the user.
3066 destination = l->Target->GetRuntimeInstallPath().substr(1);
3067 cmSystemTools::ConvertToUnixSlashes(destination);
3068 cmInstallTargetGeneratorLocal g2(this, l->GetName(), destination,
3070 g2.Generate(os, config, configurationTypes);
3072 // Use a target install generator.
3073 cmInstallTargetGeneratorLocal g(this, l->GetName(), destination,
3075 g.Generate(os, config, configurationTypes);
3083 // Include the user-specified post-install script for this target.
3084 if (const char* postinstall = l->GetProperty("POST_INSTALL_SCRIPT")) {
3085 cmInstallScriptGenerator g(postinstall, false, "", false);
3086 g.Generate(os, config, configurationTypes);
3091 #if defined(CM_LG_ENCODE_OBJECT_NAMES)
3092 static bool cmLocalGeneratorShortenObjectName(std::string& objName,
3093 std::string::size_type max_len)
3095 // Replace the beginning of the path portion of the object name with
3097 std::string::size_type pos =
3098 objName.find('/', objName.size() - max_len + 32);
3099 if (pos != std::string::npos) {
3100 cmCryptoHash md5(cmCryptoHash::AlgoMD5);
3101 std::string md5name = cmStrCat(md5.HashString(objName.substr(0, pos)),
3102 cm::string_view(objName).substr(pos));
3105 // The object name is now short enough.
3108 // The object name could not be shortened enough.
3112 bool cmLocalGeneratorCheckObjectName(std::string& objName,
3113 std::string::size_type dir_len,
3114 std::string::size_type max_total_len)
3116 // Enforce the maximum file name length if possible.
3117 std::string::size_type max_obj_len = max_total_len;
3118 if (dir_len < max_total_len) {
3119 max_obj_len = max_total_len - dir_len;
3120 if (objName.size() > max_obj_len) {
3121 // The current object file name is too long. Try to shorten it.
3122 return cmLocalGeneratorShortenObjectName(objName, max_obj_len);
3124 // The object file name is short enough.
3127 // The build directory in which the object will be stored is
3128 // already too deep.
3133 std::string& cmLocalGenerator::CreateSafeUniqueObjectFileName(
3134 const std::string& sin, std::string const& dir_max)
3136 // Look for an existing mapped name for this object file.
3137 auto it = this->UniqueObjectNamesMap.find(sin);
3139 // If no entry exists create one.
3140 if (it == this->UniqueObjectNamesMap.end()) {
3141 // Start with the original name.
3142 std::string ssin = sin;
3144 // Avoid full paths by removing leading slashes.
3145 ssin.erase(0, ssin.find_first_not_of('/'));
3147 // Avoid full paths by removing colons.
3148 std::replace(ssin.begin(), ssin.end(), ':', '_');
3150 // Avoid relative paths that go up the tree.
3151 cmSystemTools::ReplaceString(ssin, "../", "__/");
3154 std::replace(ssin.begin(), ssin.end(), ' ', '_');
3156 // Mangle the name if necessary.
3157 if (this->Makefile->IsOn("CMAKE_MANGLE_OBJECT_FILE_NAMES")) {
3161 sprintf(rpstr, "_p_");
3162 cmSystemTools::ReplaceString(ssin, "+", rpstr);
3163 std::string sssin = sin;
3166 for (it = this->UniqueObjectNamesMap.begin();
3167 it != this->UniqueObjectNamesMap.end(); ++it) {
3168 if (it->second == ssin) {
3176 cmSystemTools::ReplaceString(ssin, "_p_", rpstr);
3177 sprintf(rpstr, "_p%d_", cc++);
3181 #if defined(CM_LG_ENCODE_OBJECT_NAMES)
3182 if (!cmLocalGeneratorCheckObjectName(ssin, dir_max.size(),
3183 this->ObjectPathMax)) {
3184 // Warn if this is the first time the path has been seen.
3185 if (this->ObjectMaxPathViolations.insert(dir_max).second) {
3186 std::ostringstream m;
3187 /* clang-format off */
3188 m << "The object file directory\n"
3189 << " " << dir_max << "\n"
3190 << "has " << dir_max.size() << " characters. "
3191 << "The maximum full path to an object file is "
3192 << this->ObjectPathMax << " characters "
3193 << "(see CMAKE_OBJECT_PATH_MAX). "
3195 << " " << ssin << "\n"
3196 << "cannot be safely placed under this directory. "
3197 << "The build may not work correctly.";
3198 /* clang-format on */
3199 this->IssueMessage(MessageType::WARNING, m.str());
3206 // Insert the newly mapped object file name.
3207 std::map<std::string, std::string>::value_type e(sin, ssin);
3208 it = this->UniqueObjectNamesMap.insert(e).first;
3211 // Return the map entry.
3215 void cmLocalGenerator::ComputeObjectFilenames(
3216 std::map<cmSourceFile const*, std::string>& /*unused*/,
3217 cmGeneratorTarget const* /*unused*/)
3221 bool cmLocalGenerator::IsWindowsShell() const
3223 return this->GetState()->UseWindowsShell();
3226 bool cmLocalGenerator::IsWatcomWMake() const
3228 return this->GetState()->UseWatcomWMake();
3231 bool cmLocalGenerator::IsMinGWMake() const
3233 return this->GetState()->UseMinGWMake();
3236 bool cmLocalGenerator::IsNMake() const
3238 return this->GetState()->UseNMake();
3241 bool cmLocalGenerator::IsNinjaMulti() const
3243 return this->GetState()->UseNinjaMulti();
3246 std::string cmLocalGenerator::GetObjectFileNameWithoutTarget(
3247 const cmSourceFile& source, std::string const& dir_max,
3248 bool* hasSourceExtension, char const* customOutputExtension)
3250 // Construct the object file name using the full path to the source
3251 // file which is its only unique identification.
3252 std::string const& fullPath = source.GetFullPath();
3254 // Try referencing the source relative to the source tree.
3255 std::string relFromSource = this->MaybeConvertToRelativePath(
3256 this->GetCurrentSourceDirectory(), fullPath);
3257 assert(!relFromSource.empty());
3258 bool relSource = !cmSystemTools::FileIsFullPath(relFromSource);
3259 bool subSource = relSource && relFromSource[0] != '.';
3261 // Try referencing the source relative to the binary tree.
3262 std::string relFromBinary = this->MaybeConvertToRelativePath(
3263 this->GetCurrentBinaryDirectory(), fullPath);
3264 assert(!relFromBinary.empty());
3265 bool relBinary = !cmSystemTools::FileIsFullPath(relFromBinary);
3266 bool subBinary = relBinary && relFromBinary[0] != '.';
3268 // Select a nice-looking reference to the source file to construct
3269 // the object file name.
3270 std::string objectName;
3271 if ((relSource && !relBinary) || (subSource && !subBinary)) {
3272 objectName = relFromSource;
3273 } else if ((relBinary && !relSource) || (subBinary && !subSource)) {
3274 objectName = relFromBinary;
3275 } else if (relFromBinary.length() < relFromSource.length()) {
3276 objectName = relFromBinary;
3278 objectName = relFromSource;
3281 // if it is still a full path check for the try compile case
3282 // try compile never have in source sources, and should not
3283 // have conflicting source file names in the same target
3284 if (cmSystemTools::FileIsFullPath(objectName)) {
3285 if (this->GetGlobalGenerator()->GetCMakeInstance()->GetIsInTryCompile()) {
3286 objectName = cmSystemTools::GetFilenameName(source.GetFullPath());
3290 // Ensure that for the CMakeFiles/<target>.dir/generated_source_file
3291 // we don't end up having:
3292 // CMakeFiles/<target>.dir/CMakeFiles/<target>.dir/generated_source_file.obj
3293 const char* unitySourceFile = source.GetProperty("UNITY_SOURCE_FILE");
3294 const char* pchExtension = source.GetProperty("PCH_EXTENSION");
3295 const bool isPchObject = objectName.find("cmake_pch") != std::string::npos;
3296 if (unitySourceFile || pchExtension || isPchObject) {
3298 customOutputExtension = pchExtension;
3301 cmsys::RegularExpression var("(CMakeFiles/[^/]+.dir/)");
3302 if (var.find(objectName)) {
3303 objectName.erase(var.start(), var.end() - var.start());
3307 // Replace the original source file extension with the object file
3309 bool keptSourceExtension = true;
3310 if (!source.GetPropertyAsBool("KEEP_EXTENSION")) {
3311 // Decide whether this language wants to replace the source
3312 // extension with the object extension. For CMake 2.4
3313 // compatibility do this by default.
3314 bool replaceExt = this->NeedBackwardsCompatibility_2_4();
3316 std::string lang = source.GetLanguage();
3317 if (!lang.empty()) {
3318 replaceExt = this->Makefile->IsOn(
3319 cmStrCat("CMAKE_", lang, "_OUTPUT_EXTENSION_REPLACE"));
3323 // Remove the source extension if it is to be replaced.
3324 if (replaceExt || customOutputExtension) {
3325 keptSourceExtension = false;
3326 std::string::size_type dot_pos = objectName.rfind('.');
3327 if (dot_pos != std::string::npos) {
3328 objectName = objectName.substr(0, dot_pos);
3332 // Store the new extension.
3333 if (customOutputExtension) {
3334 objectName += customOutputExtension;
3336 objectName += this->GlobalGenerator->GetLanguageOutputExtension(source);
3339 if (hasSourceExtension) {
3340 *hasSourceExtension = keptSourceExtension;
3343 // Convert to a safe name.
3344 return this->CreateSafeUniqueObjectFileName(objectName, dir_max);
3347 std::string cmLocalGenerator::GetSourceFileLanguage(const cmSourceFile& source)
3349 return source.GetLanguage();
3352 cmake* cmLocalGenerator::GetCMakeInstance() const
3354 return this->GlobalGenerator->GetCMakeInstance();
3357 std::string const& cmLocalGenerator::GetSourceDirectory() const
3359 return this->GetCMakeInstance()->GetHomeDirectory();
3362 std::string const& cmLocalGenerator::GetBinaryDirectory() const
3364 return this->GetCMakeInstance()->GetHomeOutputDirectory();
3367 std::string const& cmLocalGenerator::GetCurrentBinaryDirectory() const
3369 return this->StateSnapshot.GetDirectory().GetCurrentBinary();
3372 std::string const& cmLocalGenerator::GetCurrentSourceDirectory() const
3374 return this->StateSnapshot.GetDirectory().GetCurrentSource();
3377 std::string cmLocalGenerator::MaybeConvertToRelativePath(
3378 std::string const& local_path, std::string const& remote_path) const
3380 return this->StateSnapshot.GetDirectory().ConvertToRelPathIfNotContained(
3381 local_path, remote_path);
3384 std::string cmLocalGenerator::GetTargetDirectory(
3385 const cmGeneratorTarget* /*unused*/) const
3387 cmSystemTools::Error("GetTargetDirectory"
3388 " called on cmLocalGenerator");
3392 KWIML_INT_uint64_t cmLocalGenerator::GetBackwardsCompatibility()
3394 // The computed version may change until the project is fully
3396 if (!this->BackwardsCompatibilityFinal) {
3397 unsigned int major = 0;
3398 unsigned int minor = 0;
3399 unsigned int patch = 0;
3400 if (const char* value =
3401 this->Makefile->GetDefinition("CMAKE_BACKWARDS_COMPATIBILITY")) {
3402 switch (sscanf(value, "%u.%u.%u", &major, &minor, &patch)) {
3414 this->BackwardsCompatibility = CMake_VERSION_ENCODE(major, minor, patch);
3415 this->BackwardsCompatibilityFinal = true;
3418 return this->BackwardsCompatibility;
3421 bool cmLocalGenerator::NeedBackwardsCompatibility_2_4()
3423 // Check the policy to decide whether to pay attention to this
3425 switch (this->GetPolicyStatus(cmPolicies::CMP0001)) {
3426 case cmPolicies::WARN:
3427 // WARN is just OLD without warning because user code does not
3428 // always affect whether this check is done.
3429 case cmPolicies::OLD:
3430 // Old behavior is to check the variable.
3432 case cmPolicies::NEW:
3433 // New behavior is to ignore the variable.
3435 case cmPolicies::REQUIRED_IF_USED:
3436 case cmPolicies::REQUIRED_ALWAYS:
3437 // This will never be the case because the only way to require
3438 // the setting is to require the user to specify version policy
3439 // 2.6 or higher. Once we add that requirement then this whole
3440 // method can be removed anyway.
3444 // Compatibility is needed if CMAKE_BACKWARDS_COMPATIBILITY is set
3445 // equal to or lower than the given version.
3446 KWIML_INT_uint64_t actual_compat = this->GetBackwardsCompatibility();
3447 return (actual_compat && actual_compat <= CMake_VERSION_ENCODE(2, 4, 255));
3450 cmPolicies::PolicyStatus cmLocalGenerator::GetPolicyStatus(
3451 cmPolicies::PolicyID id) const
3453 return this->Makefile->GetPolicyStatus(id);
3456 bool cmLocalGenerator::CheckDefinition(std::string const& define) const
3458 // Many compilers do not support -DNAME(arg)=sdf so we disable it.
3459 std::string::size_type pos = define.find_first_of("(=");
3460 if (pos != std::string::npos) {
3461 if (define[pos] == '(') {
3462 std::ostringstream e;
3463 /* clang-format off */
3464 e << "WARNING: Function-style preprocessor definitions may not be "
3465 << "passed on the compiler command line because many compilers "
3466 << "do not support it.\n"
3467 << "CMake is dropping a preprocessor definition: " << define << "\n"
3468 << "Consider defining the macro in a (configured) header file.\n";
3469 /* clang-format on */
3470 cmSystemTools::Message(e.str());
3475 // Many compilers do not support # in the value so we disable it.
3476 if (define.find_first_of('#') != std::string::npos) {
3477 std::ostringstream e;
3478 /* clang-format off */
3479 e << "WARNING: Preprocessor definitions containing '#' may not be "
3480 << "passed on the compiler command line because many compilers "
3481 << "do not support it.\n"
3482 << "CMake is dropping a preprocessor definition: " << define << "\n"
3483 << "Consider defining the macro in a (configured) header file.\n";
3484 /* clang-format on */
3485 cmSystemTools::Message(e.str());
3489 // Assume it is supported.
3493 static void cmLGInfoProp(cmMakefile* mf, cmGeneratorTarget* target,
3494 const std::string& prop)
3496 if (const char* val = target->GetProperty(prop)) {
3497 mf->AddDefinition(prop, val);
3501 void cmLocalGenerator::GenerateAppleInfoPList(cmGeneratorTarget* target,
3502 const std::string& targetName,
3503 const std::string& fname)
3505 // Find the Info.plist template.
3506 const char* in = target->GetProperty("MACOSX_BUNDLE_INFO_PLIST");
3507 std::string inFile = (in && *in) ? in : "MacOSXBundleInfo.plist.in";
3508 if (!cmSystemTools::FileIsFullPath(inFile)) {
3509 std::string inMod = this->Makefile->GetModulesFile(inFile);
3510 if (!inMod.empty()) {
3514 if (!cmSystemTools::FileExists(inFile, true)) {
3515 std::ostringstream e;
3516 e << "Target " << target->GetName() << " Info.plist template \"" << inFile
3517 << "\" could not be found.";
3518 cmSystemTools::Error(e.str());
3522 // Convert target properties to variables in an isolated makefile
3523 // scope to configure the file. If properties are set they will
3524 // override user make variables. If not the configuration will fall
3525 // back to the directory-level values set by the user.
3526 cmMakefile* mf = this->Makefile;
3527 cmMakefile::ScopePushPop varScope(mf);
3528 mf->AddDefinition("MACOSX_BUNDLE_EXECUTABLE_NAME", targetName);
3529 cmLGInfoProp(mf, target, "MACOSX_BUNDLE_INFO_STRING");
3530 cmLGInfoProp(mf, target, "MACOSX_BUNDLE_ICON_FILE");
3531 cmLGInfoProp(mf, target, "MACOSX_BUNDLE_GUI_IDENTIFIER");
3532 cmLGInfoProp(mf, target, "MACOSX_BUNDLE_LONG_VERSION_STRING");
3533 cmLGInfoProp(mf, target, "MACOSX_BUNDLE_BUNDLE_NAME");
3534 cmLGInfoProp(mf, target, "MACOSX_BUNDLE_SHORT_VERSION_STRING");
3535 cmLGInfoProp(mf, target, "MACOSX_BUNDLE_BUNDLE_VERSION");
3536 cmLGInfoProp(mf, target, "MACOSX_BUNDLE_COPYRIGHT");
3537 mf->ConfigureFile(inFile, fname, false, false, false);
3540 void cmLocalGenerator::GenerateFrameworkInfoPList(
3541 cmGeneratorTarget* target, const std::string& targetName,
3542 const std::string& fname)
3544 // Find the Info.plist template.
3545 const char* in = target->GetProperty("MACOSX_FRAMEWORK_INFO_PLIST");
3546 std::string inFile = (in && *in) ? in : "MacOSXFrameworkInfo.plist.in";
3547 if (!cmSystemTools::FileIsFullPath(inFile)) {
3548 std::string inMod = this->Makefile->GetModulesFile(inFile);
3549 if (!inMod.empty()) {
3553 if (!cmSystemTools::FileExists(inFile, true)) {
3554 std::ostringstream e;
3555 e << "Target " << target->GetName() << " Info.plist template \"" << inFile
3556 << "\" could not be found.";
3557 cmSystemTools::Error(e.str());
3561 // Convert target properties to variables in an isolated makefile
3562 // scope to configure the file. If properties are set they will
3563 // override user make variables. If not the configuration will fall
3564 // back to the directory-level values set by the user.
3565 cmMakefile* mf = this->Makefile;
3566 cmMakefile::ScopePushPop varScope(mf);
3567 mf->AddDefinition("MACOSX_FRAMEWORK_NAME", targetName);
3568 cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_ICON_FILE");
3569 cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_IDENTIFIER");
3570 cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_SHORT_VERSION_STRING");
3571 cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_BUNDLE_VERSION");
3572 mf->ConfigureFile(inFile, fname, false, false, false);
3576 void CreateGeneratedSource(cmLocalGenerator& lg, const std::string& output,
3577 cmCommandOrigin origin,
3578 const cmListFileBacktrace& lfbt)
3580 if (cmGeneratorExpression::Find(output) == std::string::npos) {
3581 // Outputs without generator expressions from the project are already
3582 // created and marked as generated. Do not mark them again, because
3583 // other commands might have overwritten the property.
3584 if (origin == cmCommandOrigin::Generator) {
3585 lg.GetMakefile()->GetOrCreateGeneratedSource(output);
3588 lg.GetCMakeInstance()->IssueMessage(
3589 MessageType::FATAL_ERROR,
3590 "Generator expressions in custom command outputs are not implemented!",
3595 void CreateGeneratedSources(cmLocalGenerator& lg,
3596 const std::vector<std::string>& outputs,
3597 cmCommandOrigin origin,
3598 const cmListFileBacktrace& lfbt)
3600 for (std::string const& o : outputs) {
3601 CreateGeneratedSource(lg, o, origin, lfbt);
3605 cmSourceFile* AddCustomCommand(
3606 cmLocalGenerator& lg, const cmListFileBacktrace& lfbt,
3607 const std::vector<std::string>& outputs,
3608 const std::vector<std::string>& byproducts,
3609 const std::vector<std::string>& depends, const std::string& main_dependency,
3610 const cmImplicitDependsList& implicit_depends,
3611 const cmCustomCommandLines& commandLines, const char* comment,
3612 const char* workingDir, bool replace, bool escapeOldStyle,
3613 bool uses_terminal, bool command_expand_lists, const std::string& depfile,
3614 const std::string& job_pool)
3616 cmMakefile* mf = lg.GetMakefile();
3618 // Choose a source file on which to store the custom command.
3619 cmSourceFile* file = nullptr;
3620 if (!commandLines.empty() && !main_dependency.empty()) {
3621 // The main dependency was specified. Use it unless a different
3622 // custom command already used it.
3623 file = mf->GetSource(main_dependency);
3624 if (file && file->GetCustomCommand() && !replace) {
3625 // The main dependency already has a custom command.
3626 if (commandLines == file->GetCustomCommand()->GetCommandLines()) {
3627 // The existing custom command is identical. Silently ignore
3631 // The existing custom command is different. We need to
3632 // generate a rule file for this new command.
3635 file = mf->CreateSource(main_dependency);
3639 // Generate a rule file if the main dependency is not available.
3641 cmGlobalGenerator* gg = lg.GetGlobalGenerator();
3643 // Construct a rule file associated with the first output produced.
3644 std::string outName = gg->GenerateRuleFile(outputs[0]);
3646 // Check if the rule file already exists.
3647 file = mf->GetSource(outName, cmSourceFileLocationKind::Known);
3648 if (file && file->GetCustomCommand() && !replace) {
3649 // The rule file already exists.
3650 if (commandLines != file->GetCustomCommand()->GetCommandLines()) {
3651 lg.GetCMakeInstance()->IssueMessage(
3652 MessageType::FATAL_ERROR,
3653 cmStrCat("Attempt to add a custom rule to output\n ", outName,
3654 "\nwhich already has a custom rule."),
3660 // Create a cmSourceFile for the rule file.
3662 file = mf->CreateSource(outName, true, cmSourceFileLocationKind::Known);
3664 file->SetProperty("__CMAKE_RULE", "1");
3667 // Attach the custom command to the file.
3669 // Construct a complete list of dependencies.
3670 std::vector<std::string> depends2(depends);
3671 if (!main_dependency.empty()) {
3672 depends2.push_back(main_dependency);
3675 std::unique_ptr<cmCustomCommand> cc = cm::make_unique<cmCustomCommand>(
3676 outputs, byproducts, depends2, commandLines, lfbt, comment, workingDir);
3677 cc->SetEscapeOldStyle(escapeOldStyle);
3678 cc->SetEscapeAllowMakeVars(true);
3679 cc->SetImplicitDepends(implicit_depends);
3680 cc->SetUsesTerminal(uses_terminal);
3681 cc->SetCommandExpandLists(command_expand_lists);
3682 cc->SetDepfile(depfile);
3683 cc->SetJobPool(job_pool);
3684 file->SetCustomCommand(std::move(cc));
3686 mf->AddSourceOutputs(file, outputs, byproducts);
3693 void AddCustomCommandToTarget(cmLocalGenerator& lg,
3694 const cmListFileBacktrace& lfbt,
3695 cmCommandOrigin origin, cmTarget* target,
3696 const std::vector<std::string>& byproducts,
3697 const std::vector<std::string>& depends,
3698 const cmCustomCommandLines& commandLines,
3699 cmCustomCommandType type, const char* comment,
3700 const char* workingDir, bool escapeOldStyle,
3701 bool uses_terminal, const std::string& depfile,
3702 const std::string& job_pool,
3703 bool command_expand_lists)
3705 cmMakefile* mf = lg.GetMakefile();
3707 // Always create the byproduct sources and mark them generated.
3708 CreateGeneratedSources(lg, byproducts, origin, lfbt);
3710 // Add the command to the appropriate build step for the target.
3711 std::vector<std::string> no_output;
3712 cmCustomCommand cc(no_output, byproducts, depends, commandLines, lfbt,
3713 comment, workingDir);
3714 cc.SetEscapeOldStyle(escapeOldStyle);
3715 cc.SetEscapeAllowMakeVars(true);
3716 cc.SetUsesTerminal(uses_terminal);
3717 cc.SetCommandExpandLists(command_expand_lists);
3718 cc.SetDepfile(depfile);
3719 cc.SetJobPool(job_pool);
3721 case cmCustomCommandType::PRE_BUILD:
3722 target->AddPreBuildCommand(std::move(cc));
3724 case cmCustomCommandType::PRE_LINK:
3725 target->AddPreLinkCommand(std::move(cc));
3727 case cmCustomCommandType::POST_BUILD:
3728 target->AddPostBuildCommand(std::move(cc));
3732 mf->AddTargetByproducts(target, byproducts);
3735 cmSourceFile* AddCustomCommandToOutput(
3736 cmLocalGenerator& lg, const cmListFileBacktrace& lfbt,
3737 cmCommandOrigin origin, const std::vector<std::string>& outputs,
3738 const std::vector<std::string>& byproducts,
3739 const std::vector<std::string>& depends, const std::string& main_dependency,
3740 const cmImplicitDependsList& implicit_depends,
3741 const cmCustomCommandLines& commandLines, const char* comment,
3742 const char* workingDir, bool replace, bool escapeOldStyle,
3743 bool uses_terminal, bool command_expand_lists, const std::string& depfile,
3744 const std::string& job_pool)
3746 // Always create the output sources and mark them generated.
3747 CreateGeneratedSources(lg, outputs, origin, lfbt);
3748 CreateGeneratedSources(lg, byproducts, origin, lfbt);
3750 return AddCustomCommand(
3751 lg, lfbt, outputs, byproducts, depends, main_dependency, implicit_depends,
3752 commandLines, comment, workingDir, replace, escapeOldStyle, uses_terminal,
3753 command_expand_lists, depfile, job_pool);
3756 void AppendCustomCommandToOutput(cmLocalGenerator& lg,
3757 const cmListFileBacktrace& lfbt,
3758 const std::string& output,
3759 const std::vector<std::string>& depends,
3760 const cmImplicitDependsList& implicit_depends,
3761 const cmCustomCommandLines& commandLines)
3763 // Lookup an existing command.
3764 if (cmSourceFile* sf = lg.GetMakefile()->GetSourceFileWithOutput(output)) {
3765 if (cmCustomCommand* cc = sf->GetCustomCommand()) {
3766 cc->AppendCommands(commandLines);
3767 cc->AppendDepends(depends);
3768 cc->AppendImplicitDepends(implicit_depends);
3773 // No existing command found.
3774 lg.GetCMakeInstance()->IssueMessage(
3775 MessageType::FATAL_ERROR,
3776 cmStrCat("Attempt to append to output\n ", output,
3777 "\nwhich is not already a custom command output."),
3781 void AddUtilityCommand(cmLocalGenerator& lg, const cmListFileBacktrace& lfbt,
3782 cmCommandOrigin origin, cmTarget* target,
3783 const cmUtilityOutput& force, const char* workingDir,
3784 const std::vector<std::string>& byproducts,
3785 const std::vector<std::string>& depends,
3786 const cmCustomCommandLines& commandLines,
3787 bool escapeOldStyle, const char* comment,
3788 bool uses_terminal, bool command_expand_lists,
3789 const std::string& job_pool)
3791 // Always create the byproduct sources and mark them generated.
3792 CreateGeneratedSource(lg, force.Name, origin, lfbt);
3793 CreateGeneratedSources(lg, byproducts, origin, lfbt);
3795 // Use an empty comment to avoid generation of default comment.
3800 std::string no_main_dependency;
3801 cmImplicitDependsList no_implicit_depends;
3802 cmSourceFile* rule = AddCustomCommand(
3803 lg, lfbt, { force.Name }, byproducts, depends, no_main_dependency,
3804 no_implicit_depends, commandLines, comment, workingDir, /*replace=*/false,
3805 escapeOldStyle, uses_terminal, command_expand_lists, /*depfile=*/"",
3808 lg.GetMakefile()->AddTargetByproducts(target, byproducts);
3811 if (!force.NameCMP0049.empty()) {
3812 target->AddSource(force.NameCMP0049);