Coverage Report

Created: 2026-04-29 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/CMake/Source/cmExportBuildFileGenerator.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 "cmExportBuildFileGenerator.h"
4
5
#include <algorithm>
6
#include <map>
7
#include <memory>
8
#include <set>
9
#include <sstream>
10
#include <utility>
11
12
#include "cmExportSet.h"
13
#include "cmGeneratorExpression.h"
14
#include "cmGeneratorFileSet.h"
15
#include "cmGeneratorFileSets.h"
16
#include "cmGeneratorTarget.h"
17
#include "cmGlobalGenerator.h"
18
#include "cmList.h"
19
#include "cmLocalGenerator.h"
20
#include "cmMakefile.h"
21
#include "cmStateTypes.h"
22
#include "cmStringAlgorithms.h"
23
#include "cmTarget.h"
24
#include "cmTargetExport.h"
25
#include "cmValue.h"
26
27
class cmSourceFile;
28
29
cmExportBuildFileGenerator::cmExportBuildFileGenerator()
30
0
{
31
0
  this->LG = nullptr;
32
0
  this->ExportSet = nullptr;
33
0
}
34
35
void cmExportBuildFileGenerator::Compute(cmLocalGenerator* lg)
36
0
{
37
0
  this->LG = lg;
38
0
  if (this->ExportSet) {
39
0
    this->ExportSet->Compute(lg);
40
0
  }
41
0
}
42
43
cmStateEnums::TargetType cmExportBuildFileGenerator::GetExportTargetType(
44
  cmGeneratorTarget const* target) const
45
0
{
46
0
  cmStateEnums::TargetType targetType = target->GetType();
47
  // An object library exports as an interface library if we cannot
48
  // tell clients where to find the objects.  This is sufficient
49
  // to support transitive usage requirements on other targets that
50
  // use the object library.
51
0
  if (targetType == cmStateEnums::OBJECT_LIBRARY &&
52
0
      !target->Target->HasKnownObjectFileLocation(nullptr)) {
53
0
    targetType = cmStateEnums::INTERFACE_LIBRARY;
54
0
  }
55
0
  return targetType;
56
0
}
57
58
void cmExportBuildFileGenerator::SetExportSet(cmExportSet* exportSet)
59
0
{
60
0
  this->ExportSet = exportSet;
61
0
}
62
63
void cmExportBuildFileGenerator::SetImportLocationProperty(
64
  std::string const& config, std::string const& suffix,
65
  cmGeneratorTarget* target, ImportPropertyMap& properties)
66
0
{
67
  // Get the makefile in which to lookup target information.
68
0
  cmMakefile* mf = target->Makefile;
69
70
0
  if (target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
71
0
    std::string prop = cmStrCat("IMPORTED_OBJECTS", suffix);
72
73
    // Compute all the object files inside this target and setup
74
    // IMPORTED_OBJECTS as a list of object files
75
0
    std::vector<cmSourceFile const*> objectSources;
76
0
    target->GetObjectSources(objectSources, config);
77
0
    std::string const obj_dir = target->GetObjectDirectory(config);
78
0
    std::vector<std::string> objects;
79
0
    for (cmSourceFile const* sf : objectSources) {
80
0
      std::string const& obj = target->GetObjectName(sf);
81
0
      objects.push_back(obj_dir + obj);
82
0
    }
83
84
    // Store the property.
85
0
    properties[prop] = cmList::to_string(objects);
86
0
  } else {
87
    // Add the main target file.
88
0
    {
89
0
      std::string prop = cmStrCat("IMPORTED_LOCATION", suffix);
90
0
      std::string value;
91
0
      if (target->IsAppBundleOnApple()) {
92
0
        value =
93
0
          target->GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact);
94
0
      } else {
95
0
        value = target->GetFullPath(config,
96
0
                                    cmStateEnums::RuntimeBinaryArtifact, true);
97
0
      }
98
0
      properties[prop] = value;
99
0
    }
100
101
    // Add the import library for windows DLLs.
102
0
    if (target->HasImportLibrary(config)) {
103
0
      std::string prop = cmStrCat("IMPORTED_IMPLIB", suffix);
104
0
      std::string value =
105
0
        target->GetFullPath(config, cmStateEnums::ImportLibraryArtifact, true);
106
0
      if (mf->GetDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX")) {
107
0
        target->GetImplibGNUtoMS(config, value, value,
108
0
                                 "${CMAKE_IMPORT_LIBRARY_SUFFIX}");
109
0
      }
110
0
      properties[prop] = value;
111
0
    }
112
0
  }
