| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmCommonTargetGenerator.h" | |
| #include <algorithm> | |
| #include <utility> | |
| #include <cm/filesystem> | |
| #include <cm/string_view> | |
| #include <cmext/algorithm> | |
| #include <cmext/string_view> | |
| #include "cmComputeLinkInformation.h" | |
| #include "cmGenExContext.h" | |
| #include "cmGeneratorExpression.h" | |
| #include "cmGeneratorExpressionDAGChecker.h" | |
| #include "cmGeneratorTarget.h" | |
| #include "cmGlobalCommonGenerator.h" | |
| #include "cmGlobalGenerator.h" | |
| #include "cmList.h" | |
| #include "cmListFileCache.h" | |
| #include "cmLocalCommonGenerator.h" | |
| #include "cmLocalGenerator.h" | |
| #include "cmMakefile.h" | |
| #include "cmMessageType.h" | |
| #include "cmOutputConverter.h" | |
| #include "cmRange.h" | |
| #include "cmSourceFile.h" | |
| #include "cmState.h" | |
| #include "cmStringAlgorithms.h" | |
| #include "cmSystemTools.h" | |
| #include "cmTargetTypes.h" | |
| #include "cmValue.h" | |
| namespace { | |
| bool LinkedTargetHasModules(cmGeneratorTarget const* target, | |
| std::string const& lang, std::string const& config, | |
| cmGlobalCommonGenerator const* gg, | |
| cmGeneratorTarget const* currentTarget) | |
| { | |
| if (!target) { | |
| return false; | |
| } | |
| if (lang == "CXX"_s && target->HasCxxImportModuleErrors()) { | |
| return true; | |
| } | |
| if (target->IsImported()) { | |
| return false; | |
| } | |
| if (!gg->TargetOrderIndexLess(target, currentTarget)) { | |
| return false; | |
| } | |
| if (target->GetType() == cm::TargetType::INTERFACE_LIBRARY && | |
| !target->IsSynthetic()) { | |
| return false; | |
| } | |
| if (lang == "CXX"_s && target->HaveCxx20ModuleSources()) { | |
| return true; | |
| } | |
| if (lang == "Fortran"_s && target->HaveFortranSources(config)) { | |
| return true; | |
| } | |
| return false; | |
| } | |
| } | |
| cmCommonTargetGenerator::cmCommonTargetGenerator(cmGeneratorTarget* gt) | |
| : GeneratorTarget(gt) | |
| , Makefile(gt->Makefile) | |
| , LocalCommonGenerator( | |
| static_cast<cmLocalCommonGenerator*>(gt->LocalGenerator)) | |
| , GlobalCommonGenerator(static_cast<cmGlobalCommonGenerator*>( | |
| gt->LocalGenerator->GetGlobalGenerator())) | |
| , ConfigNames(this->LocalCommonGenerator->GetConfigNames()) | |
| { | |
| } | |
| cmCommonTargetGenerator::~cmCommonTargetGenerator() = default; | |
| std::vector<std::string> const& cmCommonTargetGenerator::GetConfigNames() const | |
| { | |
| return this->ConfigNames; | |
| } | |
| cmValue cmCommonTargetGenerator::GetFeature(std::string const& feature, | |
| std::string const& config) | |
| { | |
| return this->GeneratorTarget->GetFeature(feature, config); | |
| } | |
| void cmCommonTargetGenerator::AppendFortranFormatFlags( | |
| std::string& flags, cmSourceFile const& source) | |
| { | |
| std::string const srcfmt = source.GetSafeProperty("Fortran_FORMAT"); | |
| cmOutputConverter::FortranFormat format = | |
| cmOutputConverter::GetFortranFormat(srcfmt); | |
| if (format == cmOutputConverter::FortranFormatNone) { | |
| std::string const& tgtfmt = | |
| this->GeneratorTarget->GetSafeProperty("Fortran_FORMAT"); | |
| format = cmOutputConverter::GetFortranFormat(tgtfmt); | |
| } | |
| char const* var = nullptr; | |
| switch (format) { | |
| case cmOutputConverter::FortranFormatFixed: | |
| var = "CMAKE_Fortran_FORMAT_FIXED_FLAG"; | |
| break; | |
| case cmOutputConverter::FortranFormatFree: | |
| var = "CMAKE_Fortran_FORMAT_FREE_FLAG"; | |
| break; | |
| default: | |
| break; | |
| } | |
| if (var) { | |
| this->LocalCommonGenerator->AppendFlags( | |
| flags, this->Makefile->GetSafeDefinition(var)); | |
| } | |
| } | |
| void cmCommonTargetGenerator::AppendFortranPreprocessFlags( | |
| std::string& flags, cmSourceFile const& source, | |
| PreprocessFlagsRequired requires_pp) | |
| { | |
| std::string const srcpp = source.GetSafeProperty("Fortran_PREPROCESS"); | |
| cmOutputConverter::FortranPreprocess preprocess = | |
| cmOutputConverter::GetFortranPreprocess(srcpp); | |
| if (preprocess == cmOutputConverter::FortranPreprocess::Unset) { | |
| std::string const& tgtpp = | |
| this->GeneratorTarget->GetSafeProperty("Fortran_PREPROCESS"); | |
| preprocess = cmOutputConverter::GetFortranPreprocess(tgtpp); | |
| } | |
| char const* var = nullptr; | |
| switch (preprocess) { | |
| case cmOutputConverter::FortranPreprocess::Needed: | |
| if (requires_pp == PreprocessFlagsRequired::YES) { | |
| var = "CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_ON"; | |
| } | |
| break; | |
| case cmOutputConverter::FortranPreprocess::NotNeeded: | |
| var = "CMAKE_Fortran_COMPILE_OPTIONS_PREPROCESS_OFF"; | |
| break; | |
| default: | |
| break; | |
| } | |
| if (var) { | |
| this->LocalCommonGenerator->AppendCompileOptions( | |
| flags, this->Makefile->GetSafeDefinition(var)); | |
| } | |
| } | |
| std::string cmCommonTargetGenerator::GetFlags(std::string const& l, | |
| std::string const& config, | |
| std::string const& arch) | |
| { | |
| std::string const key = cmStrCat(config, arch); | |
| auto i = this->Configs[key].FlagsByLanguage.find(l); | |
| if (i == this->Configs[key].FlagsByLanguage.end()) { | |
| std::string flags; | |
| this->LocalCommonGenerator->GetTargetCompileFlags(this->GeneratorTarget, | |
| config, l, flags, arch); | |
| ByLanguageMap::value_type entry(l, flags); | |
| i = this->Configs[key].FlagsByLanguage.insert(entry).first; | |
| } | |
| return i->second; | |
| } | |
| std::string cmCommonTargetGenerator::GetDefines(std::string const& l, | |
| std::string const& config) | |
| { | |
| auto i = this->Configs[config].DefinesByLanguage.find(l); | |
| if (i == this->Configs[config].DefinesByLanguage.end()) { | |
| std::set<std::string> defines; | |
| this->LocalCommonGenerator->GetTargetDefines(this->GeneratorTarget, config, | |
| l, defines); | |
| std::string definesString; | |
| this->LocalCommonGenerator->JoinDefines(defines, definesString, l); | |
| ByLanguageMap::value_type entry(l, definesString); | |
| i = this->Configs[config].DefinesByLanguage.insert(entry).first; | |
| } | |
| return i->second; | |
| } | |
| std::string cmCommonTargetGenerator::GetIncludes(std::string const& l, | |
| std::string const& config) | |
| { | |
| auto i = this->Configs[config].IncludesByLanguage.find(l); | |
| if (i == this->Configs[config].IncludesByLanguage.end()) { | |
| std::string includes; | |
| this->AddIncludeFlags(includes, l, config); | |
| ByLanguageMap::value_type entry(l, includes); | |
| i = this->Configs[config].IncludesByLanguage.insert(entry).first; | |
| } | |
| return i->second; | |
| } | |
| cmCommonTargetGenerator::LinkedTargetDirs | |
| cmCommonTargetGenerator::GetLinkedTargetDirectories( | |
| std::string const& lang, std::string const& config) const | |
| { | |
| LinkedTargetDirs dirs; | |
| std::set<cmGeneratorTarget const*> forward_emitted; | |
| std::set<cmGeneratorTarget const*> direct_emitted; | |
| cmGlobalCommonGenerator* const gg = this->GlobalCommonGenerator; | |
| enum class Forwarding | |
| { | |
| Yes, | |
| No | |
| }; | |
| if (cmComputeLinkInformation* cli = | |
| this->GeneratorTarget->GetLinkInformation(config)) { | |
| auto findSyntheticTarget = | |
| [this, &config, | |
| cli](cmGeneratorTarget const* linkee) -> cmGeneratorTarget const* { | |
| if (!linkee) { | |
| return nullptr; | |
| } | |
| // Check the map of direct synthetic dependencies for a substitute | |
| auto const& synthDeps = this->GeneratorTarget->GetSyntheticDeps(config); | |
| auto it = synthDeps.find(linkee); | |
| if (it != synthDeps.end() && !it->second.empty()) { | |
| return *it->second.begin(); | |
| } | |
| // Check linked targets to find synthetic targets for transitive deps | |
| std::vector<cmGeneratorTarget const*> pending; | |
| std::set<cmGeneratorTarget const*> visited; | |
| for (auto const& dep : synthDeps) { | |
| for (auto const* synth : dep.second) { | |
| if (synth && visited.insert(synth).second) { | |
| pending.push_back(synth); | |
| } | |
| } | |
| } | |
| // Also seed the search from direct linked targets that have | |
| // CXX20 module sources, in case they hold synthetic deps for | |
| // transitive dependencies that we need to inherit. Skip targets | |
| // that already have synthetic substitutes in our own synthDeps | |
| // (their transitive deps will be explored via the substitute). | |
| for (auto const& item : cli->GetItems()) { | |
| if (item.Target && item.Target->HaveCxx20ModuleSources() && | |
| synthDeps.find(item.Target) == synthDeps.end() && | |
| visited.insert(item.Target).second) { | |
| pending.push_back(item.Target); | |
| } | |
| } | |
| while (!pending.empty()) { | |
| auto const* current = pending.back(); | |
| pending.pop_back(); | |
| auto const& transitiveSynthDeps = current->GetSyntheticDeps(config); | |
| auto itLinkeeSynth = transitiveSynthDeps.find(linkee); | |
| if (itLinkeeSynth != transitiveSynthDeps.end() && | |
| !itLinkeeSynth->second.empty()) { | |
| return *itLinkeeSynth->second.begin(); | |
| } | |
| for (auto const& entry : transitiveSynthDeps) { | |
| for (auto const* synth : entry.second) { | |
| if (synth && visited.insert(synth).second) { | |
| pending.push_back(synth); | |
| } | |
| } | |
| } | |
| } | |
| return nullptr; | |
| }; | |
| auto addLinkedTarget = [this, &lang, &config, &dirs, &direct_emitted, | |
| &forward_emitted, &findSyntheticTarget, | |
| gg](cmGeneratorTarget const* linkee, | |
| Forwarding forward) { | |
| // Check if the linkee has a synthetic target to use for importing | |
| cmGeneratorTarget const* mappedLinkee = linkee; | |
| if (auto const* synth = findSyntheticTarget(linkee)) { | |
| mappedLinkee = synth; | |
| } | |
| if (mappedLinkee && | |
| LinkedTargetHasModules(mappedLinkee, lang, config, gg, | |
| this->GeneratorTarget)) { | |
| cmLocalGenerator* lg = mappedLinkee->GetLocalGenerator(); | |
| std::string di = mappedLinkee->GetSupportDirectory(); | |
| if (lg->GetGlobalGenerator()->IsMultiConfig()) { | |
| di = cmStrCat(std::move(di), '/', config); | |
| } | |
| if (forward == Forwarding::Yes && | |
| forward_emitted.insert(mappedLinkee).second) { | |
| dirs.Forward.push_back(di); | |
| } | |
| if (direct_emitted.insert(mappedLinkee).second) { | |
| dirs.Direct.emplace_back(di); | |
| } | |
| } | |
| }; | |
| for (auto const& item : cli->GetItems()) { | |
| if (item.Target) { | |
| addLinkedTarget(item.Target, Forwarding::No); | |
| } else if (item.ObjectSource && lang == "Fortran"_s | |
| /* Object source files do not have a language associated with | |
| them. */ | |
| /* && item.ObjectSource->GetLanguage() == "Fortran"_s*/) { | |
| // Fortran modules provided by `$<TARGET_OBJECTS>` as linked items | |
| // should be collated for use in this target. | |
| addLinkedTarget(this->LocalCommonGenerator->FindGeneratorTargetToUse( | |
| item.ObjectSource->GetObjectLibrary()), | |
| Forwarding::Yes); | |
| } | |
| } | |
| for (cmGeneratorTarget const* target : cli->GetExternalObjectTargets()) { | |
| addLinkedTarget(target, Forwarding::No); | |
| } | |
| if (lang == "Fortran"_s) { | |
| // Fortran modules provided by `$<TARGET_OBJECTS>` as sources should be | |
| // collated for use in this target. | |
| for (cmGeneratorTarget const* target : | |
| this->GeneratorTarget->GetSourceObjectLibraries(config)) { | |
| addLinkedTarget(target, Forwarding::Yes); | |
| } | |
| } | |
| } | |
| return dirs; | |
| } | |
| std::string cmCommonTargetGenerator::ComputeTargetCompilePDB( | |
| std::string const& config) const | |
| { | |
| std::string compilePdbPath; | |
| if (this->GeneratorTarget->GetType() > cm::TargetType::OBJECT_LIBRARY) { | |
| return compilePdbPath; | |
| } | |
| compilePdbPath = this->GeneratorTarget->GetCompilePDBPath(config); | |
| if (compilePdbPath.empty()) { | |
| // Match VS default: `$(IntDir)vc$(PlatformToolsetVersion).pdb`. | |
| // A trailing slash tells the toolchain to add its default file name. | |
| compilePdbPath = this->GeneratorTarget->GetSupportDirectory(); | |
| if (this->GlobalCommonGenerator->IsMultiConfig()) { | |
| compilePdbPath = cmStrCat(std::move(compilePdbPath), '/', config); | |
| } | |
| compilePdbPath += '/'; | |
| if (this->GeneratorTarget->GetType() == cm::TargetType::STATIC_LIBRARY) { | |
| // Match VS default for static libs: `$(IntDir)$(ProjectName).pdb`. | |
| compilePdbPath = cmStrCat(std::move(compilePdbPath), | |
| this->GeneratorTarget->GetName(), ".pdb"); | |
| } | |
| } | |
| return compilePdbPath; | |
| } | |
| std::string cmCommonTargetGenerator::GetManifests(std::string const& config) | |
| { | |
| std::vector<cmSourceFile const*> manifest_srcs; | |
| this->GeneratorTarget->GetManifests(manifest_srcs, config); | |
| std::vector<std::string> manifests; | |
| manifests.reserve(manifest_srcs.size()); | |
| std::string const lang = this->GeneratorTarget->GetLinkerLanguage(config); | |
| std::string const manifestFlag = this->Makefile->GetDefinition( | |
| cmStrCat("CMAKE_", lang, "_LINKER_MANIFEST_FLAG")); | |
| for (cmSourceFile const* manifest_src : manifest_srcs) { | |
| manifests.push_back(manifestFlag + | |
| this->LocalCommonGenerator->ConvertToOutputFormat( | |
| this->LocalCommonGenerator->MaybeRelativeToWorkDir( | |
| manifest_src->GetFullPath()), | |
| cmOutputConverter::SHELL)); | |
| } | |
| return cmJoin(manifests, " "); | |
| } | |
| std::string cmCommonTargetGenerator::GetAIXExports(std::string const&) | |
| { | |
| std::string aixExports; | |
| if (this->GeneratorTarget->IsAIX()) { | |
| if (cmValue exportAll = | |
| this->GeneratorTarget->GetProperty("AIX_EXPORT_ALL_SYMBOLS")) { | |
| if (exportAll.IsOff()) { | |
| aixExports = "-n"; | |
| } | |
| } | |
| } | |
| return aixExports; | |
| } | |
| void cmCommonTargetGenerator::AppendOSXVerFlag(std::string& flags, | |
| std::string const& lang, | |
| char const* name, bool so) | |
| { | |
| // Lookup the flag to specify the version. | |
| std::string const fvar = | |
| cmStrCat("CMAKE_", lang, "_OSX_", name, "_VERSION_FLAG"); | |
| cmValue flag = this->Makefile->GetDefinition(fvar); | |
| // Skip if no such flag. | |
| if (!flag) { | |
| return; | |
| } | |
| // Lookup the target version information. | |
| int major; | |
| int minor; | |
| int patch; | |
| std::string const prop = cmStrCat("MACHO_", name, "_VERSION"); | |
| std::string const fallbackProp = so ? "SOVERSION" : "VERSION"; | |
| this->GeneratorTarget->GetTargetVersionFallback(prop, fallbackProp, major, | |
| minor, patch); | |
| if (major > 0 || minor > 0 || patch > 0) { | |
| // Append the flag since a non-zero version is specified. | |
| std::string vflag = cmStrCat(*flag, major, '.', minor, '.', patch); | |
| this->LocalCommonGenerator->AppendFlags(flags, vflag); | |
| } | |
| } | |
| std::vector<std::string> cmCommonTargetGenerator::GetCompilerLauncher( | |
| std::string const& lang, std::string const& config) | |
| { | |
| std::vector<std::string> compilerLauncher; | |
| if (lang == "C" || lang == "CXX" || lang == "Fortran" || lang == "CUDA" || | |
| lang == "HIP" || lang == "ISPC" || lang == "OBJC" || lang == "OBJCXX" || | |
| lang == "SYCL" || lang == "Rust") { | |
| // FIXME(#27402,#28110): The FASTBuild generator's limitations w.r.t. | |
| // shell escaping and command line ordering means we can't support | |
| // wrappers for now. | |
| std::string const generatorName = | |
| this->GeneratorTarget->GetLocalGenerator() | |
| ->GetGlobalGenerator() | |
| ->GetName(); | |
| if (generatorName.find("FASTBuild") == std::string::npos) { | |
| // Wrappers go outside of the launcher. | |
| compilerLauncher = this->ParseWrappers( | |
| this->GeneratorTarget->GetCompileWrappers(config, lang), | |
| "CMAKE_COMPILE_WRAPPER_"_s, "COMPILE_WRAPPERS"_s, | |
| this->GeneratorTarget->GetName(), config, lang); | |
| } | |
| std::string const propName = cmStrCat(lang, "_COMPILER_LAUNCHER"); | |
| cmValue cLauncherValue = this->GeneratorTarget->GetProperty(propName); | |
| std::string const evaluatedCLauncher = cmGeneratorExpression::Evaluate( | |
| *cLauncherValue, this->GeneratorTarget->GetLocalGenerator(), config, | |
| this->GeneratorTarget, nullptr, this->GeneratorTarget, lang); | |
| if (!evaluatedCLauncher.empty()) { | |
| cm::append(compilerLauncher, | |
| cmList{ evaluatedCLauncher, cmList::EmptyElements::Yes }); | |
| } | |
| } | |
| return compilerLauncher; | |
| } | |
| std::string cmCommonTargetGenerator::GenerateCodeCheckRules( | |
| cmSourceFile const& source, std::vector<std::string>& compilerLauncher, | |
| std::string const& cmakeCmd, std::string const& config, | |
| std::function<std::string(std::string const&)> const& pathConverter) | |
| { | |
| std::string const lang = | |
| this->GeneratorTarget->GetSourceFileLanguage(&source, config); | |
| std::string tidy; | |
| std::string iwyu; | |
| std::string cpplint; | |
| std::string cppcheck; | |
| std::string icstat; | |
| std::string pvs; | |
| auto evaluateProp = [&](std::string const& prop) -> std::string { | |
| cmValue const value = this->GeneratorTarget->GetProperty(prop); | |
| if (!value) { | |
| return std::string{}; | |
| } | |
| std::string evaluatedProp = cmGeneratorExpression::Evaluate( | |
| *value, this->GeneratorTarget->GetLocalGenerator(), config, | |
| this->GeneratorTarget, nullptr, this->GeneratorTarget, lang); | |
| return evaluatedProp; | |
| }; | |
| std::string const tidy_prop = cmStrCat(lang, "_CLANG_TIDY"); | |
| tidy = evaluateProp(tidy_prop); | |
| if (lang == "C" || lang == "CXX") { | |
| std::string const iwyu_prop = cmStrCat(lang, "_INCLUDE_WHAT_YOU_USE"); | |
| iwyu = evaluateProp(iwyu_prop); | |
| std::string const cpplint_prop = cmStrCat(lang, "_CPPLINT"); | |
| cpplint = evaluateProp(cpplint_prop); | |
| std::string const cppcheck_prop = cmStrCat(lang, "_CPPCHECK"); | |
| cppcheck = evaluateProp(cppcheck_prop); | |
| std::string const icstat_prop = cmStrCat(lang, "_ICSTAT"); | |
| icstat = evaluateProp(icstat_prop); | |
| std::string const pvs_prop = cmStrCat(lang, "_PVS_STUDIO"); | |
| pvs = evaluateProp(pvs_prop); | |
| } | |
| if (cmNonempty(iwyu) || cmNonempty(tidy) || cmNonempty(cpplint) || | |
| cmNonempty(cppcheck) || cmNonempty(icstat) || cmNonempty(pvs)) { | |
| std::string code_check = cmakeCmd + " -E __run_co_compile"; | |
| if (!compilerLauncher.empty()) { | |
| // In __run_co_compile case the launcher command is supplied | |
| // via --launcher=<list> and consumed | |
| for (std::string& arg : compilerLauncher) { | |
| cmSystemTools::ReplaceString(arg, ";", "\\;"); | |
| } | |
| code_check = | |
| cmStrCat(std::move(code_check), " --launcher=", | |
| this->GeneratorTarget->GetLocalGenerator()->EscapeForShell( | |
| cmJoin(compilerLauncher, ";"))); | |
| compilerLauncher.clear(); | |
| } | |
| if (cmNonempty(iwyu)) { | |
| // Only add --driver-mode if it is not already specified, as adding | |
| // it unconditionally might override a user-specified driver-mode | |
| if (iwyu.find("--driver-mode=") == std::string::npos) { | |
| cmValue const p = this->Makefile->GetDefinition( | |
| cmStrCat("CMAKE_", lang, "_INCLUDE_WHAT_YOU_USE_DRIVER_MODE")); | |
| std::string driverMode; | |
| if (cmNonempty(p)) { | |
| driverMode = *p; | |
| } else { | |
| driverMode = lang == "C" ? "gcc" : "g++"; | |
| } | |
| code_check = | |
| cmStrCat(std::move(code_check), " --iwyu=", | |
| this->GeneratorTarget->GetLocalGenerator()->EscapeForShell( | |
| cmStrCat(iwyu, ";--driver-mode=", driverMode))); | |
| } else { | |
| code_check = cmStrCat( | |
| std::move(code_check), " --iwyu=", | |
| this->GeneratorTarget->GetLocalGenerator()->EscapeForShell(iwyu)); | |
| } | |
| } | |
| if (cmNonempty(tidy)) { | |
| code_check += " --tidy="; | |
| cmValue const p = this->Makefile->GetDefinition( | |
| cmStrCat("CMAKE_", lang, "_CLANG_TIDY_DRIVER_MODE")); | |
| std::string driverMode; | |
| if (cmNonempty(p)) { | |
| driverMode = *p; | |
| } else { | |
| driverMode = lang == "C" ? "gcc" : "g++"; | |
| } | |
| std::string const generatorName = | |
| this->GeneratorTarget->GetLocalGenerator() | |
| ->GetGlobalGenerator() | |
| ->GetName(); | |
| std::string const clangTidyExportFixedDir = | |
| this->GeneratorTarget->GetClangTidyExportFixesDirectory(lang); | |
| std::string fixesFile = this->GetClangTidyReplacementsFilePath( | |
| clangTidyExportFixedDir, source, config); | |
| std::string exportFixes; | |
| if (!clangTidyExportFixedDir.empty()) { | |
| this->GlobalCommonGenerator->AddClangTidyExportFixesDir( | |
| clangTidyExportFixedDir); | |
| } | |
| if (generatorName.find("Make") != std::string::npos) { | |
| if (!clangTidyExportFixedDir.empty()) { | |
| this->GlobalCommonGenerator->AddClangTidyExportFixesFile(fixesFile); | |
| cmSystemTools::MakeDirectory( | |
| cmSystemTools::GetFilenamePath(fixesFile)); | |
| fixesFile = this->GeneratorTarget->GetLocalGenerator() | |
| ->MaybeRelativeToCurBinDir(fixesFile); | |
| exportFixes = cmStrCat(";--export-fixes=", fixesFile); | |
| } | |
| code_check += | |
| this->GeneratorTarget->GetLocalGenerator()->EscapeForShell( | |
| cmStrCat(tidy, ";--extra-arg-before=--driver-mode=", driverMode, | |
| exportFixes)); | |
| } else if (generatorName.find("Ninja") != std::string::npos || | |
| generatorName.find("FASTBuild") != std::string::npos) { | |
| if (!clangTidyExportFixedDir.empty()) { | |
| this->GlobalCommonGenerator->AddClangTidyExportFixesFile(fixesFile); | |
| cmSystemTools::MakeDirectory( | |
| cmSystemTools::GetFilenamePath(fixesFile)); | |
| if (pathConverter) { | |
| fixesFile = pathConverter(fixesFile); | |
| } | |
| exportFixes = cmStrCat(";--export-fixes=", fixesFile); | |
| } | |
| code_check += | |
| this->GeneratorTarget->GetLocalGenerator()->EscapeForShell( | |
| cmStrCat(tidy, ";--extra-arg-before=--driver-mode=", driverMode, | |
| exportFixes)); | |
| } | |
| } | |
| if (cmNonempty(pvs)) { | |
| cmMakefile* mf = | |
| this->GeneratorTarget->GetLocalGenerator()->GetMakefile(); | |
| std::string extraPvsArgs; | |
| if (lang == "CXX") { | |
| extraPvsArgs = | |
| cmStrCat(";--cxx;", mf->GetDefinition("CMAKE_CXX_COMPILER")); | |
| } else if (lang == "C") { | |
| extraPvsArgs = | |
| cmStrCat(";--cc;", mf->GetDefinition("CMAKE_C_COMPILER")); | |
| } | |
| // cocompile args | |
| code_check = cmStrCat( | |
| std::move(code_check), " --pvs-studio=", | |
| this->GeneratorTarget->GetLocalGenerator()->EscapeForShell( | |
| cmStrCat(pvs, extraPvsArgs)), | |
| " --object=", | |
| this->GeneratorTarget->GetLocalGenerator()->ConvertToOutputFormat( | |
| cmSystemTools::CollapseFullPath( | |
| cmStrCat(this->GeneratorTarget->GetObjectDirectory(config), '/', | |
| this->GeneratorTarget->GetObjectName(&source))), | |
| cmOutputConverter::SHELL)); | |
| } | |
| if (cmNonempty(cpplint)) { | |
| code_check = cmStrCat( | |
| std::move(code_check), " --cpplint=", | |
| this->GeneratorTarget->GetLocalGenerator()->EscapeForShell(cpplint)); | |
| } | |
| if (cmNonempty(cppcheck)) { | |
| code_check = cmStrCat( | |
| std::move(code_check), " --cppcheck=", | |
| this->GeneratorTarget->GetLocalGenerator()->EscapeForShell(cppcheck)); | |
| } | |
| if (cmNonempty(icstat)) { | |
| // Unless specified otherwise via CMAKE_<LANG>_ICSTAT, | |
| // populate the icstat command line using default options | |
| // for its mandatory parameters. | |
| std::string checksParam{}; | |
| if (icstat.find("--checks=") == std::string::npos) { | |
| std::string const checksFile{ "cstat_sel_checks.txt" }; | |
| checksParam = cmStrCat(";--checks=", checksFile); | |
| } | |
| std::string dbParam{}; | |
| if (icstat.find("--db=") == std::string::npos) { | |
| std::string const dbFile{ "cstat.db" }; | |
| dbParam = cmStrCat(";--db=", dbFile); | |
| } | |
| cm::string_view const analyzeCmd{ ";analyze" }; | |
| code_check = | |
| cmStrCat(std::move(code_check), " --icstat=", | |
| this->GeneratorTarget->GetLocalGenerator()->EscapeForShell( | |
| cmStrCat(icstat, checksParam, dbParam, analyzeCmd))); | |
| } | |
| if (cmNonempty(tidy) || (cmNonempty(cpplint)) || (cmNonempty(cppcheck)) || | |
| cmNonempty(pvs) || cmNonempty(icstat)) { | |
| code_check = cmStrCat( | |
| std::move(code_check), " --source=", | |
| this->GeneratorTarget->GetLocalGenerator()->ConvertToOutputFormat( | |
| source.GetFullPath(), cmOutputConverter::SHELL)); | |
| } | |
| code_check += " -- "; | |
| return code_check; | |
| } | |
| return ""; | |
| } | |
| std::vector<std::string> cmCommonTargetGenerator::GetLinkerLauncher( | |
| std::string const& config) | |
| { | |
| std::vector<std::string> linkLauncher; | |
| std::string const lang = this->GeneratorTarget->GetLinkerLanguage(config); | |
| // FIXME(#27402,#28110): The FASTBuild generator's limitations w.r.t. | |
| // shell escaping and command line ordering means we can't support | |
| // wrappers for now. | |
| std::string const generatorName = this->GeneratorTarget->GetLocalGenerator() | |
| ->GetGlobalGenerator() | |
| ->GetName(); | |
| if (generatorName.find("FASTBuild") == std::string::npos) { | |
| // Wrappers go outside of the launcher. | |
| linkLauncher = | |
| this->ParseWrappers(this->GeneratorTarget->GetLinkWrappers(config, lang), | |
| "CMAKE_LINK_WRAPPER_"_s, "LINK_WRAPPERS"_s, | |
| this->GeneratorTarget->GetName(), config, lang); | |
| } | |
| std::string const propName = cmStrCat(lang, "_LINKER_LAUNCHER"); | |
| cmValue launcherProp = this->GeneratorTarget->GetProperty(propName); | |
| if (cmNonempty(launcherProp)) { | |
| cm::GenEx::Context context(this->LocalCommonGenerator, config, lang); | |
| cmGeneratorExpressionDAGChecker dagChecker{ this->GeneratorTarget, | |
| propName, nullptr, nullptr, | |
| context }; | |
| std::string const evaluatedLinkLauncher = cmGeneratorExpression::Evaluate( | |
| *launcherProp, context.LG, context.Config, this->GeneratorTarget, | |
| &dagChecker, this->GeneratorTarget, context.Language); | |
| if (!evaluatedLinkLauncher.empty()) { | |
| cm::append(linkLauncher, | |
| cmList{ evaluatedLinkLauncher, cmList::EmptyElements::Yes }); | |
| } | |
| } | |
| return linkLauncher; | |
| } | |
| std::string cmCommonTargetGenerator::ConvertLauncherToShell( | |
| std::vector<std::string> const& launcher) const | |
| { | |
| if (launcher.empty()) { | |
| return std::string{}; | |
| } | |
| std::vector<std::string> args = launcher; | |
| args[0] = this->LocalCommonGenerator->ConvertToOutputFormat( | |
| args[0], cmOutputConverter::SHELL); | |
| for (std::string& arg : cmMakeRange(args.begin() + 1, args.end())) { | |
| arg = this->LocalCommonGenerator->EscapeForShell(arg); | |
| } | |
| return cmJoin(args, " "); | |
| } | |
| bool cmCommonTargetGenerator::HaveRequiredLanguages( | |
| std::vector<cmSourceFile const*> const& sources, std::string const& config, | |
| std::set<std::string>& languagesNeeded) const | |
| { | |
| for (cmSourceFile const* sf : sources) { | |
| languagesNeeded.insert( | |
| this->GeneratorTarget->GetSourceFileLanguage(sf, config)); | |
| } | |
| auto* makefile = this->Makefile; | |
| auto* state = makefile->GetState(); | |
| auto unary = [&state, &makefile](std::string const& lang) -> bool { | |
| bool const valid = state->GetLanguageEnabled(lang); | |
| if (!valid) { | |
| makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("The language ", lang, | |
| " was requested for compilation but was not enabled." | |
| " To enable a language it needs to be specified in a" | |
| " 'project' or 'enable_language' command in the root" | |
| " CMakeLists.txt")); | |
| } | |
| return valid; | |
| }; | |
| return std::all_of(languagesNeeded.cbegin(), languagesNeeded.cend(), unary); | |
| } | |
| void cmCommonTargetGenerator::ComputeRustFlagsForObjects( | |
| std::string& linkCrates, std::string& nativeObjects, | |
| std::vector<std::string> const& objects) | |
| { | |
| auto const processObject = [&](std::string const& obj) { | |
| cm::filesystem::path const objPath(obj); | |
| if (objPath.extension() == ".rlib") { | |
| // Drop the "lib..." prefix and the ".rs" suffix. The prefix is required | |
| // by Rust on the crate rlib file, but is hidden from the user when using | |
| // the crate from Rust source code, so we drop it to be consistent with | |
| // common usage in Rust. | |
| std::string objStem = objPath.stem().string(); | |
| objStem = objStem.substr(3, objStem.length() - 6); | |
| linkCrates = cmStrCat(std::move(linkCrates), " --extern=", objStem, '=', | |
| this->LocalCommonGenerator->ConvertToOutputFormat( | |
| obj, cmOutputConverter::SHELL)); | |
| } else { | |
| nativeObjects = | |
| cmStrCat(std::move(nativeObjects), " -Clink-arg=", | |
| this->LocalCommonGenerator->ConvertToOutputFormat( | |
| obj, cmOutputConverter::SHELL)); | |
| } | |
| }; | |
| for (std::string const& obj : objects) { | |
| processObject(obj); | |
| } | |
| } | |
| std::vector<std::string> cmCommonTargetGenerator::ParseWrappers( | |
| std::vector<BT<std::string>> const& wrappers, cm::string_view prefix, | |
| cm::string_view targetPropName, std::string const& targetName, | |
| std::string const& config, std::string const& lang) const | |
| { | |
| std::vector<std::string> wrapperCommandLine; | |
| if (wrappers.empty()) { | |
| return wrapperCommandLine; | |
| } | |
| cm::GenEx::Context context(this->LocalCommonGenerator, config, lang); | |
| for (BT<std::string> const& wrapper : wrappers) { | |
| std::string const wrapperPropName = cmStrCat(prefix, wrapper.Value); | |
| cmValue globalPropValue = | |
| this->Makefile->GetState()->GetGlobalProperty(wrapperPropName); | |
| if (!globalPropValue.IsSet()) { | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("Specified wrapper \'", wrapper.Value, "\' for target \'", | |
| targetName, "\' does not exist. Set the GLOBAL property \'", | |
| wrapperPropName, "\'."), | |
| wrapper.Backtrace); | |
| return {}; | |
| } | |
| cmGeneratorExpressionDAGChecker dagChecker{ this->GeneratorTarget, | |
| std::string(targetPropName), | |
| nullptr, nullptr, context }; | |
| std::string const evaluatedWrapper = cmGeneratorExpression::Evaluate( | |
| *globalPropValue, context.LG, context.Config, this->GeneratorTarget, | |
| &dagChecker, this->GeneratorTarget, context.Language); | |
| // An empty definition is a no-op wrapper, but empty arguments within a | |
| // non-empty definition must reach the command line. | |
| if (!evaluatedWrapper.empty()) { | |
| cm::append(wrapperCommandLine, | |
| cmList{ evaluatedWrapper, cmList::EmptyElements::Yes }); | |
| } | |
| } | |
| return wrapperCommandLine; | |
| } |