gecko-dev/build/moz.configure/checks.configure

190 строки
6.8 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 = str(e)
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.
# - `paths_have_priority` means that any programs found early in the PATH
# will be prioritized over programs found later in the PATH. The default is
# False, meaning that any of the programs earlier in the program list will be
# given priority, no matter where in the PATH they are found.
#
# 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,
paths_have_priority=False,
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)
allow_missing = dependable(allow_missing)
# Avoid displaying the "Checking for" message when the inputs are such
# that we don't actually want anything to be checked. It is a bit
# convoluted because of how `when` works.
# We first wrap all the inputs except allow_missing (which doesn't count
# for whether to display the "Checking for" message).
@depends_if(input, progs, paths, when=when)
def inputs(input, progs, paths):
if progs is None:
progs = ()
if not isinstance(progs, (tuple, list)):
configure_error("progs must resolve to a list or tuple!")
return namespace(value=input, progs=progs, paths=paths)
@depends(inputs, allow_missing, when=inputs)
@checking("for %s" % what, lambda x: quote(x) if x else "not found")
def check(inputs, allow_missing):
value = inputs.value
progs = inputs.progs
paths = inputs.paths
if paths_have_priority:
for path in paths:
for prog in value or progs:
log.debug("%s: Trying %s", var.lower(), quote(prog))
result = find_program(prog, [path])
if result:
return result
else:
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)
set_config(var, check)
return check