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 |