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 // Create all the imported targets.
80 for (cmGeneratorTarget* gte : this->Exports) {
81 this->GenerateImportTargetCode(os, gte, this->GetExportTargetType(gte));
83 gte->Target->AppendBuildInterfaceIncludes();
85 ImportPropertyMap properties;
87 this->PopulateInterfaceProperty("INTERFACE_INCLUDE_DIRECTORIES", gte,
88 cmGeneratorExpression::BuildInterface,
90 this->PopulateInterfaceProperty("INTERFACE_SOURCES", gte,
91 cmGeneratorExpression::BuildInterface,
93 this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS", gte,
94 cmGeneratorExpression::BuildInterface,
96 this->PopulateInterfaceProperty("INTERFACE_COMPILE_OPTIONS", gte,
97 cmGeneratorExpression::BuildInterface,
99 this->PopulateInterfaceProperty("INTERFACE_PRECOMPILE_HEADERS", gte,
100 cmGeneratorExpression::BuildInterface,
102 this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", gte,
103 cmGeneratorExpression::BuildInterface,
105 this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", gte,
106 cmGeneratorExpression::BuildInterface,
108 this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", gte,
109 cmGeneratorExpression::BuildInterface,
111 this->PopulateInterfaceProperty("INTERFACE_LINK_DIRECTORIES", gte,
112 cmGeneratorExpression::BuildInterface,
114 this->PopulateInterfaceProperty("INTERFACE_LINK_DEPENDS", gte,
115 cmGeneratorExpression::BuildInterface,
117 this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE", gte,
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());
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);
135 this->PopulateCompatibleInterfaceProperties(gte, properties);
137 this->GenerateInterfaceProperties(gte, os, properties);
139 this->GenerateTargetFileSets(gte, os);
142 // Generate import file content for each configuration.
143 for (std::string const& c : this->Configurations) {
144 this->GenerateImportConfig(os, c);
147 this->GenerateMissingTargetsCheckCode(os);
152 void cmExportBuildFileGenerator::GenerateImportTargetsConfig(
153 std::ostream& os, const std::string& config, std::string const& suffix)
155 for (cmGeneratorTarget* target : this->Exports) {
156 // Collect import properties for this target.
157 ImportPropertyMap properties;
159 if (this->GetExportTargetType(target) != cmStateEnums::INTERFACE_LIBRARY) {
160 this->SetImportLocationProperty(config, suffix, target, properties);
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,
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,
178 // Generate code in the export file.
179 this->GenerateImportPropertyCode(os, config, target, properties);
184 cmStateEnums::TargetType cmExportBuildFileGenerator::GetExportTargetType(
185 cmGeneratorTarget const* target) const
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;
199 void cmExportBuildFileGenerator::SetExportSet(cmExportSet* exportSet)
201 this->ExportSet = exportSet;
204 void cmExportBuildFileGenerator::SetImportLocationProperty(
205 const std::string& config, std::string const& suffix,
206 cmGeneratorTarget* target, ImportPropertyMap& properties)
208 // Get the makefile in which to lookup target information.
209 cmMakefile* mf = target->Makefile;
211 if (target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
212 std::string prop = cmStrCat("IMPORTED_OBJECTS", suffix);
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);
225 // Store the property.
226 properties[prop] = cmJoin(objects, ";");
228 // Add the main target file.
230 std::string prop = cmStrCat("IMPORTED_LOCATION", suffix);
232 if (target->IsAppBundleOnApple()) {
234 target->GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact);
236 value = target->GetFullPath(config,
237 cmStateEnums::RuntimeBinaryArtifact, true);
239 properties[prop] = value;
242 // Add the import library for windows DLLs.
243 if (target->HasImportLibrary(config)) {
244 std::string prop = cmStrCat("IMPORTED_IMPLIB", suffix);
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}");
251 properties[prop] = value;
256 void cmExportBuildFileGenerator::HandleMissingTarget(
257 std::string& link_libs, cmGeneratorTarget const* depender,
258 cmGeneratorTarget* dependee)
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;
268 if (exportFiles.size() == 1) {
269 std::string missingTarget = exportInfo.second;
271 missingTarget += dependee->GetExportName();
272 link_libs += missingTarget;
273 this->MissingTargets.emplace_back(std::move(missingTarget));
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);
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();
286 void cmExportBuildFileGenerator::GetTargets(
287 std::vector<std::string>& targets) const
289 if (this->ExportSet) {
290 for (std::unique_ptr<cmTargetExport> const& te :
291 this->ExportSet->GetTargetExports()) {
292 if (te->NamelinkOnly) {
295 targets.push_back(te->TargetName);
299 targets = this->Targets;
302 std::pair<std::vector<std::string>, std::string>
303 cmExportBuildFileGenerator::FindBuildExportInfo(cmGlobalGenerator* gg,
304 const std::string& name)
306 std::vector<std::string> exportFiles;
309 auto& exportSets = gg->GetBuildExportSets();
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();
321 return { exportFiles, ns };
324 void cmExportBuildFileGenerator::ComplainAboutMissingTarget(
325 cmGeneratorTarget const* depender, cmGeneratorTarget const* dependee,
326 std::vector<std::string> const& exportFiles)
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.";
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 "
339 << dependee->GetName() << "\" target to a single export.";
342 this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
343 MessageType::FATAL_ERROR, e.str(),
344 this->LG->GetMakefile()->GetBacktrace());
347 std::string cmExportBuildFileGenerator::InstallNameDir(
348 cmGeneratorTarget const* target, const std::string& config)
350 std::string install_name_dir;
352 cmMakefile* mf = target->Target->GetMakefile();
353 if (mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) {
354 install_name_dir = target->GetInstallNameDirForBuildTree(config);
357 return install_name_dir;
361 bool EntryIsContextSensitive(
362 const std::unique_ptr<cmCompiledGeneratorExpression>& cge)
364 return cge->GetHadContextSensitiveCondition();
368 std::string cmExportBuildFileGenerator::GetFileSetDirectories(
369 cmGeneratorTarget* gte, cmFileSet* fileSet, cmTargetExport* /*te*/)
371 std::vector<std::string> resultVector;
374 gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
375 auto directoryEntries = fileSet->CompileDirectoryEntries();
377 for (auto const& config : configs) {
378 auto directories = fileSet->EvaluateDirectoryEntries(
379 directoryEntries, gte->LocalGenerator, config, gte);
381 bool const contextSensitive =
382 std::any_of(directoryEntries.begin(), directoryEntries.end(),
383 EntryIsContextSensitive);
385 for (auto const& directory : directories) {
386 auto dest = cmOutputConverter::EscapeForCMake(
387 directory, cmOutputConverter::WrapQuotes::NoWrap);
389 if (contextSensitive && configs.size() != 1) {
390 resultVector.push_back(
391 cmStrCat("\"$<$<CONFIG:", config, ">:", dest, ">\""));
393 resultVector.push_back(cmStrCat('"', dest, '"'));
399 return cmJoin(resultVector, " ");
402 std::string cmExportBuildFileGenerator::GetFileSetFiles(cmGeneratorTarget* gte,
404 cmTargetExport* /*te*/)
406 std::vector<std::string> resultVector;
409 gte->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
411 auto fileEntries = fileSet->CompileFileEntries();
412 auto directoryEntries = fileSet->CompileDirectoryEntries();
414 for (auto const& config : configs) {
415 auto directories = fileSet->EvaluateDirectoryEntries(
416 directoryEntries, gte->LocalGenerator, config, gte);
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);
424 bool const contextSensitive =
425 std::any_of(directoryEntries.begin(), directoryEntries.end(),
426 EntryIsContextSensitive) ||
427 std::any_of(fileEntries.begin(), fileEntries.end(),
428 EntryIsContextSensitive);
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, ">\""));
438 resultVector.push_back(cmStrCat('"', escapedFile, '"'));
443 if (!(contextSensitive && configs.size() != 1)) {
448 return cmJoin(resultVector, " ");