43f1b8e6db41a012855a9d7282f472a98584fd28
[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   std::string config = cli.GetConfig();
61   return std::any_of(
62     items.begin(), items.end(),
63     [](cmComputeLinkInformation::Item const& item) -> bool {
64       return item.Target &&
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");
69     });
70 }
71
72 void cmLinkLineDeviceComputer::ComputeLinkLibraries(
73   cmComputeLinkInformation& cli, std::string const& stdLibString,
74   std::vector<BT<std::string>>& linkLibraries)
75 {
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;
85   // Note:
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;
91       continue;
92     }
93
94     if (item.Target) {
95       bool skip = false;
96       switch (item.Target->GetType()) {
97         case cmStateEnums::SHARED_LIBRARY:
98         case cmStateEnums::MODULE_LIBRARY:
99         case cmStateEnums::INTERFACE_LIBRARY:
100           skip = true;
101           break;
102         case cmStateEnums::STATIC_LIBRARY:
103           skip = item.Target->GetPropertyAsBool("CUDA_RESOLVE_DEVICE_SYMBOLS");
104           break;
105         default:
106           break;
107       }
108       if (skip) {
109         continue;
110       }
111     }
112
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")) {
121         linkLib.Value = item
122                           .GetFormattedItem(this->ConvertToOutputFormat(
123                             this->ConvertToLinkReference(item.Value.Value)))
124                           .Value;
125       }
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;
130       continue;
131     } else if (cmLinkItemValidForDevice(item.Value.Value)) {
132       linkLib.Value = item.Value.Value;
133     }
134
135     if (emitted.insert(linkLib.Value).second) {
136       linkLib.Value += " ";
137
138       const cmLinkImplementation* linkImpl =
139         cli.GetTarget()->GetLinkImplementation(
140           cli.GetConfig(), cmGeneratorTarget::LinkInterfaceFor::Link);
141
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;
148             break;
149           }
150         }
151       }
152
153       linkLibraries.emplace_back(linkLib);
154     }
155   }
156
157   if (!stdLibString.empty()) {
158     linkLibraries.emplace_back(cmStrCat(stdLibString, ' '));
159   }
160 }
161
162 std::string cmLinkLineDeviceComputer::GetLinkerLanguage(cmGeneratorTarget*,
163                                                         std::string const&)
164 {
165   return "CUDA";
166 }
167
168 bool requireDeviceLinking(cmGeneratorTarget& target, cmLocalGenerator& lg,
169                           const std::string& config)
170 {
171   if (!target.GetGlobalGenerator()->GetLanguageEnabled("CUDA")) {
172     return false;
173   }
174
175   if (target.GetType() == cmStateEnums::OBJECT_LIBRARY) {
176     return false;
177   }
178
179   if (!lg.GetMakefile()->IsOn("CMAKE_CUDA_COMPILER_HAS_DEVICE_LINK_PHASE")) {
180     return false;
181   }
182
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);
188   }
189
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);
194
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;
203           break;
204         default:
205           break;
206       }
207       return doDeviceLinking;
208     }
209
210     cmComputeLinkInformation* pcli = target.GetLinkInformation(config);
211     if (pcli) {
212       cmLinkLineDeviceComputer deviceLinkComputer(
213         &lg, lg.GetStateSnapshot().GetDirectory());
214       return deviceLinkComputer.ComputeRequiresDeviceLinking(*pcli);
215     }
216     return true;
217   }
218   return false;
219 }