/* 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 #include #include #include #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>& 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 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 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; }