Bug 1384202 - Add --no-interactive bootstrap support for Android SDK and NDK. r=rillian draft
authorNick Alexander <nalexander@mozilla.com>
Wed, 19 Jul 2017 15:29:11 -0700
changeset 615333 5a80bdd5ddfb551b374464f42c3783fef5a71fc3
parent 615332 2885195855497d69b1cbba02a943964dd3286b93
child 639145 6d45981aa6f6432c3acc767db352bd8fc0812b23
push id70322
push usernalexander@mozilla.com
push dateTue, 25 Jul 2017 20:22:42 +0000
reviewersrillian
bugs1384202
milestone56.0a1
Bug 1384202 - Add --no-interactive bootstrap support for Android SDK and NDK. r=rillian Hacky, but it works -- until Google updates its license hashes. This looks ahead to using |mach bootstrap| to build docker images. MozReview-Commit-ID: DF23v8tr8SW
python/mozboot/mozboot/android.py
python/mozboot/mozboot/archlinux.py
python/mozboot/mozboot/centosfedora.py
python/mozboot/mozboot/debian.py
python/mozboot/mozboot/mach_commands.py
python/mozboot/mozboot/osx.py
--- a/python/mozboot/mozboot/android.py
+++ b/python/mozboot/mozboot/android.py
@@ -136,17 +136,27 @@ def get_paths(os_name):
                                    os.path.expanduser(os.path.join('~', '.mozbuild')))
     sdk_path = os.environ.get('ANDROID_SDK_HOME',
                               os.path.join(mozbuild_path, 'android-sdk-{}'.format(os_name)))
     ndk_path = os.environ.get('ANDROID_NDK_HOME',
                               os.path.join(mozbuild_path, 'android-ndk-r11c'))
     return (mozbuild_path, sdk_path, ndk_path)
 
 
-def ensure_android(os_name, artifact_mode):
+def ensure_dir(dir):
+    '''Ensures the given directory exists'''
+    if dir and not os.path.exists(dir):
+        try:
+            os.makedirs(dir)
+        except OSError as error:
+            if error.errno != errno.EEXIST:
+                raise
+
+
+def ensure_android(os_name, artifact_mode=False, no_interactive=False):
     '''
     Ensure the Android SDK (and NDK, if `artifact_mode` is falsy) are
     installed.  If not, fetch and unpack the SDK and/or NDK from the
     given URLs.  Ensure the required Android SDK packages are
     installed.
 
     `os_name` can be 'linux' or 'macosx'.
     '''
