mirror of
https://github.com/Kitware/CMake.git
synced 2026-06-30 19:57:41 +00:00
Modify install exports to make use of the install generator's captured diagnostic context. Modify build exports to capture and use a diagnostic context. This allows diagnostics issued by export file generation to use the diagnostic context from the instigating command, and to provide a backtrace to the same.
315 lines
10 KiB
C++
315 lines
10 KiB
C++
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file LICENSE.rst or https://cmake.org/licensing for details. */
|
|
#include "cmExportBuildFileGenerator.h"
|
|
|
|
#include <algorithm>
|
|
#include <memory>
|
|
#include <set>
|
|
#include <sstream>
|
|
#include <utility>
|
|
|
|
#include "cmExportSet.h"
|
|
#include "cmGeneratorExpression.h"
|
|
#include "cmGeneratorFileSet.h"
|
|
#include "cmGeneratorFileSets.h"
|
|
#include "cmGeneratorTarget.h"
|
|
#include "cmGlobalGenerator.h"
|
|
#include "cmList.h"
|
|
#include "cmLocalGenerator.h"
|
|
#include "cmMakefile.h"
|
|
#include "cmStateTypes.h"
|
|
#include "cmStringAlgorithms.h"
|
|
#include "cmTarget.h"
|
|
#include "cmTargetExport.h"
|
|
#include "cmValue.h"
|
|
|
|
class cmSourceFile;
|
|
|
|
cmExportBuildFileGenerator::cmExportBuildFileGenerator(
|
|
cmDiagnosticContext context)
|
|
: Context(std::move(context))
|
|
{
|
|
this->LG = nullptr;
|
|
this->ExportSet = nullptr;
|
|
}
|
|
|
|
cmDiagnosticContext cmExportBuildFileGenerator::CaptureContext(
|
|
cmMakefile const& mf)
|
|
{
|
|
cmDiagnosticContext context{ mf.GetBacktrace() };
|
|
context.RecordDiagnostic(cmDiagnostics::CMD_AUTHOR, mf.GetStateSnapshot());
|
|
return context;
|
|
}
|
|
|
|
void cmExportBuildFileGenerator::Compute(cmLocalGenerator* lg)
|
|
{
|
|
this->LG = lg;
|
|
if (this->ExportSet) {
|
|
this->ExportSet->Compute(lg);
|
|
}
|
|
}
|
|
|
|
cmStateEnums::TargetType cmExportBuildFileGenerator::GetExportTargetType(
|
|
cmGeneratorTarget const* target) const
|
|
{
|
|
cmStateEnums::TargetType targetType = target->GetType();
|
|
// An object library exports as an interface library if we cannot
|
|
// tell clients where to find the objects. This is sufficient
|
|
// to support transitive usage requirements on other targets that
|
|
// use the object library.
|
|
if (targetType == cmStateEnums::OBJECT_LIBRARY &&
|
|
!target->Target->HasKnownObjectFileLocation(nullptr)) {
|
|
targetType = cmStateEnums::INTERFACE_LIBRARY;
|
|
}
|
|
return targetType;
|
|
}
|
|
|
|
void cmExportBuildFileGenerator::SetExportSet(cmExportSet* exportSet)
|
|
{
|
|
this->ExportSet = exportSet;
|
|
}
|
|
|
|
void cmExportBuildFileGenerator::SetImportLocationProperty(
|
|
std::string const& config, std::string const& suffix,
|
|
cmGeneratorTarget* target, ImportPropertyMap& properties)
|
|
{
|
|
// Get the makefile in which to lookup target information.
|
|
cmMakefile* mf = target->Makefile;
|
|
|
|
if (target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
|
|
std::string prop = cmStrCat("IMPORTED_OBJECTS", suffix);
|
|
|
|
// Compute all the object files inside this target and setup
|
|
// IMPORTED_OBJECTS as a list of object files
|
|
std::vector<cmSourceFile const*> objectSources;
|
|
target->GetObjectSources(objectSources, config);
|
|
std::string const obj_dir = target->GetObjectDirectory(config);
|
|
std::vector<std::string> objects;
|
|
for (cmSourceFile const* sf : objectSources) {
|
|
std::string const& obj = target->GetObjectName(sf);
|
|
objects.push_back(obj_dir + obj);
|
|
}
|
|
|
|
// Store the property.
|
|
properties[prop] = cmList::to_string(objects);
|
|
} else {
|
|
// Add the main target file.
|
|
{
|
|
std::string prop = cmStrCat("IMPORTED_LOCATION", suffix);
|
|
std::string value;
|
|
if (target->IsAppBundleOnApple()) {
|
|
value =
|
|
target->GetFullPath(config, cmStateEnums::RuntimeBinaryArtifact);
|
|
} else {
|
|
value = target->GetFullPath(config,
|
|
cmStateEnums::RuntimeBinaryArtifact, true);
|
|
}
|
|
properties[prop] = value;
|
|
}
|
|
|
|
// Add the import library for windows DLLs.
|
|
if (target->HasImportLibrary(config)) {
|
|
std::string prop = cmStrCat("IMPORTED_IMPLIB", suffix);
|
|
std::string value =
|
|
target->GetFullPath(config, cmStateEnums::ImportLibraryArtifact, true);
|
|
if (mf->GetDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX")) {
|
|
target->GetImplibGNUtoMS(config, value, value,
|
|
"${CMAKE_IMPORT_LIBRARY_SUFFIX}");
|
|
}
|
|
properties[prop] = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool cmExportBuildFileGenerator::CollectExports(
|
|
std::function<void(cmGeneratorTarget const*)> visitor)
|
|
{
|
|
auto pred = [&](cmExportBuildFileGenerator::TargetExport& tei) -> bool {
|
|
cmGeneratorTarget* te = this->LG->FindGeneratorTargetToUse(tei.Name);
|
|
if (this->ExportedTargets.insert(te).second) {
|
|
this->Exports.emplace_back(te, tei.XcFrameworkLocation);
|
|
visitor(te);
|
|
return true;
|
|
}
|
|
|
|
this->ComplainAboutDuplicateTarget(te->GetName());
|
|
return false;
|
|
};
|
|
|
|
std::vector<TargetExport> targets;
|
|
this->GetTargets(targets);
|
|
return std::all_of(targets.begin(), targets.end(), pred);
|
|
}
|
|
|
|
void cmExportBuildFileGenerator::HandleMissingTarget(
|
|
std::string& link_libs, cmGeneratorTarget const* depender,
|
|
cmGeneratorTarget* dependee)
|
|
{
|
|
// The target is not in the export.
|
|
if (!this->AppendMode) {
|
|
auto const& exportInfo = this->FindExportInfo(dependee);
|
|
|
|
if (exportInfo.Namespaces.size() == 1 && exportInfo.Sets.size() == 1) {
|
|
std::string missingTarget = *exportInfo.Namespaces.begin();
|
|
|
|
missingTarget += dependee->GetExportName();
|
|
link_libs += missingTarget;
|
|
this->MissingTargets.emplace_back(std::move(missingTarget));
|
|
return;
|
|
}
|
|
// We are not appending, so all exported targets should be
|
|
// known here. This is probably user-error.
|
|
this->ComplainAboutMissingTarget(depender, dependee, exportInfo);
|
|
}
|
|
// Assume the target will be exported by another command.
|
|
// Append it with the export namespace.
|
|
link_libs += this->Namespace;
|
|
link_libs += dependee->GetExportName();
|
|
}
|
|
|
|
void cmExportBuildFileGenerator::GetTargets(
|
|
std::vector<TargetExport>& targets) const
|
|
{
|
|
if (this->ExportSet) {
|
|
for (std::unique_ptr<cmTargetExport> const& te :
|
|
this->ExportSet->GetTargetExports()) {
|
|
if (te->NamelinkOnly) {
|
|
continue;
|
|
}
|
|
targets.emplace_back(te->TargetName, te->XcFrameworkLocation);
|
|
}
|
|
return;
|
|
}
|
|
targets = this->Targets;
|
|
}
|
|
|
|
cmExportFileGenerator::ExportInfo cmExportBuildFileGenerator::FindExportInfo(
|
|
cmGeneratorTarget const* target) const
|
|
{
|
|
return target->GetLocalGenerator()
|
|
->GetGlobalGenerator()
|
|
->FindBuildExportInfo(target);
|
|
}
|
|
|
|
cm::optional<cmExportBuildFileGenerator::ExportRecord>
|
|
cmExportBuildFileGenerator::FindRecordForTarget(
|
|
cmGeneratorTarget const* target) const
|
|
{
|
|
auto const& name = target->GetName();
|
|
std::vector<TargetExport> targets;
|
|
this->GetTargets(targets);
|
|
bool const contains =
|
|
std::any_of(targets.begin(), targets.end(),
|
|
[&name](TargetExport const& te) { return te.Name == name; });
|
|
cm::optional<ExportRecord> result;
|
|
if (contains) {
|
|
ExportRecord rec;
|
|
rec.Name = this->ExportSet ? this->ExportSet->GetName() : std::string{};
|
|
rec.Namespace = this->GetNamespace();
|
|
result = rec;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void cmExportBuildFileGenerator::ComplainAboutMissingTarget(
|
|
cmGeneratorTarget const* depender, cmGeneratorTarget const* dependee,
|
|
ExportInfo const& exportInfo) const
|
|
{
|
|
std::ostringstream e;
|
|
e << "export called with target \"" << depender->GetName()
|
|
<< "\" which requires target \"" << dependee->GetName() << "\" ";
|
|
if (exportInfo.Sets.empty()) {
|
|
e << "that is not in any export set.";
|
|
} else {
|
|
if (exportInfo.Sets.size() == 1) {
|
|
e << "that is not in this export set, but in another export set which "
|
|
"is "
|
|
"exported multiple times with different namespaces: ";
|
|
} else {
|
|
e << "that is not in this export set, but in multiple other export "
|
|
"sets: ";
|
|
}
|
|
e << cmJoin(exportInfo.Files, ", ") << ".\n"
|
|
<< "An exported target cannot depend upon another target which is "
|
|
"exported in more than one export set or with more than one "
|
|
"namespace. Consider consolidating the exports of the \""
|
|
<< dependee->GetName() << "\" target to a single export.";
|
|
}
|
|
|
|
this->ReportError(e.str());
|
|
}
|
|
|
|
void cmExportBuildFileGenerator::ComplainAboutDuplicateTarget(
|
|
std::string const& targetName) const
|
|
{
|
|
std::ostringstream e;
|
|
e << "given target \"" << targetName << "\" more than once.";
|
|
this->ReportError(e.str());
|
|
}
|
|
|
|
void cmExportBuildFileGenerator::IssueMessage(MessageType type,
|
|
std::string const& message) const
|
|
{
|
|
this->LG->GetMakefile()->IssueMessage(type, message);
|
|
}
|
|
|
|
void cmExportBuildFileGenerator::IssueDiagnostic(
|
|
cmDiagnosticCategory category, std::string const& message) const
|
|
{
|
|
this->LG->GetMakefile()->IssueDiagnostic(category, message, this->Context);
|
|
}
|
|
|
|
std::string cmExportBuildFileGenerator::InstallNameDir(
|
|
cmGeneratorTarget const* target, std::string const& config)
|
|
{
|
|
std::string install_name_dir;
|
|
|
|
cmMakefile* mf = target->Target->GetMakefile();
|
|
if (mf->IsOn("CMAKE_PLATFORM_HAS_INSTALLNAME")) {
|
|
install_name_dir = target->GetInstallNameDirForBuildTree(config);
|
|
}
|
|
|
|
return install_name_dir;
|
|
}
|
|
|
|
bool cmExportBuildFileGenerator::PopulateInterfaceProperties(
|
|
cmGeneratorTarget const* target, ImportPropertyMap& properties)
|
|
{
|
|
this->PopulateInterfaceProperty("INTERFACE_INCLUDE_DIRECTORIES", target,
|
|
cmGeneratorExpression::BuildInterface,
|
|
properties);
|
|
this->PopulateInterfaceProperty("INTERFACE_LINK_DIRECTORIES", target,
|
|
cmGeneratorExpression::BuildInterface,
|
|
properties);
|
|
this->PopulateInterfaceProperty("INTERFACE_LINK_DEPENDS", target,
|
|
cmGeneratorExpression::BuildInterface,
|
|
properties);
|
|
this->PopulateInterfaceProperty("INTERFACE_SOURCES", target,
|
|
cmGeneratorExpression::BuildInterface,
|
|
properties);
|
|
|
|
return this->PopulateInterfaceProperties(
|
|
target, {}, cmGeneratorExpression::BuildInterface, properties);
|
|
}
|
|
|
|
bool cmExportBuildFileGenerator::PopulateFileSetInterfaceProperties(
|
|
cmGeneratorTarget const* target, ImportFileSetPropertyMap& properties)
|
|
{
|
|
cmGeneratorFileSets const* const gfs = target->GetGeneratorFileSets();
|
|
bool result = true;
|
|
|
|
for (auto const& type : gfs->GetInterfaceFileSetTypes()) {
|
|
for (auto const* fileSet : gfs->GetInterfaceFileSets(type)) {
|
|
ImportPropertyMap& fsProperties = properties[fileSet->GetName()];
|
|
this->PopulateFileSetInterfaceProperty(
|
|
"INTERFACE_INCLUDE_DIRECTORIES", target, fileSet,
|
|
cmGeneratorExpression::BuildInterface, fsProperties);
|
|
result = result &&
|
|
this->PopulateFileSetInterfaceProperties(
|
|
target, fileSet, cmGeneratorExpression::InstallInterface,
|
|
fsProperties);
|
|
}
|
|
}
|
|
return result;
|
|
}
|