"""distutils.command.bdist_rpm Implements the Distutils 'bdist_rpm' command (create RPM source and binary distributions.""" # created 2000/04/25, by Harry Henry Gebel __revision__ = "$Id: bdist_dumb.py,v 1.3 2000/04/22 02:51:25 gward Exp $" from os.path import exists, basename import os from distutils.core import Command from distutils.util import mkpath, write_file, copy_file from distutils.errors import * from string import join, lower from types import StringType, DictType, LongType, FloatType, IntType, \ ListType, TupleType class bdist_rpm (Command): description = "create an RPM distribution" user_options = [ ('spec-only', None, "Only regenerate spec file"), ('source-only', None, "Only generate source RPM"), ('binary-only', None, "Only generate binary RPM"), ('use-bzip2', None, "Use bzip2 instead of gzip to create source distribution"), ('no-clean', None, "Do not clean RPM build directory"), ('no-rpm-opt-flags', None, "Do not pass any RPM CFLAGS to compiler") ] def initialize_options (self): self.spec_only = None self.binary_only = None self.source_only = None self.use_bzip2 = None self.no_clean = None self.no_rpm_opt_flags = None # initialize_options() def finalize_options (self): if os.name != 'posix': raise DistutilsPlatformError, \ ("don't know how to create RPM " "distributions on platform %s" % os.name) if self.binary_only and self.source_only: raise DistutilsOptionsError, \ "Cannot supply both '--source-only' and '--binary-only'" # don't pass CFLAGS to pure python distributions if not self.distribution.has_ext_modules(): self.no_rpm_opt_flags = 1 # finalize_options() def run (self): self._get_package_data() # get packaging info # make directories if self.spec_only: self.execute(mkpath, ('redhat',), "Created './redhat' directory") else: self.execute(mkpath, ('build/rpm/SOURCES',), "Created RPM source directory") self.execute(mkpath, ('build/rpm/SPECS',), "Created RPM source directory") self.execute(mkpath, ('build/rpm/BUILD',), "Created RPM source directory") self.execute(mkpath, ('build/rpm/RPMS',), "Created RPM source directory") self.execute(mkpath, ('build/rpm/SRPMS',), "Created RPM source directory") # spec file goes into .redhat directory if '--spec-only specified', # into build/rpm/spec otherwisu if self.spec_only: spec_path = './redhat/%s.spec' % self.distribution.get_name() else: spec_path = ('build/rpm/SPECS/%s.spec' % self.distribution.get_name()) self.execute(write_file, (spec_path, self._make_spec_file()), 'Writing .spec file') if self.spec_only: # stop if requested return # make a source distribution and copy to SOURCES directory with # optional icon sdist = self.find_peer ('sdist') if self.use_bzip2: sdist.formats = ['bztar'] else: sdist.formats = ['gztar'] self.run_peer('sdist') if self.use_bzip2: source = self.distribution.get_fullname() + '.tar.bz2' else: source = self.distribution.get_fullname() + '.tar.gz' self.execute(copy_file, (source, 'build/rpm/SOURCES'), 'Copying source distribution to SOURCES') if self.icon: if exists(self.icon): self.execute(copy_file, (self.icon, 'build/rpm/SOURCES'), 'Copying icon to SOURCES') else: raise DistutilsFileError, \ "Unable to find icon file '%s'" % self.icon # build package self.announce('Building RPMs') rpm_args = ['rpm',] if self.source_only: # what kind of RPMs? rpm_args.append('-bs') elif self.binary_only: rpm_args.append('-bb') else: rpm_args.append('-ba') topdir = os.getcwd() + 'build/rpm' rpm_args.extend(['--define', '_topdir ' + os.getcwd() + '/build/rpm',]) if not self.no_clean: rpm_args.append('--clean') rpm_args.append(spec_path) self.spawn(rpm_args) # run() def _get_package_data(self): ''' Get data needed to generate spec file, first from the DistributionMetadata class, then from the package_data file, which is Python code read with execfile() ''' package_type = 'rpm' # read in package data, if any if exists('package_data'): try: exec(open('package_data')) except: raise DistutilsOptionError, 'Unable to parse package data file' # set instance variables, supplying default value if not provided in # package data file self.package_data = locals() # the following variables must be {string (len() = 2): string} self.summaries = self._check_string_dict('summaries') self.descriptions = self._check_string_dict('descriptions') # The following variable must be an ordinary number or a string self.release = self._check_number_or_string('release', '1') self.serial = self._check_number_or_string('serial') # The following variables must be strings self.group = self._check_string('group', 'Development/Libraries') self.vendor = self._check_string('vendor') self.packager = self._check_string('packager') self.changelog = self._check_string('changelog') self.icon = self._check_string('icon') self.distribution_name = self._check_string('distribution_name') self.pre = self._check_string('pre') self.post = self._check_string('post') self.preun = self._check_string('preun') self.postun = self._check_string('postun') self.prep = self._check_string('prep', '%setup') if not self.no_rpm_opt_flags: self.build = (self._check_string( 'build', 'env CFLAGS="$RPM_OPT_FLAGS" python setup.py build')) else: self.build = (self._check_string('build', 'python setup.py build')) self.install = self._check_string( 'install', 'python setup.py install --root=$RPM_BUILD_ROOT --record') self.clean = self._check_string( 'clean', 'rm -rf $RPM_BUILD_ROOT') # The following variables must be a list or tuple of strings, or a # string self.doc = self._check_string_list('doc') if type(self.doc) == ListType: for readme in ('README', 'README.txt'): if exists(readme) and readme not in self.doc: self.doc.append(readme) self.doc = join(self.doc) self.provides = join(self._check_string_list('provides')) self.requires = join(self._check_string_list('requires')) self.conflicts = join(self._check_string_list('conflicts')) self.build_requires = join(self._check_string_list('build_requires')) self.obsoletes = join(self._check_string_list('obsoletes')) def _make_spec_file(self): ''' Generate an RPM spec file ''' # definitons and headers spec_file = [ '%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 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}',]) if self.use_bzip2: spec_file.append('Source0: %{name}-%{version}.tar.bz2') else: spec_file.append('Source0: %{name}-%{version}.tar.gz') spec_file.extend([ 'Copyright: ' + self.distribution.get_licence(), 'Group: ' + self.group, 'BuildRoot: %{_tmppath}/%{name}-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', ): if getattr(self, lower(field)): spec_file.append('%s: %s' % (field, getattr(self, lower(field)))) 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: ' + self.build_requires) if self.icon: spec_file.append('Icon: ' + basename(self.icon)) spec_file.extend([ '', '%description', self.distribution.get_long_description() ]) # put locale descriptions into spec file for locale in self.descriptions.keys(): spec_file.extend([ '', '%description -l ' + locale, self.descriptions[locale], ]) # rpm scripts for script in ('prep', 'build', 'install', 'clean', 'pre', 'post', 'preun', 'postun', ): if getattr(self, script): spec_file.extend([ '', '%' + script, getattr(self, script), ]) # files section spec_file.extend([ '', '%files -f INSTALLED_FILES', '%defattr(-,root,root)', ]) if self.doc: spec_file.append('%doc ' + self.doc) if self.changelog: spec_file.extend([ '', '%changelog', self.changelog ]) return spec_file def _check_string_dict(self, var_name, default_value = {}): ''' Tests a wariable to determine if it is {string: string}, var_name is the name of the wariable. Return the value if it is valid, returns default_value if the variable does not exist, raises DistutilsOptionError if the variable is not valid''' if self.package_data.has_key(var_name): pass_test = 1 # set to 0 if fails test value = self.package_data[var_name] if type(value) == DictType: for locale in value.keys(): if (type(locale) != StringType) or (type(value[locale]) != StringType): pass_test = 0 break if pass_test: return test_me raise DistutilsOptionError, \ ("Error in package_data: '%s' must be dictionary: " '{string: string}' % var_name) else: return default_value def _check_string(self, var_name, default_value = None): ''' Tests a variable in package_data to determine if it is a string, var_name is the name of the wariable. Return the value if it is a string, returns default_value if the variable does not exist, raises DistutilsOptionError if the variable is not a string''' if self.package_data.has_key(var_name): if type(self.package_data[var_name]) == StringType: return self.package_data[var_name] else: raise DistutilsOptionError, \ "Error in package_data: '%s' must be a string" % var_name else: return default_value def _check_number_or_string(self, var_name, default_value = None): ''' Tests a variable in package_data to determine if it is a number or a string, var_name is the name of the wariable. Return the value if it is valid, returns default_value if the variable does not exist, raises DistutilsOptionError if the variable is not valid''' if self.package_data.has_key(var_name): if type(self.package_data[var_name]) in (StringType, LongType, IntType, FloatType): return str(self.package_data[var_name]) else: raise DistutilsOptionError, \ ("Error in package_data: '%s' must be a string or a " 'number' % var_name) else: return default_value def _check_string_list(self, var_name, default_value = []): ''' Tests a variable in package_data to determine if it is a string or a list or tuple of strings, var_name is the name of the wariable. Return the value as a string or a list if it is valid, returns default_value if the variable does not exist, raises DistutilsOptionError if the variable is not valid''' if self.package_data.has_key(var_name): value = self.package_data[var_name] pass_test = 1 if type(value) == StringType: return value elif type(value) in (ListType, TupleType): for item in value: if type(item) != StringType: pass_test = 0 break if pass_test: return list(value) raise DistutilsOptionError, \ ("Error in package_data: '%s' must be a string or a " 'list or tuple of strings' % var_name) else: return default_value