1
0
mirror of https://github.com/Kitware/CMake.git synced 2025-10-18 08:51:52 +08:00
Files
CMake/Source/cmGeneratorTarget_Sources.cxx
Craig Scott 1b764b6816 cmLinkItem: Remove redundant cmLinkImplItem class
The cmLinkImplItem class used to contain some additional members
to support the CMP0027 policy. Since the CMP0027 policy and those
associated members were removed, the cmLinkImplItem class has been
a plain wrapper around cmLinkItem with no additional members.
It is no longer needed and cmLinkItem can be used directly wherever
cmLinkImplItem was used previously.
2025-10-07 10:44:28 -04:00

525 lines
18 KiB
C++

/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
/* clang-format off */
#include "cmGeneratorTarget.h"
/* clang-format on */
#include <cstddef>
#include <map>
#include <memory>
#include <set>
#include <sstream>
#include <string>
#include <type_traits>
#include <unordered_set>
#include <utility>
#include <vector>
#include <cm/string_view>
#include <cmext/algorithm>
#include <cmext/string_view>
#include "cmsys/RegularExpression.hxx"
#include "cmAlgorithms.h"
#include "cmEvaluatedTargetProperty.h"
#include "cmFileSet.h"
#include "cmGenExContext.h"
#include "cmGeneratorExpression.h"
#include "cmGeneratorExpressionDAGChecker.h"
#include "cmGlobalGenerator.h"
#include "cmLinkItem.h"
#include "cmList.h"
#include "cmListFileCache.h"
#include "cmLocalGenerator.h"
#include "cmMakefile.h"
#include "cmMessageType.h"
#include "cmSourceFile.h"
#include "cmSourceFileLocation.h"
#include "cmSourceGroup.h"
#include "cmStateTypes.h"
#include "cmSystemTools.h"
#include "cmTarget.h"
#include "cmValue.h"
#include "cmake.h"
namespace {
using UseTo = cmGeneratorTarget::UseTo;
void AddObjectEntries(cmGeneratorTarget const* headTarget,
cm::GenEx::Context const& context,
cmGeneratorExpressionDAGChecker* dagChecker,
EvaluatedTargetPropertyEntries& entries)
{
if (cmLinkImplementationLibraries const* impl =
headTarget->GetLinkImplementationLibraries(context.Config,
UseTo::Link)) {
entries.HadContextSensitiveCondition = impl->HadContextSensitiveCondition;
for (cmLinkItem const& lib : impl->Libraries) {
if (lib.Target &&
lib.Target->GetType() == cmStateEnums::OBJECT_LIBRARY) {
std::string uniqueName =
headTarget->GetGlobalGenerator()->IndexGeneratorTargetUniquely(
lib.Target);
std::string genex = "$<TARGET_OBJECTS:" + std::move(uniqueName) + ">";
cmGeneratorExpression ge(*headTarget->Makefile->GetCMakeInstance(),
lib.Backtrace);
std::unique_ptr<cmCompiledGeneratorExpression> cge = ge.Parse(genex);
cge->SetEvaluateForBuildsystem(true);
EvaluatedTargetPropertyEntry ee(lib, lib.Backtrace);
cmExpandList(cge->Evaluate(context, dagChecker, headTarget),
ee.Values);
if (cge->GetHadContextSensitiveCondition()) {
ee.ContextDependent = true;
}
entries.Entries.emplace_back(std::move(ee));
}
}
}
}
void addFileSetEntry(cmGeneratorTarget const* headTarget,
cm::GenEx::Context const& context,
cmGeneratorExpressionDAGChecker* dagChecker,
cmFileSet const* fileSet,
EvaluatedTargetPropertyEntries& entries)
{
auto dirCges = fileSet->CompileDirectoryEntries();
auto dirs = fileSet->EvaluateDirectoryEntries(dirCges, context, headTarget,
dagChecker);
bool contextSensitiveDirs = false;
for (auto const& dirCge : dirCges) {
if (dirCge->GetHadContextSensitiveCondition()) {
contextSensitiveDirs = true;
break;
}
}
cmake* cm = headTarget->GetLocalGenerator()->GetCMakeInstance();
for (auto& entryCge : fileSet->CompileFileEntries()) {
auto targetPropEntry =
cmGeneratorTarget::TargetPropertyEntry::CreateFileSet(
dirs, contextSensitiveDirs, std::move(entryCge), fileSet);
entries.Entries.emplace_back(EvaluateTargetPropertyEntry(
headTarget, context, dagChecker, *targetPropEntry));
EvaluatedTargetPropertyEntry const& entry = entries.Entries.back();
for (auto const& file : entry.Values) {
auto* sf = headTarget->Makefile->GetOrCreateSource(file);
if (fileSet->GetType() == "HEADERS"_s) {
sf->SetProperty("HEADER_FILE_ONLY", "TRUE");
}
#ifndef CMAKE_BOOTSTRAP
std::string e;
std::string w;
auto path = sf->ResolveFullPath(&e, &w);
if (!w.empty()) {
cm->IssueMessage(MessageType::AUTHOR_WARNING, w, entry.Backtrace);
}
if (path.empty()) {
if (!e.empty()) {
cm->IssueMessage(MessageType::FATAL_ERROR, e, entry.Backtrace);
}
return;
}
bool found = false;
for (auto const& sg : headTarget->Makefile->GetSourceGroups()) {
if (sg.MatchChildrenFiles(path)) {
found = true;
break;
}
}
if (!found) {
if (fileSet->GetType() == "HEADERS"_s) {
headTarget->Makefile->GetOrCreateSourceGroup("Header Files")
->AddGroupFile(path);
}
}
#endif
}
}
}
void AddFileSetEntries(cmGeneratorTarget const* headTarget,
cm::GenEx::Context const& context,
cmGeneratorExpressionDAGChecker* dagChecker,
EvaluatedTargetPropertyEntries& entries)
{
for (auto const& entry : headTarget->Target->GetHeaderSetsEntries()) {
for (auto const& name : cmList{ entry.Value }) {
auto const* headerSet = headTarget->Target->GetFileSet(name);
addFileSetEntry(headTarget, context, dagChecker, headerSet, entries);
}
}
for (auto const& entry : headTarget->Target->GetCxxModuleSetsEntries()) {
for (auto const& name : cmList{ entry.Value }) {
auto const* cxxModuleSet = headTarget->Target->GetFileSet(name);
addFileSetEntry(headTarget, context, dagChecker, cxxModuleSet, entries);
}
}
}
bool processSources(cmGeneratorTarget const* tgt,
EvaluatedTargetPropertyEntries& entries,
std::vector<BT<std::string>>& srcs,
std::unordered_set<std::string>& uniqueSrcs,
bool debugSources)
{
cmMakefile* mf = tgt->Target->GetMakefile();
bool contextDependent = entries.HadContextSensitiveCondition;
for (EvaluatedTargetPropertyEntry& entry : entries.Entries) {
if (entry.ContextDependent) {
contextDependent = true;
}
cmLinkItem const& item = entry.LinkItem;
std::string const& targetName = item.AsStr();
for (std::string& src : entry.Values) {
cmSourceFile* sf = mf->GetOrCreateSource(src);
std::string e;
std::string w;
std::string fullPath = sf->ResolveFullPath(&e, &w);
cmake* cm = tgt->GetLocalGenerator()->GetCMakeInstance();
if (!w.empty()) {
cm->IssueMessage(MessageType::AUTHOR_WARNING, w, entry.Backtrace);
}
if (fullPath.empty()) {
if (!e.empty()) {
cm->IssueMessage(MessageType::FATAL_ERROR, e, entry.Backtrace);
}
return contextDependent;
}
if (!targetName.empty() && !cmSystemTools::FileIsFullPath(src)) {
std::ostringstream err;
if (!targetName.empty()) {
err << "Target \"" << targetName
<< "\" contains relative path in its INTERFACE_SOURCES:\n \""
<< src << "\"";
} else {
err << "Found relative path while evaluating sources of \""
<< tgt->GetName() << "\":\n \"" << src << "\"\n";
}
tgt->GetLocalGenerator()->IssueMessage(MessageType::FATAL_ERROR,
err.str());
return contextDependent;
}
src = fullPath;
}
std::string usedSources;
for (std::string const& src : entry.Values) {
if (uniqueSrcs.insert(src).second) {
srcs.emplace_back(src, entry.Backtrace);
if (debugSources) {
usedSources += " * " + src + "\n";
}
}
}
if (!usedSources.empty()) {
tgt->GetLocalGenerator()->GetCMakeInstance()->IssueMessage(
MessageType::LOG,
std::string("Used sources for target ") + tgt->GetName() + ":\n" +
usedSources,
entry.Backtrace);
}
}
return contextDependent;
}
}
std::vector<BT<std::string>> cmGeneratorTarget::GetSourceFilePaths(
std::string const& config) const
{
std::vector<BT<std::string>> files;
cmList debugProperties{ this->Makefile->GetDefinition(
"CMAKE_DEBUG_TARGET_PROPERTIES") };
bool debugSources =
!this->DebugSourcesDone && cm::contains(debugProperties, "SOURCES");
this->DebugSourcesDone = true;
cm::GenEx::Context context(this->LocalGenerator, config,
/*language=*/std::string());
cmGeneratorExpressionDAGChecker dagChecker{
this, "SOURCES", nullptr, nullptr, context,
};
EvaluatedTargetPropertyEntries entries = EvaluateTargetPropertyEntries(
this, context, &dagChecker, this->SourceEntries);
std::unordered_set<std::string> uniqueSrcs;
bool contextDependentDirectSources =
processSources(this, entries, files, uniqueSrcs, debugSources);
// Collect INTERFACE_SOURCES of all direct link-dependencies.
EvaluatedTargetPropertyEntries linkInterfaceSourcesEntries;
AddInterfaceEntries(this, "INTERFACE_SOURCES", context, &dagChecker,
linkInterfaceSourcesEntries, IncludeRuntimeInterface::No,
UseTo::Compile);
bool contextDependentInterfaceSources = processSources(
this, linkInterfaceSourcesEntries, files, uniqueSrcs, debugSources);
// Collect TARGET_OBJECTS of direct object link-dependencies.
bool contextDependentObjects = false;
if (this->GetType() != cmStateEnums::OBJECT_LIBRARY) {
EvaluatedTargetPropertyEntries linkObjectsEntries;
AddObjectEntries(this, context, &dagChecker, linkObjectsEntries);
contextDependentObjects = processSources(this, linkObjectsEntries, files,
uniqueSrcs, debugSources);
// Note that for imported targets or multi-config generators supporting
// cross-config builds the paths to the object files must be per-config,
// so contextDependentObjects will be true here even if object libraries
// are specified without per-config generator expressions.
}
// Collect this target's file sets.
EvaluatedTargetPropertyEntries fileSetEntries;
AddFileSetEntries(this, context, &dagChecker, fileSetEntries);
bool contextDependentFileSets =
processSources(this, fileSetEntries, files, uniqueSrcs, debugSources);
// Determine if sources are context-dependent or not.
if (!contextDependentDirectSources && !contextDependentInterfaceSources &&
!contextDependentObjects && !contextDependentFileSets) {
this->SourcesAreContextDependent = Tribool::False;
} else {
this->SourcesAreContextDependent = Tribool::True;
}
return files;
}
void cmGeneratorTarget::GetSourceFiles(std::vector<cmSourceFile*>& files,
std::string const& config) const
{
std::vector<BT<cmSourceFile*>> tmp = this->GetSourceFiles(config);
files.reserve(tmp.size());
for (BT<cmSourceFile*>& v : tmp) {
files.push_back(v.Value);
}
}
std::vector<BT<cmSourceFile*>> cmGeneratorTarget::GetSourceFiles(
std::string const& config) const
{
std::vector<BT<cmSourceFile*>> files;
KindedSources const& kinded = this->GetKindedSources(config);
files.reserve(kinded.Sources.size());
for (SourceAndKind const& si : kinded.Sources) {
files.push_back(si.Source);
}
return files;
}
void cmGeneratorTarget::GetSourceFilesWithoutObjectLibraries(
std::vector<cmSourceFile*>& files, std::string const& config) const
{
std::vector<BT<cmSourceFile*>> tmp =
this->GetSourceFilesWithoutObjectLibraries(config);
files.reserve(tmp.size());
for (BT<cmSourceFile*>& v : tmp) {
files.push_back(v.Value);
}
}
std::vector<BT<cmSourceFile*>>
cmGeneratorTarget::GetSourceFilesWithoutObjectLibraries(
std::string const& config) const
{
std::vector<BT<cmSourceFile*>> files;
KindedSources const& kinded = this->GetKindedSources(config);
files.reserve(kinded.Sources.size());
for (SourceAndKind const& si : kinded.Sources) {
if (si.Source.Value->GetObjectLibrary().empty()) {
files.push_back(si.Source);
}
}
return files;
}
cmGeneratorTarget::KindedSources const& cmGeneratorTarget::GetKindedSources(
std::string const& config) const
{
// If we already processed one configuration and found no dependency
// on configuration then always use the one result.
if (this->SourcesAreContextDependent == Tribool::False) {
return this->KindedSourcesMap.begin()->second;
}
// Lookup any existing link implementation for this configuration.
std::string const key = cmSystemTools::UpperCase(config);
auto it = this->KindedSourcesMap.find(key);
if (it != this->KindedSourcesMap.end()) {
if (!it->second.Initialized) {
std::ostringstream e;
e << "The SOURCES of \"" << this->GetName()
<< "\" use a generator expression that depends on the "
"SOURCES themselves.";
this->GlobalGenerator->GetCMakeInstance()->IssueMessage(
MessageType::FATAL_ERROR, e.str(), this->GetBacktrace());
static KindedSources empty;
return empty;
}
return it->second;
}
// Add an entry to the map for this configuration.
KindedSources& files = this->KindedSourcesMap[key];
this->ComputeKindedSources(files, config);
files.Initialized = true;
return files;
}
void cmGeneratorTarget::ComputeKindedSources(KindedSources& files,
std::string const& config) const
{
// Get the source file paths by string.
std::vector<BT<std::string>> srcs = this->GetSourceFilePaths(config);
cmsys::RegularExpression header_regex(CM_HEADER_REGEX);
std::vector<cmSourceFile*> badObjLib;
std::set<cmSourceFile*> emitted;
for (BT<std::string> const& s : srcs) {
// Create each source at most once.
cmSourceFile* sf = this->Makefile->GetOrCreateSource(s.Value);
if (!emitted.insert(sf).second) {
continue;
}
// Compute the kind (classification) of this source file.
SourceKind kind;
std::string ext = cmSystemTools::LowerCase(sf->GetExtension());
cmFileSet const* fs = this->GetFileSetForSource(config, sf);
if (sf->GetCustomCommand()) {
kind = SourceKindCustomCommand;
} else if (!this->Target->IsNormal() && !this->Target->IsImported() &&
fs && (fs->GetType() == "CXX_MODULES"_s)) {
kind = SourceKindCxxModuleSource;
} else if (this->Target->GetType() == cmStateEnums::UTILITY ||
this->Target->GetType() == cmStateEnums::INTERFACE_LIBRARY
// XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165
// NOLINTNEXTLINE(bugprone-branch-clone)
) {
kind = SourceKindExtra;
} else if (this->IsSourceFilePartOfUnityBatch(sf->ResolveFullPath())) {
kind = SourceKindUnityBatched;
// XXX(clang-tidy): https://bugs.llvm.org/show_bug.cgi?id=44165
// NOLINTNEXTLINE(bugprone-branch-clone)
} else if (sf->GetPropertyAsBool("HEADER_FILE_ONLY")) {
kind = SourceKindHeader;
} else if (sf->GetPropertyAsBool("EXTERNAL_OBJECT")) {
kind = SourceKindExternalObject;
} else if (!sf->GetOrDetermineLanguage().empty()) {
kind = SourceKindObjectSource;
} else if (ext == "def") {
kind = SourceKindModuleDefinition;
if (this->GetType() == cmStateEnums::OBJECT_LIBRARY) {
badObjLib.push_back(sf);
}
} else if (ext == "idl") {
kind = SourceKindIDL;
if (this->GetType() == cmStateEnums::OBJECT_LIBRARY) {
badObjLib.push_back(sf);
}
} else if (ext == "resx") {
kind = SourceKindResx;
} else if (ext == "appxmanifest") {
kind = SourceKindAppManifest;
} else if (ext == "manifest") {
if (sf->GetPropertyAsBool("VS_DEPLOYMENT_CONTENT")) {
kind = SourceKindExtra;
} else {
kind = SourceKindManifest;
}
} else if (ext == "pfx") {
kind = SourceKindCertificate;
} else if (ext == "xaml") {
kind = SourceKindXaml;
} else if (header_regex.find(sf->ResolveFullPath())) {
kind = SourceKindHeader;
} else {
kind = SourceKindExtra;
}
// Save this classified source file in the result vector.
files.Sources.push_back({ BT<cmSourceFile*>(sf, s.Backtrace), kind });
}
if (!badObjLib.empty()) {
std::ostringstream e;
e << "OBJECT library \"" << this->GetName() << "\" contains:\n";
for (cmSourceFile* i : badObjLib) {
e << " " << i->GetLocation().GetName() << "\n";
}
e << "but may contain only sources that compile, header files, and "
"other files that would not affect linking of a normal library.";
this->GlobalGenerator->GetCMakeInstance()->IssueMessage(
MessageType::FATAL_ERROR, e.str(), this->GetBacktrace());
}
}
std::vector<cmGeneratorTarget::AllConfigSource> const&
cmGeneratorTarget::GetAllConfigSources() const
{
if (this->AllConfigSources.empty()) {
this->ComputeAllConfigSources();
}
return this->AllConfigSources;
}
void cmGeneratorTarget::ComputeAllConfigSources() const
{
std::vector<std::string> configs =
this->Makefile->GetGeneratorConfigs(cmMakefile::IncludeEmptyConfig);
std::map<cmSourceFile const*, size_t> index;
for (size_t ci = 0; ci < configs.size(); ++ci) {
KindedSources const& sources = this->GetKindedSources(configs[ci]);
for (SourceAndKind const& src : sources.Sources) {
auto mi = index.find(src.Source.Value);
if (mi == index.end()) {
AllConfigSource acs;
acs.Source = src.Source.Value;
acs.Kind = src.Kind;
this->AllConfigSources.push_back(std::move(acs));
std::map<cmSourceFile const*, size_t>::value_type entry(
src.Source.Value, this->AllConfigSources.size() - 1);
mi = index.insert(entry).first;
}
this->AllConfigSources[mi->second].Configs.push_back(ci);
}
}
}
std::vector<cmGeneratorTarget::AllConfigSource>
cmGeneratorTarget::GetAllConfigSources(SourceKind kind) const
{
std::vector<AllConfigSource> result;
for (AllConfigSource const& source : this->GetAllConfigSources()) {
if (source.Kind == kind) {
result.push_back(source);
}
}
return result;
}
std::set<std::string> cmGeneratorTarget::GetAllConfigCompileLanguages() const
{
std::set<std::string> languages;
std::vector<AllConfigSource> const& sources = this->GetAllConfigSources();
for (AllConfigSource const& si : sources) {
std::string const& lang = si.Source->GetOrDetermineLanguage();
if (!lang.empty()) {
languages.emplace(lang);
}
}
return languages;
}