mirror of
https://github.com/Kitware/CMake.git
synced 2025-10-14 02:08:27 +08: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.
947 lines
36 KiB
C++
947 lines
36 KiB
C++
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
|
#include "cmMakefileLibraryTargetGenerator.h"
|
|
|
|
#include <cstddef>
|
|
#include <set>
|
|
#include <sstream>
|
|
#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"
|
|
|
|
cmMakefileLibraryTargetGenerator::cmMakefileLibraryTargetGenerator(
|
|
cmGeneratorTarget* target)
|
|
: cmMakefileTargetGenerator(target)
|
|
{
|
|
this->CustomCommandDriver = OnDepends;
|
|
if (this->GeneratorTarget->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
|
|
this->TargetNames =
|
|
this->GeneratorTarget->GetLibraryNames(this->GetConfigName());
|
|
}
|
|
|
|
this->OSXBundleGenerator = cm::make_unique<cmOSXBundleGenerator>(target);
|
|
this->OSXBundleGenerator->SetMacContentFolders(&this->MacContentFolders);
|
|
}
|
|
|
|
cmMakefileLibraryTargetGenerator::~cmMakefileLibraryTargetGenerator() =
|
|
default;
|
|
|
|
void cmMakefileLibraryTargetGenerator::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 link rules
|
|
// Write the rule for this target type.
|
|
switch (this->GeneratorTarget->GetType()) {
|
|
case cmStateEnums::STATIC_LIBRARY:
|
|
this->WriteStaticLibraryRules();
|
|
break;
|
|
case cmStateEnums::SHARED_LIBRARY:
|
|
this->WriteSharedLibraryRules(false);
|
|
if (this->GeneratorTarget->NeedRelinkBeforeInstall(
|
|
this->GetConfigName())) {
|
|
// Write rules to link an installable version of the target.
|
|
this->WriteSharedLibraryRules(true);
|
|
}
|
|
break;
|
|
case cmStateEnums::MODULE_LIBRARY:
|
|
this->WriteModuleLibraryRules(false);
|
|
if (this->GeneratorTarget->NeedRelinkBeforeInstall(
|
|
this->GetConfigName())) {
|
|
// Write rules to link an installable version of the target.
|
|
this->WriteModuleLibraryRules(true);
|
|
}
|
|
break;
|
|
case cmStateEnums::OBJECT_LIBRARY:
|
|
this->WriteObjectLibraryRules();
|
|
break;
|
|
default:
|
|
// If language is not known, this is an error.
|
|
cmSystemTools::Error("Unknown Library Type");
|
|
break;
|
|
}
|
|
|
|
// 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 cmMakefileLibraryTargetGenerator::WriteObjectLibraryRules()
|
|
{
|
|
std::vector<std::string> commands;
|
|
std::vector<std::string> depends;
|
|
|
|
// Add post-build rules.
|
|
this->LocalGenerator->AppendCustomCommands(
|
|
commands, this->GeneratorTarget->GetPostBuildCommands(),
|
|
this->GeneratorTarget, this->LocalGenerator->GetBinaryDirectory());
|
|
|
|
// Depend on the object files.
|
|
this->AppendObjectDepends(depends);
|
|
|
|
// Write the rule.
|
|
this->LocalGenerator->WriteMakeRule(*this->BuildFileStream, nullptr,
|
|
this->GeneratorTarget->GetName(),
|
|
depends, commands, true);
|
|
|
|
// Write the main driver rule to build everything in this target.
|
|
this->WriteTargetDriverRule(this->GeneratorTarget->GetName(), false);
|
|
}
|
|
|
|
void cmMakefileLibraryTargetGenerator::WriteStaticLibraryRules()
|
|
{
|
|
const bool requiresDeviceLinking = requireDeviceLinking(
|
|
*this->GeneratorTarget, *this->LocalGenerator, this->GetConfigName());
|
|
if (requiresDeviceLinking) {
|
|
this->WriteDeviceLibraryRules("CMAKE_CUDA_DEVICE_LINK_LIBRARY", false);
|
|
}
|
|
|
|
std::string linkLanguage =
|
|
this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName());
|
|
|
|
std::string linkRuleVar = this->GeneratorTarget->GetCreateRuleVariable(
|
|
linkLanguage, this->GetConfigName());
|
|
|
|
std::string extraFlags;
|
|
this->LocalGenerator->GetStaticLibraryFlags(
|
|
extraFlags, this->GetConfigName(), linkLanguage, this->GeneratorTarget);
|
|
this->WriteLibraryRules(linkRuleVar, extraFlags, false);
|
|
}
|
|
|
|
void cmMakefileLibraryTargetGenerator::WriteSharedLibraryRules(bool relink)
|
|
{
|
|
if (this->GeneratorTarget->IsFrameworkOnApple()) {
|
|
this->WriteFrameworkRules(relink);
|
|
return;
|
|
}
|
|
|
|
if (!relink) {
|
|
const bool requiresDeviceLinking = requireDeviceLinking(
|
|
*this->GeneratorTarget, *this->LocalGenerator, this->GetConfigName());
|
|
if (requiresDeviceLinking) {
|
|
this->WriteDeviceLibraryRules("CMAKE_CUDA_DEVICE_LINK_LIBRARY", relink);
|
|
}
|
|
}
|
|
|
|
std::string linkLanguage =
|
|
this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName());
|
|
std::string linkRuleVar =
|
|
cmStrCat("CMAKE_", linkLanguage, "_CREATE_SHARED_LIBRARY");
|
|
|
|
std::string extraFlags;
|
|
this->GetTargetLinkFlags(extraFlags, linkLanguage);
|
|
this->LocalGenerator->AddConfigVariableFlags(
|
|
extraFlags, "CMAKE_SHARED_LINKER_FLAGS", this->GetConfigName());
|
|
|
|
std::unique_ptr<cmLinkLineComputer> linkLineComputer =
|
|
this->CreateLinkLineComputer(
|
|
this->LocalGenerator,
|
|
this->LocalGenerator->GetStateSnapshot().GetDirectory());
|
|
|
|
this->AddModuleDefinitionFlag(linkLineComputer.get(), extraFlags,
|
|
this->GetConfigName());
|
|
|
|
if (this->GeneratorTarget->GetPropertyAsBool("LINK_WHAT_YOU_USE")) {
|
|
this->LocalGenerator->AppendFlags(extraFlags, " -Wl,--no-as-needed");
|
|
}
|
|
this->WriteLibraryRules(linkRuleVar, extraFlags, relink);
|
|
}
|
|
|
|
void cmMakefileLibraryTargetGenerator::WriteModuleLibraryRules(bool relink)
|
|
{
|
|
if (!relink) {
|
|
const bool requiresDeviceLinking = requireDeviceLinking(
|
|
*this->GeneratorTarget, *this->LocalGenerator, this->GetConfigName());
|
|
if (requiresDeviceLinking) {
|
|
this->WriteDeviceLibraryRules("CMAKE_CUDA_DEVICE_LINK_LIBRARY", relink);
|
|
}
|
|
}
|
|
|
|
std::string linkLanguage =
|
|
this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName());
|
|
std::string linkRuleVar =
|
|
cmStrCat("CMAKE_", linkLanguage, "_CREATE_SHARED_MODULE");
|
|
|
|
std::string extraFlags;
|
|
this->GetTargetLinkFlags(extraFlags, linkLanguage);
|
|
this->LocalGenerator->AddConfigVariableFlags(
|
|
extraFlags, "CMAKE_MODULE_LINKER_FLAGS", this->GetConfigName());
|
|
|
|
std::unique_ptr<cmLinkLineComputer> linkLineComputer =
|
|
this->CreateLinkLineComputer(
|
|
this->LocalGenerator,
|
|
this->LocalGenerator->GetStateSnapshot().GetDirectory());
|
|
|
|
this->AddModuleDefinitionFlag(linkLineComputer.get(), extraFlags,
|
|
this->GetConfigName());
|
|
|
|
this->WriteLibraryRules(linkRuleVar, extraFlags, relink);
|
|
}
|
|
|
|
void cmMakefileLibraryTargetGenerator::WriteFrameworkRules(bool relink)
|
|
{
|
|
std::string linkLanguage =
|
|
this->GeneratorTarget->GetLinkerLanguage(this->GetConfigName());
|
|
std::string linkRuleVar =
|
|
cmStrCat("CMAKE_", linkLanguage, "_CREATE_MACOSX_FRAMEWORK");
|
|
|
|
std::string extraFlags;
|
|
this->GetTargetLinkFlags(extraFlags, linkLanguage);
|
|
this->LocalGenerator->AddConfigVariableFlags(
|
|
extraFlags, "CMAKE_MACOSX_FRAMEWORK_LINKER_FLAGS", this->GetConfigName());
|
|
|
|
this->WriteLibraryRules(linkRuleVar, extraFlags, relink);
|
|
}
|
|
|
|
void cmMakefileLibraryTargetGenerator::WriteDeviceLibraryRules(
|
|
const std::string& linkRuleVar, bool relink)
|
|
{
|
|
#ifndef CMAKE_BOOTSTRAP
|
|
// TODO: Merge the methods that call this method to avoid
|
|
// code duplication.
|
|
std::vector<std::string> commands;
|
|
std::string const objExt =
|
|
this->Makefile->GetSafeDefinition("CMAKE_CUDA_OUTPUT_EXTENSION");
|
|
|
|
// Get the name of the device object to generate.
|
|
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->WriteNvidiaDeviceLibraryRules(linkRuleVar, relink, commands,
|
|
targetOutput);
|
|
}
|
|
|
|
// Write the main driver rule to build everything in this target.
|
|
this->WriteTargetDriverRule(targetOutput, relink);
|
|
}
|
|
|
|
void cmMakefileLibraryTargetGenerator::WriteNvidiaDeviceLibraryRules(
|
|
const std::string& linkRuleVar, bool relink,
|
|
std::vector<std::string>& commands, const std::string& targetOutput)
|
|
{
|
|
std::string linkLanguage = "CUDA";
|
|
|
|
// Build list of dependencies.
|
|
std::vector<std::string> depends;
|
|
this->AppendLinkDepends(depends, linkLanguage);
|
|
|
|
// Add language-specific flags.
|
|
std::string langFlags;
|
|
this->LocalGenerator->AddLanguageFlagsForLinking(
|
|
langFlags, this->GeneratorTarget, linkLanguage, this->GetConfigName());
|
|
|
|
// Create set of linking flags.
|
|
std::string linkFlags;
|
|
this->GetDeviceLinkFlags(linkFlags, linkLanguage);
|
|
|
|
// Clean files associated with this library.
|
|
std::set<std::string> libCleanFiles;
|
|
libCleanFiles.insert(
|
|
this->LocalGenerator->MaybeRelativeToCurBinDir(targetOutput));
|
|
|
|
// Determine whether a link script will be used.
|
|
bool useLinkScript = this->GlobalGenerator->GetUseLinkScript();
|
|
|
|
bool useResponseFileForObjects =
|
|
this->CheckUseResponseFileForObjects(linkLanguage);
|
|
bool const useResponseFileForLibs =
|
|
this->CheckUseResponseFileForLibraries(linkLanguage);
|
|
|
|
cmRulePlaceholderExpander::RuleVariables vars;
|
|
vars.Language = linkLanguage.c_str();
|
|
|
|
// Expand the rule variables.
|
|
std::vector<std::string> real_link_commands;
|
|
{
|
|
bool useWatcomQuote =
|
|
this->Makefile->IsOn(linkRuleVar + "_USE_WATCOM_QUOTE");
|
|
|
|
// Set path conversion for link script shells.
|
|
this->LocalGenerator->SetLinkScriptShell(useLinkScript);
|
|
|
|
// Collect up flags to link in needed libraries.
|
|
std::string linkLibs;
|
|
std::unique_ptr<cmLinkLineComputer> linkLineComputer(
|
|
new cmLinkLineDeviceComputer(
|
|
this->LocalGenerator,
|
|
this->LocalGenerator->GetStateSnapshot().GetDirectory()));
|
|
linkLineComputer->SetForResponse(useResponseFileForLibs);
|
|
linkLineComputer->SetUseWatcomQuote(useWatcomQuote);
|
|
linkLineComputer->SetRelink(relink);
|
|
|
|
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, // useArchiveRules
|
|
useResponseFileForObjects, buildObjs, depends,
|
|
useWatcomQuote);
|
|
|
|
cmOutputConverter::OutputFormat output = (useWatcomQuote)
|
|
? cmOutputConverter::WATCOMQUOTE
|
|
: cmOutputConverter::SHELL;
|
|
|
|
std::string objectDir = this->GeneratorTarget->GetSupportDirectory();
|
|
objectDir = this->LocalGenerator->ConvertToOutputFormat(
|
|
this->LocalGenerator->MaybeRelativeToCurBinDir(objectDir),
|
|
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.Objects = buildObjs.c_str();
|
|
vars.ObjectDir = objectDir.c_str();
|
|
vars.Target = target.c_str();
|
|
vars.LinkLibraries = linkLibs.c_str();
|
|
vars.ObjectsQuoted = buildObjs.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());
|
|
|
|
// Construct the main link rule and expand placeholders.
|
|
rulePlaceholderExpander->SetTargetImpLib(targetOutput);
|
|
std::string linkRule = this->GetLinkRule(linkRuleVar);
|
|
cmExpandList(linkRule, real_link_commands);
|
|
|
|
// Expand placeholders.
|
|
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);
|
|
|
|
// Clean all the possible library names and symlinks.
|
|
this->CleanFiles.insert(libCleanFiles.begin(), libCleanFiles.end());
|
|
}
|
|
|
|
std::vector<std::string> commands1;
|
|
// 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();
|
|
|
|
// Compute the list of outputs.
|
|
std::vector<std::string> outputs(1, targetOutput);
|
|
|
|
// Write the build rule.
|
|
this->WriteMakeRule(*this->BuildFileStream, nullptr, outputs, depends,
|
|
commands, false);
|
|
#else
|
|
static_cast<void>(linkRuleVar);
|
|
static_cast<void>(relink);
|
|
#endif
|
|
}
|
|
|
|
void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
|
|
const std::string& linkRuleVar, const std::string& extraFlags, bool relink)
|
|
{
|
|
// TODO: Merge the methods that call this method to avoid
|
|
// code duplication.
|
|
std::vector<std::string> commands;
|
|
|
|
// Get the language to use for linking this library.
|
|
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);
|
|
}
|
|
|
|
// Create set of linking flags.
|
|
std::string linkFlags;
|
|
this->LocalGenerator->AppendFlags(linkFlags, extraFlags);
|
|
this->LocalGenerator->AppendIPOLinkerFlags(
|
|
linkFlags, this->GeneratorTarget, this->GetConfigName(), linkLanguage);
|
|
|
|
// Add OSX version flags, if any.
|
|
if (this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY ||
|
|
this->GeneratorTarget->GetType() == cmStateEnums::MODULE_LIBRARY) {
|
|
this->AppendOSXVerFlag(linkFlags, linkLanguage, "COMPATIBILITY", true);
|
|
this->AppendOSXVerFlag(linkFlags, linkLanguage, "CURRENT", false);
|
|
}
|
|
|
|
// Construct the name of the library.
|
|
this->GeneratorTarget->GetLibraryNames(this->GetConfigName());
|
|
|
|
// Construct the full path version of the names.
|
|
std::string outpath;
|
|
std::string outpathImp;
|
|
if (this->GeneratorTarget->IsFrameworkOnApple()) {
|
|
outpath = this->GeneratorTarget->GetDirectory(this->GetConfigName());
|
|
this->OSXBundleGenerator->CreateFramework(this->TargetNames.Output,
|
|
outpath, this->GetConfigName());
|
|
outpath += '/';
|
|
} else if (this->GeneratorTarget->IsCFBundleOnApple()) {
|
|
outpath = this->GeneratorTarget->GetDirectory(this->GetConfigName());
|
|
this->OSXBundleGenerator->CreateCFBundle(this->TargetNames.Output, outpath,
|
|
this->GetConfigName());
|
|
outpath += '/';
|
|
} else if (relink) {
|
|
outpath = cmStrCat(this->Makefile->GetCurrentBinaryDirectory(),
|
|
"/CMakeFiles/CMakeRelink.dir");
|
|
cmSystemTools::MakeDirectory(outpath);
|
|
outpath += '/';
|
|
if (!this->TargetNames.ImportLibrary.empty()) {
|
|
outpathImp = outpath;
|
|
}
|
|
} else {
|
|
outpath = this->GeneratorTarget->GetDirectory(this->GetConfigName());
|
|
cmSystemTools::MakeDirectory(outpath);
|
|
outpath += '/';
|
|
if (!this->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 + this->TargetNames.Output;
|
|
std::string targetFullPathPDB = pdbOutputPath + this->TargetNames.PDB;
|
|
std::string targetFullPathSO = outpath + this->TargetNames.SharedObject;
|
|
std::string targetFullPathReal = outpath + this->TargetNames.Real;
|
|
std::string targetFullPathImport =
|
|
outpathImp + this->TargetNames.ImportLibrary;
|
|
|
|
// Construct the output path version of the names for use in command
|
|
// arguments.
|
|
std::string targetOutPathPDB = this->LocalGenerator->ConvertToOutputFormat(
|
|
targetFullPathPDB, cmOutputConverter::SHELL);
|
|
|
|
std::string targetOutPath = this->LocalGenerator->ConvertToOutputFormat(
|
|
this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPath),
|
|
cmOutputConverter::SHELL);
|
|
std::string targetOutPathSO = this->LocalGenerator->ConvertToOutputFormat(
|
|
this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPathSO),
|
|
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);
|
|
|
|
this->NumberOfProgressActions++;
|
|
if (!this->NoRuleMessages) {
|
|
cmLocalUnixMakefileGenerator3::EchoProgress progress;
|
|
this->MakeEchoProgress(progress);
|
|
// Add the link message.
|
|
std::string buildEcho = cmStrCat("Linking ", linkLanguage);
|
|
switch (this->GeneratorTarget->GetType()) {
|
|
case cmStateEnums::STATIC_LIBRARY:
|
|
buildEcho += " static library ";
|
|
break;
|
|
case cmStateEnums::SHARED_LIBRARY:
|
|
buildEcho += " shared library ";
|
|
break;
|
|
case cmStateEnums::MODULE_LIBRARY:
|
|
if (this->GeneratorTarget->IsCFBundleOnApple()) {
|
|
buildEcho += " CFBundle";
|
|
}
|
|
buildEcho += " shared module ";
|
|
break;
|
|
default:
|
|
buildEcho += " library ";
|
|
break;
|
|
}
|
|
buildEcho += targetOutPath;
|
|
this->LocalGenerator->AppendEcho(
|
|
commands, buildEcho, cmLocalUnixMakefileGenerator3::EchoLink, &progress);
|
|
}
|
|
|
|
// Clean files associated with this library.
|
|
std::set<std::string> libCleanFiles;
|
|
libCleanFiles.insert(
|
|
this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPathReal));
|
|
|
|
std::vector<std::string> commands1;
|
|
// Add a command to remove any existing files for this library.
|
|
// for static libs only
|
|
if (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY) {
|
|
this->LocalGenerator->AppendCleanCommand(commands1, libCleanFiles,
|
|
this->GeneratorTarget, "target");
|
|
this->LocalGenerator->CreateCDCommand(
|
|
commands1, this->Makefile->GetCurrentBinaryDirectory(),
|
|
this->LocalGenerator->GetBinaryDirectory());
|
|
cm::append(commands, commands1);
|
|
commands1.clear();
|
|
}
|
|
|
|
if (this->TargetNames.Output != this->TargetNames.Real) {
|
|
libCleanFiles.insert(
|
|
this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPath));
|
|
}
|
|
if (this->TargetNames.SharedObject != this->TargetNames.Real &&
|
|
this->TargetNames.SharedObject != this->TargetNames.Output) {
|
|
libCleanFiles.insert(
|
|
this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPathSO));
|
|
}
|
|
if (!this->TargetNames.ImportLibrary.empty()) {
|
|
libCleanFiles.insert(
|
|
this->LocalGenerator->MaybeRelativeToCurBinDir(targetFullPathImport));
|
|
std::string implib;
|
|
if (this->GeneratorTarget->GetImplibGNUtoMS(
|
|
this->GetConfigName(), targetFullPathImport, implib)) {
|
|
libCleanFiles.insert(
|
|
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));
|
|
|
|
#ifdef _WIN32
|
|
// There may be a manifest file for this target. Add it to the
|
|
// clean set just in case.
|
|
if (this->GeneratorTarget->GetType() != cmStateEnums::STATIC_LIBRARY) {
|
|
libCleanFiles.insert(this->LocalGenerator->MaybeRelativeToCurBinDir(
|
|
targetFullPath + ".manifest"));
|
|
}
|
|
#endif
|
|
|
|
// 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();
|
|
|
|
bool useResponseFileForObjects =
|
|
this->CheckUseResponseFileForObjects(linkLanguage);
|
|
bool const useResponseFileForLibs =
|
|
this->CheckUseResponseFileForLibraries(linkLanguage);
|
|
|
|
// For static libraries there might be archiving rules.
|
|
bool haveStaticLibraryRule = false;
|
|
std::vector<std::string> archiveCreateCommands;
|
|
std::vector<std::string> archiveAppendCommands;
|
|
std::vector<std::string> archiveFinishCommands;
|
|
std::string::size_type archiveCommandLimit = std::string::npos;
|
|
if (this->GeneratorTarget->GetType() == cmStateEnums::STATIC_LIBRARY) {
|
|
haveStaticLibraryRule = this->Makefile->IsDefinitionSet(linkRuleVar);
|
|
std::string arCreateVar =
|
|
cmStrCat("CMAKE_", linkLanguage, "_ARCHIVE_CREATE");
|
|
|
|
arCreateVar = this->GeneratorTarget->GetFeatureSpecificLinkRuleVariable(
|
|
arCreateVar, linkLanguage, this->GetConfigName());
|
|
|
|
this->Makefile->GetDefExpandList(arCreateVar, archiveCreateCommands);
|
|
std::string arAppendVar =
|
|
cmStrCat("CMAKE_", linkLanguage, "_ARCHIVE_APPEND");
|
|
|
|
arAppendVar = this->GeneratorTarget->GetFeatureSpecificLinkRuleVariable(
|
|
arAppendVar, linkLanguage, this->GetConfigName());
|
|
|
|
this->Makefile->GetDefExpandList(arAppendVar, archiveAppendCommands);
|
|
std::string arFinishVar =
|
|
cmStrCat("CMAKE_", linkLanguage, "_ARCHIVE_FINISH");
|
|
|
|
arFinishVar = this->GeneratorTarget->GetFeatureSpecificLinkRuleVariable(
|
|
arFinishVar, linkLanguage, this->GetConfigName());
|
|
|
|
this->Makefile->GetDefExpandList(arFinishVar, archiveFinishCommands);
|
|
}
|
|
|
|
// Decide whether to use archiving rules.
|
|
bool useArchiveRules = !haveStaticLibraryRule &&
|
|
!archiveCreateCommands.empty() && !archiveAppendCommands.empty();
|
|
if (useArchiveRules) {
|
|
// Archiving rules are always run with a link script.
|
|
useLinkScript = true;
|
|
|
|
// Archiving rules never use a response file.
|
|
useResponseFileForObjects = false;
|
|
|
|
// Limit the length of individual object lists to less than half of
|
|
// the command line length limit (leaving half for other flags).
|
|
// This may result in several calls to the archiver.
|
|
if (size_t limit = cmSystemTools::CalculateCommandLineLengthLimit()) {
|
|
archiveCommandLimit = limit / 2;
|
|
} else {
|
|
archiveCommandLimit = 8000;
|
|
}
|
|
}
|
|
|
|
// Expand the rule variables.
|
|
std::vector<std::string> real_link_commands;
|
|
{
|
|
bool useWatcomQuote =
|
|
this->Makefile->IsOn(linkRuleVar + "_USE_WATCOM_QUOTE");
|
|
|
|
// Set path conversion for link script shells.
|
|
this->LocalGenerator->SetLinkScriptShell(useLinkScript);
|
|
|
|
// Collect up flags to link in needed libraries.
|
|
std::string linkLibs;
|
|
if (this->GeneratorTarget->GetType() != cmStateEnums::STATIC_LIBRARY) {
|
|
|
|
std::unique_ptr<cmLinkLineComputer> linkLineComputer =
|
|
this->CreateLinkLineComputer(
|
|
this->LocalGenerator,
|
|
this->LocalGenerator->GetStateSnapshot().GetDirectory());
|
|
linkLineComputer->SetForResponse(useResponseFileForLibs);
|
|
linkLineComputer->SetUseWatcomQuote(useWatcomQuote);
|
|
linkLineComputer->SetRelink(relink);
|
|
|
|
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, useArchiveRules,
|
|
useResponseFileForObjects, buildObjs, depends,
|
|
useWatcomQuote);
|
|
if (!this->DeviceLinkObject.empty()) {
|
|
buildObjs += " " +
|
|
this->LocalGenerator->ConvertToOutputFormat(
|
|
this->LocalGenerator->MaybeRelativeToCurBinDir(
|
|
this->DeviceLinkObject),
|
|
cmOutputConverter::SHELL);
|
|
}
|
|
|
|
std::string const& aixExports = this->GetAIXExports(this->GetConfigName());
|
|
|
|
// maybe create .def file from list of objects
|
|
this->GenDefFile(real_link_commands);
|
|
|
|
std::string manifests = this->GetManifests(this->GetConfigName());
|
|
|
|
cmRulePlaceholderExpander::RuleVariables vars;
|
|
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.CMTargetName = this->GeneratorTarget->GetName().c_str();
|
|
vars.CMTargetType =
|
|
cmState::GetTargetTypeName(this->GeneratorTarget->GetType()).c_str();
|
|
vars.Language = linkLanguage.c_str();
|
|
vars.AIXExports = aixExports.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.LinkLibraries = linkLibs.c_str();
|
|
vars.ObjectsQuoted = buildObjs.c_str();
|
|
std::string targetOutSOName;
|
|
if (this->GeneratorTarget->HasSOName(this->GetConfigName())) {
|
|
vars.SONameFlag = this->Makefile->GetSONameFlag(linkLanguage);
|
|
targetOutSOName = this->LocalGenerator->ConvertToOutputFormat(
|
|
this->TargetNames.SharedObject.c_str(), cmOutputConverter::SHELL);
|
|
vars.TargetSOName = targetOutSOName.c_str();
|
|
}
|
|
vars.LinkFlags = linkFlags.c_str();
|
|
|
|
vars.Manifests = manifests.c_str();
|
|
|
|
// Compute the directory portion of the install_name setting.
|
|
std::string install_name_dir;
|
|
if (this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY) {
|
|
// Get the install_name directory for the build tree.
|
|
install_name_dir = this->GeneratorTarget->GetInstallNameDirForBuildTree(
|
|
this->GetConfigName());
|
|
|
|
// Set the rule variable replacement value.
|
|
if (install_name_dir.empty()) {
|
|
vars.TargetInstallNameDir = "";
|
|
} else {
|
|
// Convert to a path for the native build tool.
|
|
install_name_dir = this->LocalGenerator->ConvertToOutputFormat(
|
|
install_name_dir, cmOutputConverter::SHELL);
|
|
vars.TargetInstallNameDir = install_name_dir.c_str();
|
|
}
|
|
}
|
|
|
|
// Add language-specific flags.
|
|
std::string langFlags;
|
|
this->LocalGenerator->AddLanguageFlagsForLinking(
|
|
langFlags, this->GeneratorTarget, linkLanguage, this->GetConfigName());
|
|
|
|
this->LocalGenerator->AddArchitectureFlags(
|
|
langFlags, this->GeneratorTarget, linkLanguage, this->GetConfigName());
|
|
|
|
vars.LanguageCompileFlags = langFlags.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());
|
|
// Construct the main link rule and expand placeholders.
|
|
rulePlaceholderExpander->SetTargetImpLib(targetOutPathImport);
|
|
if (useArchiveRules) {
|
|
// Construct the individual object list strings.
|
|
std::vector<std::string> object_strings;
|
|
this->WriteObjectsStrings(object_strings, archiveCommandLimit);
|
|
|
|
// Add the cuda device object to the list of archive files. This will
|
|
// only occur on archives which have CUDA_RESOLVE_DEVICE_SYMBOLS enabled
|
|
if (!this->DeviceLinkObject.empty()) {
|
|
object_strings.push_back(this->LocalGenerator->ConvertToOutputFormat(
|
|
this->LocalGenerator->MaybeRelativeToCurBinDir(
|
|
this->DeviceLinkObject),
|
|
cmOutputConverter::SHELL));
|
|
}
|
|
|
|
// Create the archive with the first set of objects.
|
|
auto osi = object_strings.begin();
|
|
{
|
|
vars.Objects = osi->c_str();
|
|
for (std::string const& acc : archiveCreateCommands) {
|
|
std::string cmd = launcher + acc;
|
|
rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator,
|
|
cmd, vars);
|
|
real_link_commands.push_back(std::move(cmd));
|
|
}
|
|
}
|
|
// Append to the archive with the other object sets.
|
|
for (++osi; osi != object_strings.end(); ++osi) {
|
|
vars.Objects = osi->c_str();
|
|
for (std::string const& aac : archiveAppendCommands) {
|
|
std::string cmd = launcher + aac;
|
|
rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator,
|
|
cmd, vars);
|
|
real_link_commands.push_back(std::move(cmd));
|
|
}
|
|
}
|
|
// Finish the archive.
|
|
vars.Objects = "";
|
|
for (std::string const& afc : archiveFinishCommands) {
|
|
std::string cmd = launcher + afc;
|
|
rulePlaceholderExpander->ExpandRuleVariables(this->LocalGenerator, cmd,
|
|
vars);
|
|
// If there is no ranlib the command will be ":". Skip it.
|
|
if (!cmd.empty() && cmd[0] != ':') {
|
|
real_link_commands.push_back(std::move(cmd));
|
|
}
|
|
}
|
|
} else {
|
|
// Get the set of commands.
|
|
std::string linkRule = this->GetLinkRule(linkRuleVar);
|
|
cmExpandList(linkRule, real_link_commands);
|
|
if (this->GeneratorTarget->GetPropertyAsBool("LINK_WHAT_YOU_USE") &&
|
|
(this->GeneratorTarget->GetType() == cmStateEnums::SHARED_LIBRARY)) {
|
|
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));
|
|
}
|
|
|
|
// Expand placeholders.
|
|
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.
|
|
// Frameworks are handled by cmOSXBundleGenerator.
|
|
if (targetOutPath != targetOutPathReal &&
|
|
!this->GeneratorTarget->IsFrameworkOnApple()) {
|
|
std::string symlink =
|
|
cmStrCat("$(CMAKE_COMMAND) -E cmake_symlink_library ", targetOutPathReal,
|
|
' ', targetOutPathSO, ' ', 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());
|
|
}
|
|
|
|
// Compute the list of outputs.
|
|
std::vector<std::string> outputs(1, targetFullPathReal);
|
|
if (this->TargetNames.SharedObject != this->TargetNames.Real) {
|
|
outputs.push_back(targetFullPathSO);
|
|
}
|
|
if (this->TargetNames.Output != this->TargetNames.SharedObject &&
|
|
this->TargetNames.Output != this->TargetNames.Real) {
|
|
outputs.push_back(targetFullPath);
|
|
}
|
|
|
|
// Write the build rule.
|
|
this->WriteMakeRule(*this->BuildFileStream, nullptr, outputs, depends,
|
|
commands, false);
|
|
|
|
// Write the main driver rule to build everything in this target.
|
|
this->WriteTargetDriverRule(targetFullPath, relink);
|
|
|
|
// Clean all the possible library names and symlinks.
|
|
this->CleanFiles.insert(libCleanFiles.begin(), libCleanFiles.end());
|
|
}
|