From f685f52b399e6b03ae270ca0e038f518262f94fe Mon Sep 17 00:00:00 2001 From: Anton Date: Sun, 11 Oct 2026 06:48:12 +0200 Subject: [PATCH 1/2] Fix Windows Vulkan setup runtime probe execution --- .github/workflows/generated-bindings-ci.yml | 4 ++ ci/test_setup_runtime.py | 52 +++++++++++++++++++++ setup.vsh | 12 ++++- 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 ci/test_setup_runtime.py diff --git a/.github/workflows/generated-bindings-ci.yml b/.github/workflows/generated-bindings-ci.yml index 259473a..5d04034 100644 --- a/.github/workflows/generated-bindings-ci.yml +++ b/.github/workflows/generated-bindings-ci.yml @@ -193,6 +193,10 @@ jobs: if: matrix.v-channel == 'stable' || steps.setup_v_source.outcome == 'success' shell: bash run: v run setup.vsh --check + - name: Test runtime probe execution and working directory + if: matrix.v-channel == 'stable' + shell: bash + run: python ci/test_setup_runtime.py --cc "${{ matrix.compiler }}" - name: Accept old installed headers with bundled build inputs if: runner.os == 'Linux' && matrix.v-channel == 'stable' shell: bash diff --git a/ci/test_setup_runtime.py b/ci/test_setup_runtime.py new file mode 100644 index 0000000..89fbd1c --- /dev/null +++ b/ci/test_setup_runtime.py @@ -0,0 +1,52 @@ +"""Exercise setup's runtime probe without depending on a GPU or installed SDK.""" + +import argparse +import os +from pathlib import Path +import shutil +import subprocess +import tempfile + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--v', default=os.environ.get('VEXE', 'v')) + parser.add_argument('--cc', default='msvc' if os.name == 'nt' else 'gcc') + args = parser.parse_args() + root = Path(__file__).resolve().parents[1] + compiler = shutil.which(args.v) or str(Path(args.v).resolve()) + suffix = '.exe' if os.name == 'nt' else '' + with tempfile.TemporaryDirectory(prefix='vulkan setup with spaces ') as temporary: + work = Path(temporary) + probe_source = work / 'probe.v' + probe_source.write_text('''import os +fn main() { + assert os.args[1..] == ['--summary'] + os.write_file(os.getenv('PROBE_MARKER'), os.getwd()) or { panic(err) } + if os.getenv('PROBE_FAIL') == '1' { exit(23) } + println('GPU0: fixture') +} +''', encoding='utf-8') + probe = work / ('vulkaninfo' + suffix) + subprocess.run([compiler, '-cc', args.cc, '-o', str(probe), str(probe_source)], + check=True, cwd=root) + marker = work / 'ran.txt' + env = dict(os.environ, PATH=str(work) + os.pathsep + os.environ['PATH'], + PROBE_MARKER=str(marker)) + for failure in ['0', '1']: + env['PROBE_FAIL'] = failure + result = subprocess.run([compiler, '-cc', args.cc, 'run', str(root / 'setup.vsh'), + '--check'], cwd=root, env=env, + capture_output=True, text=True, check=True) + print(result.stdout) + assert marker.is_file(), 'vulkaninfo never ran' + assert Path(marker.read_text()).resolve() == Path(tempfile.gettempdir()).resolve() + expected = ('[ok] Vulkan loader enumerated a physical device' if failure == '0' + else '[warning] vulkaninfo is installed, but no usable device was enumerated') + assert expected in result.stdout, result.stdout + result.stderr + marker.unlink() + print('Setup runtime probe tests passed.') + + +if __name__ == '__main__': + main() diff --git a/setup.vsh b/setup.vsh index da49665..9f80013 100644 --- a/setup.vsh +++ b/setup.vsh @@ -240,7 +240,17 @@ fn check() bool { if command_exists('vulkaninfo') { // Some SDK builds write their update manifest into the process working // directory. Keep diagnostics from leaving residue in the user's repo. - result := os.execute('cd ${os.quoted_path(os.temp_dir())} && vulkaninfo --summary') + original_directory := os.getwd() + os.chdir(os.temp_dir()) or { + println('[warning] Could not enter the temporary directory for Vulkan diagnostics: ${err}') + return ok + } + defer { + os.chdir(original_directory) or {} + } + // V's Windows os.execute rejects shell command chaining. Change the + // directory through the OS API so the probe actually runs on Windows. + result := os.execute('vulkaninfo --summary') if result.exit_code == 0 { println('[ok] Vulkan loader enumerated a physical device') } else { From e4b64960a386757bd4ebf36648f342985b563bc1 Mon Sep 17 00:00:00 2001 From: Anton Date: Sun, 11 Oct 2026 06:51:11 +0200 Subject: [PATCH 2/2] Use V temporary directory semantics in runtime fixture --- ci/test_setup_runtime.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/test_setup_runtime.py b/ci/test_setup_runtime.py index 89fbd1c..57f6bd8 100644 --- a/ci/test_setup_runtime.py +++ b/ci/test_setup_runtime.py @@ -22,6 +22,7 @@ def main(): probe_source.write_text('''import os fn main() { assert os.args[1..] == ['--summary'] + assert os.real_path(os.getwd()) == os.real_path(os.temp_dir()) os.write_file(os.getenv('PROBE_MARKER'), os.getwd()) or { panic(err) } if os.getenv('PROBE_FAIL') == '1' { exit(23) } println('GPU0: fixture') @@ -40,7 +41,6 @@ def main(): capture_output=True, text=True, check=True) print(result.stdout) assert marker.is_file(), 'vulkaninfo never ran' - assert Path(marker.read_text()).resolve() == Path(tempfile.gettempdir()).resolve() expected = ('[ok] Vulkan loader enumerated a physical device' if failure == '0' else '[warning] vulkaninfo is installed, but no usable device was enumerated') assert expected in result.stdout, result.stdout + result.stderr