mirror of
https://github.com/reactos/CMake.git
synced 2024-11-30 23:10:26 +00:00
354 lines
13 KiB
C++
354 lines
13 KiB
C++
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file Copyright.txt or https://cmake.org/licensing for details. */
|
|
#include "cmExportBuildFileGenerator.h"
|
|
|
|
#include <map>
|
|
#include <memory>
|
|
#include <set>
|
|
#include <sstream>
|
|
#include <utility>
|
|
|
|
#include "cmAlgorithms.h"
|
|
#include "cmExportSet.h"
|
|
#include "cmGeneratorExpression.h"
|
|
#include "cmGeneratorTarget.h"
|
|
#include "cmGlobalGenerator.h"
|
|
#include "cmLocalGenerator.h"
|
|
#include "cmMakefile.h"
|
|
#include "cmMessageType.h"
|
|
#include "cmPolicies.h"
|
|
#include "cmStateTypes.h"
|
|
#include "cmStringAlgorithms.h"
|
|
#include "cmTarget.h"
|
|
#include "cmTargetExport.h"
|
|
#include "cmake.h"
|
|
|
|
class cmSourceFile;
|
|
|
|
cmExportBuildFileGenerator::cmExportBuildFileGenerator()
|
|
{
|
|
this->LG = nullptr;
|
|
this->ExportSet = nullptr;
|
|
}
|
|
|
|
void cmExportBuildFileGenerator::Compute(cmLocalGenerator* lg)
|
|
{
|
|
this->LG = lg;
|
|
if (this->ExportSet) {
|
|
this->ExportSet->Compute(lg);
|
|
}
|
|
}
|
|
|
|
bool cmExportBuildFileGenerator::GenerateMainFile(std::ostream& os)
|
|
{
|
|
{
|
|
std::string expectedTargets;
|
|
std::string sep;
|
|
std::vector<std::string> targets;
|
|
bool generatedInterfaceRequired = false;
|
|
this->GetTargets(targets);
|
|
for (std::string const& tei : targets) {
|
|
cmGeneratorTarget* te = this->LG->FindGeneratorTargetToUse(tei);
|
|
expectedTargets += sep + this->Namespace + te->GetExportName();
|
|
sep = " ";
|
|
if (this->ExportedTargets.insert(te).second) {
|
|
this->Exports.push_back(te);
|
|
} else {
|
|
std::ostringstream e;
|
|
e << "given target \"" << te->GetName() << "\" more than once.";
|
|
this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
|
|
MessageType::FATAL_ERROR, e.str(),
|
|
this->LG->GetMakefile()->GetBacktrace());
|
|
return false;
|
|
}
|
|
generatedInterfaceRequired |=
|
|
this->GetExportTargetType(te) == cmStateEnums::INTERFACE_LIBRARY;
|
|
}
|
|
|
|
if (generatedInterfaceRequired) {
|
|
this->GenerateRequiredCMakeVersion(os, "3.0.0");
|
|
}
|
|
this->GenerateExpectedTargetsCode(os, expectedTargets);
|
|
}
|
|
|
|
std::vector<std::string> missingTargets;
|
|
|
|
// Create all the imported targets.
|
|
for (cmGeneratorTarget* gte : this->Exports) {
|
|
this->GenerateImportTargetCode(os, gte, this->GetExportTargetType(gte));
|
|
|
|
gte->Target->AppendBuildInterfaceIncludes();
|
|
|
|
ImportPropertyMap properties;
|
|
|
|
this->PopulateInterfaceProperty("INTERFACE_INCLUDE_DIRECTORIES", gte,
|
|
cmGeneratorExpression::BuildInterface,
|
|
properties, missingTargets);
|
|
this->PopulateInterfaceProperty("INTERFACE_SOURCES", gte,
|
|
cmGeneratorExpression::BuildInterface,
|
|
properties, missingTargets);
|
|
this->PopulateInterfaceProperty("INTERFACE_COMPILE_DEFINITIONS", gte,
|
|
cmGeneratorExpression::BuildInterface,
|
|
properties, missingTargets);
|
|
this->PopulateInterfaceProperty("INTERFACE_COMPILE_OPTIONS", gte,
|
|
cmGeneratorExpression::BuildInterface,
|
|
properties, missingTargets);
|
|
this->PopulateInterfaceProperty("INTERFACE_PRECOMPILE_HEADERS", gte,
|
|
cmGeneratorExpression::BuildInterface,
|
|
properties, missingTargets);
|
|
this->PopulateInterfaceProperty("INTERFACE_AUTOUIC_OPTIONS", gte,
|
|
cmGeneratorExpression::BuildInterface,
|
|
properties, missingTargets);
|
|
this->PopulateInterfaceProperty("INTERFACE_COMPILE_FEATURES", gte,
|
|
cmGeneratorExpression::BuildInterface,
|
|
properties, missingTargets);
|
|
this->PopulateInterfaceProperty("INTERFACE_LINK_OPTIONS", gte,
|
|
cmGeneratorExpression::BuildInterface,
|
|
properties, missingTargets);
|
|
this->PopulateInterfaceProperty("INTERFACE_LINK_DIRECTORIES", gte,
|
|
cmGeneratorExpression::BuildInterface,
|
|
properties, missingTargets);
|
|
this->PopulateInterfaceProperty("INTERFACE_LINK_DEPENDS", gte,
|
|
cmGeneratorExpression::BuildInterface,
|
|
properties, missingTargets);
|
|
this->PopulateInterfaceProperty("INTERFACE_POSITION_INDEPENDENT_CODE", gte,
|
|
properties);
|
|
|
|
std::string errorMessage;
|
|
if (!this->PopulateExportProperties(gte, properties, errorMessage)) {
|
|
this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
|
|
MessageType::FATAL_ERROR, errorMessage,
|
|
this->LG->GetMakefile()->GetBacktrace());
|
|
return false;
|
|
}
|
|
|
|
const bool newCMP0022Behavior =
|
|
gte->GetPolicyStatusCMP0022() != cmPolicies::WARN &&
|
|
gte->GetPolicyStatusCMP0022() != cmPolicies::OLD;
|
|
if (newCMP0022Behavior) {
|
|
this->PopulateInterfaceLinkLibrariesProperty(
|
|
gte, cmGeneratorExpression::BuildInterface, properties,
|
|
missingTargets);
|
|
}
|
|
this->PopulateCompatibleInterfaceProperties(gte, properties);
|
|
|
|
this->GenerateInterfaceProperties(gte, os, properties);
|
|
}
|
|
|
|
// Generate import file content for each configuration.
|
|
for (std::string const& c : this->Configurations) {
|
|
this->GenerateImportConfig(os, c, missingTargets);
|
|
}
|
|
|
|
this->GenerateMissingTargetsCheckCode(os, missingTargets);
|
|
|
|
return true;
|
|
}
|
|
|
|
void cmExportBuildFileGenerator::GenerateImportTargetsConfig(
|
|
std::ostream& os, const std::string& config, std::string const& suffix,
|
|
std::vector<std::string>& missingTargets)
|
|
{
|
|
for (cmGeneratorTarget* target : this->Exports) {
|
|
// Collect import properties for this target.
|
|
ImportPropertyMap properties;
|
|
|
|
if (this->GetExportTargetType(target) != cmStateEnums::INTERFACE_LIBRARY) {
|
|
this->SetImportLocationProperty(config, suffix, target, properties);
|
|
}
|
|
if (!properties.empty()) {
|
|
// Get the rest of the target details.
|
|
if (this->GetExportTargetType(target) !=
|
|
cmStateEnums::INTERFACE_LIBRARY) {
|
|
this->SetImportDetailProperties(config, suffix, target, properties,
|
|
missingTargets);
|
|
this->SetImportLinkInterface(config, suffix,
|
|
cmGeneratorExpression::BuildInterface,
|
|
target, properties, missingTargets);
|
|
}
|
|
|
|
// TODO: PUBLIC_HEADER_LOCATION
|
|
// This should wait until the build feature propagation stuff
|
|
// is done. Then this can be a propagated include directory.
|
|
// this->GenerateImportProperty(config, te->HeaderGenerator,
|
|
// properties);
|
|
|
|
// Generate code in the export file.
|
|
this->GenerateImportPropertyCode(os, config, target, properties);
|
|
}
|
|
}
|
|
}
|
|
|
|
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 &&
|
|
!this->LG->GetGlobalGenerator()->HasKnownObjectFileLocation(nullptr)) {
|
|
targetType = cmStateEnums::INTERFACE_LIBRARY;
|
|
}
|
|
return targetType;
|
|
}
|
|
|
|
void cmExportBuildFileGenerator::SetExportSet(cmExportSet* exportSet)
|
|
{
|
|
this->ExportSet = exportSet;
|
|
}
|
|
|
|
void cmExportBuildFileGenerator::SetImportLocationProperty(
|
|
const std::string& 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) {
|
|
const std::string& obj = target->GetObjectName(sf);
|
|
objects.push_back(obj_dir + obj);
|
|
}
|
|
|
|
// Store the property.
|
|
properties[prop] = cmJoin(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);
|
|
if (mf->GetDefinition("CMAKE_IMPORT_LIBRARY_SUFFIX")) {
|
|
target->GetImplibGNUtoMS(config, value, value,
|
|
"${CMAKE_IMPORT_LIBRARY_SUFFIX}");
|
|
}
|
|
properties[prop] = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
void cmExportBuildFileGenerator::HandleMissingTarget(
|
|
std::string& link_libs, std::vector<std::string>& missingTargets,
|
|
cmGeneratorTarget* depender, cmGeneratorTarget* dependee)
|
|
{
|
|
// The target is not in the export.
|
|
if (!this->AppendMode) {
|
|
const std::string name = dependee->GetName();
|
|
cmGlobalGenerator* gg =
|
|
dependee->GetLocalGenerator()->GetGlobalGenerator();
|
|
auto exportInfo = this->FindBuildExportInfo(gg, name);
|
|
std::vector<std::string> const& exportFiles = exportInfo.first;
|
|
|
|
if (exportFiles.size() == 1) {
|
|
std::string missingTarget = exportInfo.second;
|
|
|
|
missingTarget += dependee->GetExportName();
|
|
link_libs += missingTarget;
|
|
missingTargets.push_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, exportFiles);
|
|
}
|
|
// 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<std::string>& targets) const
|
|
{
|
|
if (this->ExportSet) {
|
|
for (std::unique_ptr<cmTargetExport> const& te :
|
|
this->ExportSet->GetTargetExports()) {
|
|
targets.push_back(te->TargetName);
|
|
}
|
|
return;
|
|
}
|
|
targets = this->Targets;
|
|
}
|
|
|
|
std::pair<std::vector<std::string>, std::string>
|
|
cmExportBuildFileGenerator::FindBuildExportInfo(cmGlobalGenerator* gg,
|
|
const std::string& name)
|
|
{
|
|
std::vector<std::string> exportFiles;
|
|
std::string ns;
|
|
|
|
auto& exportSets = gg->GetBuildExportSets();
|
|
|
|
for (auto const& exp : exportSets) {
|
|
const auto& exportSet = exp.second;
|
|
std::vector<std::string> targets;
|
|
exportSet->GetTargets(targets);
|
|
if (cmContains(targets, name)) {
|
|
exportFiles.push_back(exp.first);
|
|
ns = exportSet->GetNamespace();
|
|
}
|
|
}
|
|
|
|
return { exportFiles, ns };
|
|
}
|
|
|
|
void cmExportBuildFileGenerator::ComplainAboutMissingTarget(
|
|
cmGeneratorTarget* depender, cmGeneratorTarget* dependee,
|
|
std::vector<std::string> const& exportFiles)
|
|
{
|
|
std::ostringstream e;
|
|
e << "export called with target \"" << depender->GetName()
|
|
<< "\" which requires target \"" << dependee->GetName() << "\" ";
|
|
if (exportFiles.empty()) {
|
|
e << "that is not in any export set.";
|
|
} else {
|
|
e << "that is not in this export set, but in multiple other export sets: "
|
|
<< cmJoin(exportFiles, ", ") << ".\n";
|
|
e << "An exported target cannot depend upon another target which is "
|
|
"exported multiple times. Consider consolidating the exports of the "
|
|
"\""
|
|
<< dependee->GetName() << "\" target to a single export.";
|
|
}
|
|
|
|
this->LG->GetGlobalGenerator()->GetCMakeInstance()->IssueMessage(
|
|
MessageType::FATAL_ERROR, e.str(),
|
|
this->LG->GetMakefile()->GetBacktrace());
|
|
}
|
|
|
|
std::string cmExportBuildFileGenerator::InstallNameDir(
|
|
cmGeneratorTarget* target, const std::string& 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;
|
|
}
|