123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144 |
- # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
- # 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):
- if input:
- # Wrap input with type checking and normalization.
- @depends(input)
- 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,
- 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)
- @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)
- def normalized_for_config(value, progs):
- return ':' if value is None else value
- set_config(var, normalized_for_config)
- return check
|