847334b20d1c9c4f5de5b0665f63fd50ee4c5764
[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 <set>
7 #include <utility>
8
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"
21
22 class cmOutputConverter;
23
24 cmLinkLineDeviceComputer::cmLinkLineDeviceComputer(
25   cmOutputConverter* outputConverter, cmStateDirectory const& stateDir)
26   : cmLinkLineComputer(outputConverter, stateDir)
27 {
28 }
29
30 cmLinkLineDeviceComputer::~cmLinkLineDeviceComputer() = default;
31
32 static bool cmLinkItemValidForDevice(std::string const& item)
33 {
34   // Valid items are:
35   // * Non-flags (does not start in '-')
36   // * Specific flags --library, --library-path, -l, -L
37   // For example:
38   // * 'cublas_device' => pass-along
39   // * '--library pthread' => pass-along
40   // * '-lpthread' => pass-along
41   // * '-pthread' => drop
42   // * '-a' => drop
43   // * '-framework Name' (as one string) => drop
44   return (!cmHasLiteralPrefix(item, "-") || //
45           cmHasLiteralPrefix(item, "-l") || //
46           cmHasLiteralPrefix(item, "-L") || //
47           cmHasLiteralPrefix(item, "--library"));
48 }
49
50 bool cmLinkLineDeviceComputer::ComputeRequiresDeviceLinking(
51   cmComputeLinkInformation& cli)
52 {
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) {
59     if (item.Target &&
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
64         return true;
65       }
66     }
67   }
68   return false;
69 }
70
71 void cmLinkLineDeviceComputer::ComputeLinkLibraries(
72   cmComputeLinkInformation& cli, std::string const& stdLibString,
73   std::vector<BT<std::string>>& linkLibraries)
74 {
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;
84   // Note:
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;
90       continue;
91     }
92
93     if (item.Target) {
94       bool skip = false;
95       switch (item.Target->GetType()) {
96         case cmStateEnums::SHARED_LIBRARY:
97         case cmStateEnums::MODULE_LIBRARY:
98         case cmStateEnums::INTERFACE_LIBRARY:
99           skip = true;
100           break;
101         case cmStateEnums::STATIC_LIBRARY:
102           skip = item.Target->GetPropertyAsBool("CUDA_RESOLVE_DEVICE_SYMBOLS");
103           break;
104         default:
105           break;
106       }
107       if (skip) {
108         continue;
109       }
110     }
111
112     BT<std::string> linkLib;
113     if (item.IsPath) {
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));
122       }
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;
127       continue;
128     } else if (cmLinkItemValidForDevice(item.Value)) {
129       linkLib.Value += item.Value;
130     }
131
132     if (emitted.insert(linkLib.Value).second) {
133       linkLib.Value += " ";
134
135       const cmLinkImplementation* linkImpl =
136         cli.GetTarget()->GetLinkImplementation(cli.GetConfig());
137
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;
144             break;
145           }
146         }
147       }
148
149       linkLibraries.emplace_back(linkLib);
150     }
151   }
152
153   if (!stdLibString.empty()) {
154     linkLibraries.emplace_back(cmStrCat(stdLibString, ' '));
155   }
156 }
157
158 std::string cmLinkLineDeviceComputer::GetLinkerLanguage(cmGeneratorTarget*,
159                                                         std::string const&)
160 {
161   return "CUDA";
162 }
163
164 bool requireDeviceLinking(cmGeneratorTarget& target, cmLocalGenerator& lg,
165                           const std::string& config)
166 {
167   if (!target.GetGlobalGenerator()->GetLanguageEnabled("CUDA")) {
168     return false;
169   }
170
171   if (target.GetType() == cmStateEnums::OBJECT_LIBRARY) {
172     return false;
173   }
174
175   if (!lg.GetMakefile()->IsOn("CMAKE_CUDA_COMPILER_HAS_DEVICE_LINK_PHASE")) {
176     return false;
177   }
178
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);
184   }
185
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);
190
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;
201             break;
202           default:
203             break;
204         }
205         return doDeviceLinking;
206       }
207     }
208
209     cmComputeLinkInformation* pcli = target.GetLinkInformation(config);
210     if (pcli) {
211       cmLinkLineDeviceComputer deviceLinkComputer(
212         &lg, lg.GetStateSnapshot().GetDirectory());
213       return deviceLinkComputer.ComputeRequiresDeviceLinking(*pcli);
214     }
215     return true;
216   }
217   return false;
218 }