From 1259392b38402448a05460c77d242132e22fe695 Mon Sep 17 00:00:00 2001 From: Greg Ward Date: Fri, 2 Jun 2000 01:49:58 +0000 Subject: [PATCH] Fairly massive overhaul to support getting RPM inputs (extra meta-data, prep/build/etc. scripts, doc files, dependency info) from a config file rather than the dedicated "package_info" file. (The idea is that developers will provide RPM-specific info in the "[bdist_rpm]" section of setup.cfg, but of course it could also be supplied in the other config files, on the command line, or in the setup script -- or any mix of the above.) Major changes: * added a boatload of options to 'user_options' and 'initialize_options()': 'distribution_name', 'group', 'release', ... * added 'finalize_package_data()', which takes the place of '_get_package_data()' -- except it's called from 'finalize_options()', not 'run()', so we have everything figured out before we actually run the command * added 'ensure_string()', 'ensure_string_list()', 'ensure_filename()'; these take the place of '_check_string()' and friends. (These actually look like really useful type-checking methods that could come in handy all over the Distutils; should consider moving them up to Command and using them in other command classes' 'finalize_options()' method for error-checking). * various cleanup, commentary, and adaptation to the new way of storing RPM info in '_make_spec_file()' --- Lib/distutils/command/bdist_rpm.py | 262 +++++++++++++++++++++++++---- 1 file changed, 230 insertions(+), 32 deletions(-) diff --git a/Lib/distutils/command/bdist_rpm.py b/Lib/distutils/command/bdist_rpm.py index c84fda873e7..b09b657fb60 100644 --- a/Lib/distutils/command/bdist_rpm.py +++ b/Lib/distutils/command/bdist_rpm.py @@ -7,7 +7,7 @@ distributions).""" __revision__ = "$Id$" -import os, string +import os, string, re from types import * from distutils.core import Command from distutils.util import get_platform, write_file @@ -28,6 +28,63 @@ class bdist_rpm (Command): "only generate binary RPM"), ('use-bzip2', None, "use bzip2 instead of gzip to create source distribution"), + + # More meta-data: too RPM-specific to put in the setup script, + # but needs to go in the .spec file -- so we make these options + # to "bdist_rpm". The idea is that packagers would put this + # info in setup.cfg, although they are of course free to + # supply it on the command line. + ('distribution-name', None, + "name of the (Linux) distribution name to which this " + "RPM applies (*not* the name of the module distribution!)"), + ('group', None, + "package classification [default: \"Development/Libraries\"]"), + ('release', None, + "RPM release number"), + ('serial', None, + "???"), + ('vendor', None, + "RPM \"vendor\" (eg. \"Joe Blow \") " + "[default: maintainer or author from setup script]"), + ('packager', None, + "RPM packager (eg. \"Jane Doe \")" + "[default: vendor]"), + ('doc-files', None, + "list of documentation files (space or comma-separated)"), + ('changelog', None, + "RPM changelog"), + ('icon', None, + "name of icon file"), + + ('prep-cmd', None, + "?? pre-build command(s) ??"), + ('build-cmd', None, + "?? build command(s) ??"), + ('install-cmd', None, + "?? installation command(s) ??"), + ('clean-cmd', None, + "?? clean command(s) ??"), + ('pre-install', None, + "pre-install script (Bourne shell code)"), + ('post-install', None, + "post-install script (Bourne shell code)"), + ('pre-uninstall', None, + "pre-uninstall script (Bourne shell code)"), + ('post-uninstall', None, + "post-uninstall script (Bourne shell code)"), + + ('provides', None, + "???"), + ('requires', None, + "???"), + ('conflicts', None, + "???"), + ('build-requires', None, + "???"), + ('obsoletes', None, + "???"), + + # Actions to take when building RPM ('clean', None, "clean up RPM build directory [default]"), ('no-clean', None, @@ -48,6 +105,32 @@ class bdist_rpm (Command): self.binary_only = None self.source_only = None self.use_bzip2 = None + + self.distribution_name = None + self.group = None + self.release = None + self.serial = None + self.vendor = None + self.packager = None + self.doc_files = None + self.changelog = None + self.icon = None + + self.prep_cmd = None + self.build_cmd = None + self.install_cmd = None + self.clean_cmd = None + self.pre_install = None + self.post_install = None + self.pre_uninstall = None + self.post_uninstall = None + self.prep = None + self.provides = None + self.requires = None + self.conflicts = None + self.build_requires = None + self.obsoletes = None + self.clean = 1 self.use_rpm_opt_flags = 1 @@ -63,15 +146,112 @@ class bdist_rpm (Command): 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.use_rpm_opt_flags = 0 + self.finalize_package_data() + # finalize_options() + def finalize_package_data (self): + self.ensure_string('group', "Development/Libraries") + self.ensure_string('vendor', + "%s <%s>" % (self.distribution.get_contact(), + self.distribution.get_contact_email())) + self.ensure_string('packager', self.vendor) # or nothing? + self.ensure_string_list('doc_files') + if type(self.doc_files) is ListType: + for readme in ('README', 'README.txt'): + if os.path.exists(readme) and readme not in self.doc_files: + self.doc.append(readme) + + self.ensure_string('release', "1") # should it be an int? + self.ensure_string('serial') # should it be an int? + + self.ensure_string('icon') + self.ensure_string('distribution_name') + + self.ensure_string('prep_cmd', "%setup") # string or filename? + + if self.use_rpm_opt_flags: + def_build = 'env CFLAGS="$RPM_OPT_FLAGS" python setup.py build' + else: + def_build = 'python setup.py build' + self.ensure_string('build_cmd', def_build) + self.ensure_string('install_cmd', + "python setup.py install --root=$RPM_BUILD_ROOT " + "--record=INSTALLED_FILES") + self.ensure_string('clean_cmd', + "rm -rf $RPM_BUILD_ROOT") + self.ensure_filename('pre_install') + self.ensure_filename('post_install') + self.ensure_filename('pre_uninstall') + self.ensure_filename('post_uninstall') + + # XXX don't forget we punted on summaries and descriptions -- they + # should be handled here eventually! + + # Now *this* is some meta-data that belongs in the setup script... + self.ensure_string_list('provides') + self.ensure_string_list('requires') + self.ensure_string_list('conflicts') + self.ensure_string_list('build_requires') + self.ensure_string_list('obsoletes') + + # finalize_package_data () + + + # XXX these look awfully handy: should probably move them + # up to Command and use more widely. + def _ensure_stringlike (self, option, what, default=None): + val = getattr(self, option) + if val is None: + setattr(self, option, default) + return default + elif type(val) is not StringType: + raise DistutilsOptionError, \ + "'%s' must be a %s (got `%s`)" % (option, what, val) + return val + + def ensure_string (self, option, default=None): + self._ensure_stringlike(option, "string", default) + + def ensure_string_list (self, option): + val = getattr(self, option) + if val is None: + return + elif type(val) is StringType: + setattr(self, option, re.split(r',\s*|\s+', val)) + else: + if type(val) is ListType: + types = map(type, val) + ok = (types == [StringType] * len(val)) + else: + ok = 0 + + if not ok: + raise DistutilsOptionError, \ + "'%s' must be a list of strings (got %s)" % \ + (option, `val`) + + def ensure_filename (self, option, default=None): + val = self._ensure_stringlike(option, "filename", None) + if val is not None and not os.path.exists(val): + raise DistutilsOptionError, \ + "error in '%s' option: file '%s' does not exist" % \ + (option, val) + + def run (self): - self._get_package_data() # get packaging info + + 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: @@ -206,9 +386,10 @@ class bdist_rpm (Command): self.obsoletes = join(self._check_string_list('obsoletes')) def _make_spec_file(self): - ''' Generate an RPM spec file ''' - - # definitons and headers + """Generate the text of an RPM spec file and return it as a + list of strings (one per line). + """ + # definitions and headers spec_file = [ '%define name ' + self.distribution.get_name(), '%define version ' + self.distribution.get_version(), @@ -218,18 +399,25 @@ class bdist_rpm (Command): ] # put locale summaries into spec file - for locale in self.summaries.keys(): - spec_file.append('Summary(%s): %s' % (locale, - self.summaries[locale])) + # 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') + spec_file.extend([ 'Copyright: ' + self.distribution.get_licence(), 'Group: ' + self.group, @@ -247,9 +435,12 @@ class bdist_rpm (Command): 'Conflicts', 'Obsoletes', ): - if getattr(self, string.lower(field)): - spec_file.append('%s: %s' % - (field, getattr(self, string.lower(field)))) + val = getattr(self, string.lower(field)) + if type(val) is ListType: + spec_file.append('%s: %s' % (field, string.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()) @@ -258,7 +449,8 @@ class bdist_rpm (Command): spec_file.append('Distribution: ' + self.distribution_name) if self.build_requires: - spec_file.append('BuildRequires: ' + self.build_requires) + spec_file.append('BuildRequires: ' + + string.join(self.build_requires)) if self.icon: spec_file.append('Icon: ' + os.path.basename(self.icon)) @@ -270,28 +462,34 @@ class bdist_rpm (Command): ]) # put locale descriptions into spec file - for locale in self.descriptions.keys(): - spec_file.extend([ - '', - '%description -l ' + locale, - self.descriptions[locale], - ]) + # XXX again, suppressed because config file syntax doesn't + # easily support this ;-( + #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): + for (rpm_opt, attr) in (('prep', 'prep_cmd'), + ('build', 'build_cmd'), + ('install', 'install_cmd'), + ('clean', 'clean_cmd'), + ('pre', 'pre_install'), + ('post', 'post_install'), + ('preun', 'pre_uninstall'), + ('postun', 'post_uninstall')): + # XXX oops, this doesn't distinguish between "raw code" + # options and "script filename" options -- well, we probably + # should settle on one or the other, and not make the + # distinction! + val = getattr(self, attr) + if val: spec_file.extend([ '', - '%' + script, - getattr(self, script), + '%' + rpm_opt, + val ]) @@ -302,8 +500,8 @@ class bdist_rpm (Command): '%defattr(-,root,root)', ]) - if self.doc: - spec_file.append('%doc ' + self.doc) + if self.doc_files: + spec_file.append('%doc ' + string.join(self.doc_files)) if self.changelog: spec_file.extend([