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/.
7 # Templates implementing some generic checks.
8 # ==============================================================
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.
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
24 return (FatalCheckError,)
27 (FatalCheckError,) = _declare_exceptions()
29 del _declare_exceptions
31 # Helper to display "checking" messages
32 # @checking('for foo')
37 # log.info('checking for foo... ')
41 # This can be combined with e.g. @depends:
42 # @depends(some_option)
43 # @checking('for something')
46 # An optional callback can be given, that will be used to format the returned
47 # value when displaying it.
51 def checking(what, callback=None):
53 def wrapped(*args, **kwargs):
54 log.info('checking %s... ', what)
55 with log.queue_debug():
56 error, ret = None, None
58 ret = func(*args, **kwargs)
59 except FatalCheckError as e:
61 display_ret = callback(ret) if callback else ret
62 if display_ret is True:
64 elif display_ret is False or display_ret is None:
75 # Template to check for programs in $PATH.
76 # - `var` is the name of the variable that will be set with `set_config` when
77 # the program is found.
78 # - `progs` is a list (or tuple) of program names that will be searched for.
79 # It can also be a reference to a @depends function that returns such a
80 # list. If the list is empty and there is no input, the check is skipped.
81 # - `what` is a human readable description of what is being looked for. It
82 # defaults to the lowercase version of `var`.
83 # - `input` is a string reference to an existing option or a reference to a
84 # @depends function resolving to explicit input for the program check.
85 # The default is to create an option for the environment variable `var`.
86 # This argument allows to use a different kind of option (possibly using a
87 # configure flag), or doing some pre-processing with a @depends function.
88 # - `allow_missing` indicates whether not finding the program is an error.
89 # - `paths` is a list of paths or @depends function returning a list of paths
90 # that will cause the given path(s) to be searched rather than $PATH. Input
91 # paths may either be individual paths or delimited by os.pathsep, to allow
92 # passing $PATH (for example) as an element.
93 # - `paths_have_priority` means that any programs found early in the PATH
94 # will be prioritized over programs found later in the PATH. The default is
95 # False, meaning that any of the programs earlier in the program list will be
96 # given priority, no matter where in the PATH they are found.
98 # The simplest form is:
99 # check_prog('PROG', ('a', 'b'))
100 # This will look for 'a' or 'b' in $PATH, and set_config PROG to the one
101 # it can find. If PROG is already set from the environment or command line,
102 # use that value instead.
104 @imports(_from='mozbuild.shellutil', _import='quote')
105 def check_prog(var, progs, what=None, input=None, allow_missing=False,
106 paths=None, paths_have_priority=False, when=None):
107 if input is not None:
108 # Wrap input with type checking and normalization.
109 @depends(input, when=when)
113 if isinstance(value, str):
115 if isinstance(value, (tuple, list)) and len(value) == 1:
117 configure_error('input must resolve to a tuple or a list with a '
118 'single element, or a string')
120 option(env=var, nargs=1, when=when,
121 help='Path to %s' % (what or 'the %s program' % var.lower()))
123 what = what or var.lower()
125 # Trick to make a @depends function out of an immediate value.
126 progs = dependable(progs)
127 paths = dependable(paths)
129 @depends_if(input, progs, paths, when=when)
130 @checking('for %s' % what, lambda x: quote(x) if x else 'not found')
131 def check(value, progs, paths):
135 if not isinstance(progs, (tuple, list)):
136 configure_error('progs must resolve to a list or tuple!')
138 if paths_have_priority:
140 for prog in value or progs:
141 log.debug('%s: Trying %s', var.lower(), quote(prog))
142 result = find_program(prog, [path])
146 for prog in value or progs:
147 log.debug('%s: Trying %s', var.lower(), quote(prog))
148 result = find_program(prog, paths)
152 if not allow_missing or value:
153 raise FatalCheckError('Cannot find %s' % what)
155 @depends_if(check, progs, when=when)
156 def normalized_for_config(value, progs):
157 return ':' if value is None else value
159 set_config(var, normalized_for_config)