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