headless.py (5039B)
1 #!/usr/bin/env python3 2 3 # This Source Code Form is subject to the terms of the Mozilla Public 4 # License, v. 2.0. If a copy of the MPL was not distributed with this 5 # file, You can obtain one at http://mozilla.org/MPL/2.0/. 6 7 # Build and run wrench with off-screen software rendering (OSMesa/LLVMpipe) 8 # for platform-independent results. This is good for running reference tests. 9 # 10 # Usage: headless.py ARGS 11 # 12 # Pass ARGS through to wrench, after '--headless' and '--no-scissor'. 13 # 14 # Environment variables: 15 # 16 # WRENCH_HEADLESS_TARGET: If set, don't rebuild wrench. Instead, the value should 17 # be the path to an already-built cargo 'target' directory. This is useful 18 # for running a cross-compiled wrench. 19 # 20 # CARGOFLAGS: Extra flags to be passed to 'cargo build'. Split on whitespace. 21 # 22 # OPTIMIZED: This script uses the release build by default, but if this variable 23 # is set to '0' or 'false', the script uses the debug build. 24 # 25 # DEBUGGER: If set, run wrench under a debugger. Permitted values are 'rr' (run 26 # under 'rr record'), or 'gdb', 'rust-gdb', or 'cgdb' (run under the given 27 # debugger, and arrange to supply ARGS to the wrench debuggee, not the 28 # debugger) 29 30 from __future__ import print_function 31 import contextlib 32 import os 33 import subprocess 34 import sys 35 from os import path 36 from glob import glob 37 38 39 @contextlib.contextmanager 40 def cd(new_path): 41 """Context manager for changing the current working directory""" 42 previous_path = os.getcwd() 43 try: 44 os.chdir(new_path) 45 yield 46 finally: 47 os.chdir(previous_path) 48 49 50 def find_dep_path_newest(package, bin_path): 51 deps_path = path.join(path.split(bin_path)[0], "build") 52 with cd(deps_path): 53 candidates = glob(package + '-*') 54 candidates = (path.join(deps_path, c) for c in candidates) 55 """ For some reason cargo can create an extra osmesa-src without libs """ 56 candidates = (c for c in candidates if path.exists(path.join(c, 'out'))) 57 candidate_times = sorted(((path.getmtime(c), c) for c in candidates), reverse=True) 58 if len(candidate_times) > 0: 59 return candidate_times[0][1] 60 return None 61 62 63 def is_windows(): 64 """ Detect windows, mingw, cygwin """ 65 return sys.platform == 'win32' or sys.platform == 'msys' or sys.platform == 'cygwin' 66 67 68 def is_macos(): 69 return sys.platform == 'darwin' 70 71 72 def is_linux(): 73 return sys.platform.startswith('linux') 74 75 76 def debugger(): 77 if "DEBUGGER" in os.environ: 78 return os.environ["DEBUGGER"] 79 return None 80 81 82 def use_gdb(): 83 return debugger() in ['gdb', 'cgdb', 'rust-gdb'] 84 85 86 def use_rr(): 87 return debugger() == 'rr' 88 89 90 def optimized_build(): 91 if "OPTIMIZED" in os.environ: 92 opt = os.environ["OPTIMIZED"] 93 return opt not in ["0", "false"] 94 return True 95 96 97 def set_osmesa_env(bin_path): 98 """Set proper LD_LIBRARY_PATH and DRIVE for software rendering on Linux and OSX""" 99 base = find_dep_path_newest('osmesa-src', bin_path) 100 osmesa_path = path.join(base, "out", "mesa", "src", "gallium", "targets", "osmesa") 101 os.environ["GALLIUM_DRIVER"] = "llvmpipe" 102 if is_linux(): 103 print(osmesa_path) 104 os.environ["LD_LIBRARY_PATH"] = osmesa_path 105 elif is_macos(): 106 osmesa_path = path.join(base, "out", "mesa", "src", "gallium", "targets", "osmesa") 107 glapi_path = path.join(base, "out", "mesa", "src", "mapi", "shared-glapi") 108 os.environ["DYLD_LIBRARY_PATH"] = osmesa_path + ":" + glapi_path 109 110 111 extra_flags = os.getenv('CARGOFLAGS', None) 112 extra_flags = extra_flags.split(' ') if extra_flags else [] 113 114 wrench_headless_target = os.getenv('WRENCH_HEADLESS_TARGET', None) 115 116 if wrench_headless_target: 117 target_folder = wrench_headless_target 118 else: 119 target_folder = '../target/' 120 121 if optimized_build(): 122 target_folder += 'release/' 123 else: 124 target_folder += 'debug/' 125 126 # For CI purposes, don't build if WRENCH_HEADLESS_TARGET is set. 127 # This environment variable is used to point to the location of a cross-compiled 128 # wrench for the CI on some platforms. 129 if not wrench_headless_target: 130 build_cmd = ['cargo', 'build'] + extra_flags + ['--verbose', '--features', 'headless'] 131 if optimized_build(): 132 build_cmd += ['--release'] 133 subprocess.check_call(build_cmd) 134 135 dbg_cmd = [] 136 if use_rr(): 137 dbg_cmd = ['rr', 'record'] 138 elif use_gdb(): 139 dbg_cmd = [debugger(), '--args'] 140 elif debugger(): 141 print("Unknown debugger: " + debugger()) 142 sys.exit(1) 143 144 set_osmesa_env(target_folder) 145 # TODO(gw): We have an occasional accuracy issue or bug (could be WR or OSMesa) 146 # where the output of a previous test that uses intermediate targets can 147 # cause 1.0 / 255.0 pixel differences in a subsequent test. For now, we 148 # run tests with no-scissor mode, which ensures a complete target clear 149 # between test runs. But we should investigate this further... 150 cmd = dbg_cmd + [target_folder + 'wrench', '--no-scissor', '--headless'] + sys.argv[1:] 151 print('Running: `' + ' '.join(cmd) + '`') 152 subprocess.check_call(cmd, stderr=subprocess.STDOUT)