113
0
}
114
115
bool cmExportBuildFileGenerator::CollectExports(
116
  std::function<void(cmGeneratorTarget const*)> visitor)
117
0
{
118
0
  auto pred = [&](cmExportBuildFileGenerator::TargetExport& tei) -> bool {
119
0
    cmGeneratorTarget* te = this->LG->FindGeneratorTargetToUse(tei.Name);
120
0
    if (this->ExportedTargets.insert(te).second) {
121
0
      this->Exports.emplace_back(te, tei.XcFrameworkLocation);
122
0
      visitor(te);
123
0
      return true;
124
0
    }
125
126
0
    this->ComplainAboutDuplicateTarget(te->GetName());
127
0
    return false;
128
0
  };
129
130
0
  std::vector<TargetExport> targets;
131
0
  this->GetTargets(targets);
132
0
  return std::all_of(targets.begin(), targets.end(), pred);
133
0
}
134
135
void cmExportBuildFileGenerator::HandleMissingTarget(
136
  std::string& link_libs, cmGeneratorTarget const* depender,
137
  cmGeneratorTarget* dependee)
138
0
{
139
  // The target is not in the export.
140
0
  if (!this->AppendMode) {
141
0
    auto const& exportInfo = this->FindExportInfo(dependee);
142
143
0
    if (exportInfo.Namespaces.size() == 1 && exportInfo.Sets.size() == 1) {
144
0
      std::string missingTarget = *exportInfo.Namespaces.begin();
145
146
0
      missingTarget += dependee->GetExportName();
147
0
      link_libs += missingTarget;
148
0
      this->MissingTargets.emplace_back(std::move(missingTarget));
149
0
      return;
150
0
    }
151
    // We are not appending, so all exported targets should be
152
    // known here.  This is probably user-error.
153
0
    this->ComplainAboutMissingTarget(depender, dependee, exportInfo);
154
0
  }
155
  // Assume the target will be exported by another command.
156
  // Append it with the export namespace.
157
0
  link_libs += this->Namespace;
158
0
  link_libs += dependee->GetExportName();
159
0
}
160
161
void cmExportBuildFileGenerator::GetTargets(
162
  std::vector<TargetExport>& targets) const
163
0
{
164
0
  if (this->ExportSet) {
165
0
    for (std::unique_ptr<cmTargetExport> const& te :
166
0
         this->ExportSet->GetTargetExports()) {
167
0
      if (te->NamelinkOnly) {
168
0
        continue;
169
0
      }
170
0
      targets.emplace_back(te->TargetName, te->XcFrameworkLocation);
171
0
    }
172
0
    return;
173
0
  }
174
0
  targets = this->Targets;
175
0
}
176
177
cmExportFileGenerator::ExportInfo cmExportBuildFileGenerator::FindExportInfo(
178
  cmGeneratorTarget const* target) const
179
0
{
180
0
  std::vector<std::string> exportFiles;
181
0
  std::set<std::string> exportSets;
182
0
  std::set<std::string> namespaces;
183
184
0
  auto const& name = target->GetName();
185
0
  auto& allExportSets =
186
0
    target->GetLocalGenerator()->GetGlobalGenerator()->GetBuildExportSets();
187
188
0
  for (auto const& exp : allExportSets) {
189
0
    cmExportBuildFileGenerator const* const bfg = exp.second;
190
0
    cmExportSet const* const exportSet = bfg->GetExportSet();
191
0
    std::vector<TargetExport> targets;
192
0
    bfg->GetTargets(targets);
193
0
    if (std::any_of(
194
0
          targets.begin(), targets.end(),
195
0
          [&name](TargetExport const& te) { return te.Name == name; })) {
196
0
      if (exportSet) {
197
0
        exportSets.insert(exportSet->GetName());
198
0
      } else {
199
0
        exportSets.insert(exp.first);
200
0
      }
201
0
      exportFiles.push_back(exp.first);
202
0
      namespaces.insert(bfg->GetNamespace());
203
0
    }
204
0
  }
205
206
0
  return { exportFiles, exportSets, namespaces };
207
0
}
208
209
void cmExportBuildFileGenerator::ComplainAboutMissingTarget(
210
  cmGeneratorTarget const* depender, cmGeneratorTarget const* dependee,
211
  ExportInfo const& exportInfo) const
