summaryrefslogtreecommitdiffstats
path: root/build/moz.configure/checks.configure
diff options
context:
space:
mode:
Diffstat (limited to 'build/moz.configure/checks.configure')
-rw-r--r--build/moz.configure/checks.configure206
1 files changed, 206 insertions, 0 deletions
diff --git a/build/moz.configure/checks.configure b/build/moz.configure/checks.configure
new file mode 100644
index 0000000000..73e8b0a886
--- /dev/null
+++ b/build/moz.configure/checks.configure
@@ -0,0 +1,206 @@
+# -*- 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.
+# - `bootstrap` is a path relative to the bootstrap root path (e.g ~/.mozbuild)
+# where to find the program if it's bootstrapped.
+# - `validate` is a callback function that takes a path and returns True if
+# the program at that location is appropriate or not, or False if not.
+# when the callback returns False, check_prog ignores the program and goes
+# on to the next from the `progs` list.
+#
+# - `bootstrap_search_path` is not an argument that users of the template are
+# supposed to pass. See the override of check_prog in top-level moz.configure.
+#
+# 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,
+ allow_spaces=False,
+ bootstrap=None,
+ when=None,
+ validate=None,
+ bootstrap_search_path=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)
+
+ if bootstrap:
+ if input is var:
+ # A when is needed when depending on an option, so normalize
+ # to a function that can used without.
+ has_input = depends(input, when=when)(lambda x: x)
+ else:
+ has_input = input
+ # We don't want to bootstrap when an explicit value was given as input.
+ if when:
+ bootstrap_when = depends(when, has_input)(lambda w, i: w and not i)
+ else:
+ bootstrap_when = depends(has_input)(lambda i: not i)
+ paths = bootstrap_search_path(bootstrap, paths, when=bootstrap_when)
+
+ # 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
+
+ for prog in value or progs:
+ log.debug("%s: Looking for %s", var.lower(), quote(prog))
+ result = find_program(prog, paths, allow_spaces)
+ if validate and result and not validate(result):
+ log.debug("%s: %s found but didn't work", var.lower(), quote(result))
+ continue
+ if result:
+ return result
+
+ if not allow_missing or value:
+ raise FatalCheckError("Cannot find %s" % what)
+
+ set_config(var, check)
+
+ return check