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 <map>
6 #include <memory>
7 #include <set>
8 #include <sstream>
9 #include <utility>
10
11 #include <cmext/algorithm>
12
13 #include "cmExportSet.h"
14 #include "cmGeneratorExpression.h"
15 #include "cmGeneratorTarget.h"
16 #include "cmGlobalGenerator.h"
17 #include "cmLocalGenerator.h"
18 #include "cmMakefile.h"
19 #include "cmMessageType.h"
20 #include "cmPolicies.h"
21 #include "cmStateTypes.h"
22 #include "cmStringAlgorithms.h"
23 #include "cmTarget.h"
24 #include "cmTargetExport.h"
25 #include "cmValue.h"
26 #include "cmake.h"
27
28 class cmSourceFile;
29
cmExportBuildFileGenerator()30 cmExportBuildFileGenerator::cmExportBuildFileGenerator()
31 {
32 this->LG = nullptr;
33 this->ExportSet = nullptr;
34 }
35
Compute(cmLocalGenerator * lg)36 void cmExportBuildFileGenerator::Compute(cmLocalGenerator* lg)
37 {
38 this->LG = lg;
39 if (this->ExportSet) {
40 this->ExportSet->Compute(lg);
41 }
42 }
43
GenerateMainFile(std::ostream & os)44 bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
45 {
46 {
47 std::string expectedTargets;
48 std::string sep;
49 std::vector<std::string> targets;
50 bool generatedInterfaceRequired = false;
51 this->GetTargets(targets);
52 for (std::string const& tei : targets) {
53 cmGeneratorTarget* te = this->LG->FindGeneratorTargetToUse(tei);
54 expectedTargets += sep + this->Namespace + te->GetExportName();
55 sep = " ";
56 if (this->ExportedTargets.insert(te).second) {
57 this->Exports.push_back(te);
58 } else {
59 std::ostringstream e;
60 e << "given target \"" << te->GetName() << "\" more than once.";
61 this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
62 MessageType::FATAL_ERROR, e.str(),
63 this->LG->GetMakefile()->GetBacktrace());
64 return false;
65 }
66 generatedInterfaceRequired |=
67 this->GetExportTargetType(te) == cmStateEnums::INTERFACE_LIBRARY;
68 }
69
70 if (generatedInterfaceRequired) {
71 this->GenerateRequiredCMakeVersion(os, "3.0.0");
72 }
73 this->GenerateExpectedTargetsCode(os, expectedTargets);
74 }
75
76 std::vector<std::string> missingTargets;
77
78 // Create all the imported targets.
79 for (cmGeneratorTarget* gte : this->Exports) {
80 this->GenerateImportTargetCode(os, gte, this->GetExportTargetType(gte));
81
82 gte->Target->AppendBuildInterfaceIncludes();
83
84 ImportPropertyMap properties;
85
86 this->PopulateInterfaceProperty("INTERFACE_INCLUDE_DIRECTORIES", gte,
87 cmGeneratorExpression::BuildInterface,
88 properties, missingTargets);
89 this->PopulateInterfaceProperty("INTERFACE_SOURCES", gte,
90 cmGeneratorExpression::BuildInterface,
91 properties, missingTargets);
92 this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS", gte,
93 cmGeneratorExpression::BuildInterface,
94 properties, missingTargets);
95 this->PopulateInterfaceProperty("INTERFACE_COMPILE_OPTIONS", gte,
96 cmGeneratorExpression::BuildInterface,
97 properties, missingTargets);
98 this->PopulateInterfaceProperty("INTERFACE_PRECOMPILE_HEADERS", gte,
99 cmGeneratorExpression::BuildInterface,
100 properties, missingTargets);
101 this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", gte,
102 cmGeneratorExpression::BuildInterface,
103 properties, missingTargets);
104 this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", gte,
105 cmGeneratorExpression::BuildInterface,
106 properties, missingTargets);
107 this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", gte,
108 cmGeneratorExpression::BuildInterface,
109 properties, missingTargets);
110 this->PopulateInterfaceProperty("INTERFACE_LINK_DIRECTORIES", gte,
111 cmGeneratorExpression::BuildInterface,
112 properties, missingTargets);
113 this->PopulateInterfaceProperty("INTERFACE_LINK_DEPENDS", gte,
114 cmGeneratorExpression::BuildInterface,
115 properties, missingTargets);
116 this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE", gte,
117 properties);
118
119 std::string errorMessage;
120 if (!this->PopulateExportProperties(gte, properties, errorMessage)) {
121 this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
122 MessageType::FATAL_ERROR, errorMessage,
123 this->LG->GetMakefile()->GetBacktrace());
124 return false;
125 }
126
127 const bool newCMP0022Behavior =
128 gte->GetPolicyStatusCMP0022() != cmPolicies::WARN &&
129 gte->GetPolicyStatusCMP0022() != cmPolicies::OLD;
130 if (newCMP0022Behavior) {
131 this->PopulateInterfaceLinkLibrariesProperty(
132 gte, cmGeneratorExpression::BuildInterface, properties,
133 missingTargets);
134 }
135 this->PopulateCompatibleInterfaceProperties(gte, properties);
136
137 this->GenerateInterfaceProperties(gte, os, properties);
138 }
139
140 // Generate import file content for each configuration.
141 for (std::string const& c : this->Configurations) {
142 this->GenerateImportConfig(os, c, missingTargets);
143 }
144
145 this->GenerateMissingTargetsCheckCode(os, missingTargets);
146
147 return true;
148 }
149
GenerateImportTargetsConfig(std::ostream & os,const std::string & config,std::string const & suffix,std::vector<std::string> & missingTargets)150 void cmExportBuildFileGenerator::GenerateImportTargetsConfig(
151 std::ostream& os, const std::string& config, std::string const& suffix,
152 std::vector<std::string>& missingTargets)
153 {
154 for (cmGeneratorTarget* target : this->Exports) {
155 // Collect import properties for this target.
156 ImportPropertyMap properties;
157
158 if (this->GetExportTargetType(target) != cmStateEnums::INTERFACE_LIBRARY) {
159 this->SetImportLocationProperty(config, suffix, target, properties);
160 }
161 if (!properties.empty()) {
162 // Get the rest of the target details.
163 if (this->GetExportTargetType(target) !=
164 cmStateEnums::INTERFACE_LIBRARY) {
165 this->SetImportDetailProperties(config, suffix, target, properties,
166 missingTargets);
167 this->SetImportLinkInterface(config, suffix,
168 cmGeneratorExpression::BuildInterface,
169 target, properties, missingTargets);
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
GetExportTargetType(cmGeneratorTarget const * target) const184 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 !this->LG->GetGlobalGenerator()->HasKnownObjectFileLocation(nullptr)) {
194 targetType = cmStateEnums::INTERFACE_LIBRARY;
195 }
196 return targetType;
197 }
198
SetExportSet(cmExportSet * exportSet)199 void cmExportBuildFileGenerator::SetExportSet(cmExportSet* exportSet)
200 {
201 this->ExportSet = exportSet;
202 }
203
SetImportLocationProperty(const std::string & config,std::string const & suffix,cmGeneratorTarget * target,ImportPropertyMap & properties)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
HandleMissingTarget(std::string & link_libs,std::vector<std::string> & missingTargets,cmGeneratorTarget const * depender,cmGeneratorTarget * dependee)256 void cmExportBuildFileGenerator::HandleMissingTarget(
257 std::string& link_libs, std::vector<std::string>& missingTargets,
258 cmGeneratorTarget const* depender, 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 missingTargets.push_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
GetTargets(std::vector<std::string> & targets) const286 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>
FindBuildExportInfo(cmGlobalGenerator * gg,const std::string & name)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
ComplainAboutMissingTarget(cmGeneratorTarget const * depender,cmGeneratorTarget const * dependee,std::vector<std::string> const & exportFiles)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
InstallNameDir(cmGeneratorTarget const * target,const std::string & config)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