Bug 774112 - Part 1: Skeleton for one-line system bootstrapping; r=jhammel
authorGregory Szorc <gps@mozilla.com>
Tue, 11 Sep 2012 16:27:20 -0700
changeset 113150 544c405fb456b4cfdc102cbe14d704c4ead17ae0
parent 113149 1a1a98daab06467733ea7314a43f0bd70618a19a
child 113151 ac75030f29a7a5f570f3d3d89c81a6d7adb426a5
push id1708
push userakeybl@mozilla.com
push dateMon, 19 Nov 2012 21:10:21 +0000
treeherdermozilla-beta@27b14fe50103 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjhammel
bugs774112
milestone18.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 774112 - Part 1: Skeleton for one-line system bootstrapping; r=jhammel
python/mozboot/README.rst
python/mozboot/bin/bootstrap.py
python/mozboot/mozboot/__init__.py
python/mozboot/mozboot/base.py
python/mozboot/mozboot/bootstrap.py
python/mozboot/mozboot/osx.py
python/mozboot/mozboot/ubuntu.py
python/mozboot/setup.py
new file mode 100644
--- /dev/null
+++ b/python/mozboot/README.rst
@@ -0,0 +1,19 @@
+mozboot - Bootstrap your system to build Mozilla projects
+=========================================================
+
+This package contains code used for bootstrapping a system to build
+mozilla-central.
+
+This code is not part of the build system per se. Instead, it is related
+to everything up to invoking the actual build system.
+
+If you have a copy of the source tree, you run:
+
+    python bin/bootstrap.py
+
+If you don't have a copy of the source tree, you can run:
+
+    curl https://hg.mozilla.org/mozilla-central/raw-file/default/python/mozboot/bin/bootstrap.py | python -
+
+The bootstrap script will download everything it needs from hg.mozilla.org
+automatically!
new file mode 100755
--- /dev/null
+++ b/python/mozboot/bin/bootstrap.py
@@ -0,0 +1,131 @@
+#!/usr/bin/env 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/.
+
+# This script provides one-line bootstrap support to configure systems to build
+# the tree.
+#
+# The role of this script is to load the Python modules containing actual
+# bootstrap support. It does this through various means, including fetching
+# content from the upstream source repository.
+
+from __future__ import print_function, unicode_literals
+
+import os
+import shutil
+import sys
+import tempfile
+import urllib2
+
+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/base.py',
+    'mozboot/bootstrap.py',
+    'mozboot/osx.py',
+    'mozboot/ubuntu.py',
+]
+
+TEMPDIR = None
+
+def fetch_files(repo_url, repo_type):
+    repo_url = repo_url.rstrip('/')
+
+    files = {}
+
+    if repo_type == 'hgweb':
+        for path in REPOSITORY_PATHS:
+            url = repo_url + '/raw-file/default/python/mozboot/' + path
+
+            req = urllib2.urlopen(url=url, timeout=30)
+            files[path] = req.read()
+    else:
+        raise NotImplementedError('Not sure how to handle repo type.', repo_type)
+
+    return files
+
+def ensure_environment(repo_url=None, repo_type=None):
+    """Ensure we can load the Python modules necessary to perform bootstrap."""
+
+    try:
+        from mozboot.bootstrap import Bootstrapper
+        return Bootstrapper
+    except ImportError:
+        # The first fallback is to assume we are running from a tree checkout
+        # and have the files in a sibling directory.
+        pardir = os.path.join(os.path.dirname(__file__), os.path.pardir)
+        include = os.path.normpath(pardir)
+
+        sys.path.append(include)
+        try:
+            from mozboot.bootstrap import Bootstrapper
+            return Bootstrapper
+        except ImportError:
+            sys.path.pop()
+
+            # The next fallback is to download the files from the source
+            # repository.
+            files = fetch_files(repo_url, repo_type)
+
+            # Install them into a temporary location. They will be deleted
+            # after this script has finished executing.
+            global TEMPDIR
+            TEMPDIR = tempfile.mkdtemp()
+
+            for relpath in files.keys():
+                destpath = os.path.join(TEMPDIR, relpath)
+                destdir = os.path.dirname(destpath)
+
+                if not os.path.exists(destdir):
+                    os.makedirs(destdir)
+
+                with open(destpath, 'wb') as fh:
+                    fh.write(files[relpath])
+
+            # This should always work.
+            sys.path.append(TEMPDIR)
+            from mozboot.bootstrap import Bootstrapper
+            return Bootstrapper
+
+def main(args):
+    parser = OptionParser()
+    parser.add_option('-r', '--repo-url', dest='repo_url',
+        default='https://hg.mozilla.org/mozilla-central/',
+        help='Base URL of source control repository where bootstrap files can '
+             'be downloaded.')
+
+    parser.add_option('--repo-type', dest='repo_type',
+        default='hgweb',
+        help='The type of the repository. This defines how we fetch file '
+             'content. Like --repo, you should not need to set this.')
+
+    options, leftover = parser.parse_args(args)
+
+    try:
+        try:
+            cls = ensure_environment(options.repo_url, options.repo_type)
+        except Exception as e:
+            print('Could not load the bootstrap Python environment.\n')
+            print('This should never happen. Consider filing a bug.\n')
+            print('\n')
+            print(e)
+            return 1
+
+        dasboot = cls()
+        dasboot.bootstrap()
+
+        return 0
+    finally:
+        if TEMPDIR is not None:
+            shutil.rmtree(TEMPDIR)
+
+
+if __name__ == '__main__':
+    sys.exit(main(sys.argv))
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/python/mozboot/mozboot/base.py
@@ -0,0 +1,12 @@
+# 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/.
+
+class BaseBootstrapper(object):
+    """Base class for system bootstrappers."""
+    def __init__(self):
+        pass
+
+    def install_system_packages(self):
+        raise NotImplemented('%s must implement install_system_packages()' %
+            __name__)
new file mode 100644
--- /dev/null
+++ b/python/mozboot/mozboot/bootstrap.py
@@ -0,0 +1,45 @@
+# 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/.
+
+import platform
+import sys
+
+from mozboot.osx import OSXBootstrapper
+from mozboot.ubuntu import UbuntuBootstrapper
+
+
+class Bootstrapper(object):
+    """Main class that performs system bootstrap."""
+
+    def bootstrap(self):
+        cls = None
+        args = {}
+
+        if sys.platform.startswith('linux'):
+            distro, version, dist_id = platform.linux_distribution()
+
+            if distro == 'Ubuntu':
+                cls = UbuntuBootstrapper
+            else:
+                raise NotImplementedError('Bootstrap support for this Linux '
+                                          'distro not yet available.')
+
+            args['version'] = version
+            args['dist_id'] = dist_id
+
+        elif sys.platform.startswith('darwin'):
+            # TODO Support Darwin platforms that aren't OS X.
+            major, minor, point = map(int, platform.mac_ver()[0].split('.'))
+
+            cls = OSXBootstrapper
+            args['major'] = major
+            args['minor'] = minor
+            args['point'] = point
+
+        if cls is None:
+            raise NotImplementedError('Bootstrap support is not yet available '
+                                      'for your OS.')
+
+        instance = cls(**args)
+        instance.install_system_packages()
new file mode 100644
--- /dev/null
+++ b/python/mozboot/mozboot/osx.py
@@ -0,0 +1,17 @@
+# 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/.
+
+from mozboot.base import BaseBootstrapper
+
+class OSXBootstrapper(BaseBootstrapper):
+    def __init__(self, major, minor, point):
+        BaseBootstrapper.__init__(self)
+
+        if major == 10 and minor < 6:
+            raise Exception('OS X 10.6 or above is required.')
+
+    def install_system_packages(self):
+        raise NotImplementedError('OS X bootstrap not yet implemented.')
+
+
new file mode 100644
--- /dev/null
+++ b/python/mozboot/mozboot/ubuntu.py
@@ -0,0 +1,15 @@
+# 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/.
+
+from mozboot.base import BaseBootstrapper
+
+class UbuntuBootstrapper(BaseBootstrapper):
+    def __init__(self, version, dist_id):
+        BaseBootstrapper.__init__(self)
+
+        self.version = version
+        self.dist_id = dist_id
+
+    def install_system_packages(self):
+        raise NotImplementedError('Bootstrap for Ubuntu not yet implemented.')
new file mode 100644
--- /dev/null
+++ b/python/mozboot/setup.py
@@ -0,0 +1,16 @@
+# 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/.
+
+from distutils.core import setup
+
+VERSION = '0.1'
+
+setup(
+    name='mozboot',
+    description='System bootstrap for building Mozilla projects.',
+    license='MPL 2.0',
+    packages=['mozboot'],
+    version=VERSION,
+    scripts=['bin/bootstrap.py'],
+)