Coverage Report

Created: 2026-02-09 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmLinkLineDeviceComputer.cxx
Line
Count
Source
1
/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2
   file LICENSE.rst or https://cmake.org/licensing for details.  */
3
4
#include "cmLinkLineDeviceComputer.h"
5
6
#include <algorithm>
7
#include <set>
8
#include <utility>
9
#include <vector>
10
11
#include <cmext/algorithm>
12
13
#include "cmComputeLinkInformation.h"
14
#include "cmGeneratorTarget.h"
15
#include "cmGlobalGenerator.h"
16
#include "cmLinkItem.h"
17
#include "cmListFileCache.h"
18
#include "cmLocalGenerator.h"
19
#include "cmMakefile.h"
20
#include "cmStateDirectory.h"
21
#include "cmStateSnapshot.h"
22
#include "cmStateTypes.h"
23
#include "cmStringAlgorithms.h"
24
#include "cmValue.h"
25
26
class cmOutputConverter;
27
28
cmLinkLineDeviceComputer::cmLinkLineDeviceComputer(
29
  cmOutputConverter* outputConverter, cmStateDirectory const& stateDir)
30
0
  : cmLinkLineComputer(outputConverter, stateDir)
31
0
{
32
0
}
33
34
0
cmLinkLineDeviceComputer::~cmLinkLineDeviceComputer() = default;
35
36
static bool cmLinkItemValidForDevice(std::string const& item)
37
0
{
38
  // Valid items are:
39
  // * Non-flags (does not start in '-')
40
  // * Specific flags --library, --library-path, -l, -L
41
  // For example:
42
  // * 'cublas_device' => pass-along
43
  // * '--library pthread' => pass-along
44
  // * '-lpthread' => pass-along
45
  // * '-pthread' => drop
46
  // * '-a' => drop
47
  // * '-framework Name' (as one string) => drop
48
0
  return (!cmHasPrefix(item, '-') ||        //
49
0
          cmHasLiteralPrefix(item, "-l") || //
50
0
          cmHasLiteralPrefix(item, "-L") || //
51
0
          cmHasLiteralPrefix(item, "--library"));
52
0
}
53
54
bool cmLinkLineDeviceComputer::ComputeRequiresDeviceLinking(
55
  cmComputeLinkInformation& cli)
56
0
{
57
  // Determine if this item might requires device linking.
58
  // For this we only consider targets
59
0
  using ItemVector = cmComputeLinkInformation::ItemVector;
60
0
  ItemVector const& items = cli.GetItems();
61
0
  return std::any_of(
62
0
    items.begin(), items.end(),
63
0
    [](cmComputeLinkInformation::Item const& item) -> bool {
64
0
      return item.Target &&
65
0
        item.Target->GetType() == cmStateEnums::STATIC_LIBRARY &&
66
        // this dependency requires us to device link it
67
0
        !item.Target->GetPropertyAsBool("CUDA_RESOLVE_DEVICE_SYMBOLS") &&
68
0
        item.Target->GetPropertyAsBool("CUDA_SEPARABLE_COMPILATION");
69
0
    });
70
0
}
71
72
bool cmLinkLineDeviceComputer::ComputeRequiresDeviceLinkingIPOFlag(
73
  cmComputeLinkInformation& cli)
74
0
{
75
  // Determine if this item might requires device linking.
76
  // For this we only consider targets
77
0
  using ItemVector = cmComputeLinkInformation::ItemVector;
78
0
  ItemVector const& items = cli.GetItems();
79
0
  std::string config = cli.GetConfig();
80
0
  return std::any_of(
81
0
    items.begin(), items.end(),
82
0
    [config](cmComputeLinkInformation::Item const& item) -> bool {
83
0
      return item.Target &&
84
0
        item.Target->GetType() == cmStateEnums::STATIC_LIBRARY &&
85
        // this dependency requires us to device link it
86
0
        !item.Target->GetPropertyAsBool("CUDA_RESOLVE_DEVICE_SYMBOLS") &&
87
0
        item.Target->GetPropertyAsBool("CUDA_SEPARABLE_COMPILATION") &&
88
0
        item.Target->IsIPOEnabled("CUDA", config);
89
0
    });
90
0
}
91
92
void cmLinkLineDeviceComputer::ComputeLinkLibraries(
93
  cmComputeLinkInformation& cli, std::string const& stdLibString,
94
  std::vector<BT<std::string>>& linkLibraries)
