From 314440c32077dc2a902a494a99b96437d93af5b4 Mon Sep 17 00:00:00 2001 From: Martin Duffy Date: Wed, 5 Feb 2025 13:43:30 -0500 Subject: instrumentation: Run preBuild and postBuild hooks before and after make Updates the preBuild and postBuild instrumentation hooks to run before and after make is invoked. --- Help/manual/cmake-instrumentation.7.rst | 4 +- Source/cmLocalUnixMakefileGenerator3.cxx | 61 +++++++++++++++++++--- Tests/RunCMake/Instrumentation/RunCMakeTest.cmake | 6 +-- .../Instrumentation/check-make-program-hooks.cmake | 35 +++++++++++++ .../Instrumentation/check-ninja-hooks.cmake | 35 ------------- .../Instrumentation/project/CMakeLists.txt | 1 + .../query/cmake-command-make-program.cmake | 7 +++ .../query/cmake-command-ninja.cmake | 6 --- 8 files changed, 101 insertions(+), 54 deletions(-) create mode 100644 Tests/RunCMake/Instrumentation/check-make-program-hooks.cmake delete mode 100644 Tests/RunCMake/Instrumentation/check-ninja-hooks.cmake create mode 100644 Tests/RunCMake/Instrumentation/query/cmake-command-make-program.cmake delete mode 100644 Tests/RunCMake/Instrumentation/query/cmake-command-ninja.cmake diff --git a/Help/manual/cmake-instrumentation.7.rst b/Help/manual/cmake-instrumentation.7.rst index d3a1619..c3ebc75 100644 --- a/Help/manual/cmake-instrumentation.7.rst +++ b/Help/manual/cmake-instrumentation.7.rst @@ -117,8 +117,8 @@ optional. should be one of the following: * ``postGenerate`` - * ``preBuild`` (called when ``ninja`` is invoked; unavailable on Windows) - * ``postBuild`` (called when ``ninja`` completes; unavailable on Windows) + * ``preBuild`` (called when ``ninja`` or ``make`` is invoked; unavailable on Windows) + * ``postBuild`` (called when ``ninja`` or ``make`` completes; unavailable on Windows) * ``preCMakeBuild`` (called when ``cmake --build`` is invoked) * ``postCMakeBuild`` (called when ``cmake --build`` completes) * ``postInstall`` diff --git a/Source/cmLocalUnixMakefileGenerator3.cxx b/Source/cmLocalUnixMakefileGenerator3.cxx index 1256592..68c79ed 100644 --- a/Source/cmLocalUnixMakefileGenerator3.cxx +++ b/Source/cmLocalUnixMakefileGenerator3.cxx @@ -28,6 +28,7 @@ #include "cmGeneratorTarget.h" #include "cmGlobalGenerator.h" #include "cmGlobalUnixMakefileGenerator3.h" +#include "cmInstrumentation.h" #include "cmList.h" #include "cmListFileCache.h" #include "cmLocalGenerator.h" @@ -71,6 +72,26 @@ std::string cmSplitExtension(std::string const& in, std::string& base) return ext; } +#if !defined(CMAKE_BOOTSTRAP) && !defined(_WIN32) +// Helper function to add the Start Instrumentation command +void addInstrumentationCommand(cmInstrumentation* instrumentation, + std::vector& commands) +{ + // FIXME(#26668) This does not work on Windows + if (instrumentation->HasPreOrPostBuildHook()) { + std::string instrumentationCommand = + "$(CTEST_COMMAND) --start-instrumentation $(CMAKE_BINARY_DIR)"; + /* + * On Unix systems, Make will prefix the command with `/bin/sh -c`. + * Use exec so that Make is the parent process of the command. + * Add a `;` to convince BSD make to not optimize out the shell. + */ + instrumentationCommand = cmStrCat("exec ", instrumentationCommand, " ;"); + commands.push_back(instrumentationCommand); + } +} +#endif + // Helper predicate for removing absolute paths that don't point to the // source or binary directory. It is used when CMAKE_DEPENDS_IN_PROJECT_ONLY // is set ON, to only consider in-project dependencies during the build. @@ -651,19 +672,35 @@ void cmLocalUnixMakefileGenerator3::WriteMakeVariables( #endif } + auto getShellCommand = [this](std::string command) -> std::string { + std::string shellCommand = this->MaybeConvertWatcomShellCommand(command); + return shellCommand.empty() + ? this->ConvertToOutputFormat(command, cmOutputConverter::SHELL) + : shellCommand; + }; + std::string cmakeShellCommand = - this->MaybeConvertWatcomShellCommand(cmSystemTools::GetCMakeCommand()); - if (cmakeShellCommand.empty()) { - cmakeShellCommand = this->ConvertToOutputFormat( - cmSystemTools::GetCMakeCommand(), cmOutputConverter::SHELL); + getShellCommand(cmSystemTools::GetCMakeCommand()); + + makefileStream << "# The CMake executable.\n" + "CMAKE_COMMAND = " + << cmakeShellCommand << "\n"; + +#if !defined(CMAKE_BOOTSTRAP) && !defined(_WIN32) + // FIXME(#26668) This does not work on Windows + if (this->GetCMakeInstance() + ->GetInstrumentation() + ->HasPreOrPostBuildHook()) { + std::string ctestShellCommand = + getShellCommand(cmSystemTools::GetCTestCommand()); + makefileStream << "# The CTest executable.\n" + "CTEST_COMMAND = " + << ctestShellCommand << "\n"; } +#endif makefileStream - << "# The CMake executable.\n" - "CMAKE_COMMAND = " - << cmakeShellCommand << "\n" - "\n" "# The command to remove a file.\n" "RM = " << cmakeShellCommand @@ -811,6 +848,10 @@ void cmLocalUnixMakefileGenerator3::WriteSpecialTargetsBottom( std::vector no_depends; commands.push_back(std::move(runRule)); +#if !defined(CMAKE_BOOTSTRAP) && !defined(_WIN32) + addInstrumentationCommand(this->GetCMakeInstance()->GetInstrumentation(), + commands); +#endif if (!this->IsRootMakefile()) { this->CreateCDCommand(commands, this->GetBinaryDirectory(), this->GetCurrentBinaryDirectory()); @@ -1808,6 +1849,10 @@ void cmLocalUnixMakefileGenerator3::WriteLocalAllRules( this->ConvertToOutputFormat(cmakefileName, cmOutputConverter::SHELL), " 1"); commands.push_back(std::move(runRule)); +#if !defined(CMAKE_BOOTSTRAP) && !defined(_WIN32) + addInstrumentationCommand(this->GetCMakeInstance()->GetInstrumentation(), + commands); +#endif } this->CreateCDCommand(commands, this->GetBinaryDirectory(), this->GetCurrentBinaryDirectory()); diff --git a/Tests/RunCMake/Instrumentation/RunCMakeTest.cmake b/Tests/RunCMake/Instrumentation/RunCMakeTest.cmake index f98bb32..c270b71 100644 --- a/Tests/RunCMake/Instrumentation/RunCMakeTest.cmake +++ b/Tests/RunCMake/Instrumentation/RunCMakeTest.cmake @@ -117,8 +117,8 @@ instrument(cmake-command-parallel-install CHECK_SCRIPT check-data-dir.cmake) # FIXME(#26668) This does not work on Windows -if (UNIX AND ${RunCMake_GENERATOR} MATCHES "^Ninja") - instrument(cmake-command-ninja NO_WARN +if (UNIX) + instrument(cmake-command-make-program NO_WARN BUILD_MAKE_PROGRAM - CHECK_SCRIPT check-ninja-hooks.cmake) + CHECK_SCRIPT check-make-program-hooks.cmake) endif() diff --git a/Tests/RunCMake/Instrumentation/check-make-program-hooks.cmake b/Tests/RunCMake/Instrumentation/check-make-program-hooks.cmake new file mode 100644 index 0000000..60b2f7b --- /dev/null +++ b/Tests/RunCMake/Instrumentation/check-make-program-hooks.cmake @@ -0,0 +1,35 @@ +set(NUM_TRIES 30) +set(DELAY 1) + +if (NOT EXISTS ${v1}/preBuild.hook) + set(RunCMake_TEST_FAILED "preBuild hook did not run\n") +endif() + +macro(hasPostBuildArtifacts) + if (NOT postBuildRan AND EXISTS ${v1}/postBuild.hook) + set(postBuildRan 1) + endif() + if (NOT dataDirClean) + file(GLOB snippets "${v1}/data/*") + if ("${snippets}" STREQUAL "") + set(dataDirClean 1) + endif() + endif() +endmacro() + +set(postBuildRan 0) +set(dataDirClean 0) +foreach(_ RANGE ${NUM_TRIES}) + hasPostBuildArtifacts() + if (postBuildRan AND dataDirClean) + break() + endif() + execute_process(COMMAND ${CMAKE_COMMAND} -E sleep ${DELAY}) +endforeach() + +if (NOT postBuildRan) + string(APPEND RunCMake_TEST_FAILED "postBuild hook did not run\n") +endif() +if (NOT dataDirClean) + string(APPEND RunCMake_TEST_FAILED "Snippet files not fully removed post build\n") +endif() diff --git a/Tests/RunCMake/Instrumentation/check-ninja-hooks.cmake b/Tests/RunCMake/Instrumentation/check-ninja-hooks.cmake deleted file mode 100644 index 60b2f7b..0000000 --- a/Tests/RunCMake/Instrumentation/check-ninja-hooks.cmake +++ /dev/null @@ -1,35 +0,0 @@ -set(NUM_TRIES 30) -set(DELAY 1) - -if (NOT EXISTS ${v1}/preBuild.hook) - set(RunCMake_TEST_FAILED "preBuild hook did not run\n") -endif() - -macro(hasPostBuildArtifacts) - if (NOT postBuildRan AND EXISTS ${v1}/postBuild.hook) - set(postBuildRan 1) - endif() - if (NOT dataDirClean) - file(GLOB snippets "${v1}/data/*") - if ("${snippets}" STREQUAL "") - set(dataDirClean 1) - endif() - endif() -endmacro() - -set(postBuildRan 0) -set(dataDirClean 0) -foreach(_ RANGE ${NUM_TRIES}) - hasPostBuildArtifacts() - if (postBuildRan AND dataDirClean) - break() - endif() - execute_process(COMMAND ${CMAKE_COMMAND} -E sleep ${DELAY}) -endforeach() - -if (NOT postBuildRan) - string(APPEND RunCMake_TEST_FAILED "postBuild hook did not run\n") -endif() -if (NOT dataDirClean) - string(APPEND RunCMake_TEST_FAILED "Snippet files not fully removed post build\n") -endif() diff --git a/Tests/RunCMake/Instrumentation/project/CMakeLists.txt b/Tests/RunCMake/Instrumentation/project/CMakeLists.txt index 30b28c6..0d90faf 100644 --- a/Tests/RunCMake/Instrumentation/project/CMakeLists.txt +++ b/Tests/RunCMake/Instrumentation/project/CMakeLists.txt @@ -13,6 +13,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E true OUTPUT output1 output2 ) +set_property(SOURCE output1 output2 PROPERTY SYMBOLIC 1) add_custom_target(customTarget ALL DEPENDS output1 ) diff --git a/Tests/RunCMake/Instrumentation/query/cmake-command-make-program.cmake b/Tests/RunCMake/Instrumentation/query/cmake-command-make-program.cmake new file mode 100644 index 0000000..807d991 --- /dev/null +++ b/Tests/RunCMake/Instrumentation/query/cmake-command-make-program.cmake @@ -0,0 +1,7 @@ +file(TO_CMAKE_PATH "${CMAKE_SOURCE_DIR}/../hook.cmake" hook_path) +cmake_instrumentation( + API_VERSION 1 + DATA_VERSION 1 + HOOKS preBuild postBuild + CALLBACK "\"${CMAKE_COMMAND}\" -P \"${hook_path}\" 0" +) diff --git a/Tests/RunCMake/Instrumentation/query/cmake-command-ninja.cmake b/Tests/RunCMake/Instrumentation/query/cmake-command-ninja.cmake deleted file mode 100644 index 60acebd..0000000 --- a/Tests/RunCMake/Instrumentation/query/cmake-command-ninja.cmake +++ /dev/null @@ -1,6 +0,0 @@ -cmake_instrumentation( - API_VERSION 1 - DATA_VERSION 1 - HOOKS preBuild postBuild - CALLBACK "\"${CMAKE_COMMAND}\" -P \"${CMAKE_SOURCE_DIR}/../hook.cmake\" 0" -) -- cgit v0.12