Fairly massive overhaul to support getting RPM inputs (extra meta-data,
authorGreg Ward <gward@python.net>
Fri, 2 Jun 2000 01:49:58 +0000 (01:49 +0000)
committerGreg Ward <gward@python.net>
Fri, 2 Jun 2000 01:49:58 +0000 (01:49 +0000)
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

index c84fda873e79a08fbbed84d9a9177ebc1c478800..b09b657fb608b8ae58257591eb25627463b079f3 100644 (file)
@@ -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 <joe@example.com>\") "
+         "[default: maintainer or author from setup script]"),
+        ('packager', None,
+         "RPM packager (eg. \"Jane Doe <jane@example.net>\")"
+         "[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([