Tests/RunCMake/Instrumentation: Improve quoting and escaping in CMake code

This commit is contained in:
Brad King
2025-02-06 07:33:52 -05:00
parent 4148fe5487
commit a13be1301f
6 changed files with 147 additions and 139 deletions

View File

@@ -38,7 +38,7 @@ function(instrument test)
if (ARGS_COPY_QUERIES)
file(MAKE_DIRECTORY ${RunCMake_TEST_BINARY_DIR}/query)
set(generated_queries "0;1;2")
foreach(n ${generated_queries})
foreach(n IN LISTS generated_queries)
configure_file(
"${query_dir}/generated/query-${n}.json.in"
"${RunCMake_TEST_BINARY_DIR}/query/query-${n}.json"

View File

@@ -7,13 +7,13 @@ if (NOT snippets)
endif()
set(FOUND_SNIPPETS "")
foreach(snippet ${snippets})
get_filename_component(filename ${snippet} NAME)
foreach(snippet IN LISTS snippets)
get_filename_component(filename "${snippet}" NAME)
read_json(${snippet} contents)
read_json("${snippet}" contents)
# Verify snippet file is valid
verify_snippet(${snippet} ${contents})
verify_snippet("${snippet}" "${contents}")
# Append to list of collected snippet roles
if (NOT role IN_LIST FOUND_SNIPPETS)
@@ -25,78 +25,78 @@ foreach(snippet ${snippets})
if (NOT target MATCHES NOTFOUND)
set(targets "main;lib;customTarget;TARGET_NAME")
if (NOT ${target} IN_LIST targets)
snippet_error(${snippet} "Unexpected target: ${target}")
snippet_error("${snippet}" "Unexpected target: ${target}")
endif()
endif()
# Verify output
string(JSON result GET "${contents}" result)
if (NOT ${result} EQUAL 0)
snippet_error(${snippet} "Compile command had non-0 result")
snippet_error("${snippet}" "Compile command had non-0 result")
endif()
# Verify contents of compile-* Snippets
if (filename MATCHES ^compile-)
if (filename MATCHES "^compile-")
string(JSON target GET "${contents}" target)
string(JSON source GET "${contents}" source)
string(JSON language GET "${contents}" language)
if (NOT language MATCHES "C\\+\\+")
snippet_error(${snippet} "Expected C++ compile language")
snippet_error("${snippet}" "Expected C++ compile language")
endif()
if (NOT source MATCHES "${target}.cxx$")
snippet_error(${snippet} "Unexpected source file")
snippet_error("${snippet}" "Unexpected source file")
endif()
endif()
# Verify contents of link-* Snippets
if (filename MATCHES ^link-)
if (filename MATCHES "^link-")
string(JSON target GET "${contents}" target)
string(JSON targetType GET "${contents}" targetType)
string(JSON targetLabels GET "${contents}" targetLabels)
if (target MATCHES main)
if (target MATCHES "main")
if (NOT targetType MATCHES "EXECUTABLE")
snippet_error(${snippet} "Expected EXECUTABLE, target type was ${targetType}")
snippet_error("${snippet}" "Expected EXECUTABLE, target type was ${targetType}")
endif()
string(JSON nlabels LENGTH "${targetLabels}")
if (NOT nlabels STREQUAL 2)
snippet_error(${snippet} "Missing Target Labels for: ${target}")
snippet_error("${snippet}" "Missing Target Labels for: ${target}")
else()
string(JSON label1 GET "${contents}" targetLabels 0)
string(JSON label2 GET "${contents}" targetLabels 1)
if (NOT label1 MATCHES "label1" OR NOT label2 MATCHES "label2")
snippet_error(${snippet} "Missing Target Labels for: ${target}")
snippet_error("${snippet}" "Missing Target Labels for: ${target}")
endif()
endif()
endif()
if (target MATCHES lib)
if (target MATCHES "lib")
if (NOT targetType MATCHES "STATIC_LIBRARY")
snippet_error(${snippet} "Expected STATIC_LIBRARY, target type was ${targetType}")
snippet_error("${snippet}" "Expected STATIC_LIBRARY, target type was ${targetType}")
endif()
string(JSON nlabels LENGTH "${targetLabels}")
if (NOT nlabels STREQUAL 1)
snippet_error(${snippet} "Missing Target Labels for: ${target}")
snippet_error("${snippet}" "Missing Target Labels for: ${target}")
else()
string(JSON label ERROR_VARIABLE noLabels GET "${contents}" targetLabels 0)
if (NOT label MATCHES "label3")
snippet_error(${snippet} "Missing Target Labels for: ${target}")
snippet_error("${snippet}" "Missing Target Labels for: ${target}")
endif()
endif()
endif()
endif()
# Verify contents of custom-* Snippets
if (filename MATCHES ^custom-)
if (filename MATCHES "^custom-")
string(JSON outputs GET "${contents}" outputs)
if (NOT output1 MATCHES "output1" OR NOT output2 MATCHES "output2")
snippet_error(${snippet} "Custom command missing outputs")
snippet_error("${snippet}" "Custom command missing outputs")
endif()
endif()
# Verify contents of test-* Snippets
if (filename MATCHES ^test-)
if (filename MATCHES "^test-")
string(JSON testName GET "${contents}" testName)
if (NOT testName STREQUAL "test")
snippet_error(${snippet} "Unexpected testName: ${testName}")
snippet_error("${snippet}" "Unexpected testName: ${testName}")
endif()
endif()
endforeach()
@@ -115,14 +115,14 @@ if (ARGS_INSTALL)
list(APPEND EXPECTED_SNIPPETS install)
endif()
endif()
foreach(role ${EXPECTED_SNIPPETS})
list(FIND FOUND_SNIPPETS ${role} found)
if (${found} EQUAL -1)
foreach(role IN LISTS EXPECTED_SNIPPETS)
list(FIND FOUND_SNIPPETS "${role}" found)
if (found EQUAL -1)
add_error("No snippet files of role \"${role}\" were found in ${v1}")
endif()
endforeach()
foreach(role ${FOUND_SNIPPETS})
list(FIND EXPECTED_SNIPPETS ${role} found)
foreach(role IN LISTS FOUND_SNIPPETS)
list(FIND EXPECTED_SNIPPETS "${role}" found)
if (${found} EQUAL -1)
add_error("Found unexpected snippet file of role \"${role}\" in ${v1}")
endif()

View File

@@ -1,17 +1,18 @@
include(${CMAKE_CURRENT_LIST_DIR}/json.cmake)
macro(check_generated_json n)
set(expected_file ${RunCMake_TEST_BINARY_DIR}/query/query-${n}.json)
set(generated_file ${v1}/query/generated/query-${n}.json)
read_json(${expected_file} expected)
read_json(${generated_file} generated)
function(check_generated_json n)
set(expected_file "${RunCMake_TEST_BINARY_DIR}/query/query-${n}.json")
set(generated_file "${v1}/query/generated/query-${n}.json")
read_json("${expected_file}" expected)
read_json("${generated_file}" generated)
string(JSON equal EQUAL ${expected} ${generated})
if (NOT equal)
set(RunCMake_TEST_FAILED
"Generated JSON ${generated}\nNot equal to expected ${expected}"
)
endif()
endmacro()
return(PROPAGATE RunCMake_TEST_FAILED)
endfunction()
foreach(n ${generated_queries})
foreach(n IN LISTS generated_queries)
check_generated_json(${n})
endforeach()

View File

@@ -1,3 +1,5 @@
cmake_minimum_required(VERSION 3.30)
include(${CMAKE_CURRENT_LIST_DIR}/json.cmake)
# Test CALLBACK script. Prints output information and verifies index file
# Called as: cmake -P hook.cmake [CheckForStaticQuery?] [index.json]
@@ -5,39 +7,41 @@ set(index ${CMAKE_ARGV4})
if (NOT ${CMAKE_ARGV3})
set(hasStaticInfo "UNEXPECTED")
endif()
read_json(${index} contents)
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 "")
macro(add_error error)
function(add_error error)
string(APPEND ERROR_MESSAGE "${error}\n")
endmacro()
return(PROPAGATE ERROR_MESSAGE)
endfunction()
macro(has_key key json)
function(has_key key json)
cmake_parse_arguments(ARG "UNEXPECTED" "" "" ${ARGN})
unset(missingKey)
string(JSON ${key} ERROR_VARIABLE missingKey GET "${json}" ${key})
if (NOT ARG_UNEXPECTED AND NOT "${missingKey}" MATCHES NOTFOUND)
if (NOT ARG_UNEXPECTED AND NOT missingKey MATCHES NOTFOUND)
add_error("\nKey \"${key}\" not in index:\n${json}")
elseif(ARG_UNEXPECTED AND "${missingKey}" MATCHES NOTFOUND)
elseif(ARG_UNEXPECTED AND missingKey MATCHES NOTFOUND)
add_error("\nUnexpected key \"${key}\" in index:\n${json}")
endif()
endmacro()
return(PROPAGATE RunCMake_TEST_FAILED ${key})
endfunction()
has_key(version ${contents})
has_key(buildDir ${contents})
has_key(dataDir ${contents})
has_key(snippets ${contents})
has_key(version "${contents}")
has_key(buildDir "${contents}")
has_key(dataDir "${contents}")
has_key(snippets "${contents}")
if (NOT ${version} EQUAL 1)
if (NOT version EQUAL 1)
add_error("Version must be 1, got: ${version}")
endif()
string(JSON length LENGTH ${snippets})
math(EXPR length ${length}-1)
string(JSON length LENGTH "${snippets}")
math(EXPR length "${length}-1")
foreach(i RANGE ${length})
string(JSON filename GET "${snippets}" ${i})
if (NOT EXISTS ${dataDir}/${filename})
@@ -45,25 +49,25 @@ foreach(i RANGE ${length})
endif()
endforeach()
has_key(staticSystemInformation ${contents} ${hasStaticInfo})
has_key(OSName ${staticSystemInformation} ${hasStaticInfo})
has_key(OSPlatform ${staticSystemInformation} ${hasStaticInfo})
has_key(OSRelease ${staticSystemInformation} ${hasStaticInfo})
has_key(OSVersion ${staticSystemInformation} ${hasStaticInfo})
has_key(familyId ${staticSystemInformation} ${hasStaticInfo})
has_key(hostname ${staticSystemInformation} ${hasStaticInfo})
has_key(is64Bits ${staticSystemInformation} ${hasStaticInfo})
has_key(modelId ${staticSystemInformation} ${hasStaticInfo})
has_key(numberOfLogicalCPU ${staticSystemInformation} ${hasStaticInfo})
has_key(numberOfPhysicalCPU ${staticSystemInformation} ${hasStaticInfo})
has_key(processorAPICID ${staticSystemInformation} ${hasStaticInfo})
has_key(processorCacheSize ${staticSystemInformation} ${hasStaticInfo})
has_key(processorClockFrequency ${staticSystemInformation} ${hasStaticInfo})
has_key(processorName ${staticSystemInformation} ${hasStaticInfo})
has_key(totalPhysicalMemory ${staticSystemInformation} ${hasStaticInfo})
has_key(totalVirtualMemory ${staticSystemInformation} ${hasStaticInfo})
has_key(vendorID ${staticSystemInformation} ${hasStaticInfo})
has_key(vendorString ${staticSystemInformation} ${hasStaticInfo})
has_key(staticSystemInformation "${contents}" ${hasStaticInfo})
has_key(OSName "${staticSystemInformation}" ${hasStaticInfo})
has_key(OSPlatform "${staticSystemInformation}" ${hasStaticInfo})
has_key(OSRelease "${staticSystemInformation}" ${hasStaticInfo})
has_key(OSVersion "${staticSystemInformation}" ${hasStaticInfo})
has_key(familyId "${staticSystemInformation}" ${hasStaticInfo})
has_key(hostname "${staticSystemInformation}" ${hasStaticInfo})
has_key(is64Bits "${staticSystemInformation}" ${hasStaticInfo})
has_key(modelId "${staticSystemInformation}" ${hasStaticInfo})
has_key(numberOfLogicalCPU "${staticSystemInformation}" ${hasStaticInfo})
has_key(numberOfPhysicalCPU "${staticSystemInformation}" ${hasStaticInfo})
has_key(processorAPICID "${staticSystemInformation}" ${hasStaticInfo})
has_key(processorCacheSize "${staticSystemInformation}" ${hasStaticInfo})
has_key(processorClockFrequency "${staticSystemInformation}" ${hasStaticInfo})
has_key(processorName "${staticSystemInformation}" ${hasStaticInfo})
has_key(totalPhysicalMemory "${staticSystemInformation}" ${hasStaticInfo})
has_key(totalVirtualMemory "${staticSystemInformation}" ${hasStaticInfo})
has_key(vendorID "${staticSystemInformation}" ${hasStaticInfo})
has_key(vendorString "${staticSystemInformation}" ${hasStaticInfo})
if (NOT ERROR_MESSAGE MATCHES "^$")
message(FATAL_ERROR ${ERROR_MESSAGE})

