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"
9 #include "cmAlgorithms.h"
10 #include "cmComputeLinkInformation.h"
11 #include "cmGeneratorTarget.h"
12 #include "cmGlobalGenerator.h"
13 #include "cmLinkItem.h"
14 #include "cmListFileCache.h"
15 #include "cmLocalGenerator.h"
16 #include "cmMakefile.h"
17 #include "cmStateDirectory.h"
18 #include "cmStateSnapshot.h"
19 #include "cmStateTypes.h"
20 #include "cmStringAlgorithms.h"
22 class cmOutputConverter;
24 cmLinkLineDeviceComputer::cmLinkLineDeviceComputer(
25 cmOutputConverter* outputConverter, cmStateDirectory const& stateDir)
26 : cmLinkLineComputer(outputConverter, stateDir)
30 cmLinkLineDeviceComputer::~cmLinkLineDeviceComputer() = default;
32 static bool cmLinkItemValidForDevice(std::string const& item)
35 // * Non-flags (does not start in '-')
36 // * Specific flags --library, --library-path, -l, -L
38 // * 'cublas_device' => pass-along
39 // * '--library pthread' => pass-along
40 // * '-lpthread' => pass-along
41 // * '-pthread' => drop
43 // * '-framework Name' (as one string) => drop
44 return (!cmHasLiteralPrefix(item, "-") || //
45 cmHasLiteralPrefix(item, "-l") || //
46 cmHasLiteralPrefix(item, "-L") || //
47 cmHasLiteralPrefix(item, "--library"));
50 bool cmLinkLineDeviceComputer::ComputeRequiresDeviceLinking(
51 cmComputeLinkInformation& cli)
53 // Determine if this item might requires device linking.
54 // For this we only consider targets
55 using ItemVector = cmComputeLinkInformation::ItemVector;
56 ItemVector const& items = cli.GetItems();
57 std::string config = cli.GetConfig();
58 for (auto const& item : items) {
60 item.Target->GetType() == cmStateEnums::STATIC_LIBRARY) {
61 if ((!item.Target->GetPropertyAsBool("CUDA_RESOLVE_DEVICE_SYMBOLS")) &&
62 item.Target->GetPropertyAsBool("CUDA_SEPARABLE_COMPILATION")) {
63 // this dependency requires us to device link it
71 void cmLinkLineDeviceComputer::ComputeLinkLibraries(
72 cmComputeLinkInformation& cli, std::string const& stdLibString,
73 std::vector<BT<std::string>>& linkLibraries)
75 // Generate the unique set of link items when device linking.
76 // The nvcc device linker is designed so that each static library
77 // with device symbols only needs to be listed once as it doesn't
78 // care about link order.
79 std::set<std::string> emitted;
80 using ItemVector = cmComputeLinkInformation::ItemVector;
81 ItemVector const& items = cli.GetItems();
82 std::string config = cli.GetConfig();
83 bool skipItemAfterFramework = false;
85 // Any modification of this algorithm should be reflected also in
86 // cmVisualStudio10TargetGenerator::ComputeCudaLinkOptions
87 for (auto const& item : items) {
88 if (skipItemAfterFramework) {
89 skipItemAfterFramework = false;
95 switch (item.Target->GetType()) {
96 case cmStateEnums::SHARED_LIBRARY:
97 case cmStateEnums::MODULE_LIBRARY:
98 case cmStateEnums::INTERFACE_LIBRARY:
101 case cmStateEnums::STATIC_LIBRARY:
102 skip = item.Target->GetPropertyAsBool("CUDA_RESOLVE_DEVICE_SYMBOLS");
112 BT<std::string> linkLib;
114 // nvcc understands absolute paths to libraries ending in '.a' or '.lib'.
115 // These should be passed to nvlink. Other extensions need to be left
116 // out because nvlink may not understand or need them. Even though it
117 // can tolerate '.so' or '.dylib' it cannot tolerate '.so.1'.
118 if (cmHasLiteralSuffix(item.Value, ".a") ||
119 cmHasLiteralSuffix(item.Value, ".lib")) {
120 linkLib.Value += this->ConvertToOutputFormat(
121 this->ConvertToLinkReference(item.Value));
123 } else if (item.Value == "-framework") {
124 // This is the first part of '-framework Name' where the framework
125 // name is specified as a following item. Ignore both.
126 skipItemAfterFramework = true;
128 } else if (cmLinkItemValidForDevice(item.Value)) {
129 linkLib.Value += item.Value;
132 if (emitted.insert(linkLib.Value).second) {
133 linkLib.Value += " ";
135 const cmLinkImplementation* linkImpl =
136 cli.GetTarget()->GetLinkImplementation(cli.GetConfig());
138 for (const cmLinkImplItem& iter : linkImpl->Libraries) {
139 if (iter.Target != nullptr &&
140 iter.Target->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
141 std::string libPath = iter.Target->GetLocation(cli.GetConfig());
142 if (item.Value == libPath) {
143 linkLib.Backtrace = iter.Backtrace;
149 linkLibraries.emplace_back(linkLib);
153 if (!stdLibString.empty()) {
154 linkLibraries.emplace_back(cmStrCat(stdLibString, ' '));
158 std::string cmLinkLineDeviceComputer::GetLinkerLanguage(cmGeneratorTarget*,
164 bool requireDeviceLinking(cmGeneratorTarget& target, cmLocalGenerator& lg,
165 const std::string& config)
167 if (!target.GetGlobalGenerator()->GetLanguageEnabled("CUDA")) {
171 if (target.GetType() == cmStateEnums::OBJECT_LIBRARY) {
175 if (!lg.GetMakefile()->IsOn("CMAKE_CUDA_COMPILER_HAS_DEVICE_LINK_PHASE")) {
179 if (const char* resolveDeviceSymbols =
180 target.GetProperty("CUDA_RESOLVE_DEVICE_SYMBOLS")) {
181 // If CUDA_RESOLVE_DEVICE_SYMBOLS has been explicitly set we need
182 // to honor the value no matter what it is.
183 return cmIsOn(resolveDeviceSymbols);
186 // Determine if we have any dependencies that require
187 // us to do a device link step
188 cmGeneratorTarget::LinkClosure const* closure =
189 target.GetLinkClosure(config);
191 if (cmContains(closure->Languages, "CUDA")) {
192 if (const char* separableCompilation =
193 target.GetProperty("CUDA_SEPARABLE_COMPILATION")) {
194 if (cmIsOn(separableCompilation)) {
195 bool doDeviceLinking = false;
196 switch (target.GetType()) {
197 case cmStateEnums::SHARED_LIBRARY:
198 case cmStateEnums::MODULE_LIBRARY:
199 case cmStateEnums::EXECUTABLE:
200 doDeviceLinking = true;
205 return doDeviceLinking;
209 cmComputeLinkInformation* pcli = target.GetLinkInformation(config);
211 cmLinkLineDeviceComputer deviceLinkComputer(
212 &lg, lg.GetStateSnapshot().GetDirectory());
213 return deviceLinkComputer.ComputeRequiresDeviceLinking(*pcli);