#!/usr/bin/env python import argparse import glob import json import os import shutil import sys import tempfile def find_root(): # expected path is in /packages/ top_dir = os.environ.get("CLOUD_INIT_TOP_D", None) if top_dir is None: top_dir = os.path.dirname( os.path.dirname(os.path.abspath(sys.argv[0]))) if os.path.isfile(os.path.join(top_dir, 'setup.py')): return os.path.abspath(top_dir) raise OSError(("Unable to determine where your cloud-init topdir is." " set CLOUD_INIT_TOP_D?")) if "avoid-pep8-E402-import-not-top-of-file": # Use the util functions from cloudinit sys.path.insert(0, find_root()) from cloudinit import templater from cloudinit import util # Map python requirements to package names. If a match isn't found # here, we assume 'python-'. PACKAGE_MAP = { 'redhat': { 'pyserial': 'pyserial', 'pyyaml': 'PyYAML', }, 'suse': { 'pyyaml': 'python-yaml', } } # Subdirectories of the ~/rpmbuild dir RPM_BUILD_SUBDIRS = ['BUILD', 'RPMS', 'SOURCES', 'SPECS', 'SRPMS'] def run_helper(helper, args=None, strip=True): if args is None: args = [] cmd = [util.abs_join(find_root(), 'tools', helper)] + args (stdout, _stderr) = util.subp(cmd) if strip: stdout = stdout.strip() return stdout def read_dependencies(): '''Returns the Python depedencies from requirements.txt. This explicitly removes 'argparse' from the list of requirements for python >= 2.7, because with 2.7 argparse became part of the standard library.''' stdout = run_helper('read-dependencies') return [p.lower().strip() for p in stdout.splitlines() if p != 'argparse' or (p == 'argparse' and sys.version_info[0:2] < (2, 7))] def translate_dependencies(deps, distro): '''Maps python requirements into package names. We assume python- for packages not listed explicitly in PACKAGE_MAP.''' return [PACKAGE_MAP[distro][req] if req in PACKAGE_MAP[distro] else 'python-%s' % req for req in deps] def read_version(): return json.loads(run_helper('read-version', ['--json'])) def generate_spec_contents(args, version_data, tmpl_fn, top_dir, arc_fn): # Tmpl params subs = {} if args.sub_release is not None: subs['subrelease'] = str(args.sub_release) else: subs['subrelease'] = "" subs['archive_name'] = arc_fn subs['source_name'] = os.path.basename(arc_fn).replace('.tar.gz', '') subs.update(version_data) # Map to known packages python_deps = read_dependencies() package_deps = translate_dependencies(python_deps, args.distro) subs['requires'] = package_deps if args.boot == 'sysvinit': subs['sysvinit'] = True else: subs['sysvinit'] = False if args.boot == 'systemd': subs['systemd'] = True else: subs['systemd'] = False subs['init_sys'] = args.boot subs['patches'] = [os.path.basename(p) for p in args.patches] return templater.render_from_file(tmpl_fn, params=subs) def main(): parser = argparse.ArgumentParser() parser.add_argument("-d", "--distro", dest="distro", help="select distro (default: %(default)s)", metavar="DISTRO", default='redhat', choices=('redhat', 'suse')) parser.add_argument('--srpm', help='Produce a source rpm', action='store_true') parser.add_argument("-b", "--boot", dest="boot", help="select boot type (default: %(default)s)", metavar="TYPE", default='sysvinit', choices=('sysvinit', 'systemd')) parser.add_argument("-v", "--verbose", dest="verbose", help=("run verbosely" " (default: %(default)s)"), default=False, action='store_true') parser.add_argument('-s', "--sub-release", dest="sub_release", metavar="RELEASE", help=("a 'internal' release number to concat" " with the bzr version number to form" " the final version number"), type=int, default=None) parser.add_argument("-p", "--patch", dest="patches", help=("include the following patch when building"), default=[], action='append') args = parser.parse_args() capture = True if args.verbose: capture = False workdir = None try: workdir = tempfile.mkdtemp(prefix='rpmbuild') os.environ['HOME'] = workdir topdir = os.path.join(workdir, 'rpmbuild') build_dirs = [os.path.join(topdir, dir) for dir in RPM_BUILD_SUBDIRS] util.ensure_dirs(build_dirs) version_data = read_version() # Archive the code archive_fn = "cloud-init-%s.tar.gz" % version_data['version_long'] real_archive_fn = os.path.join(topdir, 'SOURCES', archive_fn) archive_fn = run_helper( 'make-tarball', ['--long', '--output=' + real_archive_fn]) print("Archived the code in %r" % (real_archive_fn)) # Form the spec file to be used tmpl_fn = util.abs_join(find_root(), 'packages', args.distro, 'cloud-init.spec.in') contents = generate_spec_contents(args, version_data, tmpl_fn, topdir, os.path.basename(archive_fn)) spec_fn = util.abs_join(topdir, 'SPECS', 'cloud-init.spec') util.write_file(spec_fn, contents) print("Created spec file at %r" % (spec_fn)) for p in args.patches: util.copy(p, util.abs_join(topdir, 'SOURCES', os.path.basename(p))) # Now build it! print("Running 'rpmbuild' in %r" % (topdir)) if args.srpm: cmd = ['rpmbuild', '-bs', '--nodeps', spec_fn] else: cmd = ['rpmbuild', '-ba', spec_fn] util.subp(cmd, capture=capture) # Copy the items built to our local dir globs = [] globs.extend(glob.glob("%s/*.rpm" % (util.abs_join(topdir, 'RPMS', 'noarch')))) globs.extend(glob.glob("%s/*.rpm" % (util.abs_join(topdir, 'RPMS', 'x86_64')))) globs.extend(glob.glob("%s/*.rpm" % (util.abs_join(topdir, 'RPMS')))) globs.extend(glob.glob("%s/*.rpm" % (util.abs_join(topdir, 'SRPMS')))) for rpm_fn in globs: tgt_fn = util.abs_join(os.getcwd(), os.path.basename(rpm_fn)) shutil.move(rpm_fn, tgt_fn) print("Wrote out %s package %r" % (args.distro, tgt_fn)) finally: if workdir is not None: shutil.rmtree(workdir) return 0 if __name__ == '__main__': sys.exit(main())