mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-07 08:36:02 -10:00
Zip members stored with method 12 are decoded with rbzip2, whole, from the mapping, from what the stream lends or else read, as Zstandard members are. A .bz2 file stands for the file named as it is without the extension: a stream does not say how long it decodes to, so the member is decoded when it is listed, its size and CRC then known, and kept for the extraction that follows. "#" after ".bz2" names its member. HAVE_RBZIP2, on unless configured off, builds both and the decoder; RVZ's bzip2 groups take the decoder alone where it is off. file_archive_whole() is the whole-file read the .zst and .bz2 backends share. archive_bz2_test lists, reads and extracts bzip2 zip members and a .bz2 file, and refuses members whose CRC is wrong.
1656 lines
66 KiB
YAML
1656 lines
66 KiB
YAML
name: CI Linux libretro-common samples
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- master
|
|
pull_request:
|
|
branches:
|
|
- master
|
|
workflow_dispatch:
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
env:
|
|
ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
|
|
|
|
jobs:
|
|
samples:
|
|
name: Build and run libretro-common/samples
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
APT_PKGS: build-essential zlib1g-dev clang ffmpeg ccache
|
|
timeout-minutes: 15
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Apt cache key
|
|
id: apt
|
|
run: sh .github/workflows/scripts/apt-cache-key.sh "$APT_PKGS"
|
|
|
|
- name: Restore apt cache
|
|
uses: actions/cache/restore@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
restore-keys: ${{ steps.apt.outputs.prefix }}
|
|
|
|
- name: Install dependencies
|
|
timeout-minutes: 20
|
|
run: sh .github/workflows/scripts/apt-install.sh $APT_PKGS
|
|
|
|
- name: Save apt cache
|
|
if: env.APT_CACHE_SAVE == '1'
|
|
uses: actions/cache/save@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
|
|
- name: Link with lld
|
|
run: sh .github/workflows/scripts/use-lld.sh
|
|
|
|
- name: Restore ccache
|
|
uses: actions/cache/restore@v4
|
|
with:
|
|
path: ~/.ccache
|
|
key: ccache-${{ github.workflow }}-${{ github.job }}-${{ github.sha }}
|
|
restore-keys: |
|
|
ccache-${{ github.workflow }}-${{ github.job }}-
|
|
|
|
- name: Enable ccache
|
|
run: |
|
|
set -eu
|
|
export CCACHE_DIR="$HOME/.ccache"
|
|
echo "CCACHE_DIR=$CCACHE_DIR" >> "$GITHUB_ENV"
|
|
echo "/usr/lib/ccache" >> "$GITHUB_PATH"
|
|
ccache -M 250M
|
|
ccache -z
|
|
|
|
- name: Build and run samples
|
|
shell: bash
|
|
working-directory: libretro-common/samples
|
|
env:
|
|
# Build samples under AddressSanitizer + UndefinedBehaviorSanitizer.
|
|
# Most overflow tests under libretro-common/samples (rpng_chunk_
|
|
# overflow_test, rbmp_test, vfs_read_overflow_test, cdrom_cuesheet
|
|
# _overflow_test, cdfs_dir_record_test, chd_meta_overflow_test, ...)
|
|
# use ASan as the regression discriminator for the bound-check
|
|
# they verify, so make ASan the default for this workflow. Each
|
|
# test's Makefile honours SANITIZER= via the conventional opt-in
|
|
# block (matching samples/tasks/http/Makefile). Test sources
|
|
# that don't have the opt-in block treat the variable as
|
|
# ignored.
|
|
MAKE_ARGS: "SANITIZER=address,undefined"
|
|
run: |
|
|
set -u
|
|
set -o pipefail
|
|
|
|
# Samples whose binary, when invoked with no arguments, runs a
|
|
# self-contained test and exits 0 on success / non-zero on
|
|
# failure. These are built AND executed.
|
|
declare -a RUN_TARGETS=(
|
|
rslc_test
|
|
float_to_s32_test
|
|
compat_fnmatch_test
|
|
snprintf
|
|
unbase64_test
|
|
crc32_test
|
|
rzstd_raw_block_test
|
|
rbzip2_test
|
|
rinflate_window_test
|
|
rdeflate_hash_test
|
|
emscripten_mmap_test
|
|
archive_zip_test
|
|
archive_zstd_test
|
|
archive_zst_test
|
|
archive_bz2_test
|
|
archive_rar_test
|
|
archive_stream_test
|
|
config_file_test
|
|
path_resolve_realpath_test
|
|
fill_pathname_test
|
|
path_mkdir_test
|
|
rpng
|
|
rzip_chunk_size_test
|
|
rzip_matches_buf_test
|
|
rzip_codec_test
|
|
rzip_parallel_test
|
|
data_transfer_source_test
|
|
data_transfer_prefix_test
|
|
data_transfer_window_test
|
|
audio_transfer_window_test
|
|
# rvorbis' codebook size bound, and a real stream past it.
|
|
rvorbis_codebook_test
|
|
rjpeg_test
|
|
rh264_lossless_test
|
|
rh264_lossless_test_nothreads
|
|
rh264_lossless_test_volatile
|
|
# The hardware path's sequencing against a checking backend.
|
|
rh264_hw_test
|
|
# rmp3's frame-at-a-time API against its pull API.
|
|
rmp3_frame_test
|
|
# raac_last_size over units followed by more.
|
|
raac_size_test
|
|
rh265_lossless_test
|
|
rh265_lossless_test_nothreads
|
|
rh265_lossless_test_volatile
|
|
rh265_malformed_test
|
|
rh265_weight_test
|
|
r7z_lzma_props_test
|
|
r7z_archive_detach_test
|
|
r7z_bcj2_test
|
|
rzip_archive_test
|
|
# The RAR reader on real archives (LZ and PPMd blocks,
|
|
# filters, Unicode names) and on mangled copies of them.
|
|
rrar_archive_test
|
|
vcdiff_test
|
|
net_ifinfo
|
|
vfs_read_overflow_test
|
|
vfs_mapped_ptr_test
|
|
vfs_prefetch_test
|
|
vfs_readahead_test
|
|
vfs_view_test
|
|
vfs_prefetch_retarget_test
|
|
vfs_prefetch_view_test
|
|
vfs_large_file_test
|
|
vfs_seek_contract_test
|
|
vfs_bulk_read_test
|
|
vfs_v5_metadata_test
|
|
vfs_hybrid_test
|
|
filestream_rbuf_fault_test
|
|
vfs_rename_replace_test
|
|
cdrom_cuesheet_overflow_test
|
|
rdvd_h262_test
|
|
rdvd_lpcm_test
|
|
rdvd_ac3_test
|
|
rdvd_packet_test
|
|
rdvd_highlight_test
|
|
rdvd_spu_test
|
|
rdvd_ifo_test
|
|
rdvd_source_test
|
|
rdvd_vfs_test
|
|
rdvd_vfs_real_test
|
|
cdfs_dir_record_test
|
|
cdfs_pure_api_test
|
|
cdfs_io_error_test
|
|
chd_meta_overflow_test
|
|
chd_stream_threads_test
|
|
chd_stream_ahead_test
|
|
rvz_test
|
|
filestream_printf_test
|
|
strlcpy_append_test
|
|
http_parse_test
|
|
rjson_test
|
|
rm3u_test
|
|
rmp4_fragment_test
|
|
rmp4_language_test
|
|
rav1_test
|
|
rav1_picture_test
|
|
rav1_reference_test
|
|
rav1_motion_test
|
|
rav1_grain_test
|
|
rav1_reference_state_test
|
|
rav1_hw_session_test
|
|
rav1_hw_stream_test
|
|
rav1_mp4_stream_test
|
|
rav1_webm_stream_test
|
|
rav1_hw_backend_test
|
|
rav1_frame_test
|
|
rav1_tile_info_test
|
|
rav1_tiles_test
|
|
rav1_sequence_test
|
|
rav1_container_test
|
|
subtitle_srt_test
|
|
subtitle_srt_fault_test
|
|
rtga_test
|
|
rbmp_test
|
|
rbmp_encode_test
|
|
# Every 16-bit input through each pixconv converter, with
|
|
# SSE2, with MMX only, and with no SIMD at all.
|
|
pixconv_test
|
|
pixconv_test_nosimd
|
|
pixconv_test_scalar
|
|
rpng_chunk_overflow_test
|
|
rpng_roundtrip_test
|
|
rpng_decode_corpus_test
|
|
rpng_apng_bounds_test
|
|
# Benchmark, not a pass/fail test, but it runs its default
|
|
# 4K comparison and exits 0, so under this job's sanitizer
|
|
# build it doubles as end-to-end ASan/UBSan smoke coverage
|
|
# of the old and new screenshot encode/write paths.
|
|
# Timings in its output are informational only.
|
|
screenshot_io_bench
|
|
word_wrap_overflow_test
|
|
task_queue_title_error_test
|
|
task_queue_snapshot_test
|
|
task_queue_init_failure_test
|
|
# Stops the worker with a bound when a detachable
|
|
# handler is stuck; waits ~1.5 s by design.
|
|
task_queue_detach_test
|
|
task_queue_main_thread_test
|
|
task_queue_budget_test
|
|
# A switch to the threaded queue takes over the tasks
|
|
# already queued and counts them.
|
|
task_queue_switch_test
|
|
# A parked handler runs again on a wake, a cancel or its
|
|
# horizon, and not before.
|
|
task_queue_park_test
|
|
# Parallel tasks on the extra workers, beside the one
|
|
# worker plain tasks keep to.
|
|
task_queue_parallel_test
|
|
# The worker below the main thread as Horizon schedules
|
|
# them; the lanes need SCHED_FIFO and skip without root.
|
|
task_queue_fifo_prio_test
|
|
image_blit_bands_test
|
|
image_loader_test
|
|
tpool_wait_test
|
|
# Four CPU-burning jobs on a pool of four must overlap;
|
|
# on a one-core runner it notes that and still passes.
|
|
tpool_parallel_test
|
|
# The pool's lock-free handoff with tpool.c's test yields
|
|
# on: no job posted is left unrun or run twice.
|
|
tpool_handoff_test
|
|
data_transfer_pool_scrub_test
|
|
retro_atomic_test
|
|
retro_atomic_extern_c_linkage_test
|
|
retro_atomic_extern_c_linkage_test_cxx
|
|
# The C++11 backend's value macros given enumerators, and
|
|
# parsed against a pre-P0558R1 <atomic> that deduces from
|
|
# the value too, as libstdc++ 8 does.
|
|
retro_atomic_cxx11_old_stl_test
|
|
# The PS2 EE backend, built off-target against a stub
|
|
# <kernel.h>: the PS2 CI lane compiles it, this runs it.
|
|
retro_atomic_ps2_backend_test
|
|
retro_spsc_test
|
|
mpsc_stack_test
|
|
# A frame handoff where neither side waits: no read sees a
|
|
# buffer being filled, the reader ends on the last frame.
|
|
retro_triple_buffer_test
|
|
retro_waitable_spsc_test
|
|
retro_eventcount_test
|
|
# retro_sleep_until_us must never return early against the
|
|
# microsecond clock; the late bound is loose for a loaded
|
|
# runner.
|
|
retro_sleep_until_test
|
|
# localtime() from four threads, before rtime_init() as
|
|
# well as after: each must get its own fields back.
|
|
rtime_localtime_test
|
|
# Runs its natural tier only here; procbarrier-tiers.yml
|
|
# forces every tier on Linux, Windows and macOS runners.
|
|
retro_procbarrier_test
|
|
retro_asym_eventcount_test
|
|
sthread_ex_test
|
|
fast_cores_test
|
|
memshm_test
|
|
fastjmp_test
|
|
rdds_block_test
|
|
faulthandler_test
|
|
memshm_area_test
|
|
msg_fifo_queue_test
|
|
audio_mixer_loop_test
|
|
# Voice volume and gain set and read from two threads while
|
|
# a third mixes: every read and every block one of the
|
|
# values set.
|
|
audio_mixer_voice_threads_test
|
|
audio_mixer_rate_guard_test
|
|
audio_mixer_pause_test
|
|
# The AC-3 / E-AC-3 frame layer against ffmpeg's streams:
|
|
# ffmpeg is in the package list above for it.
|
|
rac3_frame_test
|
|
rac3_tables_test
|
|
rac3_decode_test
|
|
rac3_encode_test
|
|
# IEC 61937 bursts against ffmpeg's spdif muxer and demuxer.
|
|
iec61937_test
|
|
# The AC-3 arm of audio_transfer against ffmpeg's decode.
|
|
audio_transfer_ac3_test
|
|
# The FLAC arm, sample-exact in every input mode, a grown
|
|
# demuxed packet set and a raised WebM wall both resuming.
|
|
audio_transfer_flac_test
|
|
# The HDR video blits: PQ and HLG to linear scRGB against
|
|
# double-precision references, and HLG tone-mapped as HDR.
|
|
image_hdr_blit_test
|
|
# PSP movies demultiplexed from program streams built for it,
|
|
# fed whole, a byte at a time and in odd chunks.
|
|
rpsmf_test
|
|
# ATRAC3 against a reference decoder's output for crafted
|
|
# stereo and joint stereo streams, then bad frames and
|
|
# garbage.
|
|
ratrac3_test
|
|
# ATRAC3plus against a reference decoder's output for a
|
|
# stereo stream (tones, gain points, swap and negate masks)
|
|
# and a mono one, then bad frames and garbage.
|
|
ratrac3p_test
|
|
# Animation playback against a scripted stream: order,
|
|
# pacing, loops, catch-up, the worker's two jobs.
|
|
video_player_test
|
|
# VP9 reference-slot sizing, the hardware session's
|
|
# metadata, retries and frame leases, and WebM dispatch
|
|
# into it.
|
|
rmp4_vp9_config_test
|
|
rvp9_config_test
|
|
rvp9_reference_test
|
|
rvp9_hw_session_test
|
|
rwebm_language_test
|
|
rwebm_hw_stream_test
|
|
# The NGX and NVAPI loaders against a stand-in NGX core
|
|
# library on LD_LIBRARY_PATH; the Windows discovery runs
|
|
# in the mingw job below.
|
|
nvidia_loader_test
|
|
# The FidelityFX API and XeSS loaders against stand-in
|
|
# libraries planted in a directory of the test's, as the
|
|
# user's runtime directory is; the Windows file names run
|
|
# in the mingw job below.
|
|
ffx_loader_test
|
|
xess_loader_test
|
|
)
|
|
|
|
# Targets that are built but deliberately not executed, each
|
|
# for a reason that is not "we forgot". Anything built and
|
|
# listed in neither this nor RUN_TARGETS is an error below,
|
|
# because a silent "not in run allowlist" skip is
|
|
# indistinguishable from an omission - which is how seven
|
|
# self-contained regression tests came to be compiled every
|
|
# run and never executed.
|
|
declare -a NORUN_TARGETS=(
|
|
# CLI tools that print usage and exit non-zero without
|
|
# arguments; they have no self-test mode.
|
|
rzip
|
|
# Performs real HTTP requests against an external host, so
|
|
# running it would make this job depend on a third party
|
|
# being up.
|
|
http_test
|
|
)
|
|
|
|
# Per-binary run command (overrides ./<binary> if present).
|
|
# config_file_test's leak exemption is gone: the harness now
|
|
# frees everything it allocates and the parser's own include
|
|
# leak is fixed, so LeakSanitizer runs at full strictness and
|
|
# any surviving allocation is a real config_file regression.
|
|
declare -A RUN_ENV=(
|
|
# fastjmp_jmp is a longjmp that is not libc's, so ASan's
|
|
# fake-stack check cannot follow it; the same holds for
|
|
# libco. The jump is what the test is for.
|
|
[fastjmp_test]="ASAN_OPTIONS=detect_leaks=1:detect_stack_use_after_return=0"
|
|
# The filter must be the one that sees SIGSEGV: ASan installs
|
|
# its own, and with it the faults never reach the code under
|
|
# test. handle_segv=0 is how a fastmem core runs.
|
|
[faulthandler_test]="ASAN_OPTIONS=detect_leaks=1:handle_segv=0 UBSAN_OPTIONS=handle_segv=0"
|
|
[nvidia_loader_test]="LD_LIBRARY_PATH=."
|
|
)
|
|
|
|
# Samples that are build-only (either they require command-line
|
|
# arguments, open network sockets, need extra fixtures, or are
|
|
# interactive demos). They are compiled to catch build-time
|
|
# regressions but not executed.
|
|
declare -a BUILD_ONLY_DIRS=(
|
|
formats/xml
|
|
# A benchmark, not a test: its numbers only mean something
|
|
# on two dedicated physical cores, which a runner is not.
|
|
queues/retro_spsc_bench
|
|
# The same, and for the same reason: it reports park and
|
|
# commit counts under contention, which a shared runner
|
|
# cannot produce meaningfully. It also builds a second
|
|
# binary, the scond variant, and being build-only covers
|
|
# both without naming either.
|
|
rthreads/retro_eventcount_bench
|
|
# What a short job costs to hand to the pool: a figure for
|
|
# a machine with cores to spare.
|
|
rthreads/tpool_bench
|
|
)
|
|
|
|
# Samples that are currently broken at build time on a stock
|
|
# Ubuntu host and are therefore neither built nor run.
|
|
declare -a SKIP_DIRS=(
|
|
# A 3DS homebrew, built with devkitARM and run on the
|
|
# console or an emulator.
|
|
queues/ctr_task_queue_prio_test
|
|
)
|
|
|
|
is_in() {
|
|
local needle=$1; shift
|
|
local h
|
|
for h in "$@"; do [[ "$h" == "$needle" ]] && return 0; done
|
|
return 1
|
|
}
|
|
|
|
fails=0
|
|
builds=0
|
|
runs=0
|
|
|
|
# Collect all Makefile directories (one or two levels deep).
|
|
mapfile -t MKDIRS < <(find . -name Makefile -printf '%h\n' | sort)
|
|
|
|
printf '\n==> %d sample directories found\n' "${#MKDIRS[@]}"
|
|
for d in "${MKDIRS[@]}"; do printf ' %s\n' "${d#./}"; done
|
|
printf '\n'
|
|
|
|
for d in "${MKDIRS[@]}"; do
|
|
rel=${d#./}
|
|
printf '========================================\n'
|
|
printf '[%s] %s\n' "$(is_in "$rel" "${SKIP_DIRS[@]}" && echo skip || echo build)" "$rel"
|
|
printf '========================================\n'
|
|
|
|
if is_in "$rel" "${SKIP_DIRS[@]}"; then
|
|
printf '[skip] %s is on the skip list\n\n' "$rel"
|
|
continue
|
|
fi
|
|
|
|
# Build
|
|
if ! ( cd "$d" && make clean all $MAKE_ARGS ); then
|
|
printf '\n::error title=Build failed::%s failed to build\n' "$rel"
|
|
fails=$((fails+1))
|
|
continue
|
|
fi
|
|
builds=$((builds+1))
|
|
|
|
# Skip run for build-only dirs
|
|
if is_in "$rel" "${BUILD_ONLY_DIRS[@]}"; then
|
|
printf '[skip-run] %s (build-only list)\n\n' "$rel"
|
|
continue
|
|
fi
|
|
|
|
# Extract targets from Makefile. Handles:
|
|
# TARGET := foo
|
|
# TARGETS = a b c
|
|
# TARGET_TEST := foo_test (second target in same Makefile)
|
|
# TARGET_TEST2 := foo_test2 (third target, fourth, ...)
|
|
# EXE is empty on Linux.
|
|
mapfile -t targets < <(
|
|
grep -hE '^(TARGET|TARGETS|TARGET_TEST[0-9]*)[[:space:]]*[:?]?=' "$d/Makefile" \
|
|
| sed -E 's/^[^=]*=[[:space:]]*//' \
|
|
| sed 's/$(EXE)//g' \
|
|
| tr -s ' \t' '\n' \
|
|
| grep -v '^$' \
|
|
| sort -u
|
|
)
|
|
|
|
for t in "${targets[@]}"; do
|
|
if ! is_in "$t" "${RUN_TARGETS[@]}"; then
|
|
if is_in "$t" "${NORUN_TARGETS[@]}"; then
|
|
printf '[skip-run] %s/%s (deliberately not run)\n' "$rel" "$t"
|
|
continue
|
|
fi
|
|
printf '::error title=Target not classified::%s/%s is built but appears in neither RUN_TARGETS nor NORUN_TARGETS. Add it to one of them.\n' "$rel" "$t"
|
|
fails=$((fails+1))
|
|
continue
|
|
fi
|
|
|
|
bin="$d/$t"
|
|
if [[ ! -x "$bin" ]]; then
|
|
printf '::error title=Missing binary::%s was in the run allowlist but %s does not exist after build\n' "$t" "$bin"
|
|
fails=$((fails+1))
|
|
continue
|
|
fi
|
|
|
|
extra_env=${RUN_ENV[$t]:-}
|
|
|
|
printf '\n[run] %s\n' "$bin"
|
|
if ( cd "$d" && env $extra_env timeout 60 "./$t" ); then
|
|
printf '[pass] %s\n\n' "$t"
|
|
runs=$((runs+1))
|
|
else
|
|
rc=$?
|
|
printf '\n::error title=Test failed::%s exited with status %d\n' "$t" "$rc"
|
|
fails=$((fails+1))
|
|
fi
|
|
done
|
|
done
|
|
|
|
printf '========================================\n'
|
|
printf 'Summary\n'
|
|
printf '========================================\n'
|
|
printf ' Built: %d\n' "$builds"
|
|
printf ' Ran: %d\n' "$runs"
|
|
printf ' Failed: %d\n' "$fails"
|
|
|
|
if [[ $fails -gt 0 ]]; then
|
|
exit 1
|
|
fi
|
|
|
|
- name: Parse a generated DVD VOB with the native readers
|
|
shell: bash
|
|
working-directory: libretro-common/samples/formats/dvd
|
|
run: |
|
|
set -euo pipefail
|
|
fixture=$(mktemp --suffix=.vob)
|
|
trap 'rm -f "$fixture"' EXIT
|
|
ffmpeg -hide_banner -loglevel error -f lavfi -i color=size=720x576:rate=25 \
|
|
-f lavfi -i sine=sample_rate=48000 -t 0.4 \
|
|
-c:v mpeg2video -b:v 2000k -c:a ac3 -ac 2 -ar 48000 -f dvd -y "$fixture"
|
|
./rdvd_packet_test "$fixture"
|
|
./rdvd_vfs_real_test "$fixture"
|
|
reference=$(mktemp --suffix=.f32)
|
|
trap 'rm -f "$fixture" "$reference"' EXIT
|
|
for sample_format in s16 s32; do
|
|
ffmpeg -hide_banner -loglevel error -f lavfi -i color=size=720x576:rate=25 \
|
|
-f lavfi -i sine=sample_rate=48000 -t 0.4 \
|
|
-c:v mpeg2video -b:v 2000k -c:a pcm_dvd -sample_fmt "$sample_format" \
|
|
-ac 2 -ar 48000 -f dvd -y "$fixture"
|
|
ffmpeg -hide_banner -loglevel error -i "$fixture" -map 0:a:0 \
|
|
-c:a pcm_f32le -f f32le -y "$reference"
|
|
./rdvd_vfs_real_test "$fixture" "$reference"
|
|
done
|
|
|
|
- name: Reconstruct progressive H262 I pictures against a reference
|
|
shell: bash
|
|
working-directory: libretro-common/samples/formats/dvd
|
|
run: |
|
|
set -euo pipefail
|
|
fixture=$(mktemp --suffix=.m2v)
|
|
reference=$(mktemp --suffix=.yuv)
|
|
trap 'rm -f "$fixture" "$reference"' EXIT
|
|
ffmpeg -hide_banner -loglevel error -f lavfi -i testsrc2=size=160x128:rate=25 \
|
|
-frames:v 3 -c:v mpeg2video -g 1 -bf 0 -q:v 3 -f mpeg2video -y "$fixture"
|
|
ffmpeg -hide_banner -loglevel error -i "$fixture" -pix_fmt yuv420p -f rawvideo -y "$reference"
|
|
./rdvd_h262_test "$fixture" "$reference"
|
|
ffmpeg -hide_banner -loglevel error -f lavfi -i testsrc2=size=160x128:rate=25 \
|
|
-frames:v 3 -c:v mpeg1video -g 1 -bf 0 -q:v 3 -f mpeg1video -y "$fixture"
|
|
ffmpeg -hide_banner -loglevel error -i "$fixture" -pix_fmt yuv420p -f rawvideo -y "$reference"
|
|
./rdvd_h262_test "$fixture" "$reference"
|
|
|
|
- name: Compile-test retro_atomic.h from a C++11 TU
|
|
shell: bash
|
|
working-directory: libretro-common
|
|
run: |
|
|
# The C++11 backend in retro_atomic.h is a fresh code path that
|
|
# none of the C samples above exercise. Compile a tiny inline
|
|
# C++11 TU against the in-tree header to catch regressions like
|
|
# accidentally re-introducing an extern "C" wrapper around the
|
|
# std::atomic include, or breaking the __cplusplus / _MSVC_LANG
|
|
# gate. This step is build-and-run, single-threaded only -- the
|
|
# behavioural SPSC stress is already covered by the C test
|
|
# binary above on this same host, and the C++11 backend bottoms
|
|
# out through the same libstdc++ __atomic_* builtins.
|
|
#
|
|
# Two fixtures share the g++/clang++ x c++11/14/17 sweep:
|
|
#
|
|
# cxx_smoke.cpp : header included at C++ linkage,
|
|
# the "ordinary" C++ TU shape.
|
|
# cxx_smoke_extern_c.cpp : header included from inside an
|
|
# extern "C" { ... } wrap, the
|
|
# shape ui_qt.cpp uses under
|
|
# !CXX_BUILD. This is the
|
|
# regression case for the
|
|
# RETRO_BEGIN_DECLS_CXX shield in
|
|
# retro_atomic.h's C++11 backend.
|
|
# The standalone build of the
|
|
# sample at samples/atomic/
|
|
# retro_atomic_extern_c_linkage/
|
|
# covers the same case under the
|
|
# default toolchain; this matrix
|
|
# extends coverage to clang++ and
|
|
# the higher language standards.
|
|
set -u
|
|
set -o pipefail
|
|
|
|
tmpdir=$(mktemp -d)
|
|
cat > "$tmpdir/cxx_smoke.cpp" <<'EOF'
|
|
#include <cstdio>
|
|
#include <cstddef>
|
|
#include <retro_atomic.h>
|
|
|
|
#if !defined(HAVE_RETRO_ATOMIC) || !defined(RETRO_ATOMIC_LOCK_FREE)
|
|
# error "retro_atomic.h: capability flags not set on a C++11 host"
|
|
#endif
|
|
|
|
int main(void) {
|
|
retro_atomic_int_t ai; retro_atomic_int_init(&ai, 0);
|
|
retro_atomic_size_t as; retro_atomic_size_init(&as, 0);
|
|
|
|
retro_atomic_store_release_int(&ai, 42);
|
|
retro_atomic_store_release_size(&as, (std::size_t)42);
|
|
|
|
int li = retro_atomic_load_acquire_int(&ai);
|
|
int ls = (int)retro_atomic_load_acquire_size(&as);
|
|
|
|
int pi = retro_atomic_fetch_add_int(&ai, 1);
|
|
int ps = (int)retro_atomic_fetch_add_size(&as, 1);
|
|
|
|
retro_atomic_inc_int(&ai);
|
|
retro_atomic_dec_size(&as);
|
|
|
|
int qi = retro_atomic_load_acquire_int(&ai);
|
|
int qs = (int)retro_atomic_load_acquire_size(&as);
|
|
|
|
std::printf("backend: %s\n", RETRO_ATOMIC_BACKEND_NAME);
|
|
|
|
bool ok = (li == 42) && (ls == 42)
|
|
&& (pi == 42) && (ps == 42)
|
|
&& (qi == 44) && (qs == 42);
|
|
std::puts(ok ? "ALL OK" : "FAIL");
|
|
return ok ? 0 : 1;
|
|
}
|
|
EOF
|
|
|
|
# Regression fixture: include retro_atomic.h from inside an
|
|
# extern "C" wrap. Faithful mirror of ui_qt.cpp's pattern.
|
|
# Pre-fix retro_atomic.h would emit ~80 "template with C
|
|
# linkage" errors here; the RETRO_BEGIN_DECLS_CXX shield
|
|
# around <atomic> makes this compile cleanly.
|
|
cat > "$tmpdir/cxx_smoke_extern_c.cpp" <<'EOF'
|
|
#include <cstdio>
|
|
#include <cstddef>
|
|
|
|
extern "C" {
|
|
#include <retro_atomic.h>
|
|
}
|
|
|
|
int main(void) {
|
|
retro_atomic_int_t ai; retro_atomic_int_init(&ai, 0);
|
|
retro_atomic_store_release_int(&ai, 7);
|
|
int v = retro_atomic_load_acquire_int(&ai);
|
|
std::printf("backend: %s\n", RETRO_ATOMIC_BACKEND_NAME);
|
|
std::puts(v == 7 ? "ALL OK" : "FAIL");
|
|
return v == 7 ? 0 : 1;
|
|
}
|
|
EOF
|
|
|
|
for cxx in g++ clang++; do
|
|
for std in c++11 c++14 c++17; do
|
|
for fixture in cxx_smoke cxx_smoke_extern_c; do
|
|
echo "==> compile-test with $cxx -std=$std ($fixture)"
|
|
$cxx -std=$std -Wall -Wextra -pedantic -O2 \
|
|
-I include \
|
|
"$tmpdir/$fixture.cpp" \
|
|
-o "$tmpdir/$fixture" \
|
|
|| { echo "::error title=C++ compile failed::$cxx -std=$std $fixture"; exit 1; }
|
|
"$tmpdir/$fixture" \
|
|
|| { echo "::error title=C++ smoke failed::$cxx -std=$std $fixture"; exit 1; }
|
|
done
|
|
done
|
|
done
|
|
|
|
rm -rf "$tmpdir"
|
|
|
|
- name: Run retro_atomic_test under Clang + ThreadSanitizer
|
|
shell: bash
|
|
working-directory: libretro-common/samples/atomic/retro_atomic_test
|
|
run: |
|
|
# The native samples job above runs with GCC and ASan/UBSan.
|
|
# Clang is the toolchain on every Apple platform, Android NDK
|
|
# (since r18), Emscripten, and PS4-ORBIS, so a Clang lane is
|
|
# not optional coverage. ThreadSanitizer is the strict
|
|
# validator for this test in particular: it instruments every
|
|
# atomic load and store and would flag a missing acquire /
|
|
# release barrier as a race in the 1M-iteration SPSC stress
|
|
# (a class of bug that x86 TSO would otherwise hide on the
|
|
# native runner).
|
|
set -u
|
|
set -o pipefail
|
|
|
|
make clean
|
|
CC=clang make -j$(nproc) all SANITIZER=thread
|
|
|
|
TSAN_OPTIONS=halt_on_error=1 ./retro_atomic_test
|
|
|
|
- name: Run video_player_test under Clang + ThreadSanitizer, and threadless
|
|
shell: bash
|
|
working-directory: libretro-common/samples/formats/video_player
|
|
run: |
|
|
# The decode worker hands two jobs back and forth with the
|
|
# polling thread through atomics alone; the threadless shape
|
|
# decodes every frame on the polling thread.
|
|
set -u
|
|
set -o pipefail
|
|
|
|
make clean
|
|
CC=clang make -j$(nproc) all SANITIZER=thread
|
|
TSAN_OPTIONS=halt_on_error=1 ./video_player_test
|
|
make clean
|
|
make -j$(nproc) all THREADS=0 SANITIZER=address,undefined
|
|
./video_player_test
|
|
for threads in 1 0; do
|
|
make clean
|
|
make -j$(nproc) all THREADS=$threads FAULT_ALLOC=1 SANITIZER=address,undefined || exit $?
|
|
VP_TEST_RECOVERY_ONLY=1 ./video_player_test || exit $?
|
|
done
|
|
|
|
- name: Run rtime_localtime_test under Clang + ThreadSanitizer
|
|
shell: bash
|
|
working-directory: libretro-common/samples/time/rtime_localtime_test
|
|
run: |
|
|
# rtime_localtime() guards the one struct localtime() fills;
|
|
# two callers let in together race on it, which TSan reports
|
|
# even on runs where neither hands back the other's fields.
|
|
set -u
|
|
set -o pipefail
|
|
|
|
make clean
|
|
CC=clang make -j$(nproc) all SANITIZER=thread
|
|
|
|
TSAN_OPTIONS=halt_on_error=1 ./rtime_localtime_test
|
|
|
|
- name: Run retro_triple_buffer_test under Clang + ThreadSanitizer
|
|
shell: bash
|
|
working-directory: libretro-common/samples/queues/retro_triple_buffer_test
|
|
run: |
|
|
# The HUB75 driver's canvases and the ffmpeg camera's frames:
|
|
# a buffer handed over with one exchange and picked up with
|
|
# another, so a missing barrier is a race only TSan reports.
|
|
set -u
|
|
set -o pipefail
|
|
|
|
make clean
|
|
CC=clang make -j$(nproc) all SANITIZER=thread
|
|
|
|
TSAN_OPTIONS=halt_on_error=1 ./retro_triple_buffer_test
|
|
|
|
- name: Run audio_mixer_voice_threads_test under Clang + ThreadSanitizer
|
|
shell: bash
|
|
working-directory: libretro-common/samples/audio/audio_mixer_voice_threads
|
|
run: |
|
|
# A voice's volume and gain are atomics the caller sets and
|
|
# reads with no lock while the audio thread mixes the voice;
|
|
# TSan reports any field the three threads share unordered.
|
|
set -u
|
|
set -o pipefail
|
|
|
|
make clean
|
|
CC=clang make -j$(nproc) all SANITIZER=thread
|
|
|
|
TSAN_OPTIONS=halt_on_error=1 ./audio_mixer_voice_threads_test
|
|
|
|
- name: Run mpsc_stack_test under Clang + ThreadSanitizer
|
|
shell: bash
|
|
working-directory: libretro-common/samples/queues/mpsc_stack_test
|
|
run: |
|
|
# mpsc_stack.h is the lock-free many-producer single-consumer
|
|
# stack the runloop's deferred messages ride. Its contract is
|
|
# the push's compare-exchange publishing the node against the
|
|
# drain's exchange; a weakened push loses nodes under
|
|
# contention, observable as a drained-count mismatch AND as a
|
|
# TSan-reported race. The default ASan/UBSan pass above
|
|
# catches neither reliably - the race needs TSan's scheduling
|
|
# to manifest - so this lane is the one that holds the
|
|
# contract.
|
|
set -u
|
|
set -o pipefail
|
|
|
|
make clean
|
|
CC=clang make -j$(nproc) all SANITIZER=thread
|
|
|
|
TSAN_OPTIONS=halt_on_error=1 ./mpsc_stack_test
|
|
|
|
- name: Run retro_spsc_test on the POSIX shm mirror
|
|
shell: bash
|
|
working-directory: libretro-common/samples/queues/retro_spsc_test
|
|
run: |
|
|
# Linux mirrors a ring through memfd; macOS and the BSDs take
|
|
# the POSIX shm path, which this builds here so it runs on
|
|
# every push, under ASan.
|
|
set -eu
|
|
make clean
|
|
make -j$(nproc) all CC="gcc -DRETRO_SPSC_MIRROR_SHM" SANITIZER=address,undefined
|
|
./retro_spsc_test
|
|
|
|
- name: Run retro_spsc_test under Clang + ThreadSanitizer
|
|
shell: bash
|
|
working-directory: libretro-common/samples/queues/retro_spsc_test
|
|
run: |
|
|
# retro_spsc.c is a lock-free SPSC byte queue built on
|
|
# retro_atomic.h. Its correctness contract is acquire-load
|
|
# / release-store on the head and tail cursors, with the
|
|
# buffer reads/writes between them ordered by those barriers.
|
|
# Missing or weakened barriers produce torn data on the
|
|
# consumer side, observable as content mismatches in the
|
|
# stress harness AND as TSan-reported races. The default
|
|
# ASan/UBSan pass above catches the content mismatches but
|
|
# not the races; this lane catches both.
|
|
#
|
|
# halt_on_error=1 makes TSan exit non-zero on the first race
|
|
# rather than continuing -- which is what we want for CI:
|
|
# any race here means the SPSC contract is broken.
|
|
set -u
|
|
set -o pipefail
|
|
|
|
make clean
|
|
CC=clang make -j$(nproc) all SANITIZER=thread
|
|
|
|
TSAN_OPTIONS=halt_on_error=1 ./retro_spsc_test
|
|
|
|
- name: Run retro_eventcount_test under Clang + ThreadSanitizer
|
|
shell: bash
|
|
working-directory: libretro-common/samples/rthreads/retro_eventcount_test
|
|
run: |
|
|
# retro_eventcount parks a consumer without putting a lock on
|
|
# the producer's fast path. What it can get wrong is a lost
|
|
# wakeup: a notify that reads zero waiters while a consumer
|
|
# that has not yet seen the new epoch is on its way into a
|
|
# park. The guard is a seq_cst fence on both sides, and a
|
|
# build with either fence weakened still passes on x86 TSO,
|
|
# so the value checks alone prove nothing -- TSan is what
|
|
# sees the missing barrier.
|
|
#
|
|
# 'make check' runs the address-wait backend and then the
|
|
# condition-variable one, which is the backend macOS, the
|
|
# BSDs and the console ports get. Both matter; only the
|
|
# first is native here.
|
|
#
|
|
# The harness carries its own watchdog, so a genuine lost
|
|
# wakeup fails the job in a minute instead of hanging it.
|
|
set -u
|
|
set -o pipefail
|
|
|
|
make clean
|
|
CC=clang make check SANITIZER=thread
|
|
|
|
- name: Run rzip_parallel_test under Clang + ThreadSanitizer
|
|
shell: bash
|
|
working-directory: libretro-common/samples/streams/rzip
|
|
run: |
|
|
# rzip_stream.c's chunk-writer pool hands each slot between the
|
|
# writer and one worker on an acquire/release status word and
|
|
# parks both sides on retro_eventcount, with no lock anywhere.
|
|
# The value checks prove in-order emission; a weakened store
|
|
# or a lost wakeup still passes them on x86 TSO, and only TSan
|
|
# sees the missing barrier. The test drives the pool at every
|
|
# width and through two hundred short lifetimes so the
|
|
# start / park / shutdown edges are exercised often.
|
|
set -u
|
|
set -o pipefail
|
|
|
|
make clean
|
|
CC=clang make rzip_parallel_test SANITIZER=thread
|
|
|
|
TSAN_OPTIONS=halt_on_error=1 ./rzip_parallel_test
|
|
|
|
- name: Run crc32_test under Clang + ThreadSanitizer
|
|
shell: bash
|
|
working-directory: libretro-common/samples/encodings/crc32
|
|
run: |
|
|
# encoding_crc32() caches x86 feature detection on first use.
|
|
# The failure that matters is publishing that result without
|
|
# release/acquire ordering, which is invisible to the value
|
|
# checks -- every thread still computes the right number on
|
|
# x86 TSO -- and only shows up as a race. The test holds its
|
|
# threads on a gate so they all make their first dispatching
|
|
# call at once; see the comment above crc_first_touch_worker
|
|
# for why that section makes exactly one call per thread.
|
|
set -u
|
|
set -o pipefail
|
|
|
|
make clean
|
|
CC=clang make -j$(nproc) all SANITIZER=thread
|
|
|
|
TSAN_OPTIONS=halt_on_error=1 ./crc32_test
|
|
|
|
- name: Prefix refill boundaries and sliding-window I/O
|
|
run: |
|
|
make -B -C libretro-common/samples/formats/data_transfer data_transfer_prefix_test data_transfer_window_test COUNT_IO=1 SAMPLE_OBJDIR=obj-inert-strict CFLAGS='-Wall -pedantic -std=gnu99 -O2 -g1 -DDT_STRICT -DDT_COUNT_IO -DHAVE_MMAP -D_GNU_SOURCE -I../../../include'
|
|
cd libretro-common/samples/formats/data_transfer
|
|
INERT_ONLY=1 ./data_transfer_prefix_test
|
|
./data_transfer_window_test
|
|
|
|
- name: ccache statistics
|
|
if: ${{ !cancelled() }}
|
|
continue-on-error: true
|
|
run: ccache -sv
|
|
|
|
- name: Save ccache
|
|
# Only master's pushes keep a cache: pull requests restore
|
|
# master's, so one push does not evict the next one's.
|
|
if: success() && github.event_name == 'push' && github.ref == 'refs/heads/master'
|
|
uses: actions/cache/save@v4
|
|
with:
|
|
path: ~/.ccache
|
|
key: ccache-${{ github.workflow }}-${{ github.job }}-${{ github.sha }}
|
|
|
|
# memmap.h picks between libretro-common's mman shim and the
|
|
# platform's own <sys/mman.h>, and Emscripten is the target where
|
|
# that choice is load-bearing: its libc declares mmap with a 64-bit
|
|
# off_t while the shim declares a 32-bit size_t, so a consumer that
|
|
# sees both declarations fails to compile, and reserve/commit has no
|
|
# meaning behind it at all - an anonymous mmap there allocates and
|
|
# zeroes the whole length, and mprotect and madvise are no-ops.
|
|
#
|
|
# None of that is reachable from the native sweep above, which
|
|
# compiles the sample with every Emscripten branch preprocessed
|
|
# away. This lane is where the declarations are held to libc's.
|
|
emscripten-memmap:
|
|
name: Emscripten memmap ABI
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 20
|
|
|
|
env:
|
|
APT_PKGS: ccache
|
|
# Pinned rather than floating: the job exists to hold a libc ABI,
|
|
# so the toolchain it is held against changes deliberately.
|
|
EM_VERSION: 5.0.1
|
|
EM_CACHE_FOLDER: emsdk-cache
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Apt cache key
|
|
id: apt
|
|
run: sh .github/workflows/scripts/apt-cache-key.sh "$APT_PKGS"
|
|
|
|
- name: Restore apt cache
|
|
uses: actions/cache/restore@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
restore-keys: ${{ steps.apt.outputs.prefix }}
|
|
|
|
- name: Install dependencies
|
|
timeout-minutes: 10
|
|
run: sh .github/workflows/scripts/apt-install.sh $APT_PKGS
|
|
|
|
- name: Save apt cache
|
|
if: env.APT_CACHE_SAVE == '1'
|
|
uses: actions/cache/save@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
|
|
- name: Link with lld
|
|
run: sh .github/workflows/scripts/use-lld.sh
|
|
|
|
- name: Restore ccache
|
|
uses: actions/cache/restore@v4
|
|
with:
|
|
path: ~/.ccache
|
|
key: ccache-${{ github.workflow }}-${{ github.job }}-${{ github.sha }}
|
|
restore-keys: |
|
|
ccache-${{ github.workflow }}-${{ github.job }}-
|
|
|
|
- name: Enable ccache
|
|
run: |
|
|
set -eu
|
|
export CCACHE_DIR="$HOME/.ccache"
|
|
echo "CCACHE_DIR=$CCACHE_DIR" >> "$GITHUB_ENV"
|
|
echo "/usr/lib/ccache" >> "$GITHUB_PATH"
|
|
ccache -M 250M
|
|
ccache -z
|
|
|
|
- name: Cache emsdk
|
|
uses: actions/cache@v4
|
|
with:
|
|
path: ${{ env.EM_CACHE_FOLDER }}
|
|
key: ${{ env.EM_VERSION }}-${{ runner.os }}
|
|
|
|
- name: Set up Emscripten
|
|
uses: emscripten-core/setup-emsdk@v15
|
|
with:
|
|
version: ${{ env.EM_VERSION }}
|
|
actions-cache-folder: ${{ env.EM_CACHE_FOLDER }}
|
|
|
|
- name: Run the memmap sample under node
|
|
shell: bash
|
|
working-directory: libretro-common/samples/memmap/emscripten_mmap
|
|
run: |
|
|
set -eu
|
|
set -o pipefail
|
|
# Fails to compile without the libc declaration; the mmap
|
|
# assignment through an off_t-typed function pointer is the
|
|
# discriminator. The mprotect / reserve / msync checks that
|
|
# follow it cover the calls the same header choice hands to
|
|
# libc.
|
|
make clean
|
|
make check-wasm
|
|
|
|
- name: Compile vfs_implementation.c with file mappings enabled
|
|
shell: bash
|
|
working-directory: libretro-common
|
|
run: |
|
|
set -eu
|
|
set -o pipefail
|
|
# The in-tree translation unit that includes memmap.h and
|
|
# <sys/mman.h> together. Makefile.emscripten does not set
|
|
# HAVE_MMAP today, so nothing else compiles this shape for
|
|
# wasm - which is exactly why the two declarations can drift
|
|
# apart unnoticed until a downstream consumer hits it.
|
|
emcc -std=c89 -D_GNU_SOURCE -DHAVE_MMAP \
|
|
-Wall -Wextra -Werror \
|
|
-I include -c vfs/vfs_implementation.c \
|
|
-o "$RUNNER_TEMP/vfs_implementation.o"
|
|
|
|
- name: ccache statistics
|
|
if: ${{ !cancelled() }}
|
|
continue-on-error: true
|
|
run: ccache -sv
|
|
|
|
- name: Save ccache
|
|
# Only master's pushes keep a cache: pull requests restore
|
|
# master's, so one push does not evict the next one's.
|
|
if: success() && github.event_name == 'push' && github.ref == 'refs/heads/master'
|
|
uses: actions/cache/save@v4
|
|
with:
|
|
path: ~/.ccache
|
|
key: ccache-${{ github.workflow }}-${{ github.job }}-${{ github.sha }}
|
|
|
|
# Cross-architecture validation lane for retro_atomic_test.
|
|
#
|
|
# The samples job above runs on x86_64, which is a strongly-ordered
|
|
# (TSO) architecture. retro_atomic.h's contract is that acquire-load
|
|
# / release-store / acq_rel-RMW emit real barriers on weakly-ordered
|
|
# SMP targets (ARM, AArch64, PowerPC, MIPS). An x86_64 host run
|
|
# cannot exercise that property, because TSO masks reordering bugs
|
|
# at the hardware level even when the macros emit no barriers at all.
|
|
#
|
|
# This job cross-compiles retro_atomic_test for AArch64 and ARMv7 and
|
|
# runs the binary under qemu-user-static. qemu-user emulates the
|
|
# weak memory model faithfully enough to expose missing-barrier bugs
|
|
# in the SPSC stress test, and is cheap enough to run on every push.
|
|
#
|
|
# We deliberately do NOT run the full samples sweep here -- the rest
|
|
# of the samples don't have architecture-dependent codegen that
|
|
# warrants the extra CI time. retro_atomic_test is the one that
|
|
# benefits from cross-arch coverage.
|
|
#
|
|
# Real ARM hardware still beats qemu (see e.g. PostgreSQL's 2025
|
|
# Win11/ARM64 atomic ordering bug, found only on real silicon),
|
|
# but qemu catches most categorical errors and is much cheaper than
|
|
# provisioning ARM runners.
|
|
config-file-fuzz-smoke:
|
|
name: config_file differential fuzz (smoke)
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
APT_PKGS: clang ccache
|
|
timeout-minutes: 10
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Apt cache key
|
|
id: apt
|
|
run: sh .github/workflows/scripts/apt-cache-key.sh "$APT_PKGS"
|
|
|
|
- name: Restore apt cache
|
|
uses: actions/cache/restore@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
restore-keys: ${{ steps.apt.outputs.prefix }}
|
|
|
|
- name: Install dependencies
|
|
timeout-minutes: 20
|
|
run: sh .github/workflows/scripts/apt-install.sh $APT_PKGS
|
|
|
|
- name: Save apt cache
|
|
if: env.APT_CACHE_SAVE == '1'
|
|
uses: actions/cache/save@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
|
|
- name: Link with lld
|
|
run: sh .github/workflows/scripts/use-lld.sh
|
|
|
|
- name: Restore ccache
|
|
uses: actions/cache/restore@v4
|
|
with:
|
|
path: ~/.ccache
|
|
key: ccache-${{ github.workflow }}-${{ github.job }}-${{ github.sha }}
|
|
restore-keys: |
|
|
ccache-${{ github.workflow }}-${{ github.job }}-
|
|
|
|
- name: Enable ccache
|
|
run: |
|
|
set -eu
|
|
export CCACHE_DIR="$HOME/.ccache"
|
|
echo "CCACHE_DIR=$CCACHE_DIR" >> "$GITHUB_ENV"
|
|
echo "/usr/lib/ccache" >> "$GITHUB_PATH"
|
|
ccache -M 250M
|
|
ccache -z
|
|
|
|
- name: Build fuzz target
|
|
shell: bash
|
|
working-directory: libretro-common/samples/file/config_file
|
|
run: |
|
|
set -eu
|
|
LC=../../..
|
|
clang -O1 -g1 -fsanitize=fuzzer,address,undefined \
|
|
-fno-sanitize-recover=all \
|
|
-I$LC/include -o config_file_fuzz config_file_fuzz.c \
|
|
$LC/file/config_file.c \
|
|
$LC/string/rstrtod.c \
|
|
$LC/file/file_path.c \
|
|
$LC/compat/compat_strl.c \
|
|
$LC/compat/compat_strcasestr.c \
|
|
$LC/compat/compat_posix_string.c \
|
|
$LC/encodings/encoding_utf.c \
|
|
$LC/lists/string_list.c \
|
|
$LC/time/rtime.c
|
|
|
|
- name: Run fuzz smoke
|
|
shell: bash
|
|
working-directory: libretro-common/samples/file/config_file
|
|
run: |
|
|
set -eu
|
|
# Seed corpus: representative config shapes plus the byte
|
|
# patterns the target's invariants care about (embedded NUL,
|
|
# comment-in-literal, directives, duplicate keys, CRLF).
|
|
# Generated here rather than committed so no binary files
|
|
# live in the tree.
|
|
mkdir -p corpus
|
|
printf 'video_driver = "vulkan"\nvideo_fullscreen = "true"\naudio_rate = "48000"\n' > corpus/basic
|
|
printf '# comment\nkey = "has # inside"\ndup = "a"\ndup = "b"\n spaced = bare\r\nempty = ""\n' > corpus/edge
|
|
printf '#include "sub.cfg"\n#reference "other.cfg"\nafter = "1"\n' > corpus/directives
|
|
python3 -c "import sys; sys.stdout.buffer.write(b'a = \"1\"\nb\x00c = \"2\"\nd = \"3\"\n')" > corpus/embedded_nul
|
|
# 60 seconds of coverage-guided differential fuzzing:
|
|
# every input is parsed whole and again through the
|
|
# streaming push API in input-derived packet splits, then
|
|
# entry-list equality, hash-map coherence and tail
|
|
# validity are enforced (see config_file_fuzz.c). Any
|
|
# divergence or sanitizer report fails the job.
|
|
./config_file_fuzz -max_len=512 -max_total_time=60 \
|
|
-print_final_stats=1 corpus/
|
|
|
|
- name: ccache statistics
|
|
if: ${{ !cancelled() }}
|
|
continue-on-error: true
|
|
run: ccache -sv
|
|
|
|
- name: Save ccache
|
|
# Only master's pushes keep a cache: pull requests restore
|
|
# master's, so one push does not evict the next one's.
|
|
if: success() && github.event_name == 'push' && github.ref == 'refs/heads/master'
|
|
uses: actions/cache/save@v4
|
|
with:
|
|
path: ~/.ccache
|
|
key: ccache-${{ github.workflow }}-${{ github.job }}-${{ github.sha }}
|
|
|
|
task-queue-mingw:
|
|
name: task_queue samples under mingw-w64 + Wine
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
APT_PKGS: build-essential gcc-mingw-w64-x86-64 wine wine64
|
|
timeout-minutes: 15
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Apt cache key
|
|
id: apt
|
|
run: sh .github/workflows/scripts/apt-cache-key.sh "$APT_PKGS"
|
|
|
|
- name: Restore apt cache
|
|
uses: actions/cache/restore@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
restore-keys: ${{ steps.apt.outputs.prefix }}
|
|
|
|
- name: Install dependencies
|
|
timeout-minutes: 20
|
|
run: sh .github/workflows/scripts/apt-install.sh $APT_PKGS
|
|
|
|
- name: Save apt cache
|
|
if: env.APT_CACHE_SAVE == '1'
|
|
uses: actions/cache/save@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
|
|
- name: Link with lld
|
|
run: sh .github/workflows/scripts/use-lld.sh
|
|
|
|
- name: Build and run the task_queue samples under Wine
|
|
shell: bash
|
|
working-directory: libretro-common/samples/queues
|
|
env:
|
|
WINEDEBUG: -all
|
|
run: |
|
|
set -u
|
|
set -o pipefail
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
# The samples job above runs these on Linux only. On Win32
|
|
# retro_sleep() is a function in time/rtime.c rather than a
|
|
# macro, so a Makefile that links on Linux can still fail to
|
|
# link for Windows; the threaded queue's Win32 paths in
|
|
# rthreads.c are not run anywhere else either.
|
|
fails=0
|
|
for d in task_queue_*; do
|
|
t=$(sed -nE 's/^TARGET[[:space:]]*:?=[[:space:]]*//p' "$d/Makefile")
|
|
printf '\n[build] %s\n' "$d"
|
|
if ! ( cd "$d" && make clean && make CC=x86_64-w64-mingw32-gcc all ); then
|
|
printf '::error title=mingw build failed::%s does not build for Windows\n' "$d"
|
|
fails=$((fails+1))
|
|
continue
|
|
fi
|
|
printf '[run] %s.exe\n' "$t"
|
|
if ( cd "$d" && timeout 120 wine "./$t.exe" ); then
|
|
printf '[pass] %s\n' "$t"
|
|
else
|
|
rc=$?
|
|
printf '::error title=Test failed under Wine::%s exited with status %d\n' "$t" "$rc"
|
|
fails=$((fails+1))
|
|
fi
|
|
done
|
|
[[ $fails -eq 0 ]]
|
|
|
|
- name: Build and run the NVIDIA loaders under Wine
|
|
shell: bash
|
|
working-directory: libretro-common/samples/gfx/nvidia
|
|
env:
|
|
WINEDEBUG: -all
|
|
run: |
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
# The Windows half of the loaders: _nvngx.dll beside the
|
|
# program and nvapi64.dll by LoadLibrary, the Direct3D 12
|
|
# table resolved, the stand-ins built with mingw.
|
|
make clean
|
|
make CC=x86_64-w64-mingw32-gcc all
|
|
timeout 120 wine ./nvidia_loader_test.exe
|
|
make clean
|
|
|
|
- name: Build and run the FidelityFX and XeSS loaders under Wine
|
|
shell: bash
|
|
working-directory: libretro-common/samples/gfx
|
|
env:
|
|
WINEDEBUG: -all
|
|
run: |
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
# The Windows half of the loaders: the SDK's .dll names by
|
|
# LoadLibraryEx from the directory, the stand-ins built with
|
|
# mingw.
|
|
for d in amd intel; do
|
|
t=$(sed -nE 's/^TARGET[[:space:]]*:?=[[:space:]]*//p' "$d/Makefile")
|
|
( cd "$d" && make clean && make CC=x86_64-w64-mingw32-gcc all && timeout 120 wine "./$t.exe" && make clean )
|
|
done
|
|
|
|
- name: Build and run the mapped read-ahead test under Wine
|
|
shell: bash
|
|
working-directory: libretro-common/samples/file/vfs
|
|
env:
|
|
WINEDEBUG: -all
|
|
run: |
|
|
set -eu
|
|
set -o pipefail
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
make clean MMAP=0
|
|
make CC=x86_64-w64-mingw32-gcc MMAP=0 vfs_readahead_test
|
|
timeout 120 wine ./vfs_readahead_test.exe
|
|
RETRO_VFS_PREFETCH=touch timeout 120 wine ./vfs_readahead_test.exe
|
|
RETRO_VFS_PREFETCH=none timeout 120 wine ./vfs_readahead_test.exe
|
|
make clean MMAP=0
|
|
|
|
- name: Build and run native DVD packets under Wine
|
|
shell: bash
|
|
working-directory: libretro-common/samples/formats/dvd
|
|
env:
|
|
WINEDEBUG: -all
|
|
run: |
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
make CC=x86_64-w64-mingw32-gcc EXE=.exe all
|
|
timeout 120 wine ./rdvd_h262_test.exe
|
|
timeout 120 wine ./rdvd_lpcm_test.exe
|
|
timeout 120 wine ./rdvd_ac3_test.exe
|
|
timeout 120 wine ./rdvd_packet_test.exe
|
|
timeout 120 wine ./rdvd_highlight_test.exe
|
|
timeout 120 wine ./rdvd_spu_test.exe
|
|
timeout 120 wine ./rdvd_ifo_test.exe
|
|
timeout 120 wine ./rdvd_source_test.exe
|
|
timeout 120 wine ./rdvd_vfs_test.exe
|
|
timeout 120 wine ./rdvd_vfs_real_test.exe
|
|
|
|
retro-atomic-cross:
|
|
name: Cross-arch retro_atomic_test (${{ matrix.arch }})
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
APT_PKGS: build-essential ${{ matrix.apt_pkgs }} qemu-user-static ccache
|
|
timeout-minutes: 10
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- arch: aarch64
|
|
cc: aarch64-linux-gnu-gcc
|
|
apt_pkgs: gcc-aarch64-linux-gnu
|
|
qemu: qemu-aarch64-static
|
|
sysroot: /usr/aarch64-linux-gnu
|
|
- arch: armv7
|
|
cc: arm-linux-gnueabihf-gcc
|
|
apt_pkgs: gcc-arm-linux-gnueabihf
|
|
qemu: qemu-arm-static
|
|
sysroot: /usr/arm-linux-gnueabihf
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Apt cache key
|
|
id: apt
|
|
run: sh .github/workflows/scripts/apt-cache-key.sh "$APT_PKGS"
|
|
|
|
- name: Restore apt cache
|
|
uses: actions/cache/restore@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
restore-keys: ${{ steps.apt.outputs.prefix }}
|
|
|
|
- name: Install dependencies
|
|
timeout-minutes: 20
|
|
run: sh .github/workflows/scripts/apt-install.sh $APT_PKGS
|
|
|
|
- name: Save apt cache
|
|
if: env.APT_CACHE_SAVE == '1'
|
|
uses: actions/cache/save@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
|
|
- name: Link with lld
|
|
run: sh .github/workflows/scripts/use-lld.sh
|
|
|
|
- name: Restore ccache
|
|
uses: actions/cache/restore@v4
|
|
with:
|
|
path: ~/.ccache
|
|
key: ccache-${{ github.workflow }}-${{ github.job }}-${{ matrix.arch }}-${{ github.sha }}
|
|
restore-keys: |
|
|
ccache-${{ github.workflow }}-${{ github.job }}-${{ matrix.arch }}-
|
|
|
|
- name: Enable ccache
|
|
run: |
|
|
set -eu
|
|
export CCACHE_DIR="$HOME/.ccache"
|
|
echo "CCACHE_DIR=$CCACHE_DIR" >> "$GITHUB_ENV"
|
|
echo "/usr/lib/ccache" >> "$GITHUB_PATH"
|
|
ccache -M 250M
|
|
ccache -z
|
|
|
|
- name: Build retro_atomic_test for ${{ matrix.arch }}
|
|
working-directory: libretro-common/samples/atomic/retro_atomic_test
|
|
run: |
|
|
set -u
|
|
set -o pipefail
|
|
make clean
|
|
CC=${{ matrix.cc }} make all
|
|
|
|
- name: Run retro_atomic_test under qemu-user
|
|
working-directory: libretro-common/samples/atomic/retro_atomic_test
|
|
run: |
|
|
set -u
|
|
set -o pipefail
|
|
${{ matrix.qemu }} -L ${{ matrix.sysroot }} ./retro_atomic_test
|
|
|
|
- name: Inspect emitted atomic instructions
|
|
working-directory: libretro-common/samples/atomic/retro_atomic_test
|
|
run: |
|
|
set -u
|
|
set -o pipefail
|
|
# Spot-check the codegen. If retro_atomic.h were silently
|
|
# falling through to a no-barrier backend on this arch, the
|
|
# asm would be conspicuously missing acquire/release
|
|
# instructions. This is a cheap sanity check on top of the
|
|
# behavioural SPSC test above.
|
|
${{ matrix.cc }} -O2 -S \
|
|
-I../../../include -DHAVE_THREADS \
|
|
retro_atomic_test.c -o /tmp/retro_atomic_test.s
|
|
echo
|
|
echo '== Unique barrier-emitting mnemonics =='
|
|
case "${{ matrix.arch }}" in
|
|
aarch64)
|
|
# Expect: ldar, stlr, and __aarch64_ldadd*_acq_rel libcalls
|
|
# (or inline ldaddal LSE on +lse builds).
|
|
pattern='\b(ldar|stlr|ldax|stlx|dmb|ldadd[a-z0-9_]*|swp[a-z0-9_]*|__aarch64_(ldadd|swp)[a-z0-9_]*acq_rel)\b'
|
|
;;
|
|
armv7)
|
|
# Expect: dmb (data memory barrier) and ldrex/strex pairs.
|
|
pattern='\b(dmb|ldrex|strex|ldrexb|strexb|ldrexh|strexh)\b'
|
|
;;
|
|
esac
|
|
mnemonics=$(grep -oE "$pattern" /tmp/retro_atomic_test.s | sort -u)
|
|
echo "$mnemonics"
|
|
if [[ -z "$mnemonics" ]]; then
|
|
echo
|
|
echo '::error title=No barrier instructions emitted::retro_atomic_test.s contains no acquire/release/barrier mnemonics for ${{ matrix.arch }}; retro_atomic.h may have fallen through to a no-barrier backend.'
|
|
exit 1
|
|
fi
|
|
|
|
- name: ccache statistics
|
|
if: ${{ !cancelled() }}
|
|
continue-on-error: true
|
|
run: ccache -sv
|
|
|
|
- name: Save ccache
|
|
# Only master's pushes keep a cache: pull requests restore
|
|
# master's, so one push does not evict the next one's.
|
|
if: success() && github.event_name == 'push' && github.ref == 'refs/heads/master'
|
|
uses: actions/cache/save@v4
|
|
with:
|
|
path: ~/.ccache
|
|
key: ccache-${{ github.workflow }}-${{ github.job }}-${{ matrix.arch }}-${{ github.sha }}
|
|
|
|
# encoding_crc32() picks a different implementation per target: the
|
|
# ARMv8 CRC32 instructions when the compiler advertises +crc, a
|
|
# PCLMULQDQ fold on x86, and a slicing-by-8 table everywhere else.
|
|
# The table path reads the input as little-endian 32-bit words and
|
|
# byte-swaps under MSB_FIRST, so a big-endian target is the only
|
|
# place a missing or wrong swap is visible -- on x86 the swap
|
|
# compiles away and every test still passes. powerpc is carried
|
|
# here for exactly that reason, and is the only big-endian lane in
|
|
# the sample suite.
|
|
#
|
|
# aarch64 appears twice on purpose. Stock aarch64 gcc does not
|
|
# define __ARM_FEATURE_CRC32, so a default build exercises the table
|
|
# path; only Makefile.libnx passes +crc today, and the second entry
|
|
# is what covers the intrinsic path at all.
|
|
#
|
|
# qemu-user cannot run the sanitizer runtimes, so these lanes rely on
|
|
# the test's own bitwise reference and its guard-page section, which
|
|
# catches an overread as a SIGSEGV rather than a redzone hit.
|
|
crc32-cross:
|
|
name: Cross-arch crc32_test (${{ matrix.name }})
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
APT_PKGS: build-essential ${{ matrix.apt_pkgs }} qemu-user-static ccache
|
|
timeout-minutes: 20
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
include:
|
|
- name: aarch64
|
|
cc: aarch64-linux-gnu-gcc
|
|
cflags: ""
|
|
apt_pkgs: gcc-aarch64-linux-gnu
|
|
qemu: qemu-aarch64-static
|
|
sysroot: /usr/aarch64-linux-gnu
|
|
- name: aarch64+crc
|
|
cc: aarch64-linux-gnu-gcc
|
|
cflags: "-march=armv8-a+crc"
|
|
apt_pkgs: gcc-aarch64-linux-gnu
|
|
qemu: qemu-aarch64-static
|
|
sysroot: /usr/aarch64-linux-gnu
|
|
- name: armv7
|
|
cc: arm-linux-gnueabihf-gcc
|
|
cflags: ""
|
|
apt_pkgs: gcc-arm-linux-gnueabihf
|
|
qemu: qemu-arm-static
|
|
sysroot: /usr/arm-linux-gnueabihf
|
|
- name: powerpc-big-endian
|
|
cc: powerpc-linux-gnu-gcc
|
|
cflags: ""
|
|
apt_pkgs: gcc-powerpc-linux-gnu
|
|
qemu: qemu-ppc-static
|
|
sysroot: /usr/powerpc-linux-gnu
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Apt cache key
|
|
id: apt
|
|
run: sh .github/workflows/scripts/apt-cache-key.sh "$APT_PKGS"
|
|
|
|
- name: Restore apt cache
|
|
uses: actions/cache/restore@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
restore-keys: ${{ steps.apt.outputs.prefix }}
|
|
|
|
- name: Install dependencies
|
|
timeout-minutes: 20
|
|
run: sh .github/workflows/scripts/apt-install.sh $APT_PKGS
|
|
|
|
- name: Save apt cache
|
|
if: env.APT_CACHE_SAVE == '1'
|
|
uses: actions/cache/save@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
|
|
- name: Link with lld
|
|
run: sh .github/workflows/scripts/use-lld.sh
|
|
|
|
- name: Restore ccache
|
|
uses: actions/cache/restore@v4
|
|
with:
|
|
path: ~/.ccache
|
|
key: ccache-${{ github.workflow }}-${{ github.job }}-${{ matrix.name }}-${{ github.sha }}
|
|
restore-keys: |
|
|
ccache-${{ github.workflow }}-${{ github.job }}-${{ matrix.name }}-
|
|
|
|
- name: Enable ccache
|
|
run: |
|
|
set -eu
|
|
export CCACHE_DIR="$HOME/.ccache"
|
|
echo "CCACHE_DIR=$CCACHE_DIR" >> "$GITHUB_ENV"
|
|
echo "/usr/lib/ccache" >> "$GITHUB_PATH"
|
|
ccache -M 250M
|
|
ccache -z
|
|
|
|
- name: Build crc32_test for ${{ matrix.name }}
|
|
working-directory: libretro-common/samples/encodings/crc32
|
|
run: |
|
|
set -u
|
|
set -o pipefail
|
|
# HAVE_THREADS=0: rthreads needs a target libpthread that the
|
|
# cross toolchains here do not reliably provide. The threaded
|
|
# sections are covered by the native Clang/TSan lane above;
|
|
# what these lanes are for is the per-architecture arithmetic.
|
|
make clean
|
|
CC="${{ matrix.cc }}" CFLAGS="${{ matrix.cflags }}" make all HAVE_THREADS=0
|
|
|
|
- name: Run crc32_test under qemu-user
|
|
working-directory: libretro-common/samples/encodings/crc32
|
|
run: |
|
|
set -u
|
|
set -o pipefail
|
|
${{ matrix.qemu }} -L ${{ matrix.sysroot }} ./crc32_test
|
|
|
|
- name: Build and run pixconv_test under qemu-user
|
|
shell: bash
|
|
working-directory: libretro-common/samples/gfx/pixconv
|
|
run: |
|
|
set -euo pipefail
|
|
# The ARM legs are the only place the NEON converters run;
|
|
# PowerPC runs the scalar paths big-endian. The armhf
|
|
# toolchain defaults to VFP without NEON, so armv7 builds a
|
|
# second time with NEON on.
|
|
make clean
|
|
CC="${{ matrix.cc }}" CFLAGS="${{ matrix.cflags }}" make all
|
|
for t in pixconv_test pixconv_test_nosimd pixconv_test_scalar; do
|
|
${{ matrix.qemu }} -L ${{ matrix.sysroot }} ./$t
|
|
done
|
|
if [ "${{ matrix.name }}" = armv7 ]; then
|
|
make clean
|
|
CC="${{ matrix.cc }}" CFLAGS="-mfpu=neon" make all
|
|
${{ matrix.qemu }} -L ${{ matrix.sysroot }} ./pixconv_test
|
|
fi
|
|
make clean
|
|
|
|
- name: Build and run the sinc kernels under qemu-user
|
|
if: startsWith(matrix.name, 'aarch64') || matrix.name == 'armv7'
|
|
shell: bash
|
|
working-directory: samples/audio
|
|
run: |
|
|
set -euo pipefail
|
|
# The only place the NEON sinc kernels run: aarch64 the
|
|
# intrinsics, armv7 the armeabi assembly for both table
|
|
# layouts, against the scalar kernels, through dispatch and
|
|
# the fast-forward stages, and shared between siblings.
|
|
flags="${{ matrix.cflags }}"
|
|
asm=0
|
|
if [ "${{ matrix.name }}" = armv7 ]; then
|
|
flags="-mfpu=neon"
|
|
asm=1
|
|
fi
|
|
tests="sinc_dispatch sinc_hq sinc_fastforward sinc_sibling"
|
|
for d in $tests; do
|
|
make -C $d clean
|
|
CFLAGS="$flags" make -C $d CC="${{ matrix.cc }}" NEON_ASM=$asm
|
|
done
|
|
# Each test is one qemu-user process on one CPU: run them all
|
|
# at once, and print each one's output once it is done.
|
|
declare -A pid
|
|
for d in $tests; do
|
|
${{ matrix.qemu }} -L ${{ matrix.sysroot }} $d/${d}_test \
|
|
> "$RUNNER_TEMP/$d.log" 2>&1 &
|
|
pid[$d]=$!
|
|
done
|
|
fail=0
|
|
for d in $tests; do
|
|
wait "${pid[$d]}" || { echo "FAIL $d"; fail=1; }
|
|
cat "$RUNNER_TEMP/$d.log"
|
|
make -C $d clean
|
|
done
|
|
exit $fail
|
|
|
|
- name: Run vfs_mapped_ptr_test without mappings (MMAP=0)
|
|
shell: bash
|
|
working-directory: libretro-common/samples/file/vfs
|
|
run: |
|
|
set -eu
|
|
# The default build has HAVE_MMAP, where filestream_matches_buf()
|
|
# compares against the memory map in a single memcmp and its
|
|
# windowed fallback never executes - so the window-boundary
|
|
# cases in the run above assert on code that did not run.
|
|
# This lane is where that path is actually under test, and it
|
|
# is also the only coverage the platforms without mappings
|
|
# get. Verified to fail here with the window advance off by
|
|
# one, which the mapped lane does not notice at all.
|
|
make clean MMAP=0 SANITIZER=address,undefined && make -j$(nproc) all MMAP=0 SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1:detect_stack_use_after_return=1 \
|
|
UBSAN_OPTIONS=print_stacktrace=1 timeout 120 \
|
|
./vfs_mapped_ptr_test
|
|
echo "[pass] vfs_mapped_ptr_test (MMAP=0, ASan)"
|
|
|
|
- name: Run vfs_v5_metadata_test against a copy_file_range that ignores len
|
|
shell: bash
|
|
working-directory: libretro-common/samples/file/vfs
|
|
run: |
|
|
set -eu
|
|
# A sandboxed kernel (gVisor on some hosted runners) answers
|
|
# copy_file_range by copying to EOF regardless of len. The
|
|
# VFS must account the bytes, retire the kernel path for the
|
|
# process, and finish the copy in place on the portable path.
|
|
# FAKE_CFR=1 is the to-EOF case, FAKE_CFR=2 a partial overshoot
|
|
# that leaves the copy part-way and has to be resumed.
|
|
for mode in 1 2; do
|
|
make clean >/dev/null
|
|
make vfs_v5_metadata_test FAKE_CFR=$mode SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1 timeout 120 \
|
|
./vfs_v5_metadata_test
|
|
echo "[pass] vfs_v5_metadata_test (FAKE_CFR=$mode, ASan)"
|
|
done
|
|
|
|
- name: ccache statistics
|
|
if: ${{ !cancelled() }}
|
|
continue-on-error: true
|
|
run: ccache -sv
|
|
|
|
- name: Save ccache
|
|
# Only master's pushes keep a cache: pull requests restore
|
|
# master's, so one push does not evict the next one's.
|
|
if: success() && github.event_name == 'push' && github.ref == 'refs/heads/master'
|
|
uses: actions/cache/save@v4
|
|
with:
|
|
path: ~/.ccache
|
|
key: ccache-${{ github.workflow }}-${{ github.job }}-${{ matrix.name }}-${{ github.sha }}
|