示例#1
0
class TestChecksConfigure(unittest.TestCase):
    def test_checking(self):
        out = StringIO()
        sandbox = ConfigureSandbox({}, stdout=out, stderr=out)
        base_dir = os.path.join(topsrcdir, 'build', 'moz.configure')
        sandbox.include_file(os.path.join(base_dir, 'checks.configure'))

        exec_(
            textwrap.dedent('''
            @checking('for a thing')
            def foo(value):
                return value
        '''), sandbox)

        foo = sandbox['foo']

        foo(True)
        self.assertEqual(out.getvalue(), 'checking for a thing... yes\n')

        out.truncate(0)
        foo(False)
        self.assertEqual(out.getvalue(), 'checking for a thing... no\n')

        out.truncate(0)
        foo(42)
        self.assertEqual(out.getvalue(), 'checking for a thing... 42\n')

        out.truncate(0)
        foo('foo')
        self.assertEqual(out.getvalue(), 'checking for a thing... foo\n')

        out.truncate(0)
        data = ['foo', 'bar']
        foo(data)
        self.assertEqual(out.getvalue(), 'checking for a thing... %r\n' % data)

        # When the function given to checking does nothing interesting, the
        # behavior is not altered
        exec_(
            textwrap.dedent('''
            @checking('for a thing', lambda x: x)
            def foo(value):
                return value
        '''), sandbox)

        foo = sandbox['foo']

        out.truncate(0)
        foo(True)
        self.assertEqual(out.getvalue(), 'checking for a thing... yes\n')

        out.truncate(0)
        foo(False)
        self.assertEqual(out.getvalue(), 'checking for a thing... no\n')

        out.truncate(0)
        foo(42)
        self.assertEqual(out.getvalue(), 'checking for a thing... 42\n')

        out.truncate(0)
        foo('foo')
        self.assertEqual(out.getvalue(), 'checking for a thing... foo\n')

        out.truncate(0)
        data = ['foo', 'bar']
        foo(data)
        self.assertEqual(out.getvalue(), 'checking for a thing... %r\n' % data)

        exec_(
            textwrap.dedent('''
            def munge(x):
                if not x:
                    return 'not found'
                if isinstance(x, (str, bool, int)):
                    return x
                return ' '.join(x)

            @checking('for a thing', munge)
            def foo(value):
                return value
        '''), sandbox)

        foo = sandbox['foo']

        out.truncate(0)
        foo(True)
        self.assertEqual(out.getvalue(), 'checking for a thing... yes\n')

        out.truncate(0)
        foo(False)
        self.assertEqual(out.getvalue(), 'checking for a thing... not found\n')

        out.truncate(0)
        foo(42)
        self.assertEqual(out.getvalue(), 'checking for a thing... 42\n')

        out.truncate(0)
        foo('foo')
        self.assertEqual(out.getvalue(), 'checking for a thing... foo\n')

        out.truncate(0)
        foo(['foo', 'bar'])
        self.assertEqual(out.getvalue(), 'checking for a thing... foo bar\n')

    KNOWN_A = ensure_exe_extension(mozpath.abspath('/usr/bin/known-a'))
    KNOWN_B = ensure_exe_extension(mozpath.abspath('/usr/local/bin/known-b'))
    KNOWN_C = ensure_exe_extension(mozpath.abspath('/home/user/bin/known c'))
    OTHER_A = ensure_exe_extension(mozpath.abspath('/lib/other/known-a'))

    def get_result(self,
                   command='',
                   args=[],
                   environ={},
                   prog='/bin/configure',
                   extra_paths=None,
                   includes=('util.configure', 'checks.configure')):
        config = {}
        out = StringIO()
        paths = {
            self.KNOWN_A: None,
            self.KNOWN_B: None,
            self.KNOWN_C: None,
        }
        if extra_paths:
            paths.update(extra_paths)
        environ = dict(environ)
        if 'PATH' not in environ:
            environ['PATH'] = os.pathsep.join(
                os.path.dirname(p) for p in paths)
        paths[self.OTHER_A] = None
        sandbox = ConfigureTestSandbox(paths, config, environ, [prog] + args,
                                       out, out)
        base_dir = os.path.join(topsrcdir, 'build', 'moz.configure')
        for f in includes:
            sandbox.include_file(os.path.join(base_dir, f))

        status = 0
        try:
            exec_(command, sandbox)
            sandbox.run()
        except SystemExit as e:
            status = e.code

        return config, out.getvalue(), status

    def test_check_prog(self):
        config, out, status = self.get_result(
            'check_prog("FOO", ("known-a",))')
        self.assertEqual(status, 0)
        self.assertEqual(config, {'FOO': self.KNOWN_A})
        self.assertEqual(out, 'checking for foo... %s\n' % self.KNOWN_A)

        config, out, status = self.get_result(
            'check_prog("FOO", ("unknown", "known-b", "known c"))')
        self.assertEqual(status, 0)
        self.assertEqual(config, {'FOO': self.KNOWN_B})
        self.assertEqual(out, 'checking for foo... %s\n' % self.KNOWN_B)

        config, out, status = self.get_result(
            'check_prog("FOO", ("unknown", "unknown-2", "known c"))')
        self.assertEqual(status, 0)
        self.assertEqual(config, {'FOO': fake_short_path(self.KNOWN_C)})
        self.assertEqual(
            out, "checking for foo... '%s'\n" % fake_short_path(self.KNOWN_C))

        config, out, status = self.get_result(
            'check_prog("FOO", ("unknown",))')
        self.assertEqual(status, 1)
        self.assertEqual(config, {})
        self.assertEqual(
            out,
            textwrap.dedent('''\
            checking for foo... not found
            DEBUG: foo: Trying unknown
            ERROR: Cannot find foo
        '''))

        config, out, status = self.get_result(
            'check_prog("FOO", ("unknown", "unknown-2", "unknown 3"))')
        self.assertEqual(status, 1)
        self.assertEqual(config, {})
        self.assertEqual(
            out,
            textwrap.dedent('''\
            checking for foo... not found
            DEBUG: foo: Trying unknown
            DEBUG: foo: Trying unknown-2
            DEBUG: foo: Trying 'unknown 3'
            ERROR: Cannot find foo
        '''))

        config, out, status = self.get_result(
            'check_prog("FOO", ("unknown", "unknown-2", "unknown 3"), '
            'allow_missing=True)')
        self.assertEqual(status, 0)
        self.assertEqual(config, {'FOO': ':'})
        self.assertEqual(out, 'checking for foo... not found\n')

    @unittest.skipIf(not sys.platform.startswith('win'), 'Windows-only test')
    def test_check_prog_exe(self):
        config, out, status = self.get_result(
            'check_prog("FOO", ("unknown", "known-b", "known c"))',
            ['FOO=known-a.exe'])
        self.assertEqual(status, 0)
        self.assertEqual(config, {'FOO': self.KNOWN_A})
        self.assertEqual(out, 'checking for foo... %s\n' % self.KNOWN_A)

        config, out, status = self.get_result(
            'check_prog("FOO", ("unknown", "known-b", "known c"))',
            ['FOO=%s' % os.path.splitext(self.KNOWN_A)[0]])
        self.assertEqual(status, 0)
        self.assertEqual(config, {'FOO': self.KNOWN_A})
        self.assertEqual(out, 'checking for foo... %s\n' % self.KNOWN_A)

    def test_check_prog_with_args(self):
        config, out, status = self.get_result(
            'check_prog("FOO", ("unknown", "known-b", "known c"))',
            ['FOO=known-a'])
        self.assertEqual(status, 0)
        self.assertEqual(config, {'FOO': self.KNOWN_A})
        self.assertEqual(out, 'checking for foo... %s\n' % self.KNOWN_A)

        config, out, status = self.get_result(
            'check_prog("FOO", ("unknown", "known-b", "known c"))',
            ['FOO=%s' % self.KNOWN_A])
        self.assertEqual(status, 0)
        self.assertEqual(config, {'FOO': self.KNOWN_A})
        self.assertEqual(out, 'checking for foo... %s\n' % self.KNOWN_A)

        path = self.KNOWN_B.replace('known-b', 'known-a')
        config, out, status = self.get_result(
            'check_prog("FOO", ("unknown", "known-b", "known c"))',
            ['FOO=%s' % path])
        self.assertEqual(status, 1)
        self.assertEqual(config, {})
        self.assertEqual(
            out,
            textwrap.dedent('''\
            checking for foo... not found
            DEBUG: foo: Trying %s
            ERROR: Cannot find foo
        ''') % path)

        config, out, status = self.get_result(
            'check_prog("FOO", ("unknown",))', ['FOO=known c'])
        self.assertEqual(status, 0)
        self.assertEqual(config, {'FOO': fake_short_path(self.KNOWN_C)})
        self.assertEqual(
            out, "checking for foo... '%s'\n" % fake_short_path(self.KNOWN_C))

        config, out, status = self.get_result(
            'check_prog("FOO", ("unknown", "unknown-2", "unknown 3"), '
            'allow_missing=True)', ['FOO=unknown'])
        self.assertEqual(status, 1)
        self.assertEqual(config, {})
        self.assertEqual(
            out,
            textwrap.dedent('''\
            checking for foo... not found
            DEBUG: foo: Trying unknown
            ERROR: Cannot find foo
        '''))

    def test_check_prog_what(self):
        config, out, status = self.get_result(
            'check_prog("CC", ("known-a",), what="the target C compiler")')
        self.assertEqual(status, 0)
        self.assertEqual(config, {'CC': self.KNOWN_A})
        self.assertEqual(
            out, 'checking for the target C compiler... %s\n' % self.KNOWN_A)

        config, out, status = self.get_result(
            'check_prog("CC", ("unknown", "unknown-2", "unknown 3"),'
            '           what="the target C compiler")')
        self.assertEqual(status, 1)
        self.assertEqual(config, {})
        self.assertEqual(
            out,
            textwrap.dedent('''\
            checking for the target C compiler... not found
            DEBUG: cc: Trying unknown
            DEBUG: cc: Trying unknown-2
            DEBUG: cc: Trying 'unknown 3'
            ERROR: Cannot find the target C compiler
        '''))

    def test_check_prog_input(self):
        config, out, status = self.get_result(
            textwrap.dedent('''
            option("--with-ccache", nargs=1, help="ccache")
            check_prog("CCACHE", ("known-a",), input="--with-ccache")
        '''), ['--with-ccache=known-b'])
        self.assertEqual(status, 0)
        self.assertEqual(config, {'CCACHE': self.KNOWN_B})
        self.assertEqual(out, 'checking for ccache... %s\n' % self.KNOWN_B)

        script = textwrap.dedent('''
            option(env="CC", nargs=1, help="compiler")
            @depends("CC")
            def compiler(value):
                return value[0].split()[0] if value else None
            check_prog("CC", ("known-a",), input=compiler)
        ''')
        config, out, status = self.get_result(script)
        self.assertEqual(status, 0)
        self.assertEqual(config, {'CC': self.KNOWN_A})
        self.assertEqual(out, 'checking for cc... %s\n' % self.KNOWN_A)

        config, out, status = self.get_result(script, ['CC=known-b'])
        self.assertEqual(status, 0)
        self.assertEqual(config, {'CC': self.KNOWN_B})
        self.assertEqual(out, 'checking for cc... %s\n' % self.KNOWN_B)

        config, out, status = self.get_result(script, ['CC=known-b -m32'])
        self.assertEqual(status, 0)
        self.assertEqual(config, {'CC': self.KNOWN_B})
        self.assertEqual(out, 'checking for cc... %s\n' % self.KNOWN_B)

    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': self.KNOWN_A})
        self.assertEqual(out, 'checking for foo... %s\n' % self.KNOWN_A)

        script = textwrap.dedent('''
            option(env="TARGET", nargs=1, default="linux", help="target")
            @depends("TARGET")
            def compiler(value):
                if value:
                    if value[0] == "linux":
                        return ("gcc", "clang")
                    if value[0] == "winnt":
                        return ("cl", "clang-cl")
            check_prog("CC", compiler)
        ''')
        config, out, status = self.get_result(script)
        self.assertEqual(status, 1)
        self.assertEqual(config, {})
        self.assertEqual(
            out,
            textwrap.dedent('''\
            checking for cc... not found
            DEBUG: cc: Trying gcc
            DEBUG: cc: Trying clang
            ERROR: Cannot find cc
        '''))

        config, out, status = self.get_result(script, ['TARGET=linux'])
        self.assertEqual(status, 1)
        self.assertEqual(config, {})
        self.assertEqual(
            out,
            textwrap.dedent('''\
            checking for cc... not found
            DEBUG: cc: Trying gcc
            DEBUG: cc: Trying clang
            ERROR: Cannot find cc
        '''))

        config, out, status = self.get_result(script, ['TARGET=winnt'])
        self.assertEqual(status, 1)
        self.assertEqual(config, {})
        self.assertEqual(
            out,
            textwrap.dedent('''\
            checking for cc... not found
            DEBUG: cc: Trying cl
            DEBUG: cc: Trying clang-cl
            ERROR: Cannot find cc
        '''))

        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': self.KNOWN_A})
        self.assertEqual(out, 'checking for cc... %s\n' % self.KNOWN_A)

        config, out, status = self.get_result(script,
                                              ['TARGET=none', 'CC=known-a'])
        self.assertEqual(status, 0)
        self.assertEqual(config, {'CC': self.KNOWN_A})
        self.assertEqual(out, 'checking for cc... %s\n' % self.KNOWN_A)

    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 must resolve to a list or tuple!')

        with self.assertRaises(ConfigureError) as e:
            self.get_result('foo = depends(when=True)(lambda: ("a", "b"))\n'
                            'check_prog("FOO", ("known-a",), input=foo)')

        self.assertEqual(
            e.exception.message,
            'input must resolve to a tuple or a list with a '
            'single element, or a string')

        with self.assertRaises(ConfigureError) as e:
            self.get_result('foo = depends(when=True)(lambda: {"a": "b"})\n'
                            'check_prog("FOO", ("known-a",), input=foo)')

        self.assertEqual(
            e.exception.message,
            'input must resolve to a tuple or a list with a '
            'single element, or a string')

    def test_check_prog_with_path(self):
        config, out, status = self.get_result(
            'check_prog("A", ("known-a",), paths=["/some/path"])')
        self.assertEqual(status, 1)
        self.assertEqual(config, {})
        self.assertEqual(
            out,
            textwrap.dedent('''\
            checking for a... not found
            DEBUG: a: Trying known-a
            ERROR: Cannot find a
        '''))

        config, out, status = self.get_result(
            'check_prog("A", ("known-a",), paths=["%s"])' %
            os.path.dirname(self.OTHER_A))
        self.assertEqual(status, 0)
        self.assertEqual(config, {'A': self.OTHER_A})
        self.assertEqual(
            out,
            textwrap.dedent('''\
            checking for a... %s
        ''' % self.OTHER_A))

        dirs = map(mozpath.dirname, (self.OTHER_A, self.KNOWN_A))
        config, out, status = self.get_result(
            textwrap.dedent('''\
            check_prog("A", ("known-a",), paths=["%s"])
        ''' % os.pathsep.join(dirs)))
        self.assertEqual(status, 0)
        self.assertEqual(config, {'A': self.OTHER_A})
        self.assertEqual(
            out,
            textwrap.dedent('''\
            checking for a... %s
        ''' % self.OTHER_A))

        dirs = map(mozpath.dirname, (self.KNOWN_A, self.KNOWN_B))
        config, out, status = self.get_result(
            textwrap.dedent('''\
            check_prog("A", ("known-a",), paths=["%s", "%s"])
        ''' % (os.pathsep.join(dirs), self.OTHER_A)))
        self.assertEqual(status, 0)
        self.assertEqual(config, {'A': self.KNOWN_A})
        self.assertEqual(
            out,
            textwrap.dedent('''\
            checking for a... %s
        ''' % self.KNOWN_A))

        config, out, status = self.get_result(
            'check_prog("A", ("known-a",), paths="%s")' %
            os.path.dirname(self.OTHER_A))

        self.assertEqual(status, 1)
        self.assertEqual(config, {})
        self.assertEqual(
            out,
            textwrap.dedent('''\
            checking for a... 
            DEBUG: a: Trying known-a
            ERROR: Paths provided to find_program must be a list of strings, not %r
        ''' % mozpath.dirname(self.OTHER_A)))

    def test_java_tool_checks(self):
        includes = ('util.configure', 'checks.configure', 'java.configure')

        def mock_valid_javac(_, args):
            if len(args) == 1 and args[0] == '-version':
                return 0, '1.7', ''
            self.fail("Unexpected arguments to mock_valid_javac: %s" % args)

        # A valid set of tools in a standard location.
        java = mozpath.abspath('/usr/bin/java')
        javah = mozpath.abspath('/usr/bin/javah')
        javac = mozpath.abspath('/usr/bin/javac')
        jar = mozpath.abspath('/usr/bin/jar')
        jarsigner = mozpath.abspath('/usr/bin/jarsigner')
        keytool = mozpath.abspath('/usr/bin/keytool')

        paths = {
            java: None,
            javah: None,
            javac: mock_valid_javac,
            jar: None,
            jarsigner: None,
            keytool: None,
        }

        config, out, status = self.get_result(includes=includes,
                                              extra_paths=paths)
        self.assertEqual(status, 0)
        self.assertEqual(
            config, {
                'JAVA': java,
                'JAVAH': javah,
                'JAVAC': javac,
                'JAR': jar,
                'JARSIGNER': jarsigner,
                'KEYTOOL': keytool,
            })
        self.assertEqual(
            out,
            textwrap.dedent('''\
             checking for java... %s
             checking for javah... %s
             checking for jar... %s
             checking for jarsigner... %s
             checking for keytool... %s
             checking for javac... %s
             checking for javac version... 1.7
        ''' % (java, javah, jar, jarsigner, keytool, javac)))

        # An alternative valid set of tools referred to by JAVA_HOME.
        alt_java = mozpath.abspath('/usr/local/bin/java')
        alt_javah = mozpath.abspath('/usr/local/bin/javah')
        alt_javac = mozpath.abspath('/usr/local/bin/javac')
        alt_jar = mozpath.abspath('/usr/local/bin/jar')
        alt_jarsigner = mozpath.abspath('/usr/local/bin/jarsigner')
        alt_keytool = mozpath.abspath('/usr/local/bin/keytool')
        alt_java_home = mozpath.dirname(mozpath.dirname(alt_java))

        paths.update({
            alt_java: None,
            alt_javah: None,
            alt_javac: mock_valid_javac,
            alt_jar: None,
            alt_jarsigner: None,
            alt_keytool: None,
        })

        config, out, status = self.get_result(includes=includes,
                                              extra_paths=paths,
                                              environ={
                                                  'JAVA_HOME': alt_java_home,
                                                  'PATH': mozpath.dirname(java)
                                              })
        self.assertEqual(status, 0)
        self.assertEqual(
            config, {
                'JAVA': alt_java,
                'JAVAH': alt_javah,
                'JAVAC': alt_javac,
                'JAR': alt_jar,
                'JARSIGNER': alt_jarsigner,
                'KEYTOOL': alt_keytool,
            })
        self.assertEqual(
            out,
            textwrap.dedent('''\
             checking for java... %s
             checking for javah... %s
             checking for jar... %s
             checking for jarsigner... %s
             checking for keytool... %s
             checking for javac... %s
             checking for javac version... 1.7
        ''' % (alt_java, alt_javah, alt_jar, alt_jarsigner, alt_keytool,
               alt_javac)))

        # We can use --with-java-bin-path instead of JAVA_HOME to similar
        # effect.
        config, out, status = self.get_result(
            args=['--with-java-bin-path=%s' % mozpath.dirname(alt_java)],
            includes=includes,
            extra_paths=paths,
            environ={'PATH': mozpath.dirname(java)})
        self.assertEqual(status, 0)
        self.assertEqual(
            config, {
                'JAVA': alt_java,
                'JAVAH': alt_javah,
                'JAVAC': alt_javac,
                'JAR': alt_jar,
                'JARSIGNER': alt_jarsigner,
                'KEYTOOL': alt_keytool,
            })
        self.assertEqual(
            out,
            textwrap.dedent('''\
             checking for java... %s
             checking for javah... %s
             checking for jar... %s
             checking for jarsigner... %s
             checking for keytool... %s
             checking for javac... %s
             checking for javac version... 1.7
        ''' % (alt_java, alt_javah, alt_jar, alt_jarsigner, alt_keytool,
               alt_javac)))

        # If --with-java-bin-path and JAVA_HOME are both set,
        # --with-java-bin-path takes precedence.
        config, out, status = self.get_result(
            args=['--with-java-bin-path=%s' % mozpath.dirname(alt_java)],
            includes=includes,
            extra_paths=paths,
            environ={
                'PATH': mozpath.dirname(java),
                'JAVA_HOME': mozpath.dirname(mozpath.dirname(java)),
            })
        self.assertEqual(status, 0)
        self.assertEqual(
            config, {
                'JAVA': alt_java,
                'JAVAH': alt_javah,
                'JAVAC': alt_javac,
                'JAR': alt_jar,
                'JARSIGNER': alt_jarsigner,
                'KEYTOOL': alt_keytool,
            })
        self.assertEqual(
            out,
            textwrap.dedent('''\
             checking for java... %s
             checking for javah... %s
             checking for jar... %s
             checking for jarsigner... %s
             checking for keytool... %s
             checking for javac... %s
             checking for javac version... 1.7
        ''' % (alt_java, alt_javah, alt_jar, alt_jarsigner, alt_keytool,
               alt_javac)))

        def mock_old_javac(_, args):
            if len(args) == 1 and args[0] == '-version':
                return 0, '1.6.9', ''
            self.fail("Unexpected arguments to mock_old_javac: %s" % args)

        # An old javac is fatal.
        paths[javac] = mock_old_javac
        config, out, status = self.get_result(
            includes=includes,
            extra_paths=paths,
            environ={'PATH': mozpath.dirname(java)})
        self.assertEqual(status, 1)
        self.assertEqual(
            config, {
                'JAVA': java,
                'JAVAH': javah,
                'JAVAC': javac,
                'JAR': jar,
                'JARSIGNER': jarsigner,
                'KEYTOOL': keytool,
            })
        self.assertEqual(
            out,
            textwrap.dedent('''\
             checking for java... %s
             checking for javah... %s
             checking for jar... %s
             checking for jarsigner... %s
             checking for keytool... %s
             checking for javac... %s
             checking for javac version... 
             ERROR: javac 1.7 or higher is required (found 1.6.9)
        ''' % (java, javah, jar, jarsigner, keytool, javac)))

        # Any missing tool is fatal when these checks run.
        del paths[jarsigner]
        config, out, status = self.get_result(
            includes=includes,
            extra_paths=paths,
            environ={'PATH': mozpath.dirname(java)})
        self.assertEqual(status, 1)
        self.assertEqual(config, {
            'JAVA': java,
            'JAVAH': javah,
            'JAR': jar,
            'JARSIGNER': ':',
        })
        self.assertEqual(
            out,
            textwrap.dedent('''\
             checking for java... %s
             checking for javah... %s
             checking for jar... %s
             checking for jarsigner... not found
             ERROR: The program jarsigner was not found.  Set $JAVA_HOME to your Java SDK directory or use '--with-java-bin-path={java-bin-dir}'
        ''' % (java, javah, jar)))

    def test_pkg_check_modules(self):
        mock_pkg_config_version = '0.10.0'
        mock_pkg_config_path = mozpath.abspath('/usr/bin/pkg-config')

        def mock_pkg_config(_, args):
            if args[0:2] == ['--errors-to-stdout', '--print-errors']:
                assert len(args) == 3
                package = args[2]
                if package == 'unknown':
                    return (
                        1,
                        "Package unknown was not found in the pkg-config search path.\n"
                        "Perhaps you should add the directory containing `unknown.pc'\n"
                        "to the PKG_CONFIG_PATH environment variable\n"
                        "No package 'unknown' found", '')
                if package == 'valid':
                    return 0, '', ''
                if package == 'new > 1.1':
                    return 1, "Requested 'new > 1.1' but version of new is 1.1", ''
            if args[0] == '--cflags':
                assert len(args) == 2
                return 0, '-I/usr/include/%s' % args[1], ''
            if args[0] == '--libs':
                assert len(args) == 2
                return 0, '-l%s' % args[1], ''
            if args[0] == '--version':
                return 0, mock_pkg_config_version, ''
            self.fail("Unexpected arguments to mock_pkg_config: %s" % args)

        def get_result(cmd, args=[], extra_paths=None):
            return self.get_result(textwrap.dedent('''\
                option('--disable-compile-environment', help='compile env')
                include('%(topsrcdir)s/build/moz.configure/util.configure')
                include('%(topsrcdir)s/build/moz.configure/checks.configure')
                include('%(topsrcdir)s/build/moz.configure/pkg.configure')
            ''' % {'topsrcdir': topsrcdir}) + cmd,
                                   args=args,
                                   extra_paths=extra_paths,
                                   includes=())

        extra_paths = {
            mock_pkg_config_path: mock_pkg_config,
        }
        includes = ('util.configure', 'checks.configure', 'pkg.configure')

        config, output, status = get_result(
            "pkg_check_modules('MOZ_VALID', 'valid')")
        self.assertEqual(status, 1)
        self.assertEqual(
            output,
            textwrap.dedent('''\
            checking for pkg_config... not found
            ERROR: *** The pkg-config script could not be found. Make sure it is
            *** in your path, or set the PKG_CONFIG environment variable
            *** to the full path to pkg-config.
        '''))

        config, output, status = get_result(
            "pkg_check_modules('MOZ_VALID', 'valid')", extra_paths=extra_paths)
        self.assertEqual(status, 0)
        self.assertEqual(
            output,
            textwrap.dedent('''\
            checking for pkg_config... %s
            checking for pkg-config version... %s
            checking for valid... yes
            checking MOZ_VALID_CFLAGS... -I/usr/include/valid
            checking MOZ_VALID_LIBS... -lvalid
        ''' % (mock_pkg_config_path, mock_pkg_config_version)))
        self.assertEqual(
            config, {
                'PKG_CONFIG': mock_pkg_config_path,
                'MOZ_VALID_CFLAGS': ('-I/usr/include/valid', ),
                'MOZ_VALID_LIBS': ('-lvalid', ),
            })

        config, output, status = get_result(
            "pkg_check_modules('MOZ_UKNOWN', 'unknown')",
            extra_paths=extra_paths)
        self.assertEqual(status, 1)
        self.assertEqual(
            output,
            textwrap.dedent('''\
            checking for pkg_config... %s
            checking for pkg-config version... %s
            checking for unknown... no
            ERROR: Package unknown was not found in the pkg-config search path.
            ERROR: Perhaps you should add the directory containing `unknown.pc'
            ERROR: to the PKG_CONFIG_PATH environment variable
            ERROR: No package 'unknown' found
        ''' % (mock_pkg_config_path, mock_pkg_config_version)))
        self.assertEqual(config, {
            'PKG_CONFIG': mock_pkg_config_path,
        })

        config, output, status = get_result(
            "pkg_check_modules('MOZ_NEW', 'new > 1.1')",
            extra_paths=extra_paths)
        self.assertEqual(status, 1)
        self.assertEqual(
            output,
            textwrap.dedent('''\
            checking for pkg_config... %s
            checking for pkg-config version... %s
            checking for new > 1.1... no
            ERROR: Requested 'new > 1.1' but version of new is 1.1
        ''' % (mock_pkg_config_path, mock_pkg_config_version)))
        self.assertEqual(config, {
            'PKG_CONFIG': mock_pkg_config_path,
        })

        # allow_missing makes missing packages non-fatal.
        cmd = textwrap.dedent('''\
        have_new_module = pkg_check_modules('MOZ_NEW', 'new > 1.1', allow_missing=True)
        @depends(have_new_module)
        def log_new_module_error(mod):
            if mod is not True:
                log.info('Module not found.')
        ''')

        config, output, status = get_result(cmd, extra_paths=extra_paths)
        self.assertEqual(status, 0)
        self.assertEqual(
            output,
            textwrap.dedent('''\
            checking for pkg_config... %s
            checking for pkg-config version... %s
            checking for new > 1.1... no
            WARNING: Requested 'new > 1.1' but version of new is 1.1
            Module not found.
        ''' % (mock_pkg_config_path, mock_pkg_config_version)))
        self.assertEqual(config, {
            'PKG_CONFIG': mock_pkg_config_path,
        })

        config, output, status = get_result(
            cmd,
            args=['--disable-compile-environment'],
            extra_paths=extra_paths)
        self.assertEqual(status, 0)
        self.assertEqual(output, 'Module not found.\n')
        self.assertEqual(config, {})

        def mock_old_pkg_config(_, args):
            if args[0] == '--version':
                return 0, '0.8.10', ''
            self.fail("Unexpected arguments to mock_old_pkg_config: %s" % args)

        extra_paths = {
            mock_pkg_config_path: mock_old_pkg_config,
        }

        config, output, status = get_result(
            "pkg_check_modules('MOZ_VALID', 'valid')", extra_paths=extra_paths)
        self.assertEqual(status, 1)
        self.assertEqual(
            output,
            textwrap.dedent('''\
            checking for pkg_config... %s
            checking for pkg-config version... 0.8.10
            ERROR: *** Your version of pkg-config is too old. You need version 0.9.0 or newer.
        ''' % mock_pkg_config_path))

    def test_simple_keyfile(self):
        includes = ('util.configure', 'checks.configure', 'keyfiles.configure')

        config, output, status = self.get_result(
            "simple_keyfile('Mozilla API')", includes=includes)
        self.assertEqual(status, 0)
        self.assertEqual(
            output,
            textwrap.dedent('''\
            checking for the Mozilla API key... no
        '''))
        self.assertEqual(config, {
            'MOZ_MOZILLA_API_KEY': 'no-mozilla-api-key',
        })

        config, output, status = self.get_result(
            "simple_keyfile('Mozilla API')",
            args=['--with-mozilla-api-keyfile=/foo/bar/does/not/exist'],
            includes=includes)
        self.assertEqual(status, 1)
        self.assertEqual(
            output,
            textwrap.dedent('''\
            checking for the Mozilla API key... no
            ERROR: '/foo/bar/does/not/exist': No such file or directory.
        '''))
        self.assertEqual(config, {})

        with MockedOpen({'key': ''}):
            config, output, status = self.get_result(
                "simple_keyfile('Mozilla API')",
                args=['--with-mozilla-api-keyfile=key'],
                includes=includes)
            self.assertEqual(status, 1)
            self.assertEqual(
                output,
                textwrap.dedent('''\
                checking for the Mozilla API key... no
                ERROR: 'key' is empty.
            '''))
            self.assertEqual(config, {})

        with MockedOpen({'key': 'fake-key\n'}):
            config, output, status = self.get_result(
                "simple_keyfile('Mozilla API')",
                args=['--with-mozilla-api-keyfile=key'],
                includes=includes)
            self.assertEqual(status, 0)
            self.assertEqual(
                output,
                textwrap.dedent('''\
                checking for the Mozilla API key... yes
            '''))
            self.assertEqual(config, {
                'MOZ_MOZILLA_API_KEY': 'fake-key',
            })

    def test_id_and_secret_keyfile(self):
        includes = ('util.configure', 'checks.configure', 'keyfiles.configure')

        config, output, status = self.get_result(
            "id_and_secret_keyfile('Bing API')", includes=includes)
        self.assertEqual(status, 0)
        self.assertEqual(
            output,
            textwrap.dedent('''\
            checking for the Bing API key... no
        '''))
        self.assertEqual(
            config, {
                'MOZ_BING_API_CLIENTID': 'no-bing-api-clientid',
                'MOZ_BING_API_KEY': 'no-bing-api-key',
            })

        config, output, status = self.get_result(
            "id_and_secret_keyfile('Bing API')",
            args=['--with-bing-api-keyfile=/foo/bar/does/not/exist'],
            includes=includes)
        self.assertEqual(status, 1)
        self.assertEqual(
            output,
            textwrap.dedent('''\
            checking for the Bing API key... no
            ERROR: '/foo/bar/does/not/exist': No such file or directory.
        '''))
        self.assertEqual(config, {})

        with MockedOpen({'key': ''}):
            config, output, status = self.get_result(
                "id_and_secret_keyfile('Bing API')",
                args=['--with-bing-api-keyfile=key'],
                includes=includes)
            self.assertEqual(status, 1)
            self.assertEqual(
                output,
                textwrap.dedent('''\
                checking for the Bing API key... no
                ERROR: 'key' is empty.
            '''))
            self.assertEqual(config, {})

        with MockedOpen({'key': 'fake-id fake-key\n'}):
            config, output, status = self.get_result(
                "id_and_secret_keyfile('Bing API')",
                args=['--with-bing-api-keyfile=key'],
                includes=includes)
            self.assertEqual(status, 0)
            self.assertEqual(
                output,
                textwrap.dedent('''\
                checking for the Bing API key... yes
            '''))
            self.assertEqual(
                config, {
                    'MOZ_BING_API_CLIENTID': 'fake-id',
                    'MOZ_BING_API_KEY': 'fake-key',
                })

        with MockedOpen({'key': 'fake-key\n'}):
            config, output, status = self.get_result(
                "id_and_secret_keyfile('Bing API')",
                args=['--with-bing-api-keyfile=key'],
                includes=includes)
            self.assertEqual(status, 1)
            self.assertEqual(
                output,
                textwrap.dedent('''\
                checking for the Bing API key... no
                ERROR: Bing API key file has an invalid format.
            '''))
            self.assertEqual(config, {})
