1# Copyright (c) 2021-2024 Huawei Device Co., Ltd. 2# Licensed under the Apache License, Version 2.0 (the "License"); 3# you may not use this file except in compliance with the License. 4# You may obtain a copy of the License at 5# 6# http://www.apache.org/licenses/LICENSE-2.0 7# 8# Unless required by applicable law or agreed to in writing, software 9# distributed under the License is distributed on an "AS IS" BASIS, 10# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11# See the License for the specific language governing permissions and 12# limitations under the License. 13 14# Add a panda assembly to the project using the specified source file 15# 16# Example usage: 17# 18# add_panda_assembly(TARGET <name> SOURCE <source> INDIR <input directory> OUTDIR <output directory> TARGETNAME <target file name>) 19# 20# Adds a panda assembly target called <name> to be build from <source> file 21# listed in the command invocation. 22function(add_panda_assembly) 23 set(prefix ARG) 24 set(noValues) 25 set(singleValues TARGET SOURCE INDIR OUTDIR TARGETNAME) 26 set(multiValues) 27 cmake_parse_arguments(${prefix} 28 "${noValues}" 29 "${singleValues}" 30 "${multiValues}" 31 ${ARGN}) 32 if (NOT DEFINED ARG_TARGET) 33 message(FATAL_ERROR "Mandatory TARGET argument is not defined.") 34 endif() 35 36 if (NOT DEFINED ARG_SOURCE) 37 message(FATAL_ERROR "Mandatory SOURCE argument is not defined.") 38 endif() 39 40 set(source_file_dir "${CMAKE_CURRENT_SOURCE_DIR}") 41 42 if (DEFINED ARG_INDIR) 43 set(source_file_dir "${ARG_INDIR}") 44 endif() 45 46 set(binary_file_dir "${CMAKE_CURRENT_BINARY_DIR}") 47 48 if (DEFINED ARG_OUTDIR) 49 set(binary_file_dir "${ARG_OUTDIR}") 50 endif() 51 52 set(target_file_name "${ARG_TARGET}") 53 54 if (DEFINED ARG_TARGETNAME) 55 set(target_file_name "${ARG_TARGETNAME}") 56 endif() 57 58 if (TARGET ARG_TARGET) 59 message(FATAL_ERROR "Target ${ARG_TARGET} is already defined.") 60 endif() 61 62 set(source_file "${source_file_dir}/${ARG_SOURCE}") 63 set(binary_file "${binary_file_dir}/${target_file_name}.abc") 64 65 if(CMAKE_CROSSCOMPILING) 66 ExternalProject_Get_Property(panda_host_tools binary_dir) 67 set(assembler_target panda_host_tools-build) 68 set(assembler_bin "${binary_dir}/assembler/ark_asm") 69 else() 70 set(assembler_target ark_asm) 71 set(assembler_bin $<TARGET_FILE:${assembler_target}>) 72 endif() 73 74 add_custom_command(OUTPUT "${binary_file}" 75 COMMENT "Building ${ARG_TARGET}" 76 COMMAND "${assembler_bin}" "${source_file}" "${binary_file}" 77 DEPENDS ${assembler_target} "${source_file}") 78 79 add_custom_target(${ARG_TARGET} DEPENDS "${binary_file}") 80 set_target_properties(${ARG_TARGET} PROPERTIES FILE "${binary_file}") 81endfunction() 82 83# Use `mkdir` instead of `file(MAKE_DIRECTORY)` to create dependency on the directory existence: 84set(COMPILER_STATS_DIR "${CMAKE_BINARY_DIR}/compiler/stats/csv") 85add_custom_target(compiler_stats_dir 86 COMMAND mkdir -p "${COMPILER_STATS_DIR}") 87 88# Add a single buildable and runnable Panda Assembly file to the build tree. 89# 90# Usage: 91# 92# panda_add_asm_file( 93# FILE <source> 94# TARGET <target> 95# [ENTRY <entry_point>] 96# [SUBDIR <subdir>] 97# [OUTPUT_FILE_VARIABLE <variable>] 98# [ERROR_FILE_VARIABLE <variable>] 99# [SKIP_BUILD TRUE|FALSE] 100# [AOT_MODE TRUE|FALSE] 101# [AOT_STATS TRUE|FALSE] 102# [AOT_COMPILER aot|llvm] 103# [DEPENDS <dependency>...] 104# [RUNTIME_OPTIONS <option>...] 105# [RUNTIME_FAIL_TEST] 106# [COMPILER_OPTIONS <option>...] 107# [AOT_GC_OPTION <option>] 108# [ENTRY_ARGUMENTS <argument>...] 109# [TIMEOUT <timeout>] 110# [LANGUAGE_CONTEXT <language>] 111# ) 112# 113# Adds a target <target> which assembles the assembly file <source> 114# with Panda assembler and runs it with Panda interpreter. 115# 116# Options: 117# 118# ENTRY 119# Entry point to execute in format <Class>::<method>. By default _GLOBAL::main is used 120# 121# SUBDIR 122# Subdirectory in the current binary directory that is used to store build artifacts. 123# Full path to directory with artifacts: ${CMAKE_CURRENT_BINARY_DIR}/<subdir>/<target> 124# 125# OUTPUT_FILE_VARIABLE, ERROR_FILE_VARIABLE 126# The variable named will be set with the paths to files with contents of the stdout and 127# stderr of the program respectively 128# 129# SKIP_BUILD 130# Do not run assembly 131# 132# AOT_MODE 133# Run test in AOT mode 134# 135# AOT_STATS 136# Creates additional independent target `${TARGET}-stats`. 137# `stats`-target dumps AOT compiler statistics in `${COMPILER_STATS_DIR}/${TARGET}.csv` 138# 139# DEPENDS 140# List of additional dependences (exclude assembler and interpreter) 141# 142# RUNTIME_OPTIONS 143# Runtime options 144# 145# RUNTIME_FAIL_TEST 146# runtime is expected to fail 147# 148# COMPILER_OPTIONS 149# Options for compiler, given both to panda and paoc 150# 151# ASSEMBLER_OPTIONS 152# Options for assembler 153# 154# AOT_GC_OPTION 155# Type of a gc 156# 157# ENTRY_ARGUMENTS 158# List of arguments that will be passed to program's entry point 159# 160# TIMEOUT 161# If specified, the program will be run and terminated with the signal 10 (corresponds 162# to SIGUSR1 on most platforms) after the given timeout. The format of the value 163# must match the `timeout` command. The run will be considered successful if the program 164# exits before the timeout with the successful exit code or if it is terminated 165# after the timeout with the signal 10. 166# 167# LANGUAGE_CONTEXT 168# Set the language-dependent semantics for the code. Possible values: core, java. 169# 170function(panda_add_asm_file) 171 set(prefix ARG) 172 set(noValues RUNTIME_FAIL_TEST) 173 set(singleValues FILE ENTRY TARGET SUBDIR OUTPUT_FILE_VARIABLE ERROR_FILE_VARIABLE SKIP_BUILD AOT_MODE AOT_STATS AOT_COMPILER TIMEOUT LANGUAGE_CONTEXT AOT_GC_OPTION) 174 set(multiValues DEPENDS RUNTIME_OPTIONS COMPILER_OPTIONS ENTRY_ARGUMENTS PRLIMIT_OPTIONS ADDITIONAL_STDLIBS LLVMAOT_OPTIONS ASSEMBLER_OPTIONS) 175 cmake_parse_arguments(${prefix} 176 "${noValues}" 177 "${singleValues}" 178 "${multiValues}" 179 ${ARGN}) 180 181 if (NOT DEFINED ARG_FILE) 182 message(FATAL_ERROR "Mandatory FILE argument is not defined.") 183 endif() 184 185 if (NOT DEFINED ARG_TARGET) 186 message(FATAL_ERROR "Mandatory TARGET argument is not defined.") 187 endif() 188 189 if (TARGET ARG_TARGET) 190 message(FATAL_ERROR "Target ${ARG_TARGET} is already defined.") 191 endif() 192 193 set(build_dir "${CMAKE_CURRENT_BINARY_DIR}/${ARG_SUBDIR}/${ARG_TARGET}") 194 set(build_log "${build_dir}/build.log") 195 set(build_out "${build_dir}/build.out") 196 set(build_err "${build_dir}/build.err") 197 set(binary_file "${build_dir}/test.abc") 198 set(launch_file "${build_dir}/launch.sh") 199 set(output_file "${build_dir}/run.out") 200 set(error_file "${build_dir}/run.err") 201 202 file(MAKE_DIRECTORY "${build_dir}") 203 204 if (DEFINED ARG_OUTPUT_FILE_VARIABLE) 205 set(${ARG_OUTPUT_FILE_VARIABLE} "${output_file}" PARENT_SCOPE) 206 endif() 207 208 if (DEFINED ARG_ERROR_FILE_VARIABLE) 209 set(${ARG_ERROR_FILE_VARIABLE} "${error_file}" PARENT_SCOPE) 210 endif() 211 212 if (ARG_SKIP_BUILD) 213 set(binary_file "${ARG_FILE}") 214 else() 215 set(assembler ark_asm) 216 add_custom_command(OUTPUT "${binary_file}" 217 COMMENT "Building ${ARG_TARGET}" 218 COMMAND ${PANDA_RUN_PREFIX} $<TARGET_FILE:${assembler}> ${ARG_ASSEMBLER_OPTIONS} --verbose --log-file "${build_log}" "${ARG_FILE}" "${binary_file}" 1>"${build_out}" 2>"${build_err}" 219 DEPENDS ${assembler} "${ARG_FILE}") 220 endif() 221 222 if (DEFINED ARG_TIMEOUT AND NOT "${ARG_TIMEOUT}" STREQUAL "") 223 set(timeout_signal 10) 224 set(timeout_prefix "timeout --preserve-status --signal=${timeout_signal} --kill-after=10s ${ARG_TIMEOUT}") 225 set(timeout_suffix "|| [ `expr \$? % 128` -eq ${timeout_signal} ]") 226 else() 227 set(timeout_prefix "") 228 set(timeout_suffix "") 229 endif() 230 231 if (NOT DEFINED ARG_ENTRY) 232 set(ARG_ENTRY "_GLOBAL::main") 233 endif() 234 235 set(panda_stdlib arkstdlib) 236 set(panda_cli ark) 237 set(context "${ARG_LANGUAGE_CONTEXT}") 238 set(stdlibs) 239 240 if ("${context}" STREQUAL "core") 241 list(APPEND stdlibs "${${panda_stdlib}_BINARY_DIR}/${panda_stdlib}.abc") 242 endif() 243 if (DEFINED ARG_ADDITIONAL_STDLIBS) 244 list(APPEND stdlibs ${ARG_ADDITIONAL_STDLIBS}) 245 endif() 246 string(REPLACE ";" ":" boot_stdlibs "${stdlibs}") 247 248 if (ARG_AOT_MODE) 249 set(aot_compiler ark_aot) 250 set(backend_compiler "aot") 251 if (DEFINED ARG_AOT_COMPILER) 252 set(backend_compiler ${ARG_AOT_COMPILER}) 253 endif() 254 set(launch_aot_file "${build_dir}/launch_aot.sh") 255 set(aot_file "${build_dir}/test.an") 256 set(aot_file_stats "${build_dir}/test_stats.an") 257 set(launcher_aot 258 "${PANDA_RUN_PREFIX}" 259 "$<TARGET_FILE:${aot_compiler}>" 260 "--paoc-mode=${backend_compiler}" 261 "--boot-panda-files \"${boot_stdlibs}\"" 262 "--load-runtimes=${context}" 263 "--paoc-panda-files \"${binary_file}\"" 264 "--paoc-output \"${aot_file}\"" 265 "--compiler-ignore-failures=false" 266 "${ARG_COMPILER_OPTIONS}" 267 "${ARG_LLVMAOT_OPTIONS}" 268 "${ARG_AOT_GC_OPTION}" 269 "1>>\"${build_out}\"" 270 "2>>\"${build_err}\"" 271 ) 272 string(REPLACE ";" " " launcher_aot "${launcher_aot}") 273 file(GENERATE OUTPUT ${launch_aot_file} CONTENT "${launcher_aot}") 274 275 # TODO(msherstennikov): enable for arm64 276 # Problem in arm64 is that ark_aotdump cannot open aot elf file with error: "undefined symbol: aot" 277 if (NOT PANDA_MINIMAL_VIXL AND PANDA_TARGET_AMD64) 278 set(aotdump_command 279 "${PANDA_RUN_PREFIX}" 280 "$<TARGET_FILE:ark_aotdump>" 281 "--output-file=/dev/null" 282 "--show-code" "disasm" 283 "${aot_file}") 284 endif() 285 286 set(aot_compile_depends "${aot_compiler}" "${binary_file}") 287 if (NOT PANDA_MINIMAL_VIXL) 288 list(APPEND aot_compile_depends "ark_aotdump") 289 endif() 290 if (DEFINED ARG_DEPENDS) 291 list(APPEND aot_compile_depends ${ARG_DEPENDS}) 292 endif() 293 294 add_custom_target(${ARG_TARGET}-compile-aot 295 COMMENT "Running aot compiler for ${ARG_TARGET}" 296 COMMAND . ${launch_aot_file} || (cat ${build_err} && echo "Command: " && cat ${launch_aot_file} && false) 297 COMMAND ${aotdump_command} 298 DEPENDS ${aot_compile_depends}) 299 add_custom_command(OUTPUT "${aot_file}" 300 DEPENDS ${ARG_TARGET}-compile-aot) 301 list(APPEND ARG_RUNTIME_OPTIONS "--aot-file=${aot_file}") 302 303 if (ARG_AOT_STATS) 304 set(launch_aot_stats_file "${build_dir}/launch_aot_stats.sh") 305 set(launcher_aot_stats 306 "${PANDA_RUN_PREFIX}" 307 "$<TARGET_FILE:${aot_compiler}>" 308 "--boot-panda-files \"${boot_stdlibs}\"" 309 "--load-runtimes=${context}" 310 "--paoc-panda-files \"${binary_file}\"" 311 "--paoc-output \"${aot_file_stats}\"" 312 "--compiler-ignore-failures=false" 313 "--compiler-dump-stats-csv=\"${COMPILER_STATS_DIR}/${ARG_TARGET}.csv\"" 314 "${ARG_COMPILER_OPTIONS}" 315 "${ARG_AOT_GC_OPTION}" 316 "1>>\"${build_out}\"" 317 "2>>\"${build_err}\"" 318 ) 319 string(REPLACE ";" " " launcher_aot_stats "${launcher_aot_stats}") 320 file(GENERATE OUTPUT ${launch_aot_stats_file} CONTENT "${launcher_aot_stats}") 321 add_custom_target(${ARG_TARGET}-stats 322 COMMENT "Gathering AOT-statistics for ${ARG_TARGET}" 323 COMMAND . ${launch_aot_stats_file} 324 DEPENDS ${aot_compiler} "${binary_file}" compiler_stats_dir) 325 add_dependencies(${ARG_TARGET}-stats ${ARG_TARGET}-compile-aot) 326 endif() 327 endif() 328 329 # NB! The lines below imply that we cannot handle ";" properly 330 # in both Panda's own options and the runned program's options. 331 # TODO: Fix this once this becomes an issue. 332 string(REPLACE ";" " " runtime_options "${ARG_COMPILER_OPTIONS} ${ARG_RUNTIME_OPTIONS}") 333 string(REPLACE ";" " " entry_arguments "${ARG_ENTRY_ARGUMENTS}") 334 335 set(prlimit_cmd "") 336 if (DEFINED ARG_PRLIMIT_OPTIONS) 337 set(prlimit_cmd "prlimit ${ARG_PRLIMIT_OPTIONS}") 338 string(REPLACE ";" " " prlimit_cmd "${prlimit_cmd}") 339 endif() 340 341 if (${runtime_options} MATCHES ".*events-output=csv.*") 342 set(runtime_options "${runtime_options} --events-file=${build_dir}/events.csv") 343 endif() 344 345 set(launcher 346 "${timeout_prefix}" 347 "${prlimit_cmd}" 348 "${PANDA_RUN_PREFIX}" 349 $<TARGET_FILE:${panda_cli}> 350 "--boot-panda-files \"${boot_stdlibs}\"" 351 "--load-runtimes=${context}" 352 "--compiler-ignore-failures=false" 353 "${runtime_options}" 354 "\"${binary_file}\"" 355 "\"${ARG_ENTRY}\"" 356 "${entry_arguments}" 357 "1>\"${output_file}\"" 358 "2>\"${error_file}\"" 359 "${timeout_suffix}" 360 ) 361 string(REPLACE ";" " " launcher "${launcher}") 362 if (ARG_RUNTIME_FAIL_TEST) 363 string(APPEND launcher " || (exit 0)") 364 endif() 365 file(GENERATE OUTPUT ${launch_file} CONTENT "${launcher}") 366 367 add_custom_target(${ARG_TARGET} 368 COMMENT "Running ${ARG_TARGET}" 369 COMMAND . ${launch_file} || (cat ${error_file} && false) 370 DEPENDS ${panda_cli} "${binary_file}" ${aot_file}) 371 372 if (ARG_AOT_MODE) 373 add_dependencies(${ARG_TARGET} ${ARG_TARGET}-compile-aot) 374 endif() 375 376 if (DEFINED ARG_DEPENDS) 377 add_dependencies(${ARG_TARGET} ${ARG_DEPENDS}) 378 endif() 379 if ("${context}" STREQUAL "core") 380 add_dependencies(${ARG_TARGET} ${panda_stdlib}) 381 endif() 382 383endfunction() 384 385# Add a single buildable and verifiable Panda Assembly file to the build tree. 386# 387# Usage: 388# 389# verifier_add_asm_file( 390# FILE <source> 391# TARGET <target> 392# [RUNTIME_OPTIONS <runtime options>] 393# [VERIFIER_OPTIONS <verifier options>] 394# [SUBDIR <subdir>] 395# [OUTPUT_FILE_VARIABLE <variable>] 396# [ERROR_FILE_VARIABLE <variable>] 397# [DEPENDS <dependency>...] 398# [TIMEOUT <timeout>] 399# [LANGUAGE_CONTEXT <language>] 400# ) 401# 402# Adds a target <target> which assembles the assembly file <source> 403# with Panda assembler and verifies it with verifier. 404# 405# Options: 406# 407# SUBDIR 408# Subdirectory in the current binary directory that is used to store build artifacts. 409# Full path to directory with artifacts: ${CMAKE_CURRENT_BINARY_DIR}/<subdir>/<target> 410# 411# OUTPUT_FILE_VARIABLE, ERROR_FILE_VARIABLE 412# The variable named will be set with the paths to files with contents of the stdout and 413# stderr of the program respectively 414# 415# DEPENDS 416# List of additional dependences (exclude assembler and interpreter) 417# 418# RUNTIME_OPTIONS 419# Runtime initialization options 420# 421# VERIFIER_OPTIONS 422# Verifier CLI options 423# 424# VERIFIER_FAIL_TEST 425# If true, verifier is allowed to exit with non-0 exit code 426# 427# TIMEOUT 428# If specified, the program will be run and terminated with the signal 10 (corresponds 429# to SIGUSR1 on most platforms) after the given timeout. The format of the value 430# must match the `timeout` command. The run will be considered successful if the program 431# exits before the timeout with the successful exit code or if it is terminated 432# after the timeout with the signal 10. 433# 434# LANGUAGE_CONTEXT 435# Set the language-dependent semantics for the code. Possible values: core, java. 436# 437function(verifier_add_asm_file) 438 set(prefix ARG) 439 set(noValues VERIFIER_FAIL_TEST) 440 set(singleValues 441 FILE 442 TARGET 443 SUBDIR 444 OUTPUT_FILE_VARIABLE 445 ERROR_FILE_VARIABLE 446 TIMEOUT 447 LANGUAGE_CONTEXT) 448 set(multiValues DEPENDS RUNTIME_OPTIONS VERIFIER_OPTIONS STDLIBS) 449 cmake_parse_arguments(${prefix} 450 "${noValues}" 451 "${singleValues}" 452 "${multiValues}" 453 ${ARGN}) 454 455 if (NOT DEFINED ARG_FILE) 456 message(FATAL_ERROR "Mandatory FILE argument is not defined.") 457 endif() 458 459 if (NOT DEFINED ARG_TARGET) 460 message(FATAL_ERROR "Mandatory TARGET argument is not defined.") 461 endif() 462 463 if (TARGET ARG_TARGET) 464 message(FATAL_ERROR "Target ${ARG_TARGET} is already defined.") 465 endif() 466 467 set(build_dir "${CMAKE_CURRENT_BINARY_DIR}/${ARG_SUBDIR}/${ARG_TARGET}") 468 set(build_out "${build_dir}/build.out") 469 set(build_err "${build_dir}/build.err") 470 set(binary_file "${build_dir}/test.abc") 471 set(launch_file "${build_dir}/verifier_launch.sh") 472 set(output_file "${build_dir}/verify.out") 473 set(error_file "${build_dir}/verify.err") 474 475 file(MAKE_DIRECTORY "${build_dir}") 476 477 if (DEFINED ARG_OUTPUT_FILE_VARIABLE) 478 set(${ARG_OUTPUT_FILE_VARIABLE} "${output_file}" PARENT_SCOPE) 479 endif() 480 481 if (DEFINED ARG_ERROR_FILE_VARIABLE) 482 set(${ARG_ERROR_FILE_VARIABLE} "${error_file}" PARENT_SCOPE) 483 endif() 484 485 set(assembler ark_asm) 486 add_custom_command(OUTPUT "${binary_file}" 487 COMMENT "Building ${ARG_TARGET}" 488 COMMAND ${PANDA_RUN_PREFIX} $<TARGET_FILE:${assembler}> "${ARG_FILE}" "${binary_file}" 1>"${build_out}" 2>"${build_err}" 489 DEPENDS ${assembler} "${ARG_FILE}") 490 491 if (DEFINED ARG_TIMEOUT AND NOT "${ARG_TIMEOUT}" STREQUAL "") 492 set(timeout_signal 10) 493 set(timeout_prefix "timeout --preserve-status --signal=${timeout_signal} --kill-after=10s ${ARG_TIMEOUT}") 494 set(timeout_suffix "|| [ `expr \$? % 128` -eq ${timeout_signal} ]") 495 else() 496 set(timeout_prefix "") 497 set(timeout_suffix "") 498 endif() 499 500 set(panda_stdlib arkstdlib) 501 set(verifier_cli verifier) 502 503 set(spaces "core") 504 if (NOT "core" STREQUAL "${ARG_LANGUAGE_CONTEXT}") 505 set(spaces "${ARG_LANGUAGE_CONTEXT}") 506 else() 507 set(stdlibs "${${panda_stdlib}_BINARY_DIR}/${panda_stdlib}.abc") 508 endif() 509 510 list(APPEND stdlibs ${ARG_STDLIBS}) 511 512 string(REPLACE ";" ":" boot_stdlibs "${stdlibs}") 513 string(REPLACE ";" ":" boot_spaces "${spaces}") 514 515 set(launcher_verifier 516 "${PANDA_RUN_PREFIX}" 517 $<TARGET_FILE:${verifier_cli}> 518 "${ARG_VERIFIER_OPTIONS}" 519 "${ARG_RUNTIME_OPTIONS}" 520 "--boot-panda-files=\"${boot_stdlibs}\"" 521 "--load-runtimes=${boot_spaces}" 522 "\"${binary_file}\"" 523 "1>\"${output_file}\"" 524 "2>\"${error_file}\"" 525 ) 526 string(REPLACE ";" " " launcher_verifier "${launcher_verifier}") 527 if (ARG_VERIFIER_FAIL_TEST) 528 string(APPEND launcher_verifier " || (exit 0)") 529 endif() 530 531 file(GENERATE OUTPUT ${launch_file} CONTENT "${launcher_verifier}") 532 533 add_custom_target(${ARG_TARGET} 534 COMMENT "Verifying ${ARG_TARGET}" 535 COMMAND . ${launch_file} 536 DEPENDS ${verifier_cli} "${binary_file}") 537 538 if (DEFINED ARG_DEPENDS) 539 add_dependencies(${ARG_TARGET} ${ARG_DEPENDS}) 540 endif() 541 542endfunction() 543