Bug 1259960 - Make check_prog more flexible about the list of programs it will check. r=chmanchester
authorMike Hommey <mh+mozilla@glandium.org>
Sat, 26 Mar 2016 10:11:29 +0900
changeset 328797 7c01ff2b685af859ce62233984a6b4b65cb418d9
parent 328796 58ea10aa8c8975d72b5a931d76077f74212d2746
child 328798 bd956b7718c96998ed80143b0f77a7f0a1f87281
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerschmanchester
bugs1259960
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1259960 - Make check_prog more flexible about the list of programs it will check. r=chmanchester
build/moz.configure/checks.configure
python/mozbuild/mozbuild/test/configure/test_checks_configure.py
--- a/build/moz.configure/checks.configure
+++ b/build/moz.configure/checks.configure
@@ -67,16 +67,18 @@ def checking(what, callback=None):
         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.
@@ -85,16 +87,17 @@ def checking(what, callback=None):
 #   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
 @advanced
 def check_prog(var, progs, what=None, input=None, allow_missing=False):
     from mozbuild.shellutil import quote
+    from mozbuild.configure import DependsFunction
 
     if input:
         # Wrap input with type checking and normalization.
         @depends(input)
         def input(value):
             if not value:
                 return
             if isinstance(value, str):
@@ -104,33 +107,37 @@ def check_prog(var, progs, what=None, in
             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()
 
-    if not isinstance(progs, (tuple, list)):
-        configure_error('progs should be a list or tuple!')
-    progs = list(progs)
+    if not isinstance(progs, DependsFunction):
+        # Trick to make a @depends function out of an immediate value.
+        progs = depends('--help')(lambda h: progs)
 
-    @depends(input)
+    @depends_if(input, progs)
     @checking('for %s' % what, lambda x: quote(x) if x else 'not found')
-    def check(value):
-        if value:
-            progs[:] = value
-        for prog in progs:
+    def check(value, progs):
+        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)
             if result:
                 return result
 
         if not allow_missing or value:
             raise FatalCheckError('Cannot find %s' % what)
 
-    @depends(check)
-    def normalized_for_config(value):
+    @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
--- a/python/mozbuild/mozbuild/test/configure/test_checks_configure.py
+++ b/python/mozbuild/mozbuild/test/configure/test_checks_configure.py
@@ -196,22 +196,87 @@ class TestChecksConfigure(unittest.TestC
         self.assertEqual(config, {'CC': '/usr/local/bin/known-b'})
         self.assertEqual(out, 'checking for cc... /usr/local/bin/known-b\n')
 
         config, out, status = self.get_result(script, ['CC=known-b -m32'])
         self.assertEqual(status, 0)
         self.assertEqual(config, {'CC': '/usr/local/bin/known-b'})
         self.assertEqual(out, 'checking for cc... /usr/local/bin/known-b\n')
 
+    def test_check_prog_progs(self):
+        config, out, status = self.get_result(
+            'check_prog("FOO", ())')
+        self.assertEqual(status, 0)
+        self.assertEqual(config, {})
+        self.assertEqual(out, '')
+
+        config, out, status = self.get_result(
+            'check_prog("FOO", ())', ['FOO=known-a'])
+        self.assertEqual(status, 0)
+        self.assertEqual(config, {'FOO': '/usr/bin/known-a'})
+        self.assertEqual(out, 'checking for foo... /usr/bin/known-a\n')
+
+        script = (
+            'option(env="TARGET", nargs=1, default="linux", help="target")\n'
+            '@depends("TARGET")\n'
+            'def compiler(value):\n'
+            '    if value:\n'
+            '        if value[0] == "linux":\n'
+            '            return ("gcc", "clang")\n'
+            '        if value[0] == "winnt":\n'
+            '            return ("cl", "clang-cl")\n'
+            'check_prog("CC", compiler)'
+        )
+        config, out, status = self.get_result(script)
+        self.assertEqual(status, 1)
+        self.assertEqual(config, {})
+        self.assertEqual(out, 'checking for cc... not found\n'
+                              'DEBUG: cc: Trying gcc\n'
+                              'DEBUG: cc: Trying clang\n'
+                              'ERROR: Cannot find cc\n')
+
+        config, out, status = self.get_result(script, ['TARGET=linux'])
+        self.assertEqual(status, 1)
+        self.assertEqual(config, {})
+        self.assertEqual(out, 'checking for cc... not found\n'
+                              'DEBUG: cc: Trying gcc\n'
+                              'DEBUG: cc: Trying clang\n'
+                              'ERROR: Cannot find cc\n')
+
+        config, out, status = self.get_result(script, ['TARGET=winnt'])
+        self.assertEqual(status, 1)
+        self.assertEqual(config, {})
+        self.assertEqual(out, 'checking for cc... not found\n'
+                              'DEBUG: cc: Trying cl\n'
+                              'DEBUG: cc: Trying clang-cl\n'
+                              'ERROR: Cannot find cc\n')
+
+        config, out, status = self.get_result(script, ['TARGET=none'])
+        self.assertEqual(status, 0)
+        self.assertEqual(config, {})
+        self.assertEqual(out, '')
+
+        config, out, status = self.get_result(script, ['TARGET=winnt',
+                                                       'CC=known-a'])
+        self.assertEqual(status, 0)
+        self.assertEqual(config, {'CC': '/usr/bin/known-a'})
+        self.assertEqual(out, 'checking for cc... /usr/bin/known-a\n')
+
+        config, out, status = self.get_result(script, ['TARGET=none',
+                                                       'CC=known-a'])
+        self.assertEqual(status, 0)
+        self.assertEqual(config, {'CC': '/usr/bin/known-a'})
+        self.assertEqual(out, 'checking for cc... /usr/bin/known-a\n')
+
     def test_check_prog_configure_error(self):
         with self.assertRaises(ConfigureError) as e:
             self.get_result('check_prog("FOO", "foo")')
 
         self.assertEqual(e.exception.message,
-                         'progs should be a list or tuple!')
+                         'progs must resolve to a list or tuple!')
 
         with self.assertRaises(ConfigureError) as e:
             self.get_result(
                 'foo = depends("--help")(lambda h: ("a", "b"))\n'
                 'check_prog("FOO", ("known-a",), input=foo)'
             )
 
         self.assertEqual(e.exception.message,