| OLD | NEW |
| 1 #!/bin/bash | 1 #!/usr/bin/env python |
| 2 | 2 |
| 3 # Copyright (c) 2009-2010 The Chromium OS Authors. All rights reserved. | 3 # Copyright (c) 2011 The Chromium OS Authors. All rights reserved. |
| 4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
| 5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
| 6 | 6 |
| 7 # Prints the arguments and dies. | 7 """Build packages on a host machine, then install them on the local target. |
| 8 print_and_die() { | |
| 9 echo $* | |
| 10 exit 1 | |
| 11 } | |
| 12 | 8 |
| 13 # Set up PORTAGE variables to be rooted in /usr/local/portage. | 9 Contacts a devserver (trunk/src/platform/dev/devserver.py) and |
| 14 setup_portage_vars() { | 10 requests that it build a package, then performs a binary install of |
| 15 export PORTDIR=/usr/local/portage | 11 that package on the local machine. |
| 16 export PKGDIR=/usr/local/portage | 12 """ |
| 17 export DISTDIR=/usr/local/portage/distfiles | |
| 18 export PORTAGE_BINHOST="${DEVKIT_URL}/static/pkgroot/${BOARD_NAME}/packages" | |
| 19 export PORTAGE_TMPDIR=/tmp | |
| 20 export CONFIG_PROTECT="-*" | |
| 21 export FEATURES="-sandbox" | |
| 22 # Accept keywords only for stable ebuilds by default. | |
| 23 [ -z "$ACCEPT_KEYWORDS" ] && ACCEPT_KEYWORDS="arm x86 ~arm ~x86" | |
| 24 export ACCEPT_KEYWORDS | |
| 25 export ROOT=/ | |
| 26 } | |
| 27 | 13 |
| 28 # Prompts the user to change the root to /usr/local. | 14 import optparse |
| 29 change_portage_root() { | 15 import os |
| 30 read -p "Do you wish to install this program into /usr/local? (Y/n) " input | 16 import shutil |
| 31 if [[ ${input} = [Nn]* ]]; then | 17 import subprocess |
| 32 echo "Better safe than sorry." | 18 import sys |
| 33 exit 1 | 19 import urllib |
| 34 fi | 20 import urllib2 |
| 35 export ROOT=/usr/local | |
| 36 } | |
| 37 | 21 |
| 38 set -e | |
| 39 | 22 |
| 40 # Get variables for the devserver from the lsb-release file. | 23 class GMerger(object): |
| 41 DEVKIT_URL=$(grep ^CHROMEOS_DEVSERVER /etc/lsb-release | cut -d = -f 2-) | 24 """emerges a package from the devserver. |
| 42 BOARD_NAME=$(grep ^CHROMEOS_RELEASE_BOARD /etc/lsb-release | cut -d = -f 2-) | |
| 43 [ -z "${BOARD_NAME}" ] && print_and_die "No board in /etc/lsb-release" | |
| 44 [ -z "${DEVKIT_URL}" ] && print_and_die "No dev server url in /etc/lsb-release" | |
| 45 | 25 |
| 46 setup_portage_vars | 26 NB: Must be instantiated using with, e.g.: |
| 27 with GMerger(open('/etc/lsb-release').readlines()) as merger: |
| 28 in order to remount /tmp as executable. |
| 29 """ |
| 47 | 30 |
| 48 # Determine if we should send a build command to the devserver. | 31 def __init__(self, lsb_release_lines): |
| 49 BUILD=1 | 32 self.lsb_release = self.ParseLsbRelease(lsb_release_lines) |
| 50 if [ x$1 == x-n ]; then | 33 try: |
| 51 BUILD=0 | 34 self.devkit_url = self.lsb_release['CHROMEOS_DEVSERVER'] |
| 52 shift | 35 self.board_name = self.lsb_release['CHROMEOS_RELEASE_BOARD'] |
| 53 fi | 36 except KeyError, e: |
| 37 sys.exit('Could not find /etc/lsb_release value: ' + e) |
| 54 | 38 |
| 55 # Package name is the last argument. | 39 def RemountOrChangeRoot(self, environ): |
| 56 # TODO(sosa) - Support multiple packages. | 40 """Remount the root filesystem rw; install into /usr/local if this fails.""" |
| 57 PACKAGE_NAME=${!#} | 41 rc = subprocess.call(['mount', '-o', 'remount,rw', '/']) |
| 42 if rc == 0: |
| 43 return |
| 44 answer = raw_input( |
| 45 'Could not mount / as writable. Install into /usr/local? (Y/n)') |
| 46 if answer[0] not in 'Yy': |
| 47 sys.exit('Better safe than sorry.') |
| 48 environ['ROOT'] = '/usr/local' |
| 58 | 49 |
| 59 # If no package name is provided skip to emerge options. | 50 def ParseLsbRelease(self, lsb_release_lines): |
| 60 [[ ${PACKAGE_NAME} == -* ]] && BUILD=0 | 51 """Convert a list of KEY=VALUE lines to a dictionary.""" |
| 52 partitioned_lines = [line.rstrip().partition('=') |
| 53 for line in lsb_release_lines] |
| 54 return dict([(fields[0], fields[2]) for fields in partitioned_lines]) |
| 61 | 55 |
| 62 mount -o remount,rw / || change_portage_root | 56 def SetupPortageEnvironment(self, environ): |
| 57 """Setup portage to use stateful partition and fetch from dev server.""" |
| 58 environ.update({ |
| 59 'PORTDIR': '/usr/local/portage', |
| 60 'PKGDIR': '/usr/local/portage', |
| 61 'DISTDIR': '/usr/local/portage/distfiles', |
| 62 'PORTAGE_BINHOST': '%s/static/pkgroot/%s/packages' % ( |
| 63 self.devkit_url, self.board_name), |
| 64 'PORTAGE_TMPDIR': '/tmp', |
| 65 'CONFIG_PROTECT': '-*', |
| 66 'FEATURES': '-sandbox', |
| 67 'ACCEPT_KEYWORDS': 'arm x86 ~arm ~x86', |
| 68 'ROOT': '/', |
| 69 }) |
| 63 | 70 |
| 64 # Re-mount /tmp as exec. | 71 def GeneratePackageRequest(self, package_name): |
| 65 mount -o remount,exec /tmp | 72 """Build the POST string that conveys our options to the devserver.""" |
| 66 trap "mount -o remount,noexec /tmp" EXIT | 73 post_data = {'board': self.board_name, |
| 74 'pkg': package_name, |
| 75 'use': FLAGS.use, |
| 76 'accept_stable': FLAGS.accept_stable, |
| 77 } |
| 78 post_data = dict([(key, value) for (key, value) in post_data.iteritems() |
| 79 if value]) |
| 80 return urllib.urlencode(post_data) |
| 67 | 81 |
| 68 # Delete the local binary package cache. | 82 def RequestPackageBuild(self, package_name): |
| 69 rm -rf "${PKGDIR}/packages" | 83 """Contacts devserver to request a build.""" |
| 84 try: |
| 85 result = urllib2.urlopen(self.devkit_url + '/build', |
| 86 data=self.GeneratePackageRequest(package_name)) |
| 87 print result.read() |
| 88 result.close() |
| 70 | 89 |
| 71 if [ ${BUILD} == 1 ]; then | 90 except urllib2.HTTPError, e: |
| 72 echo "Building ${PACKAGE_NAME}" | 91 # The exception includes the content, which is the error mesage |
| 73 ESCAPED_PACKAGE=$(python -c \ | 92 sys.exit(e.read()) |
| 74 "import urllib; print urllib.quote('''${PACKAGE_NAME}''')") | |
| 75 ESCAPED_BOARD=$(python -c \ | |
| 76 "import urllib; print urllib.quote('''${BOARD_NAME}''')") | |
| 77 | 93 |
| 78 wget $DEVKIT_URL/build \ | |
| 79 --post-data="pkg=${ESCAPED_PACKAGE}&board=${ESCAPED_BOARD}" | |
| 80 fi | |
| 81 | 94 |
| 82 echo "Emerging ${PACKAGE_NAME}" | 95 def main(): |
| 83 emerge --getbinpkgonly --usepkgonly "$@" | 96 global FLAGS |
| 97 parser = optparse.OptionParser(usage='usage: %prog [options] package_name') |
| 98 parser.add_option('--accept_stable', |
| 99 action='store_true', dest='accept_stable', default=False, |
| 100 help=('Build even if a cros_workon package is not ' |
| 101 'using the live package')) |
| 102 parser.add_option('-n', '--no_devserver', |
| 103 action='store_false', dest='call_devserver', default=True, |
| 104 help='Do not actually ask the server to build') |
| 105 parser.add_option('--use', '--USE', |
| 106 dest='use', default=None, |
| 107 help='USE flags to pass to emerge on the server') |
| 108 |
| 109 (FLAGS, remaining_arguments) = parser.parse_args() |
| 110 if len(remaining_arguments) != 1: |
| 111 parser.print_help() |
| 112 sys.exit('Need exactly one package name') |
| 113 |
| 114 package_name = remaining_arguments[0] |
| 115 |
| 116 try: |
| 117 subprocess.check_call(['mount', '-o', 'remount,exec', '/tmp']) |
| 118 merger = GMerger(open('/etc/lsb-release').readlines()) |
| 119 merger.SetupPortageEnvironment(os.environ) |
| 120 merger.RemountOrChangeRoot(os.environ) |
| 121 if FLAGS.call_devserver: |
| 122 merger.RequestPackageBuild(package_name) |
| 123 else: |
| 124 print 'Not requesting fresh build on server---installing whatever we find' |
| 125 |
| 126 print 'Emerging ', package_name |
| 127 subprocess.check_call([ |
| 128 'emerge', '--getbinpkgonly', '--usepkgonly', package_name]) |
| 129 finally: |
| 130 subprocess.call(['mount', '-o', 'remount,noexec', '/tmp']) |
| 131 |
| 132 |
| 133 if __name__ == '__main__': |
| 134 main() |
| OLD | NEW |