Imported Upstream version 3.25.0
[platform/upstream/cmake.git] / Source / cmLinkLineDeviceComputer.cxx
1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing for details.  */
3
4 #include "cmLinkLineDeviceComputer.h"
5
6 #include <algorithm>
7 #include <set>
8 #include <utility>
9
10 #include <cmext/algorithm>
11
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"
23 #include "cmValue.h"
24
25 class cmOutputConverter;
26
27 cmLinkLineDeviceComputer::cmLinkLineDeviceComputer(
28   cmOutputConverter* outputConverter, cmStateDirectory const& stateDir)
29   : cmLinkLineComputer(outputConverter, stateDir)
30 {
31 }
32
33 cmLinkLineDeviceComputer::~cmLinkLineDeviceComputer() = default;
34
35 static bool cmLinkItemValidForDevice(std::string const& item)
36 {
37   // Valid items are:
38   // * Non-flags (does not start in '-')
39   // * Specific flags --library, --library-path, -l, -L
40   // For example:
41   // * 'cublas_device' => pass-along
42   // * '--library pthread' => pass-along
43   // * '-lpthread' => pass-along
44   // * '-pthread' => drop
45   // * '-a' => drop
46   // * '-framework Name' (as one string) => drop
47   return (!cmHasLiteralPrefix(item, "-") || //
48           cmHasLiteralPrefix(item, "-l") || //
49           cmHasLiteralPrefix(item, "-L") || //
50           cmHasLiteralPrefix(item, "--library"));
51 }
52
53 bool cmLinkLineDeviceComputer::ComputeRequiresDeviceLinking(
54   cmComputeLinkInformation& cli)
55 {
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   return std::any_of(
61     items.begin(), items.end(),
62     [](cmComputeLinkInformation::Item const& item) -> bool {
63       return item.Target &&
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");
68     });
69 }
70
71 bool cmLinkLineDeviceComputer::ComputeRequiresDeviceLinkingIPOFlag(
72   cmComputeLinkInformation& cli)
73 {
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();
79   return std::any_of(
80     items.begin(), items.end(),
81     [config](cmComputeLinkInformation::Item const& item) -> bool {
82       return item.Target &&
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);
88     });
89 }
90
91 void cmLinkLineDeviceComputer::ComputeLinkLibraries(
92   cmComputeLinkInformation& cli, std::string const& stdLibString,
93   std::vector<BT<std::string>>& linkLibraries)
94 {
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;
104   // Note:
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;
110       continue;
111     }
112
113     if (item.Target) {
114       bool skip = false;
115       switch (item.Target->GetType()) {
116         case cmStateEnums::SHARED_LIBRARY:
117         case cmStateEnums::MODULE_LIBRARY:
118         case cmStateEnums::INTERFACE_LIBRARY:
119           skip = true;
120           break;
121         case cmStateEnums::STATIC_LIBRARY:
122           skip = item.Target->GetPropertyAsBool("CUDA_RESOLVE_DEVICE_SYMBOLS");
123           break;
124         default:
125           break;
126       }
127       if (skip) {
128         continue;
129       }
130     }
131
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")) {
140         linkLib.Value = item
141                           .GetFormattedItem(this->ConvertToOutputFormat(
142                             this->ConvertToLinkReference(item.Value.Value)))
143                           .Value;
144       }
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;
149       continue;
150     } else if (cmLinkItemValidForDevice(item.Value.Value)) {
151       linkLib.Value = item.Value.Value;
152     }
153
154     if (emitted.insert(linkLib.Value).second) {
155       linkLib.Value += " ";
156
157       const cmLinkImplementation* linkImpl =
158         cli.GetTarget()->GetLinkImplementation(
159           cli.GetConfig(), cmGeneratorTarget::LinkInterfaceFor::Link);
160
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;
167             break;
168           }
169         }
170       }
171
172       linkLibraries.emplace_back(linkLib);
173     }
174   }
175
176   if (!stdLibString.empty()) {
177     linkLibraries.emplace_back(cmStrCat(stdLibString, ' '));
178   }
179 }
180
181 std::string cmLinkLineDeviceComputer::GetLinkerLanguage(cmGeneratorTarget*,
182                                                         std::string const&)
183 {
184   return "CUDA";
185 }
186
187 bool requireDeviceLinking(cmGeneratorTarget& target, cmLocalGenerator& lg,
188                           const std::string& config)
189 {
190   if (!target.GetGlobalGenerator()->GetLanguageEnabled("CUDA")) {
191     return false;
192   }
193
194   if (target.GetType() == cmStateEnums::OBJECT_LIBRARY) {
195     return false;
196   }
197
198   if (!lg.GetMakefile()->IsOn("CMAKE_CUDA_COMPILER_HAS_DEVICE_LINK_PHASE")) {
199     return false;
200   }
201
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);
207   }
208
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);
213
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;
222           break;
223         default:
224           break;
225       }
226       return doDeviceLinking;
227     }
228
229     cmComputeLinkInformation* pcli = target.GetLinkInformation(config);
230     if (pcli) {
231       cmLinkLineDeviceComputer deviceLinkComputer(
232         &lg, lg.GetStateSnapshot().GetDirectory());
233       return deviceLinkComputer.ComputeRequiresDeviceLinking(*pcli);
234     }
235     return true;
236   }
237   return false;
238 }