Bug 1267454 - Move java toolchain checks to Python configure. draft
authorChris Manchester <cmanchester@mozilla.com>
Thu, 05 May 2016 16:02:03 -0700
changeset 363983 2e658c05c8e4ff5a6bb9d0d2f8b0c869f221ca47
parent 363982 c666a9fbdf39f6a61ec3baed66cb5d337f6e8224
child 520182 74b4796ff5bb1ae0c55c1ed2d73c1edb9f24a72f
push id17355
push usercmanchester@mozilla.com
push dateThu, 05 May 2016 23:02:20 +0000
bugs1267454
milestone49.0a1
Bug 1267454 - Move java toolchain checks to Python configure. MozReview-Commit-ID: KEDkmJJsaUx
build/moz.configure/java.configure
build/moz.configure/old.configure
build/moz.configure/util.configure
mobile/android/moz.configure
old-configure.in
python/mozbuild/mozbuild/test/configure/test_checks_configure.py
new file mode 100644
--- /dev/null
+++ b/build/moz.configure/java.configure
@@ -0,0 +1,62 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+# Java detection
+# ========================================================
+option('--with-java-bin-path', nargs=1,
+       help='Location of Java binaries (java, javac, jar)')
+
+@depends('--with-java-bin-path')
+@imports(_from='os', _import='environ')
+def java_search_paths(path):
+    if path:
+        # Look for javac and jar in the specified path.
+        return path
+    # With no path specified, look for javac and jar in $JAVA_HOME (if set)
+    # and $PATH.
+    if 'JAVA_HOME' in environ:
+        return [os.path.join(environ['JAVA_HOME'], 'bin'),
+                environ.get('PATH', '')]
+    return [environ.get('PATH')]
+
+# Finds the given java tool, failing with a custom error message if we can't
+# find it.
+@template
+def check_java_tool(tool):
+    check = check_prog(tool.upper(), (tool,), paths=java_search_paths,
+                       allow_missing=True)
+
+    @depends(check)
+    def require_tool(result):
+        if result is None:
+            die("The program %s was not found.  Set $JAVA_HOME to your Java "
+                "SDK directory or use '--with-java-bin-path={java-bin-dir}'"
+                % tool)
+        return result
+
+    return require_tool
+
+check_java_tool('java')
+check_java_tool('javah')
+check_java_tool('jar')
+check_java_tool('jarsigner')
+check_java_tool('keytool')
+javac = check_java_tool('javac')
+
+@depends(javac)
+@checking('for javac version')
+@imports('subprocess')
+def javac_version(javac):
+    try:
+        output = subprocess.check_output([javac, '-version'],
+                                         stderr=subprocess.STDOUT).rstrip()
+        version = Version(output.split(' ')[-1])
+        if version < '1.7':
+            die('javac 1.7 or higher is required (found %s)' % version)
+        return version
+    except subprocess.CalledProcessError as e:
+        die('Failed to get javac version: %s', e.output)
--- a/build/moz.configure/old.configure
+++ b/build/moz.configure/old.configure
@@ -295,17 +295,16 @@ def old_configure_options(*options):
     '--with-doc-input-dirs',
     '--with-doc-output-dir',
     '--with-float-abi',
     '--with-fpu',
     '--with-google-api-keyfile',
     '--with-google-oauth-api-keyfile',
     '--with-intl-api',
     '--with-ios-sdk',
-    '--with-java-bin-path',
     '--with-jitreport-granularity',
     '--with-linux-headers',
     '--with-macbundlename-prefix',
     '--with-macos-private-frameworks',
     '--with-macos-sdk',
     '--with-mozilla-api-keyfile',
     '--with-nspr-cflags',
     '--with-nspr-exec-prefix',
--- a/build/moz.configure/util.configure
+++ b/build/moz.configure/util.configure
@@ -38,17 +38,17 @@ def normsep(path):
 @imports(_from='which', _import='WhichError')
 @imports('itertools')
 @imports(_from='os', _import='pathsep')
 def find_program(file, paths=None):
     if is_absolute_or_relative(file):
         return os.path.abspath(file) if os.path.isfile(file) else None
     try:
         if paths:
-            if not isinstance(paths, list):
+            if not isinstance(paths, (list, tuple)):
                 die("Paths provided to find_program must be a list of strings, "
                     "not %r" % paths)
             paths = list(itertools.chain(
                 *(p.split(pathsep) for p in paths if p)))
         return normsep(which(file, path=paths))
     except WhichError:
         return None
 
--- a/mobile/android/moz.configure
+++ b/mobile/android/moz.configure
@@ -55,9 +55,10 @@ def check_target(target):
     if target.os != 'Android':
         log.error('You must specify --target=arm-linux-androideabi (or some '
                   'other valid Android target) when building mobile/android.')
         die('See https://developer.mozilla.org/docs/Mozilla/Developer_guide/'
             'Build_Instructions/Simple_Firefox_for_Android_build '
             'for more information about the necessary options.')
 
 include('../../toolkit/moz.configure')
+include('../../build/moz.configure/java.configure')
 include('gradle.configure')
--- a/old-configure.in
+++ b/old-configure.in
@@ -2633,26 +2633,16 @@ fi
 AC_SUBST(MOZ_SYSTEM_HUNSPELL)
 
 dnl ========================================================
 dnl system libffi Support
 dnl ========================================================
 MOZ_CONFIG_FFI()
 
 dnl ========================================================
-dnl Java SDK support
-dnl ========================================================
-
-JAVA_BIN_PATH=
-MOZ_ARG_WITH_STRING(java-bin-path,
-[  --with-java-bin-path=dir
-                          Location of Java binaries (java, javac, jar)],
-    JAVA_BIN_PATH=$withval)
-
-dnl ========================================================
 dnl =
 dnl = Application
 dnl =
 dnl ========================================================
 
 MOZ_ARG_HEADER(Application)
 
 ENABLE_TESTS=1
@@ -4148,71 +4138,16 @@ dnl ====================================
 dnl = Universalchardet
 dnl ========================================================
 MOZ_ARG_DISABLE_BOOL(universalchardet,
 [  --disable-universalchardet
                           Disable universal encoding detection],
   MOZ_UNIVERSALCHARDET=,
   MOZ_UNIVERSALCHARDET=1 )
 
-if test -n "${JAVA_BIN_PATH}"; then
-  dnl Look for javac and jar in the specified path.
-  JAVA_PATH="$JAVA_BIN_PATH"
-else
-  dnl No path specified, so look for javac and jar in $JAVA_HOME & $PATH.
-  JAVA_PATH="$JAVA_HOME/bin:$PATH"
-fi
-
-MOZ_PATH_PROG(JAVA, java, :, [$JAVA_PATH])
-MOZ_PATH_PROG(JAVAC, javac, :, [$JAVA_PATH])
-MOZ_PATH_PROG(JAVAH, javah, :, [$JAVA_PATH])
-MOZ_PATH_PROG(JAR, jar, :, [$JAVA_PATH])
-MOZ_PATH_PROG(JARSIGNER, jarsigner, :, [$JAVA_PATH])
-MOZ_PATH_PROG(KEYTOOL, keytool, :, [$JAVA_PATH])
-
-if test -n "${JAVA_BIN_PATH}" -o \
-  \( "$OS_TARGET" = Android -a x"$MOZ_WIDGET_TOOLKIT" != x"gonk" \); then
-  if test -z "$JAVA" -o "$JAVA" = ":"; then
-    AC_MSG_ERROR([The program java was not found.  Set \$JAVA_HOME to your Java SDK directory or use --with-java-bin-path={java-bin-dir}])
-  fi
-  if test -z "$JAVAC" -o "$JAVAC" = ":"; then
-    AC_MSG_ERROR([The program javac was not found.  Set \$JAVA_HOME to your Java SDK directory or use --with-java-bin-path={java-bin-dir}])
-  fi
-  if test -z "$JAVAH" -o "$JAVAH" = ":"; then
-    AC_MSG_ERROR([The program javah was not found.  Set \$JAVA_HOME to your Java SDK directory or use --with-java-bin-path={java-bin-dir}])
-  fi
-  if test -z "$JAR" -o "$JAR" = ":"; then
-    AC_MSG_ERROR([The program jar was not found.  Set \$JAVA_HOME to your Java SDK directory or use --with-java-bin-path={java-bin-dir}])
-  fi
-  if test -z "$JARSIGNER" -o "$JARSIGNER" = ":"; then
-    AC_MSG_ERROR([The program jarsigner was not found.  Set \$JAVA_HOME to your Java SDK directory or use --with-java-bin-path={java-bin-dir}])
-  fi
-  if test -z "$KEYTOOL" -o "$KEYTOOL" = ":"; then
-    AC_MSG_ERROR([The program keytool was not found.  Set \$JAVA_HOME to your Java SDK directory or use --with-java-bin-path={java-bin-dir}])
-  fi
-
-  AC_MSG_CHECKING([for minimum required javac version >= 1.7])
-
-  dnl Javac spits out something like `javac 1.7.0`. This line cuts off the 'javac'
-  _javac_version=$($JAVAC -version 2>&1 | cut -d ' ' -f 2)
-
-  dnl Here, we extract the major (1) and minor (7) version numbers from the
-  dnl acquired version string.
-  _javac_major_version=$(echo $_javac_version | cut -d '.' -f 1)
-  _javac_minor_version=$(echo $_javac_version | cut -d '.' -f 2)
-
-  AC_MSG_RESULT([$_javac_version])
-
-  dnl Fail if we have a version other than 1.7.X
-  if test "$_javac_major_version" -ne "1" -o \
-       \( "$_javac_minor_version" -lt "7" \); then
-      AC_MSG_ERROR([javac 1.7 or higher is required.])
-  fi
-fi
-
 dnl ========================================================
 dnl = ANGLE OpenGL->D3D translator for WebGL
 dnl = * only applies to win32
 dnl ========================================================
 
 MOZ_ANGLE_RENDERER=
 MOZ_D3D_CPU_SUFFIX=
 MOZ_HAS_WINSDK_WITH_D3D=