示例#2
0
class TestChecksConfigure(unittest.TestCase):
    def test_checking(self):
        def make_test(to_exec):
            def test(val, msg):
                out = StringIO()
                sandbox = ConfigureSandbox({}, stdout=out, stderr=out)
                base_dir = os.path.join(topsrcdir, "build", "moz.configure")
                sandbox.include_file(os.path.join(base_dir,
                                                  "checks.configure"))
                exec_(to_exec, sandbox)
                sandbox["foo"](val)
                self.assertEqual(out.getvalue(), msg)

            return test

        test = make_test(
            textwrap.dedent("""
            @checking('for a thing')
            def foo(value):
                return value
        """))
        test(True, "checking for a thing... yes\n")
        test(False, "checking for a thing... no\n")
        test(42, "checking for a thing... 42\n")
        test("foo", "checking for a thing... foo\n")
        data = ["foo", "bar"]
        test(data, "checking for a thing... %r\n" % data)

        # When the function given to checking does nothing interesting, the
        # behavior is not altered
        test = make_test(
            textwrap.dedent("""
            @checking('for a thing', lambda x: x)
            def foo(value):
                return value
        """))
        test(True, "checking for a thing... yes\n")
        test(False, "checking for a thing... no\n")
        test(42, "checking for a thing... 42\n")
        test("foo", "checking for a thing... foo\n")
        data = ["foo", "bar"]
        test(data, "checking for a thing... %r\n" % data)

        test = make_test(
            textwrap.dedent("""
            def munge(x):
                if not x:
                    return 'not found'
                if isinstance(x, (str, bool, int)):
                    return x
                return ' '.join(x)

            @checking('for a thing', munge)
            def foo(value):
                return value
        """))
        test(True, "checking for a thing... yes\n")
        test(False, "checking for a thing... not found\n")
        test(42, "checking for a thing... 42\n")
        test("foo", "checking for a thing... foo\n")
        data = ["foo", "bar"]
        test(data, "checking for a thing... foo bar\n")

    KNOWN_A = ensure_exe_extension(mozpath.abspath("/usr/bin/known-a"))
    KNOWN_B = ensure_exe_extension(mozpath.abspath("/usr/local/bin/known-b"))
    KNOWN_C = ensure_exe_extension(mozpath.abspath("/home/user/bin/known c"))
    OTHER_A = ensure_exe_extension(mozpath.abspath("/lib/other/known-a"))

    def get_result(
            self,
            command="",
            args=[],
            environ={},
            prog="/bin/configure",
            extra_paths=None,
            includes=("util.configure", "checks.configure"),
    ):
        config = {}
        out = StringIO()
        paths = {self.KNOWN_A: None, self.KNOWN_B: None, self.KNOWN_C: None}
        if extra_paths:
            paths.update(extra_paths)
        environ = dict(environ)
        if "PATH" not in environ:
            environ["PATH"] = os.pathsep.join(
                os.path.dirname(p) for p in paths)
        paths[self.OTHER_A] = None
        sandbox = ConfigureTestSandbox(paths, config, environ, [prog] + args,
                                       out, out)
        base_dir = os.path.join(topsrcdir, "build", "moz.configure")
        for f in includes:
            sandbox.include_file(os.path.join(base_dir, f))

        status = 0
        try:
            exec_(command, sandbox)
            sandbox.run()
        except SystemExit as e:
            status = e.code

        return config, out.getvalue(), status

    def test_check_prog(self):
        config, out, status = self.get_result(
            'check_prog("FOO", ("known-a",))')
        self.assertEqual(status, 0)
        self.assertEqual(config, {"FOO": self.KNOWN_A})
        self.assertEqual(out, "checking for foo... %s\n" % self.KNOWN_A)

        config, out, status = self.get_result(
            'check_prog("FOO", ("unknown", "known-b", "known c"))')
        self.assertEqual(status, 0)
        self.assertEqual(config, {"FOO": self.KNOWN_B})
        self.assertEqual(out, "checking for foo... %s\n" % self.KNOWN_B)

        config, out, status = self.get_result(
            'check_prog("FOO", ("unknown", "unknown-2", "known c"))')
        self.assertEqual(status, 0)
        self.assertEqual(config, {"FOO": fake_short_path(self.KNOWN_C)})
        self.assertEqual(
            out, "checking for foo... %s\n" %
            shell_quote(fake_short_path(self.KNOWN_C)))

        config, out, status = self.get_result(
            'check_prog("FOO", ("unknown",))')
        self.assertEqual(status, 1)
        self.assertEqual(config, {})
        self.assertEqual(
            out,
            textwrap.dedent("""\
            checking for foo... not found
            DEBUG: foo: Looking for unknown
            ERROR: Cannot find foo
        """),
        )

        config, out, status = self.get_result(
            'check_prog("FOO", ("unknown", "unknown-2", "unknown 3"))')
        self.assertEqual(status, 1)
        self.assertEqual(config, {})
        self.assertEqual(
            out,
            textwrap.dedent("""\
            checking for foo... not found
            DEBUG: foo: Looking for unknown
            DEBUG: foo: Looking for unknown-2
            DEBUG: foo: Looking for 'unknown 3'
            ERROR: Cannot find foo
        """),
        )

        config, out, status = self.get_result(
            'check_prog("FOO", ("unknown", "unknown-2", "unknown 3"), '
            "allow_missing=True)")
        self.assertEqual(status, 0)
        self.assertEqual(config, {})
        self.assertEqual(out, "checking for foo... not found\n")

    @unittest.skipIf(not sys.platform.startswith("win"), "Windows-only test")
    def test_check_prog_exe(self):
        config, out, status = self.get_result(
            'check_prog("FOO", ("unknown", "known-b", "known c"))',
            ["FOO=known-a.exe"])
        self.assertEqual(status, 0)
        self.assertEqual(config, {"FOO": self.KNOWN_A})
        self.assertEqual(out, "checking for foo... %s\n" % self.KNOWN_A)

        config, out, status = self.get_result(
            'check_prog("FOO", ("unknown", "known-b", "known c"))',
            ["FOO=%s" % os.path.splitext(self.KNOWN_A)[0]],
        )
        self.assertEqual(status, 0)
        self.assertEqual(config, {"FOO": self.KNOWN_A})
        self.assertEqual(out, "checking for foo... %s\n" % self.KNOWN_A)

    def test_check_prog_with_args(self):
        config, out, status = self.get_result(
            'check_prog("FOO", ("unknown", "known-b", "known c"))',
            ["FOO=known-a"])
        self.assertEqual(status, 0)
        self.assertEqual(config, {"FOO": self.KNOWN_A})
        self.assertEqual(out, "checking for foo... %s\n" % self.KNOWN_A)

        config, out, status = self.get_result(
            'check_prog("FOO", ("unknown", "known-b", "known c"))',
            ["FOO=%s" % self.KNOWN_A],
        )
        self.assertEqual(status, 0)
        self.assertEqual(config, {"FOO": self.KNOWN_A})
        self.assertEqual(out, "checking for foo... %s\n" % self.KNOWN_A)

        path = self.KNOWN_B.replace("known-b", "known-a")
        config, out, status = self.get_result(
            'check_prog("FOO", ("unknown", "known-b", "known c"))',
            ["FOO=%s" % path])
        self.assertEqual(status, 1)
        self.assertEqual(config, {})
        self.assertEqual(
            out,
            textwrap.dedent("""\
            checking for foo... not found
            DEBUG: foo: Looking for %s
            ERROR: Cannot find foo
        """) % path,
        )

        config, out, status = self.get_result(
            'check_prog("FOO", ("unknown",))', ["FOO=known c"])
        self.assertEqual(status, 0)
        self.assertEqual(config, {"FOO": fake_short_path(self.KNOWN_C)})
        self.assertEqual(
            out, "checking for foo... %s\n" %
            shell_quote(fake_short_path(self.KNOWN_C)))

        config, out, status = self.get_result(
            'check_prog("FOO", ("unknown", "unknown-2", "unknown 3"), '
            "allow_missing=True)",
            ["FOO=unknown"],
        )
        self.assertEqual(status, 1)
        self.assertEqual(config, {})
        self.assertEqual(
            out,
            textwrap.dedent("""\
            checking for foo... not found
            DEBUG: foo: Looking for unknown
            ERROR: Cannot find foo
        """),
        )

    def test_check_prog_what(self):
        config, out, status = self.get_result(
            'check_prog("CC", ("known-a",), what="the target C compiler")')
        self.assertEqual(status, 0)
        self.assertEqual(config, {"CC": self.KNOWN_A})
        self.assertEqual(
            out, "checking for the target C compiler... %s\n" % self.KNOWN_A)

        config, out, status = self.get_result(
            'check_prog("CC", ("unknown", "unknown-2", "unknown 3"),'
            '           what="the target C compiler")')
        self.assertEqual(status, 1)
        self.assertEqual(config, {})
        self.assertEqual(
            out,
            textwrap.dedent("""\
            checking for the target C compiler... not found
            DEBUG: cc: Looking for unknown
            DEBUG: cc: Looking for unknown-2
            DEBUG: cc: Looking for 'unknown 3'
            ERROR: Cannot find the target C compiler
        """),
        )

    def test_check_prog_input(self):
        config, out, status = self.get_result(
            textwrap.dedent("""
            option("--with-ccache", nargs=1, help="ccache")
            check_prog("CCACHE", ("known-a",), input="--with-ccache")
        """),
            ["--with-ccache=known-b"],
        )
        self.assertEqual(status, 0)
        self.assertEqual(config, {"CCACHE": self.KNOWN_B})
        self.assertEqual(out, "checking for ccache... %s\n" % self.KNOWN_B)

        script = textwrap.dedent("""
            option(env="CC", nargs=1, help="compiler")
            @depends("CC")
            def compiler(value):
                return value[0].split()[0] if value else None
            check_prog("CC", ("known-a",), input=compiler)
        """)
        config, out, status = self.get_result(script)
        self.assertEqual(status, 0)
        self.assertEqual(config, {"CC": self.KNOWN_A})
        self.assertEqual(out, "checking for cc... %s\n" % self.KNOWN_A)

        config, out, status = self.get_result(script, ["CC=known-b"])
        self.assertEqual(status, 0)
        self.assertEqual(config, {"CC": self.KNOWN_B})
        self.assertEqual(out, "checking for cc... %s\n" % self.KNOWN_B)

        config, out, status = self.get_result(script, ["CC=known-b -m32"])
        self.assertEqual(status, 0)
        self.assertEqual(config, {"CC": self.KNOWN_B})
        self.assertEqual(out, "checking for cc... %s\n" % self.KNOWN_B)

    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": self.KNOWN_A})
        self.assertEqual(out, "checking for foo... %s\n" % self.KNOWN_A)

        script = textwrap.dedent("""
            option(env="TARGET", nargs=1, default="linux", help="target")
            @depends("TARGET")
            def compiler(value):
                if value:
                    if value[0] == "linux":
                        return ("gcc", "clang")
                    if value[0] == "winnt":
                        return ("cl", "clang-cl")
            check_prog("CC", compiler)
        """)
        config, out, status = self.get_result(script)
        self.assertEqual(status, 1)
        self.assertEqual(config, {})
        self.assertEqual(
            out,
            textwrap.dedent("""\
            checking for cc... not found
            DEBUG: cc: Looking for gcc
            DEBUG: cc: Looking for clang
            ERROR: Cannot find cc
        """),
        )

        config, out, status = self.get_result(script, ["TARGET=linux"])
        self.assertEqual(status, 1)
        self.assertEqual(config, {})
        self.assertEqual(
            out,
            textwrap.dedent("""\
            checking for cc... not found
            DEBUG: cc: Looking for gcc
            DEBUG: cc: Looking for clang
            ERROR: Cannot find cc
        """),
        )

        config, out, status = self.get_result(script, ["TARGET=winnt"])
        self.assertEqual(status, 1)
        self.assertEqual(config, {})
        self.assertEqual(
            out,
            textwrap.dedent("""\
            checking for cc... not found
            DEBUG: cc: Looking for cl
            DEBUG: cc: Looking for clang-cl
            ERROR: Cannot find cc
        """),
        )

        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": self.KNOWN_A})
        self.assertEqual(out, "checking for cc... %s\n" % self.KNOWN_A)

        config, out, status = self.get_result(script,
                                              ["TARGET=none", "CC=known-a"])
        self.assertEqual(status, 0)
        self.assertEqual(config, {"CC": self.KNOWN_A})
        self.assertEqual(out, "checking for cc... %s\n" % self.KNOWN_A)

    def test_check_prog_configure_error(self):
        with self.assertRaises(ConfigureError) as e:
            self.get_result('check_prog("FOO", "foo")')

        self.assertEqual(str(e.exception),
                         "progs must resolve to a list or tuple!")

        with self.assertRaises(ConfigureError) as e:
            self.get_result('foo = depends(when=True)(lambda: ("a", "b"))\n'
                            'check_prog("FOO", ("known-a",), input=foo)')

        self.assertEqual(
            str(e.exception),
            "input must resolve to a tuple or a list with a "
            "single element, or a string",
        )

        with self.assertRaises(ConfigureError) as e:
            self.get_result('foo = depends(when=True)(lambda: {"a": "b"})\n'
                            'check_prog("FOO", ("known-a",), input=foo)')

        self.assertEqual(
            str(e.exception),
            "input must resolve to a tuple or a list with a "
            "single element, or a string",
        )

    def test_check_prog_with_path(self):
        config, out, status = self.get_result(
            'check_prog("A", ("known-a",), paths=["/some/path"])')
        self.assertEqual(status, 1)
        self.assertEqual(config, {})
        self.assertEqual(
            out,
            textwrap.dedent("""\
            checking for a... not found
            DEBUG: a: Looking for known-a
            ERROR: Cannot find a
        """),
        )

        config, out, status = self.get_result(
            'check_prog("A", ("known-a",), paths=["%s"])' %
            os.path.dirname(self.OTHER_A))
        self.assertEqual(status, 0)
        self.assertEqual(config, {"A": self.OTHER_A})
        self.assertEqual(
            out,
            textwrap.dedent("""\
            checking for a... %s
        """ % self.OTHER_A),
        )

        dirs = map(mozpath.dirname, (self.OTHER_A, self.KNOWN_A))
        config, out, status = self.get_result(
            textwrap.dedent("""\
            check_prog("A", ("known-a",), paths=["%s"])
        """ % os.pathsep.join(dirs)))
        self.assertEqual(status, 0)
        self.assertEqual(config, {"A": self.OTHER_A})
        self.assertEqual(
            out,
            textwrap.dedent("""\
            checking for a... %s
        """ % self.OTHER_A),
        )

        dirs = map(mozpath.dirname, (self.KNOWN_A, self.KNOWN_B))
        config, out, status = self.get_result(
            textwrap.dedent("""\
            check_prog("A", ("known-a",), paths=["%s", "%s"])
        """ % (os.pathsep.join(dirs), self.OTHER_A)))
        self.assertEqual(status, 0)
        self.assertEqual(config, {"A": self.KNOWN_A})
        self.assertEqual(
            out,
            textwrap.dedent("""\
            checking for a... %s
        """ % self.KNOWN_A),
        )

        config, out, status = self.get_result(
            'check_prog("A", ("known-a",), paths="%s")' %
            os.path.dirname(self.OTHER_A))

        self.assertEqual(status, 1)
        self.assertEqual(config, {})
        self.assertEqual(
            out,
            textwrap.dedent("""\
            checking for a... """
                            # noqa  # trailing whitespace...
                            """
            DEBUG: a: Looking for known-a
            ERROR: Paths provided to find_program must be a list of strings, not %r
        """ % mozpath.dirname(self.OTHER_A)),
        )

    @unittest.skipIf(
        not sys.platform.startswith("linux"),
        "Linux-only test, assumes Java is located from a $PATH",
    )
    def test_java_tool_checks_linux(self):
        def run_configure_java(mock_fs_paths,
                               mock_java_home=None,
                               mock_path=None,
                               args=[]):
            script = textwrap.dedent("""\
                    @depends('--help')
                    def host(_):
                        return namespace(os='unknown')
                    include('%(topsrcdir)s/build/moz.configure/java.configure')
                """ % {"topsrcdir": topsrcdir})

            def mock_which(exe, path=None):
                for mock_fs_path in mock_fs_paths.keys():
                    (base, filename) = os.path.split(mock_fs_path)
                    if filename == exe:
                        if path and os.path.normpath(base) != os.path.normpath(
                                path):
                            continue
                        return mock_fs_path

            # Don't let system JAVA_HOME influence the test
            original_java_home = os.environ.pop("JAVA_HOME", None)
            configure_environ = {}

            if mock_java_home:
                os.environ["JAVA_HOME"] = mock_java_home
                configure_environ["JAVA_HOME"] = mock_java_home

            if mock_path:
                configure_environ["PATH"] = mock_path

            # * Don't attempt to invoke Java, just resolve each mock Java's version as "1.8"
            # * Even if the real file sysphabtem has a symlink at the mocked path, don't let
            #   realpath follow it, as it may influence the test.
            # * When finding a binary, check the mock paths rather than the real filesystem.
            # Note: Python doesn't allow the different "with" bits to be put in parenthesis,
            # because then it thinks it's an un-with-able tuple. Additionally, if this is cleanly
            # lined up with "\", black removes them and autoformats them to the block that is
            # below.
            with patch("mozboot.util._resolve_java_version",
                       return_value="1.8"), patch(
                           "os.path.realpath",
                           side_effect=lambda path: path), patch(
                               "mozboot.util.which", side_effect=mock_which):
                result = self.get_result(
                    args=args,
                    command=script,
                    extra_paths=paths,
                    environ=configure_environ,
                )

            if original_java_home:
                os.environ["JAVA_HOME"] = original_java_home
            return result

        java = mozpath.abspath("/usr/bin/java")
        javac = mozpath.abspath("/usr/bin/javac")
        paths = {java: None, javac: None}

        config, out, status = run_configure_java(paths)
        self.assertEqual(status, 0)
        self.assertEqual(config, {
            "JAVA": java,
            "MOZ_JAVA_CODE_COVERAGE": False
        })
        self.assertEqual(
            out,
            textwrap.dedent("""\
             checking for java... %s
        """ % java),
        )

        # An alternative valid set of tools referred to by JAVA_HOME.
        alt_java = mozpath.abspath("/usr/local/bin/java")
        alt_javac = mozpath.abspath("/usr/local/bin/javac")
        alt_java_home = mozpath.dirname(mozpath.dirname(alt_java))
        paths = {alt_java: None, alt_javac: None, java: None, javac: None}

        alt_path = mozpath.dirname(java)
        config, out, status = run_configure_java(paths, alt_java_home,
                                                 alt_path)
        self.assertEqual(status, 0)
        self.assertEqual(config, {
            "JAVA": alt_java,
            "MOZ_JAVA_CODE_COVERAGE": False
        })
        self.assertEqual(
            out,
            textwrap.dedent("""\
             checking for java... %s
        """ % alt_java),
        )

        # We can use --with-java-bin-path instead of JAVA_HOME to similar
        # effect.
        config, out, status = run_configure_java(
            paths,
            mock_path=mozpath.dirname(java),
            args=["--with-java-bin-path=%s" % mozpath.dirname(alt_java)],
        )
        self.assertEqual(status, 0)
        self.assertEqual(config, {
            "JAVA": alt_java,
            "MOZ_JAVA_CODE_COVERAGE": False
        })
        self.assertEqual(
            out,
            textwrap.dedent("""\
             checking for java... %s
        """ % alt_java),
        )

        # If --with-java-bin-path and JAVA_HOME are both set,
        # --with-java-bin-path takes precedence.
        config, out, status = run_configure_java(
            paths,
            mock_java_home=mozpath.dirname(mozpath.dirname(java)),
            mock_path=mozpath.dirname(java),
            args=["--with-java-bin-path=%s" % mozpath.dirname(alt_java)],
        )
        self.assertEqual(status, 0)
        self.assertEqual(config, {
            "JAVA": alt_java,
            "MOZ_JAVA_CODE_COVERAGE": False
        })
        self.assertEqual(
            out,
            textwrap.dedent("""\
             checking for java... %s
        """ % alt_java),
        )

        # --enable-java-coverage should set MOZ_JAVA_CODE_COVERAGE.
        alt_java_home = mozpath.dirname(mozpath.dirname(java))
        config, out, status = run_configure_java(
            paths,
            mock_java_home=alt_java_home,
            mock_path=mozpath.dirname(java),
            args=["--enable-java-coverage"],
        )
        self.assertEqual(status, 0)
        self.assertEqual(config, {
            "JAVA": java,
            "MOZ_JAVA_CODE_COVERAGE": True
        })

        # Any missing tool is fatal when these checks run.
        paths = {}
        config, out, status = run_configure_java(
            mock_fs_paths={},
            mock_path=mozpath.dirname(java),
            args=["--enable-java-coverage"],
        )
        self.assertEqual(status, 1)
        self.assertEqual(config, {})
        self.assertEqual(
            out,
            textwrap.dedent("""\
            ERROR: Could not find "java" on the $PATH. Please install the Java 1.8 JDK \
and/or set $JAVA_HOME.
            """),
        )

    def test_pkg_check_modules(self):
        mock_pkg_config_version = "0.10.0"
        mock_pkg_config_path = mozpath.abspath("/usr/bin/pkg-config")

        def mock_pkg_config(_, args):
            if args[0:2] == ("--errors-to-stdout", "--print-errors"):
                assert len(args) == 3
                package = args[2]
                if package == "unknown":
                    return (
                        1,
                        "Package unknown was not found in the pkg-config search path.\n"
                        "Perhaps you should add the directory containing `unknown.pc'\n"
                        "to the PKG_CONFIG_PATH environment variable\n"
                        "No package 'unknown' found",
                        "",
                    )
                if package == "valid":
                    return 0, "", ""
                if package == "new > 1.1":
                    return 1, "Requested 'new > 1.1' but version of new is 1.1", ""
            if args[0] == "--cflags":
                assert len(args) == 2
                return 0, "-I/usr/include/%s" % args[1], ""
            if args[0] == "--libs":
                assert len(args) == 2
                return 0, "-l%s" % args[1], ""
            if args[0] == "--version":
                return 0, mock_pkg_config_version, ""
            self.fail("Unexpected arguments to mock_pkg_config: %s" % (args, ))

        def get_result(cmd, args=[], extra_paths=None):
            return self.get_result(
                textwrap.dedent("""\
                option('--disable-compile-environment', help='compile env')
                compile_environment = depends(when='--enable-compile-environment')(lambda: True)
                toolchain_prefix = depends(when=True)(lambda: None)
                multiarch_dir = depends(when=True)(lambda: None)
                sysroot_path = depends(when=True)(lambda: None)
                include('%(topsrcdir)s/build/moz.configure/util.configure')
                include('%(topsrcdir)s/build/moz.configure/checks.configure')
                include('%(topsrcdir)s/build/moz.configure/pkg.configure')
            """ % {"topsrcdir": topsrcdir}) + cmd,
                args=args,
                extra_paths=extra_paths,
                includes=(),
            )

        extra_paths = {mock_pkg_config_path: mock_pkg_config}

        config, output, status = get_result(
            "pkg_check_modules('MOZ_VALID', 'valid')")
        self.assertEqual(status, 1)
        self.assertEqual(
            output,
            textwrap.dedent("""\
            checking for pkg_config... not found
            ERROR: *** The pkg-config script could not be found. Make sure it is
            *** in your path, or set the PKG_CONFIG environment variable
            *** to the full path to pkg-config.
        """),
        )

        config, output, status = get_result(
            "pkg_check_modules('MOZ_VALID', 'valid')", extra_paths=extra_paths)
        self.assertEqual(status, 0)
        self.assertEqual(
            output,
            textwrap.dedent("""\
            checking for pkg_config... %s
            checking for pkg-config version... %s
            checking for valid... yes
            checking MOZ_VALID_CFLAGS... -I/usr/include/valid
            checking MOZ_VALID_LIBS... -lvalid
        """ % (mock_pkg_config_path, mock_pkg_config_version)),
        )
        self.assertEqual(
            config,
            {
                "PKG_CONFIG": mock_pkg_config_path,
                "MOZ_VALID_CFLAGS": ("-I/usr/include/valid", ),
                "MOZ_VALID_LIBS": ("-lvalid", ),
            },
        )

        config, output, status = get_result(
            "pkg_check_modules('MOZ_UKNOWN', 'unknown')",
            extra_paths=extra_paths)
        self.assertEqual(status, 1)
        self.assertEqual(
            output,
            textwrap.dedent("""\
            checking for pkg_config... %s
            checking for pkg-config version... %s
            checking for unknown... no
            ERROR: Package unknown was not found in the pkg-config search path.
            ERROR: Perhaps you should add the directory containing `unknown.pc'
            ERROR: to the PKG_CONFIG_PATH environment variable
            ERROR: No package 'unknown' found
        """ % (mock_pkg_config_path, mock_pkg_config_version)),
        )
        self.assertEqual(config, {"PKG_CONFIG": mock_pkg_config_path})

        config, output, status = get_result(
            "pkg_check_modules('MOZ_NEW', 'new > 1.1')",
            extra_paths=extra_paths)
        self.assertEqual(status, 1)
        self.assertEqual(
            output,
            textwrap.dedent("""\
            checking for pkg_config... %s
            checking for pkg-config version... %s
            checking for new > 1.1... no
            ERROR: Requested 'new > 1.1' but version of new is 1.1
        """ % (mock_pkg_config_path, mock_pkg_config_version)),
        )
        self.assertEqual(config, {"PKG_CONFIG": mock_pkg_config_path})

        # allow_missing makes missing packages non-fatal.
        cmd = textwrap.dedent("""\
        have_new_module = pkg_check_modules('MOZ_NEW', 'new > 1.1', allow_missing=True)
        @depends(have_new_module)
        def log_new_module_error(mod):
            if mod is not True:
                log.info('Module not found.')
        """)

        config, output, status = get_result(cmd, extra_paths=extra_paths)
        self.assertEqual(status, 0)
        self.assertEqual(
            output,
            textwrap.dedent("""\
            checking for pkg_config... %s
            checking for pkg-config version... %s
            checking for new > 1.1... no
            WARNING: Requested 'new > 1.1' but version of new is 1.1
            Module not found.
        """ % (mock_pkg_config_path, mock_pkg_config_version)),
        )
        self.assertEqual(config, {"PKG_CONFIG": mock_pkg_config_path})

        config, output, status = get_result(
            cmd,
            args=["--disable-compile-environment"],
            extra_paths=extra_paths)
        self.assertEqual(status, 0)
        self.assertEqual(output, "Module not found.\n")
        self.assertEqual(config, {})

        def mock_old_pkg_config(_, args):
            if args[0] == "--version":
                return 0, "0.8.10", ""
            self.fail("Unexpected arguments to mock_old_pkg_config: %s" % args)

        extra_paths = {mock_pkg_config_path: mock_old_pkg_config}

        config, output, status = get_result(
            "pkg_check_modules('MOZ_VALID', 'valid')", extra_paths=extra_paths)
        self.assertEqual(status, 1)
        self.assertEqual(
            output,
            textwrap.dedent("""\
            checking for pkg_config... %s
            checking for pkg-config version... 0.8.10
            ERROR: *** Your version of pkg-config is too old. You need version 0.9.0 or newer.
        """ % mock_pkg_config_path),
        )

    def test_simple_keyfile(self):
        includes = ("util.configure", "checks.configure", "keyfiles.configure")

        config, output, status = self.get_result(
            "simple_keyfile('Mozilla API')", includes=includes)
        self.assertEqual(status, 0)
        self.assertEqual(
            output,
            textwrap.dedent("""\
            checking for the Mozilla API key... no
        """),
        )
        self.assertEqual(config, {"MOZ_MOZILLA_API_KEY": "no-mozilla-api-key"})

        config, output, status = self.get_result(
            "simple_keyfile('Mozilla API')",
            args=["--with-mozilla-api-keyfile=/foo/bar/does/not/exist"],
            includes=includes,
        )
        self.assertEqual(status, 1)
        self.assertEqual(
            output,
            textwrap.dedent("""\
            checking for the Mozilla API key... no
            ERROR: '/foo/bar/does/not/exist': No such file or directory.
        """),
        )
        self.assertEqual(config, {})

        with MockedOpen({"key": ""}):
            config, output, status = self.get_result(
                "simple_keyfile('Mozilla API')",
                args=["--with-mozilla-api-keyfile=key"],
                includes=includes,
            )
            self.assertEqual(status, 1)
            self.assertEqual(
                output,
                textwrap.dedent("""\
                checking for the Mozilla API key... no
                ERROR: 'key' is empty.
            """),
            )
            self.assertEqual(config, {})

        with MockedOpen({"key": "fake-key\n"}):
            config, output, status = self.get_result(
                "simple_keyfile('Mozilla API')",
                args=["--with-mozilla-api-keyfile=key"],
                includes=includes,
            )
            self.assertEqual(status, 0)
            self.assertEqual(
                output,
                textwrap.dedent("""\
                checking for the Mozilla API key... yes
            """),
            )
            self.assertEqual(config, {"MOZ_MOZILLA_API_KEY": "fake-key"})

        with MockedOpen({"default": "default-key\n"}):
            config, output, status = self.get_result(
                "simple_keyfile('Mozilla API', default='default')",
                includes=includes)
            self.assertEqual(status, 0)
            self.assertEqual(
                output,
                textwrap.dedent("""\
                checking for the Mozilla API key... yes
            """),
            )
            self.assertEqual(config, {"MOZ_MOZILLA_API_KEY": "default-key"})

        with MockedOpen({"default": "default-key\n", "key": "fake-key\n"}):
            config, output, status = self.get_result(
                "simple_keyfile('Mozilla API', default='key')",
                includes=includes)
            self.assertEqual(status, 0)
            self.assertEqual(
                output,
                textwrap.dedent("""\
                checking for the Mozilla API key... yes
            """),
            )
            self.assertEqual(config, {"MOZ_MOZILLA_API_KEY": "fake-key"})

    def test_id_and_secret_keyfile(self):
        includes = ("util.configure", "checks.configure", "keyfiles.configure")

        config, output, status = self.get_result(
            "id_and_secret_keyfile('Bing API')", includes=includes)
        self.assertEqual(status, 0)
        self.assertEqual(
            output,
            textwrap.dedent("""\
            checking for the Bing API key... no
        """),
        )
        self.assertEqual(
            config,
            {
                "MOZ_BING_API_CLIENTID": "no-bing-api-clientid",
                "MOZ_BING_API_KEY": "no-bing-api-key",
            },
        )

        config, output, status = self.get_result(
            "id_and_secret_keyfile('Bing API')",
            args=["--with-bing-api-keyfile=/foo/bar/does/not/exist"],
            includes=includes,
        )
        self.assertEqual(status, 1)
        self.assertEqual(
            output,
            textwrap.dedent("""\
            checking for the Bing API key... no
            ERROR: '/foo/bar/does/not/exist': No such file or directory.
        """),
        )
        self.assertEqual(config, {})

        with MockedOpen({"key": ""}):
            config, output, status = self.get_result(
                "id_and_secret_keyfile('Bing API')",
                args=["--with-bing-api-keyfile=key"],
                includes=includes,
            )
            self.assertEqual(status, 1)
            self.assertEqual(
                output,
                textwrap.dedent("""\
                checking for the Bing API key... no
                ERROR: 'key' is empty.
            """),
            )
            self.assertEqual(config, {})

        with MockedOpen({"key": "fake-id fake-key\n"}):
            config, output, status = self.get_result(
                "id_and_secret_keyfile('Bing API')",
                args=["--with-bing-api-keyfile=key"],
                includes=includes,
            )
            self.assertEqual(status, 0)
            self.assertEqual(
                output,
                textwrap.dedent("""\
                checking for the Bing API key... yes
            """),
            )
            self.assertEqual(
                config,
                {
                    "MOZ_BING_API_CLIENTID": "fake-id",
                    "MOZ_BING_API_KEY": "fake-key"
                },
            )

        with MockedOpen({"key": "fake-key\n"}):
            config, output, status = self.get_result(
                "id_and_secret_keyfile('Bing API')",
                args=["--with-bing-api-keyfile=key"],
                includes=includes,
            )
            self.assertEqual(status, 1)
            self.assertEqual(
                output,
                textwrap.dedent("""\
                checking for the Bing API key... no
                ERROR: Bing API key file has an invalid format.
            """),
            )
            self.assertEqual(config, {})

        with MockedOpen({"default-key": "default-id default-key\n"}):
            config, output, status = self.get_result(
                "id_and_secret_keyfile('Bing API', default='default-key')",
                includes=includes,
            )
            self.assertEqual(status, 0)
            self.assertEqual(
                output,
                textwrap.dedent("""\
                checking for the Bing API key... yes
            """),
            )
            self.assertEqual(
                config,
                {
                    "MOZ_BING_API_CLIENTID": "default-id",
                    "MOZ_BING_API_KEY": "default-key",
                },
            )

        with MockedOpen({
                "default-key": "default-id default-key\n",
                "key": "fake-id fake-key\n"
        }):
            config, output, status = self.get_result(
                "id_and_secret_keyfile('Bing API', default='default-key')",
                args=["--with-bing-api-keyfile=key"],
                includes=includes,
            )
            self.assertEqual(status, 0)
            self.assertEqual(
                output,
                textwrap.dedent("""\
                checking for the Bing API key... yes
            """),
            )
            self.assertEqual(
                config,
                {
                    "MOZ_BING_API_CLIENTID": "fake-id",
                    "MOZ_BING_API_KEY": "fake-key"
                },
            )