import os, glob from types import ListType from distutils.command import bdist_rpm from distutils.core import DEBUG from distutils.file_util import write_file from distutils.spawn import find_executable # Python 2.3+ feature try: enumerate except NameError: enumerate = lambda it: zip(xrange(len(it)), it) class BDistRpm(bdist_rpm.bdist_rpm): command_name = 'bdist_rpm' # insert the 'patch_files' options in the appropriate place in the # option map (to keep --help sane-looking) user_options = [ opt[0] for opt in bdist_rpm.bdist_rpm.user_options ] index = user_options.index('doc-files=') user_options = bdist_rpm.bdist_rpm.user_options[:] user_options.insert(index, ('patch-files=', None, 'list of patches (space or comma-separated)')) del index def initialize_options(self): bdist_rpm.bdist_rpm.initialize_options(self) self.patch_files = [] self.keep_temp = None return def finalize_options(self): self.set_undefined_options('bdist', ('keep_temp', 'keep_temp')) bdist_rpm.bdist_rpm.finalize_options(self) return def finalize_package_data(self): bdist_rpm.bdist_rpm.finalize_package_data(self) self.ensure_string_list('patch_files') return def run (self): if DEBUG: print "before _get_package_data():" print "vendor =", self.vendor print "packager =", self.packager print "doc_files =", self.doc_files print "changelog =", self.changelog # make directories if self.spec_only: spec_dir = self.dist_dir self.mkpath(spec_dir) else: rpm_dir = {} for d in ('SOURCES', 'SPECS', 'BUILD', 'RPMS', 'SRPMS'): rpm_dir[d] = os.path.join(self.rpm_base, d) self.mkpath(rpm_dir[d]) spec_dir = rpm_dir['SPECS'] # Spec file goes into 'dist_dir' if '--spec-only specified', # build/rpm. otherwise. spec_path = os.path.join(spec_dir, "%s.spec" % self.distribution.get_name()) self.execute(write_file, (spec_path, self._make_spec_file()), "writing '%s'" % spec_path) if self.spec_only: # stop if requested return # Make a source distribution and copy to SOURCES directory with # optional icon. sdist = self.reinitialize_command('sdist') if self.use_bzip2: sdist.formats = ['bztar'] else: sdist.formats = ['gztar'] self.run_command('sdist') source = sdist.get_archive_files()[0] source_dir = rpm_dir['SOURCES'] self.copy_file(source, source_dir) # Add any user-defined patches for patch in self.patch_files: self.copy_file(patch, source_dir) if self.icon: if os.path.exists(self.icon): self.copy_file(self.icon, source_dir) else: raise DistutilsFileError, \ "icon file '%s' does not exist" % self.icon # build package self.announce("building RPMs") if find_executable('rpmbuild'): rpm_cmd = ['rpmbuild'] else: rpm_cmd = ['rpm'] if self.source_only: # what kind of RPMs? rpm_cmd.append('-bs') elif self.binary_only: rpm_cmd.append('-bb') else: rpm_cmd.append('-ba') if self.rpm3_mode: rpm_cmd.extend(['--define', '_topdir %s/%s' % (os.getcwd(), self.rpm_base), ]) if not self.keep_temp: rpm_cmd.append('--clean') rpm_cmd.append(spec_path) self.spawn(rpm_cmd) # XXX this is a nasty hack -- we really should have a proper way to # find out the names of the RPM files created. if not self.dry_run: if not self.binary_only: srpms = glob.glob(os.path.join(rpm_dir['SRPMS'], "*.rpm")) for srpm in srpms: self.move_file(srpm, self.dist_dir) if not self.source_only: rpms = glob.glob(os.path.join(rpm_dir['RPMS'], "*/*.rpm")) for rpm in rpms: self.move_file(rpm, self.dist_dir) return def _make_spec_file(self): """Generate the text of an RPM spec file and return it as a list of strings (one per line). """ # definitions and headers pyver = "python -c 'import sys;print sys.version[:3]'" spec_file = [ '%{expand: %%define pyver %(' + pyver + ')}', '%define pylib %{_libdir}/python%{pyver}/site-packages', '', '%define name ' + self.distribution.get_name(), '%define version ' + self.distribution.get_version(), '%define release ' + self.release, '', 'Summary: ' + self.distribution.get_description(), ] # put locale summaries into spec file # XXX not supported for now (hard to put a dictionary # in a config file -- arg!) #for locale in self.summaries.keys(): # spec_file.append('Summary(%s): %s' % (locale, # self.summaries[locale])) spec_file.extend([ 'Name: %{name}', 'Version: %{version}', 'Release: %{release}',]) # XXX yuck! this filename is available from the "sdist" command, # but only after it has run: and we create the spec file before # running "sdist", in case of --spec-only. if self.use_bzip2: spec_file.append('Source0: %{name}-%{version}.tar.bz2') else: spec_file.append('Source0: %{name}-%{version}.tar.gz') for patch_info in enumerate(self.patch_files): spec_file.append('Patch%d: %s' % patch_info) spec_file.extend([ 'License: ' + self.distribution.get_license(), 'Group: ' + self.group, 'BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot', 'Prefix: %{_prefix}', ]) # noarch if no extension modules if not self.distribution.has_ext_modules(): spec_file.append('BuildArchitectures: noarch') for field in ('Vendor', 'Packager', 'Provides', 'Requires', 'Conflicts', 'Obsoletes', ): val = getattr(self, field.lower()) if type(val) is ListType: spec_file.append('%s: %s' % (field, ''.join(val))) elif val is not None: spec_file.append('%s: %s' % (field, val)) if self.distribution.get_url() != 'UNKNOWN': spec_file.append('Url: ' + self.distribution.get_url()) if self.distribution_name: spec_file.append('Distribution: ' + self.distribution_name) if self.build_requires: spec_file.append('BuildRequires: ' + ''.join(self.build_requires)) if self.icon: spec_file.append('Icon: ' + os.path.basename(self.icon)) spec_file.extend([ '', '%description', self.distribution.get_long_description() ]) # Add the sub-package information distributions = [] for pkg in self.distribution.sub_packages: dist = self.distribution.get_package_distribution(pkg) spec_file.extend([ '', '%package ' + dist.get_name(), 'Summary: ' + dist.get_description(), 'Group: ' + self.group, ]) for dep in dist.dependencies: spec_file.append( 'Requires: %%{name}-%s = %%{version}-%%{release}' % dep) spec_file.extend([ '', '%description ' + dist.get_name(), dist.get_long_description(), ]) # Save it for later use in the %files section distributions.append(dist) # Add the docs sub-package if self.distribution.has_docs(): spec_file.extend([ '', '%package Docs', 'Summary: Documentation for %{name}', 'Group: ' + self.group, '', '%description Docs', ('The %{name}-Docs package contains documentation on the ' '%{name} libraries. The documentation is provided in ASCII ' 'text files and in HTML files.'), ]) # Add the tests sub-package if self.distribution.has_devel(): spec_file.extend([ '', '%package Tests', 'Summary: The test suite for %{name}', 'Group: ' + self.group, ]) for pkg in self.distribution.sub_packages: spec_file.append( 'Requires: %%{name}-%s = %%{version}-%%{release}' % pkg) spec_file.extend([ '', '%description Tests', ('This package includes various tests for %{name}, including ' 'regression tests and benchmarks.'), ]) # rpm scripts # %prep spec_file.extend([ '', '%prep', '%setup', ]) # %build if self.use_rpm_opt_flags: python = 'env CFLAGS="$RPM_OPT_FLAGS" ' + self.python else: python = self.python spec_file.extend([ '', '%build', python + (' setup.py config \\\n' '\t--prefix=%{_prefix} \\\n' '\t--exec-prefix=%{_exec_prefix} \\\n' '\t--pythonlibdir=%{pylib} \\\n' '\t--bindir=%{_bindir} \\\n' '\t--datadir=%{_datadir}/%{name} \\\n' '\t--sysconfdir=%{_sysconfdir} \\\n' '\t--localstatedir=%{_localstatedir}/%{name} \\\n' '\t--libdir=%{_libdir}/%{name} \\\n' '\t--docdir=%{_docdir}'), '%s setup.py build' % python, ]) # %install spec_file.extend([ '', '%install', '%s setup.py install --root=$RPM_BUILD_ROOT' % self.python, ]) # %clean spec_file.extend([ '', '%clean', 'rm -rf $RPM_BUILD_ROOT', ]) # %pre # %post # %preun # %postun # %verifyscript # %files def finalize_install_for_rpm(cmd): install = cmd.reinitialize_command('install') install.install_lib = '%{pylib}' install.install_scripts = '%{_bindir}' install.install_data = '%{_datadir}/%{name}' install.install_sysconf = '%{_sysconfdir}' install.install_localstate = '%{_localstatedir}/%{name}' install.install_docs = '%{_docdir}' install.install_devel = '%{_libdir}/%{name}' install.with_docs = 1 install.ensure_finalized() return for dist in distributions: spec_file.extend([ '', '%%files %s' % dist.get_name(), '%defattr(-,root,root)', ]) finalize_install_for_rpm(dist) install_sysconf = dist.get_command_obj('install_sysconf') install_sysconf.ensure_finalized() for fname in install_sysconf.get_outputs(): spec_file.append('%config ' + fname) for command in ('install_lib', 'install_scripts', 'install_data', 'install_localstate'): cmd_obj = dist.get_command_obj(command) cmd_obj.ensure_finalized() for fname in cmd_obj.get_outputs(): spec_file.append(fname) finalize_install_for_rpm(self) if self.distribution.has_docs(): spec_file.extend([ '', '%files Docs', '%defattr(-,root,root)', ]) install_docs = self.get_finalized_command('install_docs') for fname in install_docs.get_outputs(): spec_file.append(fname) if self.distribution.has_devel(): spec_file.extend([ '', '%files Tests', '%defattr(-,root,root)', ]) install_devel = self.get_finalized_command('install_devel') for fname in install_devel.get_outputs(): spec_file.append(fname) return spec_file