2 # Copyright (c) 2014-2016, 2019 Intel Corporation
4 # Permission is hereby granted, free of charge, to any person obtaining a copy
5 # of this software and associated documentation files (the "Software"), to deal
6 # in the Software without restriction, including without limitation the rights
7 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 # copies of the Software, and to permit persons to whom the Software is
9 # furnished to do so, subject to the following conditions:
11 # The above copyright notice and this permission notice shall be included in
12 # all copies or substantial portions of the Software.
14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 """A collection of various bits that don't seem to belong anywhere else.
24 This is the classic catchall "utils" module from most projects, that for
25 historically reasons is called "core" in piglit.
36 from framework
import exceptions
42 'collect_system_info',
47 PLATFORMS
= ["glx", "x11_egl", "wayland", "gbm", "mixed_glx_egl", "wgl", "surfaceless_egl"]
50 class PiglitConfig(configparser
.ConfigParser
):
51 """Custom Config parser that provides a few extra helpers."""
52 def __init__(self
, *args
, **kwargs
):
53 super().__init
__(*args
, **kwargs
)
56 def read_file(self
, f
, source
=None):
57 super().read_file(f
, source
)
58 self
.filename
= os
.path
.abspath(source
or f
.name
)
60 def safe_get(self
, section
, option
, fallback
=None, **kwargs
):
61 """A version of self.get that doesn't raise NoSectionError or
64 This is equivalent to passing if the option isn't found. It will return
65 None if an error is caught
69 return self
.get(section
, option
, **kwargs
)
70 except (configparser
.NoOptionError
, configparser
.NoSectionError
):
73 def required_get(self
, section
, option
, **kwargs
):
74 """A version of self.get that raises PiglitFatalError.
76 If self.get returns NoSectionError or NoOptionError then this will
77 raise a PiglitFatalException, aborting the program.
81 return self
.get(section
, option
, **kwargs
)
82 except configparser
.NoSectionError
:
83 raise exceptions
.PiglitFatalError(
84 'No Section "{}" in file "{}".\n'
85 'This section is required.'.format(
86 section
, self
.filename
))
87 except configparser
.NoOptionError
:
88 raise exceptions
.PiglitFatalError(
89 'No option "{}" from section "{}" in file "{}".\n'
90 'This option is required.'.format(
91 option
, section
, self
.filename
))
94 PIGLIT_CONFIG
= PiglitConfig(allow_no_value
=True)
97 def get_config(arg
=None):
99 PIGLIT_CONFIG
.read_file(arg
)
101 # Load the piglit.conf. First try looking in the current directory,
102 # then trying the XDG_CONFIG_HOME, then $HOME/.config/, finally try the
105 os
.environ
.get('XDG_CONFIG_HOME',
106 os
.path
.expandvars('$HOME/.config')),
107 os
.path
.join(os
.path
.dirname(__file__
), '..')]:
109 with
open(os
.path
.join(d
, 'piglit.conf'), 'r') as f
:
110 PIGLIT_CONFIG
.read_file(f
)
116 def get_option(env_varname
, config_option
, default
=None, required
=False):
117 """Query the given environment variable and then piglit.conf for the option.
119 Return the value of the default argument if opt is None.
122 opt
= os
.environ
.get(env_varname
, None)
126 opt
= PIGLIT_CONFIG
.safe_get(config_option
[0], config_option
[1])
128 if required
and not opt
:
129 raise exceptions
.PiglitFatalError(
130 'Cannot get env "{}" or conf value "{}:{}"'.format(
131 env_varname
, config_option
[0], config_option
[1]))
132 return opt
or default
135 def check_dir(dirname
, failifexists
=False, handler
=None):
136 """Check for the existence of a directory and create it if possible.
138 This function will check for the existence of a directory. If that
139 directory doesn't exist it will try to create it. If the directory does
140 exist than it does one of two things.
141 1) If "failifexists" is False (default): it will just return
142 2) If "failifexists" is True it will raise an PiglitException, it is the
143 job of the caller using failifexists=True to handle this exception
145 Both failifexists and handler can be passed, but failifexists will have
149 dirname -- the name of the directory to check
152 failifexists -- If True and the directory exists then PiglitException will
153 be raised (default: False)
154 handler -- a callable that is passed dirname if the thing to check exists.
160 # If the error is not "no file or directory" or "not a dir", then
161 # either raise an exception, call the handler function, or return
162 if e
.errno
not in [errno
.ENOENT
, errno
.ENOTDIR
]:
164 raise exceptions
.PiglitException
165 elif handler
is not None:
169 # makedirs is expensive, so check before # calling it.
170 if not os
.path
.exists(dirname
):
173 if e
.errno
!= errno
.EEXIST
:
177 def collect_system_info():
178 """ Get relevant information about the system running piglit
180 This method runs through a list of tuples, where element 1 is the name of
181 the program being run, and element 2 is a command to run (in a form accepted
185 progs
= [('wglinfo', ['wglinfo']),
186 ('glxinfo', ['glxinfo']),
187 ('uname', ['uname', '-a']),
188 ('clinfo', ['clinfo']),
189 ('lspci', ['lspci', '-nn'])]
193 for name
, command
in progs
:
195 out
= subprocess
.check_output(command
, stderr
=subprocess
.STDOUT
)
196 result
[name
] = out
.decode('utf-8')
198 # If we get the 'no file or directory' error then pass, that means
199 # that the binary isn't installed or isn't relevant to the system.
200 # If it's any other OSError, then raise
201 if e
.errno
!= errno
.ENOENT
and e
.errno
!= errno
.EACCES
:
203 except subprocess
.CalledProcessError
:
204 # If the binary is installed by doesn't work on the window system
205 # (glxinfo) it will raise this error. go on
211 def parse_listfile(filename
):
213 Parses a newline-seperated list in a text file and returns a python list
214 object. It will expand tildes on Unix-like system to the users home
223 ['/home/user/tests1', '/home/users/tests2/main', '/tmp/test3']
225 with
open(filename
, 'r') as file:
226 return [os
.path
.expanduser(i
.strip()) for i
in file.readlines()]
229 class lazy_property(object): # pylint: disable=invalid-name,too-few-public-methods
230 """Descriptor that replaces the function it wraps with the value generated.
232 This makes a property that is truly lazy, it is calculated once on demand,
233 and stored. This makes this very useful for values that you might want to
234 calculate and reuse, but they cannot change.
236 This works by very cleverly shadowing itself with the calculated value. It
237 adds the value to the instance, which pushes itself up the MRO and will
238 never be queried again.
241 def __init__(self
, func
):
244 def __get__(self
, instance
, cls
):
245 value
= self
.__func
(instance
)
246 setattr(instance
, self
.__func
.__name
__, value
)
251 """A decorator function that measures the runtime of a given function in
252 milliseconds and prints the result.
253 The function name and runtime in ms will be printed in the format
254 "Finished [function name] in [runtime] ms".
257 @functools.wraps(func
)
258 def wrapper(*args
, **kwargs
):
259 start_time
: float = time
.monotonic()
260 value
= func(*args
, **kwargs
)
261 end_time
: float = time
.monotonic()
262 run_time
: float = end_time
- start_time
263 print(f
"Finished {func.__name__} in {run_time.__round__(6) * 1000} ms")