cmGeneratorTarget: Refactor header set verification for C and CXX

Split GenerateHeaderSetVerificationFile into three focused helpers:
- ResolveHeaderLanguage: determines the language for a header; returns
  early for headers with an explicit LANGUAGE property (validated
  against the supported set), and otherwise computes and caches the
  target's default language using a lattice join (CXX > C) over target
  sources and globally enabled languages.
- GenerateStubForLanguage: maps a resolved language to a stub file
  extension (.c or .cxx) and creates the verification source.
- GenerateHeaderSetVerificationFile: orchestrates the above, moving
  the SKIP_LINTING guard here and switching the return type to
  cm::optional<std::string> so callers can distinguish "no file" from
  an empty path.

No behavior change; only C and CXX are supported at this point.
This commit is contained in:
Mickaël Germain authored and Brad King committed 2026-03-10 09:21:52 -04:00
1 parent e6f93056ce
commit f974ef0f75
2 files changed
+111 -53

No files matched your search

+101 -51
View File
@@ -5839,7 +5839,7 @@ bool cmGeneratorTarget::AddHeaderSetVerification()
return false;
}
cm::optional<std::set<std::string>> languages;
cm::optional<cm::optional<std::string>> defaultLanguage;
for (auto const* fileSet : fileSets) {
auto const& dirCges = fileSet->CompileDirectoryEntries();
auto const& fileCges = fileSet->CompileFileEntries();
@@ -5875,12 +5875,14 @@ bool cmGeneratorTarget::AddHeaderSetVerification()
for (auto const& files : filesPerDir) {
for (auto const& file : files.second) {
std::string filename = this->GenerateHeaderSetVerificationFile(
*this->Makefile->GetOrCreateSource(file), files.first,
verifyTargetName, languages);
if (filename.empty()) {
cm::optional<std::string> filenameOpt =
this->GenerateHeaderSetVerificationFile(
*this->Makefile->GetOrCreateSource(file), files.first,
verifyTargetName, defaultLanguage);
if (!filenameOpt) {
continue;
}
std::string filename = *filenameOpt;
if (!verifyTarget) {
{
@@ -5987,54 +5989,19 @@ bool cmGeneratorTarget::AddHeaderSetVerification()
return true;
}
std::string cmGeneratorTarget::GenerateHeaderSetVerificationFile(
cm::optional<std::string> cmGeneratorTarget::GenerateHeaderSetVerificationFile(
cmSourceFile& source, std::string const& dir,
std::string const& verifyTargetName,
cm::optional<std::set<std::string>>& languages) const
cm::optional<cm::optional<std::string>>& defaultLanguage) const
{
std::string extension;
std::string language = source.GetOrDetermineLanguage();
if (source.GetPropertyAsBool("SKIP_LINTING")) {
return std::string{};
return cm::nullopt;
}
if (language.empty()) {
if (!languages) {
languages.emplace();
for (auto const& tgtSource : this->GetAllConfigSources()) {
auto const& tgtSourceLanguage =
tgtSource.Source->GetOrDetermineLanguage();
if (tgtSourceLanguage == "CXX") {
languages->insert("CXX");
break; // C++ overrides everything else, so we don't need to keep
// checking.
}
if (tgtSourceLanguage == "C") {
languages->insert("C");
}
}
if (languages->empty()) {
std::vector<std::string> languagesVector;
this->GlobalGenerator->GetEnabledLanguages(languagesVector);
languages->insert(languagesVector.begin(), languagesVector.end());
}
}
if (languages->count("CXX")) {
language = "CXX";
} else if (languages->count("C")) {
language = "C";
}
}
if (language == "C") {
extension = ".c";
} else if (language == "CXX") {
extension = ".cxx";
} else {
return "";
cm::optional<std::string> language =
this->ResolveHeaderLanguage(source, defaultLanguage);
if (!language) {
return cm::nullopt;
}
std::string headerFilename = dir;
@@ -6043,10 +6010,93 @@ std::string cmGeneratorTarget::GenerateHeaderSetVerificationFile(
}
headerFilename += source.GetLocation().GetName();
auto filename =
return this->GenerateStubForLanguage(*language, headerFilename,
verifyTargetName, source);
}
cm::optional<std::string> cmGeneratorTarget::ResolveHeaderLanguage(
cmSourceFile& source,
cm::optional<cm::optional<std::string>>& defaultLanguage) const
{
static std::array<cm::string_view, 2> const supportedLangs{ {
"C",
"CXX",
} };
auto isSupported = [](cm::string_view lang) -> bool {
return std::find(supportedLangs.begin(), supportedLangs.end(), lang) !=
supportedLangs.end();
};
// If the source has an explicit language, validate and return it.
std::string language = source.GetOrDetermineLanguage();
if (!language.empty()) {
if (!isSupported(language)) {
return cm::nullopt;
}
return cm::optional<std::string>(std::move(language));
}
/*
Compute and cache the default language for unlanguaged headers.
The lattice join is run once per file set, not once per header.
Lattice: CXX > C
*/
if (!defaultLanguage) {
std::set<std::string> langs;
for (AllConfigSource const& tgtSource : this->GetAllConfigSources()) {
std::string const& lang = tgtSource.Source->GetOrDetermineLanguage();
if (isSupported(lang)) {
langs.insert(lang);
}
}
if (langs.empty()) {
std::vector<std::string> languagesVector;
this->GlobalGenerator->GetEnabledLanguages(languagesVector);
for (std::string const& lang : languagesVector) {
if (isSupported(lang)) {
langs.insert(lang);
}
}
}
cm::optional<std::string> resolved;
if (langs.count("CXX")) {
resolved = "CXX";
} else if (langs.count("C")) {
resolved = "C";
}
defaultLanguage = resolved;
}
return *defaultLanguage;
}
cm::optional<std::string> cmGeneratorTarget::GenerateStubForLanguage(
std::string const& language, std::string const& headerFilename,
std::string const& verifyTargetName, cmSourceFile& source) const
{
static std::array<std::pair<cm::string_view, cm::string_view>, 2> const
langToExt = { {
{ "C", ".c" },
{ "CXX", ".cxx" },
} };
// NOLINTNEXTLINE(readability-qualified-auto)
auto const it =
std::find_if(langToExt.begin(), langToExt.end(),
[&](std::pair<cm::string_view, cm::string_view> const& p) {
return p.first == language;
});
if (it == langToExt.end()) {
return cm::nullopt;
}
std::string filename =
cmStrCat(this->LocalGenerator->GetCurrentBinaryDirectory(), '/',
verifyTargetName, '/', headerFilename, extension);
auto* verificationSource = this->Makefile->GetOrCreateSource(filename);
verifyTargetName, '/', headerFilename, it->second);
cmSourceFile* verificationSource =
this->Makefile->GetOrCreateSource(filename);
source.SetSpecialSourceType(
cmSourceFile::SpecialSourceType::HeaderSetVerificationSource);
verificationSource->SetProperty("LANGUAGE", language);
@@ -6065,7 +6115,7 @@ std::string cmGeneratorTarget::GenerateHeaderSetVerificationFile(
<< "#include <" << headerFilename << "> /* IWYU pragma: associated */\n";
fout.close();
return filename;
return cm::optional<std::string>(std::move(filename));
}
std::string cmGeneratorTarget::GetImportedXcFrameworkPath(
+10 -2
View File
@@ -1110,10 +1110,18 @@ public:
void AddSystemIncludeDirectory(std::string const& inc,
std::string const& lang);
bool AddHeaderSetVerification();
std::string GenerateHeaderSetVerificationFile(
cm::optional<std::string> GenerateHeaderSetVerificationFile(
cmSourceFile& source, std::string const& dir,
std::string const& verifyTargetName,
cm::optional<std::set<std::string>>& languages) const;
cm::optional<cm::optional<std::string>>& defaultLanguage) const;
cm::optional<std::string> ResolveHeaderLanguage(
cmSourceFile& source,
cm::optional<cm::optional<std::string>>& defaultLanguage) const;
cm::optional<std::string> GenerateStubForLanguage(
std::string const& language, std::string const& headerFilename,
std::string const& verifyTargetName, cmSourceFile& source) const;
std::string GetImportedXcFrameworkPath(std::string const& config) const;