summaryrefslogtreecommitdiffstats
path: root/gfx/wr/wrench/script/headless.py
blob: 109fca1cb2eab6eba9095f9b17c220734707180b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#!/usr/bin/python

# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# Build and run wrench with off-screen software rendering (OSMesa/LLVMpipe)
# for platform-independent results. This is good for running reference tests.
#
# Usage: headless.py ARGS
#
# Pass ARGS through to wrench, after '--headless' and '--no-scissor'.
#
# Environment variables:
#
# WRENCH_HEADLESS_TARGET: If set, don't rebuild wrench. Instead, the value should
#     be the path to an already-built cargo 'target' directory. This is useful
#     for running a cross-compiled wrench.
#
# CARGOFLAGS: Extra flags to be passed to 'cargo build'. Split on whitespace.
#
# OPTIMIZED: This script uses the release build by default, but if this variable
#     is set to '0' or 'false', the script uses the debug build.
#
# DEBUGGER: If set, run wrench under a debugger. Permitted values are 'rr' (run
#     under 'rr record'), or 'gdb', 'rust-gdb', or 'cgdb' (run under the given
#     debugger, and arrange to supply ARGS to the wrench debuggee, not the
#     debugger)

from __future__ import print_function
import contextlib
import os
import subprocess
import sys
from os import path
from glob import glob


@contextlib.contextmanager
def cd(new_path):
    """Context manager for changing the current working directory"""
    previous_path = os.getcwd()
    try:
        os.chdir(new_path)
        yield
    finally:
        os.chdir(previous_path)


def find_dep_path_newest(package, bin_path):
    deps_path = path.join(path.split(bin_path)[0], "build")
    with cd(deps_path):
        candidates = glob(package + '-*')
    candidates = (path.join(deps_path, c) for c in candidates)
    """ For some reason cargo can create an extra osmesa-src without libs """
    candidates = (c for c in candidates if path.exists(path.join(c, 'out')))
    candidate_times = sorted(((path.getmtime(c), c) for c in candidates), reverse=True)
    if len(candidate_times) > 0:
        return candidate_times[0][1]
    return None


def is_windows():
    """ Detect windows, mingw, cygwin """
    return sys.platform == 'win32' or sys.platform == 'msys' or sys.platform == 'cygwin'


def is_macos():
    return sys.platform == 'darwin'


def is_linux():
    return sys.platform.startswith('linux')


def debugger():
    if "DEBUGGER" in os.environ:
        return os.environ["DEBUGGER"]
    return None


def use_gdb():
    return debugger() in ['gdb', 'cgdb', 'rust-gdb']


def use_rr():
    return debugger() == 'rr'


def optimized_build():
    if "OPTIMIZED" in os.environ:
        opt = os.environ["OPTIMIZED"]
        return opt not in ["0", "false"]
    return True


def set_osmesa_env(bin_path):
    """Set proper LD_LIBRARY_PATH and DRIVE for software rendering on Linux and OSX"""
    base = find_dep_path_newest('osmesa-src', bin_path)
    osmesa_path = path.join(base, "out", "mesa", "src", "gallium", "targets", "osmesa")
    os.environ["GALLIUM_DRIVER"] = "llvmpipe"
    if is_linux():
        print(osmesa_path)
        os.environ["LD_LIBRARY_PATH"] = osmesa_path
    elif is_macos():
        osmesa_path = path.join(base, "out", "mesa", "src", "gallium", "targets", "osmesa")
        glapi_path = path.join(base, "out", "mesa", "src", "mapi", "shared-glapi")
        os.environ["DYLD_LIBRARY_PATH"] = osmesa_path + ":" + glapi_path


extra_flags = os.getenv('CARGOFLAGS', None)
extra_flags = extra_flags.split(' ') if extra_flags else []

wrench_headless_target = os.getenv('WRENCH_HEADLESS_TARGET', None)

if wrench_headless_target:
    target_folder = wrench_headless_target
else:
    target_folder = '../target/'

if optimized_build():
    target_folder += 'release/'
else:
    target_folder += 'debug/'

# For CI purposes, don't build if WRENCH_HEADLESS_TARGET is set.
# This environment variable is used to point to the location of a cross-compiled
# wrench for the CI on some platforms.
if not wrench_headless_target:
    build_cmd = ['cargo', 'build'] + extra_flags + ['--verbose', '--features', 'headless']
    if optimized_build():
        build_cmd += ['--release']
    subprocess.check_call(build_cmd)

dbg_cmd = []
if use_rr():
    dbg_cmd = ['rr', 'record']
elif use_gdb():
    dbg_cmd = [debugger(), '--args']
elif debugger():
    print("Unknown debugger: " + debugger())
    sys.exit(1)

set_osmesa_env(target_folder)
# TODO(gw): We have an occasional accuracy issue or bug (could be WR or OSMesa)
#           where the output of a previous test that uses intermediate targets can
#           cause 1.0 / 255.0 pixel differences in a subsequent test. For now, we
#           run tests with no-scissor mode, which ensures a complete target clear
#           between test runs. But we should investigate this further...
cmd = dbg_cmd + [target_folder + 'wrench', '--no-scissor', '--headless'] + sys.argv[1:]
print('Running: `' + ' '.join(cmd) + '`')
subprocess.check_call(cmd, stderr=subprocess.STDOUT)