| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmLocalGenerator.h" | |
| #include <algorithm> | |
| #include <array> | |
| #include <cassert> | |
| #include <cstdio> | |
| #include <cstdlib> | |
| #include <initializer_list> | |
| #include <iterator> | |
| #include <sstream> | |
| #include <unordered_set> | |
| #include <utility> | |
| #include <vector> | |
| #include <cm/memory> | |
| #include <cm/optional> | |
| #include <cm/string_view> | |
| #include <cmext/algorithm> | |
| #include <cmext/string_view> | |
| #include "cmsys/RegularExpression.hxx" | |
| #include "cmsys/String.h" | |
| #include "cmAlgorithms.h" | |
| #include "cmCMakePath.h" | |
| #include "cmComputeLinkInformation.h" | |
| #include "cmCryptoHash.h" | |
| #include "cmCustomCommand.h" | |
| #include "cmCustomCommandGenerator.h" | |
| #include "cmCustomCommandLines.h" | |
| #include "cmCustomCommandTypes.h" | |
| #include "cmFileSetMetadata.h" | |
| #include "cmGeneratedFileStream.h" | |
| #include "cmGeneratorExpression.h" | |
| #include "cmGeneratorExpressionEvaluationFile.h" | |
| #include "cmGeneratorFileSet.h" | |
| #include "cmGeneratorRule.h" | |
| #include "cmGeneratorTarget.h" | |
| #include "cmGlobalGenerator.h" | |
| #include "cmInstallGenerator.h" | |
| #include "cmInstallScriptGenerator.h" | |
| #include "cmInstallTargetGenerator.h" | |
| #include "cmLinkLineComputer.h" | |
| #include "cmLinkLineDeviceComputer.h" | |
| #include "cmList.h" | |
| #include "cmMakefile.h" | |
| #include "cmMessageType.h" | |
| #include "cmObjectLocation.h" | |
| #include "cmRange.h" | |
| #include "cmRulePlaceholderExpander.h" | |
| #include "cmScriptGenerator.h" | |
| #include "cmSourceFile.h" | |
| #include "cmSourceFileLocation.h" | |
| #include "cmSourceFileLocationKind.h" | |
| #include "cmSourceGroup.h" | |
| #include "cmStandardLevelResolver.h" | |
| #include "cmState.h" | |
| #include "cmStateDirectory.h" | |
| #include "cmStateTypes.h" | |
| #include "cmStringAlgorithms.h" | |
| #include "cmSystemTools.h" | |
| #include "cmTarget.h" | |
| #include "cmTargetTypes.h" | |
| #include "cmTestGenerator.h" | |
| #include "cmValue.h" | |
| #include "cmake.h" | |
| #if defined(__HAIKU__) | |
| # include <FindDirectory.h> | |
| # include <StorageDefs.h> | |
| #endif | |
| namespace { | |
| // List of variables that are replaced when | |
| // rules are expanded. These variables are | |
| // replaced in the form <var> with GetSafeDefinition(var). | |
| // ${LANG} is replaced in the variable first with all enabled | |
| // languages. | |
| auto ruleReplaceVars = { | |
| "CMAKE_${LANG}_COMPILER", | |
| "CMAKE_SHARED_MODULE_${LANG}_FLAGS", | |
| "CMAKE_SHARED_LIBRARY_${LANG}_FLAGS", | |
| "CMAKE_SHARED_LIBRARY_SONAME_${LANG}_FLAG", | |
| "CMAKE_${LANG}_ARCHIVE", | |
| "CMAKE_AR", | |
| "CMAKE_SOURCE_DIR", | |
| "CMAKE_BINARY_DIR", | |
| "CMAKE_CURRENT_SOURCE_DIR", | |
| "CMAKE_CURRENT_BINARY_DIR", | |
| "CMAKE_RANLIB", | |
| "CMAKE_MT", | |
| "CMAKE_TAPI", | |
| "CMAKE_CUDA_HOST_COMPILER", | |
| "CMAKE_CUDA_HOST_LINK_LAUNCHER", | |
| "CMAKE_HIP_HOST_COMPILER", | |
| "CMAKE_HIP_HOST_LINK_LAUNCHER", | |
| "CMAKE_CL_SHOWINCLUDES_PREFIX", | |
| }; | |
| // Variables whose placeholders now map to an empty string. | |
| // Our platform modules no longer use these, but third-party code might. | |
| auto ruleReplaceEmptyVars = { | |
| "CMAKE_SHARED_LIBRARY_CREATE_${LANG}_FLAGS", | |
| "CMAKE_SHARED_MODULE_CREATE_${LANG}_FLAGS", | |
| "CMAKE_${LANG}_LINK_FLAGS", | |
| }; | |
| } | |
| cmLocalGenerator::cmLocalGenerator(cmGlobalGenerator* gg, cmMakefile* makefile) | |
| : cmOutputConverter(makefile->GetStateSnapshot()) | |
| , DirectoryBacktrace(makefile->GetBacktrace()) | |
| { | |
| this->GlobalGenerator = gg; | |
| this->Makefile = makefile; | |
| this->AliasTargets = makefile->GetAliasTargets(); | |
| this->EmitUniversalBinaryFlags = true; | |
| this->ComputeObjectMaxPath(); | |
| // Canonicalize entries of the CPATH environment variable the same | |
| // way detection of CMAKE_<LANG>_IMPLICIT_INCLUDE_DIRECTORIES does. | |
| { | |
| std::vector<std::string> cpath; | |
| cmSystemTools::GetPath(cpath, "CPATH"); | |
| for (std::string const& cp : cpath) { | |
| if (cmSystemTools::FileIsFullPath(cp)) { | |
| this->EnvCPATH.emplace_back(cmSystemTools::CollapseFullPath(cp)); | |
| } | |
| } | |
| } | |
| std::vector<std::string> enabledLanguages = | |
| this->GetState()->GetEnabledLanguages(); | |
| if (cmValue sysrootCompile = | |
| this->Makefile->GetDefinition("CMAKE_SYSROOT_COMPILE")) { | |
| this->CompilerSysroot = *sysrootCompile; | |
| } else { | |
| this->CompilerSysroot = this->Makefile->GetSafeDefinition("CMAKE_SYSROOT"); | |
| } | |
| if (cmValue sysrootLink = | |
| this->Makefile->GetDefinition("CMAKE_SYSROOT_LINK")) { | |
| this->LinkerSysroot = *sysrootLink; | |
| } else { | |
| this->LinkerSysroot = this->Makefile->GetSafeDefinition("CMAKE_SYSROOT"); | |
| } | |
| // OSX SYSROOT can be required by some tools, like tapi | |
| { | |
| cmValue osxSysroot = this->Makefile->GetDefinition("CMAKE_OSX_SYSROOT"); | |
| this->VariableMappings["CMAKE_OSX_SYSROOT"] = | |
| osxSysroot.IsEmpty() ? "/" : this->EscapeForShell(*osxSysroot, true); | |
| } | |
| if (cmValue appleArchSysroots = | |
| this->Makefile->GetDefinition("CMAKE_APPLE_ARCH_SYSROOTS")) { | |
| std::string const& appleArchs = | |
| this->Makefile->GetSafeDefinition("CMAKE_OSX_ARCHITECTURES"); | |
| cmList archs(appleArchs); | |
| cmList sysroots{ appleArchSysroots, cmList::EmptyElements::Yes }; | |
| if (archs.size() == sysroots.size()) { | |
| for (cmList::size_type i = 0; i < archs.size(); ++i) { | |
| this->AppleArchSysroots[archs[i]] = sysroots[i]; | |
| } | |
| } else { | |
| std::string const e = | |
| cmStrCat("CMAKE_APPLE_ARCH_SYSROOTS:\n ", *appleArchSysroots, | |
| "\n" | |
| "is not the same length as CMAKE_OSX_ARCHITECTURES:\n ", | |
| appleArchs); | |
| this->IssueMessage(MessageType::FATAL_ERROR, e); | |
| } | |
| } | |
| for (std::string const& lang : enabledLanguages) { | |
| if (lang == "NONE") { | |
| continue; | |
| } | |
| this->Compilers[cmStrCat("CMAKE_", lang, "_COMPILER")] = lang; | |
| this->VariableMappings[cmStrCat("CMAKE_", lang, "_COMPILER")] = | |
| this->Makefile->GetSafeDefinition(cmStrCat("CMAKE_", lang, "_COMPILER")); | |
| std::string const& compilerArg1 = | |
| cmStrCat("CMAKE_", lang, "_COMPILER_ARG1"); | |
| std::string const& compilerTarget = | |
| cmStrCat("CMAKE_", lang, "_COMPILER_TARGET"); | |
| std::string const& compilerOptionTarget = | |
| cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_TARGET"); | |
| std::string const& compilerExternalToolchain = | |
| cmStrCat("CMAKE_", lang, "_COMPILER_EXTERNAL_TOOLCHAIN"); | |
| std::string const& compilerOptionExternalToolchain = | |
| cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_EXTERNAL_TOOLCHAIN"); | |
| std::string const& compilerOptionSysroot = | |
| cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_SYSROOT"); | |
| this->VariableMappings[compilerArg1] = | |
| this->Makefile->GetSafeDefinition(compilerArg1); | |
| this->VariableMappings[compilerTarget] = | |
| this->Makefile->GetSafeDefinition(compilerTarget); | |
| this->VariableMappings[compilerOptionTarget] = | |
| this->Makefile->GetSafeDefinition(compilerOptionTarget); | |
| this->VariableMappings[compilerExternalToolchain] = | |
| this->Makefile->GetSafeDefinition(compilerExternalToolchain); | |
| this->VariableMappings[compilerOptionExternalToolchain] = | |
| this->Makefile->GetSafeDefinition(compilerOptionExternalToolchain); | |
| this->VariableMappings[compilerOptionSysroot] = | |
| this->Makefile->GetSafeDefinition(compilerOptionSysroot); | |
| for (std::string replaceVar : ruleReplaceVars) { | |
| if (replaceVar.find("${LANG}") != std::string::npos) { | |
| cmSystemTools::ReplaceString(replaceVar, "${LANG}", lang); | |
| } | |
| this->VariableMappings[replaceVar] = | |
| this->Makefile->GetSafeDefinition(replaceVar); | |
| } | |
| for (std::string replaceVar : ruleReplaceEmptyVars) { | |
| if (replaceVar.find("${LANG}") != std::string::npos) { | |
| cmSystemTools::ReplaceString(replaceVar, "${LANG}", lang); | |
| } | |
| this->VariableMappings[replaceVar] = std::string(); | |
| } | |
| } | |
| } | |
| std::unique_ptr<cmRulePlaceholderExpander> | |
| cmLocalGenerator::CreateRulePlaceholderExpander(cmBuildStep buildStep) const | |
| { | |
| return cm::make_unique<cmRulePlaceholderExpander>( | |
| buildStep, this->Compilers, this->VariableMappings, this->CompilerSysroot, | |
| this->LinkerSysroot, | |
| this->GetState()->UseWatcomWMake() || this->GetState()->UseBorlandMake() | |
| ? cmRulePlaceholderExpander::UseShortPaths::Yes | |
| : cmRulePlaceholderExpander::UseShortPaths::No); | |
| } | |
| void cmLocalGenerator::AddDirectoryTestPrepDependency( | |
| std::string const& config, DirectoryTestPrepDependency dependency) | |
| { | |
| this->DiscoveryTestPrepDependencies[config].push_back(std::move(dependency)); | |
| } | |
| bool cmLocalGenerator::GetDirectoryTestPrepTarget(DirectoryTestPrepTarget& out, | |
| std::string const& config) | |
| { | |
| cmValue const directoryTestPrepDependencies = | |
| this->GetStateSnapshot().GetDirectory().GetProperty( | |
| "CMAKE_TEST_BUILD_DEPENDS"); | |
| std::string const directoryId = | |
| cmCryptoHash(cmCryptoHash::AlgoMD5) | |
| .HashString(this->GetCurrentBinaryDirectory()) | |
| .substr(0, 8); | |
| out.Name = cmStrCat("test_prep/directory_", directoryId); | |
| out.Comment = | |
| cmStrCat("Build extra dependencies for test prep in directory ", | |
| this->GetCurrentBinaryDirectory()); | |
| out.Dependencies.clear(); | |
| auto const discovery = this->DiscoveryTestPrepDependencies.find(config); | |
| if (discovery != this->DiscoveryTestPrepDependencies.end()) { | |
| out.Dependencies = discovery->second; | |
| } | |
| cmList buildDepends; | |
| if (directoryTestPrepDependencies) { | |
| buildDepends.assign( | |
| cmList{ *directoryTestPrepDependencies, cmList::EmptyElements::Yes }); | |
| } | |
| cmTestGenerator::BuildDependencies deps; | |
| cmTestGenerator::EvaluateBuildDependencies( | |
| this, config, this->Makefile->GetBacktrace(), "directory_test_prep", {}, | |
| std::vector<std::string>(buildDepends.begin(), buildDepends.end()), deps, | |
| this->Makefile); | |
| for (cmGeneratorTarget* target : deps.Targets) { | |
| out.Dependencies.push_back({ target->GetName(), target }); | |
| } | |
| for (cmTestGenerator::BuildDependencies::FileDependency const& file : | |
| deps.Files) { | |
| out.Dependencies.push_back( | |
| { file.Path, nullptr, file.Owner, file.Generated }); | |
| } | |
| return !out.Dependencies.empty(); | |
| } | |
| cmLocalGenerator::~cmLocalGenerator() = default; | |
| void cmLocalGenerator::IssueMessage(MessageType type, std::string const& text, | |
| cmListFileBacktrace const& bt) const | |
| { | |
| this->GetMakefile()->IssueMessage(type, text, bt); | |
| } | |
| void cmLocalGenerator::IssueDiagnostic( | |
| cmDiagnosticCategory category, std::string const& text, | |
| cmDiagnosticContext const& context) const | |
| { | |
| this->GetMakefile()->IssueDiagnostic(category, text, context); | |
| } | |
| void cmLocalGenerator::IssuePolicyWarning(cmPolicies::PolicyID policy, | |
| cm::string_view preface, | |
| cm::string_view postface, | |
| cmListFileBacktrace const& bt) const | |
| { | |
| this->GetMakefile()->IssuePolicyWarning(policy, preface, postface, bt); | |
| } | |
| void cmLocalGenerator::ComputeObjectMaxPath() | |
| { | |
| // Choose a maximum object file name length. | |
| #if defined(_WIN32) || defined(__CYGWIN__) | |
| this->ObjectPathMax = 250; | |
| #else | |
| this->ObjectPathMax = 1000; | |
| #endif | |
| cmValue plen = this->Makefile->GetDefinition("CMAKE_OBJECT_PATH_MAX"); | |
| if (cmNonempty(plen)) { | |
| unsigned int pmax; | |
| if (sscanf(plen->c_str(), "%u", &pmax) == 1) { | |
| if (pmax >= 128) { | |
| this->ObjectPathMax = pmax; | |
| } else { | |
| this->IssueDiagnostic( | |
| cmDiagnostics::CMD_AUTHOR, | |
| cmStrCat("CMAKE_OBJECT_PATH_MAX is set to ", pmax, | |
| ", which is less than the minimum of 128. " | |
| "The value will be ignored.")); | |
| } | |
| } else { | |
| this->IssueDiagnostic( | |
| cmDiagnostics::CMD_AUTHOR, | |
| cmStrCat("CMAKE_OBJECT_PATH_MAX is set to \"", *plen, | |
| "\", which fails to parse as a positive integer. " | |
| "The value will be ignored.")); | |
| } | |
| } | |
| this->ObjectMaxPathViolations.clear(); | |
| } | |
| static void MoveSystemIncludesToEnd(std::vector<std::string>& includeDirs, | |
| std::string const& config, | |
| std::string const& lang, | |
| cmGeneratorTarget const* target) | |
| { | |
| if (!target) { | |
| return; | |
| } | |
| std::stable_sort( | |
| includeDirs.begin(), includeDirs.end(), | |
| [&target, &config, &lang](std::string const& a, std::string const& b) { | |
| return !target->IsSystemIncludeDirectory(a, config, lang) && | |
| target->IsSystemIncludeDirectory(b, config, lang); | |
| }); | |
| } | |
| static void MoveSystemIncludesToEnd(std::vector<BT<std::string>>& includeDirs, | |
| std::string const& config, | |
| std::string const& lang, | |
| cmGeneratorTarget const* target) | |
| { | |
| if (!target) { | |
| return; | |
| } | |
| std::stable_sort(includeDirs.begin(), includeDirs.end(), | |
| [target, &config, &lang](BT<std::string> const& a, | |
| BT<std::string> const& b) { | |
| return !target->IsSystemIncludeDirectory(a.Value, config, | |
| lang) && | |
| target->IsSystemIncludeDirectory(b.Value, config, lang); | |
| }); | |
| } | |
| void cmLocalGenerator::TraceDependencies() const | |
| { | |
| // Generate the rule files for each target. | |
| auto const& targets = this->GetGeneratorTargets(); | |
| for (auto const& target : targets) { | |
| if (!target->IsInBuildSystem()) { | |
| continue; | |
| } | |
| target->TraceDependencies(); | |
| } | |
| } | |
| #ifndef CMAKE_BOOTSTRAP | |
| void cmLocalGenerator::ResolveSourceGroupGenex() | |
| { | |
| this->Makefile->ResolveSourceGroupGenex(this); | |
| } | |
| #endif | |
| void cmLocalGenerator::GenerateTestFiles() | |
| { | |
| if (!this->Makefile->IsOn("CMAKE_TESTING_ENABLED")) { | |
| return; | |
| } | |
| // Compute the set of configurations. | |
| std::vector<std::string> configurationTypes = | |
| this->Makefile->GetGeneratorConfigs(cmMakefile::OnlyMultiConfig); | |
| std::string config = this->Makefile->GetDefaultConfiguration(); | |
| std::string file = | |
| cmStrCat(this->StateSnapshot.GetDirectory().GetCurrentBinary(), | |
| "/CTestTestfile.cmake"); | |
| this->GlobalGenerator->AddTestFile(file); | |
| cmGeneratedFileStream fout(file); | |
| fout << "# CMake generated Testfile for \n" | |
| "# Source directory: " | |
| << this->StateSnapshot.GetDirectory().GetCurrentSource() | |
| << "\n" | |
| "# Build directory: " | |
| << this->StateSnapshot.GetDirectory().GetCurrentBinary() | |
| << "\n" | |
| "# \n" | |
| "# This file includes the relevant testing commands " | |
| "required for \n" | |
| "# testing this directory and lists subdirectories to " | |
| "be tested as well.\n"; | |
| std::string resourceSpecFile = | |
| this->Makefile->GetSafeDefinition("CTEST_RESOURCE_SPEC_FILE"); | |
| if (!resourceSpecFile.empty()) { | |
| fout << "set(CTEST_RESOURCE_SPEC_FILE \"" << resourceSpecFile << "\")\n"; | |
| } | |
| std::string resourceErrorAction = | |
| this->Makefile->GetSafeDefinition("CTEST_RESOURCE_ERROR_ACTION"); | |
| if (!resourceErrorAction.empty()) { | |
| fout << "set(CTEST_RESOURCE_ERROR_ACTION \"" << resourceErrorAction | |
| << "\")\n"; | |
| } | |
| auto writeTestIncludeFile = [this, &fout, &configurationTypes, | |
| &config](std::string const& entry) { | |
| // Entries without a generator expression are emitted verbatim, | |
| // preserving CTest-time ${VAR} expansion of the path. | |
| if (cmGeneratorExpression::Find(entry) == std::string::npos) { | |
| fout << "include(\"" << entry << "\")\n"; | |
| return; | |
| } | |
| // Emit one include() per path in an evaluated result. As for list-valued | |
| // properties, the result is split on ';', and each path is quoted so that | |
| // any '"', '$', or '\' cannot break the include() argument or trigger a | |
| // second-stage CTest ${...} expansion. An empty result adds no include. | |
| auto writeIncludes = [&fout](std::vector<std::string> const& paths, | |
| char const* indent) { | |
| for (std::string const& path : paths) { | |
| fout << indent << "include(" << cmScriptGenerator::Quote(path) | |
| << ")\n"; | |
| } | |
| }; | |
| if (!this->GlobalGenerator->IsMultiConfig() || | |
| configurationTypes.empty()) { | |
| // Single-config generator, or a multi-config generator with no | |
| // configuration types: evaluate once with the default configuration | |
| // and emit unconditional includes. | |
| std::vector<std::string> paths; | |
| cmExpandList(cmGeneratorExpression::Evaluate(entry, this, config), | |
| paths); | |
| writeIncludes(paths, ""); | |
| return; | |
| } | |
| // Multi-config generator: evaluate the entry for every configuration. | |
| std::vector<std::string> results; | |
| results.reserve(configurationTypes.size()); | |
| bool allSame = true; | |
| for (std::string const& ct : configurationTypes) { | |
| results.emplace_back(cmGeneratorExpression::Evaluate(entry, this, ct)); | |
| if (results.back() != results.front()) { | |
| allSame = false; | |
| } | |
| } | |
| // A config-independent result collapses to unconditional includes, so it | |
| // still runs when ctest is invoked without -C. | |
| if (allSame) { | |
| std::vector<std::string> paths; | |
| cmExpandList(results.front(), paths); | |
| writeIncludes(paths, ""); | |
| return; | |
| } | |
| // Otherwise guard each per-config result to match the config guards used | |
| // for add_test(). A configuration whose result yields no include is | |
| // skipped so no empty guard is emitted. | |
| bool first = true; | |
| for (size_t i = 0; i < configurationTypes.size(); ++i) { | |
| std::vector<std::string> paths; | |
| cmExpandList(results[i], paths); | |
| if (paths.empty()) { | |
| continue; | |
| } | |
| fout << (first ? "if(" : "elseif(") | |
| << cmScriptGenerator::CreateConfigTest("CTEST_CONFIGURATION_TYPE", | |
| configurationTypes[i]) | |
| << ")\n"; | |
| writeIncludes(paths, " "); | |
| first = false; | |
| } | |
| if (!first) { | |
| fout << "endif()\n"; | |
| } | |
| }; | |
| cmValue testIncludeFile = this->Makefile->GetProperty("TEST_INCLUDE_FILE"); | |
| if (testIncludeFile) { | |
| writeTestIncludeFile(*testIncludeFile); | |
| } | |
| cmValue testIncludeFiles = this->Makefile->GetProperty("TEST_INCLUDE_FILES"); | |
| if (testIncludeFiles) { | |
| cmList includesList{ *testIncludeFiles }; | |
| for (std::string const& i : includesList) { | |
| writeTestIncludeFile(i); | |
| } | |
| } | |
| // Ask each test generator to write its code. | |
| for (auto const& tester : this->Makefile->GetTestGenerators()) { | |
| tester->Generate(fout, config, configurationTypes); | |
| } | |
| using vec_t = std::vector<cmStateSnapshot>; | |
| vec_t const& children = this->Makefile->GetStateSnapshot().GetChildren(); | |
| for (cmStateSnapshot const& i : children) { | |
| // TODO: Use add_subdirectory instead? | |
| std::string outP = i.GetDirectory().GetCurrentBinary(); | |
| outP = this->MaybeRelativeToCurBinDir(outP); | |
| fout << "subdirs(" << cmScriptGenerator::Quote(outP) << ")\n"; | |
| } | |
| // Add directory labels property | |
| cmValue directoryLabels = | |
| this->Makefile->GetDefinition("CMAKE_DIRECTORY_LABELS"); | |
| cmValue labels = this->Makefile->GetProperty("LABELS"); | |
| if (labels || directoryLabels) { | |
| fout << "set_directory_properties(PROPERTIES LABELS "; | |
| if (labels) { | |
| fout << cmScriptGenerator::Quote(*labels); | |
| } | |
| if (labels && directoryLabels) { | |
| fout << ";"; | |
| } | |
| if (directoryLabels) { | |
| fout << cmScriptGenerator::Quote(*directoryLabels); | |
| } | |
| fout << ")\n"; | |
| } | |
| } | |
| void cmLocalGenerator::CreateEvaluationFileOutputs() | |
| { | |
| std::vector<std::string> const& configs = | |
| this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); | |
| for (std::string const& c : configs) { | |
| this->CreateEvaluationFileOutputs(c); | |
| } | |
| } | |
| void cmLocalGenerator::CreateEvaluationFileOutputs(std::string const& config) | |
| { | |
| for (auto const& geef : this->Makefile->GetEvaluationFiles()) { | |
| geef->CreateOutputFile(this, config); | |
| } | |
| } | |
| void cmLocalGenerator::ProcessEvaluationFiles( | |
| std::vector<std::string>& generatedFiles) | |
| { | |
| for (auto const& geef : this->Makefile->GetEvaluationFiles()) { | |
| geef->Generate(this); | |
| if (cmSystemTools::GetFatalErrorOccurred()) { | |
| return; | |
| } | |
| std::vector<std::string> files = geef->GetFiles(); | |
| std::sort(files.begin(), files.end()); | |
| std::vector<std::string> intersection; | |
| std::set_intersection(files.begin(), files.end(), generatedFiles.begin(), | |
| generatedFiles.end(), | |
| std::back_inserter(intersection)); | |
| if (!intersection.empty()) { | |
| cmSystemTools::Error("Files to be generated by multiple different " | |
| "commands: " + | |
| cmWrap('"', intersection, '"', " ")); | |
| return; | |
| } | |
| cm::append(generatedFiles, files); | |
| std::inplace_merge(generatedFiles.begin(), | |
| generatedFiles.end() - files.size(), | |
| generatedFiles.end()); | |
| } | |
| } | |
| void cmLocalGenerator::GenerateInstallRules() | |
| { | |
| // Compute the install prefix. | |
| cmValue installPrefix = | |
| this->Makefile->GetDefinition("CMAKE_INSTALL_PREFIX"); | |
| std::string prefix = *installPrefix; | |
| #if defined(_WIN32) && !defined(__CYGWIN__) | |
| if (!installPrefix) { | |
| if (!cmSystemTools::GetEnv("SystemDrive", prefix)) { | |
| prefix = "C:"; | |
| } | |
| cmValue project_name = this->Makefile->GetDefinition("PROJECT_NAME"); | |
| if (cmNonempty(project_name)) { | |
| prefix += "/Program Files/"; | |
| prefix += *project_name; | |
| } else { | |
| prefix += "/InstalledCMakeProject"; | |
| } | |
| } | |
| #elif defined(__HAIKU__) | |
| char dir[B_PATH_NAME_LENGTH]; | |
| if (!installPrefix) { | |
| if (find_directory(B_SYSTEM_DIRECTORY, -1, false, dir, sizeof(dir)) == | |
| B_OK) { | |
| prefix = dir; | |
| } else { | |
| prefix = "/boot/system"; | |
| } | |
| } | |
| #else | |
| if (!installPrefix) { | |
| prefix = "/usr/local"; | |
| } | |
| #endif | |
| if (cmValue stagingPrefix = | |
| this->Makefile->GetDefinition("CMAKE_STAGING_PREFIX")) { | |
| prefix = *stagingPrefix; | |
| } | |
| // Compute the set of configurations. | |
| std::vector<std::string> configurationTypes = | |
| this->Makefile->GetGeneratorConfigs(cmMakefile::OnlyMultiConfig); | |
| std::string config = this->Makefile->GetDefaultConfiguration(); | |
| // Choose a default install configuration. | |
| std::string default_config = config; | |
| char const* default_order[] = { "RELEASE", "MINSIZEREL", "RELWITHDEBINFO", | |
| "DEBUG", nullptr }; | |
| for (char const** c = default_order; *c && default_config.empty(); ++c) { | |
| for (std::string const& configurationType : configurationTypes) { | |
| if (cmSystemTools::UpperCase(configurationType) == *c) { | |
| default_config = configurationType; | |
| } | |
| } | |
| } | |
| if (default_config.empty() && !configurationTypes.empty()) { | |
| default_config = configurationTypes[0]; | |
| } | |
| // Create the install script file. | |
| std::string file = this->StateSnapshot.GetDirectory().GetCurrentBinary(); | |
| std::string homedir = this->GetState()->GetBinaryDirectory(); | |
| int toplevel_install = 0; | |
| if (file == homedir) { | |
| toplevel_install = 1; | |
| } | |
| file += "/cmake_install.cmake"; | |
| this->GetGlobalGenerator()->AddInstallScript(file); | |
| cmGeneratedFileStream fout(file); | |
| // Write the header. | |
| /* clang-format off */ | |
| fout << "# Install script for directory: " | |
| << this->StateSnapshot.GetDirectory().GetCurrentSource() | |
| << "\n\n" | |
| "# Set the install prefix\n" | |
| "if(NOT DEFINED CMAKE_INSTALL_PREFIX)\n" | |
| " set(CMAKE_INSTALL_PREFIX \"" << prefix << "\")\n" | |
| "endif()\n" | |
| << R"(string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX )" | |
| << "\"${CMAKE_INSTALL_PREFIX}\")\n\n"; | |
| /* clang-format on */ | |
| // Write support code for generating per-configuration install rules. | |
| /* clang-format off */ | |
| fout << | |
| "# Set the install configuration name.\n" | |
| "if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME)\n" | |
| " if(BUILD_TYPE)\n" | |
| " string(REGEX REPLACE \"^[^A-Za-z0-9_]+\" \"\"\n" | |
| " CMAKE_INSTALL_CONFIG_NAME \"${BUILD_TYPE}\")\n" | |
| " else()\n" | |
| " set(CMAKE_INSTALL_CONFIG_NAME \"" << default_config << "\")\n" | |
| " endif()\n" | |
| " message(STATUS \"Install configuration: " | |
| "\\\"${CMAKE_INSTALL_CONFIG_NAME}\\\"\")\n" | |
| "endif()\n" | |
| "\n"; | |
| /* clang-format on */ | |
| // Write support code for dealing with component-specific installs. | |
| /* clang-format off */ | |
| fout << | |
| "# Set the component getting installed.\n" | |
| "if(NOT CMAKE_INSTALL_COMPONENT)\n" | |
| " if(COMPONENT)\n" | |
| " message(STATUS \"Install component: \\\"${COMPONENT}\\\"\")\n" | |
| " set(CMAKE_INSTALL_COMPONENT \"${COMPONENT}\")\n" | |
| " else()\n" | |
| " set(CMAKE_INSTALL_COMPONENT)\n" | |
| " endif()\n" | |
| "endif()\n" | |
| "\n"; | |
| /* clang-format on */ | |
| // Copy user-specified install options to the install code. | |
| if (cmValue so_no_exe = | |
| this->Makefile->GetDefinition("CMAKE_INSTALL_SO_NO_EXE")) { | |
| /* clang-format off */ | |
| fout << | |
| "# Install shared libraries without execute permission?\n" | |
| "if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE)\n" | |
| " set(CMAKE_INSTALL_SO_NO_EXE \"" << *so_no_exe << "\")\n" | |
| "endif()\n" | |
| "\n"; | |
| /* clang-format on */ | |
| } | |
| // Copy cmake cross compile state to install code. | |
| if (cmValue crosscompiling = | |
| this->Makefile->GetDefinition("CMAKE_CROSSCOMPILING")) { | |
| /* clang-format off */ | |
| fout << | |
| "# Is this installation the result of a crosscompile?\n" | |
| "if(NOT DEFINED CMAKE_CROSSCOMPILING)\n" | |
| " set(CMAKE_CROSSCOMPILING \"" << *crosscompiling << "\")\n" | |
| "endif()\n" | |
| "\n"; | |
| /* clang-format on */ | |
| } | |
| // Write default directory permissions. | |
| if (cmValue defaultDirPermissions = this->Makefile->GetDefinition( | |
| "CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS")) { | |
| /* clang-format off */ | |
| fout << | |
| "# Set default install directory permissions.\n" | |
| "if(NOT DEFINED CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS)\n" | |
| " set(CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS \"" | |
| << *defaultDirPermissions << "\")\n" | |
| "endif()\n" | |
| "\n"; | |
| /* clang-format on */ | |
| } | |
| // Write out CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM so that | |
| // installed code that uses `file(GET_RUNTIME_DEPENDENCIES)` | |
| // has same platform variable as when running cmake | |
| if (cmValue platform = this->Makefile->GetDefinition( | |
| "CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM")) { | |
| /* clang-format off */ | |
| fout << | |
| "# Set OS and executable format for runtime-dependencies.\n" | |
| "if(NOT DEFINED CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM)\n" | |
| " set(CMAKE_GET_RUNTIME_DEPENDENCIES_PLATFORM \"" | |
| << *platform << "\")\n" | |
| "endif()\n" | |
| "\n"; | |
| /* clang-format on */ | |
| } | |
| // Write out CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL so that | |
| // installed code that uses `file(GET_RUNTIME_DEPENDENCIES)` | |
| // has same tool selected as when running cmake | |
| if (cmValue command = | |
| this->Makefile->GetDefinition("CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL")) { | |
| /* clang-format off */ | |
| fout << | |
| "# Set tool for dependency-resolution of runtime-dependencies.\n" | |
| "if(NOT DEFINED CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL)\n" | |
| " set(CMAKE_GET_RUNTIME_DEPENDENCIES_TOOL \"" | |
| << *command << "\")\n" | |
| "endif()\n" | |
| "\n"; | |
| /* clang-format on */ | |
| } | |
| // Write out CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND so that | |
| // installed code that uses `file(GET_RUNTIME_DEPENDENCIES)` | |
| // has same path to the tool as when running cmake | |
| if (cmValue command = this->Makefile->GetDefinition( | |
| "CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND")) { | |
| /* clang-format off */ | |
| fout << | |
| "# Set path to tool for dependency-resolution of runtime-dependencies.\n" | |
| "if(NOT DEFINED CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND)\n" | |
| " set(CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND \"" | |
| << *command << "\")\n" | |
| "endif()\n" | |
| "\n"; | |
| /* clang-format on */ | |
| } | |
| // Write out CMAKE_OBJDUMP so that installed code that uses | |
| // `file(GET_RUNTIME_DEPENDENCIES)` and hasn't specified | |
| // CMAKE_GET_RUNTIME_DEPENDENCIES_COMMAND has consistent | |
| // logic to fallback to CMAKE_OBJDUMP when `objdump` is | |
| // not on the path | |
| if (cmValue command = this->Makefile->GetDefinition("CMAKE_OBJDUMP")) { | |
| /* clang-format off */ | |
| fout << | |
| "# Set path to fallback-tool for dependency-resolution.\n" | |
| "if(NOT DEFINED CMAKE_OBJDUMP)\n" | |
| " set(CMAKE_OBJDUMP \"" | |
| << *command << "\")\n" | |
| "endif()\n" | |
| "\n"; | |
| /* clang-format on */ | |
| } | |
| this->AddGeneratorSpecificInstallSetup(fout); | |
| // Ask each install generator to write its code. | |
| cmPolicies::PolicyStatus status = this->GetPolicyStatus(cmPolicies::CMP0082); | |
| auto const& installers = this->Makefile->GetInstallGenerators(); | |
| bool haveSubdirectoryInstall = false; | |
| bool haveInstallAfterSubdirectory = false; | |
| if (status == cmPolicies::WARN) { | |
| for (auto const& installer : installers) { | |
| installer->CheckCMP0082(haveSubdirectoryInstall, | |
| haveInstallAfterSubdirectory); | |
| installer->Generate(fout, config, configurationTypes); | |
| } | |
| } else { | |
| for (auto const& installer : installers) { | |
| installer->Generate(fout, config, configurationTypes); | |
| } | |
| } | |
| // Write rules from old-style specification stored in targets. | |
| this->GenerateTargetInstallRules(fout, config, configurationTypes); | |
| // Include install scripts from subdirectories. | |
| switch (status) { | |
| case cmPolicies::WARN: | |
| if (haveInstallAfterSubdirectory && | |
| this->Makefile->PolicyOptionalWarningEnabled( | |
| "CMAKE_POLICY_WARNING_CMP0082")) { | |
| this->IssuePolicyWarning(cmPolicies::CMP0082); | |
| } | |
| CM_FALLTHROUGH; | |
| case cmPolicies::OLD: { | |
| std::vector<cmStateSnapshot> children = | |
| this->Makefile->GetStateSnapshot().GetChildren(); | |
| if (!children.empty()) { | |
| fout << "if(NOT CMAKE_INSTALL_LOCAL_ONLY)\n"; | |
| fout << " # Include the install script for each subdirectory.\n"; | |
| for (cmStateSnapshot const& c : children) { | |
| if (!c.GetDirectory().GetPropertyAsBool("EXCLUDE_FROM_ALL")) { | |
| std::string odir = c.GetDirectory().GetCurrentBinary(); | |
| cmSystemTools::ConvertToUnixSlashes(odir); | |
| fout << " include(\"" << odir << "/cmake_install.cmake\")\n"; | |
| } | |
| } | |
| fout << "\n"; | |
| fout << "endif()\n\n"; | |
| } | |
| } break; | |
| case cmPolicies::NEW: | |
| // NEW behavior is handled in | |
| // cmInstallSubdirectoryGenerator::GenerateScript() | |
| break; | |
| } | |
| /* clang-format off */ | |
| fout << | |
| "string(REPLACE \";\" \"\\n\" CMAKE_INSTALL_MANIFEST_CONTENT\n" | |
| " \"${CMAKE_INSTALL_MANIFEST_FILES}\")\n" | |
| "if(CMAKE_INSTALL_LOCAL_ONLY)\n" | |
| " file(WRITE \"" << | |
| this->StateSnapshot.GetDirectory().GetCurrentBinary() << | |
| "/install_local_manifest.txt\"\n" | |
| " \"${CMAKE_INSTALL_MANIFEST_CONTENT}\")\n" | |
| "endif()\n"; | |
| if (toplevel_install) { | |
| fout << | |
| "if(CMAKE_INSTALL_COMPONENT)\n" | |
| " if(CMAKE_INSTALL_COMPONENT MATCHES \"^[a-zA-Z0-9_.+-]+$\")\n" | |
| " set(CMAKE_INSTALL_MANIFEST \"install_manifest_" | |
| "${CMAKE_INSTALL_COMPONENT}.txt\")\n" | |
| " else()\n" | |
| " string(MD5 CMAKE_INST_COMP_HASH \"${CMAKE_INSTALL_COMPONENT}\")\n" | |
| " set(CMAKE_INSTALL_MANIFEST \"install_manifest_" | |
| "${CMAKE_INST_COMP_HASH}.txt\")\n" | |
| " unset(CMAKE_INST_COMP_HASH)\n" | |
| " endif()\n" | |
| "else()\n" | |
| " set(CMAKE_INSTALL_MANIFEST \"install_manifest.txt\")\n" | |
| "endif()\n" | |
| "\n" | |
| "if(NOT CMAKE_INSTALL_LOCAL_ONLY)\n" | |
| " file(WRITE \"" << homedir << "/${CMAKE_INSTALL_MANIFEST}\"\n" | |
| " \"${CMAKE_INSTALL_MANIFEST_CONTENT}\")\n" | |
| "endif()\n"; | |
| } | |
| /* clang-format on */ | |
| } | |
| void cmLocalGenerator::AddGeneratorTarget( | |
| std::unique_ptr<cmGeneratorTarget> gt) | |
| { | |
| cmGeneratorTarget* gt_ptr = gt.get(); | |
| this->GeneratorTargets.push_back(std::move(gt)); | |
| this->GeneratorTargetSearchIndex.emplace(gt_ptr->GetName(), gt_ptr); | |
| this->GlobalGenerator->IndexGeneratorTarget(gt_ptr); | |
| } | |
| void cmLocalGenerator::AddImportedGeneratorTarget(cmGeneratorTarget* gt) | |
| { | |
| this->ImportedGeneratorTargets.emplace(gt->GetName(), gt); | |
| this->GlobalGenerator->IndexGeneratorTarget(gt); | |
| } | |
| void cmLocalGenerator::AddOwnedImportedGeneratorTarget( | |
| std::unique_ptr<cmGeneratorTarget> gt) | |
| { | |
| this->OwnedImportedGeneratorTargets.push_back(std::move(gt)); | |
| } | |
| cmGeneratorTarget* cmLocalGenerator::FindLocalNonAliasGeneratorTarget( | |
| std::string const& name) const | |
| { | |
| auto ti = this->GeneratorTargetSearchIndex.find(name); | |
| if (ti != this->GeneratorTargetSearchIndex.end()) { | |
| return ti->second; | |
| } | |
| return nullptr; | |
| } | |
| void cmLocalGenerator::AddGeneratorRule(std::unique_ptr<cmGeneratorRule> gr) | |
| { | |
| cmGeneratorRule* gr_ptr = gr.get(); | |
| this->GeneratorRules.push_back(std::move(gr)); | |
| this->GeneratorRuleSearchIndex.emplace(gr_ptr->GetName(), gr_ptr); | |
| this->GlobalGenerator->IndexGeneratorRule(gr_ptr); | |
| } | |
| cmGeneratorRule* cmLocalGenerator::FindGeneratorRuleToUse( | |
| std::string const& name) const | |
| { | |
| auto ri = this->GeneratorRuleSearchIndex.find(name); | |
| if (ri != this->GeneratorRuleSearchIndex.end()) { | |
| return ri->second; | |
| } | |
| return this->GetGlobalGenerator()->FindGeneratorRule(name); | |
| } | |
| void cmLocalGenerator::ComputeTargetManifest() | |
| { | |
| // Collect the set of configuration types. | |
| std::vector<std::string> configNames = | |
| this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); | |
| // Add our targets to the manifest for each configuration. | |
| auto const& targets = this->GetGeneratorTargets(); | |
| for (auto const& target : targets) { | |
| if (!target->IsInBuildSystem()) { | |
| continue; | |
| } | |
| for (std::string const& c : configNames) { | |
| target->ComputeTargetManifest(c); | |
| } | |
| } | |
| } | |
| bool cmLocalGenerator::ComputeTargetCompileFeatures() | |
| { | |
| // Collect the set of configuration types. | |
| std::vector<std::string> configNames = | |
| this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); | |
| using LanguagePair = std::pair<std::string, std::string>; | |
| std::vector<LanguagePair> pairedLanguages{ { "OBJC", "C" }, | |
| { "OBJCXX", "CXX" }, | |
| { "CUDA", "CXX" }, | |
| { "HIP", "CXX" }, | |
| { "SYCL", "CXX" } }; | |
| std::set<LanguagePair> inferredEnabledLanguages; | |
| for (auto const& lang : pairedLanguages) { | |
| if (this->Makefile->GetState()->GetLanguageEnabled(lang.first)) { | |
| inferredEnabledLanguages.insert(lang); | |
| } | |
| } | |
| // Process compile features of all targets. | |
| auto const& targets = this->GetGeneratorTargets(); | |
| for (auto const& target : targets) { | |
| for (std::string const& c : configNames) { | |
| if (!target->ComputeCompileFeatures(c)) { | |
| return false; | |
| } | |
| } | |
| // Now that C/C++ _STANDARD values have been computed | |
| // set the values to ObjC/ObjCXX _STANDARD variables | |
| if (target->CanCompileSources()) { | |
| for (std::string const& c : configNames) { | |
| target->ComputeCompileFeatures(c, inferredEnabledLanguages); | |
| } | |
| } | |
| } | |
| return true; | |
| } | |
| bool cmLocalGenerator::IsRootMakefile() const | |
| { | |
| return !this->StateSnapshot.GetBuildsystemDirectoryParent().IsValid(); | |
| } | |
| cmState* cmLocalGenerator::GetState() const | |
| { | |
| return this->GlobalGenerator->GetCMakeInstance()->GetState(); | |
| } | |
| cmStateSnapshot cmLocalGenerator::GetStateSnapshot() const | |
| { | |
| return this->Makefile->GetStateSnapshot(); | |
| } | |
| std::string cmLocalGenerator::GetRuleLauncher(cmGeneratorTarget* target, | |
| std::string const& prop, | |
| std::string const& config) | |
| { | |
| cmValue value = this->Makefile->GetProperty(prop); | |
| if (target) { | |
| value = target->GetProperty(prop); | |
| } | |
| if (value) { | |
| return cmGeneratorExpression::Evaluate(*value, this, config, target); | |
| } | |
| return ""; | |
| } | |
| std::string cmLocalGenerator::ConvertToIncludeReference( | |
| std::string const& path, OutputFormat format) | |
| { | |
| return this->ConvertToOutputForExisting(path, format); | |
| } | |
| std::string cmLocalGenerator::GetIncludeFlags( | |
| std::vector<std::string> const& includeDirs, cmGeneratorTarget* target, | |
| std::string const& lang, std::string const& config, bool forResponseFile) | |
| { | |
| if (lang.empty()) { | |
| return ""; | |
| } | |
| std::vector<std::string> includes = includeDirs; | |
| MoveSystemIncludesToEnd(includes, config, lang, target); | |
| OutputFormat shellFormat = forResponseFile ? RESPONSE : SHELL; | |
| std::ostringstream includeFlags; | |
| std::string const& includeFlag = | |
| this->Makefile->GetSafeDefinition(cmStrCat("CMAKE_INCLUDE_FLAG_", lang)); | |
| bool quotePaths = false; | |
| if (this->Makefile->GetDefinition("CMAKE_QUOTE_INCLUDE_PATHS")) { | |
| quotePaths = true; | |
| } | |
| std::string sep = " "; | |
| bool repeatFlag = true; | |
| // should the include flag be repeated like ie. -IA -IB | |
| if (cmValue incSep = this->Makefile->GetDefinition( | |
| cmStrCat("CMAKE_INCLUDE_FLAG_SEP_", lang))) { | |
| // if there is a separator then the flag is not repeated but is only | |
| // given once i.e. -classpath a:b:c | |
| sep = *incSep; | |
| repeatFlag = false; | |
| } | |
| // Support special system include flag if it is available and the | |
| // normal flag is repeated for each directory. | |
| cmValue sysIncludeFlag = nullptr; | |
| cmValue sysIncludeFlagWarning = nullptr; | |
| if (repeatFlag) { | |
| sysIncludeFlag = this->Makefile->GetDefinition( | |
| cmStrCat("CMAKE_INCLUDE_SYSTEM_FLAG_", lang)); | |
| sysIncludeFlagWarning = this->Makefile->GetDefinition( | |
| cmStrCat("CMAKE_INCLUDE_SYSTEM_FLAG_", lang, "_WARNING")); | |
| } | |
| cmValue fwSearchFlag = this->Makefile->GetDefinition( | |
| cmStrCat("CMAKE_", lang, "_FRAMEWORK_SEARCH_FLAG")); | |
| cmValue sysFwSearchFlag = this->Makefile->GetDefinition( | |
| cmStrCat("CMAKE_", lang, "_SYSTEM_FRAMEWORK_SEARCH_FLAG")); | |
| bool flagUsed = false; | |
| bool sysIncludeFlagUsed = false; | |
| std::set<std::string> emitted; | |
| #ifdef __APPLE__ | |
| emitted.insert("/System/Library/Frameworks"); | |
| #endif | |
| for (std::string const& i : includes) { | |
| if (cmNonempty(fwSearchFlag) && this->Makefile->IsOn("APPLE") && | |
| cmSystemTools::IsPathToFramework(i)) { | |
| std::string const frameworkDir = cmSystemTools::GetFilenamePath(i); | |
| if (emitted.insert(frameworkDir).second) { | |
| if (sysFwSearchFlag && target && | |
| target->IsSystemIncludeDirectory(frameworkDir, config, lang)) { | |
| includeFlags << *sysFwSearchFlag; | |
| } else { | |
| includeFlags << *fwSearchFlag; | |
| } | |
| includeFlags << this->ConvertToOutputFormat(frameworkDir, shellFormat) | |
| << " "; | |
| } | |
| continue; | |
| } | |
| if (!flagUsed || repeatFlag) { | |
| if (sysIncludeFlag && target && | |
| target->IsSystemIncludeDirectory(i, config, lang)) { | |
| includeFlags << *sysIncludeFlag; | |
| sysIncludeFlagUsed = true; | |
| } else { | |
| includeFlags << includeFlag; | |
| } | |
| flagUsed = true; | |
| } | |
| std::string includePath = this->ConvertToIncludeReference(i, shellFormat); | |
| if (quotePaths && !includePath.empty() && includePath.front() != '\"') { | |
| includeFlags << "\""; | |
| } | |
| includeFlags << includePath; | |
| if (quotePaths && !includePath.empty() && includePath.front() != '\"') { | |
| includeFlags << "\""; | |
| } | |
| includeFlags << sep; | |
| } | |
| if (sysIncludeFlagUsed && sysIncludeFlagWarning) { | |
| includeFlags << *sysIncludeFlagWarning; | |
| } | |
| std::string flags = includeFlags.str(); | |
| // remove trailing separators | |
| if ((sep[0] != ' ') && !flags.empty() && flags.back() == sep[0]) { | |
| flags.back() = ' '; | |
| } | |
| return cmTrimWhitespace(flags); | |
| } | |
| void cmLocalGenerator::AddCompileOptions(std::string& flags, | |
| cmGeneratorTarget* target, | |
| std::string const& lang, | |
| std::string const& config) | |
| { | |
| std::vector<BT<std::string>> tmpFlags; | |
| this->AddCompileOptions(tmpFlags, target, lang, config); | |
| this->AppendFlags(flags, tmpFlags); | |
| } | |
| void cmLocalGenerator::AddCompileOptions(std::vector<BT<std::string>>& flags, | |
| cmGeneratorTarget* target, | |
| std::string const& lang, | |
| std::string const& config) | |
| { | |
| if (lang == "SYCL") { | |
| cmValue separable = target->GetProperty("SYCL_SEPARABLE_COMPILATION"); | |
| char const* mode = separable.IsOn() ? "ON" : "OFF"; | |
| cmValue options = this->Makefile->GetDefinition( | |
| cmStrCat("CMAKE_SYCL_COMPILE_OPTIONS_SEPARABLE_COMPILATION_", mode)); | |
| if (!options && separable) { | |
| this->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("Target \"", target->GetName(), | |
| "\" requests SYCL_SEPARABLE_COMPILATION=", mode, | |
| ", but the SYCL compiler configuration does not support " | |
| "this mode.")); | |
| return; | |
| } | |
| std::string separableFlags; | |
| if (options) { | |
| this->AppendCompileOptions(separableFlags, *options); | |
| } | |
| if (!separableFlags.empty()) { | |
| flags.emplace_back(std::move(separableFlags)); | |
| } | |
| } | |
| std::string langFlagRegexVar = cmStrCat("CMAKE_", lang, "_FLAG_REGEX"); | |
| if (cmValue langFlagRegexStr = | |
| this->Makefile->GetDefinition(langFlagRegexVar)) { | |
| // Filter flags acceptable to this language. | |
| if (cmValue targetFlags = target->GetProperty("COMPILE_FLAGS")) { | |
| std::vector<std::string> opts; | |
| cmSystemTools::ParseWindowsCommandLine(targetFlags->c_str(), opts); | |
| // Re-escape these flags since COMPILE_FLAGS were already parsed | |
| // as a command line above. | |
| std::string compileOpts; | |
| this->AppendCompileOptions(compileOpts, opts, langFlagRegexStr->c_str()); | |
| if (!compileOpts.empty()) { | |
| flags.emplace_back(std::move(compileOpts)); | |
| } | |
| } | |
| std::vector<BT<std::string>> targetCompileOpts = | |
| target->GetCompileOptions(config, lang); | |
| // COMPILE_OPTIONS are escaped. | |
| this->AppendCompileOptions(flags, targetCompileOpts, | |
| langFlagRegexStr->c_str()); | |
| } else { | |
| // Use all flags. | |
| if (cmValue targetFlags = target->GetProperty("COMPILE_FLAGS")) { | |
| // COMPILE_FLAGS are not escaped for historical reasons. | |
| std::string compileFlags; | |
| this->AppendFlags(compileFlags, *targetFlags); | |
| if (!compileFlags.empty()) { | |
| flags.emplace_back(std::move(compileFlags)); | |
| } | |
| } | |
| std::vector<BT<std::string>> targetCompileOpts = | |
| target->GetCompileOptions(config, lang); | |
| // COMPILE_OPTIONS are escaped. | |
| this->AppendCompileOptions(flags, targetCompileOpts); | |
| } | |
| cmStandardLevelResolver standardResolver(this->Makefile); | |
| for (auto const& it : target->GetMaxLanguageStandards()) { | |
| cmValue standard = target->GetLanguageStandard(it.first, config); | |
| if (!standard) { | |
| continue; | |
| } | |
| if (standardResolver.IsLaterStandard(it.first, *standard, it.second)) { | |
| this->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("The COMPILE_FEATURES property of target \"", | |
| target->GetName(), | |
| "\" was evaluated when computing the link " | |
| "implementation, and the \"", | |
| cmStandardLevelResolver::GetStandardPropertyName(it.first), | |
| "\" was \"", it.second, | |
| "\" for that computation. Computing the " | |
| "COMPILE_FEATURES based on the link implementation resulted " | |
| "in a higher \"", | |
| cmStandardLevelResolver::GetStandardPropertyName(it.first), | |
| "\" \"", *standard, | |
| "\". " | |
| "This is not permitted. The COMPILE_FEATURES may not both " | |
| "depend on " | |
| "and be depended on by the link implementation.\n")); | |
| return; | |
| } | |
| } | |
| // Add Warning as errors flags | |
| if (!this->GetCMakeInstance()->GetIgnoreCompileWarningAsError()) { | |
| cmValue const wError = target->GetProperty("COMPILE_WARNING_AS_ERROR"); | |
| cmValue const wErrorOpts = this->Makefile->GetDefinition( | |
| cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_WARNING_AS_ERROR")); | |
| if (wError.IsOn() && wErrorOpts.IsSet()) { | |
| std::string wErrorFlags; | |
| this->AppendCompileOptions(wErrorFlags, *wErrorOpts); | |
| if (!wErrorFlags.empty()) { | |
| flags.emplace_back(std::move(wErrorFlags)); | |
| } | |
| } | |
| } | |
| // Add compile flag for the MSVC compiler only. | |
| cmMakefile* mf = this->GetMakefile(); | |
| if (cmValue jmc = | |
| mf->GetDefinition(cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_JMC"))) { | |
| // Handle Just My Code debugging flags, /JMC. | |
| // If the target is a Managed C++ one, /JMC is not compatible. | |
| if (target->GetManagedType(config) != | |
| cmGeneratorTarget::ManagedType::Managed) { | |
| // add /JMC flags if target property VS_JUST_MY_CODE_DEBUGGING is set | |
| // to ON | |
| if (cmValue jmcExprGen = | |
| target->GetProperty("VS_JUST_MY_CODE_DEBUGGING")) { | |
| std::string isJMCEnabled = | |
| cmGeneratorExpression::Evaluate(*jmcExprGen, this, config); | |
| if (cmIsOn(isJMCEnabled)) { | |
| cmList optList{ *jmc }; | |
| std::string jmcFlags; | |
| this->AppendCompileOptions(jmcFlags, optList); | |
| if (!jmcFlags.empty()) { | |
| flags.emplace_back(std::move(jmcFlags)); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| cmTarget* cmLocalGenerator::AddCustomCommandToTarget( | |
| std::string const& target, cmCustomCommandType type, | |
| std::unique_ptr<cmCustomCommand> cc, cmObjectLibraryCommands objLibCommands) | |
| { | |
| cmTarget* t = this->Makefile->GetCustomCommandTarget( | |
| target, objLibCommands, this->DirectoryBacktrace); | |
| if (!t) { | |
| return nullptr; | |
| } | |
| cc->SetBacktrace(this->DirectoryBacktrace); | |
| detail::AddCustomCommandToTarget(*this, cmCommandOrigin::Generator, t, type, | |
| std::move(cc)); | |
| return t; | |
| } | |
| cmSourceFile* cmLocalGenerator::AddCustomCommandToOutput( | |
| std::unique_ptr<cmCustomCommand> cc, bool replace) | |
| { | |
| // Make sure there is at least one output. | |
| if (cc->GetOutputs().empty()) { | |
| cmSystemTools::Error("Attempt to add a custom rule with no output!"); | |
| return nullptr; | |
| } | |
| cc->SetBacktrace(this->DirectoryBacktrace); | |
| return detail::AddCustomCommandToOutput(*this, cmCommandOrigin::Generator, | |
| std::move(cc), replace); | |
| } | |
| cmTarget* cmLocalGenerator::AddUtilityCommand( | |
| std::string const& utilityName, bool excludeFromAll, | |
| std::unique_ptr<cmCustomCommand> cc) | |
| { | |
| cmTarget* target = | |
| this->Makefile->AddNewUtilityTarget(utilityName, excludeFromAll); | |
| target->SetIsGeneratorProvided(true); | |
| if (cc->GetCommandLines().empty() && cc->GetDepends().empty()) { | |
| return target; | |
| } | |
| cc->SetBacktrace(this->DirectoryBacktrace); | |
| detail::AddUtilityCommand(*this, cmCommandOrigin::Generator, target, | |
| std::move(cc)); | |
| return target; | |
| } | |
| std::vector<BT<std::string>> cmLocalGenerator::GetIncludeDirectoriesImplicit( | |
| cmGeneratorTarget const* target, std::string const& lang, | |
| std::string const& config, bool stripImplicitDirs, | |
| bool appendAllImplicitDirs) const | |
| { | |
| std::vector<BT<std::string>> result; | |
| // Do not repeat an include path. | |
| std::set<std::string> emitted; | |
| auto emitDir = [&result, &emitted](std::string const& dir) { | |
| if (emitted.insert(dir).second) { | |
| result.emplace_back(dir); | |
| } | |
| }; | |
| auto emitBT = [&result, &emitted](BT<std::string> const& dir) { | |
| if (emitted.insert(dir.Value).second) { | |
| result.emplace_back(dir); | |
| } | |
| }; | |
| // When automatic include directories are requested for a build then | |
| // include the source and binary directories at the beginning of the | |
| // include path to approximate include file behavior for an | |
| // in-source build. This does not account for the case of a source | |
| // file in a subdirectory of the current source directory but we | |
| // cannot fix this because not all native build tools support | |
| // per-source-file include paths. | |
| if (this->Makefile->IsOn("CMAKE_INCLUDE_CURRENT_DIR")) { | |
| // Current binary directory | |
| emitDir(this->StateSnapshot.GetDirectory().GetCurrentBinary()); | |
| // Current source directory | |
| emitDir(this->StateSnapshot.GetDirectory().GetCurrentSource()); | |
| } | |
| if (!target) { | |
| return result; | |
| } | |
| // Standard include directories to be added unconditionally at the end. | |
| // These are intended to simulate additional implicit include directories. | |
| cmList userStandardDirs; | |
| { | |
| std::string const value = this->Makefile->GetSafeDefinition( | |
| cmStrCat("CMAKE_", lang, "_STANDARD_INCLUDE_DIRECTORIES")); | |
| userStandardDirs.assign(value); | |
| for (std::string& usd : userStandardDirs) { | |
| cmSystemTools::ConvertToUnixSlashes(usd); | |
| } | |
| } | |
| // Implicit include directories | |
| std::vector<std::string> implicitDirs; | |
| std::set<std::string> implicitSet; | |
| // Include directories to be excluded as if they were implicit. | |
| std::set<std::string> implicitExclude; | |
| { | |
| // Raw list of implicit include directories | |
| // Start with "standard" directories that we unconditionally add below. | |
| std::vector<std::string> impDirVec = userStandardDirs; | |
| // Load implicit include directories for this language. | |
| // We ignore this for Fortran because: | |
| // * There are no standard library headers to avoid overriding. | |
| // * Compilers like gfortran do not search their own implicit include | |
| // directories for modules ('.mod' files). | |
| if (lang != "Fortran") { | |
| size_t const impDirVecOldSize = impDirVec.size(); | |
| cmList::append(impDirVec, | |
| this->Makefile->GetDefinition(cmStrCat( | |
| "CMAKE_", lang, "_IMPLICIT_INCLUDE_DIRECTORIES"))); | |
| // FIXME: Use cmRange with 'advance()' when it supports non-const. | |
| for (size_t i = impDirVecOldSize; i < impDirVec.size(); ++i) { | |
| cmSystemTools::ConvertToUnixSlashes(impDirVec[i]); | |
| } | |
| // The CMAKE_<LANG>_IMPLICIT_INCLUDE_DIRECTORIES are computed using | |
| // try_compile in CMAKE_DETERMINE_COMPILER_ABI, but the implicit include | |
| // directories are not known during that try_compile. This can be a | |
| // problem when the HIP runtime include path is /usr/include because the | |
| // runtime include path is always added to the userDirs and the compiler | |
| // includes standard library headers via "__clang_hip_runtime_wrapper.h". | |
| if (lang == "HIP" && impDirVec.size() == impDirVecOldSize && | |
| !cm::contains(impDirVec, "/usr/include")) { | |
| implicitExclude.emplace("/usr/include"); | |
| } | |
| } | |
| // The Platform/UnixPaths module used to hard-code /usr/include for C, CXX, | |
| // and CUDA in CMAKE_<LANG>_IMPLICIT_INCLUDE_DIRECTORIES, but those | |
| // variables are now computed. On macOS the /usr/include directory is | |
| // inside the platform SDK so the computed value does not contain it | |
| // directly. In this case adding -I/usr/include can hide SDK headers so we | |
| // must still exclude it. | |
| if ((lang == "C" || lang == "CXX" || lang == "CUDA") && | |
| !cm::contains(impDirVec, "/usr/include") && | |
| std::find_if(impDirVec.begin(), impDirVec.end(), | |
| [](std::string const& d) { | |
| return cmHasLiteralSuffix(d, "/usr/include"); | |
| }) != impDirVec.end()) { | |
| // Only exclude this hard coded path for backwards compatibility. | |
| implicitExclude.emplace("/usr/include"); | |
| } | |
| for (std::string const& i : impDirVec) { | |
| if (implicitSet.insert(this->GlobalGenerator->GetRealPath(i)).second) { | |
| implicitDirs.emplace_back(i); | |
| } | |
| } | |
| } | |
| bool const isCorCxx = (lang == "C" || lang == "CXX" || lang == "SYCL"); | |
| // Resolve symlinks in CPATH for comparison with resolved include paths. | |
| // We do this here instead of when EnvCPATH is populated in case symlinks | |
| // on disk have changed in the meantime. | |
| std::set<std::string> resolvedEnvCPATH; | |
| if (isCorCxx) { | |
| for (std::string const& i : this->EnvCPATH) { | |
| resolvedEnvCPATH.emplace(this->GlobalGenerator->GetRealPath(i)); | |
| } | |
| } | |
| // Checks if this is not an excluded (implicit) include directory. | |
| auto notExcluded = [this, &implicitSet, &implicitExclude, &resolvedEnvCPATH, | |
| isCorCxx](std::string const& dir) -> bool { | |
| std::string const& real_dir = this->GlobalGenerator->GetRealPath(dir); | |
| return | |
| // Do not exclude directories that are not in any excluded set. | |
| !(cm::contains(implicitSet, real_dir) || | |
| cm::contains(implicitExclude, dir)) | |
| // Do not exclude entries of the CPATH environment variable even though | |
| // they are implicitly searched by the compiler. They are meant to be | |
| // user-specified directories that can be re-ordered or converted to | |
| // -isystem without breaking real compiler builtin headers. | |
| || (isCorCxx && cm::contains(resolvedEnvCPATH, real_dir)); | |
| }; | |
| // Get the target-specific include directories. | |
| std::vector<BT<std::string>> userDirs = | |
| target->GetIncludeDirectories(config, lang); | |
| // Support putting all the in-project include directories first if | |
| // it is requested by the project. | |
| if (this->Makefile->IsOn("CMAKE_INCLUDE_DIRECTORIES_PROJECT_BEFORE")) { | |
| std::string const& topSourceDir = this->GetState()->GetSourceDirectory(); | |
| std::string const& topBinaryDir = this->GetState()->GetBinaryDirectory(); | |
| for (BT<std::string> const& udr : userDirs) { | |
| // Emit this directory only if it is a subdirectory of the | |
| // top-level source or binary tree. | |
| if (cmSystemTools::ComparePath(udr.Value, topSourceDir) || | |
| cmSystemTools::ComparePath(udr.Value, topBinaryDir) || | |
| cmSystemTools::IsSubDirectory(udr.Value, topSourceDir) || | |
| cmSystemTools::IsSubDirectory(udr.Value, topBinaryDir)) { | |
| if (notExcluded(udr.Value)) { | |
| emitBT(udr); | |
| } | |
| } | |
| } | |
| } | |
| // Emit remaining non implicit user directories. | |
| for (BT<std::string> const& udr : userDirs) { | |
| if (notExcluded(udr.Value)) { | |
| emitBT(udr); | |
| } | |
| } | |
| // Sort result | |
| MoveSystemIncludesToEnd(result, config, lang, target); | |
| // Append standard include directories for this language. | |
| userDirs.reserve(userDirs.size() + userStandardDirs.size()); | |
| for (std::string& usd : userStandardDirs) { | |
| emitDir(usd); | |
| userDirs.emplace_back(std::move(usd)); | |
| } | |
| // Append compiler implicit include directories | |
| if (!stripImplicitDirs) { | |
| // Append implicit directories that were requested by the user only | |
| for (BT<std::string> const& udr : userDirs) { | |
| if (cm::contains(implicitSet, cmSystemTools::GetRealPath(udr.Value))) { | |
| emitBT(udr); | |
| } | |
| } | |
| // Append remaining implicit directories (on demand) | |
| if (appendAllImplicitDirs) { | |
| for (std::string& imd : implicitDirs) { | |
| emitDir(imd); | |
| } | |
| } | |
| } | |
| return result; | |
| } | |
| void cmLocalGenerator::GetIncludeDirectoriesImplicit( | |
| std::vector<std::string>& dirs, cmGeneratorTarget const* target, | |
| std::string const& lang, std::string const& config, bool stripImplicitDirs, | |
| bool appendAllImplicitDirs) const | |
| { | |
| std::vector<BT<std::string>> tmp = this->GetIncludeDirectoriesImplicit( | |
| target, lang, config, stripImplicitDirs, appendAllImplicitDirs); | |
| dirs.reserve(dirs.size() + tmp.size()); | |
| for (BT<std::string>& v : tmp) { | |
| dirs.emplace_back(std::move(v.Value)); | |
| } | |
| } | |
| std::vector<BT<std::string>> cmLocalGenerator::GetIncludeDirectories( | |
| cmGeneratorTarget const* target, std::string const& lang, | |
| std::string const& config) const | |
| { | |
| return this->GetIncludeDirectoriesImplicit(target, lang, config); | |
| } | |
| void cmLocalGenerator::GetIncludeDirectories(std::vector<std::string>& dirs, | |
| cmGeneratorTarget const* target, | |
| std::string const& lang, | |
| std::string const& config) const | |
| { | |
| this->GetIncludeDirectoriesImplicit(dirs, target, lang, config); | |
| } | |
| void cmLocalGenerator::GetStaticLibraryFlags(std::string& flags, | |
| std::string const& config, | |
| std::string const& linkLanguage, | |
| cmGeneratorTarget* target) | |
| { | |
| std::vector<BT<std::string>> tmpFlags = | |
| this->GetStaticLibraryFlags(config, linkLanguage, target); | |
| this->AppendFlags(flags, tmpFlags); | |
| } | |
| std::vector<BT<std::string>> cmLocalGenerator::GetStaticLibraryFlags( | |
| std::string const& config, std::string const& linkLanguage, | |
| cmGeneratorTarget* target) | |
| { | |
| std::vector<BT<std::string>> flags; | |
| if (linkLanguage != "Swift" && !this->IsSplitSwiftBuild()) { | |
| std::string staticLibFlags; | |
| this->AddConfigVariableFlags(staticLibFlags, "CMAKE_STATIC_LINKER_FLAGS", | |
| config); | |
| if (!staticLibFlags.empty()) { | |
| flags.emplace_back(std::move(staticLibFlags)); | |
| } | |
| } | |
| std::string staticLibFlags; | |
| std::string const configUpper = cmSystemTools::UpperCase(config); | |
| this->AppendFlags(staticLibFlags, | |
| target->GetSafeProperty("STATIC_LIBRARY_FLAGS")); | |
| if (!configUpper.empty()) { | |
| std::string name = "STATIC_LIBRARY_FLAGS_" + configUpper; | |
| this->AppendFlags(staticLibFlags, target->GetSafeProperty(name)); | |
| } | |
| if (!staticLibFlags.empty()) { | |
| flags.emplace_back(std::move(staticLibFlags)); | |
| } | |
| std::vector<BT<std::string>> staticLibOpts = | |
| target->GetStaticLibraryLinkOptions(config, linkLanguage); | |
| // STATIC_LIBRARY_OPTIONS are escaped. | |
| this->AppendCompileOptions(flags, staticLibOpts); | |
| return flags; | |
| } | |
| void cmLocalGenerator::GetDeviceLinkFlags( | |
| cmLinkLineDeviceComputer& linkLineComputer, std::string const& config, | |
| std::string& linkLibs, std::string& linkFlags, std::string& frameworkPath, | |
| std::string& linkPath, cmGeneratorTarget* target) | |
| { | |
| cmGeneratorTarget::DeviceLinkSetter setter(*target); | |
| cmComputeLinkInformation* pcli = target->GetLinkInformation(config); | |
| auto linklang = linkLineComputer.GetLinkerLanguage(target, config); | |
| auto ipoEnabled = target->IsIPOEnabled(linklang, config); | |
| if (!ipoEnabled && pcli) { | |
| ipoEnabled = linkLineComputer.ComputeRequiresDeviceLinkingIPOFlag(*pcli); | |
| } | |
| if (ipoEnabled) { | |
| if (cmValue cudaIPOFlags = this->Makefile->GetDefinition( | |
| cmStrCat("CMAKE_", linklang, "_DEVICE_LINK_OPTIONS_IPO"))) { | |
| linkFlags += *cudaIPOFlags; | |
| } | |
| } | |
| if (pcli) { | |
| // Compute the required device link libraries when | |
| // resolving gpu lang device symbols | |
| this->OutputLinkLibraries(pcli, &linkLineComputer, linkLibs, frameworkPath, | |
| linkPath); | |
| } | |
| this->AddVisibilityPresetFlags(linkFlags, target, linklang); | |
| this->GetGlobalGenerator()->EncodeLiteral(linkFlags); | |
| std::vector<std::string> linkOpts; | |
| target->GetLinkOptions(linkOpts, config, linklang); | |
| this->SetLinkScriptShell(this->GetGlobalGenerator()->GetUseLinkScript()); | |
| // LINK_OPTIONS are escaped. | |
| this->AppendCompileOptions(linkFlags, linkOpts); | |
| this->SetLinkScriptShell(false); | |
| } | |
| void cmLocalGenerator::GetTargetFlags( | |
| cmLinkLineComputer* linkLineComputer, std::string const& config, | |
| std::string& linkLibs, std::string& flags, std::string& linkFlags, | |
| std::string& frameworkPath, std::string& linkPath, cmGeneratorTarget* target) | |
| { | |
| std::vector<BT<std::string>> linkFlagsList; | |
| std::vector<BT<std::string>> linkPathList; | |
| std::vector<BT<std::string>> linkLibsList; | |
| this->GetTargetFlags(linkLineComputer, config, linkLibsList, flags, | |
| linkFlagsList, frameworkPath, linkPathList, target); | |
| this->AppendFlags(linkFlags, linkFlagsList); | |
| this->AppendFlags(linkPath, linkPathList); | |
| this->AppendFlags(linkLibs, linkLibsList); | |
| } | |
| void cmLocalGenerator::GetTargetFlags( | |
| cmLinkLineComputer* linkLineComputer, std::string const& config, | |
| std::vector<BT<std::string>>& linkLibs, std::string& flags, | |
| std::vector<BT<std::string>>& linkFlags, std::string& frameworkPath, | |
| std::vector<BT<std::string>>& linkPath, cmGeneratorTarget* target) | |
| { | |
| std::string const configUpper = cmSystemTools::UpperCase(config); | |
| cmComputeLinkInformation* pcli = target->GetLinkInformation(config); | |
| std::string const linkLanguage = | |
| linkLineComputer->GetLinkerLanguage(target, config); | |
| { | |
| std::string createFlags; | |
| this->AppendTargetCreationLinkFlags(createFlags, target, linkLanguage); | |
| if (!createFlags.empty()) { | |
| linkFlags.emplace_back(std::move(createFlags)); | |
| } | |
| } | |
| switch (target->GetType()) { | |
| case cm::TargetType::STATIC_LIBRARY: | |
| linkFlags = this->GetStaticLibraryFlags(config, linkLanguage, target); | |
| break; | |
| case cm::TargetType::MODULE_LIBRARY: | |
| CM_FALLTHROUGH; | |
| case cm::TargetType::SHARED_LIBRARY: { | |
| if (this->IsSplitSwiftBuild() || linkLanguage != "Swift") { | |
| std::string libFlags; | |
| this->AddTargetTypeLinkerFlags(libFlags, target, linkLanguage, config); | |
| if (!libFlags.empty()) { | |
| linkFlags.emplace_back(std::move(libFlags)); | |
| } | |
| } | |
| std::string langLinkFlags; | |
| this->AddPerLanguageLinkFlags(langLinkFlags, target, linkLanguage, | |
| config); | |
| if (!langLinkFlags.empty()) { | |
| linkFlags.emplace_back(std::move(langLinkFlags)); | |
| } | |
| std::string sharedLibFlags; | |
| this->AddTargetPropertyLinkFlags(sharedLibFlags, target, config); | |
| if (!sharedLibFlags.empty()) { | |
| this->GetGlobalGenerator()->EncodeLiteral(sharedLibFlags); | |
| linkFlags.emplace_back(std::move(sharedLibFlags)); | |
| } | |
| std::vector<BT<std::string>> linkOpts = | |
| target->GetLinkOptions(config, linkLanguage); | |
| this->SetLinkScriptShell(this->GetGlobalGenerator()->GetUseLinkScript()); | |
| // LINK_OPTIONS are escaped. | |
| this->AppendCompileOptions(linkFlags, linkOpts); | |
| this->SetLinkScriptShell(false); | |
| if (pcli) { | |
| this->OutputLinkLibraries(pcli, linkLineComputer, linkLibs, | |
| frameworkPath, linkPath); | |
| } | |
| } break; | |
| case cm::TargetType::EXECUTABLE: { | |
| if (linkLanguage != "Swift" || | |
| (this->IsSplitSwiftBuild() && | |
| target->GetPolicyStatusCMP0214() == cmPolicies::NEW)) { | |
| std::string exeFlags; | |
| this->AddTargetTypeLinkerFlags(exeFlags, target, linkLanguage, config); | |
| if (!exeFlags.empty()) { | |
| linkFlags.emplace_back(std::move(exeFlags)); | |
| } | |
| } | |
| std::string langLinkFlags; | |
| this->AddPerLanguageLinkFlags(langLinkFlags, target, linkLanguage, | |
| config); | |
| if (!langLinkFlags.empty()) { | |
| linkFlags.emplace_back(std::move(langLinkFlags)); | |
| } | |
| { | |
| auto exeType = cmStrCat( | |
| "CMAKE_", linkLanguage, "_CREATE_", | |
| (target->IsWin32Executable(config) ? "WIN32" : "CONSOLE"), "_EXE"); | |
| std::string exeFlags; | |
| this->AppendFlags(exeFlags, this->Makefile->GetDefinition(exeType), | |
| exeType, target, cmBuildStep::Link, linkLanguage); | |
| if (!exeFlags.empty()) { | |
| linkFlags.emplace_back(std::move(exeFlags)); | |
| } | |
| } | |
| std::string exeFlags; | |
| if (target->IsExecutableWithExports()) { | |
| exeFlags += this->Makefile->GetSafeDefinition( | |
| cmStrCat("CMAKE_EXE_EXPORTS_", linkLanguage, "_FLAG")); | |
| exeFlags += " "; | |
| } | |
| this->AddLanguageFlagsForLinking(flags, target, linkLanguage, config); | |
| if (pcli) { | |
| this->OutputLinkLibraries(pcli, linkLineComputer, linkLibs, | |
| frameworkPath, linkPath); | |
| } | |
| if (this->Makefile->IsOn("BUILD_SHARED_LIBS")) { | |
| std::string sFlagVar = | |
| cmStrCat("CMAKE_SHARED_BUILD_", linkLanguage, "_FLAGS"); | |
| exeFlags += this->Makefile->GetSafeDefinition(sFlagVar); | |
| exeFlags += " "; | |
| } | |
| std::string exeExportFlags = | |
| this->GetExeExportFlags(linkLanguage, *target); | |
| if (!exeExportFlags.empty()) { | |
| exeFlags += exeExportFlags; | |
| exeFlags += " "; | |
| } | |
| this->AddTargetPropertyLinkFlags(exeFlags, target, config); | |
| if (!exeFlags.empty()) { | |
| this->GetGlobalGenerator()->EncodeLiteral(exeFlags); | |
| linkFlags.emplace_back(std::move(exeFlags)); | |
| } | |
| std::vector<BT<std::string>> linkOpts = | |
| target->GetLinkOptions(config, linkLanguage); | |
| this->SetLinkScriptShell(this->GetGlobalGenerator()->GetUseLinkScript()); | |
| // LINK_OPTIONS are escaped. | |
| this->AppendCompileOptions(linkFlags, linkOpts); | |
| this->SetLinkScriptShell(false); | |
| } break; | |
| default: | |
| break; | |
| } | |
| std::string extraLinkFlags; | |
| this->AppendLinkerTypeFlags(extraLinkFlags, target, config, linkLanguage); | |
| this->AppendPositionIndependentLinkerFlags(extraLinkFlags, target, config, | |
| linkLanguage); | |
| this->AppendWarningAsErrorLinkerFlags(extraLinkFlags, target, linkLanguage); | |
| this->AppendIPOLinkerFlags(extraLinkFlags, target, config, linkLanguage); | |
| this->AppendModuleDefinitionFlag(extraLinkFlags, target, linkLineComputer, | |
| config, linkLanguage); | |
| if (!extraLinkFlags.empty()) { | |
| this->GetGlobalGenerator()->EncodeLiteral(extraLinkFlags); | |
| linkFlags.emplace_back(std::move(extraLinkFlags)); | |
| } | |
| } | |
| void cmLocalGenerator::GetTargetCompileFlags(cmGeneratorTarget* target, | |
| std::string const& config, | |
| std::string const& lang, | |
| std::string& flags, | |
| std::string const& arch) | |
| { | |
| std::vector<BT<std::string>> tmpFlags = | |
| this->GetTargetCompileFlags(target, config, lang, arch); | |
| this->AppendFlags(flags, tmpFlags); | |
| } | |
| std::vector<BT<std::string>> cmLocalGenerator::GetTargetCompileFlags( | |
| cmGeneratorTarget* target, std::string const& config, | |
| std::string const& lang, std::string const& arch) | |
| { | |
| std::vector<BT<std::string>> flags; | |
| std::string compileFlags; | |
| cmMakefile* mf = this->GetMakefile(); | |
| // Add language-specific flags. | |
| this->AddLanguageFlags(compileFlags, target, cmBuildStep::Compile, lang, | |
| config); | |
| if (target->IsIPOEnabled(lang, config)) { | |
| this->AppendFeatureOptions(compileFlags, lang, "IPO"); | |
| } | |
| this->AddArchitectureFlags(compileFlags, target, lang, config, arch); | |
| if (lang == "Fortran") { | |
| this->AppendFlags(compileFlags, | |
| this->GetTargetFortranFlags(target, config)); | |
| } else if (lang == "Swift") { | |
| // Only set the compile mode if CMP0157 is set | |
| if (cm::optional<cmSwiftCompileMode> swiftCompileMode = | |
| this->GetSwiftCompileMode(target, config)) { | |
| std::string swiftCompileModeFlag; | |
| switch (*swiftCompileMode) { | |
| case cmSwiftCompileMode::Incremental: { | |
| swiftCompileModeFlag = "-incremental"; | |
| if (cmValue flag = | |
| mf->GetDefinition("CMAKE_Swift_COMPILE_OPTIONS_INCREMENTAL")) { | |
| swiftCompileModeFlag = *flag; | |
| } | |
| break; | |
| } | |
| case cmSwiftCompileMode::Wholemodule: { | |
| swiftCompileModeFlag = "-wmo"; | |
| if (cmValue flag = | |
| mf->GetDefinition("CMAKE_Swift_COMPILE_OPTIONS_WMO")) { | |
| swiftCompileModeFlag = *flag; | |
| } | |
| break; | |
| } | |
| case cmSwiftCompileMode::Singlefile: | |
| break; | |
| case cmSwiftCompileMode::Unknown: { | |
| this->IssueDiagnostic( | |
| cmDiagnostics::CMD_AUTHOR, | |
| cmStrCat("Unknown Swift_COMPILATION_MODE on target '", | |
| target->GetName(), '\'')); | |
| } | |
| } | |
| this->AppendFlags(compileFlags, swiftCompileModeFlag); | |
| } | |
| } | |
| this->AddFeatureFlags(compileFlags, target, lang, config); | |
| this->AddVisibilityPresetFlags(compileFlags, target, lang); | |
| this->AddColorDiagnosticsFlags(compileFlags, lang); | |
| this->AppendFlags(compileFlags, mf->GetDefineFlags()); | |
| this->AppendFlags(compileFlags, | |
| this->GetFrameworkFlags(lang, config, target)); | |
| this->AppendFlags(compileFlags, | |
| this->GetXcFrameworkFlags(lang, config, target)); | |
| if (!compileFlags.empty()) { | |
| flags.emplace_back(std::move(compileFlags)); | |
| } | |
| this->AddCompileOptions(flags, target, lang, config); | |
| return flags; | |
| } | |
| std::string cmLocalGenerator::GetFrameworkFlags(std::string const& lang, | |
| std::string const& config, | |
| cmGeneratorTarget* target) | |
| { | |
| cmLocalGenerator* lg = target->GetLocalGenerator(); | |
| cmMakefile* mf = lg->GetMakefile(); | |
| if (!target->IsApple()) { | |
| return std::string(); | |
| } | |
| cmValue fwSearchFlag = | |
| mf->GetDefinition(cmStrCat("CMAKE_", lang, "_FRAMEWORK_SEARCH_FLAG")); | |
| cmValue sysFwSearchFlag = mf->GetDefinition( | |
| cmStrCat("CMAKE_", lang, "_SYSTEM_FRAMEWORK_SEARCH_FLAG")); | |
| if (!fwSearchFlag && !sysFwSearchFlag) { | |
| return std::string{}; | |
| } | |
| std::set<std::string> emitted; | |
| #ifdef __APPLE__ /* don't insert this when crosscompiling e.g. to iphone */ | |
| emitted.insert("/System/Library/Frameworks"); | |
| #endif | |
| std::vector<std::string> includes; | |
| lg->GetIncludeDirectories(includes, target, "C", config); | |
| // check all include directories for frameworks as this | |
| // will already have added a -F for the framework | |
| for (std::string const& include : includes) { | |
| if (lg->GetGlobalGenerator()->NameResolvesToFramework(include)) { | |
| std::string frameworkDir = cmStrCat(include, "/../"); | |
| frameworkDir = cmSystemTools::CollapseFullPath(frameworkDir); | |
| emitted.insert(frameworkDir); | |
| } | |
| } | |
| std::string flags; | |
| if (cmComputeLinkInformation* cli = target->GetLinkInformation(config)) { | |
| std::vector<std::string> const& frameworks = cli->GetFrameworkPaths(); | |
| for (std::string const& framework : frameworks) { | |
| if (emitted.insert(framework).second) { | |
| if (sysFwSearchFlag && | |
| target->IsSystemIncludeDirectory(framework, config, lang)) { | |
| flags += *sysFwSearchFlag; | |
| } else { | |
| flags += *fwSearchFlag; | |
| } | |
| flags += | |
| lg->ConvertToOutputFormat(framework, cmOutputConverter::SHELL); | |
| flags += " "; | |
| } | |
| } | |
| } | |
| return flags; | |
| } | |
| std::string cmLocalGenerator::GetXcFrameworkFlags(std::string const& lang, | |
| std::string const& config, | |
| cmGeneratorTarget* target) | |
| { | |
| cmLocalGenerator* lg = target->GetLocalGenerator(); | |
| cmMakefile* mf = lg->GetMakefile(); | |
| if (!target->IsApple()) { | |
| return std::string(); | |
| } | |
| cmValue includeSearchFlag = | |
| mf->GetDefinition(cmStrCat("CMAKE_INCLUDE_FLAG_", lang)); | |
| cmValue sysIncludeSearchFlag = | |
| mf->GetDefinition(cmStrCat("CMAKE_INCLUDE_SYSTEM_FLAG_", lang)); | |
| if (!includeSearchFlag && !sysIncludeSearchFlag) { | |
| return std::string{}; | |
| } | |
| std::string flags; | |
| if (cmComputeLinkInformation* cli = target->GetLinkInformation(config)) { | |
| std::vector<std::string> const& paths = cli->GetXcFrameworkHeaderPaths(); | |
| for (std::string const& path : paths) { | |
| if (sysIncludeSearchFlag && | |
| target->IsSystemIncludeDirectory(path, config, lang)) { | |
| flags += *sysIncludeSearchFlag; | |
| } else { | |
| flags += *includeSearchFlag; | |
| } | |
| flags += lg->ConvertToOutputFormat(path, cmOutputConverter::SHELL); | |
| flags += " "; | |
| } | |
| } | |
| return flags; | |
| } | |
| void cmLocalGenerator::GetTargetDefines(cmGeneratorTarget const* target, | |
| std::string const& config, | |
| std::string const& lang, | |
| std::set<std::string>& defines) const | |
| { | |
| std::set<BT<std::string>> tmp = this->GetTargetDefines(target, config, lang); | |
| for (BT<std::string> const& v : tmp) { | |
| defines.emplace(v.Value); | |
| } | |
| } | |
| std::set<BT<std::string>> cmLocalGenerator::GetTargetDefines( | |
| cmGeneratorTarget const* target, std::string const& config, | |
| std::string const& lang) const | |
| { | |
| std::set<BT<std::string>> defines; | |
| // Add the export symbol definition for shared library objects. | |
| if (std::string const* exportMacro = target->GetExportMacro()) { | |
| this->AppendDefines(defines, *exportMacro); | |
| } | |
| for (auto const& sharedLibCompileDef : | |
| target->GetSharedLibraryCompileDefs(config)) { | |
| this->AppendDefines(defines, sharedLibCompileDef); | |
| } | |
| // Add preprocessor definitions for this target and configuration. | |
| std::vector<BT<std::string>> targetDefines = | |
| target->GetCompileDefinitions(config, lang); | |
| this->AppendDefines(defines, targetDefines); | |
| return defines; | |
| } | |
| std::string cmLocalGenerator::GetTargetFortranFlags( | |
| cmGeneratorTarget const* /*unused*/, std::string const& /*unused*/) | |
| { | |
| // Implemented by specific generators that override this. | |
| return std::string(); | |
| } | |
| /** | |
| * Output the linking rules on a command line. For executables, | |
| * targetLibrary should be a NULL pointer. For libraries, it should point | |
| * to the name of the library. This will not link a library against itself. | |
| */ | |
| void cmLocalGenerator::OutputLinkLibraries( | |
| cmComputeLinkInformation* pcli, cmLinkLineComputer* linkLineComputer, | |
| std::string& linkLibraries, std::string& frameworkPath, | |
| std::string& linkPath) | |
| { | |
| std::vector<BT<std::string>> linkLibrariesList; | |
| std::vector<BT<std::string>> linkPathList; | |
| this->OutputLinkLibraries(pcli, linkLineComputer, linkLibrariesList, | |
| frameworkPath, linkPathList); | |
| pcli->AppendValues(linkLibraries, linkLibrariesList); | |
| pcli->AppendValues(linkPath, linkPathList); | |
| } | |
| void cmLocalGenerator::OutputLinkLibraries( | |
| cmComputeLinkInformation* pcli, cmLinkLineComputer* linkLineComputer, | |
| std::vector<BT<std::string>>& linkLibraries, std::string& frameworkPath, | |
| std::vector<BT<std::string>>& linkPath) | |
| { | |
| cmComputeLinkInformation& cli = *pcli; | |
| std::string linkLanguage = cli.GetLinkLanguage(); | |
| std::string libPathFlag; | |
| if (cmValue value = this->Makefile->GetDefinition( | |
| cmStrCat("CMAKE_", cli.GetLinkLanguage(), "_LIBRARY_PATH_FLAG"))) { | |
| libPathFlag = *value; | |
| } else { | |
| libPathFlag = | |
| this->Makefile->GetRequiredDefinition("CMAKE_LIBRARY_PATH_FLAG"); | |
| } | |
| std::string libPathTerminator; | |
| if (cmValue value = this->Makefile->GetDefinition(cmStrCat( | |
| "CMAKE_", cli.GetLinkLanguage(), "_LIBRARY_PATH_TERMINATOR"))) { | |
| libPathTerminator = *value; | |
| } else { | |
| libPathTerminator = | |
| this->Makefile->GetRequiredDefinition("CMAKE_LIBRARY_PATH_TERMINATOR"); | |
| } | |
| // Add standard link directories for this language | |
| std::string stdLinkDirString = this->Makefile->GetSafeDefinition( | |
| cmStrCat("CMAKE_", cli.GetLinkLanguage(), "_STANDARD_LINK_DIRECTORIES")); | |
| // Add standard libraries for this language. | |
| std::string stdLibString = this->Makefile->GetSafeDefinition( | |
| cmStrCat("CMAKE_", cli.GetLinkLanguage(), "_STANDARD_LIBRARIES")); | |
| // Append the framework search path flags. | |
| cmValue fwSearchFlag = this->Makefile->GetDefinition( | |
| cmStrCat("CMAKE_", linkLanguage, "_FRAMEWORK_SEARCH_FLAG")); | |
| frameworkPath = linkLineComputer->ComputeFrameworkPath(cli, fwSearchFlag); | |
| linkLineComputer->ComputeLinkPath(cli, libPathFlag, libPathTerminator, | |
| stdLinkDirString, linkPath); | |
| linkLineComputer->ComputeLinkLibraries(cli, stdLibString, linkLibraries); | |
| } | |
| std::string cmLocalGenerator::GetExeExportFlags( | |
| std::string const& linkLanguage, cmGeneratorTarget& tgt) const | |
| { | |
| std::string linkFlags; | |
| // Flags to export symbols from an executable. | |
| if (tgt.GetType() == cm::TargetType::EXECUTABLE && | |
| this->StateSnapshot.GetState()->GetGlobalPropertyAsBool( | |
| "TARGET_SUPPORTS_SHARED_LIBS")) { | |
| // Only add the flags if ENABLE_EXPORTS is on, | |
| // except on AIX where we compute symbol exports. | |
| if (!tgt.IsAIX() && tgt.GetPropertyAsBool("ENABLE_EXPORTS")) { | |
| linkFlags = this->Makefile->GetSafeDefinition( | |
| cmStrCat("CMAKE_SHARED_LIBRARY_LINK_", linkLanguage, "_FLAGS")); | |
| } | |
| } | |
| return linkFlags; | |
| } | |
| bool cmLocalGenerator::AllAppleArchSysrootsAreTheSame( | |
| std::vector<std::string> const& archs, cmValue sysroot) | |
| { | |
| if (!sysroot) { | |
| return false; | |
| } | |
| return std::all_of(archs.begin(), archs.end(), | |
| [this, sysroot](std::string const& arch) -> bool { | |
| std::string const& archSysroot = | |
| this->AppleArchSysroots[arch]; | |
| return cmIsOff(archSysroot) || *sysroot == archSysroot; | |
| }); | |
| } | |
| void cmLocalGenerator::AddArchitectureFlags(std::string& flags, | |
| cmGeneratorTarget const* target, | |
| std::string const& lang, | |
| std::string const& config, | |
| std::string const& filterArch) | |
| { | |
| // Only add Apple specific flags on Apple platforms | |
| if (target->IsApple() && this->EmitUniversalBinaryFlags) { | |
| std::vector<std::string> archs = target->GetAppleArchs(config, lang); | |
| if (!archs.empty() && | |
| (lang == "C" || lang == "CXX" || lang == "OBJC" || lang == "OBJCXX" || | |
| lang == "ASM" || lang == "SYCL")) { | |
| for (std::string const& arch : archs) { | |
| if (filterArch.empty() || filterArch == arch) { | |
| flags += " -arch "; | |
| flags += arch; | |
| } | |
| } | |
| } | |
| cmValue sysroot = this->Makefile->GetDefinition("CMAKE_OSX_SYSROOT"); | |
| if (sysroot.IsEmpty() && | |
| this->Makefile->IsOn( | |
| cmStrCat("CMAKE_", lang, "_COMPILER_APPLE_SYSROOT_REQUIRED"))) { | |
| sysroot = this->Makefile->GetDefinition("_CMAKE_OSX_SYSROOT_PATH"); | |
| } | |
| if (sysroot && *sysroot == "/") { | |
| sysroot = nullptr; | |
| } | |
| std::string sysrootFlagVar = cmStrCat("CMAKE_", lang, "_SYSROOT_FLAG"); | |
| cmValue sysrootFlag = this->Makefile->GetDefinition(sysrootFlagVar); | |
| if (cmNonempty(sysrootFlag)) { | |
| if (!this->AppleArchSysroots.empty() && | |
| !this->AllAppleArchSysrootsAreTheSame(archs, sysroot)) { | |
| for (std::string const& arch : archs) { | |
| std::string const& archSysroot = this->AppleArchSysroots[arch]; | |
| if (cmIsOff(archSysroot)) { | |
| continue; | |
| } | |
| if (filterArch.empty() || filterArch == arch) { | |
| flags = cmStrCat(std::move(flags), " -Xarch_", arch, ' '); | |
| // Combine sysroot flag and path to work with -Xarch | |
| std::string arch_sysroot = *sysrootFlag + archSysroot; | |
| flags += this->ConvertToOutputFormat(arch_sysroot, SHELL); | |
| } | |
| } | |
| } else if (cmNonempty(sysroot)) { | |
| flags += " "; | |
| flags += *sysrootFlag; | |
| flags += " "; | |
| flags += this->ConvertToOutputFormat(*sysroot, SHELL); | |
| } | |
| } | |
| cmValue deploymentTarget = | |
| this->Makefile->GetDefinition("CMAKE_OSX_DEPLOYMENT_TARGET"); | |
| if (cmNonempty(deploymentTarget)) { | |
| std::string deploymentTargetFlagVar = | |
| cmStrCat("CMAKE_", lang, "_OSX_DEPLOYMENT_TARGET_FLAG"); | |
| cmValue deploymentTargetFlag = | |
| this->Makefile->GetDefinition(deploymentTargetFlagVar); | |
| if (cmNonempty(deploymentTargetFlag) && | |
| // CMAKE_<LANG>_COMPILER_TARGET overrides a --target= for | |
| // CMAKE_OSX_DEPLOYMENT_TARGET, e.g., for visionOS. | |
| (!cmHasLiteralPrefix(*deploymentTarget, "--target=") || | |
| this->Makefile | |
| ->GetDefinition(cmStrCat("CMAKE_", lang, "_COMPILER_TARGET")) | |
| .IsEmpty())) { | |
| std::string flag = *deploymentTargetFlag; | |
| // Add the deployment target architecture to the flag, if needed. | |
| static std::string const kARCH = "<ARCH>"; | |
| std::string::size_type archPos = flag.find(kARCH); | |
| if (archPos != std::string::npos) { | |
| // This placeholder is meant for visionOS, so default to arm64 | |
| // unless only non-arm64 archs are given. | |
| std::string const arch = | |
| (archs.empty() || cm::contains(archs, "arm64")) ? "arm64" | |
| : archs[0]; | |
| // Replace the placeholder with its value. | |
| flag = cmStrCat(flag.substr(0, archPos), arch, | |
| flag.substr(archPos + kARCH.size())); | |
| } | |
| // Add the deployment target version to the flag. | |
| static std::string const kVERSION_MIN = "<VERSION_MIN>"; | |
| std::string::size_type verPos = flag.find(kVERSION_MIN); | |
| if (verPos != std::string::npos) { | |
| // Replace the placeholder with its value. | |
| flag = cmStrCat(flag.substr(0, verPos), *deploymentTarget, | |
| flag.substr(verPos + kVERSION_MIN.size())); | |
| } else { | |
| // There is no placeholder, so append the value. | |
| flag = cmStrCat(std::move(flag), *deploymentTarget); | |
| } | |
| flags += " "; | |
| flags += flag; | |
| } | |
| } | |
| } | |
| } | |
| void cmLocalGenerator::AddLanguageFlags(std::string& flags, | |
| cmGeneratorTarget const* target, | |
| cmBuildStep compileOrLink, | |
| std::string const& lang, | |
| std::string const& config) | |
| { | |
| if (target->IsSYCLAppendMode(lang)) { | |
| this->AddConfigVariableFlags(flags, "CMAKE_CXX_FLAGS", config); | |
| // Defaults initialized from the same source need only be applied once, | |
| // otherwise retain both strings verbatim. | |
| auto const appendSYCLFlags = [this, &flags](std::string const& suffix) { | |
| auto const& syclFlags = | |
| this->Makefile->GetDefinition(cmStrCat("CMAKE_SYCL_FLAGS", suffix)); | |
| if (syclFlags != | |
| this->Makefile->GetDefinition(cmStrCat("CMAKE_CXX_FLAGS", suffix))) { | |
| this->AppendFlags(flags, syclFlags); | |
| } | |
| }; | |
| appendSYCLFlags(""); | |
| if (!config.empty()) { | |
| appendSYCLFlags(cmStrCat('_', cmSystemTools::UpperCase(config))); | |
| } | |
| } else { | |
| this->AddConfigVariableFlags(flags, cmStrCat("CMAKE_", lang, "_FLAGS"), | |
| config); | |
| } | |
| // Add the language standard flag for compiling, and sometimes linking. | |
| if (compileOrLink == cmBuildStep::Compile || | |
| (compileOrLink == cmBuildStep::Link && | |
| // Some toolchains require use of the language standard flag | |
| // when linking in order to use the matching standard library. | |
| // FIXME: If CMake gains an abstraction for standard library | |
| // selection, this will have to be reconciled with it. | |
| this->Makefile->IsOn( | |
| cmStrCat("CMAKE_", lang, "_LINK_WITH_STANDARD_COMPILE_OPTION")))) { | |
| cmStandardLevelResolver standardResolver(this->Makefile); | |
| std::string const& optionFlagDef = | |
| standardResolver.GetCompileOptionDef(target, lang, config); | |
| if (!optionFlagDef.empty()) { | |
| cmValue opt = | |
| target->Target->GetMakefile()->GetDefinition(optionFlagDef); | |
| if (opt) { | |
| cmList optList{ *opt }; | |
| for (std::string const& i : optList) { | |
| this->AppendFlagEscape(flags, i); | |
| } | |
| } | |
| } | |
| } | |
| std::string compilerId = this->Makefile->GetSafeDefinition( | |
| cmStrCat("CMAKE_", lang, "_COMPILER_ID")); | |
| std::string compilerSimulateId = this->Makefile->GetSafeDefinition( | |
| cmStrCat("CMAKE_", lang, "_SIMULATE_ID")); | |
| bool const compilerTargetsMsvcABI = | |
| (compilerId == "MSVC" || compilerSimulateId == "MSVC"); | |
| bool const compilerTargetsWatcomABI = | |
| (compilerId == "OpenWatcom" || compilerSimulateId == "OpenWatcom"); | |
| if (lang == "Swift") { | |
| target->AddSwiftTargetFlags(flags); | |
| } else if (lang == "CUDA") { | |
| target->AddCUDAArchitectureFlags(compileOrLink, config, flags); | |
| target->AddCUDAToolkitFlags(flags); | |
| } else if (lang == "SYCL") { | |
| target->AddSYCLDeviceTargetFlags(compileOrLink, config, flags); | |
| } else if (lang == "ISPC") { | |
| target->AddISPCTargetFlags(flags); | |
| } else if (lang == "RC" && | |
| this->Makefile->GetSafeDefinition("CMAKE_RC_COMPILER") | |
| .find("llvm-rc") != std::string::npos) { | |
| compilerId = this->Makefile->GetSafeDefinition("CMAKE_C_COMPILER_ID"); | |
| if (!compilerId.empty()) { | |
| compilerSimulateId = | |
| this->Makefile->GetSafeDefinition("CMAKE_C_SIMULATE_ID"); | |
| } else { | |
| compilerId = this->Makefile->GetSafeDefinition("CMAKE_CXX_COMPILER_ID"); | |
| compilerSimulateId = | |
| this->Makefile->GetSafeDefinition("CMAKE_CXX_SIMULATE_ID"); | |
| } | |
| } else if (lang == "HIP") { | |
| target->AddHIPArchitectureFlags(compileOrLink, config, flags); | |
| } else if (lang == "Rust") { | |
| target->AddRustTargetFlags(flags); | |
| } | |
| // Add VFS Overlay for Clang compilers | |
| if (compilerId == "Clang" || compilerId == "DPClang") { | |
| if (cmValue vfsOverlay = | |
| this->Makefile->GetDefinition("CMAKE_CLANG_VFS_OVERLAY")) { | |
| if (compilerSimulateId == "MSVC") { | |
| this->AppendCompileOptions( | |
| flags, | |
| std::vector<std::string>{ "-Xclang", "-ivfsoverlay", "-Xclang", | |
| *vfsOverlay }); | |
| } else { | |
| this->AppendCompileOptions( | |
| flags, std::vector<std::string>{ "-ivfsoverlay", *vfsOverlay }); | |
| } | |
| } | |
| } | |
| // Add MSVC runtime library flags. This is activated by the presence | |
| // of a default selection whether or not it is overridden by a property. | |
| cmValue msvcRuntimeLibraryDefault = | |
| this->Makefile->GetDefinition("CMAKE_MSVC_RUNTIME_LIBRARY_DEFAULT"); | |
| if (cmNonempty(msvcRuntimeLibraryDefault)) { | |
| cmValue msvcRuntimeLibraryValue = | |
| target->GetProperty("MSVC_RUNTIME_LIBRARY"); | |
| if (!msvcRuntimeLibraryValue) { | |
| msvcRuntimeLibraryValue = msvcRuntimeLibraryDefault; | |
| } | |
| std::string const msvcRuntimeLibrary = cmGeneratorExpression::Evaluate( | |
| *msvcRuntimeLibraryValue, this, config, target); | |
| if (!msvcRuntimeLibrary.empty()) { | |
| if (cmValue msvcRuntimeLibraryOptions = this->Makefile->GetDefinition( | |
| cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_MSVC_RUNTIME_LIBRARY_", | |
| msvcRuntimeLibrary))) { | |
| this->AppendCompileOptions(flags, *msvcRuntimeLibraryOptions); | |
| } else if (compilerTargetsMsvcABI && | |
| !cmSystemTools::GetErrorOccurredFlag()) { | |
| // The compiler uses the MSVC ABI so it needs a known runtime library. | |
| this->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("MSVC_RUNTIME_LIBRARY value '", msvcRuntimeLibrary, | |
| "' not known for this ", lang, " compiler.")); | |
| } | |
| } | |
| } | |
| // Add Watcom runtime library flags. This is activated by the presence | |
| // of a default selection whether or not it is overridden by a property. | |
| cmValue watcomRuntimeLibraryDefault = | |
| this->Makefile->GetDefinition("CMAKE_WATCOM_RUNTIME_LIBRARY_DEFAULT"); | |
| if (cmNonempty(watcomRuntimeLibraryDefault)) { | |
| cmValue watcomRuntimeLibraryValue = | |
| target->GetProperty("WATCOM_RUNTIME_LIBRARY"); | |
| if (!watcomRuntimeLibraryValue) { | |
| watcomRuntimeLibraryValue = watcomRuntimeLibraryDefault; | |
| } | |
| std::string const watcomRuntimeLibrary = cmGeneratorExpression::Evaluate( | |
| *watcomRuntimeLibraryValue, this, config, target); | |
| if (!watcomRuntimeLibrary.empty()) { | |
| if (cmValue watcomRuntimeLibraryOptions = | |
| this->Makefile->GetDefinition(cmStrCat( | |
| "CMAKE_", lang, "_COMPILE_OPTIONS_WATCOM_RUNTIME_LIBRARY_", | |
| watcomRuntimeLibrary))) { | |
| this->AppendCompileOptions(flags, *watcomRuntimeLibraryOptions); | |
| } else if (compilerTargetsWatcomABI && | |
| !cmSystemTools::GetErrorOccurredFlag()) { | |
| // The compiler uses the Watcom ABI so it needs a known runtime | |
| // library. | |
| this->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("WATCOM_RUNTIME_LIBRARY value '", watcomRuntimeLibrary, | |
| "' not known for this ", lang, " compiler.")); | |
| } | |
| } | |
| } | |
| // Add MSVC runtime checks flags. This is activated by the presence | |
| // of a default selection whether or not it is overridden by a property. | |
| cmValue msvcRuntimeChecksDefault = | |
| this->Makefile->GetDefinition("CMAKE_MSVC_RUNTIME_CHECKS_DEFAULT"); | |
| if (cmNonempty(msvcRuntimeChecksDefault)) { | |
| cmValue msvcRuntimeChecksValue = | |
| target->GetProperty("MSVC_RUNTIME_CHECKS"); | |
| if (!msvcRuntimeChecksValue) { | |
| msvcRuntimeChecksValue = msvcRuntimeChecksDefault; | |
| } | |
| cmList msvcRuntimeChecksList = cmGeneratorExpression::Evaluate( | |
| *msvcRuntimeChecksValue, this, config, target); | |
| msvcRuntimeChecksList.remove_duplicates(); | |
| // RTC1/RTCsu VS GUI workaround | |
| std::string const stackFrameErrorCheck = "StackFrameErrorCheck"; | |
| std::string const unitinitializedVariable = "UninitializedVariable"; | |
| std::string const rtcSU = "RTCsu"; | |
| if ((cm::contains(msvcRuntimeChecksList, stackFrameErrorCheck) && | |
| cm::contains(msvcRuntimeChecksList, unitinitializedVariable)) || | |
| cm::contains(msvcRuntimeChecksList, rtcSU)) { | |
| msvcRuntimeChecksList.remove_items( | |
| { stackFrameErrorCheck, unitinitializedVariable, rtcSU }); | |
| msvcRuntimeChecksList.append(rtcSU); | |
| } | |
| for (std::string const& msvcRuntimeChecks : msvcRuntimeChecksList) { | |
| if (cmValue msvcRuntimeChecksOptions = | |
| this->Makefile->GetDefinition(cmStrCat( | |
| "CMAKE_", lang, | |
| "_COMPILE_OPTIONS_MSVC_RUNTIME_CHECKS_" + msvcRuntimeChecks))) { | |
| this->AppendCompileOptions(flags, *msvcRuntimeChecksOptions); | |
| } else if (compilerTargetsMsvcABI && | |
| !cmSystemTools::GetErrorOccurredFlag()) { | |
| // The compiler uses the MSVC ABI so it needs a known runtime checks. | |
| this->IssueMessage(MessageType::FATAL_ERROR, | |
| cmStrCat("MSVC_RUNTIME_CHECKS value '", | |
| msvcRuntimeChecks, "' not known for this ", | |
| lang, " compiler.")); | |
| } | |
| } | |
| } | |
| // Add MSVC debug information format flags if CMP0141 is NEW. | |
| if (cm::optional<std::string> msvcDebugInformationFormat = | |
| this->GetMSVCDebugFormatName(config, target)) { | |
| if (!msvcDebugInformationFormat->empty()) { | |
| if (cmValue msvcDebugInformationFormatOptions = | |
| this->Makefile->GetDefinition( | |
| cmStrCat("CMAKE_", lang, | |
| "_COMPILE_OPTIONS_MSVC_DEBUG_INFORMATION_FORMAT_", | |
| *msvcDebugInformationFormat))) { | |
| this->AppendCompileOptions(flags, *msvcDebugInformationFormatOptions); | |
| } else if (compilerTargetsMsvcABI && | |
| !cmSystemTools::GetErrorOccurredFlag()) { | |
| // The compiler uses the MSVC ABI so it needs a known runtime library. | |
| this->IssueMessage(MessageType::FATAL_ERROR, | |
| cmStrCat("MSVC_DEBUG_INFORMATION_FORMAT value '", | |
| *msvcDebugInformationFormat, | |
| "' not known for this ", lang, | |
| " compiler.")); | |
| } | |
| } | |
| } | |
| } | |
| void cmLocalGenerator::AddLanguageFlagsForLinking( | |
| std::string& flags, cmGeneratorTarget const* target, std::string const& lang, | |
| std::string const& config) | |
| { | |
| this->AddLanguageFlags(flags, target, cmBuildStep::Link, lang, config); | |
| if (lang == "SYCL" && !target->IsDeviceLink() && | |
| this->Makefile->IsOn("CMAKE_SYCL_COMPILER_HAS_DEVICE_LINK_PHASE")) { | |
| if (auto* linkInfo = target->GetLinkInformation(config)) { | |
| if (requireDeviceLinking(*linkInfo->GetTarget(), *this, config, lang)) { | |
| this->AppendFlags(flags, "-fno-sycl"); | |
| } | |
| } | |
| } | |
| if (target->IsIPOEnabled(lang, config)) { | |
| this->AppendFeatureOptions(flags, lang, "IPO"); | |
| } | |
| } | |
| cmGeneratorTarget* cmLocalGenerator::FindGeneratorTargetToUse( | |
| std::string const& name) const | |
| { | |
| auto imported = this->ImportedGeneratorTargets.find(name); | |
| if (imported != this->ImportedGeneratorTargets.end()) { | |
| return imported->second; | |
| } | |
| // find local alias to imported target | |
| auto aliased = this->AliasTargets.find(name); | |
| if (aliased != this->AliasTargets.end()) { | |
| imported = this->ImportedGeneratorTargets.find(aliased->second); | |
| if (imported != this->ImportedGeneratorTargets.end()) { | |
| return imported->second; | |
| } | |
| } | |
| if (cmGeneratorTarget* t = this->FindLocalNonAliasGeneratorTarget(name)) { | |
| return t; | |
| } | |
| return this->GetGlobalGenerator()->FindGeneratorTarget(name); | |
| } | |
| bool cmLocalGenerator::GetRealDependency(std::string const& inName, | |
| std::string const& config, | |
| std::string& dep, | |
| cmPolicies::PolicyStatus cmp0212) | |
| { | |
| // Older CMake code may specify the dependency using the target | |
| // output file rather than the target name. Such code would have | |
| // been written before there was support for target properties that | |
| // modify the name so stripping down to just the file name should | |
| // produce the target name in this case. | |
| std::string name = cmSystemTools::GetFilenameName(inName); | |
| // If the input name is the empty string, there is no real | |
| // dependency. Short-circuit the other checks: | |
| if (name.empty()) { | |
| return false; | |
| } | |
| // Look for a CMake target with the given name. | |
| cmGeneratorTarget* target = this->FindGeneratorTargetToUse(name); | |
| if (!target && cmHasSuffix(name, ".exe"_s) && cmp0212 != cmPolicies::NEW) { | |
| // If it doesn't exist, try to strip the `.exe` suffix per CMP0212. | |
| std::string strippedName = | |
| cmSystemTools::GetFilenameWithoutLastExtension(name); | |
| if (cmGeneratorTarget* strippedTarget = | |
| this->FindGeneratorTargetToUse(strippedName)) { | |
| name = strippedName; | |
| target = strippedTarget; | |
| } | |
| } | |
| if (target) { | |
| // make sure it is not just a coincidence that the target name | |
| // found is part of the inName | |
| if (cmSystemTools::FileIsFullPath(inName)) { | |
| std::string tLocation; | |
| if (target->GetType() >= cm::TargetType::EXECUTABLE && | |
| target->GetType() <= cm::TargetType::MODULE_LIBRARY) { | |
| tLocation = target->GetLocation(config); | |
| tLocation = cmSystemTools::GetFilenamePath(tLocation); | |
| tLocation = cmSystemTools::CollapseFullPath(tLocation); | |
| } | |
| std::string depLocation = | |
| cmSystemTools::GetFilenamePath(std::string(inName)); | |
| depLocation = cmSystemTools::CollapseFullPath(depLocation); | |
| if (depLocation != tLocation) { | |
| // it is a full path to a depend that has the same name | |
| // as a target but is in a different location so do not use | |
| // the target as the depend | |
| dep = inName; | |
| return true; | |
| } | |
| } | |
| switch (target->GetType()) { | |
| case cm::TargetType::EXECUTABLE: | |
| case cm::TargetType::STATIC_LIBRARY: | |
| case cm::TargetType::SHARED_LIBRARY: | |
| case cm::TargetType::MODULE_LIBRARY: | |
| case cm::TargetType::UNKNOWN_LIBRARY: | |
| dep = target->GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact, | |
| /*realname=*/true); | |
| return true; | |
| case cm::TargetType::OBJECT_LIBRARY: | |
| // An object library has no single file on which to depend. | |
| // This was listed to get the target-level dependency. | |
| case cm::TargetType::INTERFACE_LIBRARY: | |
| // An interface library has no file on which to depend. | |
| // This was listed to get the target-level dependency. | |
| case cm::TargetType::UTILITY: | |
| case cm::TargetType::GLOBAL_TARGET: | |
| // A utility target has no file on which to depend. This was listed | |
| // only to get the target-level dependency. | |
| return false; | |
| } | |
| } | |
| // The name was not that of a CMake target. It must name a file. | |
| dep = this->GetRealFileDependency(inName); | |
| return true; | |
| } | |
| std::string cmLocalGenerator::GetRealFileDependency( | |
| std::string const& name) const | |
| { | |
| if (cmSystemTools::FileIsFullPath(name)) { | |
| // This is a full path. Return it as given. | |
| return name; | |
| } | |
| // Check for a source file in this directory that matches the | |
| // dependency. | |
| if (cmSourceFile* sf = this->Makefile->GetSource(name)) { | |
| return sf->ResolveFullPath(); | |
| } | |
| // Treat the name as relative to the source directory in which it | |
| // was given. | |
| std::string dep = cmStrCat(this->GetCurrentSourceDirectory(), '/', name); | |
| // If the in-source path does not exist, assume it instead lives in the | |
| // binary directory. | |
| if (!cmSystemTools::FileExists(dep)) { | |
| dep = cmStrCat(this->GetCurrentBinaryDirectory(), '/', name); | |
| } | |
| return cmSystemTools::CollapseFullPath(dep); | |
| } | |
| static void AddVisibilityCompileOption(std::string& flags, | |
| cmGeneratorTarget const* target, | |
| cmLocalGenerator* lg, | |
| std::string const& lang) | |
| { | |
| std::string compileOption = | |
| cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_VISIBILITY"); | |
| cmValue opt = lg->GetMakefile()->GetDefinition(compileOption); | |
| if (!opt) { | |
| return; | |
| } | |
| std::string flagDefine = lang + "_VISIBILITY_PRESET"; | |
| cmValue prop = target->GetProperty(flagDefine); | |
| if (!prop) { | |
| return; | |
| } | |
| if ((*prop != "hidden") && (*prop != "default") && (*prop != "protected") && | |
| (*prop != "internal")) { | |
| cmSystemTools::Error( | |
| cmStrCat("Target ", target->GetName(), " uses unsupported value \"", | |
| *prop, "\" for ", flagDefine, | |
| ". The supported values are: default, hidden, protected, and " | |
| "internal.")); | |
| return; | |
| } | |
| std::string option = *opt + *prop; | |
| lg->AppendFlags(flags, option); | |
| } | |
| static void AddInlineVisibilityCompileOption(std::string& flags, | |
| cmGeneratorTarget const* target, | |
| cmLocalGenerator* lg, | |
| std::string const& lang) | |
| { | |
| std::string compileOption = | |
| cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_VISIBILITY_INLINES_HIDDEN"); | |
| cmValue opt = lg->GetMakefile()->GetDefinition(compileOption); | |
| if (!opt) { | |
| return; | |
| } | |
| bool prop = target->GetPropertyAsBool("VISIBILITY_INLINES_HIDDEN"); | |
| if (!prop) { | |
| return; | |
| } | |
| lg->AppendFlags(flags, *opt); | |
| } | |
| void cmLocalGenerator::AddVisibilityPresetFlags( | |
| std::string& flags, cmGeneratorTarget const* target, std::string const& lang) | |
| { | |
| if (lang.empty()) { | |
| return; | |
| } | |
| AddVisibilityCompileOption(flags, target, this, lang); | |
| if (lang == "CXX" || lang == "OBJCXX" || lang == "SYCL") { | |
| AddInlineVisibilityCompileOption(flags, target, this, lang); | |
| } | |
| } | |
| void cmLocalGenerator::AddFeatureFlags(std::string& flags, | |
| cmGeneratorTarget const* target, | |
| std::string const& lang, | |
| std::string const& config) | |
| { | |
| cm::TargetType const targetType = target->GetType(); | |
| bool shared = ((targetType == cm::TargetType::SHARED_LIBRARY) || | |
| (targetType == cm::TargetType::MODULE_LIBRARY)); | |
| if (target->GetLinkInterfaceDependentBoolProperty( | |
| "POSITION_INDEPENDENT_CODE", config)) { | |
| this->AddPositionIndependentFlags(flags, lang, targetType); | |
| } | |
| if (shared) { | |
| this->AppendFeatureOptions(flags, lang, "DLL"); | |
| } | |
| } | |
| void cmLocalGenerator::AddPositionIndependentFlags(std::string& flags, | |
| std::string const& lang, | |
| cm::TargetType targetType) | |
| { | |
| std::string picFlags; | |
| if (targetType == cm::TargetType::EXECUTABLE) { | |
| picFlags = this->Makefile->GetSafeDefinition( | |
| cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_PIE")); | |
| } | |
| if (picFlags.empty()) { | |
| picFlags = this->Makefile->GetSafeDefinition( | |
| cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_PIC")); | |
| } | |
| if (!picFlags.empty()) { | |
| cmList options{ picFlags }; | |
| for (std::string const& o : options) { | |
| this->AppendFlagEscape(flags, o); | |
| } | |
| } | |
| } | |
| void cmLocalGenerator::AddColorDiagnosticsFlags(std::string& flags, | |
| std::string const& lang) | |
| { | |
| cmValue diag = this->Makefile->GetDefinition("CMAKE_COLOR_DIAGNOSTICS"); | |
| if (diag.IsSet()) { | |
| std::string colorFlagName; | |
| if (diag.IsOn()) { | |
| colorFlagName = | |
| cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_COLOR_DIAGNOSTICS"); | |
| } else { | |
| colorFlagName = | |
| cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_COLOR_DIAGNOSTICS_OFF"); | |
| } | |
| cmList options{ this->Makefile->GetDefinition(colorFlagName) }; | |
| for (auto const& option : options) { | |
| this->AppendFlagEscape(flags, option); | |
| } | |
| } | |
| } | |
| void cmLocalGenerator::AddConfigVariableFlags(std::string& flags, | |
| std::string const& var, | |
| std::string const& config) | |
| { | |
| // Add the flags from the variable itself. | |
| this->AppendFlags(flags, this->Makefile->GetSafeDefinition(var)); | |
| // Add the flags from the build-type specific variable. | |
| if (!config.empty()) { | |
| std::string const flagsVar = | |
| cmStrCat(var, '_', cmSystemTools::UpperCase(config)); | |
| this->AppendFlags(flags, this->Makefile->GetSafeDefinition(flagsVar)); | |
| } | |
| } | |
| void cmLocalGenerator::AddConfigVariableFlags(std::string& flags, | |
| std::string const& var, | |
| cmGeneratorTarget const* target, | |
| cmBuildStep compileOrLink, | |
| std::string const& lang, | |
| std::string const& config) | |
| { | |
| std::string newFlags; | |
| this->AddConfigVariableFlags(newFlags, var, config); | |
| if (!newFlags.empty()) { | |
| this->AppendFlags(flags, newFlags, var, target, compileOrLink, lang); | |
| } | |
| } | |
| void cmLocalGenerator::AppendFlags(std::string& flags, | |
| std::string const& newFlags) const | |
| { | |
| bool allSpaces = | |
| std::all_of(newFlags.begin(), newFlags.end(), cmsysString_isspace); | |
| if (!newFlags.empty() && !allSpaces) { | |
| if (!flags.empty()) { | |
| flags += ' '; | |
| } | |
| flags += newFlags; | |
| } | |
| } | |
| void cmLocalGenerator::AppendFlags( | |
| std::string& flags, std::vector<BT<std::string>> const& newFlags) const | |
| { | |
| for (BT<std::string> const& flag : newFlags) { | |
| this->AppendFlags(flags, flag.Value); | |
| } | |
| } | |
| void cmLocalGenerator::AppendFlagEscape(std::string& flags, | |
| std::string const& rawFlag) const | |
| { | |
| this->AppendFlags( | |
| flags, | |
| this->EscapeForShell(rawFlag, false, false, false, this->IsNinjaMulti())); | |
| } | |
| void cmLocalGenerator::AppendLinkFlagsWithParsing( | |
| std::string& flags, std::string const& newFlags, | |
| cmGeneratorTarget const* target, std::string const& language) | |
| { | |
| std::vector<std::string> options; | |
| cmSystemTools::ParseUnixCommandLine(newFlags.c_str(), options); | |
| this->SetLinkScriptShell(this->GlobalGenerator->GetUseLinkScript()); | |
| std::vector<BT<std::string>> optionsWithBT{ options.size() }; | |
| std::transform(options.cbegin(), options.cend(), optionsWithBT.begin(), | |
| [](std::string const& item) -> BT<std::string> { | |
| return BT<std::string>{ item }; | |
| }); | |
| target->ResolveLinkerWrapper(optionsWithBT, language); | |
| for (auto const& item : optionsWithBT) { | |
| this->AppendFlagEscape(flags, item.Value); | |
| } | |
| this->SetLinkScriptShell(false); | |
| } | |
| void cmLocalGenerator::AppendFlags(std::string& flags, | |
| std::string const& newFlags, | |
| std::string const& name, | |
| cmGeneratorTarget const* target, | |
| cmBuildStep compileOrLink, | |
| std::string const& language) | |
| { | |
| switch (target->GetPolicyStatusCMP0181()) { | |
| case cmPolicies::WARN: | |
| if (!this->Makefile->GetCMakeInstance()->GetIsInTryCompile() && | |
| this->Makefile->PolicyOptionalWarningEnabled( | |
| "CMAKE_POLICY_WARNING_CMP0181")) { | |
| this->Makefile->IssuePolicyWarning( | |
| cmPolicies::CMP0181, {}, | |
| cmStrCat("Since the policy is not set, the contents of variable '"_s, | |
| name, "' will be used as is."_s), | |
| target->GetBacktrace()); | |
| } | |
| CM_FALLTHROUGH; | |
| case cmPolicies::OLD: | |
| this->AppendFlags( | |
| flags, this->GetGlobalGenerator()->GetEncodedLiteral(newFlags)); | |
| break; | |
| case cmPolicies::NEW: | |
| if (compileOrLink == cmBuildStep::Link) { | |
| this->AppendLinkFlagsWithParsing(flags, newFlags, target, language); | |
| } else { | |
| this->AppendFlags(flags, newFlags); | |
| } | |
| } | |
| } | |
| void cmLocalGenerator::AddISPCDependencies(cmGeneratorTarget* target) | |
| { | |
| std::vector<std::string> enabledLanguages = | |
| this->GetState()->GetEnabledLanguages(); | |
| if (std::find(enabledLanguages.begin(), enabledLanguages.end(), "ISPC") == | |
| enabledLanguages.end()) { | |
| return; | |
| } | |
| cmValue ispcHeaderSuffixProp = target->GetProperty("ISPC_HEADER_SUFFIX"); | |
| assert(ispcHeaderSuffixProp); | |
| std::vector<std::string> ispcArchSuffixes = | |
| detail::ComputeISPCObjectSuffixes(target); | |
| bool const extra_objects = (ispcArchSuffixes.size() > 1); | |
| std::vector<std::string> configsList = | |
| this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); | |
| for (std::string const& config : configsList) { | |
| std::string rootObjectDir = target->GetObjectDirectory(config); | |
| std::string headerDir = rootObjectDir; | |
| if (cmValue prop = target->GetProperty("ISPC_HEADER_DIRECTORY")) { | |
| headerDir = cmSystemTools::CollapseFullPath( | |
| cmStrCat(this->GetBinaryDirectory(), '/', *prop)); | |
| } | |
| std::vector<cmSourceFile*> sources; | |
| target->GetSourceFiles(sources, config); | |
| // build up the list of ispc headers and extra objects that this target is | |
| // generating | |
| for (cmSourceFile const* sf : sources) { | |
| // Generate this object file's rule file. | |
| std::string const lang = target->GetSourceFileLanguage(sf, config); | |
| if (lang == "ISPC") { | |
| std::string const& objectName = target->GetObjectName(sf); | |
| // Drop both ".obj" and the source file extension | |
| std::string ispcSource = | |
| cmSystemTools::GetFilenameWithoutLastExtension(objectName); | |
| ispcSource = | |
| cmSystemTools::GetFilenameWithoutLastExtension(ispcSource); | |
| auto headerPath = | |
| cmStrCat(headerDir, '/', ispcSource, *ispcHeaderSuffixProp); | |
| target->AddISPCGeneratedHeader(headerPath, config); | |
| if (extra_objects) { | |
| std::vector<std::pair<cmSourceFile const*, std::string>> objs; | |
| for (auto& obj : detail::ComputeISPCExtraObjects( | |
| objectName, rootObjectDir, ispcArchSuffixes)) { | |
| objs.push_back({ sf, std::move(obj) }); | |
| } | |
| target->AddISPCGeneratedObject(std::move(objs), config); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| void cmLocalGenerator::AddPchDependencies(cmGeneratorTarget* target) | |
| { | |
| std::vector<std::string> configsList = | |
| this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); | |
| for (std::string const& config : configsList) { | |
| // FIXME: Refactor collection of sources to not evaluate object | |
| // libraries. | |
| std::vector<cmSourceFile*> sources; | |
| target->GetSourceFiles(sources, config); | |
| std::string const configUpper = cmSystemTools::UpperCase(config); | |
| static std::array<std::string, 4> const langs = { { "C", "CXX", "OBJC", | |
| "OBJCXX" } }; | |
| std::set<std::string> pchLangSet; | |
| if (this->GetGlobalGenerator()->IsXcode()) { | |
| for (std::string const& lang : langs) { | |
| std::string const pchHeader = target->GetPchHeader(config, lang, ""); | |
| if (!pchHeader.empty()) { | |
| pchLangSet.emplace(lang); | |
| } | |
| } | |
| } | |
| for (std::string const& lang : langs) { | |
| auto langSources = std::count_if( | |
| sources.begin(), sources.end(), | |
| [&target, &config, &lang](cmSourceFile* sf) { | |
| auto const* const fileSet = target->GetFileSetForSource(config, sf); | |
| return lang == target->GetSourceFileLanguage(sf, config) && | |
| !((fileSet && fileSet->GetProperty("SKIP_PRECOMPILE_HEADERS")) || | |
| sf->GetProperty("SKIP_PRECOMPILE_HEADERS")); | |
| }); | |
| if (langSources == 0) { | |
| continue; | |
| } | |
| std::vector<std::string> pchArchs = target->GetPchArchs(config, lang); | |
| if (pchArchs.size() > 1) { | |
| std::string useMultiArchPch; | |
| for (std::string const& arch : pchArchs) { | |
| std::string const pchHeader = | |
| target->GetPchHeader(config, lang, arch); | |
| if (!pchHeader.empty()) { | |
| useMultiArchPch = cmStrCat(std::move(useMultiArchPch), ";-Xarch_", | |
| arch, ";-include", pchHeader); | |
| } | |
| } | |
| if (!useMultiArchPch.empty()) { | |
| target->Target->AppendProperty( | |
| cmStrCat(lang, "_COMPILE_OPTIONS_USE_PCH"), | |
| cmStrCat("$<$<CONFIG:", config, ">:", useMultiArchPch, '>')); | |
| } | |
| } | |
| for (std::string const& arch : pchArchs) { | |
| std::string const pchSource = target->GetPchSource(config, lang, arch); | |
| std::string const pchHeader = target->GetPchHeader(config, lang, arch); | |
| if (pchSource.empty() || pchHeader.empty()) { | |
| if (this->GetGlobalGenerator()->IsXcode() && !pchLangSet.empty()) { | |
| for (auto* sf : sources) { | |
| auto const sourceLanguage = | |
| target->GetSourceFileLanguage(sf, config); | |
| if (!sourceLanguage.empty() && | |
| pchLangSet.find(sourceLanguage) == pchLangSet.end()) { | |
| sf->SetProperty("SKIP_PRECOMPILE_HEADERS", "ON"); | |
| } | |
| } | |
| } | |
| continue; | |
| } | |
| cmValue pchExtension = | |
| this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION"); | |
| if (pchExtension.IsEmpty()) { | |
| continue; | |
| } | |
| auto* reuseTarget = target->GetPchReuseTarget(); | |
| bool const haveReuseTarget = reuseTarget && reuseTarget != target; | |
| auto* pch_sf = this->Makefile->GetOrCreateSource( | |
| pchSource, false, cmSourceFileLocationKind::Known); | |
| pch_sf->SetSpecialSourceType( | |
| cmSourceFile::SpecialSourceType::PchSource); | |
| // PCH sources should never be scanned as they cannot contain C++ | |
| // module references. | |
| pch_sf->SetProperty("CXX_SCAN_FOR_MODULES", "0"); | |
| if (!this->GetGlobalGenerator()->IsXcode()) { | |
| if (!haveReuseTarget) { | |
| target->AddSource(pchSource, true); | |
| } | |
| std::string const pchFile = target->GetPchFile(config, lang, arch); | |
| // Exclude the pch files from linking | |
| if (this->Makefile->IsOn("CMAKE_LINK_PCH")) { | |
| if (!haveReuseTarget) { | |
| pch_sf->AppendProperty( | |
| "OBJECT_OUTPUTS", | |
| cmStrCat("$<$<CONFIG:", config, ">:", pchFile, '>')); | |
| } else { | |
| if (this->Makefile->IsOn("CMAKE_PCH_COPY_COMPILE_PDB")) { | |
| std::string const compilerId = | |
| this->Makefile->GetSafeDefinition( | |
| cmStrCat("CMAKE_", lang, "_COMPILER_ID")); | |
| std::string const compilerVersion = | |
| this->Makefile->GetSafeDefinition( | |
| cmStrCat("CMAKE_", lang, "_COMPILER_VERSION")); | |
| std::string const langFlags = | |
| this->Makefile->GetSafeDefinition( | |
| cmStrCat("CMAKE_", lang, "_FLAGS_", configUpper)); | |
| bool editAndContinueDebugInfo = false; | |
| bool programDatabaseDebugInfo = false; | |
| cm::optional<std::string> msvcDebugInformationFormat = | |
| this->GetMSVCDebugFormatName(config, target); | |
| if (msvcDebugInformationFormat && | |
| !msvcDebugInformationFormat->empty()) { | |
| editAndContinueDebugInfo = | |
| *msvcDebugInformationFormat == "EditAndContinue"; | |
| programDatabaseDebugInfo = | |
| *msvcDebugInformationFormat == "ProgramDatabase"; | |
| } else { | |
| editAndContinueDebugInfo = | |
| langFlags.find("/ZI") != std::string::npos || | |
| langFlags.find("-ZI") != std::string::npos; | |
| programDatabaseDebugInfo = | |
| langFlags.find("/Zi") != std::string::npos || | |
| langFlags.find("-Zi") != std::string::npos; | |
| } | |
| if (editAndContinueDebugInfo) { | |
| this->CopyPchCompilePdb(config, lang, target, reuseTarget, | |
| { ".pdb", ".idb" }); | |
| } else if (programDatabaseDebugInfo) { | |
| this->CopyPchCompilePdb(config, lang, target, reuseTarget, | |
| { ".pdb" }); | |
| } | |
| } | |
| // Link to the pch object file | |
| std::string pchSourceObj; | |
| // Fastbuild will propagate pch.obj for us, no need to link to it | |
| // explicitly. | |
| if (!this->GetGlobalGenerator()->IsFastbuild()) { | |
| pchSourceObj = | |
| reuseTarget->GetPchFileObject(config, lang, arch); | |
| } | |
| if (target->GetType() != cm::TargetType::OBJECT_LIBRARY) { | |
| std::string linkerProperty = "LINK_FLAGS_"; | |
| if (target->GetType() == cm::TargetType::STATIC_LIBRARY) { | |
| linkerProperty = "STATIC_LIBRARY_FLAGS_"; | |
| } | |
| target->Target->AppendProperty( | |
| cmStrCat(linkerProperty, configUpper), | |
| cmStrCat(' ', | |
| this->ConvertToOutputFormat(pchSourceObj, SHELL)), | |
| cm::nullopt, true); | |
| } else if (reuseTarget->GetType() == | |
| cm::TargetType::OBJECT_LIBRARY) { | |
| target->Target->AppendProperty( | |
| "INTERFACE_LINK_LIBRARIES", | |
| cmStrCat("$<$<CONFIG:", config, | |
| ">:$<LINK_ONLY:", pchSourceObj, ">>")); | |
| } | |
| } | |
| } else { | |
| pch_sf->SetProperty("PCH_EXTENSION", pchExtension); | |
| } | |
| // Add pchHeader to source files, which will | |
| // be grouped as "Precompile Header File" | |
| auto* pchHeader_sf = this->Makefile->GetOrCreateSource( | |
| pchHeader, false, cmSourceFileLocationKind::Known); | |
| pchHeader_sf->SetSpecialSourceType( | |
| cmSourceFile::SpecialSourceType::PchHeader); | |
| std::string err; | |
| pchHeader_sf->ResolveFullPath(&err); | |
| if (!err.empty()) { | |
| this->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("Unable to resolve full path of PCH-header '", | |
| pchHeader, "' assigned to target ", target->GetName(), | |
| ", although its path is supposed to be known!")); | |
| } | |
| target->AddSource(pchHeader); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| void cmLocalGenerator::CopyPchCompilePdb( | |
| std::string const& config, std::string const& language, | |
| cmGeneratorTarget* target, cmGeneratorTarget* reuseTarget, | |
| std::vector<std::string> const& extensions) | |
| { | |
| std::string const copy_script = cmStrCat(target->GetSupportDirectory(), | |
| "/copy_idb_pdb_", config, ".cmake"); | |
| cmGeneratedFileStream file(copy_script); | |
| file.SetCopyIfDifferent(true); | |
| file << "# CMake generated file\n"; | |
| file << "# The compiler generated pdb file needs to be written to disk\n" | |
| << "# by mspdbsrv. The foreach retry loop is needed to make sure\n" | |
| << "# the pdb file is ready to be copied.\n\n"; | |
| auto configGenex = [&](cm::string_view expr) -> std::string { | |
| if (this->GetGlobalGenerator()->IsMultiConfig()) { | |
| return cmStrCat("$<$<CONFIG:", config, ">:", expr, '>'); | |
| } | |
| return std::string(expr); | |
| }; | |
| std::vector<std::string> byproducts; | |
| auto replaceExtension = [](std::string const& path, | |
| std::string const& ext) -> std::string { | |
| auto const dir = cmSystemTools::GetFilenamePath(path); | |
| auto const base = cmSystemTools::GetFilenameWithoutLastExtension(path); | |
| if (dir.empty()) { | |
| return cmStrCat(base, ext); | |
| } | |
| return cmStrCat(dir, '/', base, ext); | |
| }; | |
| for (auto const& extension : extensions) { | |
| std::string const from_file = | |
| replaceExtension(reuseTarget->GetCompilePDBPath(config), extension); | |
| std::string const to_dir = target->GetCompilePDBDirectory(config); | |
| std::string const to_file = | |
| replaceExtension(reuseTarget->GetCompilePDBName(config), extension); | |
| std::string const dest_file = cmStrCat(to_dir, '/', to_file); | |
| file << "foreach(retry RANGE 1 30)\n"; | |
| file << " if (EXISTS \"" << from_file << "\" AND (NOT EXISTS \"" | |
| << dest_file << "\" OR NOT \"" << dest_file << "\" IS_NEWER_THAN \"" | |
| << from_file << "\"))\n"; | |
| file << " file(MAKE_DIRECTORY \"" << to_dir << "\")\n"; | |
| file << " execute_process(COMMAND ${CMAKE_COMMAND} -E copy"; | |
| file << " \"" << from_file << "\"" << " \"" << to_dir | |
| << "\" RESULT_VARIABLE result " << " ERROR_QUIET)\n"; | |
| file << " if (NOT result EQUAL 0)\n" | |
| << " execute_process(COMMAND ${CMAKE_COMMAND}" | |
| << " -E sleep 1)\n" | |
| << " else()\n"; | |
| file << " break()\n" | |
| << " endif()\n"; | |
| file << " elseif(NOT EXISTS \"" << from_file << "\")\n" | |
| << " execute_process(COMMAND ${CMAKE_COMMAND}" << " -E sleep 1)\n" | |
| << " endif()\n"; | |
| file << "endforeach()\n"; | |
| byproducts.push_back(configGenex(dest_file)); | |
| } | |
| // Multiple targets reusing the same PCH may share a compiler PDB | |
| // directory, in which case they copy to the same destination. Use a | |
| // per-target stamp file as the rule output to avoid duplicate rules. | |
| std::string const stamp_file = cmStrCat(target->GetSupportDirectory(), | |
| "/copy_idb_pdb_", config, ".stamp"); | |
| file << "file(TOUCH \"" << stamp_file << "\")\n"; | |
| cmCustomCommandLines commandLines = | |
| cmMakeSingleCommandLine({ configGenex(cmSystemTools::GetCMakeCommand()), | |
| configGenex("-P"), configGenex(copy_script) }); | |
| auto const comment = | |
| cmStrCat("Copying PDB for PCH reuse from ", reuseTarget->GetName(), | |
| " for ", target->GetName()); | |
| ; | |
| auto cc = cm::make_unique<cmCustomCommand>(); | |
| cc->SetCommandLines(commandLines); | |
| cc->SetComment(comment); | |
| cc->SetStdPipesUTF8(true); | |
| cc->AppendDepends( | |
| { reuseTarget->GetPchFile(config, language), copy_script }); | |
| // Fastbuild needs to know that this custom command actually depends on a | |
| // target that produces PCH, so it can sort by dependencies correctly. | |
| if (this->GetGlobalGenerator()->IsFastbuild()) { | |
| cc->AppendDepends({ reuseTarget->GetName() }); | |
| } | |
| if (this->GetGlobalGenerator()->IsVisualStudio()) { | |
| cc->SetByproducts(byproducts); | |
| this->AddCustomCommandToTarget( | |
| target->GetName(), cmCustomCommandType::PRE_BUILD, std::move(cc), | |
| cmObjectLibraryCommands::Accept); | |
| } else { | |
| cc->SetOutputs(configGenex(stamp_file)); | |
| cmSourceFile* copy_rule = this->AddCustomCommandToOutput(std::move(cc)); | |
| if (copy_rule) { | |
| copy_rule->SetProperty("CXX_SCAN_FOR_MODULES", "0"); | |
| auto* pch_pdb_sf = target->AddSource(copy_rule->ResolveFullPath()); | |
| pch_pdb_sf->SetSpecialSourceType( | |
| cmSourceFile::SpecialSourceType::PchPdbReuseSource); | |
| } | |
| } | |
| } | |
| cm::optional<std::string> cmLocalGenerator::GetMSVCDebugFormatName( | |
| std::string const& config, cmGeneratorTarget const* target) | |
| { | |
| // MSVC debug information format selection is activated by the presence | |
| // of a default whether or not it is overridden by a property. | |
| cm::optional<std::string> msvcDebugInformationFormat; | |
| cmValue msvcDebugInformationFormatDefault = this->Makefile->GetDefinition( | |
| "CMAKE_MSVC_DEBUG_INFORMATION_FORMAT_DEFAULT"); | |
| if (cmNonempty(msvcDebugInformationFormatDefault)) { | |
| cmValue msvcDebugInformationFormatValue = | |
| target->GetProperty("MSVC_DEBUG_INFORMATION_FORMAT"); | |
| if (!msvcDebugInformationFormatValue) { | |
| msvcDebugInformationFormatValue = msvcDebugInformationFormatDefault; | |
| } | |
| msvcDebugInformationFormat = cmGeneratorExpression::Evaluate( | |
| *msvcDebugInformationFormatValue, this, config, target); | |
| } | |
| return msvcDebugInformationFormat; | |
| } | |
| cm::optional<cmSwiftCompileMode> cmLocalGenerator::GetSwiftCompileMode( | |
| cmGeneratorTarget const* target, std::string const& config) | |
| { | |
| cmMakefile const* mf = this->GetMakefile(); | |
| cmValue const swiftCompileModeDefault = | |
| mf->GetDefinition("CMAKE_Swift_COMPILATION_MODE_DEFAULT"); | |
| if (!cmNonempty(swiftCompileModeDefault)) { | |
| return {}; | |
| } | |
| cmValue swiftCompileMode = target->GetProperty("Swift_COMPILATION_MODE"); | |
| if (!swiftCompileMode) { | |
| swiftCompileMode = swiftCompileModeDefault; | |
| } | |
| std::string const expandedCompileMode = | |
| cmGeneratorExpression::Evaluate(*swiftCompileMode, this, config, target); | |
| if (expandedCompileMode == "wholemodule") { | |
| return cmSwiftCompileMode::Wholemodule; | |
| } | |
| if (expandedCompileMode == "singlefile") { | |
| return cmSwiftCompileMode::Singlefile; | |
| } | |
| if (expandedCompileMode == "incremental") { | |
| return cmSwiftCompileMode::Incremental; | |
| } | |
| return cmSwiftCompileMode::Unknown; | |
| } | |
| bool cmLocalGenerator::IsSplitSwiftBuild() const | |
| { | |
| return cmNonempty(this->GetMakefile()->GetDefinition( | |
| "CMAKE_Swift_COMPILATION_MODE_DEFAULT")); | |
| } | |
| namespace { | |
| inline void RegisterUnitySources(cmGeneratorTarget* target, cmSourceFile* sf, | |
| std::string const& filename) | |
| { | |
| target->AddSourceFileToUnityBatch(sf->ResolveFullPath()); | |
| sf->SetProperty("UNITY_SOURCE_FILE", filename); | |
| } | |
| } | |
| cmLocalGenerator::UnitySource cmLocalGenerator::WriteUnitySource( | |
| cmGeneratorTarget* target, std::vector<std::string> const& configs, | |
| cmRange<std::vector<UnityBatchedSource>::const_iterator> sources, | |
| cmValue beforeInclude, cmValue afterInclude, std::string filename, | |
| std::string const& unityFileDirectory, UnityPathMode pathMode) const | |
| { | |
| cmValue uniqueIdName = target->GetProperty("UNITY_BUILD_UNIQUE_ID"); | |
| cmGeneratedFileStream file( | |
| filename, false, target->GetGlobalGenerator()->GetMakefileEncoding()); | |
| file.SetCopyIfDifferent(true); | |
| file << "/* generated by CMake */\n\n"; | |
| bool perConfig = false; | |
| for (UnityBatchedSource const& ubs : sources) { | |
| cm::optional<std::string> cond; | |
| if (ubs.Configs.size() != configs.size()) { | |
| perConfig = true; | |
| cond = std::string(); | |
| cm::string_view sep; | |
| for (size_t ci : ubs.Configs) { | |
| cond = cmStrCat(*cond, sep, "defined(CMAKE_UNITY_CONFIG_", | |
| cmSystemTools::UpperCase(configs[ci]), ')'); | |
| sep = " || "_s; | |
| } | |
| } | |
| RegisterUnitySources(target, ubs.Source, filename); | |
| WriteUnitySourceInclude(file, cond, ubs.Source->ResolveFullPath(), | |
| beforeInclude, afterInclude, uniqueIdName, | |
| pathMode, unityFileDirectory); | |
| } | |
| return UnitySource(std::move(filename), perConfig); | |
| } | |
| void cmLocalGenerator::WriteUnitySourceInclude( | |
| std::ostream& unity_file, cm::optional<std::string> const& cond, | |
| std::string const& sf_full_path, cmValue beforeInclude, cmValue afterInclude, | |
| cmValue uniqueIdName, UnityPathMode pathMode, | |
| std::string const& unityFileDirectory) const | |
| { | |
| if (cond) { | |
| unity_file << "#if " << *cond << "\n"; | |
| } | |
| std::string pathToHash; | |
| std::string relocatableIncludePath; | |
| auto PathEqOrSubDir = [](std::string const& a, std::string const& b) { | |
| return (cmSystemTools::ComparePath(a, b) || | |
| cmSystemTools::IsSubDirectory(a, b)); | |
| }; | |
| auto const path = cmSystemTools::GetFilenamePath(sf_full_path); | |
| if (PathEqOrSubDir(path, this->GetBinaryDirectory())) { | |
| relocatableIncludePath = | |
| cmSystemTools::RelativePath(unityFileDirectory, sf_full_path); | |
| pathToHash = "BLD_" + | |
| cmSystemTools::RelativePath(this->GetBinaryDirectory(), sf_full_path); | |
| } else if (PathEqOrSubDir(path, this->GetSourceDirectory())) { | |
| relocatableIncludePath = | |
| cmSystemTools::RelativePath(this->GetSourceDirectory(), sf_full_path); | |
| pathToHash = "SRC_" + relocatableIncludePath; | |
| } else { | |
| relocatableIncludePath = sf_full_path; | |
| pathToHash = "ABS_" + sf_full_path; | |
| } | |
| if (cmNonempty(uniqueIdName)) { | |
| cmCryptoHash hasher(cmCryptoHash::AlgoMD5); | |
| unity_file << "/* " << pathToHash << " */\n" | |
| << "#undef " << *uniqueIdName << "\n" | |
| << "#define " << *uniqueIdName << " unity_" | |
| << hasher.HashString(pathToHash) << "\n"; | |
| } | |
| if (beforeInclude) { | |
| unity_file << *beforeInclude << "\n"; | |
| } | |
| // clang-tidy-17 has new include checks that needs NOLINT too. | |
| unity_file | |
| << "/* NOLINTNEXTLINE(bugprone-suspicious-include,misc-include-cleaner) " | |
| "*/\n"; | |
| if (pathMode == UnityPathMode::Relative) { | |
| unity_file << "#include \"" << relocatableIncludePath << "\"\n"; | |
| } else { | |
| unity_file << "#include \"" << sf_full_path << "\"\n"; | |
| } | |
| if (afterInclude) { | |
| unity_file << *afterInclude << "\n"; | |
| } | |
| if (cond) { | |
| unity_file << "#endif\n"; | |
| } | |
| unity_file << "\n"; | |
| } | |
| namespace { | |
| std::string unity_file_extension(std::string const& lang) | |
| { | |
| std::string extension; | |
| if (lang == "C") { | |
| extension = "_c.c"; | |
| } else if (lang == "CXX") { | |
| extension = "_cxx.cxx"; | |
| } else if (lang == "CUDA") { | |
| extension = "_cu.cu"; | |
| } else if (lang == "OBJC") { | |
| extension = "_m.m"; | |
| } else if (lang == "OBJCXX") { | |
| extension = "_mm.mm"; | |
| } | |
| return extension; | |
| } | |
| char const* unity_file_prefix(cmGeneratorTarget* target) | |
| { | |
| if (cmValue val = target->GetProperty("UNITY_BUILD_FILENAME_PREFIX")) { | |
| return val->c_str(); | |
| } | |
| return "unity_"; | |
| } | |
| } | |
| std::vector<cmLocalGenerator::UnitySource> | |
| cmLocalGenerator::AddUnityFilesModeAuto( | |
| cmGeneratorTarget* target, std::string const& lang, | |
| std::vector<std::string> const& configs, | |
| std::vector<UnityBatchedSource> const& filtered_sources, | |
| cmValue beforeInclude, cmValue afterInclude, | |
| std::string const& filename_base, UnityPathMode pathMode, size_t batchSize) | |
| { | |
| if (batchSize == 0) { | |
| batchSize = filtered_sources.size(); | |
| } | |
| char const* filename_prefix = unity_file_prefix(target); | |
| std::vector<UnitySource> unity_files; | |
| for (size_t itemsLeft = filtered_sources.size(), chunk, batch = 0; | |
| itemsLeft > 0; itemsLeft -= chunk, ++batch) { | |
| chunk = std::min(itemsLeft, batchSize); | |
| std::string filename = cmStrCat(filename_base, filename_prefix, batch, | |
| unity_file_extension(lang)); | |
| auto const begin = filtered_sources.begin() + batch * batchSize; | |
| auto const end = begin + chunk; | |
| unity_files.emplace_back(this->WriteUnitySource( | |
| target, configs, cmMakeRange(begin, end), beforeInclude, afterInclude, | |
| std::move(filename), filename_base, pathMode)); | |
| } | |
| return unity_files; | |
| } | |
| std::vector<cmLocalGenerator::UnitySource> | |
| cmLocalGenerator::AddUnityFilesModeGroup( | |
| cmGeneratorTarget* target, std::string const& lang, | |
| std::vector<std::string> const& configs, | |
| std::vector<UnityBatchedSource> const& filtered_sources, | |
| cmValue beforeInclude, cmValue afterInclude, | |
| std::string const& filename_base, UnityPathMode pathMode) | |
| { | |
| std::vector<UnitySource> unity_files; | |
| // sources organized by group name. Drop any source | |
| // without a group | |
| std::unordered_map<std::string, std::vector<UnityBatchedSource>> | |
| explicit_mapping; | |
| for (UnityBatchedSource const& ubs : filtered_sources) { | |
| if (cmValue value = ubs.Source->GetProperty("UNITY_GROUP")) { | |
| auto i = explicit_mapping.find(*value); | |
| if (i == explicit_mapping.end()) { | |
| std::vector<UnityBatchedSource> sources{ ubs }; | |
| explicit_mapping.emplace(*value, std::move(sources)); | |
| } else { | |
| i->second.emplace_back(ubs); | |
| } | |
| } | |
| } | |
| char const* filename_prefix = unity_file_prefix(target); | |
| for (auto const& item : explicit_mapping) { | |
| auto const& name = item.first; | |
| std::string filename = cmStrCat(filename_base, filename_prefix, name, | |
| unity_file_extension(lang)); | |
| unity_files.emplace_back(this->WriteUnitySource( | |
| target, configs, cmMakeRange(item.second), beforeInclude, afterInclude, | |
| std::move(filename), filename_base, pathMode)); | |
| } | |
| return unity_files; | |
| } | |
| void cmLocalGenerator::AddUnityBuild(cmGeneratorTarget* target) | |
| { | |
| // cmFastbuildNormalTargetGenerator handles unity build. | |
| if (this->GetGlobalGenerator()->IsFastbuild() || | |
| !target->GetPropertyAsBool("UNITY_BUILD")) { | |
| return; | |
| } | |
| std::vector<UnityBatchedSource> unitySources; | |
| std::vector<std::string> configs = | |
| this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); | |
| std::map<cmSourceFile const*, size_t> index; | |
| for (size_t ci = 0; ci < configs.size(); ++ci) { | |
| // FIXME: Refactor collection of sources to not evaluate object libraries. | |
| // Their final set of object files might be transformed by unity builds. | |
| std::vector<cmSourceFile*> sources; | |
| target->GetSourceFiles(sources, configs[ci]); | |
| for (cmSourceFile* sf : sources) { | |
| cmGeneratorFileSet const* fileSet = | |
| target->GetFileSetForSource(configs[ci], sf); | |
| if (fileSet && | |
| !cm::FileSetMetadata::GetAttributes(fileSet->GetType()) | |
| .contains(cm::FileSetMetadata::FileSetAttributes::UnityBuild)) { | |
| continue; | |
| } | |
| if (target->GetSourceFileLanguage(sf, configs[ci]) != | |
| sf->GetOrDetermineLanguage()) { | |
| continue; | |
| } | |
| // Files which need C++ scanning cannot participate in unity builds as | |
| // there is a single place in TUs that may perform module-dependency bits | |
| // and a unity source cannot `#include` them in-order and represent a | |
| // valid TU. | |
| if (sf->GetLanguage() == "CXX"_s && | |
| target->NeedDyndepForSource("CXX", configs[ci], sf)) { | |
| continue; | |
| } | |
| auto mi = index.find(sf); | |
| if (mi == index.end()) { | |
| unitySources.emplace_back(sf); | |
| std::map<cmSourceFile const*, size_t>::value_type entry( | |
| sf, unitySources.size() - 1); | |
| mi = index.insert(entry).first; | |
| } | |
| unitySources[mi->second].Configs.emplace_back(ci); | |
| } | |
| } | |
| std::string filename_base = | |
| cmStrCat(target->GetCMFSupportDirectory(), "/Unity/"); | |
| cmValue batchSizeString = target->GetProperty("UNITY_BUILD_BATCH_SIZE"); | |
| size_t const unityBatchSize = batchSizeString | |
| ? static_cast<size_t>(std::atoi(batchSizeString->c_str())) | |
| : 0; | |
| cmValue beforeInclude = | |
| target->GetProperty("UNITY_BUILD_CODE_BEFORE_INCLUDE"); | |
| cmValue afterInclude = target->GetProperty("UNITY_BUILD_CODE_AFTER_INCLUDE"); | |
| cmValue unityMode = target->GetProperty("UNITY_BUILD_MODE"); | |
| UnityPathMode pathMode = target->GetPropertyAsBool("UNITY_BUILD_RELOCATABLE") | |
| ? UnityPathMode::Relative | |
| : UnityPathMode::Absolute; | |
| for (std::string lang : { "C", "CXX", "OBJC", "OBJCXX", "CUDA" }) { | |
| std::vector<UnityBatchedSource> filtered_sources; | |
| std::copy_if( | |
| unitySources.begin(), unitySources.end(), | |
| std::back_inserter(filtered_sources), | |
| [&](UnityBatchedSource const& ubs) -> bool { | |
| cmSourceFile* sf = ubs.Source; | |
| if (sf->GetLanguage() != lang) { | |
| return false; | |
| } | |
| for (auto idx : ubs.Configs) { | |
| cmGeneratorFileSet const* fileSet = | |
| target->GetFileSetForSource(configs[idx], sf); | |
| if (fileSet && | |
| (fileSet->GetProperty("SKIP_UNITY_BUILD_INCLUSION").IsOn() || | |
| fileSet->GetProperty(fileSet->BelongsTo(target) | |
| ? "COMPILE_OPTIONS" | |
| : "INTERFACE_COMPILE_OPTIONS") || | |
| fileSet->GetProperty(fileSet->BelongsTo(target) | |
| ? "COMPILE_DEFINITIONS" | |
| : "INTERFACE_COMPILE_DEFINITIONS") || | |
| fileSet->GetProperty(fileSet->BelongsTo(target) | |
| ? "INCLUDE_DIRECTORIES" | |
| : "INTERFACE_INCLUDE_DIRECTORIES"))) { | |
| return false; | |
| } | |
| } | |
| return !sf->GetPropertyAsBool("SKIP_UNITY_BUILD_INCLUSION") && | |
| !sf->GetPropertyAsBool("HEADER_FILE_ONLY") && | |
| !sf->GetProperty("COMPILE_OPTIONS") && | |
| !sf->GetProperty("COMPILE_DEFINITIONS") && | |
| !sf->GetProperty("COMPILE_FLAGS") && | |
| !sf->GetProperty("INCLUDE_DIRECTORIES"); | |
| }); | |
| std::vector<UnitySource> unity_files; | |
| if (!unityMode || *unityMode == "BATCH") { | |
| unity_files = AddUnityFilesModeAuto( | |
| target, lang, configs, filtered_sources, beforeInclude, afterInclude, | |
| filename_base, pathMode, unityBatchSize); | |
| } else if (unityMode && *unityMode == "GROUP") { | |
| unity_files = AddUnityFilesModeGroup( | |
| target, lang, configs, filtered_sources, beforeInclude, afterInclude, | |
| filename_base, pathMode); | |
| } else { | |
| // unity mode is set to an unsupported value | |
| std::string e(cmStrCat("Invalid UNITY_BUILD_MODE value of ", *unityMode, | |
| " assigned to target ", target->GetName(), | |
| ". Acceptable values are BATCH and GROUP.")); | |
| this->IssueMessage(MessageType::FATAL_ERROR, e); | |
| } | |
| for (UnitySource const& file : unity_files) { | |
| auto* unity = this->GetMakefile()->GetOrCreateSource(file.Path); | |
| unity->SetSpecialSourceType( | |
| cmSourceFile::SpecialSourceType::UnitySource); | |
| target->AddSource(file.Path, true); | |
| unity->SetProperty("SKIP_UNITY_BUILD_INCLUSION", "ON"); | |
| unity->SetProperty("UNITY_SOURCE_FILE", file.Path); | |
| unity->SetProperty("CXX_SCAN_FOR_MODULES", "0"); | |
| if (file.PerConfig) { | |
| unity->SetProperty("COMPILE_DEFINITIONS", | |
| "CMAKE_UNITY_CONFIG_$<UPPER_CASE:$<CONFIG>>"); | |
| } | |
| if (pathMode == UnityPathMode::Relative) { | |
| unity->AppendProperty("INCLUDE_DIRECTORIES", | |
| this->GetSourceDirectory(), false); | |
| } | |
| } | |
| } | |
| } | |
| void cmLocalGenerator::AddPerLanguageLinkFlags(std::string& flags, | |
| cmGeneratorTarget const* target, | |
| std::string const& lang, | |
| std::string const& config) | |
| { | |
| switch (target->GetType()) { | |
| case cm::TargetType::MODULE_LIBRARY: | |
| case cm::TargetType::SHARED_LIBRARY: | |
| case cm::TargetType::EXECUTABLE: | |
| break; | |
| default: | |
| return; | |
| } | |
| std::string langLinkFlags = | |
| this->Makefile->GetSafeDefinition(cmStrCat("CMAKE_", lang, "_LINK_FLAGS")); | |
| switch (target->GetPolicyStatusCMP0210()) { | |
| case cmPolicies::WARN: | |
| // WARN only when CMAKE_<LANG>_LINK_FLAGS is set, and when the current | |
| // target is not an executable, and CMAKE_<LANG>_LINK_FLAGS is not equal | |
| // to CMAKE_EXECUTABLE_CREATE_<LANG>_FLAGS. This warns users trying to | |
| // use the NEW behavior on old projects (since CMake will be ignoring | |
| // their wishes), while also exempting cases when the latter variable | |
| // (substituted for the former spelling under the NEW behavior) is being | |
| // used legitimately by CMake. | |
| // Additionally, WARN at most once per language, instead of on every | |
| // target. | |
| if (!langLinkFlags.empty() && | |
| target->GetType() != cm::TargetType::EXECUTABLE && | |
| langLinkFlags != | |
| this->Makefile->GetSafeDefinition( | |
| cmStrCat("CMAKE_EXECUTABLE_CREATE_", lang, "_FLAGS")) && | |
| this->GlobalGenerator->ShouldWarnCMP0210(lang)) { | |
| this->IssuePolicyWarning( | |
| cmPolicies::CMP0210, {}, | |
| cmStrCat("For compatibility with older versions of CMake, CMAKE_"_s, | |
| lang, | |
| "_LINK_FLAGS will be ignored for all non-EXECUTABLE " | |
| "targets which use these flags."_s)); | |
| } | |
| CM_FALLTHROUGH; | |
| case cmPolicies::OLD: | |
| // OLD behavior is to do nothing here, since the use of | |
| // CMAKE_<LANG>_LINK_FLAGS for EXECUTABLEs is handled elsewhere. | |
| break; | |
| case cmPolicies::NEW: | |
| // NEW behavior is to support per-language link flags for all target | |
| // types. | |
| this->AppendLinkFlagsWithParsing(flags, langLinkFlags, target, lang); | |
| if (!config.empty()) { | |
| std::string lankLinkFlagsConfig = | |
| this->Makefile->GetSafeDefinition(cmStrCat( | |
| "CMAKE_", lang, "_LINK_FLAGS_", cmSystemTools::UpperCase(config))); | |
| this->AppendLinkFlagsWithParsing(flags, lankLinkFlagsConfig, target, | |
| lang); | |
| } | |
| break; | |
| } | |
| } | |
| void cmLocalGenerator::AppendTargetCreationLinkFlags( | |
| std::string& flags, cmGeneratorTarget const* target, | |
| std::string const& linkLanguage) | |
| { | |
| std::string createFlagsVar; | |
| cmValue createFlagsVal; | |
| switch (target->GetType()) { | |
| case cm::TargetType::STATIC_LIBRARY: | |
| break; | |
| case cm::TargetType::MODULE_LIBRARY: | |
| createFlagsVar = | |
| cmStrCat("CMAKE_SHARED_MODULE_CREATE_", linkLanguage, "_FLAGS"); | |
| createFlagsVal = this->Makefile->GetDefinition(createFlagsVar); | |
| // On some platforms we use shared library creation flags for modules. | |
| CM_FALLTHROUGH; | |
| case cm::TargetType::SHARED_LIBRARY: | |
| if (!createFlagsVal) { | |
| createFlagsVar = | |
| cmStrCat("CMAKE_SHARED_LIBRARY_CREATE_", linkLanguage, "_FLAGS"); | |
| createFlagsVal = this->Makefile->GetDefinition(createFlagsVar); | |
| } | |
| break; | |
| case cm::TargetType::EXECUTABLE: | |
| createFlagsVar = target->GetPolicyStatusCMP0210() == cmPolicies::NEW | |
| ? cmStrCat("CMAKE_EXECUTABLE_CREATE_", linkLanguage, "_FLAGS") | |
| : cmStrCat("CMAKE_", linkLanguage, "_LINK_FLAGS"); | |
| createFlagsVal = this->Makefile->GetDefinition(createFlagsVar); | |
| break; | |
| default: | |
| break; | |
| } | |
| if (createFlagsVal) { | |
| this->AppendFlags(flags, *createFlagsVal, createFlagsVar, target, | |
| cmBuildStep::Link, linkLanguage); | |
| } | |
| } | |
| void cmLocalGenerator::AppendLinkerTypeFlags(std::string& flags, | |
| cmGeneratorTarget* target, | |
| std::string const& config, | |
| std::string const& linkLanguage) | |
| { | |
| switch (target->GetType()) { | |
| case cm::TargetType::EXECUTABLE: | |
| case cm::TargetType::SHARED_LIBRARY: | |
| case cm::TargetType::MODULE_LIBRARY: | |
| break; | |
| default: | |
| return; | |
| } | |
| auto linkMode = | |
| cmStrCat("CMAKE_", linkLanguage, target->IsDeviceLink() ? "_DEVICE_" : "_", | |
| "LINK_MODE"); | |
| auto mode = this->Makefile->GetDefinition(linkMode); | |
| if (mode && mode != "DRIVER"_s) { | |
| return; | |
| } | |
| auto linkerType = target->GetLinkerTypeProperty(linkLanguage, config); | |
| if (linkerType.empty()) { | |
| linkerType = "DEFAULT"; | |
| } | |
| auto usingLinker = | |
| cmStrCat("CMAKE_", linkLanguage, "_USING_", | |
| target->IsDeviceLink() ? "DEVICE_" : "", "LINKER_", linkerType); | |
| auto linkerTypeFlags = this->Makefile->GetDefinition(usingLinker); | |
| if (linkerTypeFlags) { | |
| if (!linkerTypeFlags.IsEmpty()) { | |
| auto linkerFlags = cmExpandListWithBacktrace(linkerTypeFlags); | |
| target->ResolveLinkerWrapper(linkerFlags, linkLanguage); | |
| this->AppendFlags(flags, linkerFlags); | |
| } | |
| } else if (linkerType != "DEFAULT"_s) { | |
| auto isCMakeLinkerType = [](std::string const& type) -> bool { | |
| return std::all_of(type.cbegin(), type.cend(), cmsysString_isupper); | |
| }; | |
| if (isCMakeLinkerType(linkerType)) { | |
| this->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("LINKER_TYPE '", linkerType, | |
| "' is unknown or not supported by this toolchain.")); | |
| } else { | |
| this->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("LINKER_TYPE '", linkerType, | |
| "' is unknown. Did you forget to define the '", usingLinker, | |
| "' variable?")); | |
| } | |
| } | |
| } | |
| void cmLocalGenerator::AddTargetTypeLinkerFlags( | |
| std::string& flags, cmGeneratorTarget const* target, std::string const& lang, | |
| std::string const& config) | |
| { | |
| std::string linkerFlagsVar; | |
| switch (target->GetType()) { | |
| case cm::TargetType::EXECUTABLE: | |
| linkerFlagsVar = "CMAKE_EXE_LINKER_FLAGS"; | |
| break; | |
| case cm::TargetType::SHARED_LIBRARY: | |
| linkerFlagsVar = "CMAKE_SHARED_LINKER_FLAGS"; | |
| break; | |
| case cm::TargetType::MODULE_LIBRARY: | |
| linkerFlagsVar = "CMAKE_MODULE_LINKER_FLAGS"; | |
| break; | |
| default: | |
| return; | |
| } | |
| this->AddConfigVariableFlags(flags, linkerFlagsVar, target, | |
| cmBuildStep::Link, lang, config); | |
| } | |
| void cmLocalGenerator::AddTargetPropertyLinkFlags( | |
| std::string& flags, cmGeneratorTarget const* target, | |
| std::string const& config) | |
| { | |
| cmValue targetLinkFlags = target->GetProperty("LINK_FLAGS"); | |
| if (targetLinkFlags) { | |
| this->AppendFlags(flags, *targetLinkFlags); | |
| } | |
| if (!config.empty()) { | |
| cmValue targetLinkFlagsConfig = target->GetProperty( | |
| cmStrCat("LINK_FLAGS_", cmSystemTools::UpperCase(config))); | |
| if (targetLinkFlagsConfig) { | |
| this->AppendFlags(flags, *targetLinkFlagsConfig); | |
| } | |
| } | |
| } | |
| void cmLocalGenerator::AppendIPOLinkerFlags(std::string& flags, | |
| cmGeneratorTarget* target, | |
| std::string const& config, | |
| std::string const& lang) | |
| { | |
| if (!target->IsIPOEnabled(lang, config)) { | |
| return; | |
| } | |
| switch (target->GetType()) { | |
| case cm::TargetType::EXECUTABLE: | |
| case cm::TargetType::SHARED_LIBRARY: | |
| case cm::TargetType::MODULE_LIBRARY: | |
| break; | |
| default: | |
| return; | |
| } | |
| std::string const name = cmStrCat("CMAKE_", lang, "_LINK_OPTIONS_IPO"); | |
| cmValue rawFlagsList = this->Makefile->GetDefinition(name); | |
| if (!rawFlagsList) { | |
| return; | |
| } | |
| cmList flagsList{ *rawFlagsList }; | |
| for (std::string const& o : flagsList) { | |
| this->AppendFlagEscape(flags, o); | |
| } | |
| } | |
| void cmLocalGenerator::AppendPositionIndependentLinkerFlags( | |
| std::string& flags, cmGeneratorTarget* target, std::string const& config, | |
| std::string const& lang) | |
| { | |
| // For now, only EXECUTABLE is concerned | |
| if (target->GetType() != cm::TargetType::EXECUTABLE) { | |
| return; | |
| } | |
| char const* PICValue = target->GetLinkPIEProperty(config); | |
| if (!PICValue && lang != "Rust") { | |
| // POSITION_INDEPENDENT_CODE is not set, note that for Rust we do not | |
| // return as the compiler tends to enable PIE all the time, which is the | |
| // opposite of what C & C++ compilers do. So instead of letting the rust | |
| // compiler decide on its own whether PIE should be enabled, we explicit | |
| // set it. | |
| return; | |
| } | |
| std::string const mode = cmIsOn(PICValue) ? "PIE" : "NO_PIE"; | |
| std::string supported = | |
| cmStrCat("CMAKE_", lang, "_LINK_", mode, "_SUPPORTED"); | |
| if (this->Makefile->GetDefinition(supported).IsOff()) { | |
| return; | |
| } | |
| std::string name = cmStrCat("CMAKE_", lang, "_LINK_OPTIONS_", mode); | |
| auto pieFlags = this->Makefile->GetSafeDefinition(name); | |
| if (pieFlags.empty()) { | |
| return; | |
| } | |
| cmList flagsList{ pieFlags }; | |
| for (auto const& flag : flagsList) { | |
| this->AppendFlagEscape(flags, flag); | |
| } | |
| } | |
| void cmLocalGenerator::AppendWarningAsErrorLinkerFlags( | |
| std::string& flags, cmGeneratorTarget* target, std::string const& lang) | |
| { | |
| if (this->GetCMakeInstance()->GetIgnoreLinkWarningAsError()) { | |
| return; | |
| } | |
| switch (target->GetType()) { | |
| case cm::TargetType::EXECUTABLE: | |
| case cm::TargetType::SHARED_LIBRARY: | |
| case cm::TargetType::MODULE_LIBRARY: | |
| break; | |
| default: | |
| return; | |
| } | |
| auto const wError = target->GetProperty("LINK_WARNING_AS_ERROR"); | |
| if (wError.IsOff()) { | |
| return; | |
| } | |
| cmList wErrorOptions; | |
| if (wError.IsOn()) { | |
| wErrorOptions = { "DRIVER", "LINKER" }; | |
| } else { | |
| wErrorOptions = wError; | |
| std::sort(wErrorOptions.begin(), wErrorOptions.end()); | |
| wErrorOptions.erase( | |
| std::unique(wErrorOptions.begin(), wErrorOptions.end()), | |
| wErrorOptions.end()); | |
| } | |
| auto linkModeIsDriver = | |
| this->Makefile->GetDefinition(cmStrCat("CMAKE_", lang, "_LINK_MODE")) == | |
| "DRIVER"_s; | |
| std::string errorMessage; | |
| for (auto const& option : wErrorOptions) { | |
| if (option != "DRIVER"_s && option != "LINKER"_s) { | |
| errorMessage = cmStrCat(std::move(errorMessage), " ", option, '\n'); | |
| continue; | |
| } | |
| if (option == "DRIVER"_s && !linkModeIsDriver) { | |
| continue; | |
| } | |
| auto const wErrorOpts = this->Makefile->GetDefinition(cmStrCat( | |
| "CMAKE_", lang, '_', (option == "DRIVER"_s ? "COMPILE" : "LINK"), | |
| "_OPTIONS_WARNING_AS_ERROR")); | |
| if (wErrorOpts.IsSet()) { | |
| auto items = | |
| cmExpandListWithBacktrace(wErrorOpts, target->GetBacktrace()); | |
| if (option == "LINKER"_s) { | |
| target->ResolveLinkerWrapper(items, lang); | |
| } | |
| for (auto const& item : items) { | |
| this->AppendFlagEscape(flags, item.Value); | |
| } | |
| } | |
| } | |
| if (!errorMessage.empty()) { | |
| this->Makefile->GetCMakeInstance()->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat( | |
| "Erroneous value(s) for 'LINK_WARNING_AS_ERROR' property of target '", | |
| target->GetName(), "':\n", errorMessage)); | |
| } | |
| } | |
| void cmLocalGenerator::AppendDependencyInfoLinkerFlags( | |
| std::string& flags, cmGeneratorTarget* target, std::string const& config, | |
| std::string const& linkLanguage) | |
| { | |
| if (!this->GetGlobalGenerator()->SupportsLinkerDependencyFile() || | |
| !target->HasLinkDependencyFile(config)) { | |
| return; | |
| } | |
| auto depFlag = *this->Makefile->GetDefinition( | |
| cmStrCat("CMAKE_", linkLanguage, "_LINKER_DEPFILE_FLAGS")); | |
| if (depFlag.empty()) { | |
| return; | |
| } | |
| auto depFile = this->ConvertToOutputFormat( | |
| this->MaybeRelativeToWorkDir(this->GetLinkDependencyFile(target, config)), | |
| cmOutputConverter::SHELL); | |
| auto rulePlaceholderExpander = this->CreateRulePlaceholderExpander(); | |
| cmRulePlaceholderExpander::RuleVariables linkDepsVariables; | |
| linkDepsVariables.DependencyFile = depFile.c_str(); | |
| rulePlaceholderExpander->ExpandRuleVariables(this, depFlag, | |
| linkDepsVariables); | |
| auto depFlags = cmExpandListWithBacktrace(depFlag); | |
| target->ResolveLinkerWrapper(depFlags, linkLanguage); | |
| this->AppendFlags(flags, depFlags); | |
| } | |
| std::string cmLocalGenerator::GetLinkDependencyFile( | |
| cmGeneratorTarget* /*target*/, std::string const& /*config*/) const | |
| { | |
| return "link.d"; | |
| } | |
| void cmLocalGenerator::AppendModuleDefinitionFlag( | |
| std::string& flags, cmGeneratorTarget const* target, | |
| cmLinkLineComputer* linkLineComputer, std::string const& config, | |
| std::string const& lang) | |
| { | |
| cmGeneratorTarget::ModuleDefinitionInfo const* mdi = | |
| target->GetModuleDefinitionInfo(config); | |
| if (!mdi || mdi->DefFile.empty()) { | |
| return; | |
| } | |
| cmValue defFileFlag = this->Makefile->GetDefinition( | |
| cmStrCat("CMAKE_", lang, "_LINK_DEF_FILE_FLAG")); | |
| if (!defFileFlag) { | |
| defFileFlag = this->Makefile->GetDefinition("CMAKE_LINK_DEF_FILE_FLAG"); | |
| } | |
| if (!defFileFlag) { | |
| return; | |
| } | |
| // Append the flag and value. Use ConvertToLinkReference to help | |
| // vs6's "cl -link" pass it to the linker. | |
| std::string flag = | |
| cmStrCat(*defFileFlag, | |
| this->ConvertToOutputFormat( | |
| linkLineComputer->ConvertToLinkReference(mdi->DefFile), | |
| cmOutputConverter::SHELL)); | |
| this->AppendFlags(flags, flag); | |
| } | |
| bool cmLocalGenerator::AppendLWYUFlags(std::string& flags, | |
| cmGeneratorTarget const* target, | |
| std::string const& lang) | |
| { | |
| auto useLWYU = target->GetPropertyAsBool("LINK_WHAT_YOU_USE") && | |
| (target->GetType() == cm::TargetType::EXECUTABLE || | |
| target->GetType() == cm::TargetType::SHARED_LIBRARY || | |
| target->GetType() == cm::TargetType::MODULE_LIBRARY); | |
| if (useLWYU) { | |
| auto const& lwyuFlag = this->GetMakefile()->GetSafeDefinition( | |
| cmStrCat("CMAKE_", lang, "_LINK_WHAT_YOU_USE_FLAG")); | |
| useLWYU = !lwyuFlag.empty(); | |
| if (useLWYU) { | |
| std::vector<BT<std::string>> lwyuOpts; | |
| lwyuOpts.emplace_back(lwyuFlag); | |
| this->AppendFlags(flags, target->ResolveLinkerWrapper(lwyuOpts, lang)); | |
| } | |
| } | |
| return useLWYU; | |
| } | |
| void cmLocalGenerator::AppendCompileOptions(std::string& options, | |
| std::string const& options_list, | |
| char const* regex) const | |
| { | |
| // Short-circuit if there are no options. | |
| if (options_list.empty()) { | |
| return; | |
| } | |
| // Expand the list of options. | |
| cmList options_vec{ options_list }; | |
| this->AppendCompileOptions(options, options_vec, regex); | |
| } | |
| void cmLocalGenerator::AppendCompileOptions( | |
| std::string& options, std::vector<std::string> const& options_vec, | |
| char const* regex) const | |
| { | |
| if (regex) { | |
| // Filter flags upon specified reges. | |
| cmsys::RegularExpression r(regex); | |
| for (std::string const& opt : options_vec) { | |
| if (r.find(opt)) { | |
| this->AppendFlagEscape(options, opt); | |
| } | |
| } | |
| } else { | |
| for (std::string const& opt : options_vec) { | |
| this->AppendFlagEscape(options, opt); | |
| } | |
| } | |
| } | |
| void cmLocalGenerator::AppendCompileOptions( | |
| std::vector<BT<std::string>>& options, | |
| std::vector<BT<std::string>> const& options_vec, char const* regex) const | |
| { | |
| if (regex) { | |
| // Filter flags upon specified regular expressions. | |
| cmsys::RegularExpression r(regex); | |
| for (BT<std::string> const& opt : options_vec) { | |
| if (r.find(opt.Value)) { | |
| std::string flag; | |
| this->AppendFlagEscape(flag, opt.Value); | |
| options.emplace_back(std::move(flag), opt.Backtrace); | |
| } | |
| } | |
| } else { | |
| for (BT<std::string> const& opt : options_vec) { | |
| std::string flag; | |
| this->AppendFlagEscape(flag, opt.Value); | |
| options.emplace_back(std::move(flag), opt.Backtrace); | |
| } | |
| } | |
| } | |
| void cmLocalGenerator::AppendIncludeDirectories( | |
| std::vector<std::string>& includes, std::string const& includes_list, | |
| cmSourceFile const& sourceFile) const | |
| { | |
| // Short-circuit if there are no includes. | |
| if (includes_list.empty()) { | |
| return; | |
| } | |
| // Expand the list of includes. | |
| cmList includes_vec{ includes_list }; | |
| this->AppendIncludeDirectories(includes, includes_vec, sourceFile); | |
| } | |
| void cmLocalGenerator::AppendIncludeDirectories( | |
| std::vector<std::string>& includes, | |
| std::vector<std::string> const& includes_vec, | |
| cmSourceFile const& sourceFile) const | |
| { | |
| std::unordered_set<std::string> uniqueIncludes; | |
| for (std::string const& include : includes_vec) { | |
| if (!cmSystemTools::FileIsFullPath(include)) { | |
| this->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("Found relative path while evaluating include directories of " | |
| "\"", | |
| sourceFile.GetLocation().GetName(), "\":\n \"", include, | |
| "\"\n")); | |
| return; | |
| } | |
| std::string inc = include; | |
| if (!cmIsOff(inc)) { | |
| cmSystemTools::ConvertToUnixSlashes(inc); | |
| } | |
| if (uniqueIncludes.insert(inc).second) { | |
| includes.push_back(std::move(inc)); | |
| } | |
| } | |
| } | |
| void cmLocalGenerator::AppendDefines(std::set<std::string>& defines, | |
| std::string const& defines_list) const | |
| { | |
| std::set<BT<std::string>> tmp; | |
| this->AppendDefines(tmp, cmExpandListWithBacktrace(defines_list)); | |
| for (BT<std::string> const& i : tmp) { | |
| defines.emplace(i.Value); | |
| } | |
| } | |
| void cmLocalGenerator::AppendDefines( | |
| std::set<std::string>& defines, | |
| std::vector<BT<std::string>> const& defines_vec) const | |
| { | |
| std::set<BT<std::string>> tmp; | |
| this->AppendDefines(tmp, defines_vec); | |
| for (BT<std::string> const& i : tmp) { | |
| defines.emplace(i.Value); | |
| } | |
| } | |
| void cmLocalGenerator::AppendDefines(std::set<BT<std::string>>& defines, | |
| std::string const& defines_list) const | |
| { | |
| // Short-circuit if there are no definitions. | |
| if (defines_list.empty()) { | |
| return; | |
| } | |
| // Expand the list of definitions. | |
| this->AppendDefines(defines, cmExpandListWithBacktrace(defines_list)); | |
| } | |
| void cmLocalGenerator::AppendDefines( | |
| std::set<BT<std::string>>& defines, | |
| std::vector<BT<std::string>> const& defines_vec) const | |
| { | |
| for (BT<std::string> const& d : defines_vec) { | |
| // Skip unsupported definitions. | |
| if (!this->CheckDefinition(d.Value)) { | |
| continue; | |
| } | |
| // remove any leading -D | |
| if (cmHasLiteralPrefix(d.Value, "-D")) { | |
| defines.emplace(d.Value.substr(2), d.Backtrace); | |
| } else { | |
| defines.insert(d); | |
| } | |
| } | |
| } | |
| void cmLocalGenerator::JoinDefines(std::set<std::string> const& defines, | |
| std::string& definesString, | |
| std::string const& lang) | |
| { | |
| // Lookup the define flag for the current language. | |
| std::string dflag = "-D"; | |
| if (!lang.empty()) { | |
| cmValue df = | |
| this->Makefile->GetDefinition(cmStrCat("CMAKE_", lang, "_DEFINE_FLAG")); | |
| if (cmNonempty(df)) { | |
| dflag = *df; | |
| } | |
| } | |
| char const* itemSeparator = definesString.empty() ? "" : " "; | |
| for (std::string const& define : defines) { | |
| // Append the definition with proper escaping. | |
| std::string def = dflag; | |
| if (this->GetState()->UseWatcomWMake()) { | |
| // The Watcom compiler does its own command line parsing instead | |
| // of using the windows shell rules. Definitions are one of | |
| // -DNAME | |
| // -DNAME=<cpp-token> | |
| // -DNAME="c-string with spaces and other characters(?@#$)" | |
| // | |
| // Watcom will properly parse each of these cases from the | |
| // command line without any escapes. However we still have to | |
| // get the '$' and '#' characters through WMake as '$$' and | |
| // '$#'. | |
| for (char c : define) { | |
| if (c == '$' || c == '#') { | |
| def += '$'; | |
| } | |
| def += c; | |
| } | |
| } else { | |
| // Make the definition appear properly on the command line. Use | |
| // -DNAME="value" instead of -D"NAME=value" for historical reasons. | |
| std::string::size_type eq = define.find('='); | |
| def += define.substr(0, eq); | |
| if (eq != std::string::npos) { | |
| def += "="; | |
| def += this->EscapeForShell(define.substr(eq + 1), true); | |
| } | |
| } | |
| definesString += itemSeparator; | |
| itemSeparator = " "; | |
| definesString += def; | |
| } | |
| } | |
| void cmLocalGenerator::AppendFeatureOptions(std::string& flags, | |
| std::string const& lang, | |
| char const* feature) | |
| { | |
| cmValue optionList = this->Makefile->GetDefinition( | |
| cmStrCat("CMAKE_", lang, "_COMPILE_OPTIONS_", feature)); | |
| if (optionList) { | |
| cmList options{ *optionList }; | |
| for (std::string const& o : options) { | |
| this->AppendFlagEscape(flags, o); | |
| } | |
| } | |
| } | |
| cmValue cmLocalGenerator::GetFeature(std::string const& feature, | |
| std::string const& config) | |
| { | |
| std::string featureName = feature; | |
| // TODO: Define accumulation policy for features (prepend, append, | |
| // replace). Currently we always replace. | |
| if (!config.empty()) { | |
| featureName += "_"; | |
| featureName += cmSystemTools::UpperCase(config); | |
| } | |
| cmStateSnapshot snp = this->StateSnapshot; | |
| while (snp.IsValid()) { | |
| if (cmValue value = snp.GetDirectory().GetProperty(featureName)) { | |
| return value; | |
| } | |
| snp = snp.GetBuildsystemDirectoryParent(); | |
| } | |
| return nullptr; | |
| } | |
| std::string cmLocalGenerator::GetProjectName() const | |
| { | |
| return this->StateSnapshot.GetProjectName(); | |
| } | |
| std::string cmLocalGenerator::ConstructComment( | |
| cmCustomCommandGenerator const& ccg, char const* default_comment) const | |
| { | |
| // Check for a comment provided with the command. | |
| if (cm::optional<std::string> comment = ccg.GetComment()) { | |
| return *comment; | |
| } | |
| // Construct a reasonable default comment if possible. | |
| if (!ccg.GetOutputs().empty()) { | |
| std::string comment; | |
| comment = "Generating "; | |
| char const* sep = ""; | |
| for (std::string const& o : ccg.GetOutputs()) { | |
| comment += sep; | |
| comment += this->MaybeRelativeToCurBinDir(o); | |
| sep = ", "; | |
| } | |
| return comment; | |
| } | |
| // Otherwise use the provided default. | |
| return default_comment; | |
| } | |
| class cmInstallTargetGeneratorLocal : public cmInstallTargetGenerator | |
| { | |
| public: | |
| cmInstallTargetGeneratorLocal(cmLocalGenerator* lg, std::string const& t, | |
| std::string const& dest, bool implib) | |
| : cmInstallTargetGenerator( | |
| t, dest, implib, "", std::vector<std::string>(), "Unspecified", | |
| cmInstallGenerator::SelectMessageLevel(lg->GetMakefile()), false, | |
| false) | |
| { | |
| this->Compute(lg); | |
| } | |
| }; | |
| void cmLocalGenerator::GenerateTargetInstallRules( | |
| std::ostream& os, std::string const& config, | |
| std::vector<std::string> const& configurationTypes) | |
| { | |
| // Convert the old-style install specification from each target to | |
| // an install generator and run it. | |
| auto const& tgts = this->GetGeneratorTargets(); | |
| for (auto const& l : tgts) { | |
| if (l->GetType() == cm::TargetType::INTERFACE_LIBRARY) { | |
| continue; | |
| } | |
| // Include the user-specified pre-install script for this target. | |
| if (cmValue preinstall = l->GetProperty("PRE_INSTALL_SCRIPT")) { | |
| cmInstallScriptGenerator g(*preinstall, false, "", false, false); | |
| g.Generate(os, config, configurationTypes); | |
| } | |
| // Install this target if a destination is given. | |
| if (!l->Target->GetInstallPath().empty()) { | |
| // Compute the full install destination. Note that converting | |
| // to unix slashes also removes any trailing slash. | |
| // We also skip over the leading slash given by the user. | |
| std::string destination = l->Target->GetInstallPath().substr(1); | |
| cmSystemTools::ConvertToUnixSlashes(destination); | |
| if (destination.empty()) { | |
| destination = "."; | |
| } | |
| // Generate the proper install generator for this target type. | |
| switch (l->GetType()) { | |
| case cm::TargetType::EXECUTABLE: | |
| case cm::TargetType::STATIC_LIBRARY: | |
| case cm::TargetType::MODULE_LIBRARY: { | |
| // Use a target install generator. | |
| cmInstallTargetGeneratorLocal g(this, l->GetName(), destination, | |
| false); | |
| g.Generate(os, config, configurationTypes); | |
| } break; | |
| case cm::TargetType::SHARED_LIBRARY: { | |
| #if defined(_WIN32) || defined(__CYGWIN__) | |
| // Special code to handle DLL. Install the import library | |
| // to the normal destination and the DLL to the runtime | |
| // destination. | |
| cmInstallTargetGeneratorLocal g1(this, l->GetName(), destination, | |
| true); | |
| g1.Generate(os, config, configurationTypes); | |
| // We also skip over the leading slash given by the user. | |
| destination = l->Target->GetRuntimeInstallPath().substr(1); | |
| cmSystemTools::ConvertToUnixSlashes(destination); | |
| cmInstallTargetGeneratorLocal g2(this, l->GetName(), destination, | |
| false); | |
| g2.Generate(os, config, configurationTypes); | |
| #else | |
| // Use a target install generator. | |
| cmInstallTargetGeneratorLocal g(this, l->GetName(), destination, | |
| false); | |
| g.Generate(os, config, configurationTypes); | |
| #endif | |
| } break; | |
| default: | |
| break; | |
| } | |
| } | |
| // Include the user-specified post-install script for this target. | |
| if (cmValue postinstall = l->GetProperty("POST_INSTALL_SCRIPT")) { | |
| cmInstallScriptGenerator g(*postinstall, false, "", false, false); | |
| g.Generate(os, config, configurationTypes); | |
| } | |
| } | |
| } | |
| namespace { | |
| bool cmLocalGeneratorShortenObjectName(std::string& objName, | |
| std::string::size_type max_len) | |
| { | |
| // Check if the path can be shortened using an md5 sum replacement for | |
| // a portion of the path. | |
| std::string::size_type md5Len = 32; | |
| std::string::size_type numExtraChars = objName.size() - max_len + md5Len; | |
| std::string::size_type pos = objName.find('/', numExtraChars); | |
| if (pos == std::string::npos) { | |
| pos = objName.rfind('/', numExtraChars); | |
| if (pos == std::string::npos || pos <= md5Len) { | |
| return false; | |
| } | |
| } | |
| // Replace the beginning of the path portion of the object name with | |
| // its own md5 sum. | |
| cmCryptoHash md5(cmCryptoHash::AlgoMD5); | |
| std::string md5name = cmStrCat(md5.HashString(objName.substr(0, pos)), | |
| cm::string_view(objName).substr(pos)); | |
| objName = md5name; | |
| // The object name is now shorter, check if it is short enough. | |
| return pos >= numExtraChars; | |
| } | |
| bool cmLocalGeneratorCheckObjectName(std::string& objName, | |
| std::string::size_type dir_len, | |
| std::string::size_type max_total_len) | |
| { | |
| // Enforce the maximum file name length if possible. | |
| std::string::size_type max_obj_len = max_total_len; | |
| if (dir_len < max_total_len) { | |
| max_obj_len = max_total_len - dir_len; | |
| if (objName.size() > max_obj_len) { | |
| // The current object file name is too long. Try to shorten it. | |
| return cmLocalGeneratorShortenObjectName(objName, max_obj_len); | |
| } | |
| // The object file name is short enough. | |
| return true; | |
| } | |
| // The build directory in which the object will be stored is | |
| // already too deep. | |
| return false; | |
| } | |
| } | |
| std::string cmLocalGenerator::CreateSafeObjectFileName( | |
| std::string const& sin) const | |
| { | |
| // Start with the original name. | |
| std::string ssin = sin; | |
| // Avoid full paths by removing leading slashes. | |
| ssin.erase(0, ssin.find_first_not_of('/')); | |
| // Avoid full paths by removing colons. | |
| std::replace(ssin.begin(), ssin.end(), ':', '_'); | |
| // Avoid relative paths that go up the tree. | |
| cmSystemTools::ReplaceString(ssin, "../", "__/"); | |
| // Avoid spaces. | |
| std::replace(ssin.begin(), ssin.end(), ' ', '_'); | |
| return ssin; | |
| } | |
| // search a the source group defined for the file set or the source itself | |
| // in case of match, result is stored in a cache to speed-up future searches | |
| cmSourceGroup* cmLocalGenerator::FindSourceGroup( | |
| cmGeneratorTarget const* target, cmSourceFile const* source, | |
| std::string const& config) | |
| { | |
| #if !defined(CMAKE_BOOTSTRAP) | |
| std::string const SOURCE_GROUP{ "SOURCE_GROUP" }; | |
| std::string const& targetName = target->GetName(); | |
| cmGeneratorFileSet const* fileSet = | |
| target->GetFileSetForSource(config, source); | |
| if (fileSet) { | |
| std::string fsName = fileSet->GetName(); | |
| std::string const fsKey = cmStrCat(targetName, "::", fsName); | |
| auto const indexIt = this->SourceGroupSearchIndex.find(fsKey); | |
| if (indexIt != this->SourceGroupSearchIndex.cend()) { | |
| return indexIt->second; | |
| } | |
| cmValue fsSG = fileSet->GetProperty(SOURCE_GROUP); | |
| if (!fsSG.IsEmpty()) { | |
| cmSourceGroup* sg = this->GetMakefile()->GetOrCreateSourceGroup(*fsSG); | |
| this->SourceGroupSearchIndex.emplace(fsKey, sg); | |
| return sg; | |
| } | |
| cmSourceGroup* sourceGroup = cmSourceGroup::FindSourceGroup( | |
| targetName, fsName, this->Makefile->GetSourceGroups()); | |
| if (sourceGroup) { | |
| this->SourceGroupSearchIndex.emplace(fsKey, sourceGroup); | |
| return sourceGroup; | |
| } | |
| } | |
| // no source group defined for the file set or the source is not part of a | |
| // file set. | |
| // search for source group defined at source level | |
| auto const indexIt = | |
| this->SourceGroupSearchIndex.find(source->GetFullPath()); | |
| if (indexIt != this->SourceGroupSearchIndex.cend()) { | |
| return indexIt->second; | |
| } | |
| cmValue srcSG = source->GetProperty(SOURCE_GROUP); | |
| if (!srcSG.IsEmpty()) { | |
| cmSourceGroup* sg = this->GetMakefile()->GetOrCreateSourceGroup(*srcSG); | |
| this->SourceGroupSearchIndex.emplace(source->GetFullPath(), sg); | |
| return sg; | |
| } | |
| // look-up in source groups definitions | |
| return this->FindSourceGroup(source->GetFullPath()); | |
| #else | |
| static_cast<void>(target); | |
| static_cast<void>(source); | |
| static_cast<void>(config); | |
| return nullptr; | |
| #endif | |
| } | |
| cmSourceGroup* cmLocalGenerator::FindSourceGroup(std::string const& source) | |
| { | |
| #if !defined(CMAKE_BOOTSTRAP) | |
| auto const indexIt = SourceGroupSearchIndex.find(source); | |
| if (indexIt != SourceGroupSearchIndex.cend()) { | |
| if (cmSourceGroup* result = indexIt->second) { | |
| return result; | |
| } | |
| } | |
| cmSourceGroup* sourceGroup = | |
| cmSourceGroup::FindSourceGroup(source, this->Makefile->GetSourceGroups()); | |
| if (sourceGroup) { | |
| // Update index if we have a miss | |
| SourceGroupSearchIndex.emplace(source, sourceGroup); | |
| } | |
| return sourceGroup; | |
| #else | |
| static_cast<void>(source); | |
| return nullptr; | |
| #endif | |
| } | |
| std::string& cmLocalGenerator::CreateSafeUniqueObjectFileName( | |
| std::string const& sin, std::string const& dir_max) | |
| { | |
| // Look for an existing mapped name for this object file. | |
| auto it = this->UniqueObjectNamesMap.find(sin); | |
| // If no entry exists create one. | |
| if (it == this->UniqueObjectNamesMap.end()) { | |
| auto ssin = this->CreateSafeObjectFileName(sin); | |
| // Mangle the name if necessary. | |
| if (this->Makefile->IsOn("CMAKE_MANGLE_OBJECT_FILE_NAMES")) { | |
| bool done; | |
| int cc = 0; | |
| char rpstr[100]; | |
| snprintf(rpstr, sizeof(rpstr), "_p_"); | |
| cmSystemTools::ReplaceString(ssin, "+", rpstr); | |
| std::string sssin = sin; | |
| do { | |
| done = true; | |
| for (it = this->UniqueObjectNamesMap.begin(); | |
| it != this->UniqueObjectNamesMap.end(); ++it) { | |
| if (it->second == ssin) { | |
| done = false; | |
| } | |
| } | |
| if (done) { | |
| break; | |
| } | |
| sssin = ssin; | |
| cmSystemTools::ReplaceString(ssin, "_p_", rpstr); | |
| snprintf(rpstr, sizeof(rpstr), "_p%d_", cc++); | |
| } while (!done); | |
| } | |
| if (!cmLocalGeneratorCheckObjectName(ssin, dir_max.size(), | |
| this->ObjectPathMax)) { | |
| // Warn if this is the first time the path has been seen. | |
| if (this->ObjectMaxPathViolations.insert(dir_max).second) { | |
| this->IssueMessage( | |
| MessageType::WARNING, | |
| cmStrCat("The object file directory\n" | |
| " ", | |
| dir_max, | |
| "\n" | |
| "has ", | |
| dir_max.size(), | |
| " characters. " | |
| "The maximum full path to an object file is ", | |
| this->ObjectPathMax, | |
| " characters " | |
| "(see CMAKE_OBJECT_PATH_MAX). " | |
| "Object file\n" | |
| " ", | |
| ssin, | |
| "\n" | |
| "cannot be safely placed under this directory. " | |
| "The build may not work correctly.")); | |
| } | |
| } | |
| // Insert the newly mapped object file name. | |
| std::map<std::string, std::string>::value_type e(sin, ssin); | |
| it = this->UniqueObjectNamesMap.insert(e).first; | |
| } | |
| // Return the map entry. | |
| return it->second; | |
| } | |
| void cmLocalGenerator::ComputeObjectFilenames( | |
| std::map<cmSourceFile const*, cmObjectLocations>& /*unused*/, | |
| std::string const& /*unused*/, cmGeneratorTarget const* /*unused*/) | |
| { | |
| } | |
| bool cmLocalGenerator::IsWindowsShell() const | |
| { | |
| return this->GetState()->UseWindowsShell(); | |
| } | |
| bool cmLocalGenerator::IsWatcomWMake() const | |
| { | |
| return this->GetState()->UseWatcomWMake(); | |
| } | |
| bool cmLocalGenerator::IsMinGWMake() const | |
| { | |
| return this->GetState()->UseMinGWMake(); | |
| } | |
| bool cmLocalGenerator::IsNMake() const | |
| { | |
| return this->GetState()->UseNMake(); | |
| } | |
| bool cmLocalGenerator::IsNinjaMulti() const | |
| { | |
| return this->GetState()->UseNinjaMulti(); | |
| } | |
| bool cmLocalGenerator::IsWindowsVSIDE() const | |
| { | |
| return this->GetState()->UseWindowsVSIDE(); | |
| } | |
| namespace { | |
| std::string relativeIfUnder(std::string const& top, std::string const& cur, | |
| std::string const& path) | |
| { | |
| // Use a path relative to 'cur' if it can be expressed without | |
| // a `../` sequence that leaves 'top'. | |
| if (cmSystemTools::IsSubDirectory(path, cur) || | |
| (cmSystemTools::IsSubDirectory(cur, top) && | |
| cmSystemTools::IsSubDirectory(path, top))) { | |
| return cmSystemTools::ForceToRelativePath(cur, path); | |
| } | |
| return path; | |
| } | |
| } | |
| std::string cmLocalGenerator::GetRelativeSourceFileName( | |
| cmSourceFile const& source) const | |
| { | |
| // Construct the object file name using the full path to the source | |
| // file which is its only unique identification. | |
| std::string const& fullPath = source.GetFullPath(); | |
| // Try referencing the source relative to the source tree. | |
| std::string relFromSource = relativeIfUnder( | |
| this->GetSourceDirectory(), this->GetCurrentSourceDirectory(), fullPath); | |
| assert(!relFromSource.empty()); | |
| bool relSource = !cmSystemTools::FileIsFullPath(relFromSource); | |
| bool subSource = relSource && relFromSource[0] != '.'; | |
| // Try referencing the source relative to the binary tree. | |
| std::string relFromBinary = relativeIfUnder( | |
| this->GetBinaryDirectory(), this->GetCurrentBinaryDirectory(), fullPath); | |
| assert(!relFromBinary.empty()); | |
| bool relBinary = !cmSystemTools::FileIsFullPath(relFromBinary); | |
| bool subBinary = relBinary && relFromBinary[0] != '.'; | |
| // Select a nice-looking reference to the source file to construct | |
| // the object file name. | |
| std::string objectName; | |
| // XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165 | |
| // NOLINTNEXTLINE(bugprone-branch-clone) | |
| if ((relSource && !relBinary) || (subSource && !subBinary)) { | |
| objectName = relFromSource; | |
| } else if ((relBinary && !relSource) || (subBinary && !subSource) || | |
| relFromBinary.length() < relFromSource.length()) { | |
| objectName = relFromBinary; | |
| } else { | |
| objectName = relFromSource; | |
| } | |
| return objectName; | |
| } | |
| std::string cmLocalGenerator::GetCustomObjectFileName( | |
| cmSourceFile const& source) const | |
| { | |
| if (!this->GetGlobalGenerator()->SupportsCustomObjectNames()) { | |
| return std::string{}; | |
| } | |
| if (auto objName = source.GetProperty("OBJECT_NAME")) { | |
| cmGeneratorExpression ge(*this->GetCMakeInstance()); | |
| auto cge = ge.Parse(objName); | |
| static std::string const INVALID_GENEX = | |
| "_cmake_invalid_object_name_genex"; | |
| static std::string const INVALID_VALUE = | |
| "_cmake_invalid_object_name_value"; | |
| if (!cge) { | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("The \"OBJECT_NAME\" property for\n ", source.GetFullPath(), | |
| "\nis not a valid generator expression (", objName, ").")); | |
| return INVALID_GENEX; | |
| } | |
| if (cge->GetHadHeadSensitiveCondition()) { | |
| // Not reachable; all target-sensitive genexes actually fail to parse. | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("The \"OBJECT_NAME\" property for\n ", source.GetFullPath(), | |
| "\ncontains a condition that queries the consuming target " | |
| "which is not supported (", | |
| objName, ").")); | |
| return INVALID_GENEX; | |
| } | |
| if (cge->GetHadLinkLanguageSensitiveCondition()) { | |
| // Not reachable; all target-sensitive genexes actually fail to parse. | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("The \"OBJECT_NAME\" property for\n ", source.GetFullPath(), | |
| "\ncontains a condition that queries the link language " | |
| "which is not supported (", | |
| objName, ").")); | |
| return INVALID_GENEX; | |
| } | |
| auto objNameValue = cge->Evaluate(this, ""); | |
| if (cge->GetHadContextSensitiveCondition()) { | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("The \"OBJECT_NAME\" property for\n ", source.GetFullPath(), | |
| "\ncontains a context-sensitive condition which is not " | |
| "supported (", | |
| objName, ").")); | |
| return INVALID_GENEX; | |
| } | |
| // Skip if it evaluates to empty. | |
| if (!objNameValue.empty()) { | |
| cmCMakePath objNamePath = objNameValue; | |
| // Verify that it is a relative path. | |
| if (objNamePath.IsAbsolute()) { | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat( | |
| "The \"OBJECT_NAME\" property for\n ", source.GetFullPath(), | |
| "\nresolves to an absolute path which is not supported:\n ", | |
| objNameValue)); | |
| return INVALID_VALUE; | |
| } | |
| auto isInvalidComponent = [](cmCMakePath const& component) -> bool { | |
| return component == ".."_s; | |
| }; | |
| // Verify that it contains no `..` components. | |
| if (std::any_of(objNamePath.begin(), objNamePath.end(), | |
| isInvalidComponent)) { | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("The \"OBJECT_NAME\" property for\n ", | |
| source.GetFullPath(), "\ncontains an invalid component (", | |
| objNameValue, ").")); | |
| return INVALID_VALUE; | |
| } | |
| return objNameValue; | |
| } | |
| } | |
| return std::string{}; | |
| } | |
| std::string cmLocalGenerator::GetCustomInstallObjectFileName( | |
| cmSourceFile const& source, std::string const& config, | |
| char const* custom_ext) const | |
| { | |
| if (auto objName = source.GetProperty("INSTALL_OBJECT_NAME")) { | |
| cmGeneratorExpression ge(*this->GetCMakeInstance()); | |
| auto cge = ge.Parse(objName); | |
| static std::string const INVALID_GENEX = | |
| "_cmake_invalid_object_name_genex"; | |
| static std::string const INVALID_VALUE = | |
| "_cmake_invalid_object_name_value"; | |
| if (!cge) { | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("The \"INSTALL_OBJECT_NAME\" property for\n ", | |
| source.GetFullPath(), | |
| "\nis not a valid generator expression (", objName, ").")); | |
| return INVALID_GENEX; | |
| } | |
| if (cge->GetHadHeadSensitiveCondition()) { | |
| // Not reachable; all target-sensitive genexes actually fail to parse. | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("The \"INSTALL_OBJECT_NAME\" property for\n ", | |
| source.GetFullPath(), | |
| "\ncontains a condition that queries the consuming target " | |
| "which is not supported (", | |
| objName, ").")); | |
| return INVALID_GENEX; | |
| } | |
| if (cge->GetHadLinkLanguageSensitiveCondition()) { | |
| // Not reachable; all target-sensitive genexes actually fail to parse. | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("The \"INSTALL_OBJECT_NAME\" property for\n ", | |
| source.GetFullPath(), | |
| "\ncontains a condition that queries the link language " | |
| "which is not supported (", | |
| objName, ").")); | |
| return INVALID_GENEX; | |
| } | |
| auto objNameValue = cge->Evaluate(this, config); | |
| // Skip if it evaluates to empty. | |
| if (!objNameValue.empty()) { | |
| cmCMakePath objNamePath = objNameValue; | |
| // Verify that it is a relative path. | |
| if (objNamePath.IsAbsolute()) { | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat( | |
| "The \"INSTALL_OBJECT_NAME\" property for\n ", | |
| source.GetFullPath(), | |
| "\nresolves to an absolute path which is not supported:\n ", | |
| objNameValue)); | |
| return INVALID_VALUE; | |
| } | |
| auto isInvalidComponent = [](cmCMakePath const& component) -> bool { | |
| return component == ".."_s; | |
| }; | |
| // Verify that it contains no `..` components. | |
| if (std::any_of(objNamePath.begin(), objNamePath.end(), | |
| isInvalidComponent)) { | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("The \"INSTALL_OBJECT_NAME\" property for\n ", | |
| source.GetFullPath(), "\ncontains an invalid component (", | |
| objNameValue, ").")); | |
| return INVALID_VALUE; | |
| } | |
| if (custom_ext) { | |
| objNameValue += custom_ext; | |
| } else { | |
| objNameValue += | |
| this->GetGlobalGenerator()->GetLanguageOutputExtension(source); | |
| } | |
| return objNameValue; | |
| } | |
| } | |
| return std::string{}; | |
| } | |
| void cmLocalGenerator::FillCustomInstallObjectLocations( | |
| cmSourceFile const& source, std::string const& config, | |
| char const* custom_ext, | |
| std::map<std::string, cmObjectLocation>& mapping) const | |
| { | |
| auto installLoc = | |
| this->GetCustomInstallObjectFileName(source, config, custom_ext); | |
| if (!installLoc.empty()) { | |
| mapping[config] = installLoc; | |
| } | |
| } | |
| std::string cmLocalGenerator::GetObjectFileNameWithoutTarget( | |
| cmSourceFile const& source, std::string const& dir_max, | |
| bool* hasSourceExtension, char const* customOutputExtension, | |
| bool const* forceShortObjectName) | |
| { | |
| bool useShortObjectNames = this->UseShortObjectNames(); | |
| if (forceShortObjectName) { | |
| useShortObjectNames = *forceShortObjectName; | |
| } | |
| if (!useShortObjectNames && | |
| this->GetGlobalGenerator()->SupportsCustomObjectNames()) { | |
| auto customName = this->GetCustomObjectFileName(source); | |
| if (!customName.empty()) { | |
| auto ext = this->GlobalGenerator->GetLanguageOutputExtension(source); | |
| if (customOutputExtension) { | |
| ext = *customOutputExtension; | |
| } | |
| return cmStrCat(customName, ext); | |
| } | |
| } | |
| // This can return an absolute path in the case where source is | |
| // not relative to the current source or binary directories | |
| std::string objectName = this->GetRelativeSourceFileName(source); | |
| // if it is still a full path check for the try compile case | |
| // try compile never have in source sources, and should not | |
| // have conflicting source file names in the same target | |
| if (cmSystemTools::FileIsFullPath(objectName)) { | |
| if (this->GetGlobalGenerator()->GetCMakeInstance()->GetIsInTryCompile()) { | |
| objectName = cmSystemTools::GetFilenameName(source.GetFullPath()); | |
| } | |
| } | |
| bool const isPchObject = source.IsPchHeader() || source.IsPchSource(); | |
| // Short object path policy selected, use as little info as necessary to | |
| // select an object name | |
| bool keptSourceExtension = true; | |
| if (useShortObjectNames) { | |
| objectName = this->GetShortObjectFileName(source); | |
| keptSourceExtension = false; | |
| } | |
| // Ensure that for the CMakeFiles/<target>.dir/generated_source_file | |
| // we don't end up having: | |
| // CMakeFiles/<target>.dir/CMakeFiles/<target>.dir/generated_source_file.obj | |
| cmValue unitySourceFile = source.GetProperty("UNITY_SOURCE_FILE"); | |
| cmValue pchExtension = source.GetProperty("PCH_EXTENSION"); | |
| if (unitySourceFile || pchExtension || isPchObject) { | |
| if (pchExtension) { | |
| customOutputExtension = pchExtension->c_str(); | |
| } | |
| cmsys::RegularExpression var("(CMakeFiles/[^/]+.dir/)"); | |
| if (var.find(objectName)) { | |
| objectName.erase(var.start(), var.end() - var.start()); | |
| } | |
| } | |
| // Replace the original source file extension with the object file | |
| // extension. | |
| if (!source.GetPropertyAsBool("KEEP_EXTENSION")) { | |
| // Decide whether this language wants to replace the source | |
| // extension with the object extension. | |
| bool replaceExt = false; | |
| std::string lang = source.GetLanguage(); | |
| if (!lang.empty()) { | |
| replaceExt = this->Makefile->IsOn( | |
| cmStrCat("CMAKE_", lang, "_OUTPUT_EXTENSION_REPLACE")); | |
| } | |
| // Remove the source extension if it is to be replaced. | |
| if (replaceExt || customOutputExtension) { | |
| keptSourceExtension = false; | |
| std::string::size_type dot_pos = objectName.rfind('.'); | |
| if (dot_pos != std::string::npos) { | |
| objectName = objectName.substr(0, dot_pos); | |
| } | |
| } | |
| // Strip source file extension when shortening object file paths | |
| if (useShortObjectNames) { | |
| objectName = cmSystemTools::GetFilenameWithoutExtension(objectName); | |
| } | |
| // Store the new extension. | |
| if (customOutputExtension) { | |
| objectName += customOutputExtension; | |
| } else { | |
| objectName += this->GlobalGenerator->GetLanguageOutputExtension(source); | |
| } | |
| } | |
| if (hasSourceExtension) { | |
| *hasSourceExtension = keptSourceExtension; | |
| } | |
| if (source.GetLanguage() == "Rust") { | |
| cmValue const rustEmit = source.GetRustEmitProperty(); | |
| // Rust requires any rlib to start with lib prefix on all platforms to | |
| // allow linking to them as crate. So we enforce having lib prefix for rust | |
| // "object" files. | |
| if (rustEmit == "link") { | |
| cmCMakePath objectPath(objectName); | |
| std::string const objectFileName = | |
| "lib" + objectPath.GetFileName().String(); | |
| objectPath.ReplaceFileName(objectFileName); | |
| objectName = objectPath.String(); | |
| } | |
| } | |
| // Convert to a safe name. | |
| return this->CreateSafeUniqueObjectFileName(objectName, dir_max); | |
| } | |
| bool cmLocalGenerator::UseShortObjectNames( | |
| cmStateEnums::IntermediateDirKind kind) const | |
| { | |
| return this->GlobalGenerator->UseShortObjectNames(kind); | |
| } | |
| std::string cmLocalGenerator::GetObjectOutputRoot( | |
| cmStateEnums::IntermediateDirKind kind) const | |
| { | |
| if (this->UseShortObjectNames(kind)) { | |
| return cmStrCat(this->GetCurrentBinaryDirectory(), '/', | |
| this->GlobalGenerator->GetShortBinaryOutputDir()); | |
| } | |
| return cmStrCat(this->GetCurrentBinaryDirectory(), "/CMakeFiles"); | |
| } | |
| bool cmLocalGenerator::AlwaysUsesCMFPaths() const | |
| { | |
| return true; | |
| } | |
| std::string cmLocalGenerator::GetShortObjectFileName( | |
| cmSourceFile const& source) const | |
| { | |
| std::string objectName = this->GetRelativeSourceFileName(source); | |
| cmCryptoHash objNameHasher(cmCryptoHash::AlgoSHA3_512); | |
| std::string terseObjectName = | |
| objNameHasher.HashString(objectName).substr(0, 8); | |
| return terseObjectName; | |
| } | |
| std::string cmLocalGenerator::ComputeShortTargetDirectory( | |
| cmGeneratorTarget const* target) const | |
| { | |
| auto const& tgtName = target->GetName(); | |
| return this->GetGlobalGenerator()->ComputeTargetShortName( | |
| this->GetCurrentBinaryDirectory(), tgtName); | |
| } | |
| std::string cmLocalGenerator::GetSourceFileLanguage(cmSourceFile const& source) | |
| { | |
| return source.GetLanguage(); | |
| } | |
| cmake* cmLocalGenerator::GetCMakeInstance() const | |
| { | |
| return this->GlobalGenerator->GetCMakeInstance(); | |
| } | |
| std::string const& cmLocalGenerator::GetSourceDirectory() const | |
| { | |
| return this->GetCMakeInstance()->GetHomeDirectory(); | |
| } | |
| std::string const& cmLocalGenerator::GetBinaryDirectory() const | |
| { | |
| return this->GetCMakeInstance()->GetHomeOutputDirectory(); | |
| } | |
| std::string const& cmLocalGenerator::GetCurrentBinaryDirectory() const | |
| { | |
| return this->StateSnapshot.GetDirectory().GetCurrentBinary(); | |
| } | |
| std::string const& cmLocalGenerator::GetCurrentSourceDirectory() const | |
| { | |
| return this->StateSnapshot.GetDirectory().GetCurrentSource(); | |
| } | |
| std::string cmLocalGenerator::GetTargetDirectory( | |
| cmGeneratorTarget const* /*unused*/, | |
| cmStateEnums::IntermediateDirKind /*kind*/) const | |
| { | |
| cmSystemTools::Error("GetTargetDirectory" | |
| " called on cmLocalGenerator"); | |
| return ""; | |
| } | |
| cmPolicies::PolicyStatus cmLocalGenerator::GetPolicyStatus( | |
| cmPolicies::PolicyID id) const | |
| { | |
| return this->Makefile->GetPolicyStatus(id); | |
| } | |
| bool cmLocalGenerator::CheckDefinition(std::string const& define) const | |
| { | |
| // Many compilers do not support -DNAME(arg)=sdf so we disable it. | |
| std::string::size_type pos = define.find_first_of("(="); | |
| if (pos != std::string::npos) { | |
| if (define[pos] == '(') { | |
| cmSystemTools::Message(cmStrCat( | |
| "WARNING: Function-style preprocessor definitions may not be " | |
| "passed on the compiler command line because many compilers " | |
| "do not support it.\n" | |
| "CMake is dropping a preprocessor definition: ", | |
| define, | |
| "\n" | |
| "Consider defining the macro in a (configured) header file.\n")); | |
| return false; | |
| } | |
| } | |
| // Many compilers do not support # in the value so we disable it. | |
| if (define.find_first_of('#') != std::string::npos) { | |
| cmSystemTools::Message(cmStrCat( | |
| "WARNING: Preprocessor definitions containing '#' may not be " | |
| "passed on the compiler command line because many compilers " | |
| "do not support it.\n" | |
| "CMake is dropping a preprocessor definition: ", | |
| define, | |
| "\n" | |
| "Consider defining the macro in a (configured) header file.\n")); | |
| return false; | |
| } | |
| // Assume it is supported. | |
| return true; | |
| } | |
| static void cmLGInfoProp(cmMakefile* mf, cmGeneratorTarget* target, | |
| std::string const& prop) | |
| { | |
| if (cmValue val = target->GetProperty(prop)) { | |
| mf->AddDefinition(prop, *val); | |
| } | |
| } | |
| void cmLocalGenerator::GenerateAppleInfoPList(cmGeneratorTarget* target, | |
| std::string const& targetName, | |
| std::string const& fname) | |
| { | |
| // Find the Info.plist template. | |
| cmValue in = target->GetProperty("MACOSX_BUNDLE_INFO_PLIST"); | |
| std::string inFile = cmNonempty(in) ? *in : "MacOSXBundleInfo.plist.in"; | |
| if (!cmSystemTools::FileIsFullPath(inFile)) { | |
| std::string inMod = this->Makefile->GetModulesFile(inFile); | |
| if (!inMod.empty()) { | |
| inFile = inMod; | |
| } | |
| } | |
| if (!cmSystemTools::FileExists(inFile, true)) { | |
| cmSystemTools::Error(cmStrCat("Target ", target->GetName(), | |
| " Info.plist template \"", inFile, | |
| "\" could not be found.")); | |
| return; | |
| } | |
| // Convert target properties to variables in an isolated makefile | |
| // scope to configure the file. If properties are set they will | |
| // override user make variables. If not the configuration will fall | |
| // back to the directory-level values set by the user. | |
| cmMakefile* mf = this->Makefile; | |
| cmMakefile::ScopePushPop varScope(mf); | |
| mf->AddDefinition("MACOSX_BUNDLE_EXECUTABLE_NAME", targetName); | |
| cmLGInfoProp(mf, target, "MACOSX_BUNDLE_INFO_STRING"); | |
| cmLGInfoProp(mf, target, "MACOSX_BUNDLE_ICON_FILE"); | |
| cmLGInfoProp(mf, target, "MACOSX_BUNDLE_GUI_IDENTIFIER"); | |
| cmLGInfoProp(mf, target, "MACOSX_BUNDLE_LONG_VERSION_STRING"); | |
| cmLGInfoProp(mf, target, "MACOSX_BUNDLE_BUNDLE_NAME"); | |
| cmLGInfoProp(mf, target, "MACOSX_BUNDLE_SHORT_VERSION_STRING"); | |
| cmLGInfoProp(mf, target, "MACOSX_BUNDLE_BUNDLE_VERSION"); | |
| cmLGInfoProp(mf, target, "MACOSX_BUNDLE_COPYRIGHT"); | |
| mf->ConfigureFile(inFile, fname, false, false, false); | |
| } | |
| void cmLocalGenerator::GenerateFrameworkInfoPList( | |
| cmGeneratorTarget* target, std::string const& targetName, | |
| std::string const& fname) | |
| { | |
| // Find the Info.plist template. | |
| cmValue in = target->GetProperty("MACOSX_FRAMEWORK_INFO_PLIST"); | |
| std::string inFile = cmNonempty(in) ? *in : "MacOSXFrameworkInfo.plist.in"; | |
| if (!cmSystemTools::FileIsFullPath(inFile)) { | |
| std::string inMod = this->Makefile->GetModulesFile(inFile); | |
| if (!inMod.empty()) { | |
| inFile = inMod; | |
| } | |
| } | |
| if (!cmSystemTools::FileExists(inFile, true)) { | |
| cmSystemTools::Error(cmStrCat("Target ", target->GetName(), | |
| " Info.plist template \"", inFile, | |
| "\" could not be found.")); | |
| return; | |
| } | |
| // Convert target properties to variables in an isolated makefile | |
| // scope to configure the file. If properties are set they will | |
| // override user make variables. If not the configuration will fall | |
| // back to the directory-level values set by the user. | |
| cmMakefile* mf = this->Makefile; | |
| cmMakefile::ScopePushPop varScope(mf); | |
| mf->AddDefinition("MACOSX_FRAMEWORK_NAME", targetName); | |
| cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_ICON_FILE"); | |
| cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_IDENTIFIER"); | |
| cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_SHORT_VERSION_STRING"); | |
| cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_BUNDLE_NAME"); | |
| cmLGInfoProp(mf, target, "MACOSX_FRAMEWORK_BUNDLE_VERSION"); | |
| mf->ConfigureFile(inFile, fname, false, false, false); | |
| } | |
| namespace { | |
| cm::string_view CustomOutputRoleKeyword(cmLocalGenerator::OutputRole role) | |
| { | |
| return (role == cmLocalGenerator::OutputRole::Primary ? "OUTPUT"_s | |
| : "BYPRODUCTS"_s); | |
| } | |
| void CreateGeneratedSource(cmLocalGenerator& lg, std::string const& output, | |
| cmLocalGenerator::OutputRole role, | |
| cmCommandOrigin origin, | |
| cmListFileBacktrace const& lfbt) | |
| { | |
| if (cmGeneratorExpression::Find(output) != std::string::npos) { | |
| lg.GetCMakeInstance()->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| "Generator expressions in custom command outputs are not implemented!", | |
| lfbt); | |
| return; | |
| } | |
| // Make sure the file will not be generated into the source | |
| // directory during an out of source build. | |
| if (!lg.GetMakefile()->CanIWriteThisFile(output)) { | |
| lg.GetCMakeInstance()->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat(CustomOutputRoleKeyword(role), " path\n ", output, | |
| "\nin a source directory as an output of custom command."), | |
| lfbt); | |
| return; | |
| } | |
| // Make sure the output file name has no invalid characters. | |
| bool const hashNotAllowed = lg.GetState()->UseBorlandMake(); | |
| std::string::size_type pos = output.find_first_of("<>"); | |
| if (pos == std::string::npos && hashNotAllowed) { | |
| pos = output.find_first_of('#'); | |
| } | |
| if (pos != std::string::npos) { | |
| lg.GetCMakeInstance()->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat(CustomOutputRoleKeyword(role), " containing a \"", output[pos], | |
| "\" is not allowed."), | |
| lfbt); | |
| return; | |
| } | |
| // Outputs without generator expressions from the project are already | |
| // created and marked as generated. Do not mark them again, because | |
| // other commands might have overwritten the property. | |
| if (origin == cmCommandOrigin::Generator) { | |
| lg.GetMakefile()->GetOrCreateGeneratedSource(output); | |
| } | |
| } | |
| std::string ComputeCustomCommandRuleFileName(cmLocalGenerator& lg, | |
| cmListFileBacktrace const& bt, | |
| std::string const& output) | |
| { | |
| // If the output path has no generator expressions, use it directly. | |
| if (cmGeneratorExpression::Find(output) == std::string::npos) { | |
| return output; | |
| } | |
| // The output path contains a generator expression, but we must choose | |
| // a single source file path to which to attach the custom command. | |
| // Use some heuristics to provide a nice-looking name when possible. | |
| // If the only genex is $<CONFIG>, replace that gracefully. | |
| { | |
| std::string simple = output; | |
| cmSystemTools::ReplaceString(simple, "$<CONFIG>", "(CONFIG)"); | |
| if (cmGeneratorExpression::Find(simple) == std::string::npos) { | |
| return simple; | |
| } | |
| } | |
| // If the genex evaluates to the same value in all configurations, use that. | |
| { | |
| std::vector<std::string> allConfigOutputs = | |
| lg.ExpandCustomCommandOutputGenex(output, bt); | |
| if (allConfigOutputs.size() == 1) { | |
| return allConfigOutputs.front(); | |
| } | |
| } | |
| // Fall back to a deterministic unique name. | |
| cmCryptoHash h(cmCryptoHash::AlgoSHA256); | |
| return cmStrCat(lg.GetCurrentBinaryDirectory(), "/CMakeFiles/", | |
| h.HashString(output).substr(0, 16)); | |
| } | |
| cmSourceFile* AddCustomCommand(cmLocalGenerator& lg, cmCommandOrigin origin, | |
| std::unique_ptr<cmCustomCommand> cc, | |
| bool replace) | |
| { | |
| cmMakefile* mf = lg.GetMakefile(); | |
| auto const& lfbt = cc->GetBacktrace(); | |
| auto const& outputs = cc->GetOutputs(); | |
| auto const& byproducts = cc->GetByproducts(); | |
| auto const& commandLines = cc->GetCommandLines(); | |
| // Choose a source file on which to store the custom command. | |
| cmSourceFile* file = nullptr; | |
| if (!commandLines.empty() && cc->HasMainDependency()) { | |
| auto const& main_dependency = cc->GetMainDependency(); | |
| // The main dependency was specified. Use it unless a different | |
| // custom command already used it. | |
| file = mf->GetSource(main_dependency); | |
| if (file && file->GetCustomCommand() && !replace) { | |
| // The main dependency already has a custom command. | |
| if (commandLines == file->GetCustomCommand()->GetCommandLines()) { | |
| // The existing custom command is identical. Silently ignore | |
| // the duplicate. | |
| return file; | |
| } | |
| // The existing custom command is different. We need to | |
| // generate a rule file for this new command. | |
| file = nullptr; | |
| } else if (!file) { | |
| file = mf->CreateSource(main_dependency); | |
| } | |
| } | |
| // Generate a rule file if the main dependency is not available. | |
| if (!file) { | |
| cmGlobalGenerator* gg = lg.GetGlobalGenerator(); | |
| // Construct a rule file associated with the first output produced. | |
| std::string outName = gg->GenerateRuleFile( | |
| ComputeCustomCommandRuleFileName(lg, lfbt, outputs[0])); | |
| // Check if the rule file already exists. | |
| file = mf->GetSource(outName, cmSourceFileLocationKind::Known); | |
| if (file && file->GetCustomCommand() && !replace) { | |
| // The rule file already exists. | |
| if (commandLines != file->GetCustomCommand()->GetCommandLines()) { | |
| lg.GetCMakeInstance()->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("Attempt to add a custom rule to output\n ", outName, | |
| "\nwhich already has a custom rule."), | |
| lfbt); | |
| } | |
| return file; | |
| } | |
| // Create a cmSourceFile for the rule file. | |
| if (!file) { | |
| file = mf->CreateSource(outName, true, cmSourceFileLocationKind::Known); | |
| } | |
| file->SetProperty("__CMAKE_RULE", "1"); | |
| } | |
| // Attach the custom command to the file. | |
| if (file) { | |
| cc->SetEscapeAllowMakeVars(true); | |
| lg.AddSourceOutputs(file, outputs, cmLocalGenerator::OutputRole::Primary, | |
| lfbt, origin); | |
| lg.AddSourceOutputs(file, byproducts, | |
| cmLocalGenerator::OutputRole::Byproduct, lfbt, origin); | |
| file->SetCustomCommand(std::move(cc)); | |
| } | |
| return file; | |
| } | |
| bool AnyOutputMatches(std::string const& name, | |
| std::vector<std::string> const& outputs) | |
| { | |
| return std::any_of(outputs.begin(), outputs.end(), | |
| [&name](std::string const& output) -> bool { | |
| std::string::size_type pos = output.rfind(name); | |
| // If the output matches exactly | |
| return (pos != std::string::npos && | |
| pos == output.size() - name.size() && | |
| (pos == 0 || output[pos - 1] == '/')); | |
| }); | |
| } | |
| bool AnyTargetCommandOutputMatches( | |
| std::string const& name, std::vector<cmCustomCommand> const& commands) | |
| { | |
| return std::any_of(commands.begin(), commands.end(), | |
| [&name](cmCustomCommand const& command) -> bool { | |
| return AnyOutputMatches(name, command.GetByproducts()); | |
| }); | |
| } | |
| } | |
| namespace detail { | |
| void AddCustomCommandToTarget(cmLocalGenerator& lg, cmCommandOrigin origin, | |
| cmTarget* target, cmCustomCommandType type, | |
| std::unique_ptr<cmCustomCommand> cc) | |
| { | |
| // Add the command to the appropriate build step for the target. | |
| cc->SetEscapeAllowMakeVars(true); | |
| cc->SetTarget(target->GetName()); | |
| lg.AddTargetByproducts(target, cc->GetByproducts(), cc->GetBacktrace(), | |
| origin); | |
| switch (type) { | |
| case cmCustomCommandType::PRE_BUILD: | |
| target->AddPreBuildCommand(std::move(*cc)); | |
| break; | |
| case cmCustomCommandType::PRE_LINK: | |
| target->AddPreLinkCommand(std::move(*cc)); | |
| break; | |
| case cmCustomCommandType::POST_BUILD: | |
| target->AddPostBuildCommand(std::move(*cc)); | |
| break; | |
| } | |
| cc.reset(); | |
| } | |
| cmSourceFile* AddCustomCommandToOutput(cmLocalGenerator& lg, | |
| cmCommandOrigin origin, | |
| std::unique_ptr<cmCustomCommand> cc, | |
| bool replace) | |
| { | |
| return AddCustomCommand(lg, origin, std::move(cc), replace); | |
| } | |
| void AppendCustomCommandToOutput(cmLocalGenerator& lg, | |
| cmListFileBacktrace const& lfbt, | |
| std::string const& output, | |
| std::vector<std::string> const& depends, | |
| cmImplicitDependsList const& implicit_depends, | |
| cmCustomCommandLines const& commandLines) | |
| { | |
| // Lookup an existing command. | |
| cmSourceFile* sf = nullptr; | |
| if (cmGeneratorExpression::Find(output) == std::string::npos) { | |
| sf = lg.GetSourceFileWithOutput(output); | |
| } else { | |
| // This output path has a generator expression. Evaluate it to | |
| // find the output for any configurations. | |
| for (std::string const& out : | |
| lg.ExpandCustomCommandOutputGenex(output, lfbt)) { | |
| sf = lg.GetSourceFileWithOutput(out); | |
| if (sf) { | |
| break; | |
| } | |
| } | |
| } | |
| if (sf) { | |
| if (cmCustomCommand* cc = sf->GetCustomCommand()) { | |
| cc->AppendCommands(commandLines); | |
| cc->AppendDepends(depends); | |
| if (cc->GetCodegen() && !implicit_depends.empty()) { | |
| lg.GetCMakeInstance()->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| "Cannot append IMPLICIT_DEPENDS to existing CODEGEN custom " | |
| "command."); | |
| } | |
| cc->AppendImplicitDepends(implicit_depends); | |
| return; | |
| } | |
| } | |
| // No existing command found. | |
| lg.GetCMakeInstance()->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("Attempt to APPEND to custom command with output\n ", output, | |
| "\nwhich is not already a custom command output."), | |
| lfbt); | |
| } | |
| void AddUtilityCommand(cmLocalGenerator& lg, cmCommandOrigin origin, | |
| cmTarget* target, std::unique_ptr<cmCustomCommand> cc) | |
| { | |
| // They might be moved away | |
| auto byproducts = cc->GetByproducts(); | |
| auto lfbt = cc->GetBacktrace(); | |
| // Use an empty comment to avoid generation of default comment. | |
| if (!cc->GetComment()) { | |
| cc->SetComment(""); | |
| } | |
| // Create the generated symbolic output name of the utility target. | |
| std::string output = | |
| lg.CreateUtilityOutput(target->GetName(), byproducts, lfbt); | |
| cc->SetOutputs(output); | |
| cmSourceFile* rule = AddCustomCommand(lg, origin, std::move(cc), | |
| /*replace=*/false); | |
| if (rule) { | |
| lg.AddTargetByproducts(target, byproducts, lfbt, origin); | |
| } | |
| target->AddSource(output); | |
| } | |
| std::vector<std::string> ComputeISPCObjectSuffixes(cmGeneratorTarget* target) | |
| { | |
| cmValue const targetProperty = target->GetProperty("ISPC_INSTRUCTION_SETS"); | |
| cmList ispcTargets; | |
| if (!targetProperty.IsOff()) { | |
| ispcTargets.assign(targetProperty); | |
| for (auto& ispcTarget : ispcTargets) { | |
| // transform targets into the suffixes | |
| auto pos = ispcTarget.find('-'); | |
| auto target_suffix = ispcTarget.substr(0, pos); | |
| // ISPC uses underscores in output file suffixes where the target name | |
| // has dots (e.g. "avx10.2dmr" produces files with "_avx10_2dmr" suffix) | |
| std::replace(target_suffix.begin(), target_suffix.end(), '.', '_'); | |
| if (target_suffix == | |
| "avx1") { // when targeting avx1 ISPC uses the 'avx' output string | |
| target_suffix = "avx"; | |
| } else if (target_suffix == "sse4_1" || target_suffix == "sse4_2") { | |
| // when targeting sse4.1 or sse4.2 ISPC uses the 'sse4' output string | |
| target_suffix = "sse4"; | |
| } | |
| ispcTarget = target_suffix; | |
| } | |
| } | |
| return std::move(ispcTargets.data()); | |
| } | |
| std::vector<std::string> ComputeISPCExtraObjects( | |
| std::string const& objectName, std::string const& buildDirectory, | |
| std::vector<std::string> const& ispcSuffixes) | |
| { | |
| auto normalizedDir = cmSystemTools::CollapseFullPath(buildDirectory); | |
| std::vector<std::string> computedObjects; | |
| computedObjects.reserve(ispcSuffixes.size()); | |
| auto extension = cmSystemTools::GetFilenameLastExtensionView(objectName); | |
| // We can't use cmSystemTools::GetFilenameWithoutLastExtension as it | |
| // drops any directories in objectName | |
| auto objNameNoExt = objectName; | |
| std::string::size_type dot_pos = objectName.rfind('.'); | |
| if (dot_pos != std::string::npos) { | |
| objNameNoExt.resize(dot_pos); | |
| } | |
| for (auto const& ispcTarget : ispcSuffixes) { | |
| computedObjects.emplace_back( | |
| cmStrCat(normalizedDir, '/', objNameNoExt, '_', ispcTarget, extension)); | |
| } | |
| return computedObjects; | |
| } | |
| } | |
| cmSourcesWithOutput cmLocalGenerator::GetSourcesWithOutput( | |
| std::string const& name) const | |
| { | |
| // Linear search? Also see GetSourceFileWithOutput for detail. | |
| if (!cmSystemTools::FileIsFullPath(name)) { | |
| cmSourcesWithOutput sources; | |
| sources.Target = this->LinearGetTargetWithOutput(name); | |
| sources.Source = this->LinearGetSourceFileWithOutput( | |
| name, cmSourceOutputKind::OutputOrByproduct, sources.SourceIsByproduct); | |
| return sources; | |
| } | |
| // Otherwise we use an efficient lookup map. | |
| auto o = this->OutputToSource.find(name); | |
| if (o != this->OutputToSource.end()) { | |
| return o->second.Sources; | |
| } | |
| return {}; | |
| } | |
| void cmLocalGenerator::AppendOutputsAndByproducts( | |
| std::unordered_set<std::string>& outputs) const | |
| { | |
| for (auto const& entry : this->OutputToSource) { | |
| outputs.insert(entry.first); | |
| } | |
| } | |
| cmSourceFile* cmLocalGenerator::GetSourceFileWithOutput( | |
| std::string const& name, cmSourceOutputKind kind) const | |
| { | |
| // If the queried path is not absolute we use the backward compatible | |
| // linear-time search for an output with a matching suffix. | |
| if (!cmSystemTools::FileIsFullPath(name)) { | |
| bool byproduct = false; | |
| return this->LinearGetSourceFileWithOutput(name, kind, byproduct); | |
| } | |
| // Otherwise we use an efficient lookup map. | |
| auto o = this->OutputToSource.find(name); | |
| if (o != this->OutputToSource.end() && | |
| (!o->second.Sources.SourceIsByproduct || | |
| kind == cmSourceOutputKind::OutputOrByproduct)) { | |
| // Source file could also be null pointer for example if we found the | |
| // byproduct of a utility target, a PRE_BUILD, PRE_LINK, or POST_BUILD | |
| // command of a target, or a not yet created custom command. | |
| return o->second.Sources.Source; | |
| } | |
| return nullptr; | |
| } | |
| std::string cmLocalGenerator::CreateUtilityOutput( | |
| std::string const& targetName, std::vector<std::string> const&, | |
| cmListFileBacktrace const&) | |
| { | |
| std::string force = | |
| cmStrCat(this->GetCurrentBinaryDirectory(), "/CMakeFiles/", targetName); | |
| // The output is not actually created so mark it symbolic. | |
| if (cmSourceFile* sf = this->Makefile->GetOrCreateGeneratedSource(force)) { | |
| sf->SetProperty("SYMBOLIC", "1"); | |
| } else { | |
| cmSystemTools::Error("Could not get source file entry for " + force); | |
| } | |
| return force; | |
| } | |
| std::vector<cmCustomCommandGenerator> | |
| cmLocalGenerator::MakeCustomCommandGenerators(cmCustomCommand const& cc, | |
| std::string const& config) | |
| { | |
| std::vector<cmCustomCommandGenerator> ccgs; | |
| ccgs.emplace_back(cc, config, this); | |
| return ccgs; | |
| } | |
| std::vector<std::string> cmLocalGenerator::ExpandCustomCommandOutputPaths( | |
| cmCompiledGeneratorExpression const& cge, std::string const& config) | |
| { | |
| cmList paths{ cge.Evaluate(this, config) }; | |
| for (std::string& p : paths) { | |
| p = cmSystemTools::CollapseFullPath(p, this->GetCurrentBinaryDirectory()); | |
| } | |
| return std::move(paths.data()); | |
| } | |
| std::vector<std::string> cmLocalGenerator::ExpandCustomCommandOutputGenex( | |
| std::string const& o, cmListFileBacktrace const& bt) | |
| { | |
| std::vector<std::string> allConfigOutputs; | |
| cmGeneratorExpression ge(*this->GetCMakeInstance(), bt); | |
| std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(o); | |
| std::vector<std::string> configs = | |
| this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig); | |
| for (std::string const& config : configs) { | |
| std::vector<std::string> configOutputs = | |
| this->ExpandCustomCommandOutputPaths(*cge, config); | |
| allConfigOutputs.reserve(allConfigOutputs.size() + configOutputs.size()); | |
| std::move(configOutputs.begin(), configOutputs.end(), | |
| std::back_inserter(allConfigOutputs)); | |
| } | |
| auto endUnique = | |
| cmRemoveDuplicates(allConfigOutputs.begin(), allConfigOutputs.end()); | |
| allConfigOutputs.erase(endUnique, allConfigOutputs.end()); | |
| return allConfigOutputs; | |
| } | |
| void cmLocalGenerator::AddTargetByproducts( | |
| cmTarget* target, std::vector<std::string> const& byproducts, | |
| cmListFileBacktrace const& bt, cmCommandOrigin origin) | |
| { | |
| for (std::string const& o : byproducts) { | |
| if (cmGeneratorExpression::Find(o) == std::string::npos) { | |
| this->UpdateOutputToSourceMap(o, target, bt, origin); | |
| continue; | |
| } | |
| // This byproduct path has a generator expression. Evaluate it to | |
| // register the byproducts for all configurations. | |
| for (std::string const& b : this->ExpandCustomCommandOutputGenex(o, bt)) { | |
| this->UpdateOutputToSourceMap(b, target, bt, cmCommandOrigin::Generator); | |
| } | |
| } | |
| } | |
| void cmLocalGenerator::AddSourceOutputs( | |
| cmSourceFile* source, std::vector<std::string> const& outputs, | |
| OutputRole role, cmListFileBacktrace const& bt, cmCommandOrigin origin) | |
| { | |
| for (std::string const& o : outputs) { | |
| if (cmGeneratorExpression::Find(o) == std::string::npos) { | |
| this->UpdateOutputToSourceMap(o, source, role, bt, origin); | |
| continue; | |
| } | |
| // This output path has a generator expression. Evaluate it to | |
| // register the outputs for all configurations. | |
| for (std::string const& out : | |
| this->ExpandCustomCommandOutputGenex(o, bt)) { | |
| this->UpdateOutputToSourceMap(out, source, role, bt, | |
| cmCommandOrigin::Generator); | |
| } | |
| } | |
| } | |
| void cmLocalGenerator::UpdateOutputToSourceMap(std::string const& byproduct, | |
| cmTarget* target, | |
| cmListFileBacktrace const& bt, | |
| cmCommandOrigin origin) | |
| { | |
| SourceEntry entry; | |
| entry.Sources.Target = target; | |
| auto pr = this->OutputToSource.emplace(byproduct, entry); | |
| if (pr.second) { | |
| this->GlobalGenerator->InvalidateGeneratedByBuildIndex(); | |
| CreateGeneratedSource(*this, byproduct, OutputRole::Byproduct, origin, bt); | |
| } else { | |
| SourceEntry& current = pr.first->second; | |
| // Has the target already been set? | |
| if (!current.Sources.Target) { | |
| current.Sources.Target = target; | |
| } else { | |
| // Multiple custom commands/targets produce the same output (source file | |
| // or target). See also comment in other UpdateOutputToSourceMap | |
| // overload. | |
| // | |
| // TODO: Warn the user about this case. | |
| } | |
| } | |
| } | |
| void cmLocalGenerator::UpdateOutputToSourceMap(std::string const& output, | |
| cmSourceFile* source, | |
| OutputRole role, | |
| cmListFileBacktrace const& bt, | |
| cmCommandOrigin origin) | |
| { | |
| SourceEntry entry; | |
| entry.Sources.Source = source; | |
| entry.Sources.SourceIsByproduct = role == OutputRole::Byproduct; | |
| auto pr = this->OutputToSource.emplace(output, entry); | |
| if (pr.second) { | |
| this->GlobalGenerator->InvalidateGeneratedByBuildIndex(); | |
| CreateGeneratedSource(*this, output, role, origin, bt); | |
| } else { | |
| SourceEntry& current = pr.first->second; | |
| // Outputs take precedence over byproducts | |
| if (!current.Sources.Source || | |
| (current.Sources.SourceIsByproduct && role == OutputRole::Primary)) { | |
| current.Sources.Source = source; | |
| current.Sources.SourceIsByproduct = false; | |
| } else { | |
| // Multiple custom commands produce the same output but may | |
| // be attached to a different source file (MAIN_DEPENDENCY). | |
| // LinearGetSourceFileWithOutput would return the first one, | |
| // so keep the mapping for the first one. | |
| // | |
| // TODO: Warn the user about this case. However, the VS 8 generator | |
| // triggers it for separate generate.stamp rules in ZERO_CHECK and | |
| // individual targets. | |
| } | |
| } | |
| } | |
| cmTarget* cmLocalGenerator::LinearGetTargetWithOutput( | |
| std::string const& name) const | |
| { | |
| // We go through the ordered vector of targets to get reproducible results | |
| // should multiple names match. | |
| for (cmTarget* t : this->Makefile->GetOrderedTargets()) { | |
| // Does the output of any command match the source file name? | |
| if (AnyTargetCommandOutputMatches(name, t->GetPreBuildCommands())) { | |
| return t; | |
| } | |
| if (AnyTargetCommandOutputMatches(name, t->GetPreLinkCommands())) { | |
| return t; | |
| } | |
| if (AnyTargetCommandOutputMatches(name, t->GetPostBuildCommands())) { | |
| return t; | |
| } | |
| } | |
| return nullptr; | |
| } | |
| cmSourceFile* cmLocalGenerator::LinearGetSourceFileWithOutput( | |
| std::string const& name, cmSourceOutputKind kind, bool& byproduct) const | |
| { | |
| // Outputs take precedence over byproducts. | |
| byproduct = false; | |
| cmSourceFile* fallback = nullptr; | |
| // Look through all the source files that have custom commands and see if the | |
| // custom command has the passed source file as an output. | |
| for (auto const& src : this->Makefile->GetSourceFiles()) { | |
| // Does this source file have a custom command? | |
| if (src->GetCustomCommand()) { | |
| // Does the output of the custom command match the source file name? | |
| if (AnyOutputMatches(name, src->GetCustomCommand()->GetOutputs())) { | |
| // Return the first matching output. | |
| return src.get(); | |
| } | |
| if (kind == cmSourceOutputKind::OutputOrByproduct) { | |
| if (AnyOutputMatches(name, src->GetCustomCommand()->GetByproducts())) { | |
| // Do not return the source yet as there might be a matching output. | |
| fallback = src.get(); | |
| } | |
| } | |
| } | |
| } | |
| // Did we find a byproduct? | |
| byproduct = fallback != nullptr; | |
| return fallback; | |
| } |