| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmMakefileTargetGenerator.h" | |
| #include <algorithm> | |
| #include <array> | |
| #include <cassert> | |
| #include <cstdio> | |
| #include <functional> | |
| #include <iterator> | |
| #include <sstream> | |
| #include <unordered_map> | |
| #include <unordered_set> | |
| #include <utility> | |
| #include <cm/memory> | |
| #include <cm/optional> | |
| #include <cm/string_view> | |
| #include <cmext/algorithm> | |
| #include <cmext/string_view> | |
| #include "cm_codecvt_Encoding.hxx" | |
| #include "cmComputeLinkInformation.h" | |
| #include "cmCustomCommand.h" | |
| #include "cmCustomCommandGenerator.h" | |
| #include "cmFileSetMetadata.h" | |
| #include "cmGenExContext.h" | |
| #include "cmGeneratedFileStream.h" | |
| #include "cmGeneratorExpression.h" | |
| #include "cmGeneratorFileSet.h" | |
| #include "cmGeneratorOptions.h" | |
| #include "cmGeneratorTarget.h" | |
| #include "cmGlobalUnixMakefileGenerator3.h" | |
| #include "cmLinkLineComputer.h" // IWYU pragma: keep | |
| #include "cmList.h" | |
| #include "cmListFileCache.h" | |
| #include "cmLocalCommonGenerator.h" | |
| #include "cmLocalGenerator.h" | |
| #include "cmLocalUnixMakefileGenerator3.h" | |
| #include "cmMakefile.h" | |
| #include "cmMakefileExecutableTargetGenerator.h" | |
| #include "cmMakefileLibraryTargetGenerator.h" | |
| #include "cmMakefileUtilityTargetGenerator.h" | |
| #include "cmMessageType.h" | |
| #include "cmOutputConverter.h" | |
| #include "cmPolicies.h" | |
| #include "cmRange.h" | |
| #include "cmRulePlaceholderExpander.h" | |
| #include "cmScriptGenerator.h" | |
| #include "cmSourceFile.h" | |
| #include "cmSourceFileLocationKind.h" | |
| #include "cmState.h" | |
| #include "cmStateDirectory.h" | |
| #include "cmStateSnapshot.h" | |
| #include "cmStateTypes.h" | |
| #include "cmStringAlgorithms.h" | |
| #include "cmSystemTools.h" | |
| #include "cmTargetTypes.h" | |
| #include "cmValue.h" | |
| #include "cmake.h" | |
| cmMakefileTargetGenerator::cmMakefileTargetGenerator(cmGeneratorTarget* target) | |
| : cmCommonTargetGenerator(target) | |
| { | |
| this->CustomCommandDriver = OnBuild; | |
| this->LocalGenerator = | |
| static_cast<cmLocalUnixMakefileGenerator3*>(target->GetLocalGenerator()); | |
| this->GlobalGenerator = static_cast<cmGlobalUnixMakefileGenerator3*>( | |
| this->LocalGenerator->GetGlobalGenerator()); | |
| cmake* cm = this->GlobalGenerator->GetCMakeInstance(); | |
| this->NoRuleMessages = false; | |
| if (cmValue ruleStatus = | |
| cm->GetState()->GetGlobalProperty("RULE_MESSAGES")) { | |
| this->NoRuleMessages = ruleStatus.IsOff(); | |
| } | |
| switch (this->GeneratorTarget->GetPolicyStatusCMP0113()) { | |
| case cmPolicies::WARN: | |
| CM_FALLTHROUGH; | |
| case cmPolicies::OLD: | |
| this->CMP0113New = false; | |
| break; | |
| case cmPolicies::NEW: | |
| this->CMP0113New = true; | |
| break; | |
| } | |
| this->MacOSXContentGenerator = | |
| cm::make_unique<MacOSXContentGeneratorType>(this); | |
| } | |
| cmMakefileTargetGenerator::~cmMakefileTargetGenerator() = default; | |
| std::unique_ptr<cmMakefileTargetGenerator> cmMakefileTargetGenerator::New( | |
| cmGeneratorTarget* tgt) | |
| { | |
| std::unique_ptr<cmMakefileTargetGenerator> result; | |
| switch (tgt->GetType()) { | |
| case cm::TargetType::EXECUTABLE: | |
| result = cm::make_unique<cmMakefileExecutableTargetGenerator>(tgt); | |
| break; | |
| case cm::TargetType::STATIC_LIBRARY: | |
| case cm::TargetType::SHARED_LIBRARY: | |
| case cm::TargetType::MODULE_LIBRARY: | |
| case cm::TargetType::OBJECT_LIBRARY: | |
| result = cm::make_unique<cmMakefileLibraryTargetGenerator>(tgt); | |
| break; | |
| case cm::TargetType::INTERFACE_LIBRARY: | |
| case cm::TargetType::UTILITY: | |
| result = cm::make_unique<cmMakefileUtilityTargetGenerator>(tgt); | |
| break; | |
| default: | |
| return result; | |
| // break; /* unreachable */ | |
| } | |
| return result; | |
| } | |
| std::string const& cmMakefileTargetGenerator::GetConfigName() const | |
| { | |
| auto const& configNames = this->LocalGenerator->GetConfigNames(); | |
| assert(configNames.size() == 1); | |
| return configNames.front(); | |
| } | |
| void cmMakefileTargetGenerator::GetDeviceLinkFlags( | |
| std::string& linkFlags, std::string const& linkLanguage) | |
| { | |
| cmGeneratorTarget::DeviceLinkSetter setter(*this->GetGeneratorTarget()); | |
| std::vector<std::string> linkOpts; | |
| this->GeneratorTarget->GetLinkOptions(linkOpts, this->GetConfigName(), | |
| linkLanguage); | |
| this->LocalGenerator->SetLinkScriptShell( | |
| this->GlobalGenerator->GetUseLinkScript()); | |
| // LINK_OPTIONS are escaped. | |
| this->LocalGenerator->AppendCompileOptions(linkFlags, linkOpts); | |
| this->LocalGenerator->SetLinkScriptShell(false); | |
| } | |
| void cmMakefileTargetGenerator::GetTargetLinkFlags( | |
| std::string& flags, std::string const& linkLanguage) | |
| { | |
| this->LocalGenerator->AddTargetPropertyLinkFlags( | |
| flags, this->GeneratorTarget, this->GetConfigName()); | |
| std::vector<std::string> opts; | |
| this->GeneratorTarget->GetLinkOptions(opts, this->GetConfigName(), | |
| linkLanguage); | |
| this->LocalGenerator->SetLinkScriptShell( | |
| this->GlobalGenerator->GetUseLinkScript()); | |
| // LINK_OPTIONS are escaped. | |
| this->LocalGenerator->AppendCompileOptions(flags, opts); | |
| this->LocalGenerator->SetLinkScriptShell(false); | |
| this->LocalGenerator->AppendLinkerTypeFlags( | |
| flags, this->GeneratorTarget, this->GetConfigName(), linkLanguage); | |
| this->LocalGenerator->AppendPositionIndependentLinkerFlags( | |
| flags, this->GeneratorTarget, this->GetConfigName(), linkLanguage); | |
| this->LocalGenerator->AppendWarningAsErrorLinkerFlags( | |
| flags, this->GeneratorTarget, linkLanguage); | |
| this->LocalGenerator->AppendDependencyInfoLinkerFlags( | |
| flags, this->GeneratorTarget, this->GetConfigName(), linkLanguage); | |
| } | |
| void cmMakefileTargetGenerator::CreateRuleFile() | |
| { | |
| // Create a directory for this target. | |
| this->TargetBuildDirectoryFull = | |
| this->GeneratorTarget->GetSupportDirectory(); | |
| this->TargetBuildDirectory = this->LocalGenerator->MaybeRelativeToCurBinDir( | |
| this->TargetBuildDirectoryFull); | |
| cmSystemTools::MakeDirectory(this->TargetBuildDirectoryFull); | |
| // Construct the rule file name. | |
| this->BuildFileName = cmStrCat(this->TargetBuildDirectory, "/build.make"); | |
| this->BuildFileNameFull = | |
| cmStrCat(this->TargetBuildDirectoryFull, "/build.make"); | |
| // Construct the rule file name. | |
| this->ProgressFileNameFull = | |
| cmStrCat(this->TargetBuildDirectoryFull, "/progress.make"); | |
| // reset the progress count | |
| this->NumberOfProgressActions = 0; | |
| // Open the rule file. This should be copy-if-different because the | |
| // rules may depend on this file itself. | |
| this->BuildFileStream = cm::make_unique<cmGeneratedFileStream>( | |
| this->BuildFileNameFull, false, | |
| this->GlobalGenerator->GetMakefileEncoding()); | |
| if (!this->BuildFileStream) { | |
| return; | |
| } | |
| this->BuildFileStream->SetCopyIfDifferent(true); | |
| this->LocalGenerator->WriteDisclaimer(*this->BuildFileStream); | |
| if (this->GlobalGenerator->AllowDeleteOnError()) { | |
| std::vector<std::string> no_depends; | |
| std::vector<std::string> no_commands; | |
| this->LocalGenerator->WriteMakeRule( | |
| *this->BuildFileStream, "Delete rule output on recipe failure.", | |
| ".DELETE_ON_ERROR", no_depends, no_commands, false); | |
| } | |
| this->LocalGenerator->WriteSpecialTargetsTop(*this->BuildFileStream); | |
| } | |
| void cmMakefileTargetGenerator::WriteTargetBuildRules() | |
| { | |
| cm::GenEx::Context context(this->LocalGenerator, this->GetConfigName()); | |
| this->GeneratorTarget->CheckCxxModuleStatus(this->GetConfigName()); | |
| // -- Write the custom commands for this target | |
| // Evaluates generator expressions and expands prop_value | |
| auto evaluatedFiles = [this](std::string const& prop_value) -> cmList { | |
| cmList files{ cmGeneratorExpression::Evaluate( | |
| prop_value, this->LocalGenerator, this->GetConfigName(), | |
| this->GeneratorTarget) }; | |
| return files; | |
| }; | |
| // Look for additional files registered for cleaning in this directory. | |
| if (cmValue prop_value = | |
| this->Makefile->GetProperty("ADDITIONAL_MAKE_CLEAN_FILES")) { | |
| auto const files = evaluatedFiles(*prop_value); | |
| this->CleanFiles.insert(files.begin(), files.end()); | |
| } | |
| // Look for additional files registered for cleaning in this target. | |
| if (cmValue prop_value = | |
| this->GeneratorTarget->GetProperty("ADDITIONAL_CLEAN_FILES")) { | |
| auto const files = evaluatedFiles(*prop_value); | |
| // For relative path support | |
| std::string const& binaryDir = | |
| this->LocalGenerator->GetCurrentBinaryDirectory(); | |
| for (std::string const& cfl : files) { | |
| this->CleanFiles.insert(cmSystemTools::CollapseFullPath(cfl, binaryDir)); | |
| } | |
| } | |
| // Look for ISPC extra object files generated by this target | |
| auto const ispcAdditionalObjs = | |
| this->GeneratorTarget->GetGeneratedISPCObjects(this->GetConfigName()); | |
| for (auto const& ispcObj : ispcAdditionalObjs) { | |
| this->CleanFiles.insert( | |
| this->LocalGenerator->MaybeRelativeToCurBinDir(ispcObj.second)); | |
| } | |
| // add custom commands to the clean rules? | |
| bool const clean = this->Makefile->GetProperty("CLEAN_NO_CUSTOM").IsOff(); | |
| // First generate the object rule files. Save a list of all object | |
| // files for this target. | |
| std::vector<cmSourceFile const*> customCommands; | |
| this->GeneratorTarget->GetCustomCommands(customCommands, | |
| this->GetConfigName()); | |
| std::vector<std::string> codegen_depends; | |
| codegen_depends.reserve(customCommands.size()); | |
| for (cmSourceFile const* sf : customCommands) { | |
| if (this->CMP0113New && | |
| !this->LocalGenerator->GetCommandsVisited(this->GeneratorTarget) | |
| .insert(sf) | |
| .second) { | |
| continue; | |
| } | |
| cmCustomCommandGenerator ccg(*sf->GetCustomCommand(), | |
| this->GetConfigName(), this->LocalGenerator); | |
| this->GenerateCustomRuleFile(ccg); | |
| if (clean) { | |
| std::vector<std::string> const& outputs = ccg.GetOutputs(); | |
| for (std::string const& output : outputs) { | |
| this->CleanFiles.insert( | |
| this->LocalGenerator->MaybeRelativeToCurBinDir(output)); | |
| } | |
| std::vector<std::string> const& byproducts = ccg.GetByproducts(); | |
| for (std::string const& byproduct : byproducts) { | |
| this->CleanFiles.insert( | |
| this->LocalGenerator->MaybeRelativeToCurBinDir(byproduct)); | |
| } | |
| } | |
| if (ccg.GetCC().GetCodegen()) { | |
| std::string const& output = ccg.GetOutputs().front(); | |
| // We always attach the actual commands to the first output. | |
| codegen_depends.emplace_back(output); | |
| } | |
| } | |
| // Some make tools need a special dependency for an empty rule. | |
| if (codegen_depends.empty()) { | |
| std::string hack = this->GlobalGenerator->GetEmptyRuleHackDepends(); | |
| if (!hack.empty()) { | |
| codegen_depends.emplace_back(std::move(hack)); | |
| } | |
| } | |
| // Construct the codegen target. | |
| { | |
| std::string const codegenTarget = cmStrCat( | |
| this->LocalGenerator->GetRelativeTargetDirectory(this->GeneratorTarget), | |
| "/codegen"); | |
| // Write the rule. | |
| this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr, | |
| codegenTarget, codegen_depends, {}, | |
| true); | |
| } | |
| // Add byproducts from build events to the clean rules | |
| if (clean) { | |
| std::vector<cmCustomCommand> buildEventCommands = | |
| this->GeneratorTarget->GetPreBuildCommands(); | |
| cm::append(buildEventCommands, | |
| this->GeneratorTarget->GetPreLinkCommands()); | |
| cm::append(buildEventCommands, | |
| this->GeneratorTarget->GetPostBuildCommands()); | |
| for (auto const& be : buildEventCommands) { | |
| cmCustomCommandGenerator beg(be, this->GetConfigName(), | |
| this->LocalGenerator); | |
| std::vector<std::string> const& byproducts = beg.GetByproducts(); | |
| for (std::string const& byproduct : byproducts) { | |
| this->CleanFiles.insert( | |
| this->LocalGenerator->MaybeRelativeToCurBinDir(byproduct)); | |
| } | |
| } | |
| } | |
| std::vector<cmSourceFile const*> headerSources; | |
| this->GeneratorTarget->GetHeaderSources(headerSources, | |
| this->GetConfigName()); | |
| this->OSXBundleGenerator->GenerateMacOSXContentStatements( | |
| headerSources, this->MacOSXContentGenerator.get(), this->GetConfigName()); | |
| std::vector<cmSourceFile const*> extraSources; | |
| this->GeneratorTarget->GetExtraSources(extraSources, this->GetConfigName()); | |
| this->OSXBundleGenerator->GenerateMacOSXContentStatements( | |
| extraSources, this->MacOSXContentGenerator.get(), this->GetConfigName()); | |
| cmValue pchExtension = this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION"); | |
| std::vector<cmSourceFile const*> externalObjects; | |
| this->GeneratorTarget->GetExternalObjects(externalObjects, | |
| this->GetConfigName()); | |
| for (cmSourceFile const* sf : externalObjects) { | |
| auto const& objectFileName = sf->GetFullPath(); | |
| if (!cmHasSuffix(objectFileName, pchExtension)) { | |
| this->ExternalObjects.push_back(objectFileName); | |
| } | |
| } | |
| std::vector<cmSourceFile const*> objectSources; | |
| this->GeneratorTarget->GetObjectSources(objectSources, | |
| this->GetConfigName()); | |
| // validate that all languages requested are enabled. | |
| std::set<std::string> requiredLangs; | |
| if (this->HaveRequiredLanguages(objectSources, this->GetConfigName(), | |
| requiredLangs)) { | |
| for (cmSourceFile const* sf : objectSources) { | |
| // Generate this object file's rule file. | |
| this->WriteObjectRuleFiles(*sf); | |
| } | |
| } | |
| for (cmSourceFile const* sf : objectSources) { | |
| cmGeneratorFileSet const* fileSet = | |
| this->GeneratorTarget->GetFileSetForSource(this->GetConfigName(), sf); | |
| if (fileSet && fileSet->GetType() == cm::FileSetMetadata::CXX_MODULES && | |
| sf->GetLanguage() != "CXX"_s) { | |
| this->Makefile->IssueMessage( | |
| MessageType::FATAL_ERROR, | |
| cmStrCat("Target \"", this->GeneratorTarget->GetName(), | |
| "\" contains the source\n ", sf->GetFullPath(), | |
| "\nin a file set of type \"", | |
| cm::FileSetMetadata::CXX_MODULES, | |
| R"(" but the source is not classified as a "CXX" source.)")); | |
| } | |
| } | |
| } | |
| void cmMakefileTargetGenerator::WriteCommonCodeRules() | |
| { | |
| char const* root = (this->Makefile->IsOn("CMAKE_MAKE_INCLUDE_FROM_ROOT") | |
| ? "$(CMAKE_BINARY_DIR)/" | |
| : ""); | |
| // Include the dependencies for the target. | |
| std::string dependFileNameFull = | |
| cmStrCat(this->TargetBuildDirectoryFull, "/depend.make"); | |
| *this->BuildFileStream | |
| << "# Include any dependencies generated for this target.\n" | |
| << this->GlobalGenerator->IncludeDirective << " " << root | |
| << cmSystemTools::ConvertToOutputPath( | |
| this->LocalGenerator->MaybeRelativeToTopBinDir(dependFileNameFull)) | |
| << "\n"; | |
| // Scan any custom commands to check if DEPFILE option is specified | |
| bool ccGenerateDeps = false; | |
| std::vector<cmSourceFile const*> customCommands; | |
| this->GeneratorTarget->GetCustomCommands(customCommands, | |
| this->GetConfigName()); | |
| for (cmSourceFile const* sf : customCommands) { | |
| if (!sf->GetCustomCommand()->GetDepfile().empty()) { | |
| ccGenerateDeps = true; | |
| break; | |
| } | |
| } | |
| std::string depsUseCompiler = "CMAKE_DEPENDS_USE_COMPILER"; | |
| bool compilerGenerateDeps = | |
| this->GlobalGenerator->SupportsCompilerDependencies() && | |
| (!this->Makefile->IsDefinitionSet(depsUseCompiler) || | |
| this->Makefile->IsOn(depsUseCompiler)); | |
| bool linkerGenerateDeps = | |
| this->GeneratorTarget->HasLinkDependencyFile(this->GetConfigName()); | |
| if (compilerGenerateDeps || linkerGenerateDeps || ccGenerateDeps) { | |
| std::string const compilerDependFile = | |
| cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.make"); | |
| *this->BuildFileStream << "# Include any dependencies generated by the " | |
| "compiler for this target.\n" | |
| << this->GlobalGenerator->IncludeDirective << " " | |
| << root | |
| << cmSystemTools::ConvertToOutputPath( | |
| this->LocalGenerator->MaybeRelativeToTopBinDir( | |
| compilerDependFile)) | |
| << "\n\n"; | |
| // Write an empty dependency file. | |
| cmGeneratedFileStream depFileStream( | |
| compilerDependFile, false, this->GlobalGenerator->GetMakefileEncoding()); | |
| depFileStream << "# Empty compiler generated dependencies file for " | |
| << this->GeneratorTarget->GetName() << ".\n" | |
| << "# This may be replaced when dependencies are built.\n"; | |
| // remove internal dependency file | |
| cmSystemTools::RemoveFile( | |
| cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.internal")); | |
| std::string compilerDependTimestamp = | |
| cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.ts"); | |
| if (!cmSystemTools::FileExists(compilerDependTimestamp)) { | |
| // Write a dependency timestamp file. | |
| cmGeneratedFileStream timestampFileStream( | |
| compilerDependTimestamp, false, | |
| this->GlobalGenerator->GetMakefileEncoding()); | |
| timestampFileStream | |
| << "# CMAKE generated file: DO NOT EDIT!\n" | |
| << "# Timestamp file for compiler generated dependencies " | |
| "management for " | |
| << this->GeneratorTarget->GetName() << ".\n"; | |
| } | |
| } | |
| if (compilerGenerateDeps) { | |
| // deactivate no longer needed legacy dependency files | |
| // Write an empty dependency file. | |
| cmGeneratedFileStream legacyDepFileStream( | |
| dependFileNameFull, false, this->GlobalGenerator->GetMakefileEncoding()); | |
| legacyDepFileStream | |
| << "# Empty dependencies file for " << this->GeneratorTarget->GetName() | |
| << ".\n" | |
| << "# This may be replaced when dependencies are built.\n"; | |
| // remove internal dependency file | |
| cmSystemTools::RemoveFile( | |
| cmStrCat(this->TargetBuildDirectoryFull, "/depend.internal")); | |
| } else { | |
| // make sure the depend file exists | |
| if (!cmSystemTools::FileExists(dependFileNameFull)) { | |
| // Write an empty dependency file. | |
| cmGeneratedFileStream depFileStream( | |
| dependFileNameFull, false, | |
| this->GlobalGenerator->GetMakefileEncoding()); | |
| depFileStream << "# Empty dependencies file for " | |
| << this->GeneratorTarget->GetName() << ".\n" | |
| << "# This may be replaced when dependencies are built.\n"; | |
| } | |
| } | |
| if (!this->NoRuleMessages) { | |
| // Include the progress variables for the target. | |
| *this->BuildFileStream | |
| << "# Include the progress variables for this target.\n" | |
| << this->GlobalGenerator->IncludeDirective << " " << root | |
| << cmSystemTools::ConvertToOutputPath( | |
| this->LocalGenerator->MaybeRelativeToTopBinDir( | |
| this->ProgressFileNameFull)) | |
| << "\n\n"; | |
| } | |
| // Open the flags file. This should be copy-if-different because the | |
| // rules may depend on this file itself. | |
| this->FlagFileNameFull = | |
| cmStrCat(this->TargetBuildDirectoryFull, "/flags.make"); | |
| this->FlagFileStream = cm::make_unique<cmGeneratedFileStream>( | |
| this->FlagFileNameFull, false, | |
| this->GlobalGenerator->GetMakefileEncoding()); | |
| if (!this->FlagFileStream) { | |
| return; | |
| } | |
| this->FlagFileStream->SetCopyIfDifferent(true); | |
| this->LocalGenerator->WriteDisclaimer(*this->FlagFileStream); | |
| // Include the flags for the target. | |
| *this->BuildFileStream | |
| << "# Include the compile flags for this target's objects.\n" | |
| << this->GlobalGenerator->IncludeDirective << " " << root | |
| << cmSystemTools::ConvertToOutputPath( | |
| this->LocalGenerator->MaybeRelativeToTopBinDir( | |
| this->FlagFileNameFull)) | |
| << "\n\n"; | |
| } | |
| void cmMakefileTargetGenerator::WriteTargetLanguageFlags() | |
| { | |
| // write language flags for target | |
| std::set<std::string> languages; | |
| this->GeneratorTarget->GetLanguages( | |
| languages, this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE")); | |
| // put the compiler in the rules.make file so that if it changes | |
| // things rebuild | |
| for (std::string const& language : languages) { | |
| std::string compiler = cmStrCat("CMAKE_", language, "_COMPILER"); | |
| *this->FlagFileStream << "# compile " << language << " with " | |
| << this->Makefile->GetSafeDefinition(compiler) | |
| << "\n"; | |
| } | |
| bool const escapeOctothorpe = this->GlobalGenerator->CanEscapeOctothorpe(); | |
| for (std::string const& language : languages) { | |
| std::string defines = this->GetDefines(language, this->GetConfigName()); | |
| std::string includes = this->GetIncludes(language, this->GetConfigName()); | |
| if (escapeOctothorpe) { | |
| // Escape comment characters so they do not terminate assignment. | |
| cmSystemTools::ReplaceString(defines, "#", "\\#"); | |
| cmSystemTools::ReplaceString(includes, "#", "\\#"); | |
| } | |
| *this->FlagFileStream << language << "_DEFINES = " << defines << "\n\n"; | |
| *this->FlagFileStream << language << "_INCLUDES = " << includes << "\n\n"; | |
| std::vector<std::string> architectures = | |
| this->GeneratorTarget->GetAppleArchs(this->GetConfigName(), language); | |
| architectures.emplace_back(); | |
| for (std::string const& arch : architectures) { | |
| std::string flags = | |
| this->GetFlags(language, this->GetConfigName(), arch); | |
| if (escapeOctothorpe) { | |
| cmSystemTools::ReplaceString(flags, "#", "\\#"); | |
| } | |
| *this->FlagFileStream << language << "_FLAGS" << arch << " = " << flags | |
| << "\n\n"; | |
| } | |
| } | |
| } | |
| void cmMakefileTargetGenerator::MacOSXContentGeneratorType::operator()( | |
| cmSourceFile const& source, char const* pkgloc, std::string const& config) | |
| { | |
| // Skip OS X content when not building a Framework or Bundle. | |
| if (!this->Generator->GetGeneratorTarget()->IsBundleOnApple()) { | |
| return; | |
| } | |
| std::string macdir = | |
| this->Generator->OSXBundleGenerator->InitMacOSXContentDirectory(pkgloc, | |
| config); | |
| // Get the input file location. | |
| std::string const& input = source.GetFullPath(); | |
| // Get the output file location. | |
| std::string output = | |
| cmStrCat(macdir, '/', cmSystemTools::GetFilenameName(input)); | |
| this->Generator->CleanFiles.insert( | |
| this->Generator->LocalGenerator->MaybeRelativeToCurBinDir(output)); | |
| output = this->Generator->LocalGenerator->MaybeRelativeToTopBinDir(output); | |
| // Create a rule to copy the content into the bundle. | |
| std::vector<std::string> depends; | |
| std::vector<std::string> commands; | |
| depends.push_back(input); | |
| std::string copyEcho = cmStrCat("Copying OS X content ", output); | |
| this->Generator->LocalGenerator->AppendEcho( | |
| commands, copyEcho, cmLocalUnixMakefileGenerator3::EchoBuild); | |
| std::string copyCommand = | |
| cmStrCat(cmSystemTools::FileIsDirectory(input) | |
| ? "$(CMAKE_COMMAND) -E copy_directory " | |
| : "$(CMAKE_COMMAND) -E copy ", | |
| this->Generator->LocalGenerator->ConvertToOutputFormat( | |
| input, cmOutputConverter::SHELL), | |
| ' ', | |
| this->Generator->LocalGenerator->ConvertToOutputFormat( | |
| output, cmOutputConverter::SHELL)); | |
| commands.push_back(std::move(copyCommand)); | |
| this->Generator->LocalGenerator->WriteMakeRule( | |
| *this->Generator->BuildFileStream, nullptr, output, depends, commands, | |
| false); | |
| this->Generator->ExtraFiles.insert(output); | |
| } | |
| void cmMakefileTargetGenerator::WriteObjectRuleFiles( | |
| cmSourceFile const& source) | |
| { | |
| // Identify the language of the source file. | |
| std::string const lang = this->GeneratorTarget->GetSourceFileLanguage( | |
| &source, this->GetConfigName()); | |
| if (lang.empty()) { | |
| // don't know anything about this file so skip it | |
| return; | |
| } | |
| // Use compiler to generate dependencies, if supported. | |
| bool const compilerGenerateDeps = | |
| this->GlobalGenerator->SupportsCompilerDependencies() && | |
| this->Makefile | |
| ->GetDefinition(cmStrCat("CMAKE_", lang, "_DEPENDS_USE_COMPILER")) | |
| .IsOn(); | |
| auto const scanner = compilerGenerateDeps ? cmDependencyScannerKind::Compiler | |
| : cmDependencyScannerKind::CMake; | |
| // Get the full path name of the object file. | |
| std::string const& objectName = | |
| this->GeneratorTarget->GetObjectName(&source); | |
| std::string const obj = | |
| cmStrCat(this->TargetBuildDirectory, '/', objectName); | |
| // Avoid generating duplicate rules. | |
| if (this->ObjectFiles.find(obj) == this->ObjectFiles.end()) { | |
| this->ObjectFiles.insert(obj); | |
| } else { | |
| cmSystemTools::Message( | |
| cmStrCat("Warning: Source file \"", source.GetFullPath(), | |
| "\" is listed multiple times for target \"", | |
| this->GeneratorTarget->GetName(), "\"."), | |
| "Warning"); | |
| return; | |
| } | |
| // Create the directory containing the object file. This may be a | |
| // subdirectory under the target's directory. | |
| { | |
| std::string const dir = cmSystemTools::GetFilenamePath(obj); | |
| cmSystemTools::MakeDirectory(this->LocalGenerator->ConvertToFullPath(dir)); | |
| } | |
| // Save this in the target's list of object files. | |
| this->Objects.push_back(obj); | |
| this->CleanFiles.insert(obj); | |
| std::vector<std::string> depends; | |
| // The object file should be checked for dependency integrity. | |
| std::string objFullPath = | |
| cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), '/', obj); | |
| std::string const srcFullPath = source.GetFullPath(); | |
| this->LocalGenerator->AddImplicitDepends(this->GeneratorTarget, lang, | |
| objFullPath, srcFullPath, scanner); | |
| this->LocalGenerator->AppendRuleDepend(depends, | |
| this->FlagFileNameFull.c_str()); | |
| this->LocalGenerator->AppendRuleDepends(depends, | |
| this->FlagFileDepends[lang]); | |
| // generate the depend scanning rule | |
| this->WriteObjectDependRules(source, depends); | |
| std::string const config = this->GetConfigName(); | |
| std::string const configUpper = cmSystemTools::UpperCase(config); | |
| // lookup for the associated file set, if any. | |
| auto const* fileSet = | |
| this->GeneratorTarget->GetFileSetForSource(config, &source); | |
| // Add precompile headers dependencies | |
| std::vector<std::string> pchArchs = | |
| this->GeneratorTarget->GetPchArchs(config, lang); | |
| std::string filterArch; | |
| std::unordered_map<std::string, std::string> pchSources; | |
| for (std::string const& arch : pchArchs) { | |
| std::string const pchSource = | |
| this->GeneratorTarget->GetPchSource(config, lang, arch); | |
| if (pchSource == source.GetFullPath()) { | |
| filterArch = arch; | |
| } | |
| if (!pchSource.empty()) { | |
| pchSources.insert(std::make_pair(pchSource, arch)); | |
| } | |
| } | |
| if (!pchSources.empty() && | |
| !((fileSet && fileSet->GetProperty("SKIP_PRECOMPILE_HEADERS")) || | |
| source.GetProperty("SKIP_PRECOMPILE_HEADERS"))) { | |
| for (std::string const& arch : pchArchs) { | |
| std::string const& pchHeader = | |
| this->GeneratorTarget->GetPchHeader(config, lang, arch); | |
| depends.push_back(pchHeader); | |
| if (pchSources.find(source.GetFullPath()) == pchSources.end()) { | |
| depends.push_back( | |
| this->GeneratorTarget->GetPchFile(config, lang, arch)); | |
| } | |
| this->LocalGenerator->AddImplicitDepends( | |
| this->GeneratorTarget, lang, objFullPath, pchHeader, scanner); | |
| } | |
| } | |
| if (lang != "ISPC") { | |
| auto const& headers = | |
| this->GeneratorTarget->GetGeneratedISPCHeaders(config); | |
| if (!headers.empty()) { | |
| depends.insert(depends.end(), headers.begin(), headers.end()); | |
| } | |
| } | |
| std::string relativeObj = | |
| cmStrCat(this->LocalGenerator->GetHomeRelativeOutputPath(), obj); | |
| // Write the build rule. | |
| // Build the set of compiler flags. | |
| std::string flags; | |
| // Explicitly add the explicit language flag before any other flag | |
| // so user flags can override it. | |
| this->GeneratorTarget->AddExplicitLanguageFlags(flags, source, config); | |
| // Add language-specific flags. | |
| std::string const langFlags = | |
| cmStrCat("$(", lang, "_FLAGS", filterArch, ')'); | |
| this->LocalGenerator->AppendFlags(flags, langFlags); | |
| cmGeneratorExpressionInterpreter genexInterpreter( | |
| this->LocalGenerator, config, this->GeneratorTarget, lang); | |
| // Add Fortran format flags. | |
| if (lang == "Fortran") { | |
| this->AppendFortranFormatFlags(flags, source); | |
| this->AppendFortranPreprocessFlags(flags, source); | |
| } | |
| std::string ispcHeaderRelative; | |
| std::string ispcHeaderForShell; | |
| if (lang == "ISPC") { | |
| std::string ispcSource = | |
| cmSystemTools::GetFilenameWithoutLastExtension(objectName); | |
| ispcSource = cmSystemTools::GetFilenameWithoutLastExtension(ispcSource); | |
| cmValue const ispcSuffixProp = | |
| this->GeneratorTarget->GetProperty("ISPC_HEADER_SUFFIX"); | |
| assert(ispcSuffixProp); | |
| std::string directory = this->GeneratorTarget->GetObjectDirectory(config); | |
| if (cmValue prop = | |
| this->GeneratorTarget->GetProperty("ISPC_HEADER_DIRECTORY")) { | |
| directory = | |
| cmStrCat(this->LocalGenerator->GetBinaryDirectory(), '/', *prop); | |
| } | |
| ispcHeaderRelative = cmStrCat(directory, '/', ispcSource, *ispcSuffixProp); | |
| ispcHeaderForShell = this->LocalGenerator->ConvertToOutputFormat( | |
| ispcHeaderRelative, cmOutputConverter::SHELL); | |
| } | |
| // Add flags from source file properties. | |
| std::string const COMPILE_FLAGS("COMPILE_FLAGS"); | |
| if (cmValue cflags = source.GetProperty(COMPILE_FLAGS)) { | |
| std::string const& evaluatedFlags = | |
| genexInterpreter.Evaluate(*cflags, COMPILE_FLAGS); | |
| this->LocalGenerator->AppendFlags(flags, evaluatedFlags); | |
| *this->FlagFileStream << "# Custom flags: " << relativeObj | |
| << "_FLAGS = " << evaluatedFlags << "\n" | |
| << "\n"; | |
| } | |
| std::string const COMPILE_OPTIONS("COMPILE_OPTIONS"); | |
| if (cmValue coptions = source.GetProperty(COMPILE_OPTIONS)) { | |
| std::string const& evaluatedOptions = | |
| genexInterpreter.Evaluate(*coptions, COMPILE_OPTIONS); | |
| this->LocalGenerator->AppendCompileOptions(flags, evaluatedOptions); | |
| *this->FlagFileStream << "# Custom options: " << relativeObj | |
| << "_OPTIONS = " << evaluatedOptions << "\n" | |
| << "\n"; | |
| } | |
| // Add flags from file set properties. | |
| if (fileSet) { | |
| auto options = fileSet->BelongsTo(this->GeneratorTarget) | |
| ? fileSet->GetCompileOptions(config, lang) | |
| : fileSet->GetInterfaceCompileOptions(config, lang); | |
| if (!options.empty()) { | |
| this->LocalGenerator->AppendCompileOptions(flags, | |
| cm::remove_BT(options)); | |
| *this->FlagFileStream << "# Custom options (File Set '" | |
| << fileSet->GetName() << "'): " << relativeObj | |
| << "_OPTIONS = " << cmList::to_string(options) | |
| << "\n" | |
| << "\n"; | |
| } | |
| } | |
| // Add precompile headers compile options. | |
| if (!pchSources.empty() && | |
| !((fileSet && fileSet->GetProperty("SKIP_PRECOMPILE_HEADERS")) || | |
| source.GetProperty("SKIP_PRECOMPILE_HEADERS"))) { | |
| std::string pchOptions; | |
| auto const pchIt = pchSources.find(source.GetFullPath()); | |
| if (pchIt != pchSources.end()) { | |
| pchOptions = this->GeneratorTarget->GetPchCreateCompileOptions( | |
| config, lang, pchIt->second); | |
| } else { | |
| pchOptions = | |
| this->GeneratorTarget->GetPchUseCompileOptions(config, lang); | |
| } | |
| std::string const& evaluatedFlags = | |
| genexInterpreter.Evaluate(pchOptions, COMPILE_OPTIONS); | |
| this->LocalGenerator->AppendCompileOptions(flags, evaluatedFlags); | |
| *this->FlagFileStream << "# PCH options: " << relativeObj | |
| << "_OPTIONS = " << evaluatedFlags << "\n" | |
| << "\n"; | |
| } | |
| // Add include directories from file set properties. | |
| std::vector<std::string> includes; | |
| if (fileSet) { | |
| auto fsIncludes = fileSet->BelongsTo(this->GeneratorTarget) | |
| ? fileSet->GetIncludeDirectories(config, lang) | |
| : fileSet->GetInterfaceIncludeDirectories(config, lang); | |
| if (!fsIncludes.empty()) { | |
| this->LocalGenerator->AppendIncludeDirectories( | |
| includes, cm::remove_BT(fsIncludes), source); | |
| *this->FlagFileStream | |
| << "# Custom include directories (File Set '" << fileSet->GetName() | |
| << "'): " << relativeObj | |
| << "_INCLUDE_DIRECTORIES = " << cmList::to_string(fsIncludes) << "\n" | |
| << "\n"; | |
| } | |
| } | |
| // Add include directories from source file properties. | |
| std::string const INCLUDE_DIRECTORIES("INCLUDE_DIRECTORIES"); | |
| if (cmValue cincludes = source.GetProperty(INCLUDE_DIRECTORIES)) { | |
| std::string const& evaluatedIncludes = | |
| genexInterpreter.Evaluate(*cincludes, INCLUDE_DIRECTORIES); | |
| this->LocalGenerator->AppendIncludeDirectories(includes, evaluatedIncludes, | |
| source); | |
| *this->FlagFileStream << "# Custom include directories: " << relativeObj | |
| << "_INCLUDE_DIRECTORIES = " << evaluatedIncludes | |
| << "\n" | |
| << "\n"; | |
| } | |
| // Add language-specific defines. | |
| std::set<std::string> defines; | |
| // Add source-specific preprocessor definitions. | |
| std::string const COMPILE_DEFINITIONS("COMPILE_DEFINITIONS"); | |
| if (cmValue compile_defs = source.GetProperty(COMPILE_DEFINITIONS)) { | |
| std::string const& evaluatedDefs = | |
| genexInterpreter.Evaluate(*compile_defs, COMPILE_DEFINITIONS); | |
| this->LocalGenerator->AppendDefines(defines, evaluatedDefs); | |
| *this->FlagFileStream << "# Custom defines: " << relativeObj | |
| << "_DEFINES = " << evaluatedDefs << "\n" | |
| << "\n"; | |
| } | |
| std::string const defPropName = | |
| cmStrCat("COMPILE_DEFINITIONS_", configUpper); | |
| if (cmValue config_compile_defs = source.GetProperty(defPropName)) { | |
| std::string const& evaluatedDefs = | |
| genexInterpreter.Evaluate(*config_compile_defs, COMPILE_DEFINITIONS); | |
| this->LocalGenerator->AppendDefines(defines, evaluatedDefs); | |
| *this->FlagFileStream << "# Custom defines: " << relativeObj << "_DEFINES_" | |
| << configUpper << " = " << evaluatedDefs << "\n" | |
| << "\n"; | |
| } | |
| // Add file set preprocessor definitions | |
| if (fileSet) { | |
| auto fsDefines = fileSet->BelongsTo(this->GeneratorTarget) | |
| ? fileSet->GetCompileDefinitions(config, lang) | |
| : fileSet->GetInterfaceCompileDefinitions(config, lang); | |
| if (!fsDefines.empty()) { | |
| this->LocalGenerator->AppendDefines(defines, fsDefines); | |
| *this->FlagFileStream << "# Custom defines (File Set '" | |
| << fileSet->GetName() << "'): " << relativeObj | |
| << "_DEFINES = " << cmList::to_string(fsDefines) | |
| << "\n" | |
| << "\n"; | |
| } | |
| } | |
| // Get the output paths for source and object files. | |
| std::string const sourceFile = this->LocalGenerator->ConvertToOutputFormat( | |
| source.GetFullPath(), cmOutputConverter::SHELL); | |
| // Construct the build message. | |
| std::vector<std::string> no_depends; | |
| std::vector<std::string> commands; | |
| // add in a progress call if needed | |
| this->NumberOfProgressActions++; | |
| if (!this->NoRuleMessages) { | |
| cmLocalUnixMakefileGenerator3::EchoProgress progress; | |
| this->MakeEchoProgress(progress); | |
| std::string buildEcho = | |
| cmStrCat("Building ", lang, " object ", relativeObj); | |
| this->LocalGenerator->AppendEcho(commands, buildEcho, | |
| cmLocalUnixMakefileGenerator3::EchoBuild, | |
| &progress); | |
| } | |
| std::string targetOutPathReal; | |
| std::string targetOutPathPDB; | |
| std::string targetOutPathCompilePDB; | |
| { | |
| std::string targetFullPathReal; | |
| std::string targetFullPathPDB; | |
| std::string targetFullPathCompilePDB = | |
| this->ComputeTargetCompilePDB(this->GetConfigName()); | |
| if (this->GeneratorTarget->GetType() == cm::TargetType::EXECUTABLE || | |
| this->GeneratorTarget->GetType() == cm::TargetType::STATIC_LIBRARY || | |
| this->GeneratorTarget->GetType() == cm::TargetType::SHARED_LIBRARY || | |
| this->GeneratorTarget->GetType() == cm::TargetType::MODULE_LIBRARY) { | |
| targetFullPathReal = this->GeneratorTarget->GetFullPath( | |
| this->GetConfigName(), cmStateEnums::RuntimeBinaryArtifact, true); | |
| targetFullPathPDB = cmStrCat( | |
| this->GeneratorTarget->GetPDBDirectory(this->GetConfigName()), '/', | |
| this->GeneratorTarget->GetPDBName(this->GetConfigName())); | |
| } | |
| targetOutPathReal = this->LocalGenerator->ConvertToOutputFormat( | |
| this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPathReal), | |
| cmOutputConverter::SHELL); | |
| targetOutPathPDB = this->LocalGenerator->ConvertToOutputFormat( | |
| targetFullPathPDB, cmOutputConverter::SHELL); | |
| targetOutPathCompilePDB = this->LocalGenerator->ConvertToOutputFormat( | |
| this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPathCompilePDB), | |
| cmOutputConverter::SHELL); | |
| if (this->LocalGenerator->IsMinGWMake() && | |
| cmHasSuffix(targetOutPathCompilePDB, '\\')) { | |
| // mingw32-make incorrectly interprets 'a\ b c' as 'a b' and 'c' | |
| // (but 'a\ b "c"' as 'a\', 'b', and 'c'!). Workaround this by | |
| // avoiding a trailing backslash in the argument. | |
| targetOutPathCompilePDB.back() = '/'; | |
| } | |
| std::string const compilePdbOutputPath = | |
| this->GeneratorTarget->GetCompilePDBDirectory(this->GetConfigName()); | |
| cmSystemTools::MakeDirectory(compilePdbOutputPath); | |
| } | |
| cmRulePlaceholderExpander::RuleVariables vars; | |
| vars.CMTargetName = this->GeneratorTarget->GetName().c_str(); | |
| vars.CMTargetType = | |
| cmState::GetTargetTypeName(this->GeneratorTarget->GetType()).c_str(); | |
| vars.Language = lang.c_str(); | |
| vars.Target = targetOutPathReal.c_str(); | |
| vars.TargetPDB = targetOutPathPDB.c_str(); | |
| vars.TargetCompilePDB = targetOutPathCompilePDB.c_str(); | |
| vars.Source = sourceFile.c_str(); | |
| std::string const shellObj = | |
| this->LocalGenerator->ConvertToOutputFormat(obj, cmOutputConverter::SHELL); | |
| vars.Object = shellObj.c_str(); | |
| std::string objectDir = this->GeneratorTarget->GetSupportDirectory(); | |
| objectDir = this->LocalGenerator->ConvertToOutputFormat( | |
| this->LocalGenerator->MaybeRelativeToCurBinDir(objectDir), | |
| cmOutputConverter::SHELL); | |
| vars.ObjectDir = objectDir.c_str(); | |
| std::string targetSupportDir = | |
| this->GeneratorTarget->GetCMFSupportDirectory(); | |
| targetSupportDir = this->LocalGenerator->ConvertToOutputFormat( | |
| this->LocalGenerator->MaybeRelativeToTopBinDir(targetSupportDir), | |
| cmOutputConverter::SHELL); | |
| vars.TargetSupportDir = targetSupportDir.c_str(); | |
| std::string objectFileDir = cmSystemTools::GetFilenamePath(obj); | |
| objectFileDir = this->LocalGenerator->ConvertToOutputFormat( | |
| this->LocalGenerator->MaybeRelativeToCurBinDir(objectFileDir), | |
| cmOutputConverter::SHELL); | |
| vars.ObjectFileDir = objectFileDir.c_str(); | |
| vars.Flags = flags.c_str(); | |
| vars.ISPCHeader = ispcHeaderForShell.c_str(); | |
| vars.Config = this->GetConfigName().c_str(); | |
| vars.RustEmit = source.GetRustEmitProperty()->c_str(); | |
| std::string definesString = cmStrCat("$(", lang, "_DEFINES)"); | |
| this->LocalGenerator->JoinDefines(defines, definesString, lang); | |
| vars.Defines = definesString.c_str(); | |
| std::string includesString = this->LocalGenerator->GetIncludeFlags( | |
| includes, this->GeneratorTarget, lang, config); | |
| this->LocalGenerator->AppendFlags(includesString, | |
| cmStrCat("$(", lang, "_INCLUDES)")); | |
| vars.Includes = includesString.c_str(); | |
| std::string dependencyTarget; | |
| std::string shellDependencyFile; | |
| std::string dependencyTimestamp; | |
| if (compilerGenerateDeps) { | |
| dependencyTarget = this->LocalGenerator->EscapeForShell( | |
| this->LocalGenerator->ConvertToMakefilePath( | |
| this->LocalGenerator->MaybeRelativeToTopBinDir(relativeObj))); | |
| vars.DependencyTarget = dependencyTarget.c_str(); | |
| auto depFile = cmStrCat(obj, ".d"); | |
| shellDependencyFile = this->LocalGenerator->ConvertToOutputFormat( | |
| depFile, cmOutputConverter::SHELL); | |
| vars.DependencyFile = shellDependencyFile.c_str(); | |
| this->CleanFiles.insert(depFile); | |
| dependencyTimestamp = this->LocalGenerator->MaybeRelativeToTopBinDir( | |
| cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.ts")); | |
| } | |
| bool const lang_has_preprocessor = | |
| ((lang == "C") || (lang == "CXX") || (lang == "OBJC") || | |
| (lang == "OBJCXX") || (lang == "Fortran") || (lang == "CUDA") || | |
| lang == "SYCL" || lang == "ISPC" || lang == "HIP" || lang == "ASM"); | |
| auto rulePlaceholderExpander = | |
| this->LocalGenerator->CreateRulePlaceholderExpander(); | |
| // Construct the compile rules. | |
| { | |
| std::string cudaCompileMode; | |
| if (lang == "CUDA") { | |
| if (this->GeneratorTarget->GetPropertyAsBool( | |
| "CUDA_SEPARABLE_COMPILATION")) { | |
| std::string const& rdcFlag = | |
| this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_RDC_FLAG"); | |
| cudaCompileMode = cmStrCat(std::move(cudaCompileMode), rdcFlag, ' '); | |
| } | |
| static std::array<cm::string_view, 4> const compileModes{ | |
| { "PTX"_s, "CUBIN"_s, "FATBIN"_s, "OPTIX"_s } | |
| }; | |
| bool useNormalCompileMode = true; | |
| for (cm::string_view mode : compileModes) { | |
| auto propName = cmStrCat("CUDA_", mode, "_COMPILATION"); | |
| auto defName = cmStrCat("_CMAKE_CUDA_", mode, "_FLAG"); | |
| if (this->GeneratorTarget->GetPropertyAsBool(propName)) { | |
| std::string const& flag = | |
| this->Makefile->GetRequiredDefinition(defName); | |
| cudaCompileMode = cmStrCat(std::move(cudaCompileMode), flag); | |
| useNormalCompileMode = false; | |
| break; | |
| } | |
| } | |
| if (useNormalCompileMode) { | |
| std::string const& wholeFlag = | |
| this->Makefile->GetRequiredDefinition("_CMAKE_CUDA_WHOLE_FLAG"); | |
| cudaCompileMode = cmStrCat(std::move(cudaCompileMode), wholeFlag); | |
| } | |
| vars.CudaCompileMode = cudaCompileMode.c_str(); | |
| } | |
| cmList compileCommands; | |
| std::string const& compileRule = this->Makefile->GetRequiredDefinition( | |
| cmStrCat("CMAKE_", lang, "_COMPILE_OBJECT")); | |
| compileCommands.assign(compileRule); | |
| if (this->GeneratorTarget->GetPropertyAsBool("EXPORT_COMPILE_COMMANDS") && | |
| lang_has_preprocessor && compileCommands.size() == 1) { | |
| std::string compileCommand = compileCommands[0]; | |
| // no launcher for CMAKE_EXPORT_COMPILE_COMMANDS | |
| rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator, | |
| compileCommand, vars); | |
| std::string const workingDirectory = | |
| this->LocalGenerator->GetCurrentBinaryDirectory(); | |
| std::string::size_type lfPos = compileCommand.find(langFlags); | |
| if (lfPos != std::string::npos) { | |
| compileCommand.replace(lfPos, langFlags.size(), | |
| this->GetFlags(lang, this->GetConfigName())); | |
| } | |
| std::string const langDefines = cmStrCat("$(", lang, "_DEFINES)"); | |
| std::string::size_type const ldPos = compileCommand.find(langDefines); | |
| if (ldPos != std::string::npos) { | |
| compileCommand.replace(ldPos, langDefines.size(), | |
| this->GetDefines(lang, this->GetConfigName())); | |
| } | |
| std::string const langIncludes = cmStrCat("$(", lang, "_INCLUDES)"); | |
| std::string::size_type const liPos = compileCommand.find(langIncludes); | |
| if (liPos != std::string::npos) { | |
| compileCommand.replace(liPos, langIncludes.size(), | |
| this->GetIncludes(lang, this->GetConfigName())); | |
| } | |
| cmValue const eliminate[] = { | |
| this->Makefile->GetDefinition("CMAKE_START_TEMP_FILE"), | |
| this->Makefile->GetDefinition("CMAKE_END_TEMP_FILE") | |
| }; | |
| for (cmValue const& el : eliminate) { | |
| if (el) { | |
| cmSystemTools::ReplaceString(compileCommand, *el, ""); | |
| } | |
| } | |
| this->GlobalGenerator->AddCXXCompileCommand( | |
| source.GetFullPath(), workingDirectory, compileCommand, relativeObj); | |
| } | |
| // See if we need to use a compiler launcher like ccache or distcc | |
| std::vector<std::string> compilerLauncher; | |
| if (!compileCommands.empty()) { | |
| compilerLauncher = GetCompilerLauncher(lang, config); | |
| } | |
| cmValue const fsSkipCodeCheckVal = | |
| fileSet ? fileSet->GetProperty("SKIP_LINTING") : nullptr; | |
| cmValue const srcSkipCodeCheckVal = source.GetProperty("SKIP_LINTING"); | |
| bool const skipCodeCheck = fsSkipCodeCheckVal.IsSet() | |
| ? fsSkipCodeCheckVal.IsOn() | |
| : (srcSkipCodeCheckVal.IsSet() | |
| ? srcSkipCodeCheckVal.IsOn() | |
| : this->GetGeneratorTarget()->GetPropertyAsBool("SKIP_LINTING")); | |
| if (!skipCodeCheck) { | |
| std::string const codeCheck = this->GenerateCodeCheckRules( | |
| source, compilerLauncher, "$(CMAKE_COMMAND)", config, nullptr); | |
| if (!codeCheck.empty()) { | |
| compileCommands.front().insert(0, codeCheck); | |
| } | |
| } | |
| // If compiler launcher was specified and not consumed above, it | |
| // goes to the beginning of the command line. | |
| if (!compileCommands.empty() && !compilerLauncher.empty()) { | |
| compileCommands.front().insert( | |
| 0, cmStrCat(this->ConvertLauncherToShell(compilerLauncher), ' ')); | |
| } | |
| std::string launcher; | |
| { | |
| std::string val = this->LocalGenerator->GetRuleLauncher( | |
| this->GeneratorTarget, "RULE_LAUNCH_COMPILE", | |
| this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE")); | |
| if (cmNonempty(val)) { | |
| launcher = cmStrCat(val, ' '); | |
| } | |
| } | |
| std::string flagsWithDeps(flags); | |
| if (compilerGenerateDeps) { | |
| // Injects dependency computation | |
| auto depFlags = this->Makefile->GetSafeDefinition( | |
| cmStrCat("CMAKE_DEPFILE_FLAGS_", lang)); | |
| if (!depFlags.empty()) { | |
| // Add dependency flags | |
| rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator, | |
| depFlags, vars); | |
| flagsWithDeps.append(1, ' '); | |
| flagsWithDeps.append(depFlags); | |
| } | |
| vars.Flags = flagsWithDeps.c_str(); | |
| auto const& extraCommands = this->Makefile->GetSafeDefinition( | |
| cmStrCat("CMAKE_", lang, "_DEPENDS_EXTRA_COMMANDS")); | |
| if (!extraCommands.empty()) { | |
| compileCommands.append(extraCommands); | |
| } | |
| auto const& depFormat = this->Makefile->GetRequiredDefinition( | |
| cmStrCat("CMAKE_", lang, "_DEPFILE_FORMAT")); | |
| if (depFormat == "msvc"_s) { | |
| // compiler must be launched through a wrapper to pick-up dependencies | |
| std::string depFilter = | |
| "$(CMAKE_COMMAND) -E cmake_cl_compile_depends "; | |
| depFilter = | |
| cmStrCat(std::move(depFilter), "--dep-file=", shellDependencyFile); | |
| depFilter = | |
| cmStrCat(std::move(depFilter), " --working-dir=", | |
| this->LocalGenerator->ConvertToOutputFormat( | |
| this->LocalGenerator->GetCurrentBinaryDirectory(), | |
| cmOutputConverter::SHELL)); | |
| auto const& prefix = this->Makefile->GetSafeDefinition( | |
| cmStrCat("CMAKE_", lang, "_CL_SHOWINCLUDES_PREFIX")); | |
| depFilter = cmStrCat(std::move(depFilter), " --filter-prefix=", | |
| this->LocalGenerator->ConvertToOutputFormat( | |
| prefix, cmOutputConverter::SHELL)); | |
| depFilter += " -- "; | |
| compileCommands.front().insert(0, depFilter); | |
| } | |
| } | |
| // Expand placeholders in the commands. | |
| for (std::string& compileCommand : compileCommands) { | |
| compileCommand = cmStrCat(launcher, std::move(compileCommand)); | |
| rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator, | |
| compileCommand, vars); | |
| } | |
| // Change the command working directory to the local build tree. | |
| this->LocalGenerator->CreateCDCommand( | |
| compileCommands, this->LocalGenerator->GetCurrentBinaryDirectory(), | |
| this->LocalGenerator->GetBinaryDirectory()); | |
| cm::append(commands, compileCommands); | |
| } | |
| // Check for extra outputs created by the compilation. | |
| cmList outputs; | |
| outputs.emplace_back(relativeObj); | |
| if (cmValue extra_outputs_str = source.GetProperty("OBJECT_OUTPUTS")) { | |
| std::string evaluated_outputs = cmGeneratorExpression::Evaluate( | |
| *extra_outputs_str, this->LocalGenerator, config); | |
| if (!evaluated_outputs.empty()) { | |
| // Register these as extra files to clean. | |
| outputs.append(evaluated_outputs); | |
| } | |
| } | |
| if (!ispcHeaderRelative.empty()) { | |
| // can't move ispcHeader as vars is using it | |
| outputs.emplace_back(ispcHeaderRelative); | |
| } | |
| if (outputs.size() > 1) { | |
| this->CleanFiles.insert(outputs.begin() + 1, outputs.end()); | |
| } | |
| if (compilerGenerateDeps) { | |
| depends.push_back(dependencyTimestamp); | |
| } | |
| // Write the rule. | |
| this->WriteMakeRule(*this->BuildFileStream, nullptr, outputs, depends, | |
| commands); | |
| if (compilerGenerateDeps) { | |
| // set back flags without dependency generation | |
| vars.Flags = flags.c_str(); | |
| } | |
| bool do_preprocess_rules = lang_has_preprocessor && | |
| this->LocalGenerator->GetCreatePreprocessedSourceRules(); | |
| bool do_assembly_rules = lang_has_preprocessor && | |
| this->LocalGenerator->GetCreateAssemblySourceRules(); | |
| if (do_preprocess_rules || do_assembly_rules) { | |
| std::vector<std::string> force_depends; | |
| force_depends.emplace_back("cmake_force"); | |
| std::string::size_type dot_pos = relativeObj.rfind('.'); | |
| std::string relativeObjBase = relativeObj.substr(0, dot_pos); | |
| dot_pos = obj.rfind('.'); | |
| std::string objBase = obj.substr(0, dot_pos); | |
| if (do_preprocess_rules) { | |
| commands.clear(); | |
| std::string const relativeObjI = relativeObjBase + ".i"; | |
| std::string const objI = objBase + ".i"; | |
| std::string preprocessEcho = | |
| cmStrCat("Preprocessing ", lang, " source to ", objI); | |
| this->LocalGenerator->AppendEcho( | |
| commands, preprocessEcho, cmLocalUnixMakefileGenerator3::EchoBuild); | |
| std::string preprocessRuleVar = | |
| cmStrCat("CMAKE_", lang, "_CREATE_PREPROCESSED_SOURCE"); | |
| if (cmValue preprocessRule = | |
| this->Makefile->GetDefinition(preprocessRuleVar)) { | |
| cmList preprocessCommands{ *preprocessRule }; | |
| std::string shellObjI = this->LocalGenerator->ConvertToOutputFormat( | |
| objI, cmOutputConverter::SHELL); | |
| vars.PreprocessedSource = shellObjI.c_str(); | |
| // Expand placeholders in the commands. | |
| for (std::string& preprocessCommand : preprocessCommands) { | |
| // no launcher for preprocessor commands | |
| rulePlaceholderExpander->ExpandRuleVariables( | |
| this->LocalGenerator, preprocessCommand, vars); | |
| } | |
| this->LocalGenerator->CreateCDCommand( | |
| preprocessCommands, | |
| this->LocalGenerator->GetCurrentBinaryDirectory(), | |
| this->LocalGenerator->GetBinaryDirectory()); | |
| cm::append(commands, preprocessCommands); | |
| } else { | |
| std::string cmd = | |
| cmStrCat("$(CMAKE_COMMAND) -E cmake_unimplemented_variable ", | |
| preprocessRuleVar); | |
| commands.push_back(std::move(cmd)); | |
| } | |
| this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr, | |
| relativeObjI, force_depends, | |
| commands, false); | |
| } | |
| if (do_assembly_rules) { | |
| commands.clear(); | |
| std::string relativeObjS = relativeObjBase + ".s"; | |
| std::string objS = objBase + ".s"; | |
| std::string assemblyEcho = | |
| cmStrCat("Compiling ", lang, " source to assembly ", objS); | |
| this->LocalGenerator->AppendEcho( | |
| commands, assemblyEcho, cmLocalUnixMakefileGenerator3::EchoBuild); | |
| std::string assemblyRuleVar = | |
| cmStrCat("CMAKE_", lang, "_CREATE_ASSEMBLY_SOURCE"); | |
| if (cmValue assemblyRule = | |
| this->Makefile->GetDefinition(assemblyRuleVar)) { | |
| cmList assemblyCommands{ *assemblyRule }; | |
| std::string shellObjS = this->LocalGenerator->ConvertToOutputFormat( | |
| objS, cmOutputConverter::SHELL); | |
| vars.AssemblySource = shellObjS.c_str(); | |
| // Expand placeholders in the commands. | |
| for (std::string& assemblyCommand : assemblyCommands) { | |
| // no launcher for assembly commands | |
| rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator, | |
| assemblyCommand, vars); | |
| } | |
| this->LocalGenerator->CreateCDCommand( | |
| assemblyCommands, this->LocalGenerator->GetCurrentBinaryDirectory(), | |
| this->LocalGenerator->GetBinaryDirectory()); | |
| cm::append(commands, assemblyCommands); | |
| } else { | |
| std::string cmd = | |
| cmStrCat("$(CMAKE_COMMAND) -E cmake_unimplemented_variable ", | |
| assemblyRuleVar); | |
| commands.push_back(std::move(cmd)); | |
| } | |
| this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr, | |
| relativeObjS, force_depends, | |
| commands, false); | |
| } | |
| } | |
| } | |
| void cmMakefileTargetGenerator::WriteTargetCleanRules() | |
| { | |
| std::vector<std::string> depends; | |
| std::vector<std::string> commands; | |
| // Construct the clean target name. | |
| std::string const cleanTarget = cmStrCat( | |
| this->LocalGenerator->GetRelativeTargetDirectory(this->GeneratorTarget), | |
| "/clean"); | |
| // Construct the clean command. | |
| this->LocalGenerator->AppendCleanCommand(commands, this->CleanFiles, | |
| this->GeneratorTarget); | |
| this->LocalGenerator->CreateCDCommand( | |
| commands, this->LocalGenerator->GetCurrentBinaryDirectory(), | |
| this->LocalGenerator->GetBinaryDirectory()); | |
| // Write the rule. | |
| this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr, | |
| cleanTarget, depends, commands, true); | |
| } | |
| bool cmMakefileTargetGenerator::WriteMakeRule( | |
| std::ostream& os, char const* comment, | |
| std::vector<std::string> const& outputs, | |
| std::vector<std::string> const& depends, | |
| std::vector<std::string> const& commands, bool in_help) | |
| { | |
| bool symbolic = false; | |
| if (outputs.empty()) { | |
| return symbolic; | |
| } | |
| // Check whether we need to bother checking for a symbolic output. | |
| bool const need_symbolic = this->GlobalGenerator->GetNeedSymbolicMark(); | |
| // Check whether the first output is marked as symbolic. | |
| if (need_symbolic) { | |
| if (cmSourceFile* sf = this->Makefile->GetSource(outputs[0])) { | |
| symbolic = sf->GetPropertyAsBool("SYMBOLIC"); | |
| } | |
| } | |
| // We always attach the actual commands to the first output. | |
| this->LocalGenerator->WriteMakeRule(os, comment, outputs[0], depends, | |
| commands, symbolic, in_help); | |
| // For single outputs, we are done. | |
| if (outputs.size() == 1) { | |
| return symbolic; | |
| } | |
| // For multiple outputs, make the extra ones depend on the first one. | |
| std::vector<std::string> const output_depends(1, outputs[0]); | |
| for (std::string const& output : cmMakeRange(outputs).advance(1)) { | |
| // Touch the extra output so "make" knows that it was updated, | |
| // but only if the output was actually created. | |
| std::string const out = this->LocalGenerator->ConvertToOutputFormat( | |
| this->LocalGenerator->MaybeRelativeToTopBinDir(output), | |
| cmOutputConverter::SHELL); | |
| std::vector<std::string> output_commands; | |
| bool o_symbolic = false; | |
| if (need_symbolic) { | |
| if (cmSourceFile const* sf = this->Makefile->GetSource(output)) { | |
| o_symbolic = sf->GetPropertyAsBool("SYMBOLIC"); | |
| } | |
| } | |
| symbolic = symbolic && o_symbolic; | |
| if (!o_symbolic) { | |
| output_commands.push_back("@$(CMAKE_COMMAND) -E touch_nocreate " + out); | |
| } | |
| this->LocalGenerator->WriteMakeRule(os, nullptr, output, output_depends, | |
| output_commands, o_symbolic, in_help); | |
| if (!o_symbolic) { | |
| // At build time, remove the first output if this one does not exist | |
| // so that "make" will rerun the real commands that create this one. | |
| MultipleOutputPairsType::value_type p(output, outputs[0]); | |
| this->MultipleOutputPairs.insert(p); | |
| } | |
| } | |
| return symbolic; | |
| } | |
| void cmMakefileTargetGenerator::WriteTargetLinkDependRules() | |
| { | |
| if (!this->GeneratorTarget->HasLinkDependencyFile(this->GetConfigName())) { | |
| return; | |
| } | |
| auto depFile = this->LocalGenerator->GetLinkDependencyFile( | |
| this->GeneratorTarget, this->GetConfigName()); | |
| this->CleanFiles.insert(depFile); | |
| this->LocalGenerator->AddImplicitDepends( | |
| this->GeneratorTarget, "LINK", | |
| this->GeneratorTarget->GetFullPath(this->GetConfigName()), depFile, | |
| cmDependencyScannerKind::Compiler); | |
| } | |
| std::string cmMakefileTargetGenerator::GetClangTidyReplacementsFilePath( | |
| std::string const& directory, cmSourceFile const& source, | |
| std::string const& config) const | |
| { | |
| (void)config; | |
| auto const& objectName = this->GeneratorTarget->GetObjectName(&source); | |
| // NOTE: This may be better to use `this->TargetBuildDirectory` instead of | |
| // `MaybeRelativeToTopBinDir(this->TargetBuildDirectoryFull)` here. The main | |
| // difference is that the current behavior looks odd to relative | |
| // `<LANG>_CLANG_TIDY_EXPORT_FIXES_DIR` settings. Each subdirectory has its | |
| // own export fixes directory *and* adds its relative-from-root path | |
| // underneath it. However, when using an absolute export fixes directory, the | |
| // source directory structure is preserved. The main benefit of the former is | |
| // shorter paths everywhere versus the status quo of the existing code. | |
| cmLocalGenerator* lg = this->GeneratorTarget->GetLocalGenerator(); | |
| auto fixesFile = cmSystemTools::CollapseFullPath( | |
| cmStrCat(directory, '/', | |
| lg->CreateSafeObjectFileName( | |
| lg->MaybeRelativeToTopBinDir(this->TargetBuildDirectoryFull)), | |
| '/', objectName, ".yaml")); | |
| return fixesFile; | |
| } | |
| void cmMakefileTargetGenerator::WriteTargetDependRules() | |
| { | |
| // must write the targets depend info file | |
| this->InfoFileNameFull = | |
| cmStrCat(this->TargetBuildDirectoryFull, "/DependInfo.cmake"); | |
| this->InfoFileStream = | |
| cm::make_unique<cmGeneratedFileStream>(this->InfoFileNameFull); | |
| if (!this->InfoFileStream) { | |
| return; | |
| } | |
| this->InfoFileStream->SetCopyIfDifferent(true); | |
| this->LocalGenerator->WriteDependLanguageInfo(*this->InfoFileStream, | |
| this->GeneratorTarget); | |
| // Store multiple output pairs in the depend info file. | |
| if (!this->MultipleOutputPairs.empty()) { | |
| /* clang-format off */ | |
| *this->InfoFileStream | |
| << "\n" | |
| << "# Pairs of files generated by the same build rule.\n" | |
| << "set(CMAKE_MULTIPLE_OUTPUT_PAIRS\n"; | |
| /* clang-format on */ | |
| for (auto const& pi : this->MultipleOutputPairs) { | |
| *this->InfoFileStream << " " << cmScriptGenerator::Quote(pi.first) | |
| << ' ' << cmScriptGenerator::Quote(pi.second) | |
| << '\n'; | |
| } | |
| *this->InfoFileStream << " )\n\n"; | |
| } | |
| // Store list of targets linked directly or transitively. | |
| { | |
| /* clang-format off */ | |
| *this->InfoFileStream | |
| << "\n" | |
| "# Targets to which this target links which contain Fortran sources.\n" | |
| "set(CMAKE_Fortran_TARGET_LINKED_INFO_FILES\n"; | |
| /* clang-format on */ | |
| auto const dirs = | |
| this->GetLinkedTargetDirectories("Fortran", this->GetConfigName()); | |
| for (std::string const& d : dirs.Direct) { | |
| *this->InfoFileStream << " \"" << d << "/DependInfo.cmake\"\n"; | |
| } | |
| *this->InfoFileStream << " )\n"; | |
| /* clang-format off */ | |
| *this->InfoFileStream | |
| << "\n" | |
| "# Targets to which this target links which contain Fortran sources.\n" | |
| "set(CMAKE_Fortran_TARGET_FORWARD_LINKED_INFO_FILES\n"; | |
| /* clang-format on */ | |
| for (std::string const& d : dirs.Forward) { | |
| *this->InfoFileStream << " \"" << d << "/DependInfo.cmake\"\n"; | |
| } | |
| *this->InfoFileStream << " )\n"; | |
| } | |
| std::string const& working_dir = | |
| this->LocalGenerator->GetCurrentBinaryDirectory(); | |
| /* clang-format off */ | |
| *this->InfoFileStream | |
| << "\n" | |
| << "# Fortran module output directory.\n" | |
| << "set(CMAKE_Fortran_TARGET_MODULE_DIR \"" | |
| << this->GeneratorTarget->GetFortranModuleDirectory(working_dir) | |
| << "\")\n"; | |
| if (this->GeneratorTarget->IsFortranBuildingIntrinsicModules()) { | |
| *this->InfoFileStream | |
| << "\n" | |
| << "# Fortran compiler is building intrinsic modules.\n" | |
| << "set(CMAKE_Fortran_TARGET_BUILDING_INTRINSIC_MODULES ON) \n"; | |
| } | |
| /* clang-format on */ | |
| // and now write the rule to use it | |
| std::vector<std::string> depends; | |
| std::vector<std::string> commands; | |
| // Construct the name of the dependency generation target. | |
| std::string const depTarget = cmStrCat( | |
| this->LocalGenerator->GetRelativeTargetDirectory(this->GeneratorTarget), | |
| "/depend"); | |
| // Add a command to call CMake to scan dependencies. CMake will | |
| // touch the corresponding depends file after scanning dependencies. | |
| std::ostringstream depCmd; | |
| // TODO: Account for source file properties and directory-level | |
| // definitions when scanning for dependencies. | |
| #if !defined(_WIN32) || defined(__CYGWIN__) | |
| // This platform supports symlinks, so cmSystemTools will translate | |
| // paths. Make sure PWD is set to the original name of the home | |
| // output directory to help cmSystemTools to create the same | |
| // translation table for the dependency scanning process. | |
| depCmd << "cd " | |
| << (this->LocalGenerator->ConvertToOutputFormat( | |
| this->LocalGenerator->GetBinaryDirectory(), | |
| cmOutputConverter::SHELL)) | |
| << " && "; | |
| #endif | |
| // Generate a call this signature: | |
| // | |
| // cmake -E cmake_depends <generator> | |
| // <home-src-dir> <start-src-dir> | |
| // <home-out-dir> <start-out-dir> | |
| // <dep-info> --color=$(COLOR) | |
| // <target-name> | |
| // | |
| // This gives the dependency scanner enough information to recreate | |
| // the state of our local generator sufficiently for its needs. | |
| depCmd << "$(CMAKE_COMMAND) -E cmake_depends \"" | |
| << this->GlobalGenerator->GetName() << "\" " | |
| << this->LocalGenerator->ConvertToOutputFormat( | |
| this->LocalGenerator->GetSourceDirectory(), | |
| cmOutputConverter::SHELL) | |
| << " " | |
| << this->LocalGenerator->ConvertToOutputFormat( | |
| this->LocalGenerator->GetCurrentSourceDirectory(), | |
| cmOutputConverter::SHELL) | |
| << " " | |
| << this->LocalGenerator->ConvertToOutputFormat( | |
| this->LocalGenerator->GetBinaryDirectory(), | |
| cmOutputConverter::SHELL) | |
| << " " | |
| << this->LocalGenerator->ConvertToOutputFormat( | |
| this->LocalGenerator->GetCurrentBinaryDirectory(), | |
| cmOutputConverter::SHELL) | |
| << " " | |
| << this->LocalGenerator->ConvertToOutputFormat( | |
| this->InfoFileNameFull, cmOutputConverter::SHELL); | |
| if (this->LocalGenerator->GetColorMakefile()) { | |
| depCmd << " \"--color=$(COLOR)\""; | |
| } | |
| depCmd << ' ' | |
| << this->LocalGenerator->ConvertToOutputFormat( | |
| this->GeneratorTarget->GetName(), cmOutputConverter::SHELL); | |
| commands.push_back(depCmd.str()); | |
| // Make sure all custom command outputs in this target are built. | |
| if (this->CustomCommandDriver == OnDepends) { | |
| this->DriveCustomCommands(depends); | |
| } | |
| // Write the rule. | |
| this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr, | |
| depTarget, depends, commands, true); | |
| } | |
| void cmMakefileTargetGenerator::DriveCustomCommands( | |
| std::vector<std::string>& depends) | |
| { | |
| // Depend on all custom command outputs. | |
| cm::append(depends, this->CustomCommandOutputs); | |
| } | |
| void cmMakefileTargetGenerator::WriteObjectDependRules( | |
| cmSourceFile const& source, std::vector<std::string>& depends) | |
| { | |
| // Create the list of dependencies known at cmake time. These are | |
| // shared between the object file and dependency scanning rule. | |
| depends.push_back(source.GetFullPath()); | |
| if (cmValue objectDeps = source.GetProperty("OBJECT_DEPENDS")) { | |
| cmExpandList(*objectDeps, depends); | |
| } | |
| } | |
| void cmMakefileTargetGenerator::WriteDeviceLinkRule( | |
| std::vector<std::string>& commands, std::string const& output) | |
| { | |
| std::string architecturesStr = | |
| this->GeneratorTarget->GetSafeProperty("CUDA_ARCHITECTURES"); | |
| if (cmIsOff(architecturesStr)) { | |
| this->Makefile->IssueMessage(MessageType::FATAL_ERROR, | |
| "CUDA_SEPARABLE_COMPILATION on Clang " | |
| "requires CUDA_ARCHITECTURES to be set."); | |
| return; | |
| } | |
| cmLocalUnixMakefileGenerator3* localGen{ this->LocalGenerator }; | |
| cmList architectures{ architecturesStr }; | |
| std::string const& relPath = localGen->GetHomeRelativeOutputPath(); | |
| // Ensure there are no duplicates. | |
| std::vector<std::string> const linkDeps = [&]() -> std::vector<std::string> { | |
| std::vector<std::string> deps; | |
| this->AppendTargetDepends(deps, true); | |
| this->GeneratorTarget->GetLinkDepends(deps, this->GetConfigName(), "CUDA"); | |
| for (std::string const& obj : this->Objects) { | |
| deps.emplace_back(cmStrCat(relPath, obj)); | |
| } | |
| std::unordered_set<std::string> const depsSet(deps.begin(), deps.end()); | |
| deps.clear(); | |
| std::copy(depsSet.begin(), depsSet.end(), std::back_inserter(deps)); | |
| return deps; | |
| }(); | |
| std::string const objectDir = this->GeneratorTarget->ObjectDirectory; | |
| std::string const relObjectDir = | |
| localGen->MaybeRelativeToCurBinDir(objectDir); | |
| // Construct a list of files associated with this executable that | |
| // may need to be cleaned. | |
| std::vector<std::string> cleanFiles; | |
| cleanFiles.push_back(localGen->MaybeRelativeToCurBinDir(output)); | |
| std::string profiles; | |
| std::vector<std::string> fatbinaryDepends; | |
| std::string const registerFile = | |
| cmStrCat(objectDir, "cmake_cuda_register.h"); | |
| // Link device code for each architecture. | |
| for (std::string const& architectureKind : architectures) { | |
| std::string registerFileCmd; | |
| // The generated register file contains macros that when expanded | |
| // register the device routines. Because the routines are the same for | |
| // all architectures the register file will be the same too. Thus | |
| // generate it only on the first invocation to reduce overhead. | |
| if (fatbinaryDepends.empty()) { | |
| std::string const registerFileRel = | |
| cmStrCat(relPath, relObjectDir, "cmake_cuda_register.h"); | |
| registerFileCmd = | |
| cmStrCat(" --register-link-binaries=", registerFileRel); | |
| cleanFiles.push_back(registerFileRel); | |
| } | |
| // Clang always generates real code, so strip the specifier. | |
| std::string const architecture = | |
| architectureKind.substr(0, architectureKind.find('-')); | |
| std::string const cubin = | |
| cmStrCat(objectDir, "sm_", architecture, ".cubin"); | |
| profiles = cmStrCat(std::move(profiles), " -im=profile=sm_", architecture, | |
| ",file=", cubin); | |
| fatbinaryDepends.emplace_back(cubin); | |
| std::string command = cmStrCat( | |
| this->Makefile->GetRequiredDefinition("CMAKE_CUDA_DEVICE_LINKER"), | |
| " -arch=sm_", architecture, registerFileCmd, " -o=$@ ", | |
| cmJoin(linkDeps, " ")); | |
| localGen->WriteMakeRule(*this->BuildFileStream, nullptr, cubin, linkDeps, | |
| { command }, false); | |
| } | |
| // Combine all architectures into a single fatbinary. | |
| std::string const fatbinaryCommand = | |
| cmStrCat(this->Makefile->GetRequiredDefinition("CMAKE_CUDA_FATBINARY"), | |
| " -64 -cmdline=--compile-only -compress-all -link " | |
| "--embedded-fatbin=$@", | |
| profiles); | |
| std::string const fatbinaryOutput = | |
| cmStrCat(objectDir, "cmake_cuda_fatbin.h"); | |
| std::string const fatbinaryOutputRel = | |
| cmStrCat(relPath, relObjectDir, "cmake_cuda_fatbin.h"); | |
| localGen->WriteMakeRule(*this->BuildFileStream, nullptr, fatbinaryOutputRel, | |
| fatbinaryDepends, { fatbinaryCommand }, false); | |
| // Compile the stub that registers the kernels and contains the | |
| // fatbinaries. | |
| cmRulePlaceholderExpander::RuleVariables vars; | |
| vars.CMTargetName = this->GetGeneratorTarget()->GetName().c_str(); | |
| vars.CMTargetType = | |
| cmState::GetTargetTypeName(this->GetGeneratorTarget()->GetType()).c_str(); | |
| vars.Language = "CUDA"; | |
| vars.Object = output.c_str(); | |
| vars.Fatbinary = fatbinaryOutput.c_str(); | |
| vars.RegisterFile = registerFile.c_str(); | |
| vars.Config = this->GetConfigName().c_str(); | |
| std::string linkFlags; | |
| this->GetDeviceLinkFlags(linkFlags, "CUDA"); | |
| vars.LinkFlags = linkFlags.c_str(); | |
| std::string const flags = this->GetFlags("CUDA", this->GetConfigName()); | |
| vars.Flags = flags.c_str(); | |
| std::string compileCmd = this->GetLinkRule("CMAKE_CUDA_DEVICE_LINK_COMPILE"); | |
| auto rulePlaceholderExpander = | |
| localGen->CreateRulePlaceholderExpander(cmBuildStep::Link); | |
| rulePlaceholderExpander->ExpandRuleVariables(localGen, compileCmd, vars); | |
| commands.emplace_back(compileCmd); | |
| localGen->WriteMakeRule(*this->BuildFileStream, nullptr, output, | |
| { fatbinaryOutputRel }, commands, false); | |
| // Clean all the possible executable names and symlinks. | |
| this->CleanFiles.insert(cleanFiles.begin(), cleanFiles.end()); | |
| } | |
| void cmMakefileTargetGenerator::GenerateCustomRuleFile( | |
| cmCustomCommandGenerator const& ccg) | |
| { | |
| // Collect the commands. | |
| std::vector<std::string> commands; | |
| std::string comment = this->LocalGenerator->ConstructComment(ccg); | |
| if (!comment.empty()) { | |
| // add in a progress call if needed | |
| this->NumberOfProgressActions++; | |
| if (!this->NoRuleMessages) { | |
| cmLocalUnixMakefileGenerator3::EchoProgress progress; | |
| this->MakeEchoProgress(progress); | |
| this->LocalGenerator->AppendEcho( | |
| commands, comment, cmLocalUnixMakefileGenerator3::EchoGenerate, | |
| &progress); | |
| } | |
| } | |
| // Now append the actual user-specified commands. | |
| std::ostringstream content; | |
| this->LocalGenerator->AppendCustomCommand( | |
| commands, ccg, this->GeneratorTarget, | |
| this->LocalGenerator->GetBinaryDirectory(), false, &content); | |
| // Collect the dependencies. | |
| std::vector<std::string> depends; | |
| this->LocalGenerator->AppendCustomDepend(depends, ccg); | |
| if (!ccg.GetCC().GetDepfile().empty()) { | |
| // Add dependency over timestamp file for dependencies management | |
| auto dependTimestamp = this->LocalGenerator->MaybeRelativeToTopBinDir( | |
| cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.ts")); | |
| depends.emplace_back(std::move(dependTimestamp)); | |
| } | |
| // Write the rule. | |
| std::vector<std::string> const& outputs = ccg.GetOutputs(); | |
| bool const symbolic = this->WriteMakeRule(*this->BuildFileStream, nullptr, | |
| outputs, depends, commands); | |
| // Symbolic inputs are not expected to exist, so add dummy rules. | |
| if (this->CMP0113New && !depends.empty()) { | |
| std::vector<std::string> no_depends; | |
| std::vector<std::string> no_commands; | |
| for (std::string const& dep : depends) { | |
| if (cmSourceFile* dsf = | |
| this->Makefile->GetSource(dep, cmSourceFileLocationKind::Known)) { | |
| if (dsf->GetPropertyAsBool("SYMBOLIC")) { | |
| this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr, | |
| dep, no_depends, no_commands, | |
| true); | |
| } | |
| } | |
| } | |
| } | |
| // If the rule has changed make sure the output is rebuilt. | |
| if (!symbolic) { | |
| this->GlobalGenerator->AddRuleHash(ccg.GetOutputs(), content.str()); | |
| } | |
| // Setup implicit dependency scanning. | |
| for (auto const& idi : ccg.GetCC().GetImplicitDepends()) { | |
| std::string objFullPath = cmSystemTools::CollapseFullPath( | |
| outputs[0], this->LocalGenerator->GetCurrentBinaryDirectory()); | |
| std::string srcFullPath = cmSystemTools::CollapseFullPath( | |
| idi.second, this->LocalGenerator->GetCurrentBinaryDirectory()); | |
| this->LocalGenerator->AddImplicitDepends(this->GeneratorTarget, idi.first, | |
| objFullPath, srcFullPath); | |
| } | |
| // Setup implicit depend for depfile if any | |
| if (!ccg.GetCC().GetDepfile().empty()) { | |
| std::string objFullPath = cmSystemTools::CollapseFullPath( | |
| outputs[0], this->LocalGenerator->GetCurrentBinaryDirectory()); | |
| this->LocalGenerator->AddImplicitDepends( | |
| this->GeneratorTarget, "CUSTOM", objFullPath, ccg.GetFullDepfile(), | |
| cmDependencyScannerKind::Compiler); | |
| } | |
| this->CustomCommandOutputs.insert(outputs.begin(), outputs.end()); | |
| } | |
| void cmMakefileTargetGenerator::MakeEchoProgress( | |
| cmLocalUnixMakefileGenerator3::EchoProgress& progress) const | |
| { | |
| progress.Dir = | |
| cmStrCat(this->LocalGenerator->GetBinaryDirectory(), "/CMakeFiles"); | |
| progress.Arg = | |
| cmStrCat("$(CMAKE_PROGRESS_", this->NumberOfProgressActions, ')'); | |
| } | |
| void cmMakefileTargetGenerator::WriteObjectsVariable( | |
| std::string& variableName, std::string& variableNameExternal, | |
| bool useWatcomQuote) | |
| { | |
| // Write a make variable assignment that lists all objects for the | |
| // target. | |
| variableName = this->LocalGenerator->CreateMakeVariable( | |
| this->GeneratorTarget->GetName(), "_OBJECTS"); | |
| *this->BuildFileStream << "# Object files for target " | |
| << this->GeneratorTarget->GetName() << "\n" | |
| << variableName << " ="; | |
| auto const& lineContinue = this->GlobalGenerator->LineContinueDirective; | |
| cmValue pchExtension = this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION"); | |
| for (std::string const& obj : this->Objects) { | |
| if (cmHasSuffix(obj, pchExtension)) { | |
| continue; | |
| } | |
| *this->BuildFileStream << " " << lineContinue; | |
| *this->BuildFileStream | |
| << cmLocalUnixMakefileGenerator3::ConvertToQuotedOutputPath( | |
| obj, useWatcomQuote); | |
| } | |
| *this->BuildFileStream << "\n"; | |
| // Write a make variable assignment that lists all external objects | |
| // for the target. | |
| variableNameExternal = this->LocalGenerator->CreateMakeVariable( | |
| this->GeneratorTarget->GetName(), "_EXTERNAL_OBJECTS"); | |
| /* clang-format off */ | |
| *this->BuildFileStream | |
| << "\n" | |
| << "# External object files for target " | |
| << this->GeneratorTarget->GetName() << "\n" | |
| << variableNameExternal << " ="; | |
| /* clang-format on */ | |
| for (std::string const& obj : this->ExternalObjects) { | |
| *this->BuildFileStream << " " << lineContinue; | |
| *this->BuildFileStream | |
| << cmLocalUnixMakefileGenerator3::ConvertToQuotedOutputPath( | |
| obj, useWatcomQuote); | |
| } | |
| *this->BuildFileStream << "\n" | |
| << "\n"; | |
| } | |
| class cmMakefileTargetGeneratorObjectStrings | |
| { | |
| public: | |
| cmMakefileTargetGeneratorObjectStrings(std::vector<std::string>& strings, | |
| cmOutputConverter* outputConverter, | |
| bool useWatcomQuote, | |
| cmStateDirectory const& stateDir, | |
| std::string::size_type limit) | |
| : Strings(strings) | |
| , OutputConverter(outputConverter) | |
| , UseWatcomQuote(useWatcomQuote) | |
| , StateDir(stateDir) | |
| , LengthLimit(limit) | |
| { | |
| this->Space = ""; | |
| } | |
| void Feed(std::string const& obj) | |
| { | |
| // Construct the name of the next object. | |
| this->NextObject = this->OutputConverter->ConvertToOutputFormat( | |
| this->OutputConverter->MaybeRelativeToCurBinDir(obj), | |
| cmOutputConverter::RESPONSE, this->UseWatcomQuote); | |
| // Roll over to next string if the limit will be exceeded. | |
| if (this->LengthLimit != std::string::npos && | |
| (this->CurrentString.length() + 1 + this->NextObject.length() > | |
| this->LengthLimit)) { | |
| this->Strings.push_back(this->CurrentString); | |
| this->CurrentString.clear(); | |
| this->Space = ""; | |
| } | |
| // Separate from previous object. | |
| this->CurrentString += this->Space; | |
| this->Space = " "; | |
| // Append this object. | |
| this->CurrentString += this->NextObject; | |
| } | |
| void Done() { this->Strings.push_back(this->CurrentString); } | |
| private: | |
| std::vector<std::string>& Strings; | |
| cmOutputConverter* OutputConverter; | |
| bool UseWatcomQuote; | |
| cmStateDirectory StateDir; | |
| std::string::size_type LengthLimit; | |
| std::string CurrentString; | |
| std::string NextObject; | |
| char const* Space; | |
| }; | |
| void cmMakefileTargetGenerator::WriteObjectsStrings( | |
| std::vector<std::string>& objStrings, bool useWatcomQuote, | |
| std::string::size_type limit) | |
| { | |
| cmValue pchExtension = this->Makefile->GetDefinition("CMAKE_PCH_EXTENSION"); | |
| cmMakefileTargetGeneratorObjectStrings helper( | |
| objStrings, this->LocalGenerator, useWatcomQuote, | |
| this->LocalGenerator->GetStateSnapshot().GetDirectory(), limit); | |
| for (std::string const& obj : this->Objects) { | |
| if (cmHasSuffix(obj, pchExtension)) { | |
| continue; | |
| } | |
| helper.Feed(obj); | |
| } | |
| for (std::string const& obj : this->ExternalObjects) { | |
| helper.Feed(obj); | |
| } | |
| auto const ispcAdditionalObjs = | |
| this->GeneratorTarget->GetGeneratedISPCObjects(this->GetConfigName()); | |
| for (auto const& obj : ispcAdditionalObjs) { | |
| helper.Feed(obj.second); | |
| } | |
| helper.Done(); | |
| } | |
| void cmMakefileTargetGenerator::WriteTargetDriverRule( | |
| std::string const& main_output, bool relink) | |
| { | |
| // Compute the name of the driver target. | |
| std::string dir = | |
| this->LocalGenerator->GetRelativeTargetDirectory(this->GeneratorTarget); | |
| std::string buildTargetRuleName = | |
| cmStrCat(std::move(dir), relink ? "/preinstall" : "/build"); | |
| buildTargetRuleName = | |
| this->LocalGenerator->MaybeRelativeToTopBinDir(buildTargetRuleName); | |
| // Build the list of target outputs to drive. | |
| std::vector<std::string> depends{ main_output }; | |
| char const* comment = nullptr; | |
| if (relink) { | |
| // Setup the comment for the preinstall driver. | |
| comment = "Rule to relink during preinstall."; | |
| } else { | |
| // Setup the comment for the main build driver. | |
| comment = "Rule to build all files generated by this target."; | |
| // Make sure all custom command outputs in this target are built. | |
| if (this->CustomCommandDriver == OnBuild) { | |
| this->DriveCustomCommands(depends); | |
| } | |
| // Make sure the extra files are built. | |
| cm::append(depends, this->ExtraFiles); | |
| } | |
| // Write the driver rule. | |
| std::vector<std::string> no_commands; | |
| this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, comment, | |
| buildTargetRuleName, depends, | |
| no_commands, true); | |
| } | |
| void cmMakefileTargetGenerator::AppendTargetDepends( | |
| std::vector<std::string>& depends, bool ignoreType) | |
| { | |
| // Static libraries never depend on anything for linking. | |
| if (this->GeneratorTarget->GetType() == cm::TargetType::STATIC_LIBRARY && | |
| !ignoreType) { | |
| return; | |
| } | |
| std::string const& cfg = this->GetConfigName(); | |
| if (this->GeneratorTarget->HasLinkDependencyFile(cfg)) { | |
| depends.push_back( | |
| cmStrCat(this->TargetBuildDirectoryFull, "/compiler_depend.ts")); | |
| } | |
| // Loop over all library dependencies. | |
| if (cmComputeLinkInformation const* cli = | |
| this->GeneratorTarget->GetLinkInformation(cfg)) { | |
| cm::append(depends, cli->GetDepends()); | |
| } | |
| } | |
| void cmMakefileTargetGenerator::AppendObjectDepends( | |
| std::vector<std::string>& depends) | |
| { | |
| // Add dependencies on the compiled object files. | |
| std::string const& relPath = | |
| this->LocalGenerator->GetHomeRelativeOutputPath(); | |
| for (std::string const& obj : this->Objects) { | |
| std::string objTarget = cmStrCat(relPath, obj); | |
| depends.push_back(std::move(objTarget)); | |
| } | |
| // Add dependencies on the external object files. | |
| cm::append(depends, this->ExternalObjects); | |
| // Add dependency on the Rust main crate root file. | |
| if (cmSourceFile const* mainCrateRoot = | |
| this->GeneratorTarget->GetRustMainCrateRoot(this->GetConfigName())) { | |
| depends.push_back(mainCrateRoot->GetFullPath()); | |
| } | |
| // Add a dependency on the rule file itself. | |
| this->LocalGenerator->AppendRuleDepend(depends, | |
| this->BuildFileNameFull.c_str()); | |
| } | |
| void cmMakefileTargetGenerator::AppendLinkDepends( | |
| std::vector<std::string>& depends, std::string const& linkLanguage) | |
| { | |
| this->AppendObjectDepends(depends); | |
| // Add dependencies on targets that must be built first. | |
| this->AppendTargetDepends(depends); | |
| // Add a dependency on the link definitions file, if any. | |
| if (cmGeneratorTarget::ModuleDefinitionInfo const* mdi = | |
| this->GeneratorTarget->GetModuleDefinitionInfo( | |
| this->GetConfigName())) { | |
| for (cmSourceFile const* src : mdi->Sources) { | |
| depends.push_back(src->GetFullPath()); | |
| } | |
| } | |
| // Add a dependency on user-specified manifest files, if any. | |
| std::vector<cmSourceFile const*> manifest_srcs; | |
| this->GeneratorTarget->GetManifests(manifest_srcs, this->GetConfigName()); | |
| for (cmSourceFile const* manifest_src : manifest_srcs) { | |
| depends.push_back(manifest_src->GetFullPath()); | |
| } | |
| // Add user-specified dependencies. | |
| this->GeneratorTarget->GetLinkDepends(depends, this->GetConfigName(), | |
| linkLanguage); | |
| } | |
| std::string cmMakefileTargetGenerator::GetLinkRule( | |
| std::string const& linkRuleVar) | |
| { | |
| std::string linkRule = this->Makefile->GetRequiredDefinition(linkRuleVar); | |
| if (this->GeneratorTarget->HasImplibGNUtoMS(this->GetConfigName())) { | |
| std::string ruleVar = | |
| cmStrCat("CMAKE_", | |
| this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName()), | |
| "_GNUtoMS_RULE"); | |
| if (cmValue rule = this->Makefile->GetDefinition(ruleVar)) { | |
| linkRule += *rule; | |
| } | |
| } | |
| return linkRule; | |
| } | |
| void cmMakefileTargetGenerator::CloseFileStreams() | |
| { | |
| this->BuildFileStream.reset(); | |
| this->InfoFileStream.reset(); | |
| this->FlagFileStream.reset(); | |
| } | |
| void cmMakefileTargetGenerator::CreateLinkScript( | |
| char const* name, std::vector<std::string> const& link_commands, | |
| std::vector<std::string>& makefile_commands, | |
| std::vector<std::string>& makefile_depends) | |
| { | |
| // Create the link script file. | |
| std::string linkScriptName = | |
| cmStrCat(this->TargetBuildDirectoryFull, '/', name); | |
| cmGeneratedFileStream linkScriptStream(linkScriptName); | |
| linkScriptStream.SetCopyIfDifferent(true); | |
| for (std::string const& link_command : link_commands) { | |
| // Do not write out empty commands or commands beginning in the | |
| // shell no-op ":". | |
| if (!link_command.empty() && link_command[0] != ':') { | |
| linkScriptStream << link_command << "\n"; | |
| } | |
| } | |
| // Create the makefile command to invoke the link script. | |
| std::string link_command = | |
| cmStrCat("$(CMAKE_COMMAND) -E cmake_link_script ", | |
| this->LocalGenerator->ConvertToOutputFormat( | |
| this->LocalGenerator->MaybeRelativeToCurBinDir(linkScriptName), | |
| cmOutputConverter::SHELL), | |
| " --verbose=$(VERBOSE)"); | |
| makefile_commands.push_back(std::move(link_command)); | |
| makefile_depends.push_back(std::move(linkScriptName)); | |
| } | |
| bool cmMakefileTargetGenerator::CheckUseResponseFileForObjects( | |
| std::string const& l) const | |
| { | |
| // Check for an explicit setting one way or the other. | |
| std::string const responseVar = | |
| cmStrCat("CMAKE_", l, "_USE_RESPONSE_FILE_FOR_OBJECTS"); | |
| if (cmValue val = this->Makefile->GetDefinition(responseVar)) { | |
| if (!val->empty()) { | |
| return val.IsOn(); | |
| } | |
| } | |
| // Check for a system limit. | |
| if (size_t const limit = cmSystemTools::CalculateCommandLineLengthLimit()) { | |
| // Compute the total length of our list of object files with room | |
| // for argument separation and quoting. This does not convert paths | |
| // relative to CMAKE_CURRENT_BINARY_DIR like the final list will be, so | |
| // the actual list will likely be much shorter than this. However, in | |
| // the worst case all objects will remain as absolute paths. | |
| size_t length = 0; | |
| for (std::string const& obj : this->Objects) { | |
| length += obj.size() + 3; | |
| } | |
| for (std::string const& ext_obj : this->ExternalObjects) { | |
| length += ext_obj.size() + 3; | |
| } | |
| // We need to guarantee room for both objects and libraries, so | |
| // if the objects take up more than half then use a response file | |
| // for them. | |
| if (length > (limit / 2)) { | |
| return true; | |
| } | |
| } | |
| // We do not need a response file for objects. | |
| return false; | |
| } | |
| bool cmMakefileTargetGenerator::CheckUseResponseFileForLibraries( | |
| std::string const& l) const | |
| { | |
| // Check for an explicit setting one way or the other. | |
| std::string const responseVar = | |
| cmStrCat("CMAKE_", l, "_USE_RESPONSE_FILE_FOR_LIBRARIES"); | |
| if (cmValue val = this->Makefile->GetDefinition(responseVar)) { | |
| if (!val->empty()) { | |
| return val.IsOn(); | |
| } | |
| } | |
| // We do not need a response file for libraries. | |
| return false; | |
| } | |
| std::string cmMakefileTargetGenerator::CreateResponseFile( | |
| std::string const& name, std::string const& options, | |
| std::vector<std::string>& makefile_depends, std::string const& language) | |
| { | |
| // FIXME: Find a better way to determine the response file encoding, | |
| // perhaps using tool-specific platform information variables. | |
| // For now, use the makefile encoding as a heuristic. | |
| codecvt_Encoding responseEncoding = | |
| this->GlobalGenerator->GetMakefileEncoding(); | |
| // Non-MSVC tooling doesn't understand BOM encoded files. | |
| if (responseEncoding == codecvt_Encoding::UTF8_WITH_BOM && | |
| (language == "CUDA" || !this->Makefile->IsOn("MSVC"))) { | |
| responseEncoding = codecvt_Encoding::UTF8; | |
| } | |
| // Create the response file. | |
| std::string responseFileNameFull = | |
| cmStrCat(this->TargetBuildDirectoryFull, '/', name); | |
| cmGeneratedFileStream responseStream(responseFileNameFull, false, | |
| responseEncoding); | |
| responseStream.SetCopyIfDifferent(true); | |
| responseStream << options << "\n"; | |
| // Add a dependency so the target will rebuild when the set of | |
| // objects changes. | |
| makefile_depends.push_back(std::move(responseFileNameFull)); | |
| // Construct the name to be used on the command line. | |
| std::string responseFileName = | |
| cmStrCat(this->TargetBuildDirectory, '/', name); | |
| return responseFileName; | |
| } | |
| std::unique_ptr<cmLinkLineComputer> | |
| cmMakefileTargetGenerator::CreateLinkLineComputer( | |
| cmOutputConverter* outputConverter, cmStateDirectory const& stateDir) | |
| { | |
| if (this->Makefile->IsOn("MSVC60")) { | |
| return this->GlobalGenerator->CreateMSVC60LinkLineComputer(outputConverter, | |
| stateDir); | |
| } | |
| return this->GlobalGenerator->CreateLinkLineComputer(outputConverter, | |
| stateDir); | |
| } | |
| void cmMakefileTargetGenerator::CreateLinkLibs( | |
| cmLinkLineComputer* linkLineComputer, std::string& linkLibs, | |
| bool useResponseFile, std::vector<std::string>& makefile_depends, | |
| std::string const& linkLanguage, ResponseFlagFor responseMode) | |
| { | |
| if (cmComputeLinkInformation* pcli = | |
| this->GeneratorTarget->GetLinkInformation(this->GetConfigName())) { | |
| std::string frameworkPath; | |
| std::string linkPath; | |
| this->LocalGenerator->OutputLinkLibraries(pcli, linkLineComputer, linkLibs, | |
| frameworkPath, linkPath); | |
| linkLibs = cmStrCat(frameworkPath, linkPath, std::move(linkLibs)); | |
| } | |
| if (useResponseFile && | |
| linkLibs.find_first_not_of(' ') != std::string::npos) { | |
| // Lookup the response file reference flag. | |
| std::string responseFlag = this->GetResponseFlag(responseMode); | |
| // Create this response file. | |
| std::string const responseFileName = | |
| (responseMode == DeviceLink) ? "deviceLinkLibs.rsp" : "linkLibs.rsp"; | |
| std::string const responseLang = | |
| (responseMode == DeviceLink) ? "CUDA" : linkLanguage; | |
| std::string link_rsp = this->CreateResponseFile( | |
| responseFileName, linkLibs, makefile_depends, responseLang); | |
| // Reference the response file. | |
| linkLibs = cmStrCat(std::move(responseFlag), | |
| this->LocalGenerator->ConvertToOutputFormat( | |
| std::move(link_rsp), cmOutputConverter::SHELL)); | |
| } | |
| } | |
| void cmMakefileTargetGenerator::CreateObjectLists( | |
| bool useLinkScript, bool useArchiveRules, bool useResponseFile, | |
| std::string& buildObjs, std::vector<std::string>& makefile_depends, | |
| bool useWatcomQuote, std::string const& linkLanguage, | |
| ResponseFlagFor responseMode) | |
| { | |
| std::string variableName; | |
| std::string variableNameExternal; | |
| this->WriteObjectsVariable(variableName, variableNameExternal, | |
| useWatcomQuote); | |
| if (useResponseFile) { | |
| // MSVC response files cannot exceed 128K. | |
| std::string::size_type constexpr responseFileLimit = 131000; | |
| // Construct the individual object list strings. | |
| std::vector<std::string> object_strings; | |
| this->WriteObjectsStrings(object_strings, useWatcomQuote, | |
| responseFileLimit); | |
| // Lookup the response file reference flag. | |
| std::string const responseFlag = this->GetResponseFlag(responseMode); | |
| // Write a response file for each string. | |
| char const* sep = ""; | |
| for (unsigned int i = 0; i < object_strings.size(); ++i) { | |
| // Number the response files. | |
| std::string responseFileName = | |
| cmStrCat((responseMode == DeviceLink) ? "deviceObjects" : "objects", | |
| i + 1, ".rsp"); | |
| // Create this response file. | |
| std::string objects_rsp = this->CreateResponseFile( | |
| responseFileName, object_strings[i], makefile_depends, linkLanguage); | |
| buildObjs = | |
| cmStrCat(std::move(buildObjs), | |
| sep, // Separate from previous response file references. | |
| responseFlag, // Reference the response file. | |
| this->LocalGenerator->ConvertToOutputFormat( | |
| objects_rsp, cmOutputConverter::SHELL)); | |
| sep = " "; | |
| } | |
| } else if (useLinkScript) { | |
| if (!useArchiveRules) { | |
| std::vector<std::string> objStrings; | |
| this->WriteObjectsStrings(objStrings, useWatcomQuote); | |
| buildObjs = objStrings[0]; | |
| } | |
| } else { | |
| buildObjs = | |
| cmStrCat("$(", variableName, ") $(", variableNameExternal, ')'); | |
| } | |
| } | |
| bool cmMakefileTargetGenerator::CreateRustLinkArguments( | |
| std::string const& linkLanguage, std::string& rustMainCrateRootPath, | |
| std::string& rustLinkCrates, std::string& rustNativeObjects) | |
| { | |
| if (linkLanguage == "Rust") { | |
| this->ComputeRustFlagsForObjects(rustLinkCrates, rustNativeObjects, | |
| this->Objects); | |
| this->ComputeRustFlagsForObjects(rustLinkCrates, rustNativeObjects, | |
| this->ExternalObjects); | |
| cmSourceFile const* mainCrateRoot = | |
| this->GeneratorTarget->GetRustMainCrateRoot(this->GetConfigName()); | |
| if (!mainCrateRoot) { | |
| this->Makefile->IssueMessage(MessageType::FATAL_ERROR, | |
| cmStrCat("Target ", | |
| this->GeneratorTarget->GetName(), | |
| " has no main crate root.")); | |
| return false; | |
| } | |
| rustMainCrateRootPath = mainCrateRoot->GetFullPath(); | |
| rustMainCrateRootPath = this->LocalGenerator->ConvertToOutputFormat( | |
| rustMainCrateRootPath, cmOutputConverter::SHELL); | |
| return true; | |
| } | |
| return false; | |
| } | |
| void cmMakefileTargetGenerator::AddIncludeFlags(std::string& flags, | |
| std::string const& lang, | |
| std::string const& /*config*/) | |
| { | |
| std::string const responseVar = | |
| cmStrCat("CMAKE_", lang, "_USE_RESPONSE_FILE_FOR_INCLUDES"); | |
| bool const useResponseFile = this->Makefile->IsOn(responseVar); | |
| std::vector<std::string> includes; | |
| this->LocalGenerator->GetIncludeDirectories(includes, this->GeneratorTarget, | |
| lang, this->GetConfigName()); | |
| std::string const includeFlags = this->LocalGenerator->GetIncludeFlags( | |
| includes, this->GeneratorTarget, lang, this->GetConfigName(), | |
| useResponseFile); | |
| if (includeFlags.empty()) { | |
| return; | |
| } | |
| if (useResponseFile) { | |
| std::string const responseFlagVar = | |
| cmStrCat("CMAKE_", lang, "_RESPONSE_FILE_FLAG"); | |
| std::string responseFlag = | |
| this->Makefile->GetSafeDefinition(responseFlagVar); | |
| if (responseFlag.empty()) { | |
| responseFlag = "@"; | |
| } | |
| std::string const name = cmStrCat("includes_", lang, ".rsp"); | |
| std::string const includes_rsp = this->CreateResponseFile( | |
| name, includeFlags, this->FlagFileDepends[lang], lang); | |
| std::string const arg = | |
| cmStrCat(std::move(responseFlag), | |
| this->LocalGenerator->ConvertToOutputFormat( | |
| includes_rsp, cmOutputConverter::SHELL)); | |
| this->LocalGenerator->AppendFlags(flags, arg); | |
| } else { | |
| this->LocalGenerator->AppendFlags(flags, includeFlags); | |
| } | |
| } | |
| void cmMakefileTargetGenerator::GenDefFile( | |
| std::vector<std::string>& real_link_commands) | |
| { | |
| cmGeneratorTarget::ModuleDefinitionInfo const* mdi = | |
| this->GeneratorTarget->GetModuleDefinitionInfo(this->GetConfigName()); | |
| if (!mdi || !mdi->DefFileGenerated) { | |
| return; | |
| } | |
| std::string cmd = cmSystemTools::GetCMakeCommand(); | |
| cmd = cmStrCat( | |
| this->LocalGenerator->ConvertToOutputFormat(cmd, cmOutputConverter::SHELL), | |
| " -E __create_def ", | |
| this->LocalGenerator->ConvertToOutputFormat( | |
| this->LocalGenerator->MaybeRelativeToCurBinDir(mdi->DefFile), | |
| cmOutputConverter::SHELL), | |
| ' '); | |
| std::string objlist_file = mdi->DefFile + ".objs"; | |
| cmd += this->LocalGenerator->ConvertToOutputFormat( | |
| this->LocalGenerator->MaybeRelativeToCurBinDir(objlist_file), | |
| cmOutputConverter::SHELL); | |
| cmValue const nm_executable = this->Makefile->GetDefinition("CMAKE_NM"); | |
| if (!cmIsOff(nm_executable)) { | |
| cmd += " --nm="; | |
| cmd += this->LocalCommonGenerator->ConvertToOutputFormat( | |
| *nm_executable, cmOutputConverter::SHELL); | |
| } | |
| real_link_commands.insert(real_link_commands.begin(), cmd); | |
| // create a list of obj files for the -E __create_def to read | |
| cmGeneratedFileStream fout(objlist_file); | |
| if (mdi->WindowsExportAllSymbols) { | |
| for (std::string const& obj : this->Objects) { | |
| if (cmHasLiteralSuffix(obj, ".obj")) { | |
| fout << obj << "\n"; | |
| } | |
| } | |
| for (std::string const& obj : this->ExternalObjects) { | |
| fout << obj << "\n"; | |
| } | |
| } | |
| for (cmSourceFile const* src : mdi->Sources) { | |
| fout << src->GetFullPath() << "\n"; | |
| } | |
| } | |
| std::string cmMakefileTargetGenerator::GetResponseFlag( | |
| ResponseFlagFor mode) const | |
| { | |
| std::string responseFlag = "@"; | |
| std::string responseFlagVar; | |
| auto const lang = | |
| this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName()); | |
| if (mode == cmMakefileTargetGenerator::ResponseFlagFor::Link) { | |
| responseFlagVar = cmStrCat("CMAKE_", lang, "_RESPONSE_FILE_LINK_FLAG"); | |
| } else if (mode == cmMakefileTargetGenerator::ResponseFlagFor::DeviceLink) { | |
| responseFlagVar = "CMAKE_CUDA_RESPONSE_FILE_DEVICE_LINK_FLAG"; | |
| } else if (mode == cmMakefileTargetGenerator::ResponseFlagFor::Archive) { | |
| responseFlagVar = cmStrCat("CMAKE_", lang, "_RESPONSE_FILE_ARCHIVE_FLAG"); | |
| } | |
| if (cmValue const p = this->Makefile->GetDefinition(responseFlagVar)) { | |
| responseFlag = *p; | |
| } else if (mode == cmMakefileTargetGenerator::ResponseFlagFor::Archive) { | |
| responseFlagVar = cmStrCat("CMAKE_", lang, "_RESPONSE_FILE_LINK_FLAG"); | |
| if (cmValue const q = this->Makefile->GetDefinition(responseFlagVar)) { | |
| responseFlag = *q; | |
| } | |
| } | |
| return responseFlag; | |
| } |