| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying | |
| file LICENSE.rst or https://cmake.org/licensing for details. */ | |
| #include "cmLinkLineDeviceComputer.h" | |
| #include <algorithm> | |
| #include <set> | |
| #include <utility> | |
| #include <vector> | |
| #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 (!cmHasPrefix(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 += " "; | |
| cmLinkImplementation const* linkImpl = | |
| cli.GetTarget()->GetLinkImplementation(cli.GetConfig(), | |
| cmGeneratorTarget::UseTo::Link); | |
| for (cmLinkItem const& 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, | |
| std::string const& 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; | |
| } |