mirror of
https://git.launchpad.net/ubuntu-dev-tools
synced 2025-03-13 16:11:15 +00:00
353 lines
11 KiB
Python
Executable File
353 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/'):
|
|
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)
|
|
|
|
return '%s /usr/sbin/%s %s' % (self.auth, self.builder,
|
|
' '.join(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:
|
|
app.extra_components = False
|
|
args.remove('mainonly')
|
|
|
|
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((arg for arg in args if arg)).replace(
|
|
' --debug-echo', '')
|
|
|
|
if __name__ == '__main__':
|
|
|
|
try:
|
|
main()
|
|
except KeyboardInterrupt:
|
|
print 'Manually aborted.'
|
|
sys.exit(1)
|