Imported Upstream version 3.23.2
[platform/upstream/cmake.git] / Source / cmExportBuildFileGenerator.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 #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 <cmext/algorithm>
13
14 #include "cmExportSet.h"
15 #include "cmFileSet.h"
16 #include "cmGeneratorExpression.h"
17 #include "cmGeneratorTarget.h"
18 #include "cmGlobalGenerator.h"
19 #include "cmLocalGenerator.h"
20 #include "cmMakefile.h"
21 #include "cmMessageType.h"
22 #include "cmOutputConverter.h"
23 #include "cmPolicies.h"
24 #include "cmStateTypes.h"
25 #include "cmStringAlgorithms.h"
26 #include "cmTarget.h"
27 #include "cmTargetExport.h"
28 #include "cmValue.h"
29 #include "cmake.h"
30
31 class cmSourceFile;
32
33 cmExportBuildFileGenerator::cmExportBuildFileGenerator()
34 {
35   this->LG = nullptr;
36   this->ExportSet = nullptr;
37 }
38
39 void cmExportBuildFileGenerator::Compute(cmLocalGenerator* lg)
40 {
41   this->LG = lg;
42   if (this->ExportSet) {
43     this->ExportSet->Compute(lg);
44   }
45 }
46
47 bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
48 {
49   {
50     std::string expectedTargets;
51     std::string sep;
52     std::vector<std::string> targets;
53     bool generatedInterfaceRequired = false;
54     this->GetTargets(targets);
55     for (std::string const& tei : targets) {
56       cmGeneratorTarget* te = this->LG->FindGeneratorTargetToUse(tei);
57       expectedTargets += sep + this->Namespace + te->GetExportName();
58       sep = " ";
59       if (this->ExportedTargets.insert(te).second) {
60         this->Exports.push_back(te);
61       } else {
62         std::ostringstream e;
63         e << "given target \"" << te->GetName() << "\" more than once.";
64         this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
65           MessageType::FATAL_ERROR, e.str(),
66           this->LG->GetMakefile()->GetBacktrace());
67         return false;
68       }
69       generatedInterfaceRequired |=
70         this->GetExportTargetType(te) == cmStateEnums::INTERFACE_LIBRARY;
71     }
72
73     if (generatedInterfaceRequired) {
74       this->GenerateRequiredCMakeVersion(os, "3.0.0");
75     }
76     this->GenerateExpectedTargetsCode(os, expectedTargets);
77   }
78
79   std::vector<std::string> missingTargets;
80
81   // Create all the imported targets.
82   for (cmGeneratorTarget* gte : this->Exports) {
83     this->GenerateImportTargetCode(os, gte, this->GetExportTargetType(gte));
84
85     gte->Target->AppendBuildInterfaceIncludes();
86
87     ImportPropertyMap properties;
88
89     this->PopulateInterfaceProperty("INTERFACE_INCLUDE_DIRECTORIES", gte,
90                                     cmGeneratorExpression::BuildInterface,
91                                     properties, missingTargets);
92     this->PopulateInterfaceProperty("INTERFACE_SOURCES", gte,
93                                     cmGeneratorExpression::BuildInterface,
94                                     properties, missingTargets);
95     this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS", gte,
96                                     cmGeneratorExpression::BuildInterface,
97                                     properties, missingTargets);
98     this->PopulateInterfaceProperty("INTERFACE_COMPILE_OPTIONS", gte,
99                                     cmGeneratorExpression::BuildInterface,
100                                     properties, missingTargets);
101     this->PopulateInterfaceProperty("INTERFACE_PRECOMPILE_HEADERS", gte,
102                                     cmGeneratorExpression::BuildInterface,
103                                     properties, missingTargets);
104     this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", gte,
105                                     cmGeneratorExpression::BuildInterface,
106                                     properties, missingTargets);
107     this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", gte,
108                                     cmGeneratorExpression::BuildInterface,
109                                     properties, missingTargets);
110     this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", gte,
111                                     cmGeneratorExpression::BuildInterface,
112                                     properties, missingTargets);
113     this->PopulateInterfaceProperty("INTERFACE_LINK_DIRECTORIES", gte,
114                                     cmGeneratorExpression::BuildInterface,
115                                     properties, missingTargets);
116     this->PopulateInterfaceProperty("INTERFACE_LINK_DEPENDS", gte,
117                                     cmGeneratorExpression::BuildInterface,
118                                     properties, missingTargets);
119     this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE", gte,
120                                     properties);
121
122     std::string errorMessage;
123     if (!this->PopulateExportProperties(gte, properties, errorMessage)) {
124       this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
125         MessageType::FATAL_ERROR, errorMessage,
126         this->LG->GetMakefile()->GetBacktrace());
127       return false;
128     }
129
130     const bool newCMP0022Behavior =
131       gte->GetPolicyStatusCMP0022() != cmPolicies::WARN &&
132       gte->GetPolicyStatusCMP0022() != cmPolicies::OLD;
133     if (newCMP0022Behavior) {
134       this->PopulateInterfaceLinkLibrariesProperty(
135         gte, cmGeneratorExpression::BuildInterface, properties,
136         missingTargets);
137     }
138     this->PopulateCompatibleInterfaceProperties(gte, properties);
139
140     this->GenerateInterfaceProperties(gte, os, properties);
141
142     this->GenerateTargetFileSets(gte, os);
143   }
144
145   // Generate import file content for each configuration.
146   for (std::string const& c : this->Configurations) {
147     this->GenerateImportConfig(os, c, missingTargets);
148   }
149
150   this->GenerateMissingTargetsCheckCode(os, missingTargets);
151
152   return true;
153 }
154
155 void cmExportBuildFileGenerator::GenerateImportTargetsConfig(
156   std::ostream& os, const std::string& config, std::string const& suffix,
157   std::vector<std::string>& missingTargets)
158 {
159   for (cmGeneratorTarget* target : this->Exports) {
160     // Collect import properties for this target.
161     ImportPropertyMap properties;
162
163     if (this->GetExportTargetType(target) != cmStateEnums::INTERFACE_LIBRARY) {
164       this->SetImportLocationProperty(config, suffix, target, properties);
165     }
166     if (!properties.empty()) {
167       // Get the rest of the target details.
168       if (this->GetExportTargetType(target) !=
169           cmStateEnums::INTERFACE_LIBRARY) {
170         this->SetImportDetailProperties(config, suffix, target, properties,
171                                         missingTargets);
172         this->SetImportLinkInterface(config, suffix,
173                                      cmGeneratorExpression::BuildInterface,
174                                      target, properties, missingTargets);
175       }
176
177       // TODO: PUBLIC_HEADER_LOCATION
178       // This should wait until the build feature propagation stuff
179       // is done.  Then this can be a propagated include directory.
180       // this->GenerateImportProperty(config, te->HeaderGenerator,
181       //                              properties);
182
183       // Generate code in the export file.
184       this->GenerateImportPropertyCode(os, config, target, properties);
185     }
186   }
187 }
188
189 cmStateEnums::TargetType cmExportBuildFileGenerator::GetExportTargetType(
190   cmGeneratorTarget const* target) const
191 {
192   cmStateEnums::TargetType targetType = target->GetType();
193   // An object library exports as an interface library if we cannot
194   // tell clients where to find the objects.  This is sufficient
195   // to support transitive usage requirements on other targets that
196   // use the object library.
197   if (targetType == cmStateEnums::OBJECT_LIBRARY &&
198       !this->LG->GetGlobalGenerator()->HasKnownObjectFileLocation(nullptr)) {
199     targetType = cmStateEnums::INTERFACE_LIBRARY;
200   }
201   return targetType;
202 }
203
204 void cmExportBuildFileGenerator::SetExportSet(cmExportSet* exportSet)
205 {
206   this->ExportSet = exportSet;
207 }
208
209 void cmExportBuildFileGenerator::SetImportLocationProperty(
210   const std::string& config, std::string const& suffix,
211   cmGeneratorTarget* target, ImportPropertyMap& properties)
212 {
213   // Get the makefile in which to lookup target information.
214   cmMakefile* mf = target->Makefile;
215
216   if (target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
217     std::string prop = cmStrCat("IMPORTED_OBJECTS", suffix);
218
219     // Compute all the object files inside this target and setup
220     // IMPORTED_OBJECTS as a list of object files
221     std::vector<cmSourceFile const*> objectSources;
222     target->GetObjectSources(objectSources, config);
223     std::string const obj_dir = target->GetObjectDirectory(config);
224     std::vector<std::string> objects;
225     for (cmSourceFile const* sf : objectSources) {
226       const std::string& obj = target->GetObjectName(sf);
227       objects.push_back(obj_dir + obj);
228     }
229
230     // Store the property.
231     properties[prop] = cmJoin(objects, ";");
232   } else {
233     // Add the main target file.
234     {
235       std::string prop = cmStrCat("IMPORTED_LOCATION", suffix);
236       std::string value;
237       if (target->IsAppBundleOnApple()) {
238         value =
239           target->GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact);
240       } else {
241         value = target->GetFullPath(config,
242                                     cmStateEnums::RuntimeBinaryArtifact, true);
243       }
244       properties[prop] = value;
245     }
246
247     // Add the import library for windows DLLs.
248     if (target->HasImportLibrary(config)) {
249       std::string prop = cmStrCat("IMPORTED_IMPLIB", suffix);
250       std::string value =
251         target->GetFullPath(config, cmStateEnums::ImportLibraryArtifact);
252       if (mf->GetDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX")) {
253         target->GetImplibGNUtoMS(config, value, value,
254                                  "${CMAKE_IMPORT_LIBRARY_SUFFIX}");
255       }
256       properties[prop] = value;
257     }
258   }
259 }
260
261 void cmExportBuildFileGenerator::HandleMissingTarget(
262   std::string& link_libs, std::vector<std::string>& missingTargets,
263   cmGeneratorTarget const* depender, cmGeneratorTarget* dependee)
264 {
265   // The target is not in the export.
266   if (!this->AppendMode) {
267     const std::string name = dependee->GetName();
268     cmGlobalGenerator* gg =
269       dependee->GetLocalGenerator()->GetGlobalGenerator();
270     auto exportInfo = this->FindBuildExportInfo(gg, name);
271     std::vector<std::string> const& exportFiles = exportInfo.first;
272
273     if (exportFiles.size() == 1) {
274       std::string missingTarget = exportInfo.second;
275
276       missingTarget += dependee->GetExportName();
277       link_libs += missingTarget;
278       missingTargets.push_back(std::move(missingTarget));
279       return;
280     }
281     // We are not appending, so all exported targets should be
282     // known here.  This is probably user-error.
283     this->ComplainAboutMissingTarget(depender, dependee, exportFiles);
284   }
285   // Assume the target will be exported by another command.
286   // Append it with the export namespace.
287   link_libs += this->Namespace;
288   link_libs += dependee->GetExportName();
289 }
290
291 void cmExportBuildFileGenerator::GetTargets(
292   std::vector<std::string>& targets) const
293 {
294   if (this->ExportSet) {
295     for (std::unique_ptr<cmTargetExport> const& te :
296          this->ExportSet->GetTargetExports()) {
297       if (te->NamelinkOnly) {
298         continue;
299       }
300       targets.push_back(te->TargetName);
301     }
302     return;
303   }
304   targets = this->Targets;
305 }
306
307 std::pair<std::vector<std::string>, std::string>
308 cmExportBuildFileGenerator::FindBuildExportInfo(cmGlobalGenerator* gg,
309                                                 const std::string& name)
310 {
311   std::vector<std::string> exportFiles;
312   std::string ns;
313
314   auto& exportSets = gg->GetBuildExportSets();
315
316   for (auto const& exp : exportSets) {
317     const auto& exportSet = exp.second;
318     std::vector<std::string> targets;
319     exportSet->GetTargets(targets);
320     if (cm::contains(targets, name)) {
321       exportFiles.push_back(exp.first);
322       ns = exportSet->GetNamespace();
323     }
324   }
325
326   return { exportFiles, ns };
327 }
328
329 void cmExportBuildFileGenerator::ComplainAboutMissingTarget(
330   cmGeneratorTarget const* depender, cmGeneratorTarget const* dependee,
331   std::vector<std::string> const& exportFiles)
332 {
333   std::ostringstream e;
334   e << "export called with target \"" << depender->GetName()
335     << "\" which requires target \"" << dependee->GetName() << "\" ";
336   if (exportFiles.empty()) {
337     e << "that is not in any export set.";
338   } else {
339     e << "that is not in this export set, but in multiple other export sets: "
340       << cmJoin(exportFiles, ", ") << ".\n";
341     e << "An exported target cannot depend upon another target which is "
342          "exported multiple times. Consider consolidating the exports of the "
343          "\""
344       << dependee->GetName() << "\" target to a single export.";
345   }
346
347   this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
348     MessageType::FATAL_ERROR, e.str(),
349     this->LG->GetMakefile()->GetBacktrace());
350 }
351
352 std::string cmExportBuildFileGenerator::InstallNameDir(
353   cmGeneratorTarget const* target, const std::string& config)
354 {
355   std::string install_name_dir;
356
357   cmMakefile* mf = target->Target->GetMakefile();
358   if (mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) {
359     install_name_dir = target->GetInstallNameDirForBuildTree(config);
360   }
361
362   return install_name_dir;
363 }
364
365 namespace {
366 bool EntryIsContextSensitive(
367   const std::unique_ptr<cmCompiledGeneratorExpression>& cge)
368 {
369   return cge->GetHadContextSensitiveCondition();
370 }
371 }
372
373 std::string cmExportBuildFileGenerator::GetFileSetDirectories(
374   cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* /*te*/)
375 {
376   std::vector<std::string> resultVector;
377
378   auto configs =
379     gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
380   auto directoryEntries = fileSet->CompileDirectoryEntries();
381
382   for (auto const& config : configs) {
383     auto directories = fileSet->EvaluateDirectoryEntries(
384       directoryEntries, gte->LocalGenerator, config, gte);
385
386     bool const contextSensitive =
387       std::any_of(directoryEntries.begin(), directoryEntries.end(),
388                   EntryIsContextSensitive);
389
390     for (auto const& directory : directories) {
391       auto dest = cmOutputConverter::EscapeForCMake(
392         directory, cmOutputConverter::WrapQuotes::NoWrap);
393
394       if (contextSensitive && configs.size() != 1) {
395         resultVector.push_back(
396           cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\""));
397       } else {
398         resultVector.push_back(cmStrCat('"', dest, '"'));
399         break;
400       }
401     }
402   }
403
404   return cmJoin(resultVector, " ");
405 }
406
407 std::string cmExportBuildFileGenerator::GetFileSetFiles(cmGeneratorTarget* gte,
408                                                         cmFileSet* fileSet,
409                                                         cmTargetExport* /*te*/)
410 {
411   std::vector<std::string> resultVector;
412
413   auto configs =
414     gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
415
416   auto fileEntries = fileSet->CompileFileEntries();
417   auto directoryEntries = fileSet->CompileDirectoryEntries();
418
419   for (auto const& config : configs) {
420     auto directories = fileSet->EvaluateDirectoryEntries(
421       directoryEntries, gte->LocalGenerator, config, gte);
422
423     std::map<std::string, std::vector<std::string>> files;
424     for (auto const& entry : fileEntries) {
425       fileSet->EvaluateFileEntry(directories, files, entry,
426                                  gte->LocalGenerator, config, gte);
427     }
428
429     bool const contextSensitive =
430       std::any_of(directoryEntries.begin(), directoryEntries.end(),
431                   EntryIsContextSensitive) ||
432       std::any_of(fileEntries.begin(), fileEntries.end(),
433                   EntryIsContextSensitive);
434
435     for (auto const& it : files) {
436       for (auto const& filename : it.second) {
437         auto escapedFile = cmOutputConverter::EscapeForCMake(
438           filename, cmOutputConverter::WrapQuotes::NoWrap);
439         if (contextSensitive && configs.size() != 1) {
440           resultVector.push_back(
441             cmStrCat("\"$<$<CONFIG:", config, ">:", escapedFile, ">\""));
442         } else {
443           resultVector.push_back(cmStrCat('"', escapedFile, '"'));
444         }
445       }
446     }
447
448     if (!(contextSensitive && configs.size() != 1)) {
449       break;
450     }
451   }
452
453   return cmJoin(resultVector, " ");
454 }