View File

@@ -1,8 +1,4 @@
macro(read_json filename outvar)
file(READ ${filename} contents)
# string(JSON *) will fail if JSON file contains any forward-slash paths
string(REGEX REPLACE "[\\]([a-zA-Z0-9 ])" "/\\1" contents ${contents})
# string(JSON *) will fail if JSON file contains any escaped quotes \"
string(REPLACE "\\\"" "'" contents ${contents})
set(${outvar} ${contents})
endmacro()
function(read_json filename outvar)
file(READ "${filename}" ${outvar})
return(PROPAGATE ${outvar})
endfunction()

View File

@@ -1,94 +1,100 @@
# Performs generic (non-project specific) validation of v1 Snippet File Contents
macro(add_error error)
string(APPEND RunCMake_TEST_FAILED "${error}\n")
endmacro()
function(add_error error)
string(APPEND RunCMake_TEST_FAILED " ${error}\n")
return(PROPAGATE RunCMake_TEST_FAILED)
endfunction()
macro(snippet_error snippet error)
function(snippet_error snippet error)
add_error("Error in snippet file ${snippet}:\n${error}")
endmacro()
return(PROPAGATE RunCMake_TEST_FAILED)
endfunction()
macro(has_key snippet json key)
function(has_key snippet json key)
string(JSON data ERROR_VARIABLE missingKey GET "${json}" ${key})
if (NOT ${missingKey} MATCHES NOTFOUND)
snippet_error(${snippet} "Missing ${key}")
if (NOT missingKey MATCHES NOTFOUND)
snippet_error("${snippet}" "Missing ${key}")
endif()
endmacro()
return(PROPAGATE RunCMake_TEST_FAILED)
endfunction()
macro(has_not_key snippet json key)
function(has_not_key snippet json key)
string(JSON data ERROR_VARIABLE missingKey GET "${json}" ${key})
if (${missingKey} MATCHES NOTFOUND)
snippet_error(${snippet} "Has unexpected ${key}")
if (missingKey MATCHES NOTFOUND)
snippet_error("${snippet}" "Has unexpected ${key}")
endif()
endmacro()
return(PROPAGATE RunCMake_TEST_FAILED)
endfunction()
macro(snippet_has_fields snippet contents)
get_filename_component(filename ${snippet} NAME)
has_key(${snippet} ${contents} command)
has_key(${snippet} ${contents} role)
has_key(${snippet} ${contents} result)
if (filename MATCHES ^link-*)
has_key(${snippet} ${contents} target)
has_key(${snippet} ${contents} outputs)
has_key(${snippet} ${contents} outputSizes)
has_key(${snippet} ${contents} targetType)
has_key(${snippet} ${contents} targetLabels)
elseif (filename MATCHES ^compile-*)
has_key(${snippet} ${contents} target)
has_key(${snippet} ${contents} outputs)
has_key(${snippet} ${contents} outputSizes)
has_key(${snippet} ${contents} source)
has_key(${snippet} ${contents} language)
elseif (filename MATCHES ^custom-*)
has_key(${snippet} ${contents} target)
has_key(${snippet} ${contents} outputs)
has_key(${snippet} ${contents} outputSizes)
elseif (filename MATCHES ^test-*)
has_key(${snippet} ${contents} testName)
function(snippet_has_fields snippet contents)
get_filename_component(filename "${snippet}" NAME)
has_key("${snippet}" "${contents}" command)
has_key("${snippet}" "${contents}" role)
has_key("${snippet}" "${contents}" result)
if (filename MATCHES "^link-*")
has_key("${snippet}" "${contents}" target)
has_key("${snippet}" "${contents}" outputs)
has_key("${snippet}" "${contents}" outputSizes)
has_key("${snippet}" "${contents}" targetType)
has_key("${snippet}" "${contents}" targetLabels)
elseif (filename MATCHES "^compile-*")
has_key("${snippet}" "${contents}" target)
has_key("${snippet}" "${contents}" outputs)
has_key("${snippet}" "${contents}" outputSizes)
has_key("${snippet}" "${contents}" source)
has_key("${snippet}" "${contents}" language)
elseif (filename MATCHES "^custom-*")
has_key("${snippet}" "${contents}" target)
has_key("${snippet}" "${contents}" outputs)
has_key("${snippet}" "${contents}" outputSizes)
elseif (filename MATCHES "^test-*")
has_key("${snippet}" "${contents}" testName)
endif()
if(ARGS_DYNAMIC_QUERY)
has_key(${snippet} ${contents} dynamicSystemInformation)
has_key("${snippet}" "${contents}" dynamicSystemInformation)
string(JSON dynamicSystemInfo ERROR_VARIABLE noInfo GET "${contents}" dynamicSystemInformation)
if (noInfo MATCHES NOTFOUND)
has_key(${snippet} ${dynamicSystemInfo} beforeCPULoadAverage)
has_key(${snippet} ${dynamicSystemInfo} beforeHostMemoryUsed)
has_key(${snippet} ${dynamicSystemInfo} beforeCPULoadAverage)
has_key(${snippet} ${dynamicSystemInfo} beforeHostMemoryUsed)
has_key("${snippet}" ${dynamicSystemInfo} beforeCPULoadAverage)
has_key("${snippet}" ${dynamicSystemInfo} beforeHostMemoryUsed)
has_key("${snippet}" ${dynamicSystemInfo} beforeCPULoadAverage)
has_key("${snippet}" ${dynamicSystemInfo} beforeHostMemoryUsed)
endif()
else()
has_not_key(${snippet} ${contents} dynamicSystemInformation)
has_not_key("${snippet}" "${contents}" dynamicSystemInformation)
string(JSON dynamicSystemInfo ERROR_VARIABLE noInfo GET "${contents}" dynamicSystemInformation)
if (noInfo MATCHES NOTFOUND)
has_not_key(${snippet} ${dynamicSystemInfo} beforeCPULoadAverage)
has_not_key(${snippet} ${dynamicSystemInfo} beforeHostMemoryUsed)
has_not_key(${snippet} ${dynamicSystemInfo} beforeCPULoadAverage)
has_not_key(${snippet} ${dynamicSystemInfo} beforeHostMemoryUsed)
has_not_key("${snippet}" ${dynamicSystemInfo} beforeCPULoadAverage)
has_not_key("${snippet}" ${dynamicSystemInfo} beforeHostMemoryUsed)
has_not_key("${snippet}" ${dynamicSystemInfo} beforeCPULoadAverage)
has_not_key("${snippet}" ${dynamicSystemInfo} beforeHostMemoryUsed)
endif()
endif()
endmacro()
return(PROPAGATE RunCMake_TEST_FAILED)
endfunction()
macro(snippet_valid_timing contents)
function(snippet_valid_timing contents)
string(JSON start GET "${contents}" timeStart)
string(JSON duration GET "${contents}" duration)
if (${start} LESS 0)
snippet_error(${snippet} "Negative time start: ${start}")
if (start LESS 0)
snippet_error("${snippet}" "Negative time start: ${start}")
endif()
if (${duration} LESS 0)
snippet_error(${snippet} "Negative duration: ${end}")
if (duration LESS 0)
snippet_error("${snippet}" "Negative duration: ${end}")
endif()
endmacro()
return(PROPAGATE RunCMake_TEST_FAILED)
endfunction()
macro(verify_snippet snippet contents)
snippet_has_fields(${snippet} ${contents})
snippet_valid_timing(${contents})
function(verify_snippet snippet contents)
snippet_has_fields("${snippet}" "${contents}")
snippet_valid_timing("${contents}")
string(JSON version GET "${contents}" version)
if (NOT ${version} EQUAL 1)
snippet_error(${snippet} "Version must be 1, got: ${version}")
if (NOT version EQUAL 1)
snippet_error("${snippet}" "Version must be 1, got: ${version}")
endif()
string(JSON role GET "${contents}" role)
get_filename_component(filename ${snippet} NAME)
if (NOT ${filename} MATCHES ^${role}-)
snippet_error(${snippet} "Role \"${role}\" doesn't match snippet filename")
get_filename_component(filename "${snippet}" NAME)
if (NOT filename MATCHES "^${role}-")
snippet_error("${snippet}" "Role \"${role}\" doesn't match snippet filename")
endif()
string(JSON outputs ERROR_VARIABLE noOutputs GET "${contents}" outputs)
if (NOT outputs MATCHES NOTFOUND)
@@ -96,7 +102,8 @@ macro(verify_snippet snippet contents)
list(LENGTH outputs outputsLen)
list(LENGTH outputSizes outputSizesLen)
if (outputSizes MATCHES NOTFOUND OR NOT outputsLen EQUAL outputSizesLen)
snippet_error(${snippet} "outputs and outputSizes do not match")
snippet_error("${snippet}" "outputs and outputSizes do not match")
endif()
endif()
endmacro()
return(PROPAGATE RunCMake_TEST_FAILED role)
endfunction()