Coverage Report

Created: 2026-02-09 06:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmAddLibraryCommand.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
#include "cmAddLibraryCommand.h"
4
5
#include "cmExecutionStatus.h"
6
#include "cmGeneratorExpression.h"
7
#include "cmGlobalGenerator.h"
8
#include "cmMakefile.h"
9
#include "cmMessageType.h"
10
#include "cmPolicies.h"
11
#include "cmState.h"
12
#include "cmStateTypes.h"
13
#include "cmStringAlgorithms.h"
14
#include "cmSystemTools.h"
15
#include "cmTarget.h"
16
#include "cmValue.h"
17
18
bool cmAddLibraryCommand(std::vector<std::string> const& args,
19
                         cmExecutionStatus& status)
20
0
{
21
0
  if (args.empty()) {
22
0
    status.SetError("called with incorrect number of arguments");
23
0
    return false;
24
0
  }
25
26
0
  cmMakefile& mf = status.GetMakefile();
27
  // Library type defaults to value of BUILD_SHARED_LIBS, if it exists,
28
  // otherwise it defaults to static library.
29
0
  cmStateEnums::TargetType type = cmStateEnums::SHARED_LIBRARY;
30
0
  if (mf.GetDefinition("BUILD_SHARED_LIBS").IsOff()) {
31
0
    type = cmStateEnums::STATIC_LIBRARY;
32
0
  }
33
0
  bool excludeFromAll = false;
34
0
  bool importTarget = false;
35
0
  bool importGlobal = false;
36
0
  bool symbolicTarget = false;
37
38
0
  auto s = args.begin();
39
40
0
  std::string const& libName = *s;
41
42
0
  ++s;
43
44
  // If the second argument is "SHARED" or "STATIC", then it controls
45
  // the type of library.  Otherwise, it is treated as a source or
46
  // source list name. There may be two keyword arguments, check for them
47
0
  bool haveSpecifiedType = false;
48
0
  bool isAlias = false;
49
0
  while (s != args.end()) {
50
0
    std::string libType = *s;
51
0
    if (libType == "STATIC") {
52
0
      if (type == cmStateEnums::INTERFACE_LIBRARY) {
53
0
        status.SetError(
54
0
          "INTERFACE library specified with conflicting STATIC type.");
55
0
        return false;
56
0
      }
57
0
      ++s;
58
0
      type = cmStateEnums::STATIC_LIBRARY;
59
0
      haveSpecifiedType = true;
60
0
    } else if (libType == "SHARED") {
61
0
      if (type == cmStateEnums::INTERFACE_LIBRARY) {
62
0
        status.SetError(
63
0
          "INTERFACE library specified with conflicting SHARED type.");
64
0
        return false;
65
0
      }
66
0
      ++s;
67
0
      type = cmStateEnums::SHARED_LIBRARY;
68
0
      haveSpecifiedType = true;
69
0
    } else if (libType == "MODULE") {
70
0
      if (type == cmStateEnums::INTERFACE_LIBRARY) {
71
0
        status.SetError(
72
0
          "INTERFACE library specified with conflicting MODULE type.");
73
0
        return false;
74
0
      }
75
0
      ++s;
76
0
      type = cmStateEnums::MODULE_LIBRARY;
77
0
      haveSpecifiedType = true;
78
0
    } else if (libType == "OBJECT") {
79
0
      if (type == cmStateEnums::INTERFACE_LIBRARY) {
80
0
        status.SetError(
81
0
          "INTERFACE library specified with conflicting OBJECT type.");
82
0
        return false;
83
0
      }
84
0
      ++s;
85
0
      type = cmStateEnums::OBJECT_LIBRARY;
86
0
      haveSpecifiedType = true;
87
0
    } else if (libType == "UNKNOWN") {
88
0
      if (type == cmStateEnums::INTERFACE_LIBRARY) {
89
0
        status.SetError(
90
0
          "INTERFACE library specified with conflicting UNKNOWN type.");
91
0
        return false;
92
0
      }
93
0
      ++s;
94
0
      type = cmStateEnums::UNKNOWN_LIBRARY;
95
0
      haveSpecifiedType = true;
96
0
    } else if (libType == "ALIAS") {
97
0
      if (type == cmStateEnums::INTERFACE_LIBRARY) {
98
0
        status.SetError(
99
0
          "INTERFACE library specified with conflicting ALIAS type.");
100
0
        return false;
101
0
      }
102
0
      ++s;
103
0
      isAlias = true;
104
0
    } else if (libType == "INTERFACE") {
105
0
      if (haveSpecifiedType) {
106
0
        status.SetError(
107
0
          "INTERFACE library specified with conflicting/multiple types.");
108
0
        return false;
109
0
      }
110
0
      if (isAlias) {
111
0
        status.SetError(
112
0
          "INTERFACE library specified with conflicting ALIAS type.");
113
0
        return false;
114
0
      }
115
0
      ++s;
116
0
      type = cmStateEnums::INTERFACE_LIBRARY;
117
0
      haveSpecifiedType = true;
118
0
    } else if (*s == "EXCLUDE_FROM_ALL") {
119
0
      ++s;
120
0
      excludeFromAll = true;
121
0
    } else if (*s == "SYMBOLIC") {
122
0
      ++s;
123
0
      symbolicTarget = true;
124
0
    } else if (*s == "IMPORTED") {
125
0
      ++s;
126
0
      importTarget = true;
127
0
    } else if (importTarget && *s == "GLOBAL") {
128
0
      ++s;
129
0
      importGlobal = true;
130
0
    } else if (type == cmStateEnums::INTERFACE_LIBRARY && *s == "GLOBAL") {
131
0
      status.SetError(
132
0
        "GLOBAL option may only be used with IMPORTED libraries.");
133
0
      return false;
134
0
    } else {
135
0
      break;
136
0
    }
137
0
  }
138
139
0
  if (importTarget && !importGlobal) {
140
0
    importGlobal = mf.IsImportedTargetGlobalScope();
141
0
  }
142
143
0
  if (type == cmStateEnums::INTERFACE_LIBRARY) {
144
0
    if (importGlobal && !importTarget) {
145
0
      status.SetError(
146
0
        "INTERFACE library specified as GLOBAL, but not as IMPORTED.");
147
0
      return false;
148
0
    }
149
0
  }
150
151
0
  bool nameOk = cmGeneratorExpression::IsValidTargetName(libName) &&
152
0
    !cmGlobalGenerator::IsReservedTarget(libName);
153
154
0
  if (nameOk && !importTarget && !isAlias) {
155
0
    nameOk = libName.find(':') == std::string::npos;
156
0
  }
157
0
  if (!nameOk) {
158
0
    mf.IssueInvalidTargetNameError(libName);
159
0
    return false;
160
0
  }
161
162
0
  if (isAlias) {
163
0
    if (!cmGeneratorExpression::IsValidTargetName(libName)) {
164
0
      status.SetError("Invalid name for ALIAS: " + libName);
165
0
      return false;
166
0
    }
167
0
    if (excludeFromAll) {
168
0
      status.SetError("EXCLUDE_FROM_ALL with ALIAS makes no sense.");
169
0
      return false;
170
0
    }
171
0
    if (importTarget || importGlobal) {
172
0
      status.SetError("IMPORTED with ALIAS is not allowed.");
173
0
      return false;
174
0
    }
175
0
    if (args.size() != 3) {
176
0
      status.SetError("ALIAS requires exactly one target argument.");
177
0
      return false;
178
0
    }
179
180
0
    if (mf.GetPolicyStatus(cmPolicies::CMP0107) == cmPolicies::NEW) {
181
      // Make sure the target does not already exist.
182
0
      if (mf.FindTargetToUse(libName)) {
183
0
        status.SetError(cmStrCat(
184
0
          "cannot create ALIAS target \"", libName,
185
0
          "\" because another target with the same name already exists."));
186
0
        return false;
187
0
      }
188
0
    }
189
190
0
    std::string const& aliasedName = *s;
191
0
    if (mf.IsAlias(aliasedName)) {
192
0
      status.SetError(cmStrCat("cannot create ALIAS target \"", libName,
193
0
                               "\" because target \"", aliasedName,
194
0
                               "\" is itself an ALIAS."));
195
0
      return false;
196
0
    }
197
0
    cmTarget* aliasedTarget =
198
0
      mf.FindTargetToUse(aliasedName, { cmStateEnums::TargetDomain::NATIVE });
199
0
    if (!aliasedTarget) {
200
0
      status.SetError(cmStrCat("cannot create ALIAS target \"", libName,
201
0
                               "\" because target \"", aliasedName,
202
0
                               "\" does not already exist."));
203
0
      return false;
204
0
    }
205
0
    cmStateEnums::TargetType aliasedType = aliasedTarget->GetType();
206
0
    if (aliasedType != cmStateEnums::SHARED_LIBRARY &&
207
0
        aliasedType != cmStateEnums::STATIC_LIBRARY &&
208
0
        aliasedType != cmStateEnums::MODULE_LIBRARY &&
209
0
        aliasedType != cmStateEnums::OBJECT_LIBRARY &&
210
0
        aliasedType != cmStateEnums::INTERFACE_LIBRARY &&
211
0
        !(aliasedType == cmStateEnums::UNKNOWN_LIBRARY &&
212
0
          aliasedTarget->IsImported())) {
213
0
      status.SetError(cmStrCat("cannot create ALIAS target \"", libName,
214
0
                               "\" because target \"", aliasedName,
215
0
                               "\" is not a library."));
216
0
      return false;
217
0
    }
218
0
    mf.AddAlias(libName, aliasedName,
219
0
                !aliasedTarget->IsImported() ||
220
0
                  aliasedTarget->IsImportedGloballyVisible());
221
0
    return true;
222
0
  }
223
224
0
  if (importTarget && excludeFromAll) {
225
0
    status.SetError("excludeFromAll with IMPORTED target makes no sense.");
226
0
    return false;
227
0
  }
228
229
  /* ideally we should check whether for the linker language of the target
230
     CMAKE_${LANG}_CREATE_SHARED_LIBRARY is defined and if not default to
231
     STATIC. But at this point we know only the name of the target, but not
232
     yet its linker language. */
233
0
  if ((type == cmStateEnums::SHARED_LIBRARY ||
234
0
       type == cmStateEnums::MODULE_LIBRARY) &&
235
0
      !mf.GetState()->GetGlobalPropertyAsBool("TARGET_SUPPORTS_SHARED_LIBS")) {
236
0
    switch (status.GetMakefile().GetPolicyStatus(cmPolicies::CMP0164)) {
237
0
      case cmPolicies::WARN:
238
0
        mf.IssueMessage(
239
0
          MessageType::AUTHOR_WARNING,
240
0
          cmStrCat(
241
0
            "ADD_LIBRARY called with ",
242
0
            (type == cmStateEnums::SHARED_LIBRARY ? "SHARED" : "MODULE"),
243
0
            " option but the target platform does not support dynamic "
244
0
            "linking. "
245
0
            "Building a STATIC library instead. This may lead to problems."));
246
0
        CM_FALLTHROUGH;
247
0
      case cmPolicies::OLD:
248
0
        type = cmStateEnums::STATIC_LIBRARY;
249
0
        break;
250
0
      case cmPolicies::NEW:
251
0
        mf.IssueMessage(
252
0
          MessageType::FATAL_ERROR,
253
0
          cmStrCat(
254
0
            "ADD_LIBRARY called with ",
255
0
            (type == cmStateEnums::SHARED_LIBRARY ? "SHARED" : "MODULE"),
256
0
            " option but the target platform does not support dynamic "
257
0
            "linking."));
258
0
        cmSystemTools::SetFatalErrorOccurred();
259
0
        return false;
260
0
      default:
261
0
        break;
262
0
    }
263
0
  }
264
265
  // Handle imported target creation.
266
0
  if (importTarget) {
267
    // The IMPORTED signature requires a type to be specified explicitly.
268
0
    if (!haveSpecifiedType) {
269
0
      status.SetError("called with IMPORTED argument but no library type.");
270
0
      return false;
271
0
    }
272
0
    if (type == cmStateEnums::INTERFACE_LIBRARY) {
273
0
      if (!cmGeneratorExpression::IsValidTargetName(libName)) {
274
0
        status.SetError(cmStrCat(
275
0
          "Invalid name for IMPORTED INTERFACE library target: ", libName));
276
0
        return false;
277
0
      }
278
0
    }
279
280
    // Make sure the target does not already exist.
281
0
    if (mf.FindTargetToUse(libName)) {
282
0
      status.SetError(cmStrCat(
283
0
        "cannot create imported target \"", libName,
284
0
        "\" because another target with the same name already exists."));
285
0
      return false;
286
0
    }
287
288
    // Create the imported target.
289
0
    cmTarget* target = mf.AddImportedTarget(libName, type, importGlobal);
290
0
    target->SetSymbolic(symbolicTarget);
291
0
    return true;
292
0
  }
293
294
  // A non-imported target may not have UNKNOWN type.
295
0
  if (type == cmStateEnums::UNKNOWN_LIBRARY) {
296
0
    mf.IssueMessage(
297
0
      MessageType::FATAL_ERROR,
298
0
      "The UNKNOWN library type may be used only for IMPORTED libraries.");
299
0
    return true;
300
0
  }
301
302
  // Enforce name uniqueness.
303
0
  {
304
0
    std::string msg;
305
0
    if (!mf.EnforceUniqueName(libName, msg)) {
306
0
      status.SetError(msg);
307
0
      return false;
308
0
    }
309
0
  }
310
311
0
  if (type == cmStateEnums::INTERFACE_LIBRARY) {
312
0
    if (!cmGeneratorExpression::IsValidTargetName(libName) ||
313
0
        libName.find("::") != std::string::npos) {
314
0
      status.SetError(
315
0
        cmStrCat("Invalid name for INTERFACE library target: ", libName));
316
0
      return false;
317
0
    }
318
0
  }
319
320
0
  if (symbolicTarget && type != cmStateEnums::INTERFACE_LIBRARY) {
321
0
    status.SetError(
322
0
      "SYMBOLIC option may only be used with INTERFACE libraries");
323
0
    return false;
324
0
  }
325
326
0
  std::vector<std::string> srcs(s, args.end());
327
0
  cmTarget* target = mf.AddLibrary(libName, type, srcs, excludeFromAll);
328
0
  target->SetSymbolic(symbolicTarget);
329
330
0
  return true;
331
0
}