1 /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
2 file Copyright.txt or https://cmake.org/licensing for details. */
4 #include "cmLinkLineDeviceComputer.h"
10 #include <cmext/algorithm>
12 #include "cmComputeLinkInformation.h"
13 #include "cmGeneratorTarget.h"
14 #include "cmGlobalGenerator.h"
15 #include "cmLinkItem.h"
16 #include "cmListFileCache.h"
17 #include "cmLocalGenerator.h"
18 #include "cmMakefile.h"
19 #include "cmStateDirectory.h"
20 #include "cmStateSnapshot.h"
21 #include "cmStateTypes.h"
22 #include "cmStringAlgorithms.h"
25 class cmOutputConverter;
27 cmLinkLineDeviceComputer::cmLinkLineDeviceComputer(
28 cmOutputConverter* outputConverter, cmStateDirectory const& stateDir)
29 : cmLinkLineComputer(outputConverter, stateDir)
33 cmLinkLineDeviceComputer::~cmLinkLineDeviceComputer() = default;
35 static bool cmLinkItemValidForDevice(std::string const& item)
38 // * Non-flags (does not start in '-')
39 // * Specific flags --library, --library-path, -l, -L
41 // * 'cublas_device' => pass-along
42 // * '--library pthread' => pass-along
43 // * '-lpthread' => pass-along
44 // * '-pthread' => drop
46 // * '-framework Name' (as one string) => drop
47 return (!cmHasLiteralPrefix(item, "-") || //
48 cmHasLiteralPrefix(item, "-l") || //
49 cmHasLiteralPrefix(item, "-L") || //
50 cmHasLiteralPrefix(item, "--library"));
53 bool cmLinkLineDeviceComputer::ComputeRequiresDeviceLinking(
54 cmComputeLinkInformation& cli)
56 // Determine if this item might requires device linking.
57 // For this we only consider targets
58 using ItemVector = cmComputeLinkInformation::ItemVector;
59 ItemVector const& items = cli.GetItems();
60 std::string config = cli.GetConfig();
62 items.begin(), items.end(),
63 [](cmComputeLinkInformation::Item const& item) -> bool {
65 item.Target->GetType() == cmStateEnums::STATIC_LIBRARY &&
66 // this dependency requires us to device link it
67 !item.Target->GetPropertyAsBool("CUDA_RESOLVE_DEVICE_SYMBOLS") &&
68 item.Target->GetPropertyAsBool("CUDA_SEPARABLE_COMPILATION");
72 void cmLinkLineDeviceComputer::ComputeLinkLibraries(
73 cmComputeLinkInformation& cli, std::string const& stdLibString,
74 std::vector<BT<std::string>>& linkLibraries)
76 // Generate the unique set of link items when device linking.
77 // The nvcc device linker is designed so that each static library
78 // with device symbols only needs to be listed once as it doesn't
79 // care about link order.
80 std::set<std::string> emitted;
81 using ItemVector = cmComputeLinkInformation::ItemVector;
82 ItemVector const& items = cli.GetItems();
83 std::string config = cli.GetConfig();
84 bool skipItemAfterFramework = false;
86 // Any modification of this algorithm should be reflected also in
87 // cmVisualStudio10TargetGenerator::ComputeCudaLinkOptions
88 for (auto const& item : items) {
89 if (skipItemAfterFramework) {
90 skipItemAfterFramework = false;
96 switch (item.Target->GetType()) {
97 case cmStateEnums::SHARED_LIBRARY:
98 case cmStateEnums::MODULE_LIBRARY:
99 case cmStateEnums::INTERFACE_LIBRARY:
102 case cmStateEnums::STATIC_LIBRARY:
103 skip = item.Target->GetPropertyAsBool("CUDA_RESOLVE_DEVICE_SYMBOLS");
113 BT<std::string> linkLib;
114 if (item.IsPath == cmComputeLinkInformation::ItemIsPath::Yes) {
115 // nvcc understands absolute paths to libraries ending in '.a' or '.lib'.
116 // These should be passed to nvlink. Other extensions need to be left
117 // out because nvlink may not understand or need them. Even though it
118 // can tolerate '.so' or '.dylib' it cannot tolerate '.so.1'.
119 if (cmHasLiteralSuffix(item.Value.Value, ".a") ||
120 cmHasLiteralSuffix(item.Value.Value, ".lib")) {
122 .GetFormattedItem(this->ConvertToOutputFormat(
123 this->ConvertToLinkReference(item.Value.Value)))
126 } else if (item.Value == "-framework") {
127 // This is the first part of '-framework Name' where the framework
128 // name is specified as a following item. Ignore both.
129 skipItemAfterFramework = true;
131 } else if (cmLinkItemValidForDevice(item.Value.Value)) {
132 linkLib.Value = item.Value.Value;
135 if (emitted.insert(linkLib.Value).second) {
136 linkLib.Value += " ";
138 const cmLinkImplementation* linkImpl =
139 cli.GetTarget()->GetLinkImplementation(
140 cli.GetConfig(), cmGeneratorTarget::LinkInterfaceFor::Link);
142 for (const cmLinkImplItem& iter : linkImpl->Libraries) {
143 if (iter.Target != nullptr &&
144 iter.Target->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
145 std::string libPath = iter.Target->GetLocation(cli.GetConfig());
146 if (item.Value == libPath) {
147 linkLib.Backtrace = iter.Backtrace;
153 linkLibraries.emplace_back(linkLib);
157 if (!stdLibString.empty()) {
158 linkLibraries.emplace_back(cmStrCat(stdLibString, ' '));
162 std::string cmLinkLineDeviceComputer::GetLinkerLanguage(cmGeneratorTarget*,
168 bool requireDeviceLinking(cmGeneratorTarget& target, cmLocalGenerator& lg,
169 const std::string& config)
171 if (!target.GetGlobalGenerator()->GetLanguageEnabled("CUDA")) {
175 if (target.GetType() == cmStateEnums::OBJECT_LIBRARY) {
179 if (!lg.GetMakefile()->IsOn("CMAKE_CUDA_COMPILER_HAS_DEVICE_LINK_PHASE")) {
183 if (cmValue resolveDeviceSymbols =
184 target.GetProperty("CUDA_RESOLVE_DEVICE_SYMBOLS")) {
185 // If CUDA_RESOLVE_DEVICE_SYMBOLS has been explicitly set we need
186 // to honor the value no matter what it is.
187 return cmIsOn(*resolveDeviceSymbols);
190 // Determine if we have any dependencies that require
191 // us to do a device link step
192 cmGeneratorTarget::LinkClosure const* closure =
193 target.GetLinkClosure(config);
195 if (cm::contains(closure->Languages, "CUDA")) {
196 if (cmIsOn(target.GetProperty("CUDA_SEPARABLE_COMPILATION"))) {
197 bool doDeviceLinking = false;
198 switch (target.GetType()) {
199 case cmStateEnums::SHARED_LIBRARY:
200 case cmStateEnums::MODULE_LIBRARY:
201 case cmStateEnums::EXECUTABLE:
202 doDeviceLinking = true;
207 return doDeviceLinking;
210 cmComputeLinkInformation* pcli = target.GetLinkInformation(config);
212 cmLinkLineDeviceComputer deviceLinkComputer(
213 &lg, lg.GetStateSnapshot().GetDirectory());
214 return deviceLinkComputer.ComputeRequiresDeviceLinking(*pcli);