@@ -159,16 +169,31 @@ def ensure_android(os_name, artifact_mod
     sdk_url = 'https://dl.google.com/android/repository/sdk-tools-{}-3859397.zip'.format(os_tag)
     ndk_url = android_ndk_url(os_name)
 
     ensure_android_sdk_and_ndk(mozbuild_path, os_name,
                                sdk_path=sdk_path, sdk_url=sdk_url,
                                ndk_path=ndk_path, ndk_url=ndk_url,
                                artifact_mode=artifact_mode)
 
+    if no_interactive:
+        # Cribbed from observation and https://stackoverflow.com/a/38381577.
+        path = os.path.join(mozbuild_path, 'android-sdk-{}'.format(os_name), 'licenses')
+        ensure_dir(path)
+
+        licenses = {
+            'android-sdk-license': '8933bad161af4178b1185d1a37fbf41ea5269c55',
+            'android-sdk-preview-license': '84831b9409646a918e30573bab4c9c91346d8abd',
+        }
+        for license, tag in licenses.items():
+            lname = os.path.join(path, license)
+            if not os.path.isfile(lname):
+                open(lname, 'w').write('\n{}\n'.format(tag))
+
+
     # We expect the |sdkmanager| tool to be at
     # ~/.mozbuild/android-sdk-$OS_NAME/tools/bin/sdkmanager.
     sdkmanager_tool = os.path.join(sdk_path, 'tools', 'bin', 'sdkmanager')
     ensure_android_packages(sdkmanager_tool=sdkmanager_tool)
 
 
 def ensure_android_sdk_and_ndk(mozbuild_path, os_name, sdk_path, sdk_url, ndk_path, ndk_url, artifact_mode):
     '''
@@ -244,30 +269,32 @@ def android_ndk_url(os_name, ver='r11c')
 
 def main(argv):
     import optparse # No argparse, which is new in Python 2.7.
     import platform
 
     parser = optparse.OptionParser()
     parser.add_option('-a', '--artifact-mode', dest='artifact_mode', action='store_true',
                       help='If true, install only the Android SDK (and not the Android NDK).')
+    parser.add_option('--no-interactive', dest='no_interactive', action='store_true',
+                      help='Accept the Android SDK licenses without user interaction.')
 
     options, _ = parser.parse_args(argv)
 
     os_name = None
     if platform.system() == 'Darwin':
         os_name = 'macosx'
     elif platform.system() == 'Linux':
         os_name = 'linux'
     elif platform.system() == 'Windows':
         os_name = 'windows'
     else:
         raise NotImplementedError("We don't support bootstrapping the Android SDK (or Android NDK) "
                                   "on {} yet!".format(platform.system()))
 
-    ensure_android(os_name, options.artifact_mode)
+    ensure_android(os_name, artifact_mode=options.artifact_mode, no_interactive=options.no_interactive)
     suggest_mozconfig(os_name, options.artifact_mode)
 
     return 0
 
 
 if __name__ == '__main__':
     sys.exit(main(sys.argv))
--- a/python/mozboot/mozboot/archlinux.py
+++ b/python/mozboot/mozboot/archlinux.py
@@ -118,17 +118,18 @@ class ArchlinuxBootstrapper(StyloInstall
                   '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.
         import android
-        android.ensure_android('linux', artifact_mode=artifact_mode)
+        android.ensure_android('linux', artifact_mode=artifact_mode,
+                               no_interactive=self.no_interactive)
 
     def suggest_mobile_android_mozconfig(self, artifact_mode=False):
         import android
         android.suggest_mozconfig('linux', artifact_mode=artifact_mode)
 
     def suggest_mobile_android_artifact_mode_mozconfig(self):
         self.suggest_mobile_android_mozconfig(artifact_mode=True)
 
--- a/python/mozboot/mozboot/centosfedora.py
+++ b/python/mozboot/mozboot/centosfedora.py
@@ -107,17 +107,18 @@ class CentOSFedoraBootstrapper(StyloInst
 
             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)
 
         import android
-        android.ensure_android('linux', artifact_mode=artifact_mode)
+        android.ensure_android('linux', artifact_mode=artifact_mode,
+                               no_interactive=self.no_interactive)
 
     def suggest_mobile_android_mozconfig(self, artifact_mode=False):
         import android
         android.suggest_mozconfig('linux', artifact_mode=artifact_mode)
 
     def suggest_mobile_android_artifact_mode_mozconfig(self):
         self.suggest_mobile_android_mozconfig(artifact_mode=True)
 
--- a/python/mozboot/mozboot/debian.py
+++ b/python/mozboot/mozboot/debian.py
@@ -122,17 +122,18 @@ class DebianBootstrapper(StyloInstall, B
         # 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.
         import android
-        android.ensure_android('linux', artifact_mode=artifact_mode)
+        android.ensure_android('linux', artifact_mode=artifact_mode,
+                               no_interactive=self.no_interactive)
 
     def suggest_mobile_android_mozconfig(self, artifact_mode=False):
         import android
         android.suggest_mozconfig('linux', artifact_mode=artifact_mode)
 
     def suggest_mobile_android_artifact_mode_mozconfig(self):
         self.suggest_mobile_android_mozconfig(artifact_mode=True)
 
--- a/python/mozboot/mozboot/mach_commands.py
+++ b/python/mozboot/mozboot/mach_commands.py
@@ -18,20 +18,22 @@ class Bootstrap(object):
     """Bootstrap system and mach for optimal development experience."""
 
     @Command('bootstrap', category='devenv',
              description='Install required system packages for building.')
     @CommandArgument('--application-choice',
                      default=None,
                      help='Pass in an application choice (see mozboot.bootstrap.APPLICATIONS) '
                      'instead of using the default interactive prompt.')
-    def bootstrap(self, application_choice=None):
+    @CommandArgument('--no-interactive', dest='no_interactive', action='store_true',
+                     help='Answer yes to any (Y/n) interactive prompts.')
+    def bootstrap(self, application_choice=None, no_interactive=False):
         from mozboot.bootstrap import Bootstrapper
 
-        bootstrapper = Bootstrapper(choice=application_choice)
+        bootstrapper = Bootstrapper(choice=application_choice, no_interactive=no_interactive)
         bootstrapper.bootstrap()
 
 
 @CommandProvider
 class VersionControlCommands(object):
     def __init__(self, context):
         self._context = context
 
--- a/python/mozboot/mozboot/osx.py
+++ b/python/mozboot/mozboot/osx.py
@@ -349,17 +349,18 @@ 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 Firefox for Android.')
 
         # 2. Android pieces.
         import android
-        android.ensure_android('macosx', artifact_mode=artifact_mode)
+        android.ensure_android('macosx', artifact_mode=artifact_mode,
+                               no_interactive=self.no_interactive)
 
     def suggest_homebrew_mobile_android_mozconfig(self, artifact_mode=False):
         import android
         android.suggest_mozconfig('macosx', artifact_mode=artifact_mode)
 
     def _ensure_macports_packages(self, packages):
         self.port = self.which('port')
         assert self.port is not None
@@ -412,17 +413,18 @@ class OSXBootstrapper(BaseBootstrapper):
                             '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 Firefox for Android.')
 
         # 2. Android pieces.
         import android
-        android.ensure_android('macosx', artifact_mode=artifact_mode)
+        android.ensure_android('macosx', artifact_mode=artifact_mode,
+                               no_interactive=self.no_interactive)
 
     def suggest_macports_mobile_android_mozconfig(self, artifact_mode=False):
         import android
         android.suggest_mozconfig('macosx', artifact_mode=artifact_mode)
 
     def ensure_package_manager(self):
         '''
         Search package mgr in sys.path, if none is found, prompt the user to install one.