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..57f6bd8 --- /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'] + 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') +} +''', 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' + 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 {