From 6116bcb0663e91fc7fa82718ab5e83a6377e5421 Mon Sep 17 00:00:00 2001 From: Arctic Lampyrid Date: Tue, 19 Mar 2024 11:45:58 +0800 Subject: fileapi: Add CONFIGURE_DEPENDS glob info to cmakeFiles object Fixes: #25668 Co-authored-by: Brad King --- Help/manual/cmake-file-api.7.rst | 50 +++++++++++++++++++++- .../release/dev/fileapi-provide-glob-dependent.rst | 9 ++++ Source/cmFileAPI.cxx | 2 +- Source/cmFileAPICMakeFiles.cxx | 40 +++++++++++++++++ Source/cmGlobVerificationManager.cxx | 15 +++++++ Source/cmGlobVerificationManager.h | 3 ++ Source/cmState.cxx | 5 +++ Source/cmState.h | 1 + Source/cmake.cxx | 5 +++ Source/cmake.h | 1 + .../RunCMake/CommandLine/E_capabilities-stdout.txt | 2 +- Tests/RunCMake/FileAPI/cmakeFiles-v1-check.py | 44 ++++++++++++++++++- Tests/RunCMake/FileAPI/cmakeFiles-v1.cmake | 10 +++++ 13 files changed, 182 insertions(+), 5 deletions(-) create mode 100644 Help/release/dev/fileapi-provide-glob-dependent.rst diff --git a/Help/manual/cmake-file-api.7.rst b/Help/manual/cmake-file-api.7.rst index c249ee2..260030e 100644 --- a/Help/manual/cmake-file-api.7.rst +++ b/Help/manual/cmake-file-api.7.rst @@ -1489,7 +1489,7 @@ There is only one ``cmakeFiles`` object major version, version 1. { "kind": "cmakeFiles", - "version": { "major": 1, "minor": 0 }, + "version": { "major": 1, "minor": 1 }, "paths": { "build": "/path/to/top-level-build-dir", "source": "/path/to/top-level-source-dir" @@ -1511,6 +1511,16 @@ There is only one ``cmakeFiles`` object major version, version 1. "isExternal": true, "path": "/path/to/cmake/Modules/CMakeGenericSystem.cmake" } + ], + "globsDependent": [ + { + "expression": "src/*.cxx", + "recurse": true, + "files": [ + "src/foo.cxx", + "src/bar.cxx" + ] + } ] } @@ -1553,6 +1563,44 @@ The members specific to ``cmakeFiles`` objects are: Optional member that is present with boolean value ``true`` if the path specifies a file in the CMake installation. +``globsDependent`` + Optional member that is present when the project calls :command:`file(GLOB)` + or :command:`file(GLOB_RECURSE)` with the ``CONFIGURE_DEPENDS`` option. + The value is a JSON array of JSON objects, each specifying a globbing + expression and the list of paths it matched. If the globbing expression + no longer matches the same list of paths, CMake considers the build system + to be out of date. + + This field was added in ``cmakeFiles`` version 1.1. + + The members of each entry are: + + ``expression`` + A string specifying the globbing expression. + + ``recurse`` + Optional member that is present with boolean value ``true`` + if the entry corresponds to a :command:`file(GLOB_RECURSE)` call. + Otherwise the entry corresponds to a :command:`file(GLOB)` call. + + ``listDirectories`` + Optional member that is present with boolean value ``true`` if + :command:`file(GLOB)` was called without ``LIST_DIRECTORIES false`` or + :command:`file(GLOB_RECURSE)` was called with ``LIST_DIRECTORIES true``. + + ``followSymlinks`` + Optional member that is present with boolean value ``true`` if + :command:`file(GLOB)` was called with the ``FOLLOW_SYMLINKS`` option. + + ``relative`` + Optional member that is present if :command:`file(GLOB)` was called + with the ``RELATIVE `` option. The value is a string containing + the ```` given. + + ``paths`` + A JSON array of strings specifying the paths matched by the call + to :command:`file(GLOB)` or :command:`file(GLOB_RECURSE)`. + Object Kind "toolchains" ------------------------ diff --git a/Help/release/dev/fileapi-provide-glob-dependent.rst b/Help/release/dev/fileapi-provide-glob-dependent.rst new file mode 100644 index 0000000..fa02272 --- /dev/null +++ b/Help/release/dev/fileapi-provide-glob-dependent.rst @@ -0,0 +1,9 @@ +fileapi-provide-glob-dependent +------------------------------ + +* The :manual:`cmake-file-api(7)` "cmakeFiles" version 1 object's ``version`` + field has been updated to 1.1. + +* The :manual:`cmake-file-api(7)` "cmakeFiles" version 1 object gained a + ``globsDependent`` field to report :command:`file(GLOB)` calls using + ``CONFIGURE_DEPENDS``. diff --git a/Source/cmFileAPI.cxx b/Source/cmFileAPI.cxx index 4524ba6..d4a7175 100644 --- a/Source/cmFileAPI.cxx +++ b/Source/cmFileAPI.cxx @@ -830,7 +830,7 @@ Json::Value cmFileAPI::BuildCache(Object const& object) // The "cmakeFiles" object kind. -static unsigned int const CMakeFilesV1Minor = 0; +static unsigned int const CMakeFilesV1Minor = 1; void cmFileAPI::BuildClientRequestCMakeFiles( ClientRequest& r, std::vector const& versions) diff --git a/Source/cmFileAPICMakeFiles.cxx b/Source/cmFileAPICMakeFiles.cxx index b62fe04..bc80319 100644 --- a/Source/cmFileAPICMakeFiles.cxx +++ b/Source/cmFileAPICMakeFiles.cxx @@ -31,6 +31,8 @@ class CMakeFiles Json::Value DumpPaths(); Json::Value DumpInputs(); Json::Value DumpInput(std::string const& file); + Json::Value DumpGlobsDependent(); + Json::Value DumpGlobDependent(cmGlobCacheEntry const& entry); public: CMakeFiles(cmFileAPI& fileAPI, unsigned long version); @@ -53,6 +55,10 @@ Json::Value CMakeFiles::Dump() Json::Value cmakeFiles = Json::objectValue; cmakeFiles["paths"] = this->DumpPaths(); cmakeFiles["inputs"] = this->DumpInputs(); + Json::Value globsDependent = this->DumpGlobsDependent(); + if (!globsDependent.empty()) { + cmakeFiles["globsDependent"] = std::move(globsDependent); + } return cmakeFiles; } @@ -108,6 +114,40 @@ Json::Value CMakeFiles::DumpInput(std::string const& file) return input; } + +Json::Value CMakeFiles::DumpGlobsDependent() +{ + Json::Value globsDependent = Json::arrayValue; + for (cmGlobCacheEntry const& entry : + this->FileAPI.GetCMakeInstance()->GetGlobCacheEntries()) { + globsDependent.append(this->DumpGlobDependent(entry)); + } + return globsDependent; +} + +Json::Value CMakeFiles::DumpGlobDependent(cmGlobCacheEntry const& entry) +{ + Json::Value globDependent = Json::objectValue; + globDependent["expression"] = entry.Expression; + if (entry.Recurse) { + globDependent["recurse"] = true; + } + if (entry.ListDirectories) { + globDependent["listDirectories"] = true; + } + if (entry.FollowSymlinks) { + globDependent["followSymlinks"] = true; + } + if (!entry.Relative.empty()) { + globDependent["relative"] = entry.Relative; + } + Json::Value paths = Json::arrayValue; + for (std::string const& file : entry.Files) { + paths.append(file); + } + globDependent["paths"] = std::move(paths); + return globDependent; +} } Json::Value cmFileAPICMakeFilesDump(cmFileAPI& fileAPI, unsigned long version) diff --git a/Source/cmGlobVerificationManager.cxx b/Source/cmGlobVerificationManager.cxx index 63c9baa..73365d2 100644 --- a/Source/cmGlobVerificationManager.cxx +++ b/Source/cmGlobVerificationManager.cxx @@ -176,6 +176,21 @@ void cmGlobVerificationManager::AddCacheEntry( } } +std::vector cmGlobVerificationManager::GetCacheEntries() + const +{ + std::vector entries; + for (auto const& i : this->Cache) { + CacheEntryKey k = std::get<0>(i); + CacheEntryValue v = std::get<1>(i); + if (v.Initialized) { + entries.emplace_back(k.Recurse, k.ListDirectories, k.FollowSymlinks, + k.Relative, k.Expression, v.Files); + } + } + return entries; +} + void cmGlobVerificationManager::Reset() { this->Cache.clear(); diff --git a/Source/cmGlobVerificationManager.h b/Source/cmGlobVerificationManager.h index b7d8168..e9c519b 100644 --- a/Source/cmGlobVerificationManager.h +++ b/Source/cmGlobVerificationManager.h @@ -33,6 +33,9 @@ protected: const std::string& variable, const cmListFileBacktrace& bt, cmMessenger* messenger); + //! Get all cache entries + std::vector GetCacheEntries() const; + //! Clear the glob cache for state reset. void Reset(); diff --git a/Source/cmState.cxx b/Source/cmState.cxx index 8c881c5..608f070 100644 --- a/Source/cmState.cxx +++ b/Source/cmState.cxx @@ -248,6 +248,11 @@ void cmState::AddGlobCacheEntry(const cmGlobCacheEntry& entry, messenger); } +std::vector cmState::GetGlobCacheEntries() const +{ + return this->GlobVerificationManager->GetCacheEntries(); +} + void cmState::RemoveCacheEntry(std::string const& key) { this->CacheManager->RemoveCacheEntry(key); diff --git a/Source/cmState.h b/Source/cmState.h index 3837556..5aceb6a 100644 --- a/Source/cmState.h +++ b/Source/cmState.h @@ -268,6 +268,7 @@ private: const std::string& variable, cmListFileBacktrace const& bt, cmMessenger* messenger); + std::vector GetGlobCacheEntries() const; cmPropertyDefinitionMap PropertyDefinitions; std::vector EnabledLanguages; diff --git a/Source/cmake.cxx b/Source/cmake.cxx index 43a0b06..ea6a380 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -2958,6 +2958,11 @@ void cmake::AddGlobCacheEntry(const cmGlobCacheEntry& entry, this->Messenger.get()); } +std::vector cmake::GetGlobCacheEntries() const +{ + return this->State->GetGlobCacheEntries(); +} + std::vector cmake::GetAllExtensions() const { std::vector allExt = this->CLikeSourceFileExtensions.ordered; diff --git a/Source/cmake.h b/Source/cmake.h index 3647a11..2d17ed2 100644 --- a/Source/cmake.h +++ b/Source/cmake.h @@ -355,6 +355,7 @@ public: void AddGlobCacheEntry(const cmGlobCacheEntry& entry, const std::string& variable, cmListFileBacktrace const& bt); + std::vector GetGlobCacheEntries() const; /** * Get the system information and write it to the file specified diff --git a/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt b/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt index 2bbe1c8..0c2a951 100644 --- a/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt +++ b/Tests/RunCMake/CommandLine/E_capabilities-stdout.txt @@ -1 +1 @@ -^{"debugger":(true|false),"fileApi":{"requests":\[{"kind":"codemodel","version":\[{"major":2,"minor":7}]},{"kind":"configureLog","version":\[{"major":1,"minor":0}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":0}]},{"kind":"toolchains","version":\[{"major":1,"minor":0}]}]},"generators":\[.*\],"serverMode":false,"tls":(true|false),"version":{.*}}$ +^{"debugger":(true|false),"fileApi":{"requests":\[{"kind":"codemodel","version":\[{"major":2,"minor":7}]},{"kind":"configureLog","version":\[{"major":1,"minor":0}]},{"kind":"cache","version":\[{"major":2,"minor":0}]},{"kind":"cmakeFiles","version":\[{"major":1,"minor":1}]},{"kind":"toolchains","version":\[{"major":1,"minor":0}]}]},"generators":\[.*\],"serverMode":false,"tls":(true|false),"version":{.*}}$ diff --git a/Tests/RunCMake/FileAPI/cmakeFiles-v1-check.py b/Tests/RunCMake/FileAPI/cmakeFiles-v1-check.py index 49dfe87..9410c7e 100644 --- a/Tests/RunCMake/FileAPI/cmakeFiles-v1-check.py +++ b/Tests/RunCMake/FileAPI/cmakeFiles-v1-check.py @@ -3,7 +3,7 @@ from check_index import * def check_objects(o): assert is_list(o) assert len(o) == 1 - check_index_object(o[0], "cmakeFiles", 1, 0, check_object_cmakeFiles) + check_index_object(o[0], "cmakeFiles", 1, 1, check_object_cmakeFiles) def check_input(actual, expected): assert is_dict(actual) @@ -23,8 +23,27 @@ def check_input(actual, expected): assert sorted(actual.keys()) == sorted(expected_keys) +def check_glob_dependent(actual, expected): + assert is_dict(actual) + + if "followSymlinks" in expected: + assert is_bool(actual["followSymlinks"], expected["followSymlinks"]) + + if "listDirectories" in expected: + assert is_bool(actual["listDirectories"], expected["listDirectories"]) + + if "recurse" in expected: + assert is_bool(actual["recurse"], expected["recurse"]) + + if "relative" in expected: + assert matches(actual["relative"], expected["relative"]) + + check_list_match(lambda a, e: matches(a, e), actual["paths"], expected["paths"], allow_extra=True) + + assert sorted(actual.keys()) == sorted(expected.keys()) + def check_object_cmakeFiles(o): - assert sorted(o.keys()) == ["inputs", "kind", "paths", "version"] + assert sorted(o.keys()) == ["globsDependent", "inputs", "kind", "paths", "version"] # The "kind" and "version" members are handled by check_index_object. assert is_dict(o["paths"]) assert sorted(o["paths"].keys()) == ["build", "source"] @@ -82,12 +101,33 @@ def check_object_cmakeFiles(o): }, ] + expected_globs = [ + { + "expression": "^.*/Tests/RunCMake/FileAPI/dir/\\*$", + "paths": [ + "^.*/Tests/RunCMake/FileAPI/dir/dir$", + "^.*/Tests/RunCMake/FileAPI/dir/dirtest\\.cmake$" + ], + "listDirectories": True, + }, + { + "expression": "^.*/Tests/RunCMake/FileAPI/dir/\\*\\.cmake$", + "paths": [ + "^dir/dirtest\\.cmake$" + ], + "followSymlinks": True, + "recurse": True, + "relative": "^.*/Tests/RunCMake/FileAPI$" + } + ] + inSource = os.path.dirname(o["paths"]["build"]) == o["paths"]["source"] if inSource: for e in expected: e["path"] = e["path"].replace("^.*/Tests/RunCMake/FileAPI/", "^", 1) check_list_match(lambda a, e: matches(a["path"], e["path"]), o["inputs"], expected, check=check_input, allow_extra=True) + check_list_match(lambda a, e: matches(a["expression"], e["expression"]), o["globsDependent"], expected_globs, check=check_glob_dependent, allow_extra=True) assert is_dict(index) assert sorted(index.keys()) == ["cmake", "objects", "reply"] diff --git a/Tests/RunCMake/FileAPI/cmakeFiles-v1.cmake b/Tests/RunCMake/FileAPI/cmakeFiles-v1.cmake index 4d4d757..b98a283 100644 --- a/Tests/RunCMake/FileAPI/cmakeFiles-v1.cmake +++ b/Tests/RunCMake/FileAPI/cmakeFiles-v1.cmake @@ -5,4 +5,14 @@ file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/generated.cmake" "") include("${CMAKE_CURRENT_BINARY_DIR}/generated.cmake") include("${CMAKE_CURRENT_LIST_DIR}/../FileAPIDummyFile.cmake") +file(GLOB var + CONFIGURE_DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/dir/*") + +file(GLOB_RECURSE var + FOLLOW_SYMLINKS + RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" + CONFIGURE_DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/dir/*.cmake") + add_subdirectory(dir) -- cgit v0.12