DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Mercurial (9f1f25699e99)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
# -*- 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.
# - `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)

    @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!')

        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)

    @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