From d9b9b4cd276a8fdd74784df4a2e5523276ccb2b2 Mon Sep 17 00:00:00 2001 From: Matthew Woehlke Date: Wed, 8 Jul 2026 12:30:17 -0400 Subject: [PATCH] FileSets: Don't crash if a file's file set isn't found File sets currently have issues with files containing semicolons; particularly, names get mangled differently in different contexts. This can result in failing to find a file's file set. Prior to dee8799d16 (FileSet management: Introduce dedicated classes for generation, 2026-02-16, v4.4.0-rc1~691^2), we guarded against this, but the aforementioned commit introduced code that would try to use the found fileset object without checking if it was valid. Add the missing checks so that we do not crash trying to dereference a null pointer. Also, refactor `cmGeneratorTarget::GetSourceFilePaths` to avoid unnecessary work. Fixes: #27924 --- Source/cmGeneratorTarget_Sources.cxx | 26 +++++------ .../target_sources/FileSetSemicolon.cmake | 45 +++++++++++++++++++ .../target_sources/RunCMakeTest.cmake | 1 + 3 files changed, 58 insertions(+), 14 deletions(-) create mode 100644 Tests/RunCMake/target_sources/FileSetSemicolon.cmake diff --git a/Source/cmGeneratorTarget_Sources.cxx b/Source/cmGeneratorTarget_Sources.cxx index 6e6a7d59dc..f4d2f23e91 100644 --- a/Source/cmGeneratorTarget_Sources.cxx +++ b/Source/cmGeneratorTarget_Sources.cxx @@ -266,26 +266,24 @@ std::vector> cmGeneratorTarget::GetSourceFilePaths( AddFileSetEntries(this, this->FileSets.get(), context, &fsDagChecker, fileSetEntries); auto processFileSetEntry = [this, &config](cmSourceFile* sf) { - auto const* fileSet = this->GetFileSetForSource(config, sf); - if (fileSet->GetType() == cm::FileSetMetadata::HEADERS) { + cmGeneratorFileSet const* fileSet = this->GetFileSetForSource(config, sf); + if (fileSet && fileSet->GetType() == cm::FileSetMetadata::HEADERS) { sf->SetProperty("HEADER_FILE_ONLY", "TRUE"); - } #if !defined(CMAKE_BOOTSTRAP) - cmMakefile* mf = this->Target->GetMakefile(); - auto const& path = sf->GetFullPath(); - bool found = false; - for (auto const& sg : mf->GetSourceGroups()) { - if (sg->MatchChildrenFiles(path)) { - found = true; - break; + cmMakefile* mf = this->Target->GetMakefile(); + std::string const& path = sf->GetFullPath(); + bool found = false; + for (auto const& sg : mf->GetSourceGroups()) { + if (sg->MatchChildrenFiles(path)) { + found = true; + break; + } } - } - if (!found) { - if (fileSet->GetType() == cm::FileSetMetadata::HEADERS) { + if (!found) { mf->GetOrCreateSourceGroup("Header Files")->AddGroupFile(path); } - } #endif + } }; bool contextDependentFileSets = processSources(this, config, fileSetEntries, files, uniqueSrcs, diff --git a/Tests/RunCMake/target_sources/FileSetSemicolon.cmake b/Tests/RunCMake/target_sources/FileSetSemicolon.cmake new file mode 100644 index 0000000000..31430d8c40 --- /dev/null +++ b/Tests/RunCMake/target_sources/FileSetSemicolon.cmake @@ -0,0 +1,45 @@ +enable_language(C) + +add_library(lib1 STATIC empty.c) + +set_property( + SOURCE ${CMAKE_CURRENT_BINARY_DIR}/h\\;1.h + PROPERTY GENERATED TRUE +) + +set_property( + SOURCE ${CMAKE_CURRENT_BINARY_DIR}/h\\;2.h + PROPERTY GENERATED TRUE +) + +set_property( + SOURCE ${CMAKE_CURRENT_BINARY_DIR}/h\\;3.h + PROPERTY GENERATED TRUE +) + +target_sources(lib1 + PRIVATE FILE_SET HEADERS + BASE_DIRS ${CMAKE_CURRENT_BINARY_DIR} + FILES ${CMAKE_CURRENT_BINARY_DIR}/h\\\\\\\\;1.h +) + +set_property(TARGET lib1 APPEND PROPERTY HEADER_SET + ${CMAKE_CURRENT_BINARY_DIR}/h\\\\\\\\;2.h + ${CMAKE_CURRENT_BINARY_DIR}/h\\\\\\\\;3.h +) + +# Ensure we get the expected number of escapes when reading back the file set +# files. +get_property(headers TARGET lib1 PROPERTY HEADER_SET) +if (NOT headers MATCHES "h\\\\\\\\\\\\;1[.]h") + message(STATUS "headers: '${headers}'") + message(SEND_ERROR "'h;1.h' has wrong level of escaping") +endif() +if (NOT headers MATCHES "h\\\\\\\\\\\\;2[.]h") + message(STATUS "headers: '${headers}'") + message(SEND_ERROR "'h;2.h' has wrong level of escaping") +endif() +if (NOT headers MATCHES "h\\\\\\\\\\\\;3[.]h$") + message(STATUS "headers: '${headers}'") + message(SEND_ERROR "'h;3.h' has wrong level of escaping") +endif() diff --git a/Tests/RunCMake/target_sources/RunCMakeTest.cmake b/Tests/RunCMake/target_sources/RunCMakeTest.cmake index d02fc92081..32556202c6 100644 --- a/Tests/RunCMake/target_sources/RunCMakeTest.cmake +++ b/Tests/RunCMake/target_sources/RunCMakeTest.cmake @@ -45,6 +45,7 @@ run_cmake(FileSetBadName) run_cmake(FileSetWrongSyntax) run_cmake(FileSetDirect) run_cmake(FileSetDuplicateSource) +run_cmake(FileSetSemicolon) if(APPLE) run_cmake(FileSetFramework1) run_cmake(FileSetFramework2)