diff --git a/Makefile b/Makefile index 86efc1c..9139fb3 100755 --- a/Makefile +++ b/Makefile @@ -45,7 +45,7 @@ define run_cmake = -DCMAKE_INSTALL_PREFIX=$(abspath $(INSTALL_PREFIX)) \ -DCMAKE_EXPORT_COMPILE_COMMANDS=1 \ -DCMAKE_PREFIX_PATH=$(CURDIR)/infra/cmake \ - -DCMAKE_PROJECT_TOP_LEVEL_INCLUDES="./cmake/use-fetch-content.cmake" \ + -DCMAKE_PROJECT_TOP_LEVEL_INCLUDES="./cmake/use-fetch-content.cmake;infra/cmake/bemancmakeinstrumentation.cmake" \ $(_cmake_args) \ $(CURDIR) endef @@ -85,10 +85,10 @@ clean-install: realclean: clean-install ctest: $(_build_path)/CMakeCache.txt ## Run CTest on current build - cd $(_build_path) && ctest --output-on-failure -C $(CONFIG) + cd $(_build_path) && ctest --parallel --output-on-failure -C $(CONFIG) ctest_ : compile - cd $(_build_path) && ctest --output-on-failure -C $(CONFIG) + cd $(_build_path) && ctest --parallel --output-on-failure -C $(CONFIG) test: ctest_ ## Rebuild and run tests diff --git a/infra/README.md b/infra/README.md index 16b2672..2e6f36a 100644 --- a/infra/README.md +++ b/infra/README.md @@ -53,3 +53,21 @@ Some options for the project and target will also be supported: * `BEMAN_INSTALL_CONFIG_FILE_PACKAGES` - a list of package names (e.g., `beman.something`) for which to install the config file (default: all packages) * `_INSTALL_CONFIG_FILE_PACKAGE` - a per-project option to enable/disable config file installation (default: `ON` if the project is top-level, `OFF` otherwise). For instance for `beman.something`, the option would be `BEMAN_SOMETHING_INSTALL_CONFIG_FILE_PACKAGE`. + +#### `beman_cmake_instrumentation` + +The cmake modules in this library are intended to provide access to CMake instrumentation data in Google Trace format which is visualizable with chrome://tracing and https://ui.perfetto.dev. + +Instrumentation may be enabled either by adding to the CMAKE_PROJECT_TOP_LEVEL_INCLUDES +```sh +-DCMAKE_PROJECT_TOP_LEVEL_INCLUDES=infra/cmake/bemancmakeinstrumentation.cmake +``` +or by calling explicitly within the CMakeList.txt file. +```cmake +find_package(BemanCMakeInstrumentation) +configure_beman_cmake_instrumentation() +``` + +In either form, CMake will call `instrumentation.sh` which will copy the trace data in json format into a `.trace` subdirectory within the build directory. + +Multiple calls to `configure_beman_cmake_instrumentation` will only configure the callback hooks once, so it is safe to include multiple times, including by TOP_LEVEL_INCLUDE. diff --git a/infra/cmake/bemancmakeinstrumentation-config.cmake b/infra/cmake/bemancmakeinstrumentation-config.cmake new file mode 100755 index 0000000..135b492 --- /dev/null +++ b/infra/cmake/bemancmakeinstrumentation-config.cmake @@ -0,0 +1,29 @@ +include_guard(GLOBAL) + +cmake_minimum_required (VERSION 4.2) + +set(BEMAN_CMAKE_INSTRUMENTATION_DIR ${CMAKE_CURRENT_LIST_DIR}) + +function(configure_beman_cmake_instrumentation) + if(NOT BEMAN_CMAKE_INSTRUMENTATION_CONFIGURATION) + message(WARNING "Configuring Beman CMake Instrumentation") + + # Enable experimental feature!! + set(CMAKE_EXPERIMENTAL_INSTRUMENTATION ec7aa2dc-b87f-45a3-8022-fe01c5f59984) + + # Instrumentation query + cmake_instrumentation( + API_VERSION 1 + DATA_VERSION 1 + + OPTIONS staticSystemInformation dynamicSystemInformation trace + HOOKS postGenerate preBuild postBuild preCMakeBuild postCMakeBuild postCMakeInstall postCTest + CALLBACK ${BEMAN_CMAKE_INSTRUMENTATION_DIR}/instrumentation.sh + ) + message(WARNING "using callback script ${BB_CMAKE_INSTRUMENTATION_DIR}/instrumentation.sh") + + # Mark task as done in cache + set(BEMAN_CMAKE_INSTRUMENTATION_CONFIGURATION TRUE CACHE INTERNAL "Flag to ensure CMake Instrumentation configured only once") + endif() + +endfunction(configure_beman_cmake_instrumentation) diff --git a/infra/cmake/bemancmakeinstrumentation.cmake b/infra/cmake/bemancmakeinstrumentation.cmake new file mode 100755 index 0000000..7a171bd --- /dev/null +++ b/infra/cmake/bemancmakeinstrumentation.cmake @@ -0,0 +1,6 @@ +include_guard(GLOBAL) + +cmake_minimum_required (VERSION 4.2) + +find_package(BemanCMakeInstrumentation) +configure_beman_cmake_instrumentation() diff --git a/infra/cmake/instrumentation.sh b/infra/cmake/instrumentation.sh new file mode 100755 index 0000000..a1568bd --- /dev/null +++ b/infra/cmake/instrumentation.sh @@ -0,0 +1,115 @@ +#!/usr/bin/env bash + +set -o nounset +set -o errexit +trap 'echo "Aborting due to errexit on line $LINENO. Exit code: $?" >&2' ERR +set -o errtrace +set -o pipefail +IFS=$'\n\t' + +############################################################################### +# Environment +############################################################################### + +# $_ME +# +# This program's basename. +_ME="$(basename "${0}")" + +############################################################################### +# Help +############################################################################### + +# _print_help() +# +# Usage: +# _print_help +# +# Print the program help information. +_print_help() { + cat <] + ${_ME} -h | --help + +Options: + -h --help Show this screen. +HEREDOC +} + +############################################################################### +# Program Functions +############################################################################### +_debug_print() { + if [[ -n "${DEBUG:-}" ]]; then + printf "[DEBUG] $(date +'%H:%M:%S'): %s \n" "$1" >&2 + fi +} + +_check_file_exists() { + local file="$1" + if [[ ! -f "${file}" ]]; then + echo "Error: File not found: ${file}" >&2 + exit 1 # Exit the entire script with a non-zero status + fi +} + +_process_index() { + indexFile=${1:-} + _check_file_exists "${indexFile}" + _debug_print "$(cat "${indexFile}")" + + local buildDir + buildDir=$(jq -r '.buildDir' "${1:-}") + _debug_print "$(printf "buildDir is |%q|" "${buildDir}")" + + local dataDir + dataDir=$(jq -r '.dataDir' "${1:-}") + _debug_print "$(printf "dataDir is |%q|" "${dataDir}")" + + local hook + hook=$(jq -r '.hook' "${1:-}") + _debug_print "$(printf "hook is |%q|" "${hook}")" + + local trace + trace=$(jq -r '.trace' "${1:-}") + _debug_print "$(printf "trace is |%q|" "${trace}")" + + local outputDir + outputDir="${buildDir}/.trace" + _debug_print "$(printf "Copy trace to |%q|" "${outputDir}")" + mkdir -p "${outputDir}" + + local traceDestFile + traceDestFile="${outputDir}/${hook}-$(basename "${trace}")" + _debug_print "$(printf "traceDestFile: |%q|" "${traceDestFile}")" + cp "${dataDir}/${trace}" "${outputDir}/${hook}-$(basename "${trace}")" +} + +############################################################################### +# Main +############################################################################### + +# _main() +# +# Usage: +# _main [] [] +# +# Description: +# Entry point for the program, handling basic option parsing and dispatching. +_main() { + # Avoid complex option parsing when only one program option is expected. + if [[ "${1:-}" =~ ^-h|--help$ ]] + then + _print_help + else + _process_index "$@" + fi +} + +# Call `_main` after everything has been defined. +_main "$@"