mirror of
https://git.launchpad.net/ubuntu-dev-tools
synced 2025-03-13 08:01:09 +00:00
- Fix regression where pbuilder would get an empty string as first positional argument. - Update --debug-echo so that it doesn't hide empty parameters (now that they are wrapped around quotes they are significant).
379 lines
11 KiB
Python
Executable File
379 lines
11 KiB
Python
Executable File
#! /usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
#
|
|
# Copyright (C) 2007-2010 Siegfried-A. Gevatter <rainct@ubuntu.com>
|
|
# With some changes by Iain Lane <iain@orangesquash.org.uk>
|
|
# Based upon pbuilder-dist-simple by Jamin Collins and Jordan Mantha.
|
|
#
|
|
# ##################################################################
|
|
#
|
|
# This program is free software; you can redistribute it and/or
|
|
# modify it under the terms of the GNU General Public License
|
|
# as published by the Free Software Foundation; either version 2
|
|
# of the License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# See file /usr/share/common-licenses/GPL for more details.
|
|
#
|
|
# ##################################################################
|
|
#
|
|
# This script is a wrapper to be able to easily use pbuilder/cowbuilder for
|
|
# different distributions (eg, Gutsy, Hardy, Debian unstable, etc).
|
|
#
|
|
# You can create symlinks to a pbuilder-dist executable to get different
|
|
# configurations. For example, a symlink called pbuilder-hardy will assume
|
|
# that the target distribution is always meant to be Ubuntu Hardy.
|
|
|
|
import sys
|
|
import os
|
|
|
|
import ubuntutools.misc
|
|
|
|
debian_distros = ['etch', 'lenny', 'squeeze', 'sid', 'stable', \
|
|
'testing', 'unstable', 'experimental']
|
|
|
|
class pbuilder_dist:
|
|
|
|
def __init__(self, builder):
|
|
|
|
# Base directory where pbuilder will put all the files it creates.
|
|
self.base = None
|
|
|
|
# Name of the operation which pbuilder should perform.
|
|
self.operation = None
|
|
|
|
# Wheter additional components should be used or not. That is,
|
|
# 'universe' and 'multiverse' for Ubuntu chroots and 'contrib'
|
|
# and 'non-free' for Debian.
|
|
self.extra_components = True
|
|
|
|
# File where the log of the last operation will be saved.
|
|
self.logfile = None
|
|
|
|
# System architecture
|
|
self.system_architecture = None
|
|
|
|
# Build architecture
|
|
self.build_architecture = None
|
|
|
|
# System's distribution
|
|
self.system_distro = None
|
|
|
|
# Target distribution
|
|
self.target_distro = None
|
|
|
|
# This is an identificative string which will either take the form
|
|
# 'distribution' or 'distribution-architecture'.
|
|
self.chroot_string = None
|
|
|
|
# Authentication method
|
|
self.auth = 'sudo'
|
|
|
|
# Builder
|
|
self.builder = builder
|
|
|
|
# Ensure that the used builder is installed
|
|
for file in os.environ['PATH'].split(':'):
|
|
if os.path.exists(os.path.join(file, builder)):
|
|
builder = ''
|
|
break
|
|
if builder:
|
|
print 'Error: Could not find "%s".' % builder
|
|
sys.exit(1)
|
|
|
|
##############################################################
|
|
|
|
self.base = os.path.expanduser(os.environ.get('PBUILDFOLDER', '~/pbuilder/'))
|
|
|
|
if not os.path.exists(self.base):
|
|
os.makedirs(self.base)
|
|
|
|
if 'PBUILDAUTH' in os.environ:
|
|
self.auth = os.environ['PBUILDAUTH']
|
|
|
|
self.system_architecture = ubuntutools.misc.host_architecture()
|
|
self.system_distro = ubuntutools.misc.system_distribution()
|
|
if not self.system_architecture or not self.system_distro:
|
|
exit(1)
|
|
|
|
self.target_distro = self.system_distro
|
|
|
|
##############################################################
|
|
|
|
def __getitem__(self, name):
|
|
|
|
return getattr(self, name)
|
|
|
|
def set_target_distro(self, distro):
|
|
""" pbuilder_dist.set_target_distro(distro) -> None
|
|
|
|
Check if the given target distribution name is correct, if it
|
|
isn't know to the system ask the user for confirmation before
|
|
proceeding, and finally either save the value into the appropiate
|
|
variable or finalize pbuilder-dist's execution.
|
|
|
|
"""
|
|
|
|
if not distro.isalpha():
|
|
print 'Error: «%s» is an invalid distribution codename.' % distro
|
|
sys.exit(1)
|
|
|
|
if not os.path.isfile(os.path.join('/usr/share/debootstrap/scripts/', distro)):
|
|
if os.path.isdir('/usr/share/debootstrap/scripts/'):
|
|
# Debian experimental doesn't have a debootstrap file but
|
|
# should work nevertheless.
|
|
if distro not in debian_distros:
|
|
answer = ask('Warning: Unknown distribution «%s». Do you ' \
|
|
'want to continue [y/N]? ' % distro)
|
|
if answer not in ('y', 'Y'):
|
|
sys.exit(0)
|
|
else:
|
|
print 'Please install package "debootstrap".'
|
|
sys.exit(1)
|
|
|
|
self.target_distro = distro
|
|
|
|
def set_operation(self, operation):
|
|
""" pbuilder_dist.set_operation -> None
|
|
|
|
Check if the given string is a valid pbuilder operation and
|
|
depending on this either save it into the appropiate variable
|
|
or finalize pbuilder-dist's execution.
|
|
|
|
"""
|
|
|
|
arguments = ('create', 'update', 'build', 'clean', 'login', 'execute')
|
|
|
|
if operation not in arguments:
|
|
if operation.endswith('.dsc'):
|
|
if os.path.isfile(operation):
|
|
self.operation = 'build'
|
|
return [operation]
|
|
else:
|
|
print 'Error: Could not find file «%s».' % operation
|
|
sys.exit(1)
|
|
else:
|
|
print 'Error: «%s» is not a recognized argument.' % operation
|
|
print 'Please use one of those: ' + ', '.join(arguments) + '.'
|
|
sys.exit(1)
|
|
else:
|
|
self.operation = operation
|
|
return []
|
|
|
|
def get_command(self, remaining_arguments = None):
|
|
""" pbuilder_dist.get_command -> string
|
|
|
|
Generate the pbuilder command which matches the given configuration
|
|
and return it as a string.
|
|
|
|
"""
|
|
|
|
if not self.build_architecture:
|
|
self.chroot_string = self.target_distro
|
|
self.build_architecture = self.system_architecture
|
|
else:
|
|
self.chroot_string = '%(target_distro)s-%(build_architecture)s' % self
|
|
|
|
prefix = os.path.join(self.base, self.chroot_string)
|
|
result = '%s_result/' % prefix
|
|
|
|
if not self.logfile and self.operation != 'login':
|
|
self.logfile = os.path.normpath('%s/last_operation.log' % result)
|
|
|
|
if not os.path.isdir(result):
|
|
# Create the results directory, if it doesn't exist.
|
|
os.makedirs(result)
|
|
|
|
if self.builder == 'pbuilder':
|
|
base = '--basetgz "%s-base.tgz"' % prefix
|
|
elif self.builder == 'cowbuilder':
|
|
base = '--basepath "%s-base.cow"' % prefix
|
|
else:
|
|
print 'Error: Unrecognized builder "%s".' % self.builder
|
|
sys.exit(1)
|
|
|
|
arguments = [
|
|
'--%s' % self.operation,
|
|
base,
|
|
'--distribution "%(target_distro)s"' % self,
|
|
'--buildresult "%s"' % result,
|
|
'--aptcache "/var/cache/apt/archives/"',
|
|
'--override-config',
|
|
]
|
|
|
|
if self.logfile:
|
|
arguments.append('--logfile %s' % self.logfile)
|
|
|
|
if os.path.exists('/var/cache/archive/'):
|
|
arguments.append('--bindmounts "/var/cache/archive/"')
|
|
|
|
localrepo = '/var/cache/archive/%(target_distro)s' % self
|
|
if os.path.exists(localrepo):
|
|
arguments.append('--othermirror ' +\
|
|
'"deb file:///var/cache/archive/ %(target_distro)s/"' % self)
|
|
|
|
if self.target_distro in debian_distros:
|
|
arguments.append('--mirror "ftp://ftp.debian.org/debian"')
|
|
components = 'main'
|
|
if self.extra_components:
|
|
components += ' contrib non-free'
|
|
else:
|
|
if self.build_architecture in ('amd64','i386'):
|
|
arguments.append('--mirror "http://archive.ubuntu.com/ubuntu/"')
|
|
elif self.build_architecture == 'powerpc' and self.target_distro == 'dapper':
|
|
arguments.append('--mirror "http://archive.ubuntu.com/ubuntu/"')
|
|
else:
|
|
arguments.append('--mirror "http://ports.ubuntu.com/ubuntu-ports/"')
|
|
components = 'main restricted'
|
|
if self.extra_components:
|
|
components += ' universe multiverse'
|
|
|
|
arguments.append('--components "%s"' % components)
|
|
|
|
if self.build_architecture != self.system_architecture:
|
|
arguments.append('--debootstrapopts --arch="%(build_architecture)s"' % self)
|
|
|
|
apt_conf_dir = os.path.join(self.base, 'etc/%(target_distro)s/apt.conf' % self)
|
|
if os.path.exists(apt_conf_dir):
|
|
arguments.append('--aptconfdir "%s"' % apt_conf_dir)
|
|
|
|
# Append remaining arguments
|
|
if remaining_arguments:
|
|
arguments.extend(remaining_arguments)
|
|
|
|
def quote(argument):
|
|
""" quote(argument) -> string
|
|
|
|
Try to guess any missing quotes around the arguments so that
|
|
their meaning doesn't get lost (see LP: #398989).
|
|
|
|
"""
|
|
|
|
if argument.startswith('--'):
|
|
if '=' in argument:
|
|
return '%s="%s"' % tuple(argument.split('=', 1))
|
|
return argument
|
|
return '"%s"' % argument
|
|
|
|
return '%s /usr/sbin/%s %s' % (self.auth, self.builder,
|
|
' '.join(map(quote, arguments)))
|
|
|
|
def ask(question):
|
|
""" ask(question) -> string
|
|
|
|
Ask the given question and return the answer. Also catch
|
|
KeyboardInterrupt (Ctrl+C) and EOFError (Ctrl+D) exceptions and
|
|
immediately return None if one of those is found.
|
|
|
|
"""
|
|
|
|
try:
|
|
answer = raw_input(question)
|
|
except (KeyboardInterrupt, EOFError):
|
|
print
|
|
answer = None
|
|
|
|
return answer
|
|
|
|
def help(exit_code = 0):
|
|
""" help() -> None
|
|
|
|
Print a help message for pbuilder-dist, and exit with the given code.
|
|
|
|
"""
|
|
|
|
print 'See man pbuilder-dist for more information.'
|
|
|
|
sys.exit(exit_code)
|
|
|
|
def main():
|
|
""" main() -> None
|
|
|
|
This is pbuilder-dist's main function. It creates a pbuilder_dist
|
|
object, modifies all necessary settings taking data from the
|
|
executable's name and command line options and finally either ends
|
|
the script and runs pbuilder itself or exists with an error message.
|
|
|
|
"""
|
|
|
|
script_name = os.path.basename(sys.argv[0])
|
|
parts = script_name.split('-')
|
|
|
|
# Copy arguments into another list for save manipulation
|
|
args = sys.argv[1:]
|
|
|
|
if '-' in script_name and (parts[0] != 'pbuilder' and \
|
|
parts[0] != 'cowbuilder') or len(parts) > 3:
|
|
print 'Error: «%s» is not a valid name for a «pbuilder-dist» executable.' % script_name
|
|
sys.exit(1)
|
|
|
|
if len(args) < 1:
|
|
print 'Insufficient number of arguments.'
|
|
help(1)
|
|
|
|
if args[0] in ('-h', '--help', 'help'):
|
|
help(0)
|
|
|
|
app = pbuilder_dist(parts[0])
|
|
|
|
if len(parts) > 1 and parts[1] != 'dist' and '.' not in parts[1]:
|
|
app.set_target_distro(parts[1])
|
|
else:
|
|
app.set_target_distro(args.pop(0))
|
|
|
|
if len(parts) > 2:
|
|
requested_arch = parts[2]
|
|
elif len(args) > 0 and args[0] in ("alpha", "amd64", "arm", "armeb",
|
|
"armel", "i386", "lpia", "m68k", "mips", "mipsel", "powerpc", "ppc64",
|
|
"sh4", "sh4eb", "sparc", "sparc64"):
|
|
requested_arch = args.pop(0)
|
|
else:
|
|
requested_arch = None
|
|
|
|
if requested_arch:
|
|
app.build_architecture = requested_arch
|
|
# For some foreign architectures we need to use qemu
|
|
if requested_arch != app.system_architecture and (app.system_architecture,
|
|
requested_arch) not in [("amd64", "i386"), ("amd64", "lpia"),
|
|
("arm", "armel"), ("armel", "arm"), ("i386", "lpia"), ("lpia", "i386"),
|
|
("powerpc", "ppc64"), ("ppc64", "powerpc"), ("sparc", "sparc64"),
|
|
("sparc64", "sparc")]:
|
|
args.append('--debootstrap qemu-debootstrap')
|
|
|
|
if 'mainonly' in sys.argv or '--main-only' in sys.argv:
|
|
app.extra_components = False
|
|
if 'mainonly' in sys.argv:
|
|
args.remove('mainonly')
|
|
else:
|
|
args.remove('--main-only')
|
|
|
|
if len(args) < 1:
|
|
print 'Insufficient number of arguments.'
|
|
help(1)
|
|
|
|
# Parse the operation
|
|
args = app.set_operation(args.pop(0)) + args
|
|
|
|
if app.operation == 'build' and not '.dsc' in ' '.join(args):
|
|
print 'Error: You have to specify a .dsc file if you want to build.'
|
|
sys.exit(1)
|
|
|
|
# Execute the pbuilder command
|
|
if not '--debug-echo' in args:
|
|
sys.exit(os.system(app.get_command(args)))
|
|
else:
|
|
print app.get_command((args)).replace(
|
|
' --debug-echo', '')
|
|
|
|
if __name__ == '__main__':
|
|
|
|
try:
|
|
main()
|
|
except KeyboardInterrupt:
|
|
print 'Manually aborted.'
|
|
sys.exit(1)
|