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();
61 items.begin(), items.end(),
62 [](cmComputeLinkInformation::Item const& item) -> bool {
64 item.Target->GetType() == cmStateEnums::STATIC_LIBRARY &&
65 // this dependency requires us to device link it
66 !item.Target->GetPropertyAsBool("CUDA_RESOLVE_DEVICE_SYMBOLS") &&
67 item.Target->GetPropertyAsBool("CUDA_SEPARABLE_COMPILATION");
71 bool cmLinkLineDeviceComputer::ComputeRequiresDeviceLinkingIPOFlag(
72 cmComputeLinkInformation& cli)
74 // Determine if this item might requires device linking.
75 // For this we only consider targets
76 using ItemVector = cmComputeLinkInformation::ItemVector;
77 ItemVector const& items = cli.GetItems();
78 std::string config = cli.GetConfig();
80 items.begin(), items.end(),
81 [config](cmComputeLinkInformation::Item const& item) -> bool {
83 item.Target->GetType() == cmStateEnums::STATIC_LIBRARY &&
84 // this dependency requires us to device link it
85 !item.Target->GetPropertyAsBool("CUDA_RESOLVE_DEVICE_SYMBOLS") &&
86 item.Target->GetPropertyAsBool("CUDA_SEPARABLE_COMPILATION") &&
87 item.Target->IsIPOEnabled("CUDA", config);
91 void cmLinkLineDeviceComputer::ComputeLinkLibraries(
92 cmComputeLinkInformation& cli, std::string const& stdLibString,
93 std::vector<BT<std::string>>& linkLibraries)
95 // Generate the unique set of link items when device linking.
96 // The nvcc device linker is designed so that each static library
97 // with device symbols only needs to be listed once as it doesn't
98 // care about link order.
99 std::set<std::string> emitted;
100 using ItemVector = cmComputeLinkInformation::ItemVector;
101 ItemVector const& items = cli.GetItems();
102 std::string config = cli.GetConfig();
103 bool skipItemAfterFramework = false;
105 // Any modification of this algorithm should be reflected also in
106 // cmVisualStudio10TargetGenerator::ComputeCudaLinkOptions
107 for (auto const& item : items) {
108 if (skipItemAfterFramework) {
109 skipItemAfterFramework = false;
115 switch (item.Target->GetType()) {
116 case cmStateEnums::SHARED_LIBRARY:
117 case cmStateEnums::MODULE_LIBRARY:
118 case cmStateEnums::INTERFACE_LIBRARY:
121 case cmStateEnums::STATIC_LIBRARY:
122 skip = item.Target->GetPropertyAsBool("CUDA_RESOLVE_DEVICE_SYMBOLS");
132 BT<std::string> linkLib;
133 if (item.IsPath == cmComputeLinkInformation::ItemIsPath::Yes) {
134 // nvcc understands absolute paths to libraries ending in '.a' or '.lib'.
135 // These should be passed to nvlink. Other extensions need to be left
136 // out because nvlink may not understand or need them. Even though it
137 // can tolerate '.so' or '.dylib' it cannot tolerate '.so.1'.
138 if (cmHasLiteralSuffix(item.Value.Value, ".a") ||
139 cmHasLiteralSuffix(item.Value.Value, ".lib")) {
141 .GetFormattedItem(this->ConvertToOutputFormat(
142 this->ConvertToLinkReference(item.Value.Value)))
145 } else if (item.Value == "-framework") {
146 // This is the first part of '-framework Name' where the framework
147 // name is specified as a following item. Ignore both.
148 skipItemAfterFramework = true;
150 } else if (cmLinkItemValidForDevice(item.Value.Value)) {
151 linkLib.Value = item.Value.Value;
154 if (emitted.insert(linkLib.Value).second) {
155 linkLib.Value += " ";
157 const cmLinkImplementation* linkImpl =
158 cli.GetTarget()->GetLinkImplementation(
159 cli.GetConfig(), cmGeneratorTarget::LinkInterfaceFor::Link);
161 for (const cmLinkImplItem& iter : linkImpl->Libraries) {
162 if (iter.Target != nullptr &&
163 iter.Target->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
164 std::string libPath = iter.Target->GetLocation(cli.GetConfig());
165 if (item.Value == libPath) {
166 linkLib.Backtrace = iter.Backtrace;
172 linkLibraries.emplace_back(linkLib);
176 if (!stdLibString.empty()) {
177 linkLibraries.emplace_back(cmStrCat(stdLibString, ' '));
181 std::string cmLinkLineDeviceComputer::GetLinkerLanguage(cmGeneratorTarget*,
187 bool requireDeviceLinking(cmGeneratorTarget& target, cmLocalGenerator& lg,
188 const std::string& config)
190 if (!target.GetGlobalGenerator()->GetLanguageEnabled("CUDA")) {
194 if (target.GetType() == cmStateEnums::OBJECT_LIBRARY) {
198 if (!lg.GetMakefile()->IsOn("CMAKE_CUDA_COMPILER_HAS_DEVICE_LINK_PHASE")) {
202 if (cmValue resolveDeviceSymbols =
203 target.GetProperty("CUDA_RESOLVE_DEVICE_SYMBOLS")) {
204 // If CUDA_RESOLVE_DEVICE_SYMBOLS has been explicitly set we need
205 // to honor the value no matter what it is.
206 return cmIsOn(*resolveDeviceSymbols);
209 // Determine if we have any dependencies that require
210 // us to do a device link step
211 cmGeneratorTarget::LinkClosure const* closure =
212 target.GetLinkClosure(config);
214 if (cm::contains(closure->Languages, "CUDA")) {
215 if (cmIsOn(target.GetProperty("CUDA_SEPARABLE_COMPILATION"))) {
216 bool doDeviceLinking = false;
217 switch (target.GetType()) {
218 case cmStateEnums::SHARED_LIBRARY:
219 case cmStateEnums::MODULE_LIBRARY:
220 case cmStateEnums::EXECUTABLE:
221 doDeviceLinking = true;
226 return doDeviceLinking;
229 cmComputeLinkInformation* pcli = target.GetLinkInformation(config);
231 cmLinkLineDeviceComputer deviceLinkComputer(
232 &lg, lg.GetStateSnapshot().GetDirectory());
233 return deviceLinkComputer.ComputeRequiresDeviceLinking(*pcli);