tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

checks.configure (7732B)


      1 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
      2 # vim: set filetype=python:
      3 # This Source Code Form is subject to the terms of the Mozilla Public
      4 # License, v. 2.0. If a copy of the MPL was not distributed with this
      5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
      6 
      7 # Templates implementing some generic checks.
      8 # ==============================================================
      9 
     10 # Declare some exceptions. This is cumbersome, but since we shouldn't need a
     11 # lot of them, let's stack them all here. When adding a new one, put it in the
     12 # _declare_exceptions template, and add it to the return statement. Then
     13 # destructure in the assignment below the function declaration.
     14 
     15 
     16 @template
     17 @imports(_from="__builtin__", _import="Exception")
     18 def _declare_exceptions():
     19     class FatalCheckError(Exception):
     20         """An exception to throw from a function decorated with @checking.
     21         It will result in calling die() with the given message.
     22         Debugging messages emitted from the decorated function will also be
     23         printed out."""
     24 
     25     return (FatalCheckError,)
     26 
     27 
     28 (FatalCheckError,) = _declare_exceptions()
     29 
     30 del _declare_exceptions
     31 
     32 # Helper to display "checking" messages
     33 #   @checking('for foo')
     34 #   def foo():
     35 #       return 'foo'
     36 # is equivalent to:
     37 #   def foo():
     38 #       log.info('checking for foo... ')
     39 #       ret = foo
     40 #       log.info(ret)
     41 #       return ret
     42 # This can be combined with e.g. @depends:
     43 #   @depends(some_option)
     44 #   @checking('for something')
     45 #   def check(value):
     46 #       ...
     47 # An optional callback can be given, that will be used to format the returned
     48 # value when displaying it.
     49 
     50 
     51 @template
     52 def checking(what, callback=None):
     53     def decorator(func):
     54         def wrapped(*args, **kwargs):
     55             log.info("checking %s... ", what)
     56             with log.queue_debug():
     57                 error, ret = None, None
     58                 try:
     59                     ret = func(*args, **kwargs)
     60                 except FatalCheckError as e:
     61                     error = str(e)
     62                 display_ret = callback(ret) if callback else ret
     63                 if display_ret is True:
     64                     log.info("yes")
     65                 elif display_ret is False or display_ret is None:
     66                     log.info("no")
     67                 else:
     68                     log.info(display_ret)
     69                 if error is not None:
     70                     die(error)
     71             return ret
     72 
     73         return wrapped
     74 
     75     return decorator
     76 
     77 
     78 # Template to check for programs in $PATH.
     79 # - `var` is the name of the variable that will be set with `set_config` when
     80 #   the program is found.
     81 # - `progs` is a list (or tuple) of program names that will be searched for.
     82 #   It can also be a reference to a @depends function that returns such a
     83 #   list. If the list is empty and there is no input, the check is skipped.
     84 # - `what` is a human readable description of what is being looked for. It
     85 #   defaults to the lowercase version of `var`.
     86 # - `input` is a string reference to an existing option or a reference to a
     87 #   @depends function resolving to explicit input for the program check.
     88 #   The default is to create an option for the environment variable `var`.
     89 #   This argument allows to use a different kind of option (possibly using a
     90 #   configure flag), or doing some pre-processing with a @depends function.
     91 # - `allow_missing` indicates whether not finding the program is an error.
     92 # - `paths` is a list of paths or @depends function returning a list of paths
     93 #   that will cause the given path(s) to be searched rather than $PATH. Input
     94 #   paths may either be individual paths or delimited by os.pathsep, to allow
     95 #   passing $PATH (for example) as an element.
     96 # - `bootstrap` is a path relative to the bootstrap root path (e.g ~/.mozbuild)
     97 #   where to find the program if it's bootstrapped.
     98 # - `validate` is a callback function that takes a path and returns True if
     99 #   the program at that location is appropriate or not, or False if not.
    100 #   when the callback returns False, check_prog ignores the program and goes
    101 #   on to the next from the `progs` list.
    102 #
    103 # - `bootstrap_search_path` is not an argument that users of the template are
    104 #   supposed to pass. See the override of check_prog in top-level moz.configure.
    105 #
    106 # The simplest form is:
    107 #   check_prog('PROG', ('a', 'b'))
    108 # This will look for 'a' or 'b' in $PATH, and set_config PROG to the one
    109 # it can find. If PROG is already set from the environment or command line,
    110 # use that value instead.
    111 @template
    112 @imports(_from="mozshellutil", _import="quote")
    113 def check_prog(
    114     var,
    115     progs,
    116     what=None,
    117     input=None,
    118     allow_missing=False,
    119     paths=None,
    120     allow_spaces=False,
    121     bootstrap=None,
    122     when=None,
    123     validate=None,
    124     bootstrap_search_path=None,
    125 ):
    126     if input is not None:
    127         # Wrap input with type checking and normalization.
    128         @depends(input, when=when)
    129         def input(value):
    130             if not value:
    131                 return
    132             if isinstance(value, str):
    133                 return (value,)
    134             if isinstance(value, (tuple, list)) and len(value) == 1:
    135                 return value
    136             configure_error(
    137                 "input must resolve to a tuple or a list with a "
    138                 "single element, or a string"
    139             )
    140 
    141     else:
    142         option(
    143             env=var,
    144             nargs=1,
    145             when=when,
    146             help="Path to %s" % (what or "the %s program" % var.lower()),
    147         )
    148         input = var
    149     what = what or var.lower()
    150 
    151     # Trick to make a @depends function out of an immediate value.
    152     progs = dependable(progs)
    153     paths = dependable(paths)
    154     allow_missing = dependable(allow_missing)
    155 
    156     if bootstrap:
    157         if input is var:
    158             # A when is needed when depending on an option, so normalize
    159             # to a function that can used without.
    160             has_input = depends(input, when=when)(lambda x: x)
    161         else:
    162             has_input = input
    163         # We don't want to bootstrap when an explicit value was given as input.
    164         if when:
    165             bootstrap_when = depends(when, has_input)(lambda w, i: w and not i)
    166         else:
    167             bootstrap_when = depends(has_input)(lambda i: not i)
    168         paths = bootstrap_search_path(bootstrap, paths, when=bootstrap_when)
    169 
    170     # Avoid displaying the "Checking for" message when the inputs are such
    171     # that we don't actually want anything to be checked. It is a bit
    172     # convoluted because of how `when` works.
    173     # We first wrap all the inputs except allow_missing (which doesn't count
    174     # for whether to display the "Checking for" message).
    175     @depends_if(input, progs, paths, when=when)
    176     def inputs(input, progs, paths):
    177         if progs is None:
    178             progs = ()
    179 
    180         if not isinstance(progs, (tuple, list)):
    181             configure_error("progs must resolve to a list or tuple!")
    182 
    183         return namespace(value=input, progs=progs, paths=paths)
    184 
    185     @depends(inputs, allow_missing, when=inputs)
    186     @checking("for %s" % what, lambda x: quote(x) if x else "not found")
    187     def check(inputs, allow_missing):
    188         value = inputs.value
    189         progs = inputs.progs
    190         paths = inputs.paths
    191 
    192         for prog in value or progs:
    193             log.debug("%s: Looking for %s", var.lower(), quote(prog))
    194             result = find_program(prog, paths, allow_spaces)
    195             if validate and result and not validate(result):
    196                 log.debug("%s: %s found but didn't work", var.lower(), quote(result))
    197                 continue
    198             if result:
    199                 return result
    200 
    201         if not allow_missing or value:
    202             raise FatalCheckError("Cannot find %s" % what)
    203 
    204     set_config(var, check)
    205 
    206     return check