diff --git a/README.md b/README.md index fa1e9e7..147b183 100644 --- a/README.md +++ b/README.md @@ -229,6 +229,11 @@ replacements may be added in the future, if necessary. **TIP:** Setting `binaryDir` to something like `"${sourceDir}/build/${presetName}"` is an easy way to separate build folders for different presets. +Note: The build folder is cached when using presets, to avoid having to read the information from the preset files +again. This is automatically cleared when you change presets. If you need to clear the cache without changing presets +(i.e., you edited the `binaryDir` field in the presets file), call the `cmake-integration-refresh-build-folder-cache` +function. + ### Hiding some targets during completion @@ -298,7 +303,9 @@ for each target to annotate them with their target types in the completion list. use case and you'd rather avoid the performance cost, you can set `cmake-integration-annotate-targets` to `nil`. To apply this setting only to a specific project, configure it as a directory-local variable. -Note: This information is cached after the first retrieval. +Note: This information is cached after the first retrieval. This cache can also be saved/restored when appropriated (see +the [Persisting state](#persisting-state) section). If you need to clear the cache for some reason, call the +`cmake-integration-refresh-target-type-cache` function. ## Integration with the Conan Package Manager diff --git a/cmake-integration-configure.el b/cmake-integration-configure.el index 97c0be4..9a19f5c 100644 --- a/cmake-integration-configure.el +++ b/cmake-integration-configure.el @@ -146,6 +146,9 @@ A list of preset names if obtained from `CMakePresets.json' and choose one of them (with completion)." (interactive) + ;; Invalidate build folder cache + (setq ci--build-folder-cache nil) + (let ((all-presets (ci-get-configure-presets))) (setq ci-configure-preset (ci-select-preset all-presets "Configure preset: ")) diff --git a/cmake-integration-core.el b/cmake-integration-core.el index 5a7cd22..d7eb954 100644 --- a/cmake-integration-core.el +++ b/cmake-integration-core.el @@ -135,6 +135,53 @@ or set `cmake-integration-build-dir' manually"))) (error "Not in a project")))) +;; This function may appear trivial since it only sets a variable with `setq`. +;; However, it is designed as a separate function to allow its inclusion in +;; `ci-functions-to-save-state`. +(defun ci--set-build-folder-cache (build-folder) + "Set the build folder cache to BUILD-FOLDER." + (setq ci--build-folder-cache build-folder)) + + +(defun ci-refresh-build-folder-cache () + "Refresh the cached build folder." + (interactive) + (setq ci--build-folder-cache nil)) + + +(defun ci--get-build-folder-from-preset (configure-preset project-root-folder) + "Get the build folder from CONFIGURE-PRESET. + +If the build folder is a relative path, it is resolved against +PROJECT-ROOT-FOLDER. + +If `cmake-integration--build-folder-cache' is set, return that. If not, +then the build folder is determined from the preset's `binaryDir' field, +with any variable replacements done." + (if ci--build-folder-cache + ci--build-folder-cache + (if-let* ((binaryDir-with-replacements + (ci--get-binaryDir-with-replacements configure-preset))) + + ;; Cache the build folder for future calls and return it + (ci--set-build-folder-cache + (expand-file-name binaryDir-with-replacements project-root-folder)) + + ;; Maybe the preset or any parent preset has no binaryDir set, or + ;; maybe a parent preset is missing. We need to warn the user and than + ;; we try using the manually set build dir instead + (warn + "Could not determine build folder from preset '%s'.\n - Maybe the preset and none of its parent presets has the 'binaryDir' field, or a parent preset is missing." + (ci--get-preset-name configure-preset)) + (if-let* ((build-dir (ci--get-build-dir-if-set))) + (progn + (warn "Using manually set build folder: '%s'" build-dir) + build-dir) + (error + "Build folder could not be determined from preset '%s' and no manual build folder is set" + (ci--get-preset-name configure-preset)))))) + + (defun ci-get-build-folder () "Get the project build folder. @@ -151,22 +198,8 @@ resolved against the project root." (error "Not in a project")) (if preset - (if-let* ((binaryDir-with-replacements - (ci--get-binaryDir-with-replacements preset))) - (expand-file-name binaryDir-with-replacements project-root-folder) - ;; Maybe the preset or any parent preset has no binaryDir set, or - ;; maybe a parent preset is missing. We need to warn the user and than - ;; we try using the manually set build dir instead - (warn - "Could not determine build folder from preset '%s'.\n - Maybe the preset and none of its parent presets has the 'binaryDir' field, or a parent preset is missing." - (ci--get-preset-name preset)) - (if-let* ((build-dir (ci--get-build-dir-if-set))) - (progn - (warn "Using manually set build folder: '%s'" build-dir) - build-dir) - (error - "Build folder could not be determined from preset '%s' and no manual build folder is set" - (ci--get-preset-name preset)))) + ;; Get build directory from preset + (ci--get-build-folder-from-preset preset project-root-folder) ;; Use manually set build directory or throw an error (ci--get-build-dir-if-set t)))) diff --git a/cmake-integration-persistence.el b/cmake-integration-persistence.el index ebfcd84..c08681d 100644 --- a/cmake-integration-persistence.el +++ b/cmake-integration-persistence.el @@ -49,6 +49,7 @@ ci-build-preset ci-test-preset ci-package-preset + ci--build-folder-cache ;; CTest ci--ctest-label-include-regexp @@ -64,6 +65,8 @@ ci-select-build-preset ci-select-package-preset ci-select-conan-profile + ;; We also want to save state if the build folder cache is modified + ci--set-build-folder-cache ) "Functions which automatically save cmake-intregration state. @@ -201,12 +204,15 @@ exactly what `cmake-integration--build-current-state' returns." ;; TODO Add a test for this ;;;###autoload (autoload 'cmake-integration-save-state "cmake-integration") -(defun ci-save-state () +(defun ci-save-state (&optional _) "Save the current state of cmake-integration to persistent storage. The location is determined by `cmake-integration-persist-location'. -If not in a CMake project, no state is saved." +If not in a CMake project, no state is saved. + +Note: The optional argument is not used. It is only present so this +function can be used as an advice." (interactive) (if (ci-is-cmake-project-p) (when-let* ((state-file (ci--get-persist-file t)) diff --git a/cmake-integration-variables.el b/cmake-integration-variables.el index 590e53b..0a9512b 100644 --- a/cmake-integration-variables.el +++ b/cmake-integration-variables.el @@ -235,6 +235,10 @@ will be replaced by the project root." :type 'directory :group 'cmake-integratio "Cache for target types. Maps target names to their types.") +(defvar ci--build-folder-cache nil + "Cache for the build folder path (when presets are used).") + + (defvar ci-run-arguments "" "Command line arguments when running a target.") diff --git a/tests/cmake-integration-tests.el b/tests/cmake-integration-tests.el index 4ac4051..e781a0c 100644 --- a/tests/cmake-integration-tests.el +++ b/tests/cmake-integration-tests.el @@ -310,7 +310,8 @@ test code from inside a 'test project'." ;; Build folder is taken from the `binaryDir' field in ;; `cmake-integration-configure-preset', which is an alist. ;; Here we test with a binaryDir value with some replacements - (let* ((ci-configure-preset + (let* ((ci--build-folder-cache nil) + (ci-configure-preset '((binaryDir . "${sourceDir}/build/${presetName}") (name . "ninjamulticonfig"))) (project-root (ci--get-project-root-folder)) @@ -319,7 +320,8 @@ test code from inside a 'test project'." (should (filepath-equal-p (ci-get-build-folder) expected-build-folder))) ;; Now we test with a binaryDir that has a relative path - (let* ((ci-configure-preset + (let* ((ci--build-folder-cache nil) + (ci-configure-preset '((binaryDir . "build/${presetName}") (name . "ninjamulticonfig"))) (project-root (ci--get-project-root-folder)) (expected-build-folder @@ -327,7 +329,8 @@ test code from inside a 'test project'." (should (filepath-equal-p (ci-get-build-folder) expected-build-folder))) ;; Test with a different preset - (let* ((ci-configure-preset + (let* ((ci--build-folder-cache nil) + (ci-configure-preset '((binaryDir . "./build/${presetName}") (name . "Ninja"))) (project-root (ci--get-project-root-folder)) (expected-build-folder @@ -669,7 +672,8 @@ test code from inside a 'test project'." (let ((ci-build-preset nil)) (test-fixture-setup ;; "./test-project" - (let ((expected-run-dir (ci--get-project-root-folder))) + (let ((ci--build-folder-cache nil) + (expected-run-dir (ci--get-project-root-folder))) (should (equal (ci-get-build-command "the_target") @@ -680,7 +684,8 @@ test code from inside a 'test project'." ;; Without setting a preset (test-fixture-setup ;; "./test-project-with-presets" - (let ((expected-run-dir (ci--get-project-root-folder))) + (let ((ci--build-folder-cache nil) + (expected-run-dir (ci--get-project-root-folder))) (should (equal (ci-get-build-command "the_target") @@ -691,7 +696,8 @@ test code from inside a 'test project'." ;; Without a build preset (test-fixture-setup "./test-project-with-presets" - (let ((expected-run-dir (ci--get-project-root-folder)) + (let ((ci--build-folder-cache nil) + (expected-run-dir (ci--get-project-root-folder)) (ci-configure-preset '("default" (name . "default") (binaryDir . "theBuildFolder")))) (should @@ -704,7 +710,8 @@ test code from inside a 'test project'." ;; With a build preset (test-fixture-setup "./test-project-with-presets" - (let ((expected-run-dir (ci--get-project-root-folder)) + (let ((ci--build-folder-cache nil) + (expected-run-dir (ci--get-project-root-folder)) (ci-configure-preset '("config-preset" (name . "config-preset") @@ -723,7 +730,8 @@ test code from inside a 'test project'." ;; With a build preset and a target that has the configuration in the name (test-fixture-setup "./test-project-with-presets" - (let ((expected-run-dir (ci--get-project-root-folder)) + (let ((ci--build-folder-cache nil) + (expected-run-dir (ci--get-project-root-folder)) (ci-configure-preset '("config-preset" (name . "config-preset") @@ -742,7 +750,8 @@ test code from inside a 'test project'." ;; Passing extra args (test-fixture-setup "./test-project-with-presets" - (let ((expected-run-dir (ci--get-project-root-folder)) + (let ((ci--build-folder-cache nil) + (expected-run-dir (ci--get-project-root-folder)) (ci-configure-preset '("config-preset" (name . "config-preset") @@ -762,7 +771,8 @@ test code from inside a 'test project'." (ert-deftest test-ci-get-conan-run-command () (test-fixture-setup "./test-project" - (let* ((project-root-folder (ci--get-project-root-folder)) + (let* ((ci--build-folder-cache nil) + (project-root-folder (ci--get-project-root-folder)) (build-folder (ci-get-build-folder)) (conanfile-relative-path (file-relative-name project-root-folder build-folder)))