From 956d36d3cd3874558c18c3a5bcf9ea74756b9f63 Mon Sep 17 00:00:00 2001 From: Martin Duffy Date: Tue, 25 Nov 2025 14:15:35 -0500 Subject: [PATCH] instrumentation: Add postCMakeWorkflow hook Issue: #27414 --- Help/manual/cmake-instrumentation.7.rst | 1 + Source/cmInstrumentationQuery.cxx | 6 +-- Source/cmInstrumentationQuery.h | 1 + Source/cmake.cxx | 18 ++++++-- .../Instrumentation/RunCMakeTest.cmake | 27 +++++++++++ .../Instrumentation/check-workflow-hook.cmake | 3 ++ .../project/CMakePresets.json.in | 45 +++++++++++++++++++ .../query/cmake-command-workflow.cmake.in | 6 +++ 8 files changed, 100 insertions(+), 7 deletions(-) create mode 100644 Tests/RunCMake/Instrumentation/check-workflow-hook.cmake create mode 100644 Tests/RunCMake/Instrumentation/project/CMakePresets.json.in create mode 100644 Tests/RunCMake/Instrumentation/query/cmake-command-workflow.cmake.in diff --git a/Help/manual/cmake-instrumentation.7.rst b/Help/manual/cmake-instrumentation.7.rst index 967b5a251b..d3fccf9b86 100644 --- a/Help/manual/cmake-instrumentation.7.rst +++ b/Help/manual/cmake-instrumentation.7.rst @@ -212,6 +212,7 @@ key is required, but all other fields are optional. * ``preCMakeBuild`` (called when ``cmake --build`` is invoked) * ``postCMakeBuild`` (called when ``cmake --build`` completes) * ``postCMakeInstall`` + * ``postCMakeWorkflow`` * ``postCTest`` ``preBuild`` and ``postBuild`` are not supported when using the diff --git a/Source/cmInstrumentationQuery.cxx b/Source/cmInstrumentationQuery.cxx index d453d5cc4c..6876870fed 100644 --- a/Source/cmInstrumentationQuery.cxx +++ b/Source/cmInstrumentationQuery.cxx @@ -20,9 +20,9 @@ std::vector const cmInstrumentationQuery::OptionString{ "cdashVerbose", "trace" }; std::vector const cmInstrumentationQuery::HookString{ - "postGenerate", "preBuild", "postBuild", - "preCMakeBuild", "postCMakeBuild", "postCTest", - "postCMakeInstall", "prepareForCDash", "manual" + "postGenerate", "preBuild", "postBuild", "preCMakeBuild", + "postCMakeBuild", "postCTest", "postCMakeInstall", "postCMakeWorkflow", + "prepareForCDash", "manual" }; namespace ErrorMessages { diff --git a/Source/cmInstrumentationQuery.h b/Source/cmInstrumentationQuery.h index feeb22d669..9e82bdac8d 100644 --- a/Source/cmInstrumentationQuery.h +++ b/Source/cmInstrumentationQuery.h @@ -31,6 +31,7 @@ public: PostCMakeBuild, PostCTest, PostCMakeInstall, + PostCMakeWorkflow, PrepareForCDash, Manual }; diff --git a/Source/cmake.cxx b/Source/cmake.cxx index af6e6c70db..4e430678b9 100644 --- a/Source/cmake.cxx +++ b/Source/cmake.cxx @@ -4208,6 +4208,7 @@ std::function buildWorkflowStep( int cmake::Workflow(std::string const& presetName, WorkflowListPresets listPresets, WorkflowFresh fresh) { + int exitStatus = 0; #ifndef CMAKE_BOOTSTRAP this->SetHomeDirectory(cmSystemTools::GetLogicalWorkingDirectory()); this->SetHomeOutputDirectory(cmSystemTools::GetLogicalWorkingDirectory()); @@ -4280,11 +4281,12 @@ int cmake::Workflow(std::string const& presetName, std::vector steps; steps.reserve(expandedPreset->Steps.size()); int stepNumber = 1; + cmCMakePresetsGraph::ConfigurePreset const* configurePreset = {}; for (auto const& step : expandedPreset->Steps) { switch (step.PresetType) { case cmCMakePresetsGraph::WorkflowPreset::WorkflowStep::Type:: Configure: { - auto const* configurePreset = this->FindPresetForWorkflow( + configurePreset = this->FindPresetForWorkflow( "configure"_s, settingsFile.ConfigurePresets, step); if (!configurePreset) { return 1; @@ -4345,19 +4347,27 @@ int cmake::Workflow(std::string const& presetName, << std::flush; cmUVProcessChain::Status const status = step.Action(); if (status.ExitStatus != 0) { - return static_cast(status.ExitStatus); + exitStatus = static_cast(status.ExitStatus); + break; } auto const codeReasonPair = status.GetException(); if (codeReasonPair.first != cmUVProcessChain::ExceptionCode::None) { std::cout << "Step command ended abnormally: " << codeReasonPair.second << std::endl; - return status.SpawnResult != 0 ? status.SpawnResult : status.TermSignal; + exitStatus = + status.SpawnResult != 0 ? status.SpawnResult : status.TermSignal; + break; } first = false; } + if (configurePreset) { + cmInstrumentation instrumentation(configurePreset->BinaryDir); + instrumentation.CollectTimingData( + cmInstrumentationQuery::Hook::PostCMakeWorkflow); + } #endif - return 0; + return exitStatus; } void cmake::WatchUnusedCli(std::string const& var) diff --git a/Tests/RunCMake/Instrumentation/RunCMakeTest.cmake b/Tests/RunCMake/Instrumentation/RunCMakeTest.cmake index 6fe6581f32..ecc37a66d4 100644 --- a/Tests/RunCMake/Instrumentation/RunCMakeTest.cmake +++ b/Tests/RunCMake/Instrumentation/RunCMakeTest.cmake @@ -11,6 +11,7 @@ function(instrument test) "INSTALL" "INSTALL_PARALLEL" "TEST" + "WORKFLOW" "NO_WARN" "COPY_QUERIES" "COPY_QUERIES_GENERATED" @@ -90,6 +91,28 @@ function(instrument test) if(NOT RunCMake_GENERATOR_IS_MULTI_CONFIG) set(maybe_CMAKE_BUILD_TYPE -DCMAKE_BUILD_TYPE=Debug) endif() + if (ARGS_WORKFLOW) + configure_file( + "${RunCMake_TEST_SOURCE_DIR}/CMakePresets.json.in" + "${RunCMake_TEST_BINARY_DIR}/CMakePresets.json" + @ONLY + ) + configure_file( + "${cmake_file}.in" + "${RunCMake_TEST_BINARY_DIR}/cmake-command-workflow.cmake" + @ONLY + ) + foreach(f IN ITEMS CMakeLists.txt main.cxx lib.cxx lib.h) + configure_file( + "${RunCMake_TEST_SOURCE_DIR}/${f}" + "${RunCMake_TEST_BINARY_DIR}/${f}" + COPYONLY + ) + endforeach() + set(v1 ${RunCMake_TEST_BINARY_DIR}/build/.cmake/instrumentation-${uuid}/v1) + run_cmake_command(${test}-workflow ${CMAKE_COMMAND} --workflow default) + set(ARGS_NO_CONFIGURE TRUE) + endif() if (NOT ARGS_NO_CONFIGURE) run_cmake_with_options(${test} ${ARGS_CONFIGURE_ARG} ${maybe_CMAKE_BUILD_TYPE}) endif() @@ -207,6 +230,10 @@ if(NOT Skip_COMMAND_FAILURES_Case) CHECK_SCRIPT check-data-dir.cmake ) endif() +instrument(cmake-command-workflow + NO_WARN WORKFLOW + CHECK_SCRIPT check-workflow-hook.cmake +) # Test CUSTOM_CONTENT instrument(cmake-command-custom-content diff --git a/Tests/RunCMake/Instrumentation/check-workflow-hook.cmake b/Tests/RunCMake/Instrumentation/check-workflow-hook.cmake new file mode 100644 index 0000000000..9aac050a59 --- /dev/null +++ b/Tests/RunCMake/Instrumentation/check-workflow-hook.cmake @@ -0,0 +1,3 @@ +if (NOT EXISTS ${v1}/postCMakeWorkflow.hook) + set(RunCMake_TEST_FAILED "postCMakeWorkflow hook did not run\n") +endif() diff --git a/Tests/RunCMake/Instrumentation/project/CMakePresets.json.in b/Tests/RunCMake/Instrumentation/project/CMakePresets.json.in new file mode 100644 index 0000000000..ceb7155895 --- /dev/null +++ b/Tests/RunCMake/Instrumentation/project/CMakePresets.json.in @@ -0,0 +1,45 @@ +{ + "version": 10, + "cmakeMinimumRequired": { + "major": 4, + "minor": 2, + "patch": 0 + }, + "configurePresets": [ + { + "name": "default", + "binaryDir": "@RunCMake_TEST_BINARY_DIR@/build", + "warnings": { + "dev": false, + "unusedCli": false + }, + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "INSTRUMENT_COMMAND_FILE": "cmake-command-workflow.cmake" + }, + "generator" : "@RunCMake_GENERATOR@" + } + ], + "buildPresets": [ + { + "name": "default", + "configurePreset": "default", + "configuration": "Debug" + } + ], + "workflowPresets": [ + { + "name": "default", + "steps": [ + { + "type": "configure", + "name": "default" + }, + { + "type": "build", + "name": "default" + } + ] + } + ] +} diff --git a/Tests/RunCMake/Instrumentation/query/cmake-command-workflow.cmake.in b/Tests/RunCMake/Instrumentation/query/cmake-command-workflow.cmake.in new file mode 100644 index 0000000000..65b1f6a060 --- /dev/null +++ b/Tests/RunCMake/Instrumentation/query/cmake-command-workflow.cmake.in @@ -0,0 +1,6 @@ +cmake_instrumentation( + API_VERSION 1 + DATA_VERSION 1 + HOOKS postCMakeWorkflow + CALLBACK "@CMAKE_COMMAND@" -P "@RunCMake_TEST_SOURCE_DIR@/../hook.cmake" 0 0 +)