@@ -6701,23 +6636,16 @@ AC_SUBST_SET(MOZ_EXTENSIONS)
 AC_SUBST(LIBICONV)
 AC_SUBST(MOZ_PLACES)
 AC_SUBST(MOZ_SOCIAL)
 AC_SUBST(MOZ_TOOLKIT_SEARCH)
 AC_SUBST(MOZ_FEEDS)
 AC_SUBST(NS_PRINTING)
 AC_SUBST(MOZ_HELP_VIEWER)
 
-AC_SUBST(JAVA)
-AC_SUBST(JAVAC)
-AC_SUBST(JAVAH)
-AC_SUBST(JAR)
-AC_SUBST(JARSIGNER)
-AC_SUBST(KEYTOOL)
-
 AC_SUBST(ENABLE_TESTS)
 AC_SUBST(MOZ_UNIVERSALCHARDET)
 AC_SUBST(ACCESSIBILITY)
 AC_SUBST(MOZ_SPELLCHECK)
 AC_SUBST(MOZ_ANDROID_ANR_REPORTER)
 AC_SUBST(MOZ_CRASHREPORTER)
 AC_SUBST(MOZ_CRASHREPORTER_INJECTOR)
 AC_SUBST(MOZ_CRASHREPORTER_UPLOAD_FULL_SYMBOLS)
--- a/python/mozbuild/mozbuild/test/configure/test_checks_configure.py
+++ b/python/mozbuild/mozbuild/test/configure/test_checks_configure.py
@@ -124,32 +124,36 @@ class TestChecksConfigure(unittest.TestC
         self.assertEqual(out.getvalue(), 'checking for a thing... foo bar\n')
 
     KNOWN_A = mozpath.abspath('/usr/bin/known-a')
     KNOWN_B = mozpath.abspath('/usr/local/bin/known-b')
     KNOWN_C = mozpath.abspath('/home/user/bin/known c')
     OTHER_A = mozpath.abspath('/lib/other/known-a')
 
     def get_result(self, command='', args=[], environ={},
-                   prog='/bin/configure'):
+                   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)
-        environ['PATH'] = os.pathsep.join(os.path.dirname(p) for p in paths)
+        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')
-        sandbox.include_file(os.path.join(base_dir, 'util.configure'))
-        sandbox.include_file(os.path.join(base_dir, 'checks.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
 
@@ -442,10 +446,183 @@ class TestChecksConfigure(unittest.TestC
         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, also
+        # valid.
+        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)))
+
+        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)))
+
 if __name__ == '__main__':
     main()