212
0
{
213
0
  std::ostringstream e;
214
0
  e << "export called with target \"" << depender->GetName()
215
0
    << "\" which requires target \"" << dependee->GetName() << "\" ";
216
0
  if (exportInfo.Sets.empty()) {
217
0
    e << "that is not in any export set.";
218
0
  } else {
219
0
    if (exportInfo.Sets.size() == 1) {
220
0
      e << "that is not in this export set, but in another export set which "
221
0
           "is "
222
0
           "exported multiple times with different namespaces: ";
223
0
    } else {
224
0
      e << "that is not in this export set, but in multiple other export "
225
0
           "sets: ";
226
0
    }
227
0
    e << cmJoin(exportInfo.Files, ", ") << ".\n"
228
0
      << "An exported target cannot depend upon another target which is "
229
0
         "exported in more than one export set or with more than one "
230
0
         "namespace. Consider consolidating the exports of the \""
231
0
      << dependee->GetName() << "\" target to a single export.";
232
0
  }
233
234
0
  this->ReportError(e.str());
235
0
}
236
237
void cmExportBuildFileGenerator::ComplainAboutDuplicateTarget(
238
  std::string const& targetName) const
239
0
{
240
0
  std::ostringstream e;
241
0
  e << "given target \"" << targetName << "\" more than once.";
242
0
  this->ReportError(e.str());
243
0
}
244
245
void cmExportBuildFileGenerator::IssueMessage(MessageType type,
246
                                              std::string const& message) const
247
0
{
248
0
  this->LG->GetMakefile()->IssueMessage(type, message);
249
0
}
250
251
void cmExportBuildFileGenerator::IssueDiagnostic(
252
  cmDiagnosticCategory category, std::string const& message) const
253
0
{
254
0
  this->LG->GetMakefile()->IssueDiagnostic(category, message);
255
0
}
256
257
std::string cmExportBuildFileGenerator::InstallNameDir(
258
  cmGeneratorTarget const* target, std::string const& config)
259
0
{
260
0
  std::string install_name_dir;
261
262
0
  cmMakefile* mf = target->Target->GetMakefile();
263
0
  if (mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) {
264
0
    install_name_dir = target->GetInstallNameDirForBuildTree(config);
265
0
  }
266
267
0
  return install_name_dir;
268
0
}
269
270
bool cmExportBuildFileGenerator::PopulateInterfaceProperties(
271
  cmGeneratorTarget const* target, ImportPropertyMap& properties)
272
0
{
273
0
  this->PopulateInterfaceProperty("INTERFACE_INCLUDE_DIRECTORIES", target,
274
0
                                  cmGeneratorExpression::BuildInterface,
275
0
                                  properties);
276
0
  this->PopulateInterfaceProperty("INTERFACE_LINK_DIRECTORIES", target,
277
0
                                  cmGeneratorExpression::BuildInterface,
278
0
                                  properties);
279
0
  this->PopulateInterfaceProperty("INTERFACE_LINK_DEPENDS", target,
280
0
                                  cmGeneratorExpression::BuildInterface,
281
0
                                  properties);
282
0
  this->PopulateInterfaceProperty("INTERFACE_SOURCES", target,
283
0
                                  cmGeneratorExpression::BuildInterface,
284
0
                                  properties);
285
286
0
  return this->PopulateInterfaceProperties(
287
0
    target, {}, cmGeneratorExpression::BuildInterface, properties);
288
0
}
289
290
bool cmExportBuildFileGenerator::PopulateFileSetInterfaceProperties(
291
  cmGeneratorTarget const* target, ImportFileSetPropertyMap& properties)
292
0
{
293
0
  cmGeneratorFileSets const* const gfs = target->GetGeneratorFileSets();
294
0
  bool result = true;
295
296
0
  for (auto const& type : gfs->GetInterfaceFileSetTypes()) {
297
0
    for (auto const* fileSet : gfs->GetInterfaceFileSets(type)) {
298
0
      ImportPropertyMap& fsProperties = properties[fileSet->GetName()];
299
0
      this->PopulateFileSetInterfaceProperty(
300
0
        "INTERFACE_INCLUDE_DIRECTORIES", target, fileSet,
301
0
        cmGeneratorExpression::BuildInterface, fsProperties);
302
0
      result = result &&
303
0
        this->PopulateFileSetInterfaceProperties(
304
0
          target, fileSet, cmGeneratorExpression::InstallInterface,
305
0
          fsProperties);
306
0
    }
307
0
  }
308
0
  return result;
309
0
}