Bug 1892041 - Part 1: Update test262 features. r=spidermonkey-reviewers,dminor
[gecko.git] / build / moz.configure / checks.configure
blob73e8b0a886e258ff67229f755e11a2b504d5b73c
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.
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."""
25     return (FatalCheckError,)
28 (FatalCheckError,) = _declare_exceptions()
30 del _declare_exceptions
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.
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
73         return wrapped
75     return decorator
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.
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.
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="mozbuild.shellutil", _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,
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             )
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()
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)
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)
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 = ()
180         if not isinstance(progs, (tuple, list)):
181             configure_error("progs must resolve to a list or tuple!")
183         return namespace(value=input, progs=progs, paths=paths)
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
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
201         if not allow_missing or value:
202             raise FatalCheckError("Cannot find %s" % what)
204     set_config(var, check)
206     return check