blob: d9638a2963aabbddd0fa74263070c2890194216e [file]
cmake_minimum_required(VERSION 3.30)
include(${CMAKE_CURRENT_LIST_DIR}/json.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/validate_schema.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/verify-snippet.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/verify-trace.cmake)
# Test CALLBACK script. Prints output information and verifies index file
# Called as: cmake -P -DSTATIC_QUERY=<ON|OFF> -DTRACE_QUERY=<ON|OFF> \
# -DCMake_TEST_JSON_SCHEMA=<ON|OFF> -DPython_EXECUTABLE=<path> \
# hook.cmake index-*.json
# Get the index file as the last argument.
math(EXPR last_arg_idx "${CMAKE_ARGC} - 1")
set(index "${CMAKE_ARGV${last_arg_idx}}")
if(NOT index MATCHES "index-.*\.json")
message(FATAL_ERROR "Received unexpected index argument: ${index}")
endif()
# Verify that we received the expected arguments.
function(check_args vars)
foreach(var IN LISTS vars)
if (NOT DEFINED ${var})
message(FATAL_ERROR "Expected argument ${var}, but none given.")
endif()
endforeach()
endfunction()
check_args("STATIC_QUERY;TRACE_QUERY;Python_EXECUTABLE;CMake_TEST_JSON_SCHEMA")
function(init_query_var input_var output_var)
set(${output_var})
if (NOT ${input_var})
set(${output_var} "UNEXPECTED")
endif()
return(PROPAGATE ${output_var})
endfunction()
init_query_var(STATIC_QUERY hasStaticInfo)
init_query_var(TRACE_QUERY hasTrace)
read_json("${index}" contents)
string(JSON hook GET "${contents}" hook)
# Output is verified by *-stdout.txt files that the HOOK is run
message(STATUS ${hook})
# Not a check-*.cmake script, this is called as an instrumentation CALLBACK
set(ERROR_MESSAGE "")
function(add_error error)
string(APPEND ERROR_MESSAGE "${error}\n")
return(PROPAGATE ERROR_MESSAGE)
endfunction()
validate_schema(
"${index}"
"${CMAKE_CURRENT_LIST_DIR}/../../../Help/manual/instrumentation/index-v1-schema.json"
# We expect to always generate valid index files.
0
)
if (RunCMake_TEST_FAILED)
add_error("${RunCMake_TEST_FAILED}")
unset(RunCMake_TEST_FAILED)
endif()
json_has_key("${index}" "${contents}" version)
json_has_key("${index}" "${contents}" buildDir)
json_has_key("${index}" "${contents}" dataDir)
json_has_key("${index}" "${contents}" snippets)
if (NOT version EQUAL 1)
add_error("Version must be 1, got: ${version}")
endif()
string(JSON n_snippets LENGTH "${snippets}")
math(EXPR snippets_range "${n_snippets}-1")
foreach(i RANGE ${snippets_range})
string(JSON filename GET "${snippets}" ${i})
if (NOT EXISTS ${dataDir}/${filename})
add_error("Listed snippet: ${dataDir}/${filename} does not exist")
endif()
read_json(${dataDir}/${filename} snippet_contents)
verify_snippet_file(${dataDir}/${filename} "${snippet_contents}")
endforeach()
json_has_key("${index}" "${contents}" trace ${hasTrace})
if (NOT hasTrace STREQUAL UNEXPECTED)
if (NOT EXISTS ${dataDir}/${trace})
add_error("Listed trace file: ${dataDir}/${trace} does not exist")
endif()
verify_trace_file_name("${index}" "${trace}")
read_json(${dataDir}/${trace} trace_contents)
string(JSON n_entries LENGTH "${trace_contents}")
if (n_entries EQUAL 0)
add_error("Listed trace file: ${dataDir}/${trace} has no entries")
endif()
if (NOT n_entries EQUAL n_snippets)
add_error("Differing number of trace entries (${n_entries}) and snippets (${n_snippets})")
endif()
math(EXPR entries_range "${n_entries}-1")
foreach (i RANGE ${entries_range})
string(JSON entry GET "${trace_contents}" ${i})
verify_trace_entry("${trace}" "${entry}")
# In addition to validating the data in the trace entry, check that
# it is strictly equal to its corresponding snippet data.
# Ideally, the args from all trace entries could be checked at once
# against the list of snippets from the index file, but the order of
# snippets is not preserved in the trace file, so being equal to data from
# any snippet file is sufficient.
set(args_equals_snippet OFF)
string(JSON trace_args GET "${entry}" args)
foreach (j RANGE ${entries_range})
string(JSON snippet_file GET "${snippets}" ${j})
read_json(${dataDir}/${snippet_file} snippet_contents)
string(JSON args_equals_snippet EQUAL "${snippet_contents}" "${trace_args}")
if (args_equals_snippet)
break()
endif()
endforeach()
if (NOT args_equals_snippet)
add_error("Trace entry args does not match any snippet data: ${entry}")
endif()
endforeach()
endif()
json_has_key("${index}" "${contents}" staticSystemInformation ${hasStaticInfo})
if (NOT hasStaticInfo STREQUAL UNEXPECTED)
json_has_key("${index}" "${staticSystemInformation}" OSName ${hasStaticInfo})
json_has_key("${index}" "${staticSystemInformation}" OSPlatform ${hasStaticInfo})
json_has_key("${index}" "${staticSystemInformation}" OSRelease ${hasStaticInfo})
json_has_key("${index}" "${staticSystemInformation}" OSVersion ${hasStaticInfo})
json_has_key("${index}" "${staticSystemInformation}" familyId ${hasStaticInfo})
json_has_key("${index}" "${staticSystemInformation}" hostname ${hasStaticInfo})
json_has_key("${index}" "${staticSystemInformation}" is64Bits ${hasStaticInfo})
json_has_key("${index}" "${staticSystemInformation}" modelId ${hasStaticInfo})
json_has_key("${index}" "${staticSystemInformation}" modelName ${hasStaticInfo})
json_has_key("${index}" "${staticSystemInformation}" numberOfLogicalCPU ${hasStaticInfo})
json_has_key("${index}" "${staticSystemInformation}" numberOfPhysicalCPU ${hasStaticInfo})
json_has_key("${index}" "${staticSystemInformation}" processorAPICID ${hasStaticInfo})
json_has_key("${index}" "${staticSystemInformation}" processorCacheSize ${hasStaticInfo})
json_has_key("${index}" "${staticSystemInformation}" processorClockFrequency ${hasStaticInfo})
json_has_key("${index}" "${staticSystemInformation}" processorName ${hasStaticInfo})
json_has_key("${index}" "${staticSystemInformation}" totalPhysicalMemory ${hasStaticInfo})
json_has_key("${index}" "${staticSystemInformation}" totalVirtualMemory ${hasStaticInfo})
json_has_key("${index}" "${staticSystemInformation}" vendorID ${hasStaticInfo})
json_has_key("${index}" "${staticSystemInformation}" vendorString ${hasStaticInfo})
# FIXME(#27545): We currently do not guarantee that the fields above which
# output strings are non-empty. `vendorString` and `processorName` especially
# have shown issues on some platforms. This test is logically equivalent to
# RunCMake.cmake_host_system_information, which uses the same underlying
# implementation.
set(string_fields
OSName
OSPlatform
OSRelease
OSVersion
familyId
hostname
modelId
modelName
vendorID
vendorString
)
foreach (field IN LISTS string_fields)
string(JSON ${field}_type TYPE "${staticSystemInformation}" ${field})
if (NOT "${${field}_type}" STREQUAL "NULL" AND NOT "${${field}_type}" STREQUAL "STRING")
add_error("Got bad type '${${field}_type}' for field '${field}': ${${field}}")
endif()
if ("${${field}_type}" STREQUAL "STRING" AND ${field} STREQUAL "")
add_error("Got empty string for field '${field}'")
endif()
endforeach()
# We guarantee that the numeric fields are either indeed numeric, or else
# null.
set(numeric_fields
numberOfLogicalCPU
numberOfPhysicalCPU
processorAPICID
processorCacheSize
processorClockFrequency
totalPhysicalMemory
totalVirtualMemory
)
foreach (field IN LISTS numeric_fields)
string(JSON ${field}_type TYPE "${staticSystemInformation}" ${field})
if (NOT "${${field}_type}" STREQUAL "NULL" AND NOT "${${field}_type}" STREQUAL "NUMBER")
add_error("Got bad type '${${field}_type}' for field '${field}': ${${field}}")
endif()
if ("${${field}_type}" STREQUAL "NUMBER" AND ${field} LESS_EQUAL 0)
add_error("Got bad value for field '${field}': ${${field}}")
endif()
endforeach()
endif()
get_filename_component(v1 ${dataDir} DIRECTORY)
if (EXISTS ${v1}/${hook}.hook)
add_error("Received multiple triggers of the same hook: ${hook}")
endif()
file(WRITE ${v1}/${hook}.hook "${ERROR_MESSAGE}")
if (ERROR_MESSAGE)
message(FATAL_ERROR ${ERROR_MESSAGE})
endif()