95
0
{
96
  // Generate the unique set of link items when device linking.
97
  // The nvcc device linker is designed so that each static library
98
  // with device symbols only needs to be listed once as it doesn't
99
  // care about link order.
100
0
  std::set<std::string> emitted;
101
0
  using ItemVector = cmComputeLinkInformation::ItemVector;
102
0
  ItemVector const& items = cli.GetItems();
103
0
  std::string config = cli.GetConfig();
104
0
  bool skipItemAfterFramework = false;
105
106
0
  for (auto const& item : items) {
107
0
    if (skipItemAfterFramework) {
108
0
      skipItemAfterFramework = false;
109
0
      continue;
110
0
    }
111
112
0
    if (item.Target) {
113
0
      bool skip = false;
114
0
      switch (item.Target->GetType()) {
115
0
        case cmStateEnums::SHARED_LIBRARY:
116
0
        case cmStateEnums::MODULE_LIBRARY:
117
0
        case cmStateEnums::OBJECT_LIBRARY:
118
0
        case cmStateEnums::INTERFACE_LIBRARY:
119
0
          skip = true;
120
0
          break;
121
0
        case cmStateEnums::STATIC_LIBRARY:
122
0
          skip = item.Target->GetPropertyAsBool("CUDA_RESOLVE_DEVICE_SYMBOLS");
123
0
          break;
124
0
        default:
125
0
          break;
126
0
      }
127
0
      if (skip) {
128
0
        continue;
129
0
      }
130
0
    }
131
132
0
    BT<std::string> linkLib;
133
0
    if (item.IsPath == cmComputeLinkInformation::ItemIsPath::Yes) {
134
      // nvcc understands absolute paths to libraries ending in '.o', .a', or
135
      // '.lib'. These should be passed to nvlink.  Other extensions need to be
136
      // left out because nvlink may not understand or need them.  Even though
137
      // it can tolerate '.so' or '.dylib' it cannot tolerate '.so.1'.
138
0
      if (cmHasLiteralSuffix(item.Value.Value, ".o") ||
139
0
          cmHasLiteralSuffix(item.Value.Value, ".obj") ||
140
0
          cmHasLiteralSuffix(item.Value.Value, ".a") ||
141
0
          cmHasLiteralSuffix(item.Value.Value, ".lib")) {
142
0
        linkLib.Value = item
143
0
                          .GetFormattedItem(this->ConvertToOutputFormat(
144
0
                            this->ConvertToLinkReference(item.Value.Value)))
145
0
                          .Value;
146
0
      }
147
0
    } else if (item.Value == "-framework") {
148
      // This is the first part of '-framework Name' where the framework
149
      // name is specified as a following item.  Ignore both.
150
0
      skipItemAfterFramework = true;
151
0
      continue;
152
0
    } else if (cmLinkItemValidForDevice(item.Value.Value)) {
153
0
      linkLib.Value = item.Value.Value;
154
0
    }
155
156
0
    if (emitted.insert(linkLib.Value).second) {
157
0
      linkLib.Value += " ";
158
159
0
      cmLinkImplementation const* linkImpl =
160
0
        cli.GetTarget()->GetLinkImplementation(cli.GetConfig(),
161
0
                                               cmGeneratorTarget::UseTo::Link);
162
163
0
      for (cmLinkItem const& iter : linkImpl->Libraries) {
164
0
        if (iter.Target &&
165
0
            iter.Target->GetType() != cmStateEnums::INTERFACE_LIBRARY) {
166
0
          std::string libPath = iter.Target->GetLocation(cli.GetConfig());
167
0
          if (item.Value == libPath) {
168
0
            linkLib.Backtrace = iter.Backtrace;
169
0
            break;
170
0
          }
171
0
        }
172
0
      }
173
174
0
      linkLibraries.emplace_back(linkLib);
175
0
    }
176
0
  }
177
178
0
  if (!stdLibString.empty()) {
179
0
    linkLibraries.emplace_back(cmStrCat(stdLibString, ' '));
180
0
  }
181
0
}
182
183
std::string cmLinkLineDeviceComputer::GetLinkerLanguage(cmGeneratorTarget*,
184
                                                        std::string const&)
185
0
{
186
0
  return "CUDA";
187
0
}
188
189
bool requireDeviceLinking(cmGeneratorTarget& target, cmLocalGenerator& lg,
190
                          std::string const& config)
191
0
{
192
0
  if (!target.GetGlobalGenerator()->GetLanguageEnabled("CUDA")) {
193
0
    return false;
194
0
  }
195
196
0
  if (target.GetType() == cmStateEnums::OBJECT_LIBRARY) {
197
0
    return false;
198
0
  }
199
200
0
  if (!lg.GetMakefile()->IsOn("CMAKE_CUDA_COMPILER_HAS_DEVICE_LINK_PHASE")) {
201
0
    return false;
202
0
  }
203
204
0
  if (cmValue resolveDeviceSymbols =
205
0
        target.GetProperty("CUDA_RESOLVE_DEVICE_SYMBOLS")) {
206
    // If CUDA_RESOLVE_DEVICE_SYMBOLS has been explicitly set we need
207
    // to honor the value no matter what it is.
208
0
    return resolveDeviceSymbols.IsOn();
209
0
  }
210
211
  // Determine if we have any dependencies that require
212
  // us to do a device link step
213
0
  cmGeneratorTarget::LinkClosure const* closure =
214
0
    target.GetLinkClosure(config);
215
216
0
  if (cm::contains(closure->Languages, "CUDA")) {
217
0
    if (target.GetProperty("CUDA_SEPARABLE_COMPILATION").IsOn()) {
218
0
      bool doDeviceLinking = false;
219
0
      switch (target.GetType()) {
220
0
        case cmStateEnums::SHARED_LIBRARY:
221
0
        case cmStateEnums::MODULE_LIBRARY:
222
0
        case cmStateEnums::EXECUTABLE:
223
0
          doDeviceLinking = true;
224
0
          break;
225
0
        default:
226
0
          break;
227
0
      }
228
0
      return doDeviceLinking;
229
0
    }
230
231
0
    cmComputeLinkInformation* pcli = target.GetLinkInformation(config);
232
0
    if (pcli) {
233
0
      cmLinkLineDeviceComputer deviceLinkComputer(
234
0
        &lg, lg.GetStateSnapshot().GetDirectory());
235
0
      return deviceLinkComputer.ComputeRequiresDeviceLinking(*pcli);
236
0
    }
237
0
    return true;
238
0
  }
239
0
  return false;
240
0
}