1#!/usr/bin/env python 2# This Source Code Form is subject to the terms of the Mozilla Public 3# License, v. 2.0. If a copy of the MPL was not distributed with this file, 4# You can obtain one at http://mozilla.org/MPL/2.0/. 5 6# This script provides one-line bootstrap support to configure systems to build 7# the tree. 8# 9# The role of this script is to load the Python modules containing actual 10# bootstrap support. It does this through various means, including fetching 11# content from the upstream source repository. 12 13from __future__ import absolute_import, print_function, unicode_literals 14 15WRONG_PYTHON_VERSION_MESSAGE = ''' 16Bootstrap currently only runs on Python 2.7 or Python 3.5+. 17Please try re-running with python2.7 or python3.5+. 18 19If these aren't available on your system, you may need to install them. 20Look for a "python2" or "python3.5" package in your package manager. 21''' 22 23import sys 24 25major, minor = sys.version_info[:2] 26if (major == 2 and minor < 7) or (major == 3 and minor < 5): 27 print(WRONG_PYTHON_VERSION_MESSAGE) 28 sys.exit(1) 29 30import os 31import shutil 32import tempfile 33import zipfile 34 35from io import BytesIO 36from optparse import OptionParser 37 38# NOTE: This script is intended to be run with a vanilla Python install. We 39# have to rely on the standard library instead of Python 2+3 helpers like 40# the six module. 41try: 42 from urllib2 import urlopen 43except ImportError: 44 from urllib.request import urlopen 45 46 47# The next two variables define where in the repository the Python files 48# reside. This is used to remotely download file content when it isn't 49# available locally. 50REPOSITORY_PATH_PREFIX = 'python/mozboot/' 51 52TEMPDIR = None 53 54 55def setup_proxy(): 56 # Some Linux environments define ALL_PROXY, which is a SOCKS proxy 57 # intended for all protocols. Python doesn't currently automatically 58 # detect this like it does for http_proxy and https_proxy. 59 if 'ALL_PROXY' in os.environ and 'https_proxy' not in os.environ: 60 os.environ['https_proxy'] = os.environ['ALL_PROXY'] 61 if 'ALL_PROXY' in os.environ and 'http_proxy' not in os.environ: 62 os.environ['http_proxy'] = os.environ['ALL_PROXY'] 63 64 65def fetch_files(repo_url, repo_rev, repo_type): 66 setup_proxy() 67 repo_url = repo_url.rstrip('/') 68 69 files = {} 70 71 if repo_type == 'hgweb': 72 url = repo_url + '/archive/%s.zip/python/mozboot' % repo_rev 73 req = urlopen(url=url, timeout=30) 74 data = BytesIO(req.read()) 75 data.seek(0) 76 zip = zipfile.ZipFile(data, 'r') 77 for f in zip.infolist(): 78 # The paths are prefixed with the repo and revision name before the 79 # directory name. 80 offset = f.filename.find(REPOSITORY_PATH_PREFIX) + len(REPOSITORY_PATH_PREFIX) 81 name = f.filename[offset:] 82 83 # We only care about the Python modules. 84 if not name.startswith('mozboot/'): 85 continue 86 87 files[name] = zip.read(f) 88 89 # Retrieve distro script 90 url = repo_url + '/archive/%s.zip/third_party/python/distro/distro.py' % repo_rev 91 req = urlopen(url=url, timeout=30) 92 data = BytesIO(req.read()) 93 data.seek(0) 94 zip = zipfile.ZipFile(data, 'r') 95 files["distro.py"] = zip.read(zip.infolist()[0]) 96 97 else: 98 raise NotImplementedError('Not sure how to handle repo type.', repo_type) 99 100 return files 101 102 103def ensure_environment(repo_url=None, repo_rev=None, repo_type=None): 104 """Ensure we can load the Python modules necessary to perform bootstrap.""" 105 106 try: 107 from mozboot.bootstrap import Bootstrapper 108 return Bootstrapper 109 except ImportError: 110 # The first fallback is to assume we are running from a tree checkout 111 # and have the files in a sibling directory. 112 pardir = os.path.join(os.path.dirname(__file__), os.path.pardir) 113 include = os.path.normpath(pardir) 114 115 sys.path.append(include) 116 try: 117 from mozboot.bootstrap import Bootstrapper 118 return Bootstrapper 119 except ImportError: 120 sys.path.pop() 121 122 # The next fallback is to download the files from the source 123 # repository. 124 files = fetch_files(repo_url, repo_rev, repo_type) 125 126 # Install them into a temporary location. They will be deleted 127 # after this script has finished executing. 128 global TEMPDIR 129 TEMPDIR = tempfile.mkdtemp() 130 131 for relpath in files.keys(): 132 destpath = os.path.join(TEMPDIR, relpath) 133 destdir = os.path.dirname(destpath) 134 135 if not os.path.exists(destdir): 136 os.makedirs(destdir) 137 138 with open(destpath, 'wb') as fh: 139 fh.write(files[relpath]) 140 141 # This should always work. 142 sys.path.append(TEMPDIR) 143 from mozboot.bootstrap import Bootstrapper 144 return Bootstrapper 145 146 147def main(args): 148 parser = OptionParser() 149 parser.add_option('-r', '--repo-url', dest='repo_url', 150 default='https://hg.mozilla.org/mozilla-central/', 151 help='Base URL of source control repository where bootstrap files can ' 152 'be downloaded.') 153 parser.add_option('--repo-rev', dest='repo_rev', 154 default='default', 155 help='Revision of files in repository to fetch') 156 parser.add_option('--repo-type', dest='repo_type', 157 default='hgweb', 158 help='The type of the repository. This defines how we fetch file ' 159 'content. Like --repo, you should not need to set this.') 160 161 parser.add_option('--application-choice', dest='application_choice', 162 help='Pass in an application choice (see mozboot.bootstrap.APPLICATIONS) ' 163 'instead of using the default interactive prompt.') 164 parser.add_option('--vcs', dest='vcs', default=None, 165 help='VCS (hg or git) to use for downloading the source code, ' 166 'instead of using the default interactive prompt.') 167 parser.add_option('--no-interactive', dest='no_interactive', action='store_true', 168 help='Answer yes to any (Y/n) interactive prompts.') 169 parser.add_option('--debug', dest='debug', action='store_true', 170 help='Print extra runtime information useful for debugging and ' 171 'bug reports.') 172 173 options, leftover = parser.parse_args(args) 174 175 try: 176 try: 177 cls = ensure_environment(options.repo_url, options.repo_rev, 178 options.repo_type) 179 except Exception as e: 180 print('Could not load the bootstrap Python environment.\n') 181 print('This should never happen. Consider filing a bug.\n') 182 print('\n') 183 184 if options.debug: 185 # Raise full tracebacks during debugging and for bug reporting. 186 raise 187 188 print(e) 189 return 1 190 191 dasboot = cls(choice=options.application_choice, no_interactive=options.no_interactive, 192 vcs=options.vcs) 193 dasboot.bootstrap() 194 195 return 0 196 finally: 197 if TEMPDIR is not None: 198 shutil.rmtree(TEMPDIR) 199 200 201if __name__ == '__main__': 202 sys.exit(main(sys.argv)) 203