if(NOT DEFINED APP OR NOT DEFINED EXPECTED_VERSION) message(FATAL_ERROR "APP and EXPECTED_VERSION are required") endif() if(NOT EXPECTED_VERSION MATCHES "^[0-9]+\\.[0-9]+\\.[1-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]$") message(FATAL_ERROR "Invalid numeric build version: ${EXPECTED_VERSION}") endif() foreach(mode IN ITEMS default help version invalid extra) set(args) set(expected_exit 0) if(mode STREQUAL "help") set(args --help) elseif(mode STREQUAL "version") set(args --version) elseif(mode STREQUAL "invalid") set(args --record) set(expected_exit 2) elseif(mode STREQUAL "extra") set(args --help --record) set(expected_exit 2) endif() execute_process(COMMAND "${APP}" ${args} RESULT_VARIABLE result OUTPUT_VARIABLE output ERROR_VARIABLE error TIMEOUT 5) if(NOT "${result}" STREQUAL "${expected_exit}") message(FATAL_ERROR "${mode}: exit ${result}, expected ${expected_exit}: ${error}") endif() if(mode STREQUAL "version") # Remove only the final terminator; a clean release has no git line. string(REGEX REPLACE "\n$" "" version_lines "${output}") string(REPLACE "\n" ";" lines "${version_lines}") list(GET lines 0 first_line) if(NOT first_line STREQUAL "frameyap ${EXPECTED_VERSION}") message(FATAL_ERROR "Unexpected version: ${output}") endif() list(LENGTH lines count) if(count GREATER 1) list(GET lines 1 second_line) if(NOT second_line MATCHES "^git [0-9a-f]+( \\(uncommitted changes\\))?$") message(FATAL_ERROR "Unexpected developer information: ${output}") endif() endif() if(count GREATER 2) list(GET lines 2 trailing_line) if(NOT trailing_line STREQUAL "" OR count GREATER 3) message(FATAL_ERROR "Extra version output: ${output}") endif() endif() elseif(expected_exit EQUAL 2) if(NOT error MATCHES "Unsupported arguments") message(FATAL_ERROR "Missing rejection diagnostic") endif() elseif(NOT output MATCHES "No device access unless" OR NOT output MATCHES "--model-store DIR" OR NOT output MATCHES "manifest-managed Models UI") message(FATAL_ERROR "Missing explicit runtime opt-in or generic backend help") endif() endforeach() # A namesake worker outside the installation root is custom, even if its # basename is worker.py. Reject this before runtime setup (on offline builds, # before the generic "runtime disabled" diagnostic). foreach(extra IN ITEMS "--backend;redux" "") set(args --run --assets /app/assets/ --worker /other/worker.py) if(NOT extra STREQUAL "") list(APPEND args ${extra}) endif() execute_process(COMMAND "${APP}" ${args} RESULT_VARIABLE result OUTPUT_VARIABLE output ERROR_VARIABLE error) if(NOT result EQUAL 2 OR NOT error MATCHES "custom --worker" OR error MATCHES "runtime disabled" OR NOT output STREQUAL "") message(FATAL_ERROR "Custom worker bypassed syntax validation: ${result}: ${error}") endif() endforeach()