mirror of
https://github.com/Kitware/CMake.git
synced 2025-10-19 02:17:27 +08:00
instrumentation: Add preBuild and postBuild hooks for ninja
Allows instrumentation indexing and callbacks to occur at the start or end of every `ninja` invocation.
This commit is contained in:
@@ -117,8 +117,10 @@ optional.
|
|||||||
should be one of the following:
|
should be one of the following:
|
||||||
|
|
||||||
* ``postGenerate``
|
* ``postGenerate``
|
||||||
* ``preCMakeBuild``
|
* ``preBuild`` (:ref:`Ninja Generators`. only, when ``ninja`` is invoked)
|
||||||
* ``postCMakeBuild``
|
* ``postBuild`` (:ref:`Ninja Generators`. only, when ``ninja`` completes)
|
||||||
|
* ``preCMakeBuild`` (when ``cmake --build`` is invoked)
|
||||||
|
* ``postCMakeBuild`` (when ``cmake --build`` completes)
|
||||||
* ``postInstall``
|
* ``postInstall``
|
||||||
* ``postTest``
|
* ``postTest``
|
||||||
|
|
||||||
|
@@ -32,6 +32,7 @@
|
|||||||
#include "cmGeneratedFileStream.h"
|
#include "cmGeneratedFileStream.h"
|
||||||
#include "cmGeneratorTarget.h"
|
#include "cmGeneratorTarget.h"
|
||||||
#include "cmGlobalGenerator.h"
|
#include "cmGlobalGenerator.h"
|
||||||
|
#include "cmInstrumentation.h"
|
||||||
#include "cmLinkLineComputer.h"
|
#include "cmLinkLineComputer.h"
|
||||||
#include "cmList.h"
|
#include "cmList.h"
|
||||||
#include "cmListFileCache.h"
|
#include "cmListFileCache.h"
|
||||||
@@ -1761,6 +1762,13 @@ void cmGlobalNinjaGenerator::WriteBuiltinTargets(std::ostream& os)
|
|||||||
this->WriteTargetRebuildManifest(os);
|
this->WriteTargetRebuildManifest(os);
|
||||||
this->WriteTargetClean(os);
|
this->WriteTargetClean(os);
|
||||||
this->WriteTargetHelp(os);
|
this->WriteTargetHelp(os);
|
||||||
|
#if !defined(CMAKE_BOOTSTRAP)
|
||||||
|
if (this->GetCMakeInstance()
|
||||||
|
->GetInstrumentation()
|
||||||
|
->HasPreOrPostBuildHook()) {
|
||||||
|
this->WriteTargetInstrument(os);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
for (std::string const& config : this->GetConfigNames()) {
|
for (std::string const& config : this->GetConfigNames()) {
|
||||||
this->WriteTargetDefault(*this->GetConfigFileStream(config));
|
this->WriteTargetDefault(*this->GetConfigFileStream(config));
|
||||||
@@ -1835,6 +1843,14 @@ void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os)
|
|||||||
}
|
}
|
||||||
reBuild.ImplicitDeps.push_back(this->CMakeCacheFile);
|
reBuild.ImplicitDeps.push_back(this->CMakeCacheFile);
|
||||||
|
|
||||||
|
#if !defined(CMAKE_BOOTSTRAP)
|
||||||
|
if (this->GetCMakeInstance()
|
||||||
|
->GetInstrumentation()
|
||||||
|
->HasPreOrPostBuildHook()) {
|
||||||
|
reBuild.ExplicitDeps.push_back(this->NinjaOutputPath("start_instrument"));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Use 'console' pool to get non buffered output of the CMake re-run call
|
// Use 'console' pool to get non buffered output of the CMake re-run call
|
||||||
// Available since Ninja 1.5
|
// Available since Ninja 1.5
|
||||||
if (this->SupportsDirectConsole()) {
|
if (this->SupportsDirectConsole()) {
|
||||||
@@ -2180,6 +2196,42 @@ void cmGlobalNinjaGenerator::WriteTargetHelp(std::ostream& os)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cmGlobalNinjaGenerator::WriteTargetInstrument(std::ostream& os)
|
||||||
|
{
|
||||||
|
// Write rule
|
||||||
|
{
|
||||||
|
cmNinjaRule rule("START_INSTRUMENT");
|
||||||
|
rule.Command = cmStrCat(
|
||||||
|
"\"", cmSystemTools::GetCTestCommand(), "\" --start-instrumentation \"",
|
||||||
|
this->GetCMakeInstance()->GetHomeOutputDirectory(), "\"");
|
||||||
|
#ifndef _WIN32
|
||||||
|
/*
|
||||||
|
* On Unix systems, Ninja will prefix the command with `/bin/sh -c`.
|
||||||
|
* Use exec so that Ninja is the parent process of the command.
|
||||||
|
*/
|
||||||
|
rule.Command = cmStrCat("exec ", rule.Command);
|
||||||
|
#endif
|
||||||
|
rule.Description = "Collecting build metrics";
|
||||||
|
rule.Comment = "Rule to initialize instrumentation daemon.";
|
||||||
|
rule.Restat = "1";
|
||||||
|
WriteRule(*this->RulesFileStream, rule);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write build
|
||||||
|
{
|
||||||
|
cmNinjaBuild phony("phony");
|
||||||
|
phony.Comment = "Phony target to keep START_INSTRUMENTATION out of date.";
|
||||||
|
phony.Outputs.push_back(this->NinjaOutputPath("CMakeFiles/instrument"));
|
||||||
|
cmNinjaBuild instrument("START_INSTRUMENT");
|
||||||
|
instrument.Comment = "Start instrumentation daemon.";
|
||||||
|
instrument.Outputs.push_back(this->NinjaOutputPath("start_instrument"));
|
||||||
|
instrument.ExplicitDeps.push_back(
|
||||||
|
this->NinjaOutputPath("CMakeFiles/instrument"));
|
||||||
|
WriteBuild(os, phony);
|
||||||
|
WriteBuild(os, instrument);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void cmGlobalNinjaGenerator::InitOutputPathPrefix()
|
void cmGlobalNinjaGenerator::InitOutputPathPrefix()
|
||||||
{
|
{
|
||||||
this->OutputPathPrefix =
|
this->OutputPathPrefix =
|
||||||
|
@@ -536,6 +536,7 @@ private:
|
|||||||
void WriteTargetRebuildManifest(std::ostream& os);
|
void WriteTargetRebuildManifest(std::ostream& os);
|
||||||
bool WriteTargetCleanAdditional(std::ostream& os);
|
bool WriteTargetCleanAdditional(std::ostream& os);
|
||||||
void WriteTargetClean(std::ostream& os);
|
void WriteTargetClean(std::ostream& os);
|
||||||
|
void WriteTargetInstrument(std::ostream& os);
|
||||||
void WriteTargetHelp(std::ostream& os);
|
void WriteTargetHelp(std::ostream& os);
|
||||||
|
|
||||||
void ComputeTargetDependsClosure(
|
void ComputeTargetDependsClosure(
|
||||||
|
@@ -11,6 +11,7 @@
|
|||||||
#include <cm/optional>
|
#include <cm/optional>
|
||||||
|
|
||||||
#include <cm3p/json/writer.h>
|
#include <cm3p/json/writer.h>
|
||||||
|
#include <cm3p/uv.h>
|
||||||
|
|
||||||
#include "cmsys/Directory.hxx"
|
#include "cmsys/Directory.hxx"
|
||||||
#include "cmsys/FStream.hxx"
|
#include "cmsys/FStream.hxx"
|
||||||
@@ -22,6 +23,7 @@
|
|||||||
#include "cmStringAlgorithms.h"
|
#include "cmStringAlgorithms.h"
|
||||||
#include "cmSystemTools.h"
|
#include "cmSystemTools.h"
|
||||||
#include "cmTimestamp.h"
|
#include "cmTimestamp.h"
|
||||||
|
#include "cmUVProcessChain.h"
|
||||||
|
|
||||||
cmInstrumentation::cmInstrumentation(std::string const& binary_dir)
|
cmInstrumentation::cmInstrumentation(std::string const& binary_dir)
|
||||||
{
|
{
|
||||||
@@ -485,3 +487,46 @@ std::string cmInstrumentation::ComputeSuffixTime()
|
|||||||
<< std::setfill('0') << std::setw(4) << tms;
|
<< std::setfill('0') << std::setw(4) << tms;
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called by ctest --start-instrumentation as part of the START_INSTRUMENTATION
|
||||||
|
* rule when using the Ninja generator.
|
||||||
|
* This creates a detached process which waits for the Ninja process to die
|
||||||
|
* before running the postBuild hook. In this way, the postBuild hook triggers
|
||||||
|
* after every ninja invocation, regardless of whether the build passed or
|
||||||
|
* failed.
|
||||||
|
*/
|
||||||
|
int cmInstrumentation::SpawnBuildDaemon()
|
||||||
|
{
|
||||||
|
// preBuild Hook
|
||||||
|
this->CollectTimingData(cmInstrumentationQuery::Hook::PreBuild);
|
||||||
|
|
||||||
|
// postBuild Hook
|
||||||
|
if (this->HasHook(cmInstrumentationQuery::Hook::PostBuild)) {
|
||||||
|
auto ninja_pid = uv_os_getppid();
|
||||||
|
if (ninja_pid) {
|
||||||
|
std::vector<std::string> args;
|
||||||
|
args.push_back(cmSystemTools::GetCTestCommand());
|
||||||
|
args.push_back("--wait-and-collect-instrumentation");
|
||||||
|
args.push_back(this->binaryDir);
|
||||||
|
args.push_back(std::to_string(ninja_pid));
|
||||||
|
auto builder = cmUVProcessChainBuilder().SetDetached().AddCommand(args);
|
||||||
|
auto chain = builder.Start();
|
||||||
|
uv_run(&chain.GetLoop(), UV_RUN_DEFAULT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Always called by ctest --wait-and-collect-instrumentation in a detached
|
||||||
|
* process. Waits for the given PID to end before running the postBuild hook.
|
||||||
|
*
|
||||||
|
* See SpawnBuildDaemon()
|
||||||
|
*/
|
||||||
|
int cmInstrumentation::CollectTimingAfterBuild(int ppid)
|
||||||
|
{
|
||||||
|
while (0 == uv_kill(ppid, 0)) {
|
||||||
|
};
|
||||||
|
return this->CollectTimingData(cmInstrumentationQuery::Hook::PostBuild);
|
||||||
|
}
|
||||||
|
@@ -46,6 +46,8 @@ public:
|
|||||||
std::string& callback);
|
std::string& callback);
|
||||||
void ClearGeneratedQueries();
|
void ClearGeneratedQueries();
|
||||||
int CollectTimingData(cmInstrumentationQuery::Hook hook);
|
int CollectTimingData(cmInstrumentationQuery::Hook hook);
|
||||||
|
int SpawnBuildDaemon();
|
||||||
|
int CollectTimingAfterBuild(int ppid);
|
||||||
std::string errorMsg;
|
std::string errorMsg;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@@ -189,6 +189,18 @@ int main(int argc, char const* const* argv)
|
|||||||
return cmCTestLaunch::Main(argc, argv, cmCTestLaunch::Op::Instrument);
|
return cmCTestLaunch::Main(argc, argv, cmCTestLaunch::Op::Instrument);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dispatch post-build instrumentation daemon for ninja
|
||||||
|
if (argc == 3 && strcmp(argv[1], "--start-instrumentation") == 0) {
|
||||||
|
return cmInstrumentation(argv[2]).SpawnBuildDaemon();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dispatch 'ctest --collect-instrumentation' once given PID finishes
|
||||||
|
if (argc == 4 &&
|
||||||
|
strcmp(argv[1], "--wait-and-collect-instrumentation") == 0) {
|
||||||
|
return cmInstrumentation(argv[2]).CollectTimingAfterBuild(
|
||||||
|
std::stoi(argv[3]));
|
||||||
|
}
|
||||||
|
|
||||||
// Dispatch 'ctest --collect-instrumentation' mode directly.
|
// Dispatch 'ctest --collect-instrumentation' mode directly.
|
||||||
if (argc == 3 && strcmp(argv[1], "--collect-instrumentation") == 0) {
|
if (argc == 3 && strcmp(argv[1], "--collect-instrumentation") == 0) {
|
||||||
return cmInstrumentation(argv[2]).CollectTimingData(
|
return cmInstrumentation(argv[2]).CollectTimingData(
|
||||||
|
@@ -6,7 +6,7 @@ function(instrument test)
|
|||||||
set(config "${CMAKE_CURRENT_LIST_DIR}/config")
|
set(config "${CMAKE_CURRENT_LIST_DIR}/config")
|
||||||
set(ENV{CMAKE_CONFIG_DIR} ${config})
|
set(ENV{CMAKE_CONFIG_DIR} ${config})
|
||||||
cmake_parse_arguments(ARGS
|
cmake_parse_arguments(ARGS
|
||||||
"BUILD;INSTALL;TEST;COPY_QUERIES;NO_WARN;STATIC_QUERY;DYNAMIC_QUERY;INSTALL_PARALLEL;MANUAL_HOOK"
|
"BUILD;BUILD_MAKE_PROGRAM;INSTALL;TEST;COPY_QUERIES;NO_WARN;STATIC_QUERY;DYNAMIC_QUERY;INSTALL_PARALLEL;MANUAL_HOOK"
|
||||||
"CHECK_SCRIPT;CONFIGURE_ARG" "" ${ARGN})
|
"CHECK_SCRIPT;CONFIGURE_ARG" "" ${ARGN})
|
||||||
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test})
|
set(RunCMake_TEST_BINARY_DIR ${RunCMake_BINARY_DIR}/${test})
|
||||||
set(uuid "a37d1069-1972-4901-b9c9-f194aaf2b6e0")
|
set(uuid "a37d1069-1972-4901-b9c9-f194aaf2b6e0")
|
||||||
@@ -58,6 +58,9 @@ function(instrument test)
|
|||||||
if (ARGS_BUILD)
|
if (ARGS_BUILD)
|
||||||
run_cmake_command(${test}-build ${CMAKE_COMMAND} --build . --config Debug)
|
run_cmake_command(${test}-build ${CMAKE_COMMAND} --build . --config Debug)
|
||||||
endif()
|
endif()
|
||||||
|
if (ARGS_BUILD_MAKE_PROGRAM)
|
||||||
|
run_cmake_command(${test}-make-program ${RunCMake_MAKE_PROGRAM})
|
||||||
|
endif()
|
||||||
if (ARGS_INSTALL)
|
if (ARGS_INSTALL)
|
||||||
run_cmake_command(${test}-install ${CMAKE_COMMAND} --install . --prefix install --config Debug)
|
run_cmake_command(${test}-install ${CMAKE_COMMAND} --install . --prefix install --config Debug)
|
||||||
endif()
|
endif()
|
||||||
@@ -112,3 +115,8 @@ instrument(cmake-command-bad-arg NO_WARN)
|
|||||||
instrument(cmake-command-parallel-install
|
instrument(cmake-command-parallel-install
|
||||||
BUILD INSTALL TEST NO_WARN INSTALL_PARALLEL DYNAMIC_QUERY
|
BUILD INSTALL TEST NO_WARN INSTALL_PARALLEL DYNAMIC_QUERY
|
||||||
CHECK_SCRIPT check-data-dir.cmake)
|
CHECK_SCRIPT check-data-dir.cmake)
|
||||||
|
if (UNIX AND ${RunCMake_GENERATOR} MATCHES "^Ninja")
|
||||||
|
instrument(cmake-command-ninja NO_WARN
|
||||||
|
BUILD_MAKE_PROGRAM
|
||||||
|
CHECK_SCRIPT check-ninja-hooks.cmake)
|
||||||
|
endif()
|
||||||
|
35
Tests/RunCMake/Instrumentation/check-ninja-hooks.cmake
Normal file
35
Tests/RunCMake/Instrumentation/check-ninja-hooks.cmake
Normal file
@@ -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()
|
@@ -68,3 +68,7 @@ has_key(vendorString ${staticSystemInformation} ${hasStaticInfo})
|
|||||||
if (NOT ERROR_MESSAGE MATCHES "^$")
|
if (NOT ERROR_MESSAGE MATCHES "^$")
|
||||||
message(FATAL_ERROR ${ERROR_MESSAGE})
|
message(FATAL_ERROR ${ERROR_MESSAGE})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
get_filename_component(dataDir ${index} DIRECTORY)
|
||||||
|
get_filename_component(v1 ${dataDir} DIRECTORY)
|
||||||
|
file(TOUCH ${v1}/${hook}.hook)
|
||||||
|
@@ -0,0 +1,6 @@
|
|||||||
|
cmake_instrumentation(
|
||||||
|
API_VERSION 1
|
||||||
|
DATA_VERSION 1
|
||||||
|
HOOKS preBuild postBuild
|
||||||
|
CALLBACK "\"${CMAKE_COMMAND}\" -P \"${CMAKE_SOURCE_DIR}/../hook.cmake\" 0"
|
||||||
|
)
|
Reference in New Issue
Block a user