зеркало из https://github.com/mozilla/gecko-dev.git
150 строки
5.6 KiB
Python
150 строки
5.6 KiB
Python
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
|
# vim: set filetype=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/.
|
|
|
|
# Templates implementing some generic checks.
|
|
# ==============================================================
|
|
|
|
# Declare some exceptions. This is cumbersome, but since we shouldn't need a
|
|
# lot of them, let's stack them all here. When adding a new one, put it in the
|
|
# _declare_exceptions template, and add it to the return statement. Then
|
|
# destructure in the assignment below the function declaration.
|
|
|
|
|
|
@template
|
|
@imports(_from='__builtin__', _import='Exception')
|
|
def _declare_exceptions():
|
|
class FatalCheckError(Exception):
|
|
'''An exception to throw from a function decorated with @checking.
|
|
It will result in calling die() with the given message.
|
|
Debugging messages emitted from the decorated function will also be
|
|
printed out.'''
|
|
return (FatalCheckError,)
|
|
|
|
|
|
(FatalCheckError,) = _declare_exceptions()
|
|
|
|
del _declare_exceptions
|
|
|
|
# Helper to display "checking" messages
|
|
# @checking('for foo')
|
|
# def foo():
|
|
# return 'foo'
|
|
# is equivalent to:
|
|
# def foo():
|
|
# log.info('checking for foo... ')
|
|
# ret = foo
|
|
# log.info(ret)
|
|
# return ret
|
|
# This can be combined with e.g. @depends:
|
|
# @depends(some_option)
|
|
# @checking('for something')
|
|
# def check(value):
|
|
# ...
|
|
# An optional callback can be given, that will be used to format the returned
|
|
# value when displaying it.
|
|
|
|
|
|
@template
|
|
def checking(what, callback=None):
|
|
def decorator(func):
|
|
def wrapped(*args, **kwargs):
|
|
log.info('checking %s... ', what)
|
|
with log.queue_debug():
|
|
error, ret = None, None
|
|
try:
|
|
ret = func(*args, **kwargs)
|
|
except FatalCheckError as e:
|
|
error = e.message
|
|
display_ret = callback(ret) if callback else ret
|
|
if display_ret is True:
|
|
log.info('yes')
|
|
elif display_ret is False or display_ret is None:
|
|
log.info('no')
|
|
else:
|
|
log.info(display_ret)
|
|
if error is not None:
|
|
die(error)
|
|
return ret
|
|
return wrapped
|
|
return decorator
|
|
|
|
|
|
# Template to check for programs in $PATH.
|
|
# - `var` is the name of the variable that will be set with `set_config` when
|
|
# the program is found.
|
|
# - `progs` is a list (or tuple) of program names that will be searched for.
|
|
# It can also be a reference to a @depends function that returns such a
|
|
# list. If the list is empty and there is no input, the check is skipped.
|
|
# - `what` is a human readable description of what is being looked for. It
|
|
# defaults to the lowercase version of `var`.
|
|
# - `input` is a string reference to an existing option or a reference to a
|
|
# @depends function resolving to explicit input for the program check.
|
|
# The default is to create an option for the environment variable `var`.
|
|
# This argument allows to use a different kind of option (possibly using a
|
|
# configure flag), or doing some pre-processing with a @depends function.
|
|
# - `allow_missing` indicates whether not finding the program is an error.
|
|
# - `paths` is a list of paths or @depends function returning a list of paths
|
|
# that will cause the given path(s) to be searched rather than $PATH. Input
|
|
# paths may either be individual paths or delimited by os.pathsep, to allow
|
|
# passing $PATH (for example) as an element.
|
|
#
|
|
# The simplest form is:
|
|
# check_prog('PROG', ('a', 'b'))
|
|
# This will look for 'a' or 'b' in $PATH, and set_config PROG to the one
|
|
# it can find. If PROG is already set from the environment or command line,
|
|
# use that value instead.
|
|
@template
|
|
@imports(_from='mozbuild.shellutil', _import='quote')
|
|
def check_prog(var, progs, what=None, input=None, allow_missing=False,
|
|
paths=None, when=None):
|
|
if input is not None:
|
|
# Wrap input with type checking and normalization.
|
|
@depends(input, when=when)
|
|
def input(value):
|
|
if not value:
|
|
return
|
|
if isinstance(value, str):
|
|
return (value,)
|
|
if isinstance(value, (tuple, list)) and len(value) == 1:
|
|
return value
|
|
configure_error('input must resolve to a tuple or a list with a '
|
|
'single element, or a string')
|
|
else:
|
|
option(env=var, nargs=1, when=when,
|
|
help='Path to %s' % (what or 'the %s program' % var.lower()))
|
|
input = var
|
|
what = what or var.lower()
|
|
|
|
# Trick to make a @depends function out of an immediate value.
|
|
progs = dependable(progs)
|
|
paths = dependable(paths)
|
|
|
|
@depends_if(input, progs, paths, when=when)
|
|
@checking('for %s' % what, lambda x: quote(x) if x else 'not found')
|
|
def check(value, progs, paths):
|
|
if progs is None:
|
|
progs = ()
|
|
|
|
if not isinstance(progs, (tuple, list)):
|
|
configure_error('progs must resolve to a list or tuple!')
|
|
|
|
for prog in value or progs:
|
|
log.debug('%s: Trying %s', var.lower(), quote(prog))
|
|
result = find_program(prog, paths)
|
|
if result:
|
|
return result
|
|
|
|
if not allow_missing or value:
|
|
raise FatalCheckError('Cannot find %s' % what)
|
|
|
|
@depends_if(check, progs, when=when)
|
|
def normalized_for_config(value, progs):
|
|
return ':' if value is None else value
|
|
|
|
set_config(var, normalized_for_config)
|
|
|
|
return check
|