mirror of
https://github.com/Kitware/CMake.git
synced 2026-01-08 23:00:07 -06:00
Most calls to `MaybeConvertToRelativePath` use one of our common work directories (e.g. top of the build tree) as the local path. Add helpers for each of the common cases to simplify and clarify call sites.
664 lines
24 KiB
C++
664 lines
24 KiB
C++
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
|
#include "cmMakefileExecutableTargetGenerator.h"
|
|
|
|
#include <set>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#include <cm/memory>
|
|
#include <cmext/algorithm>
|
|
|
|
#include "cmGeneratedFileStream.h"
|
|
#include "cmGeneratorTarget.h"
|
|
#include "cmGlobalUnixMakefileGenerator3.h"
|
|
#include "cmLinkLineComputer.h"
|
|
#include "cmLinkLineDeviceComputer.h"
|
|
#include "cmLocalGenerator.h"
|
|
#include "cmLocalUnixMakefileGenerator3.h"
|
|
#include "cmMakefile.h"
|
|
#include "cmOSXBundleGenerator.h"
|
|
#include "cmOutputConverter.h"
|
|
#include "cmProperty.h"
|
|
#include "cmRulePlaceholderExpander.h"
|
|
#include "cmState.h"
|
|
#include "cmStateDirectory.h"
|
|
#include "cmStateSnapshot.h"
|
|
#include "cmStateTypes.h"
|
|
#include "cmStringAlgorithms.h"
|
|
#include "cmSystemTools.h"
|
|
|
|
cmMakefileExecutableTargetGenerator::cmMakefileExecutableTargetGenerator(
|
|
cmGeneratorTarget* target)
|
|
: cmMakefileTargetGenerator(target)
|
|
{
|
|
this->CustomCommandDriver = OnDepends;
|
|
this->TargetNames =
|
|
this->GeneratorTarget->GetExecutableNames(this->GetConfigName());
|
|
|
|
this->OSXBundleGenerator = cm::make_unique<cmOSXBundleGenerator>(target);
|
|
this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders);
|
|
}
|
|
|
|
cmMakefileExecutableTargetGenerator::~cmMakefileExecutableTargetGenerator() =
|
|
default;
|
|
|
|
void cmMakefileExecutableTargetGenerator::WriteRuleFiles()
|
|
{
|
|
// create the build.make file and directory, put in the common blocks
|
|
this->CreateRuleFile();
|
|
|
|
// write rules used to help build object files
|
|
this->WriteCommonCodeRules();
|
|
|
|
// write the per-target per-language flags
|
|
this->WriteTargetLanguageFlags();
|
|
|
|
// write in rules for object files and custom commands
|
|
this->WriteTargetBuildRules();
|
|
|
|
// write the device link rules
|
|
this->WriteDeviceExecutableRule(false);
|
|
|
|
// write the link rules
|
|
this->WriteExecutableRule(false);
|
|
if (this->GeneratorTarget->NeedRelinkBeforeInstall(this->GetConfigName())) {
|
|
// Write rules to link an installable version of the target.
|
|
this->WriteExecutableRule(true);
|
|
}
|
|
|
|
// Write clean target
|
|
this->WriteTargetCleanRules();
|
|
|
|
// Write the dependency generation rule. This must be done last so
|
|
// that multiple output pair information is available.
|
|
this->WriteTargetDependRules();
|
|
|
|
// close the streams
|
|
this->CloseFileStreams();
|
|
}
|
|
|
|
void cmMakefileExecutableTargetGenerator::WriteDeviceExecutableRule(
|
|
bool relink)
|
|
{
|
|
#ifndef CMAKE_BOOTSTRAP
|
|
const bool requiresDeviceLinking = requireDeviceLinking(
|
|
*this->GeneratorTarget, *this->LocalGenerator, this->GetConfigName());
|
|
if (!requiresDeviceLinking) {
|
|
return;
|
|
}
|
|
|
|
std::vector<std::string> commands;
|
|
|
|
// Get the name of the device object to generate.
|
|
std::string const& objExt =
|
|
this->Makefile->GetSafeDefinition("CMAKE_CUDA_OUTPUT_EXTENSION");
|
|
std::string const targetOutput =
|
|
this->GeneratorTarget->ObjectDirectory + "cmake_device_link" + objExt;
|
|
this->DeviceLinkObject = targetOutput;
|
|
|
|
this->NumberOfProgressActions++;
|
|
if (!this->NoRuleMessages) {
|
|
cmLocalUnixMakefileGenerator3::EchoProgress progress;
|
|
this->MakeEchoProgress(progress);
|
|
// Add the link message.
|
|
std::string buildEcho = cmStrCat(
|
|
"Linking CUDA device code ",
|
|
this->LocalGenerator->ConvertToOutputFormat(
|
|
this->LocalGenerator->MaybeRelativeToCurBinDir(this->DeviceLinkObject),
|
|
cmOutputConverter::SHELL));
|
|
this->LocalGenerator->AppendEcho(
|
|
commands, buildEcho, cmLocalUnixMakefileGenerator3::EchoLink, &progress);
|
|
}
|
|
|
|
if (this->Makefile->GetSafeDefinition("CMAKE_CUDA_COMPILER_ID") == "Clang") {
|
|
this->WriteDeviceLinkRule(commands, targetOutput);
|
|
} else {
|
|
this->WriteNvidiaDeviceExecutableRule(relink, commands, targetOutput);
|
|
}
|
|
|
|
// Write the main driver rule to build everything in this target.
|
|
this->WriteTargetDriverRule(targetOutput, relink);
|
|
#else
|
|
static_cast<void>(relink);
|
|
#endif
|
|
}
|
|
|
|
void cmMakefileExecutableTargetGenerator::WriteNvidiaDeviceExecutableRule(
|
|
bool relink, std::vector<std::string>& commands,
|
|
const std::string& targetOutput)
|
|
{
|
|
const std::string linkLanguage = "CUDA";
|
|
|
|
// Build list of dependencies.
|
|
std::vector<std::string> depends;
|
|
this->AppendLinkDepends(depends, linkLanguage);
|
|
|
|
// Build a list of compiler flags and linker flags.
|
|
std::string langFlags;
|
|
std::string linkFlags;
|
|
|
|
// Add language feature flags.
|
|
this->LocalGenerator->AddLanguageFlagsForLinking(
|
|
langFlags, this->GeneratorTarget, linkLanguage, this->GetConfigName());
|
|
|
|
// Add device-specific linker flags.
|
|
this->GetDeviceLinkFlags(linkFlags, linkLanguage);
|
|
|
|
// Construct a list of files associated with this executable that
|
|
// may need to be cleaned.
|
|
std::vector<std::string> exeCleanFiles;
|
|
exeCleanFiles.push_back(
|
|
this->LocalGenerator->MaybeRelativeToCurBinDir(targetOutput));
|
|
|
|
// Determine whether a link script will be used.
|
|
bool useLinkScript = this->GlobalGenerator->GetUseLinkScript();
|
|
|
|
// Construct the main link rule.
|
|
std::vector<std::string> real_link_commands;
|
|
const std::string linkRuleVar = "CMAKE_CUDA_DEVICE_LINK_EXECUTABLE";
|
|
const std::string linkRule = this->GetLinkRule(linkRuleVar);
|
|
std::vector<std::string> commands1;
|
|
cmExpandList(linkRule, real_link_commands);
|
|
|
|
bool useResponseFileForObjects =
|
|
this->CheckUseResponseFileForObjects(linkLanguage);
|
|
bool const useResponseFileForLibs =
|
|
this->CheckUseResponseFileForLibraries(linkLanguage);
|
|
|
|
// Expand the rule variables.
|
|
{
|
|
bool useWatcomQuote =
|
|
this->Makefile->IsOn(linkRuleVar + "_USE_WATCOM_QUOTE");
|
|
|
|
// Set path conversion for link script shells.
|
|
this->LocalGenerator->SetLinkScriptShell(useLinkScript);
|
|
|
|
std::unique_ptr<cmLinkLineComputer> linkLineComputer(
|
|
new cmLinkLineDeviceComputer(
|
|
this->LocalGenerator,
|
|
this->LocalGenerator->GetStateSnapshot().GetDirectory()));
|
|
linkLineComputer->SetForResponse(useResponseFileForLibs);
|
|
linkLineComputer->SetUseWatcomQuote(useWatcomQuote);
|
|
linkLineComputer->SetRelink(relink);
|
|
|
|
// Collect up flags to link in needed libraries.
|
|
std::string linkLibs;
|
|
this->CreateLinkLibs(linkLineComputer.get(), linkLibs,
|
|
useResponseFileForLibs, depends);
|
|
|
|
// Construct object file lists that may be needed to expand the
|
|
// rule.
|
|
std::string buildObjs;
|
|
this->CreateObjectLists(useLinkScript, false, useResponseFileForObjects,
|
|
buildObjs, depends, useWatcomQuote);
|
|
|
|
std::string const& aixExports = this->GetAIXExports(this->GetConfigName());
|
|
|
|
cmRulePlaceholderExpander::RuleVariables vars;
|
|
std::string objectDir = this->GeneratorTarget->GetSupportDirectory();
|
|
|
|
objectDir = this->LocalGenerator->ConvertToOutputFormat(
|
|
this->LocalGenerator->MaybeRelativeToCurBinDir(objectDir),
|
|
cmOutputConverter::SHELL);
|
|
|
|
cmOutputConverter::OutputFormat output = (useWatcomQuote)
|
|
? cmOutputConverter::WATCOMQUOTE
|
|
: cmOutputConverter::SHELL;
|
|
std::string target = this->LocalGenerator->ConvertToOutputFormat(
|
|
this->LocalGenerator->MaybeRelativeToCurBinDir(targetOutput), output);
|
|
|
|
std::string targetFullPathCompilePDB =
|
|
this->ComputeTargetCompilePDB(this->GetConfigName());
|
|
std::string targetOutPathCompilePDB =
|
|
this->LocalGenerator->ConvertToOutputFormat(targetFullPathCompilePDB,
|
|
cmOutputConverter::SHELL);
|
|
|
|
vars.Language = linkLanguage.c_str();
|
|
vars.AIXExports = aixExports.c_str();
|
|
vars.Objects = buildObjs.c_str();
|
|
vars.ObjectDir = objectDir.c_str();
|
|
vars.Target = target.c_str();
|
|
vars.LinkLibraries = linkLibs.c_str();
|
|
vars.LanguageCompileFlags = langFlags.c_str();
|
|
vars.LinkFlags = linkFlags.c_str();
|
|
vars.TargetCompilePDB = targetOutPathCompilePDB.c_str();
|
|
|
|
std::string launcher;
|
|
|
|
cmProp val = this->LocalGenerator->GetRuleLauncher(this->GeneratorTarget,
|
|
"RULE_LAUNCH_LINK");
|
|
if (cmNonempty(val)) {
|
|
launcher = cmStrCat(*val, ' ');
|
|
}
|
|
|
|
std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
|
|
this->LocalGenerator->CreateRulePlaceholderExpander());
|
|
|
|
// Expand placeholders in the commands.
|
|
rulePlaceholderExpander->SetTargetImpLib(targetOutput);
|
|
for (std::string& real_link_command : real_link_commands) {
|
|
real_link_command = cmStrCat(launcher, real_link_command);
|
|
rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator,
|
|
real_link_command, vars);
|
|
}
|
|
|
|
// Restore path conversion to normal shells.
|
|
this->LocalGenerator->SetLinkScriptShell(false);
|
|
}
|
|
|
|
// Optionally convert the build rule to use a script to avoid long
|
|
// command lines in the make shell.
|
|
if (useLinkScript) {
|
|
// Use a link script.
|
|
const char* name = (relink ? "drelink.txt" : "dlink.txt");
|
|
this->CreateLinkScript(name, real_link_commands, commands1, depends);
|
|
} else {
|
|
// No link script. Just use the link rule directly.
|
|
commands1 = real_link_commands;
|
|
}
|
|
this->LocalGenerator->CreateCDCommand(
|
|
commands1, this->Makefile->GetCurrentBinaryDirectory(),
|
|
this->LocalGenerator->GetBinaryDirectory());
|
|
cm::append(commands, commands1);
|
|
commands1.clear();
|
|
|
|
// Write the build rule.
|
|
this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr,
|
|
targetOutput, depends, commands, false);
|
|
|
|
// Clean all the possible executable names and symlinks.
|
|
this->CleanFiles.insert(exeCleanFiles.begin(), exeCleanFiles.end());
|
|
}
|
|
|
|
void cmMakefileExecutableTargetGenerator::WriteExecutableRule(bool relink)
|
|
{
|
|
std::vector<std::string> commands;
|
|
|
|
// Get the name of the executable to generate.
|
|
cmGeneratorTarget::Names targetNames =
|
|
this->GeneratorTarget->GetExecutableNames(this->GetConfigName());
|
|
|
|
// Construct the full path version of the names.
|
|
std::string outpath =
|
|
this->GeneratorTarget->GetDirectory(this->GetConfigName());
|
|
if (this->GeneratorTarget->IsAppBundleOnApple()) {
|
|
this->OSXBundleGenerator->CreateAppBundle(targetNames.Output, outpath,
|
|
this->GetConfigName());
|
|
}
|
|
outpath += '/';
|
|
std::string outpathImp;
|
|
if (relink) {
|
|
outpath = cmStrCat(this->Makefile->GetCurrentBinaryDirectory(),
|
|
"/CMakeFiles/CMakeRelink.dir");
|
|
cmSystemTools::MakeDirectory(outpath);
|
|
outpath += '/';
|
|
if (!targetNames.ImportLibrary.empty()) {
|
|
outpathImp = outpath;
|
|
}
|
|
} else {
|
|
cmSystemTools::MakeDirectory(outpath);
|
|
if (!targetNames.ImportLibrary.empty()) {
|
|
outpathImp = this->GeneratorTarget->GetDirectory(
|
|
this->GetConfigName(), cmStateEnums::ImportLibraryArtifact);
|
|
cmSystemTools::MakeDirectory(outpathImp);
|
|
outpathImp += '/';
|
|
}
|
|
}
|
|
|
|
std::string compilePdbOutputPath =
|
|
this->GeneratorTarget->GetCompilePDBDirectory(this->GetConfigName());
|
|
cmSystemTools::MakeDirectory(compilePdbOutputPath);
|
|
|
|
std::string pdbOutputPath =
|
|
this->GeneratorTarget->GetPDBDirectory(this->GetConfigName());
|
|
cmSystemTools::MakeDirectory(pdbOutputPath);
|
|
pdbOutputPath += '/';
|
|
|
|
std::string targetFullPath = outpath + targetNames.Output;
|
|
std::string targetFullPathReal = outpath + targetNames.Real;
|
|
std::string targetFullPathPDB = pdbOutputPath + targetNames.PDB;
|
|
std::string targetFullPathImport = outpathImp + targetNames.ImportLibrary;
|
|
std::string targetOutPathPDB = this->LocalGenerator->ConvertToOutputFormat(
|
|
targetFullPathPDB, cmOutputConverter::SHELL);
|
|
// Convert to the output path to use in constructing commands.
|
|
std::string targetOutPath = this->LocalGenerator->ConvertToOutputFormat(
|
|
this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPath),
|
|
cmOutputConverter::SHELL);
|
|
std::string targetOutPathReal = this->LocalGenerator->ConvertToOutputFormat(
|
|
this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPathReal),
|
|
cmOutputConverter::SHELL);
|
|
std::string targetOutPathImport =
|
|
this->LocalGenerator->ConvertToOutputFormat(
|
|
this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPathImport),
|
|
cmOutputConverter::SHELL);
|
|
|
|
// Get the language to use for linking this executable.
|
|
std::string linkLanguage =
|
|
this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName());
|
|
|
|
// Make sure we have a link language.
|
|
if (linkLanguage.empty()) {
|
|
cmSystemTools::Error("Cannot determine link language for target \"" +
|
|
this->GeneratorTarget->GetName() + "\".");
|
|
return;
|
|
}
|
|
|
|
// Build list of dependencies.
|
|
std::vector<std::string> depends;
|
|
this->AppendLinkDepends(depends, linkLanguage);
|
|
if (!this->DeviceLinkObject.empty()) {
|
|
depends.push_back(this->DeviceLinkObject);
|
|
}
|
|
|
|
this->NumberOfProgressActions++;
|
|
if (!this->NoRuleMessages) {
|
|
cmLocalUnixMakefileGenerator3::EchoProgress progress;
|
|
this->MakeEchoProgress(progress);
|
|
// Add the link message.
|
|
std::string buildEcho =
|
|
cmStrCat("Linking ", linkLanguage, " executable ", targetOutPath);
|
|
this->LocalGenerator->AppendEcho(
|
|
commands, buildEcho, cmLocalUnixMakefileGenerator3::EchoLink, &progress);
|
|
}
|
|
|
|
// Build a list of compiler flags and linker flags.
|
|
std::string flags;
|
|
std::string linkFlags;
|
|
|
|
// Add flags to create an executable.
|
|
this->LocalGenerator->AddConfigVariableFlags(
|
|
linkFlags, "CMAKE_EXE_LINKER_FLAGS", this->GetConfigName());
|
|
|
|
if (this->GeneratorTarget->IsWin32Executable(
|
|
this->Makefile->GetSafeDefinition("CMAKE_BUILD_TYPE"))) {
|
|
this->LocalGenerator->AppendFlags(
|
|
linkFlags,
|
|
this->Makefile->GetSafeDefinition(
|
|
cmStrCat("CMAKE_", linkLanguage, "_CREATE_WIN32_EXE")));
|
|
} else {
|
|
this->LocalGenerator->AppendFlags(
|
|
linkFlags,
|
|
this->Makefile->GetSafeDefinition(
|
|
cmStrCat("CMAKE_", linkLanguage, "_CREATE_CONSOLE_EXE")));
|
|
}
|
|
|
|
// Add symbol export flags if necessary.
|
|
if (this->GeneratorTarget->IsExecutableWithExports()) {
|
|
std::string export_flag_var =
|
|
cmStrCat("CMAKE_EXE_EXPORTS_", linkLanguage, "_FLAG");
|
|
this->LocalGenerator->AppendFlags(
|
|
linkFlags, this->Makefile->GetSafeDefinition(export_flag_var));
|
|
}
|
|
|
|
this->LocalGenerator->AppendFlags(linkFlags,
|
|
this->LocalGenerator->GetLinkLibsCMP0065(
|
|
linkLanguage, *this->GeneratorTarget));
|
|
|
|
if (this->GeneratorTarget->GetPropertyAsBool("LINK_WHAT_YOU_USE")) {
|
|
this->LocalGenerator->AppendFlags(linkFlags, " -Wl,--no-as-needed");
|
|
}
|
|
|
|
// Add language feature flags.
|
|
this->LocalGenerator->AddLanguageFlagsForLinking(
|
|
flags, this->GeneratorTarget, linkLanguage, this->GetConfigName());
|
|
|
|
this->LocalGenerator->AddArchitectureFlags(
|
|
flags, this->GeneratorTarget, linkLanguage, this->GetConfigName());
|
|
|
|
// Add target-specific linker flags.
|
|
this->GetTargetLinkFlags(linkFlags, linkLanguage);
|
|
|
|
{
|
|
std::unique_ptr<cmLinkLineComputer> linkLineComputer =
|
|
this->CreateLinkLineComputer(
|
|
this->LocalGenerator,
|
|
this->LocalGenerator->GetStateSnapshot().GetDirectory());
|
|
|
|
this->AddModuleDefinitionFlag(linkLineComputer.get(), linkFlags,
|
|
this->GetConfigName());
|
|
}
|
|
|
|
this->LocalGenerator->AppendIPOLinkerFlags(
|
|
linkFlags, this->GeneratorTarget, this->GetConfigName(), linkLanguage);
|
|
|
|
// Construct a list of files associated with this executable that
|
|
// may need to be cleaned.
|
|
std::vector<std::string> exeCleanFiles;
|
|
exeCleanFiles.push_back(
|
|
this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPath));
|
|
#ifdef _WIN32
|
|
// There may be a manifest file for this target. Add it to the
|
|
// clean set just in case.
|
|
exeCleanFiles.push_back(this->LocalGenerator->MaybeRelativeToCurBinDir(
|
|
targetFullPath + ".manifest"));
|
|
#endif
|
|
if (this->TargetNames.Real != this->TargetNames.Output) {
|
|
exeCleanFiles.push_back(
|
|
this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPathReal));
|
|
}
|
|
if (!this->TargetNames.ImportLibrary.empty()) {
|
|
exeCleanFiles.push_back(
|
|
this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPathImport));
|
|
std::string implib;
|
|
if (this->GeneratorTarget->GetImplibGNUtoMS(
|
|
this->GetConfigName(), targetFullPathImport, implib)) {
|
|
exeCleanFiles.push_back(
|
|
this->LocalGenerator->MaybeRelativeToCurBinDir(implib));
|
|
}
|
|
}
|
|
|
|
// List the PDB for cleaning only when the whole target is
|
|
// cleaned. We do not want to delete the .pdb file just before
|
|
// linking the target.
|
|
this->CleanFiles.insert(
|
|
this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPathPDB));
|
|
|
|
// Add the pre-build and pre-link rules building but not when relinking.
|
|
if (!relink) {
|
|
this->LocalGenerator->AppendCustomCommands(
|
|
commands, this->GeneratorTarget->GetPreBuildCommands(),
|
|
this->GeneratorTarget, this->LocalGenerator->GetBinaryDirectory());
|
|
this->LocalGenerator->AppendCustomCommands(
|
|
commands, this->GeneratorTarget->GetPreLinkCommands(),
|
|
this->GeneratorTarget, this->LocalGenerator->GetBinaryDirectory());
|
|
}
|
|
|
|
// Determine whether a link script will be used.
|
|
bool useLinkScript = this->GlobalGenerator->GetUseLinkScript();
|
|
|
|
// Construct the main link rule.
|
|
std::vector<std::string> real_link_commands;
|
|
std::string linkRuleVar = this->GeneratorTarget->GetCreateRuleVariable(
|
|
linkLanguage, this->GetConfigName());
|
|
std::string linkRule = this->GetLinkRule(linkRuleVar);
|
|
std::vector<std::string> commands1;
|
|
cmExpandList(linkRule, real_link_commands);
|
|
if (this->GeneratorTarget->IsExecutableWithExports()) {
|
|
// If a separate rule for creating an import library is specified
|
|
// add it now.
|
|
std::string implibRuleVar =
|
|
cmStrCat("CMAKE_", linkLanguage, "_CREATE_IMPORT_LIBRARY");
|
|
this->Makefile->GetDefExpandList(implibRuleVar, real_link_commands);
|
|
}
|
|
|
|
bool useResponseFileForObjects =
|
|
this->CheckUseResponseFileForObjects(linkLanguage);
|
|
bool const useResponseFileForLibs =
|
|
this->CheckUseResponseFileForLibraries(linkLanguage);
|
|
|
|
// Expand the rule variables.
|
|
{
|
|
bool useWatcomQuote =
|
|
this->Makefile->IsOn(linkRuleVar + "_USE_WATCOM_QUOTE");
|
|
|
|
// Set path conversion for link script shells.
|
|
this->LocalGenerator->SetLinkScriptShell(useLinkScript);
|
|
|
|
std::unique_ptr<cmLinkLineComputer> linkLineComputer =
|
|
this->CreateLinkLineComputer(
|
|
this->LocalGenerator,
|
|
this->LocalGenerator->GetStateSnapshot().GetDirectory());
|
|
linkLineComputer->SetForResponse(useResponseFileForLibs);
|
|
linkLineComputer->SetUseWatcomQuote(useWatcomQuote);
|
|
linkLineComputer->SetRelink(relink);
|
|
|
|
// Collect up flags to link in needed libraries.
|
|
std::string linkLibs;
|
|
this->CreateLinkLibs(linkLineComputer.get(), linkLibs,
|
|
useResponseFileForLibs, depends);
|
|
|
|
// Construct object file lists that may be needed to expand the
|
|
// rule.
|
|
std::string buildObjs;
|
|
this->CreateObjectLists(useLinkScript, false, useResponseFileForObjects,
|
|
buildObjs, depends, useWatcomQuote);
|
|
if (!this->DeviceLinkObject.empty()) {
|
|
buildObjs += " " +
|
|
this->LocalGenerator->ConvertToOutputFormat(
|
|
this->LocalGenerator->MaybeRelativeToCurBinDir(
|
|
this->DeviceLinkObject),
|
|
cmOutputConverter::SHELL);
|
|
}
|
|
|
|
// maybe create .def file from list of objects
|
|
this->GenDefFile(real_link_commands);
|
|
|
|
std::string manifests = this->GetManifests(this->GetConfigName());
|
|
|
|
cmRulePlaceholderExpander::RuleVariables vars;
|
|
vars.CMTargetName = this->GeneratorTarget->GetName().c_str();
|
|
vars.CMTargetType =
|
|
cmState::GetTargetTypeName(this->GeneratorTarget->GetType()).c_str();
|
|
vars.Language = linkLanguage.c_str();
|
|
vars.Objects = buildObjs.c_str();
|
|
std::string objectDir = this->GeneratorTarget->GetSupportDirectory();
|
|
|
|
objectDir = this->LocalGenerator->ConvertToOutputFormat(
|
|
this->LocalGenerator->MaybeRelativeToCurBinDir(objectDir),
|
|
cmOutputConverter::SHELL);
|
|
vars.ObjectDir = objectDir.c_str();
|
|
cmOutputConverter::OutputFormat output = (useWatcomQuote)
|
|
? cmOutputConverter::WATCOMQUOTE
|
|
: cmOutputConverter::SHELL;
|
|
std::string target = this->LocalGenerator->ConvertToOutputFormat(
|
|
this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPathReal),
|
|
output);
|
|
vars.Target = target.c_str();
|
|
vars.TargetPDB = targetOutPathPDB.c_str();
|
|
|
|
// Setup the target version.
|
|
std::string targetVersionMajor;
|
|
std::string targetVersionMinor;
|
|
{
|
|
std::ostringstream majorStream;
|
|
std::ostringstream minorStream;
|
|
int major;
|
|
int minor;
|
|
this->GeneratorTarget->GetTargetVersion(major, minor);
|
|
majorStream << major;
|
|
minorStream << minor;
|
|
targetVersionMajor = majorStream.str();
|
|
targetVersionMinor = minorStream.str();
|
|
}
|
|
vars.TargetVersionMajor = targetVersionMajor.c_str();
|
|
vars.TargetVersionMinor = targetVersionMinor.c_str();
|
|
|
|
vars.LinkLibraries = linkLibs.c_str();
|
|
vars.Flags = flags.c_str();
|
|
vars.LinkFlags = linkFlags.c_str();
|
|
vars.Manifests = manifests.c_str();
|
|
|
|
if (this->GeneratorTarget->GetPropertyAsBool("LINK_WHAT_YOU_USE")) {
|
|
std::string cmakeCommand =
|
|
cmStrCat(this->LocalGenerator->ConvertToOutputFormat(
|
|
cmSystemTools::GetCMakeCommand(), cmLocalGenerator::SHELL),
|
|
" -E __run_co_compile --lwyu=", targetOutPathReal);
|
|
real_link_commands.push_back(std::move(cmakeCommand));
|
|
}
|
|
|
|
std::string launcher;
|
|
|
|
cmProp val = this->LocalGenerator->GetRuleLauncher(this->GeneratorTarget,
|
|
"RULE_LAUNCH_LINK");
|
|
if (cmNonempty(val)) {
|
|
launcher = cmStrCat(*val, ' ');
|
|
}
|
|
|
|
std::unique_ptr<cmRulePlaceholderExpander> rulePlaceholderExpander(
|
|
this->LocalGenerator->CreateRulePlaceholderExpander());
|
|
|
|
// Expand placeholders in the commands.
|
|
rulePlaceholderExpander->SetTargetImpLib(targetOutPathImport);
|
|
for (std::string& real_link_command : real_link_commands) {
|
|
real_link_command = cmStrCat(launcher, real_link_command);
|
|
rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator,
|
|
real_link_command, vars);
|
|
}
|
|
|
|
// Restore path conversion to normal shells.
|
|
this->LocalGenerator->SetLinkScriptShell(false);
|
|
}
|
|
|
|
// Optionally convert the build rule to use a script to avoid long
|
|
// command lines in the make shell.
|
|
if (useLinkScript) {
|
|
// Use a link script.
|
|
const char* name = (relink ? "relink.txt" : "link.txt");
|
|
this->CreateLinkScript(name, real_link_commands, commands1, depends);
|
|
} else {
|
|
// No link script. Just use the link rule directly.
|
|
commands1 = real_link_commands;
|
|
}
|
|
this->LocalGenerator->CreateCDCommand(
|
|
commands1, this->Makefile->GetCurrentBinaryDirectory(),
|
|
this->LocalGenerator->GetBinaryDirectory());
|
|
cm::append(commands, commands1);
|
|
commands1.clear();
|
|
|
|
// Add a rule to create necessary symlinks for the library.
|
|
if (targetOutPath != targetOutPathReal) {
|
|
std::string symlink =
|
|
cmStrCat("$(CMAKE_COMMAND) -E cmake_symlink_executable ",
|
|
targetOutPathReal, ' ', targetOutPath);
|
|
commands1.push_back(std::move(symlink));
|
|
this->LocalGenerator->CreateCDCommand(
|
|
commands1, this->Makefile->GetCurrentBinaryDirectory(),
|
|
this->LocalGenerator->GetBinaryDirectory());
|
|
cm::append(commands, commands1);
|
|
commands1.clear();
|
|
}
|
|
|
|
// Add the post-build rules when building but not when relinking.
|
|
if (!relink) {
|
|
this->LocalGenerator->AppendCustomCommands(
|
|
commands, this->GeneratorTarget->GetPostBuildCommands(),
|
|
this->GeneratorTarget, this->LocalGenerator->GetBinaryDirectory());
|
|
}
|
|
|
|
// Write the build rule.
|
|
this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr,
|
|
targetFullPathReal, depends, commands,
|
|
false);
|
|
|
|
// The symlink name for the target should depend on the real target
|
|
// so if the target version changes it rebuilds and recreates the
|
|
// symlink.
|
|
if (targetFullPath != targetFullPathReal) {
|
|
depends.clear();
|
|
commands.clear();
|
|
depends.push_back(targetFullPathReal);
|
|
this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr,
|
|
targetFullPath, depends, commands,
|
|
false);
|
|
}
|
|
|
|
// Write the main driver rule to build everything in this target.
|
|
this->WriteTargetDriverRule(targetFullPath, relink);
|
|
|
|
// Clean all the possible executable names and symlinks.
|
|
this->CleanFiles.insert(exeCleanFiles.begin(), exeCleanFiles.end());
|
|
}
|