blob: 363233f187c91e1cd538939322e8fb8624937be4 [file] [log] [blame]
#!/usr/bin/env python
# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Build packages on a host machine, then install them on the local target.
Contacts a devserver (trunk/src/platform/dev/devserver.py) and
requests that it build a package, then performs a binary install of
that package on the local machine.
"""
import optparse
import os
import shutil
import subprocess
import sys
import urllib
import urllib2
class GMerger(object):
"""emerges a package from the devserver.
NB: Must be instantiated using with, e.g.:
with GMerger(open('/etc/lsb-release').readlines()) as merger:
in order to remount /tmp as executable.
"""
def __init__(self, lsb_release_lines):
self.lsb_release = self.ParseLsbRelease(lsb_release_lines)
try:
self.devkit_url = self.lsb_release['CHROMEOS_DEVSERVER']
self.board_name = self.lsb_release['CHROMEOS_RELEASE_BOARD']
except KeyError, e:
sys.exit('Could not find /etc/lsb_release value: ' + e)
def RemountOrChangeRoot(self, environ):
"""Remount the root filesystem rw; install into /usr/local if this fails."""
rc = subprocess.call(['mount', '-o', 'remount,rw', '/'])
if rc == 0:
return
answer = raw_input(
'Could not mount / as writable. Install into /usr/local? (Y/n)')
if answer[0] not in 'Yy':
sys.exit('Better safe than sorry.')
environ['ROOT'] = '/usr/local'
def ParseLsbRelease(self, lsb_release_lines):
"""Convert a list of KEY=VALUE lines to a dictionary."""
partitioned_lines = [line.rstrip().partition('=')
for line in lsb_release_lines]
return dict([(fields[0], fields[2]) for fields in partitioned_lines])
def SetupPortageEnvironment(self, environ):
"""Setup portage to use stateful partition and fetch from dev server."""
environ.update({
'PORTDIR': '/usr/local/portage',
'PKGDIR': '/usr/local/portage',
'DISTDIR': '/usr/local/portage/distfiles',
'PORTAGE_BINHOST': '%s/static/pkgroot/%s/packages' % (
self.devkit_url, self.board_name),
'PORTAGE_TMPDIR': '/tmp',
'CONFIG_PROTECT': '-*',
'FEATURES': '-sandbox',
'ACCEPT_KEYWORDS': 'arm x86 ~arm ~x86',
'ROOT': '/',
})
def GeneratePackageRequest(self, package_name):
"""Build the POST string that conveys our options to the devserver."""
post_data = {'board': self.board_name,
'pkg': package_name,
'use': FLAGS.use,
'accept_stable': FLAGS.accept_stable,
}
post_data = dict([(key, value) for (key, value) in post_data.iteritems()
if value])
return urllib.urlencode(post_data)
def RequestPackageBuild(self, package_name):
"""Contacts devserver to request a build."""
try:
result = urllib2.urlopen(self.devkit_url + '/build',
data=self.GeneratePackageRequest(package_name))
print result.read()
result.close()
except urllib2.HTTPError, e:
# The exception includes the content, which is the error mesage
sys.exit(e.read())
def main():
global FLAGS
parser = optparse.OptionParser(usage='usage: %prog [options] package_name')
parser.add_option('--accept_stable',
action='store_true', dest='accept_stable', default=False,
help=('Build even if a cros_workon package is not '
'using the live package'))
parser.add_option('-n', '--no_devserver',
action='store_false', dest='call_devserver', default=True,
help='Do not actually ask the server to build')
parser.add_option('--use', '--USE',
dest='use', default=None,
help='USE flags to pass to emerge on the server')
(FLAGS, remaining_arguments) = parser.parse_args()
if len(remaining_arguments) != 1:
parser.print_help()
sys.exit('Need exactly one package name')
package_name = remaining_arguments[0]
try:
subprocess.check_call(['mount', '-o', 'remount,exec', '/tmp'])
merger = GMerger(open('/etc/lsb-release').readlines())
merger.SetupPortageEnvironment(os.environ)
merger.RemountOrChangeRoot(os.environ)
if FLAGS.call_devserver:
merger.RequestPackageBuild(package_name)
else:
print 'Not requesting fresh build on server---installing whatever we find'
print 'Emerging ', package_name
subprocess.check_call([
'emerge', '--getbinpkgonly', '--usepkgonly', package_name])
finally:
subprocess.call(['mount', '-o', 'remount,noexec', '/tmp'])
if __name__ == '__main__':
main()