From 8687a933ef1596c60e58ccf41c8c8d5629b67f0b Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Sat, 20 Jun 2026 20:25:34 +0300 Subject: [PATCH] tests: skip closure boehm steps when libgc is unavailable (fix ubuntu-musl CI) The closure_lifetime_api and closure_context_skip_unused tests shell out to '-gc boehm'/'-gc boehm_leak' subprocesses and assert success. On the docker-ubuntu-musl image there is no musl-compatible Boehm GC library, so those builds fail with 'ld: cannot find -lgc'. The existing missing-lib guard only covered boehm_leak mode and matched the substring 'libgc', which the musl linker error does not contain. Broaden the detector to recognize the real error formats (-lgc, -lgc-threaded, macOS "library 'gc' not found", missing libgc.so) and apply the skip-when-missing guard to all non-none GC modes. Boehm coverage is preserved wherever the library is available (alpine, glibc, macOS); only genuine missing-lib failures are skipped. --- .../fns/closure_context_skip_unused_test.v | 14 ++++---- vlib/v/tests/fns/closure_lifetime_api_test.v | 34 +++++++++++++------ 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/vlib/v/tests/fns/closure_context_skip_unused_test.v b/vlib/v/tests/fns/closure_context_skip_unused_test.v index cd911ecf1..dac8023e4 100644 --- a/vlib/v/tests/fns/closure_context_skip_unused_test.v +++ b/vlib/v/tests/fns/closure_context_skip_unused_test.v @@ -2,9 +2,12 @@ import os const vexe = @VEXE -fn missing_boehm_leak_lib(output string) bool { - return output.contains('libgc') && (output.contains('was not found') - || output.contains('cannot find')) +fn missing_boehm_lib(output string) bool { + mentions_gc := output.contains('libgc') || output.contains('-lgc') + || output.contains("library 'gc'") || output.contains('bdw-gc') + return mentions_gc && (output.contains('was not found') + || output.contains('cannot find') || output.contains('not found') + || output.contains('No such file')) } fn closure_skip_unused_source() string { @@ -61,9 +64,8 @@ fn run_closure_skip_unused_case(tmp_dir string, mode string) { os.write_file(source_path, closure_skip_unused_source()) or { panic(err) } compile_cmd := '${os.quoted_path(vexe)} -skip-unused -gc ${mode} -o ${os.quoted_path(binary_path)} ${os.quoted_path(source_path)}' compile_res := os.execute(compile_cmd) - if mode == 'boehm_leak' && compile_res.exit_code != 0 - && missing_boehm_leak_lib(compile_res.output) { - eprintln('skipping boehm_leak closure skip-unused test: missing libgc') + if mode != 'none' && compile_res.exit_code != 0 && missing_boehm_lib(compile_res.output) { + eprintln('skipping ${mode} closure skip-unused test: missing libgc') return } assert compile_res.exit_code == 0, compile_res.output diff --git a/vlib/v/tests/fns/closure_lifetime_api_test.v b/vlib/v/tests/fns/closure_lifetime_api_test.v index e9a78d04e..4f1cdb48f 100644 --- a/vlib/v/tests/fns/closure_lifetime_api_test.v +++ b/vlib/v/tests/fns/closure_lifetime_api_test.v @@ -14,9 +14,24 @@ fn skip_test(reason string) { exit(0) } -fn missing_boehm_leak_lib(output string) bool { - return output.contains('libgc') && (output.contains('was not found') - || output.contains('cannot find')) +fn missing_boehm_lib(output string) bool { + mentions_gc := output.contains('libgc') || output.contains('-lgc') + || output.contains("library 'gc'") || output.contains('bdw-gc') + return mentions_gc && (output.contains('was not found') + || output.contains('cannot find') || output.contains('not found') + || output.contains('No such file')) +} + +// assert_run_succeeds_or_missing_boehm runs the program with the given gc mode and +// asserts success, but skips gracefully when a `boehm`/`boehm_leak` build fails only +// because the Boehm GC library is unavailable (e.g. musl images without musl libgc). +fn assert_run_succeeds_or_missing_boehm(tmp_dir string, name string, source string, mode string) { + res := run_program_with_gc(tmp_dir, name, source, mode) + if mode != 'none' && res.exit_code != 0 && missing_boehm_lib(res.output) { + eprintln('skipping ${mode} run for ${name}: missing libgc') + return + } + assert res.exit_code == 0, res.output } fn count_occurrences(haystack string, needle string) int { @@ -65,7 +80,7 @@ fn c_output_for_program(tmp_dir string, name string, source string) os.Result { fn assert_boehm_leak_compile_or_missing_lib(tmp_dir string, name string, source string) { res := compile_program_with_gc(tmp_dir, name, source, 'boehm_leak') - if res.exit_code != 0 && missing_boehm_leak_lib(res.output) { + if res.exit_code != 0 && missing_boehm_lib(res.output) { eprintln('skipping boehm_leak compile for ${name}: missing libgc') return } @@ -77,7 +92,7 @@ fn assert_boehm_leak_runtime_or_compile_only(tmp_dir string, name string, source if res.exit_code == 0 { return } - if missing_boehm_leak_lib(res.output) { + if missing_boehm_lib(res.output) { eprintln('skipping boehm_leak runtime for ${name}: missing libgc') return } @@ -589,8 +604,7 @@ fn test_lifetime_public_api_without_captured_closure() { assert !c_res.output.contains('builtin__closure__closure_create') assert !c_res.output.contains('_V_closure_main__') for mode in ['boehm', 'none'] { - res := run_program_with_gc(tmp_dir, 'no_captured_lifetime', source, mode) - assert res.exit_code == 0, res.output + assert_run_succeeds_or_missing_boehm(tmp_dir, 'no_captured_lifetime', source, mode) } assert_boehm_leak_compile_or_missing_lib(tmp_dir, 'no_captured_lifetime', source) } @@ -606,8 +620,7 @@ fn test_closure_lifetime_runtime_api_contract() { } source := closure_lifetime_runtime_source() for mode in ['boehm', 'none'] { - res := run_program_with_gc(tmp_dir, 'closure_lifetime_runtime', source, mode) - assert res.exit_code == 0, res.output + assert_run_succeeds_or_missing_boehm(tmp_dir, 'closure_lifetime_runtime', source, mode) } assert_boehm_leak_compile_or_missing_lib(tmp_dir, 'closure_lifetime_runtime', source) } @@ -713,8 +726,7 @@ fn test_closure_lifetime_lazy_concurrent_runtime_init() { } source := lazy_concurrent_lifetime_init_source() for mode in ['none', 'boehm'] { - res := run_program_with_gc(tmp_dir, 'closure_lifetime_lazy_init', source, mode) - assert res.exit_code == 0, res.output + assert_run_succeeds_or_missing_boehm(tmp_dir, 'closure_lifetime_lazy_init', source, mode) } } -- 2.39.5