Bug 1477487 - Part 1: Look for java and pin to Java 1.8 in |mach bootstrap|; r=agi
authorNick Alexander <nalexander@mozilla.com>
Thu, 17 Jan 2019 21:30:29 +0000
changeset 511453 b1f74fc90f37ba9bee709ea76754253dda5ef561
parent 511452 8b70ac3ef8d0473082811c12f605f51a29d293fd
child 511454 bbfe0cbb90b4d17d53773914f2f3753d830af242
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersagi
bugs1477487
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1477487 - Part 1: Look for java and pin to Java 1.8 in |mach bootstrap|; r=agi What is happening is that distribution JRE and JDK packages roll forward, installing a different version of Java than what is expected. We don't check the version installed, so sadness ensues. Right now, we require Java 1.8 to build, but in the near future, after Android-Gradle plugin 3.2.1+, we'll be free to use later Java versions. However, Android's `sdkmanager` itself requires exactly Java 1.8. We only require `sdkmanager` to install `emulator`, really -- everything else will be fetched by Gradle -- but I don't want to unravel that right now. So let's just provide decent error messages and try to prevent the worst of the footguns. Differential Revision: https://phabricator.services.mozilla.com/D16137
python/mozboot/mozboot/android.py
python/mozboot/mozboot/archlinux.py
python/mozboot/mozboot/base.py
python/mozboot/mozboot/centosfedora.py
python/mozboot/mozboot/debian.py
python/mozboot/mozboot/gentoo.py
python/mozboot/mozboot/osx.py
--- a/python/mozboot/mozboot/android.py
+++ b/python/mozboot/mozboot/android.py
@@ -291,17 +291,17 @@ def ensure_android_packages(sdkmanager_t
 
 
 def suggest_mozconfig(os_name, artifact_mode=False, java_bin_path=None):
     _mozbuild_path, sdk_path, ndk_path = get_paths(os_name)
 
     extra_lines = []
     if java_bin_path:
         extra_lines += [
-            '# With the following java and javac:',
+            '# With the following java:',
             'ac_add_options --with-java-bin-path="{}"'.format(java_bin_path),
         ]
     if extra_lines:
         extra_lines.append('')
 
     if artifact_mode:
         template = MOBILE_ANDROID_ARTIFACT_MODE_MOZCONFIG_TEMPLATE
     else:
--- a/python/mozboot/mozboot/archlinux.py
+++ b/python/mozboot/mozboot/archlinux.py
@@ -115,16 +115,17 @@ class ArchlinuxBootstrapper(NodeInstall,
             print('Failed to install all packages.  The Android developer '
                   'toolchain requires 32 bit binaries be enabled (see '
                   'https://wiki.archlinux.org/index.php/Android).  You may need to '
                   'manually enable the multilib repository following the instructions '
                   'at https://wiki.archlinux.org/index.php/Multilib.')
             raise e
 
         # 2. Android pieces.
+        self.ensure_java()
         from mozboot import android
         android.ensure_android('linux', artifact_mode=artifact_mode,
                                no_interactive=self.no_interactive)
 
     def suggest_mobile_android_mozconfig(self, artifact_mode=False):
         from mozboot import android
         android.suggest_mozconfig('linux', artifact_mode=artifact_mode)
 
--- a/python/mozboot/mozboot/base.py
+++ b/python/mozboot/mozboot/base.py
@@ -742,8 +742,62 @@ class BaseBootstrapper(object):
                 if data:
                     out.write(data)
                     h.update(data)
                 else:
                     break
         if h.hexdigest() != hexhash:
             os.remove(dest)
             raise ValueError('Hash of downloaded file does not match expected hash')
+
+    def ensure_java(self, extra_search_dirs=()):
+        """Verify the presence of java.
+
+        Note that we currently require a JDK (not just a JRE) because we
+        use `jarsigner` in local builds.
+
+        Soon we won't require Java 1.8 to build (after Bug 1515248 and
+        we use Android-Gradle plugin 3.2.1), but the Android
+        `sdkmanager` tool still requires exactly 1.8.  Sigh.  Note that
+        we no longer require javac explicitly; it's fetched by
+        Gradle.
+        """
+
+        if 'JAVA_HOME' in os.environ:
+            extra_search_dirs += (os.path.join(os.environ['JAVA_HOME'], 'bin'),)
+        java = self.which('java', extra_search_dirs)
+
+        if not java:
+            raise Exception('You need to have Java version 1.8 installed. '
+                            'Please visit http://www.java.com/en/download '
+                            'to get version 1.8.')
+
+        try:
+            output = subprocess.check_output([java,
+                                              '-XshowSettings:properties',
+                                              '-version'],
+                                             stderr=subprocess.STDOUT).rstrip()
+
+            # -version strings are pretty free-form, like: 'java version
+            # "1.8.0_192"' or 'openjdk version "11.0.1" 2018-10-16', but the
+            # -XshowSettings:properties gives the information (to stderr, sigh)
+            # like 'java.specification.version = 8'.  That flag is non-standard
+            # but has been around since at least 2011.
+            version = [line for line in output.splitlines()
+                       if 'java.specification.version' in line]
+            if not len(version) == 1:
+                raise Exception('You need to have Java version 1.8 installed '
+                                '(found {} but could not parse version "{}"). '
+                                'Check the JAVA_HOME environment variable. '
+                                'Please visit http://www.java.com/en/download '
+                                'to get version 1.8.'.format(java, output))
+
+            version = version[0].split(' = ')[-1]
+            if version not in ['1.8', '8']:
+                raise Exception('You need to have Java version 1.8 installed '
+                                '(found {} with version "{}"). '
+                                'Check the JAVA_HOME environment variable. '
+                                'Please visit http://www.java.com/en/download '
+                                'to get version 1.8.'.format(java, version))
+        except subprocess.CalledProcessError as e:
+            raise Exception('Failed to get java version from {}: {}'.format(java, e.output))
+
+        print('Your version of Java ({}) is at least 1.8 ({}).'.format(java, version))
--- a/python/mozboot/mozboot/centosfedora.py
+++ b/python/mozboot/mozboot/centosfedora.py
@@ -116,16 +116,17 @@ class CentOSFedoraBootstrapper(NodeInsta
                         'Packages/y/yasm-1.2.0-1.el6.x86_64.rpm')
 
             self.run_as_root(['rpm', '-ivh', yasm])
 
     def ensure_mobile_android_packages(self, artifact_mode=False):
         # Install Android specific packages.
         self.dnf_install(*self.mobile_android_packages)
 
+        self.ensure_java()
         from mozboot import android
         android.ensure_android('linux', artifact_mode=artifact_mode,
                                no_interactive=self.no_interactive)
 
     def suggest_mobile_android_mozconfig(self, artifact_mode=False):
         from mozboot import android
         android.suggest_mozconfig('linux', artifact_mode=artifact_mode)
 
--- a/python/mozboot/mozboot/debian.py
+++ b/python/mozboot/mozboot/debian.py
@@ -73,20 +73,18 @@ class DebianBootstrapper(NodeInstall, St
     ]
 
     # Subclasses can add packages to this variable to have them installed.
     BROWSER_DISTRO_PACKAGES = []
 
     # These are common packages for building Firefox for Android
     # (mobile/android) for all Debian-derived distros (such as Ubuntu).
     MOBILE_ANDROID_COMMON_PACKAGES = [
-        'default-jdk',
+        'openjdk-8-jdk-headless',  # Android's `sdkmanager` requires Java 1.8 exactly.
         'wget',  # For downloading the Android SDK and NDK.
-        'libncurses5:i386',  # See comments about i386 below.
-        'libstdc++6:i386',
     ]
 
     # Subclasses can add packages to this variable to have them installed.
     MOBILE_ANDROID_DISTRO_PACKAGES = []
 
     def __init__(self, distro, version, dist_id, **kwargs):
         BaseBootstrapper.__init__(self, **kwargs)
 
@@ -134,28 +132,20 @@ class DebianBootstrapper(NodeInstall, St
     def ensure_browser_packages(self, artifact_mode=False):
         # TODO: Figure out what not to install for artifact mode
         self.apt_install(*self.browser_packages)
 
     def ensure_mobile_android_packages(self, artifact_mode=False):
         # Multi-part process:
         # 1. System packages.
         # 2. Android SDK. Android NDK only if we are not in artifact mode. Android packages.
-
-        # 1. This is hard to believe, but the Android SDK binaries are 32-bit
-        # and that conflicts with 64-bit Debian and Ubuntu installations out of
-        # the box.  The solution is to add the i386 architecture.  See
-        # "Troubleshooting Ubuntu" at
-        # http://developer.android.com/sdk/installing/index.html?pkg=tools.
-        self.run_as_root(['dpkg', '--add-architecture', 'i386'])
-        # After adding a new arch, the list of packages has to be updated
-        self.apt_update()
         self.apt_install(*self.mobile_android_packages)
 
         # 2. Android pieces.
+        self.ensure_java()
         from mozboot import android
         android.ensure_android('linux', artifact_mode=artifact_mode,
                                no_interactive=self.no_interactive)
 
     def suggest_mobile_android_mozconfig(self, artifact_mode=False):
         from mozboot import android
         android.suggest_mozconfig('linux', artifact_mode=artifact_mode)
 
--- a/python/mozboot/mozboot/gentoo.py
+++ b/python/mozboot/mozboot/gentoo.py
@@ -104,16 +104,17 @@ class GentooBootstrapper(NodeInstall, St
 
         # Install the Oracle JDK. We explicitly prompt the user to accept the
         # changes because this command might need to modify the portage
         # configuration files and doing so without user supervision is dangerous
         self.run_as_root(['emerge', '--noreplace', '--quiet',
                           '--autounmask-continue', '--ask',
                           'dev-java/oracle-jdk-bin'])
 
+        self.ensure_java()
         from mozboot import android
         android.ensure_android('linux', artifact_mode=artifact_mode,
                                no_interactive=self.no_interactive)
 
     def suggest_mobile_android_mozconfig(self, artifact_mode=False):
         from mozboot import android
         android.suggest_mozconfig('linux', artifact_mode=artifact_mode)
 
--- a/python/mozboot/mozboot/osx.py
+++ b/python/mozboot/mozboot/osx.py
@@ -373,23 +373,28 @@ class OSXBootstrapper(BaseBootstrapper):
             print(JAVA_LICENSE_NOTICE)  # We accepted a license agreement for the user.
 
         is_64bits = sys.maxsize > 2**32
         if not is_64bits:
             raise Exception('You need a 64-bit version of Mac OS X to build '
                             'GeckoView/Firefox for Android.')
 
         # 2. Android pieces.
+        # Prefer homebrew's java binary by putting it on the path first.
+        os.environ['PATH'] = \
+            '{}{}{}'.format('/Library/Java/Home/bin', os.pathsep, os.environ['PATH'])
+        self.ensure_java()
         from mozboot import android
+
         android.ensure_android('macosx', artifact_mode=artifact_mode,
                                no_interactive=self.no_interactive)
 
     def suggest_homebrew_mobile_android_mozconfig(self, artifact_mode=False):
         from mozboot import android
-        # Path to java and javac from the caskroom/versions/java8 cask.
+        # Path to java from the caskroom/versions/java8 cask.
         android.suggest_mozconfig('macosx', artifact_mode=artifact_mode,
                                   java_bin_path='/Library/Java/Home/bin')
 
     def _ensure_macports_packages(self, packages):
         self.port = self.which('port')
         assert self.port is not None
 
         installed = set(self.check_output([self.port, 'installed']).split())
@@ -440,28 +445,23 @@ class OSXBootstrapper(BaseBootstrapper):
         # 2. Android SDK. Android NDK only if we are not in artifact mode. Android packages.
 
         # 1. System packages.
         packages = [
             'wget',
         ]
         self._ensure_macports_packages(packages)
 
-        # Verify the presence of java and javac.
-        if not self.which('java') or not self.which('javac'):
-            raise Exception('You need to have Java version 1.7 or later installed. '
-                            'Please visit http://www.java.com/en/download/mac_download.jsp '
-                            'to get the latest version.')
-
         is_64bits = sys.maxsize > 2**32
         if not is_64bits:
             raise Exception('You need a 64-bit version of Mac OS X to build '
                             'GeckoView/Firefox for Android.')
 
         # 2. Android pieces.
+        self.ensure_java()
         from mozboot import android
         android.ensure_android('macosx', artifact_mode=artifact_mode,
                                no_interactive=self.no_interactive)
 
     def suggest_macports_mobile_android_mozconfig(self, artifact_mode=False):
         from mozboot import android
         android.suggest_mozconfig('macosx', artifact_mode=artifact_mode)