Bug 1108771 - Part 4: Implement |mach bootstrap| for mobile/android on OS X with homebrew. r=gps
authorNick Alexander <nalexander@mozilla.com>
Sun, 21 Dec 2014 15:29:19 -0800
changeset 221188 cfbcc324f038468941ec71b4145ef11b7d0d3cf3
parent 221187 2b353a88f2b9f3927b53ea5283d1747ce395ea49
child 221189 9b7ace6537733a86b1836469f3ce403eefd1a349
push id28013
push userphilringnalda@gmail.com
push dateWed, 24 Dec 2014 23:31:28 +0000
treeherdermozilla-central@38471b0310c9 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgps
bugs1108771
milestone37.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 1108771 - Part 4: Implement |mach bootstrap| for mobile/android on OS X with homebrew. r=gps Pushing on a CLOSED TREE because this is NPOTB. The custom brew formula is a lightly edited version of an earlier revision of brew's android-ndk.rb. It's not clear that using a custom brew formula for the Android SDK version r8e is better than using the existing android Python module for installing the SDK and the NDK, but it's done now and works locally. If we really wanted to avoid shipping it, we could probably arrange to land it in https://github.com/Homebrew/homebrew-versions. I see no easy way to install the Java 1.7 JDK with macports, so in the spirit of the good now being better than the perfect later, I've punted. (I don't see an Android NDK package either, but that functionality exists in Python.) Patches wanted!
python/mozboot/bin/bootstrap.py
python/mozboot/mozboot/android-ndk-r8e.rb
python/mozboot/mozboot/osx.py
--- a/python/mozboot/bin/bootstrap.py
+++ b/python/mozboot/bin/bootstrap.py
@@ -27,16 +27,17 @@ from optparse import OptionParser
 
 # The next two variables define where in the repository the Python files
 # reside. This is used to remotely download file content when it isn't
 # available locally.
 REPOSITORY_PATH_PREFIX = 'python/mozboot'
 
 REPOSITORY_PATHS = [
     'mozboot/__init__.py',
+    'mozboot/android-ndk-r8e.rb',
     'mozboot/android.py',
     'mozboot/base.py',
     'mozboot/bootstrap.py',
     'mozboot/centos.py',
     'mozboot/debian.py',
     'mozboot/fedora.py',
     'mozboot/freebsd.py',
     'mozboot/gentoo.py',
new file mode 100644
--- /dev/null
+++ b/python/mozboot/mozboot/android-ndk-r8e.rb
@@ -0,0 +1,50 @@
+require "formula"
+
+class AndroidNdk < Formula
+  homepage "http://developer.android.com/sdk/ndk/index.html"
+
+  if MacOS.prefer_64_bit?
+    url "http://dl.google.com/android/ndk/android-ndk-r8e-darwin-x86_64.tar.bz2"
+    sha1 "8c8f0d7df5f160c3ef82f2f4836cbcaf18aabf68"
+  else
+    url "http://dl.google.com/android/ndk/android-ndk-r8e-darwin-x86.tar.bz2"
+    sha1 "60536b22b3c09015a4c7072097404a9a1316b242"
+  end
+
+  version "r8e"
+
+  depends_on "android-sdk" => :recommended
+
+  def install
+    bin.mkpath
+
+    # Now we can install both 64-bit and 32-bit targeting toolchains
+    prefix.install Dir["*"]
+
+    # Create a dummy script to launch the ndk apps
+    ndk_exec = prefix+"ndk-exec.sh"
+    ndk_exec.write <<-EOS.undent
+      #!/bin/sh
+      BASENAME=`basename $0`
+      EXEC="#{prefix}/$BASENAME"
+      test -f "$EXEC" && exec "$EXEC" "$@"
+    EOS
+    ndk_exec.chmod 0755
+    %w[ndk-build ndk-gdb ndk-stack].each { |app| bin.install_symlink ndk_exec => app }
+  end
+
+  def caveats; <<-EOS.undent
+    We agreed to the Android NDK License Agreement for you by downloading the NDK.
+    If this is unacceptable you should uninstall.
+
+    License information at:
+    http://developer.android.com/sdk/terms.html
+
+    Software and System requirements at:
+    http://developer.android.com/sdk/ndk/index.html#requirements
+
+    For more documentation on Android NDK, please check:
+      #{prefix}/docs
+    EOS
+  end
+end
--- a/python/mozboot/mozboot/osx.py
+++ b/python/mozboot/mozboot/osx.py
@@ -102,17 +102,17 @@ PACKAGE_MANAGER_OLD_CLANG = '''
 We require a newer compiler than what is provided by your version of Xcode.
 
 We will install a modern version of Clang through %s.
 '''
 
 PACKAGE_MANAGER_CHOICE = '''
 Please choose a package manager you'd like:
 1. Homebrew
-2. MacPorts
+2. MacPorts (Does not yet support bootstrapping Firefox for Android.)
 Your choice:
 '''
 
 NO_PACKAGE_MANAGER_WARNING = '''
 It seems you don't have any supported package manager installed.
 '''
 
 PACKAGE_MANAGER_EXISTS = '''
@@ -155,16 +155,21 @@ Modify your shell's configuration (e.g. 
 ~/.bash_profile) to have %s appear in $PATH before %s. e.g.
 
     export PATH=%s:$PATH
 
 Once this is done, start a new shell (likely Command+T) and run
 this bootstrap again.
 '''
 
+JAVA_LICENSE_NOTICE = '''
+We installed a recent Java toolchain for you. We agreed to the Oracle Java
+license for you by downloading the JDK. If this is unacceptable you should
+uninstall.
+'''
 
 class OSXBootstrapper(BaseBootstrapper):
     def __init__(self, version):
         BaseBootstrapper.__init__(self)
 
         self.os_version = StrictVersion(version)
 
         if self.os_version < StrictVersion('10.6'):
@@ -177,16 +182,22 @@ class OSXBootstrapper(BaseBootstrapper):
 
         choice = self.ensure_package_manager()
         self.package_manager = choice
         getattr(self, 'ensure_%s_system_packages' % self.package_manager)()
 
     def install_browser_packages(self):
         getattr(self, 'ensure_%s_browser_packages' % self.package_manager)()
 
+    def install_mobile_android_packages(self):
+        getattr(self, 'ensure_%s_mobile_android_packages' % self.package_manager)()
+
+    def suggest_mobile_android_mozconfig(self):
+        getattr(self, 'suggest_%s_mobile_android_mozconfig' % self.package_manager)()
+
     def ensure_xcode(self):
         if self.os_version < StrictVersion('10.7'):
             if not os.path.exists('/Developer/Applications/Xcode.app'):
                 print(XCODE_REQUIRED_LEGACY)
 
                 subprocess.check_call(['open', XCODE_LEGACY])
                 sys.exit(1)
 
@@ -305,16 +316,47 @@ class OSXBootstrapper(BaseBootstrapper):
 
         installed = self.check_output([self.brew, 'list']).split()
         if self.os_version < StrictVersion('10.7') and 'llvm' not in installed:
             print(PACKAGE_MANAGER_OLD_CLANG % ('Homebrew',))
 
             subprocess.check_call([self.brew, '-v', 'install', 'llvm',
                 '--with-clang', '--all-targets'])
 
+    def ensure_homebrew_mobile_android_packages(self):
+        import android
+
+        # We don't need wget because we install the Android SDK and NDK from
+        # packages.  If we used the android.py module, we'd need wget.
+        packages = [
+            ('android-sdk', 'android-sdk'),
+            ('android-ndk', 'android-ndk-r8e.rb'), # This is a locally provided brew formula!
+            ('ant', 'ant'),
+            ('brew-cask', 'caskroom/cask/brew-cask'), # For installing Java later.
+        ]
+        self._ensure_homebrew_packages(packages)
+
+        casks = [
+            ('java', 'java'),
+        ]
+        installed = self._ensure_homebrew_casks(casks)
+        if installed:
+            print(JAVA_LICENSE_NOTICE) # We accepted a license agreement for the user.
+
+        # We could probably fish this path from |brew info android-sdk|.
+        android_tool = '/usr/local/opt/android-sdk/tools/android'
+        android.ensure_android_packages(android_tool)
+
+    def suggest_homebrew_mobile_android_mozconfig(self):
+        import android
+        # We could probably fish this path from |brew info android-sdk|.
+        sdk_path = '/usr/local/opt/android-sdk/platform/%s' % android.ANDROID_PLATFORM
+        ndk_path = '/usr/local/opt/android-ndk'
+        android.suggest_mozconfig(sdk_path=sdk_path, ndk_path=ndk_path)
+
     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())
 
         missing = [package for package in packages if package not in installed]
         if missing:
@@ -335,16 +377,26 @@ class OSXBootstrapper(BaseBootstrapper):
         self._ensure_macports_packages(packages)
 
         installed = set(self.check_output([self.port, 'installed']).split())
         if self.os_version < StrictVersion('10.7') and MACPORTS_CLANG_PACKAGE not in installed:
             print(PACKAGE_MANAGER_OLD_CLANG % ('MacPorts',))
             self.run_as_root([self.port, '-v', 'install', MACPORTS_CLANG_PACKAGE])
             self.run_as_root([self.port, 'select', '--set', 'clang', 'mp-' + MACPORTS_CLANG_PACKAGE])
 
+    def ensure_macports_mobile_android_packages(self):
+        raise NotImplementedError("We don't yet support bootstrapping Firefox for Android with Macports. " +
+                                  "We don't know of a package that installs the Java 7 JDK. " +
+                                  "See https://bugzilla.mozilla.org/show_bug.cgi?id=1114382.")
+
+    def suggest_macports_mobile_android_mozconfig(self):
+        raise NotImplementedError("We don't yet support bootstrapping Firefox for Android with Macports. " +
+                                  "We don't know of a package that installs the Java 7 JDK." +
+                                  "See https://bugzilla.mozilla.org/show_bug.cgi?id=1114382.")
+
     def ensure_package_manager(self):
         '''
         Search package mgr in sys.path, if none is found, prompt the user to install one.
         If only one is found, use that one. If both are found, prompt the user to choose
         one.
         '''
         installed = []
         for name, cmd in PACKAGE_MANAGER.iteritems():