You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
240 lines
7.9 KiB
240 lines
7.9 KiB
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
|
|
|
#include "cmLinkLineDeviceComputer.h"
|
|
|
|
#include <algorithm>
|
|
#include <set>
|
|
#include <utility>
|
|
|
|
#include <cmext/algorithm>
|
|
|
|
#include "cmComputeLinkInformation.h"
|
|
#include "cmGeneratorTarget.h"
|
|
#include "cmGlobalGenerator.h"
|
|
#include "cmLinkItem.h"
|
|
#include "cmListFileCache.h"
|
|
#include "cmLocalGenerator.h"
|
|
#include "cmMakefile.h"
|
|
#include "cmStateDirectory.h"
|
|
#include "cmStateSnapshot.h"
|
|
#include "cmStateTypes.h"
|
|
#include "cmStringAlgorithms.h"
|
|
#include "cmValue.h"
|
|
|
|
class cmOutputConverter;
|
|
|
|
cmLinkLineDeviceComputer::cmLinkLineDeviceComputer(
|
|
cmOutputConverter* outputConverter, cmStateDirectory const& stateDir)
|
|
: cmLinkLineComputer(outputConverter, stateDir)
|
|
{
|
|
}
|
|
|
|
cmLinkLineDeviceComputer::~cmLinkLineDeviceComputer() = default;
|
|
|
|
static bool cmLinkItemValidForDevice(std::string const& item)
|
|
{
|
|
// Valid items are:
|
|
// * Non-flags (does not start in '-')
|
|
// * Specific flags --library, --library-path, -l, -L
|
|
// For example:
|
|
// * 'cublas_device' => pass-along
|
|
// * '--library pthread' => pass-along
|
|
// * '-lpthread' => pass-along
|
|
// * '-pthread' => drop
|
|
// * '-a' => drop
|
|
// * '-framework Name' (as one string) => drop
|
|
return (!cmHasLiteralPrefix(item, "-") || //
|
|
cmHasLiteralPrefix(item, "-l") || //
|
|
cmHasLiteralPrefix(item, "-L") || //
|
|
cmHasLiteralPrefix(item, "--library"));
|
|
}
|
|
|
|
bool cmLinkLineDeviceComputer::ComputeRequiresDeviceLinking(
|
|
cmComputeLinkInformation& cli)
|
|
{
|
|
// Determine if this item might requires device linking.
|
|
// For this we only consider targets
|
|
using ItemVector = cmComputeLinkInformation::ItemVector;
|
|
ItemVector const& items = cli.GetItems();
|
|
return std::any_of(
|
|
items.begin(), items.end(),
|
|
[](cmComputeLinkInformation::Item const& item) -> bool {
|
|
return item.Target &&
|
|
item.Target->GetType() == cmStateEnums::STATIC_LIBRARY &&
|
|
// this dependency requires us to device link it
|
|
!item.Target->GetPropertyAsBool("CUDA_RESOLVE_DEVICE_SYMBOLS") &&
|
|
item.Target->GetPropertyAsBool("CUDA_SEPARABLE_COMPILATION");
|
|
});
|
|
}
|
|
|
|
bool cmLinkLineDeviceComputer::ComputeRequiresDeviceLinkingIPOFlag(
|
|
cmComputeLinkInformation& cli)
|
|
{
|
|
// Determine if this item might requires device linking.
|
|
// For this we only consider targets
|
|
using ItemVector = cmComputeLinkInformation::ItemVector;
|
|
ItemVector const& items = cli.GetItems();
|
|
std::string config = cli.GetConfig();
|
|
return std::any_of(
|
|
items.begin(), items.end(),
|
|
[config](cmComputeLinkInformation::Item const& item) -> bool {
|
|
return item.Target &&
|
|
item.Target->GetType() == cmStateEnums::STATIC_LIBRARY &&
|
|
// this dependency requires us to device link it
|
|
!item.Target->GetPropertyAsBool("CUDA_RESOLVE_DEVICE_SYMBOLS") &&
|
|
item.Target->GetPropertyAsBool("CUDA_SEPARABLE_COMPILATION") &&
|
|
item.Target->IsIPOEnabled("CUDA", config);
|
|
});
|
|
}
|
|
|
|
void cmLinkLineDeviceComputer::ComputeLinkLibraries(
|
|
cmComputeLinkInformation& cli, std::string const& stdLibString,
|
|
std::vector<BT<std::string>>& linkLibraries)
|
|
{
|
|
// Generate the unique set of link items when device linking.
|
|
// The nvcc device linker is designed so that each static library
|
|
// with device symbols only needs to be listed once as it doesn't
|
|
// care about link order.
|
|
std::set<std::string> emitted;
|
|
using ItemVector = cmComputeLinkInformation::ItemVector;
|
|
ItemVector const& items = cli.GetItems();
|
|
std::string config = cli.GetConfig();
|
|
bool skipItemAfterFramework = false;
|
|
|
|
for (auto const& item : items) {
|
|
if (skipItemAfterFramework) {
|
|
skipItemAfterFramework = false;
|
|
continue;
|
|
}
|
|
|
|
if (item.Target) {
|
|
bool skip = false;
|
|
switch (item.Target->GetType()) {
|
|
case cmStateEnums::SHARED_LIBRARY:
|
|
case cmStateEnums::MODULE_LIBRARY:
|
|
case cmStateEnums::OBJECT_LIBRARY:
|
|
case cmStateEnums::INTERFACE_LIBRARY:
|
|
skip = true;
|
|
break;
|
|
case cmStateEnums::STATIC_LIBRARY:
|
|
skip = item.Target->GetPropertyAsBool("CUDA_RESOLVE_DEVICE_SYMBOLS");
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (skip) {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
BT<std::string> linkLib;
|
|
if (item.IsPath == cmComputeLinkInformation::ItemIsPath::Yes) {
|
|
// nvcc understands absolute paths to libraries ending in '.o', .a', or
|
|
// '.lib'. These should be passed to nvlink. Other extensions need to be
|
|
// left out because nvlink may not understand or need them. Even though
|
|
// it can tolerate '.so' or '.dylib' it cannot tolerate '.so.1'.
|
|
if (cmHasLiteralSuffix(item.Value.Value, ".o") ||
|
|
cmHasLiteralSuffix(item.Value.Value, ".obj") ||
|
|
cmHasLiteralSuffix(item.Value.Value, ".a") ||
|
|
cmHasLiteralSuffix(item.Value.Value, ".lib")) {
|
|
linkLib.Value = item
|
|
.GetFormattedItem(this->ConvertToOutputFormat(
|
|
this->ConvertToLinkReference(item.Value.Value)))
|
|
.Value;
|
|
}
|
|
} else if (item.Value == "-framework") {
|
|
// This is the first part of '-framework Name' where the framework
|
|
// name is specified as a following item. Ignore both.
|
|
skipItemAfterFramework = true;
|
|
continue;
|
|
} else if (cmLinkItemValidForDevice(item.Value.Value)) {
|
|
linkLib.Value = item.Value.Value;
|
|
}
|
|
|
|
if (emitted.insert(linkLib.Value).second) {
|
|
linkLib.Value += " ";
|
|
|
|
const cmLinkImplementation* linkImpl =
|
|
cli.GetTarget()->GetLinkImplementation(cli.GetConfig(),
|
|
cmGeneratorTarget::UseTo::Link);
|
|
|
|
for (const cmLinkImplItem& iter : linkImpl->Libraries) {
|
|
if (iter.Target &&
|
|
iter.Target->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
|
|
std::string libPath = iter.Target->GetLocation(cli.GetConfig());
|
|
if (item.Value == libPath) {
|
|
linkLib.Backtrace = iter.Backtrace;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
linkLibraries.emplace_back(linkLib);
|
|
}
|
|
}
|
|
|
|
if (!stdLibString.empty()) {
|
|
linkLibraries.emplace_back(cmStrCat(stdLibString, ' '));
|
|
}
|
|
}
|
|
|
|
std::string cmLinkLineDeviceComputer::GetLinkerLanguage(cmGeneratorTarget*,
|
|
std::string const&)
|
|
{
|
|
return "CUDA";
|
|
}
|
|
|
|
bool requireDeviceLinking(cmGeneratorTarget& target, cmLocalGenerator& lg,
|
|
const std::string& config)
|
|
{
|
|
if (!target.GetGlobalGenerator()->GetLanguageEnabled("CUDA")) {
|
|
return false;
|
|
}
|
|
|
|
if (target.GetType() == cmStateEnums::OBJECT_LIBRARY) {
|
|
return false;
|
|
}
|
|
|
|
if (!lg.GetMakefile()->IsOn("CMAKE_CUDA_COMPILER_HAS_DEVICE_LINK_PHASE")) {
|
|
return false;
|
|
}
|
|
|
|
if (cmValue resolveDeviceSymbols =
|
|
target.GetProperty("CUDA_RESOLVE_DEVICE_SYMBOLS")) {
|
|
// If CUDA_RESOLVE_DEVICE_SYMBOLS has been explicitly set we need
|
|
// to honor the value no matter what it is.
|
|
return resolveDeviceSymbols.IsOn();
|
|
}
|
|
|
|
// Determine if we have any dependencies that require
|
|
// us to do a device link step
|
|
cmGeneratorTarget::LinkClosure const* closure =
|
|
target.GetLinkClosure(config);
|
|
|
|
if (cm::contains(closure->Languages, "CUDA")) {
|
|
if (target.GetProperty("CUDA_SEPARABLE_COMPILATION").IsOn()) {
|
|
bool doDeviceLinking = false;
|
|
switch (target.GetType()) {
|
|
case cmStateEnums::SHARED_LIBRARY:
|
|
case cmStateEnums::MODULE_LIBRARY:
|
|
case cmStateEnums::EXECUTABLE:
|
|
doDeviceLinking = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return doDeviceLinking;
|
|
}
|
|
|
|
cmComputeLinkInformation* pcli = target.GetLinkInformation(config);
|
|
if (pcli) {
|
|
cmLinkLineDeviceComputer deviceLinkComputer(
|
|
&lg, lg.GetStateSnapshot().GetDirectory());
|
|
return deviceLinkComputer.ComputeRequiresDeviceLinking(*pcli);
|
|
}
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|