Files
CMake/Source/cmSourceGroup.cxx
Cristiano Carvalheiro a46491d0d3 cmLocalGenerator: Improve performance of source group lookups
Introduce an index from source file to source group in
`cmLocalGenerator` to make lookups significantly more efficient
during the source group generation, which could be particularly
slow when dealing with thousands of source files.
Falls back to the recursive lookup when the source file is not present
on the index, which is more likely to happen when dealing with
regex-based sources.

Fixes: #27359
2025-11-26 20:32:19 +00:00

199 lines
4.8 KiB
C++

/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmSourceGroup.h"
#include <utility>
#include <cm/memory>
#include "cmGeneratorExpression.h"
#include "cmSourceFile.h"
#include "cmStringAlgorithms.h"
class cmSourceGroupInternals
{
public:
SourceGroupVector GroupChildren;
};
cmSourceGroup::cmSourceGroup(std::string name, char const* regex,
char const* parentName)
: Name(std::move(name))
{
this->Internal = cm::make_unique<cmSourceGroupInternals>();
this->SetGroupRegex(regex);
if (parentName) {
this->FullName = cmStrCat(parentName, '\\');
}
this->FullName += this->Name;
}
cmSourceGroup::~cmSourceGroup() = default;
void cmSourceGroup::SetGroupRegex(char const* regex)
{
if (regex) {
this->GroupRegex.compile(regex);
} else {
this->GroupRegex.compile("^$");
}
}
void cmSourceGroup::ResolveGenex(cmLocalGenerator* lg,
std::string const& config)
{
std::set<std::string> files;
for (std::string const& file : this->GroupFiles) {
files.emplace(cmGeneratorExpression::Evaluate(file, lg, config));
}
this->GroupFiles = std::move(files);
if (!this->Internal) {
return;
}
for (auto const& group : this->Internal->GroupChildren) {
group->ResolveGenex(lg, config);
}
}
void cmSourceGroup::AddGroupFile(std::string const& name)
{
this->GroupFiles.insert(name);
}
std::string const& cmSourceGroup::GetName() const
{
return this->Name;
}
std::string const& cmSourceGroup::GetFullName() const
{
return this->FullName;
}
bool cmSourceGroup::MatchesRegex(std::string const& name) const
{
cmsys::RegularExpressionMatch match;
return this->GroupRegex.find(name.c_str(), match);
}
bool cmSourceGroup::MatchesFiles(std::string const& name) const
{
return this->GroupFiles.find(name) != this->GroupFiles.cend();
}
void cmSourceGroup::AssignSource(cmSourceFile const* sf)
{
this->SourceFiles.push_back(sf);
}
std::set<std::string> const& cmSourceGroup::GetGroupFiles() const
{
return this->GroupFiles;
}
std::vector<cmSourceFile const*> const& cmSourceGroup::GetSourceFiles() const
{
return this->SourceFiles;
}
void cmSourceGroup::AddChild(std::unique_ptr<cmSourceGroup> child)
{
this->Internal->GroupChildren.push_back(std::move(child));
}
cmSourceGroup* cmSourceGroup::LookupChild(std::string const& name) const
{
for (auto& group : this->Internal->GroupChildren) {
// look if descendant is the one we're looking for
if (group->GetName() == name) {
return group.get(); // if so return it
}
}
// if no child with this name was found return NULL
return nullptr;
}
cmSourceGroup* cmSourceGroup::MatchChildrenFiles(std::string const& name)
{
if (this->MatchesFiles(name)) {
return this;
}
for (auto& group : this->Internal->GroupChildren) {
cmSourceGroup* result = group->MatchChildrenFiles(name);
if (result) {
return result;
}
}
return nullptr;
}
cmSourceGroup const* cmSourceGroup::MatchChildrenFiles(
std::string const& name) const
{
if (this->MatchesFiles(name)) {
return this;
}
for (auto const& group : this->Internal->GroupChildren) {
cmSourceGroup const* result = group->MatchChildrenFiles(name);
if (result) {
return result;
}
}
return nullptr;
}
cmSourceGroup* cmSourceGroup::MatchChildrenRegex(std::string const& name) const
{
for (auto& group : this->Internal->GroupChildren) {
cmSourceGroup* result = group->MatchChildrenRegex(name);
if (result) {
return result;
}
}
if (this->MatchesRegex(name)) {
return const_cast<cmSourceGroup*>(this);
}
return nullptr;
}
SourceGroupVector const& cmSourceGroup::GetGroupChildren() const
{
return this->Internal->GroupChildren;
}
/**
* Find a source group whose regular expression matches the filename
* part of the given source name. Search backward through the list of
* source groups, and take the first matching group found. This way
* non-inherited SOURCE_GROUP commands will have precedence over
* inherited ones.
*/
cmSourceGroup* cmSourceGroup::FindSourceGroup(std::string const& source,
SourceGroupVector const& groups)
{
// First search for a group that lists the file explicitly.
for (auto sg = groups.rbegin(); sg != groups.rend(); ++sg) {
cmSourceGroup* result = (*sg)->MatchChildrenFiles(source);
if (result) {
return result;
}
}
// Now search for a group whose regex matches the file.
for (auto sg = groups.rbegin(); sg != groups.rend(); ++sg) {
cmSourceGroup* result = (*sg)->MatchChildrenRegex(source);
if (result) {
return result;
}
}
// Shouldn't get here, but just in case, return the default group.
return groups.data()->get();
}