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"
12 #include <cmext/algorithm>
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"
27 #include "cmTargetExport.h"
33 cmExportBuildFileGenerator::cmExportBuildFileGenerator()
36 this->ExportSet = nullptr;
39 void cmExportBuildFileGenerator::Compute(cmLocalGenerator* lg)
42 if (this->ExportSet) {
43 this->ExportSet->Compute(lg);
47 bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
50 std::string expectedTargets;
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();
59 if (this->ExportedTargets.insert(te).second) {
60 this->Exports.push_back(te);
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());
69 generatedInterfaceRequired |=
70 this->GetExportTargetType(te) == cmStateEnums::INTERFACE_LIBRARY;
73 if (generatedInterfaceRequired) {
74 this->GenerateRequiredCMakeVersion(os, "3.0.0");
76 this->GenerateExpectedTargetsCode(os, expectedTargets);
79 std::vector<std::string> missingTargets;
81 // Create all the imported targets.
82 for (cmGeneratorTarget* gte : this->Exports) {
83 this->GenerateImportTargetCode(os, gte, this->GetExportTargetType(gte));
85 gte->Target->AppendBuildInterfaceIncludes();
87 ImportPropertyMap properties;
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,
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());
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,
138 this->PopulateCompatibleInterfaceProperties(gte, properties);
140 this->GenerateInterfaceProperties(gte, os, properties);
142 this->GenerateTargetFileSets(gte, os);
145 // Generate import file content for each configuration.
146 for (std::string const& c : this->Configurations) {
147 this->GenerateImportConfig(os, c, missingTargets);
150 this->GenerateMissingTargetsCheckCode(os, missingTargets);
155 void cmExportBuildFileGenerator::GenerateImportTargetsConfig(
156 std::ostream& os, const std::string& config, std::string const& suffix,
157 std::vector<std::string>& missingTargets)
159 for (cmGeneratorTarget* target : this->Exports) {
160 // Collect import properties for this target.
161 ImportPropertyMap properties;
163 if (this->GetExportTargetType(target) != cmStateEnums::INTERFACE_LIBRARY) {
164 this->SetImportLocationProperty(config, suffix, target, properties);
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,
172 this->SetImportLinkInterface(config, suffix,
173 cmGeneratorExpression::BuildInterface,
174 target, properties, missingTargets);
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,
183 // Generate code in the export file.
184 this->GenerateImportPropertyCode(os, config, target, properties);
189 cmStateEnums::TargetType cmExportBuildFileGenerator::GetExportTargetType(
190 cmGeneratorTarget const* target) const
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;
204 void cmExportBuildFileGenerator::SetExportSet(cmExportSet* exportSet)
206 this->ExportSet = exportSet;
209 void cmExportBuildFileGenerator::SetImportLocationProperty(
210 const std::string& config, std::string const& suffix,
211 cmGeneratorTarget* target, ImportPropertyMap& properties)
213 // Get the makefile in which to lookup target information.
214 cmMakefile* mf = target->Makefile;
216 if (target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
217 std::string prop = cmStrCat("IMPORTED_OBJECTS", suffix);
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);
230 // Store the property.
231 properties[prop] = cmJoin(objects, ";");
233 // Add the main target file.
235 std::string prop = cmStrCat("IMPORTED_LOCATION", suffix);
237 if (target->IsAppBundleOnApple()) {
239 target->GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact);
241 value = target->GetFullPath(config,
242 cmStateEnums::RuntimeBinaryArtifact, true);
244 properties[prop] = value;
247 // Add the import library for windows DLLs.
248 if (target->HasImportLibrary(config)) {
249 std::string prop = cmStrCat("IMPORTED_IMPLIB", suffix);
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}");
256 properties[prop] = value;
261 void cmExportBuildFileGenerator::HandleMissingTarget(
262 std::string& link_libs, std::vector<std::string>& missingTargets,
263 cmGeneratorTarget const* depender, cmGeneratorTarget* dependee)
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;
273 if (exportFiles.size() == 1) {
274 std::string missingTarget = exportInfo.second;
276 missingTarget += dependee->GetExportName();
277 link_libs += missingTarget;
278 missingTargets.push_back(std::move(missingTarget));
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);
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();
291 void cmExportBuildFileGenerator::GetTargets(
292 std::vector<std::string>& targets) const
294 if (this->ExportSet) {
295 for (std::unique_ptr<cmTargetExport> const& te :
296 this->ExportSet->GetTargetExports()) {
297 if (te->NamelinkOnly) {
300 targets.push_back(te->TargetName);
304 targets = this->Targets;
307 std::pair<std::vector<std::string>, std::string>
308 cmExportBuildFileGenerator::FindBuildExportInfo(cmGlobalGenerator* gg,
309 const std::string& name)
311 std::vector<std::string> exportFiles;
314 auto& exportSets = gg->GetBuildExportSets();
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();
326 return { exportFiles, ns };
329 void cmExportBuildFileGenerator::ComplainAboutMissingTarget(
330 cmGeneratorTarget const* depender, cmGeneratorTarget const* dependee,
331 std::vector<std::string> const& exportFiles)
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.";
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 "
344 << dependee->GetName() << "\" target to a single export.";
347 this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
348 MessageType::FATAL_ERROR, e.str(),
349 this->LG->GetMakefile()->GetBacktrace());
352 std::string cmExportBuildFileGenerator::InstallNameDir(
353 cmGeneratorTarget const* target, const std::string& config)
355 std::string install_name_dir;
357 cmMakefile* mf = target->Target->GetMakefile();
358 if (mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) {
359 install_name_dir = target->GetInstallNameDirForBuildTree(config);
362 return install_name_dir;
366 bool EntryIsContextSensitive(
367 const std::unique_ptr<cmCompiledGeneratorExpression>& cge)
369 return cge->GetHadContextSensitiveCondition();
373 std::string cmExportBuildFileGenerator::GetFileSetDirectories(
374 cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* /*te*/)
376 std::vector<std::string> resultVector;
379 gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
380 auto directoryEntries = fileSet->CompileDirectoryEntries();
382 for (auto const& config : configs) {
383 auto directories = fileSet->EvaluateDirectoryEntries(
384 directoryEntries, gte->LocalGenerator, config, gte);
386 bool const contextSensitive =
387 std::any_of(directoryEntries.begin(), directoryEntries.end(),
388 EntryIsContextSensitive);
390 for (auto const& directory : directories) {
391 auto dest = cmOutputConverter::EscapeForCMake(
392 directory, cmOutputConverter::WrapQuotes::NoWrap);
394 if (contextSensitive && configs.size() != 1) {
395 resultVector.push_back(
396 cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\""));
398 resultVector.push_back(cmStrCat('"', dest, '"'));
404 return cmJoin(resultVector, " ");
407 std::string cmExportBuildFileGenerator::GetFileSetFiles(cmGeneratorTarget* gte,
409 cmTargetExport* /*te*/)
411 std::vector<std::string> resultVector;
414 gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
416 auto fileEntries = fileSet->CompileFileEntries();
417 auto directoryEntries = fileSet->CompileDirectoryEntries();
419 for (auto const& config : configs) {
420 auto directories = fileSet->EvaluateDirectoryEntries(
421 directoryEntries, gte->LocalGenerator, config, gte);
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);
429 bool const contextSensitive =
430 std::any_of(directoryEntries.begin(), directoryEntries.end(),
431 EntryIsContextSensitive) ||
432 std::any_of(fileEntries.begin(), fileEntries.end(),
433 EntryIsContextSensitive);
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, ">\""));
443 resultVector.push_back(cmStrCat('"', escapedFile, '"'));
448 if (!(contextSensitive && configs.size() != 1)) {
453 return cmJoin(resultVector, " ");