home *** CD-ROM | disk | FTP | other *** search
/ Freelog Special Freeware 31 / FreelogHS31.iso / Texte / scribus / scribus-1.3.3.9-win32-install.exe / lib / distutils / command / bdist_rpm.py < prev    next >
Text File  |  2004-11-18  |  20KB  |  540 lines

  1. """distutils.command.bdist_rpm
  2.  
  3. Implements the Distutils 'bdist_rpm' command (create RPM source and binary
  4. distributions)."""
  5.  
  6. # This module should be kept compatible with Python 2.1.
  7.  
  8. __revision__ = "$Id: bdist_rpm.py,v 1.46 2004/11/10 22:23:14 loewis Exp $"
  9.  
  10. import sys, os, string
  11. import glob
  12. from types import *
  13. from distutils.core import Command
  14. from distutils.debug import DEBUG
  15. from distutils.util import get_platform
  16. from distutils.file_util import write_file
  17. from distutils.errors import *
  18. from distutils import log
  19.  
  20. class bdist_rpm (Command):
  21.  
  22.     description = "create an RPM distribution"
  23.  
  24.     user_options = [
  25.         ('bdist-base=', None,
  26.          "base directory for creating built distributions"),
  27.         ('rpm-base=', None,
  28.          "base directory for creating RPMs (defaults to \"rpm\" under "
  29.          "--bdist-base; must be specified for RPM 2)"),
  30.         ('dist-dir=', 'd',
  31.          "directory to put final RPM files in "
  32.          "(and .spec files if --spec-only)"),
  33.         ('python=', None,
  34.          "path to Python interpreter to hard-code in the .spec file "
  35.          "(default: \"python\")"),
  36.         ('fix-python', None,
  37.          "hard-code the exact path to the current Python interpreter in "
  38.          "the .spec file"),
  39.         ('spec-only', None,
  40.          "only regenerate spec file"),
  41.         ('source-only', None,
  42.          "only generate source RPM"),
  43.         ('binary-only', None,
  44.          "only generate binary RPM"),
  45.         ('use-bzip2', None,
  46.          "use bzip2 instead of gzip to create source distribution"),
  47.  
  48.         # More meta-data: too RPM-specific to put in the setup script,
  49.         # but needs to go in the .spec file -- so we make these options
  50.         # to "bdist_rpm".  The idea is that packagers would put this
  51.         # info in setup.cfg, although they are of course free to
  52.         # supply it on the command line.
  53.         ('distribution-name=', None,
  54.          "name of the (Linux) distribution to which this "
  55.          "RPM applies (*not* the name of the module distribution!)"),
  56.         ('group=', None,
  57.          "package classification [default: \"Development/Libraries\"]"),
  58.         ('release=', None,
  59.          "RPM release number"),
  60.         ('serial=', None,
  61.          "RPM serial number"),
  62.         ('vendor=', None,
  63.          "RPM \"vendor\" (eg. \"Joe Blow <joe@example.com>\") "
  64.          "[default: maintainer or author from setup script]"),
  65.         ('packager=', None,
  66.          "RPM packager (eg. \"Jane Doe <jane@example.net>\")"
  67.          "[default: vendor]"),
  68.         ('doc-files=', None,
  69.          "list of documentation files (space or comma-separated)"),
  70.         ('changelog=', None,
  71.          "RPM changelog"),
  72.         ('icon=', None,
  73.          "name of icon file"),
  74.         ('provides=', None,
  75.          "capabilities provided by this package"),
  76.         ('requires=', None,
  77.          "capabilities required by this package"),
  78.         ('conflicts=', None,
  79.          "capabilities which conflict with this package"),
  80.         ('build-requires=', None,
  81.          "capabilities required to build this package"),
  82.         ('obsoletes=', None,
  83.          "capabilities made obsolete by this package"),
  84.         ('no-autoreq', None,
  85.          "do not automatically calculate dependencies"),
  86.  
  87.         # Actions to take when building RPM
  88.         ('keep-temp', 'k',
  89.          "don't clean up RPM build directory"),
  90.         ('no-keep-temp', None,
  91.          "clean up RPM build directory [default]"),
  92.         ('use-rpm-opt-flags', None,
  93.          "compile with RPM_OPT_FLAGS when building from source RPM"),
  94.         ('no-rpm-opt-flags', None,
  95.          "do not pass any RPM CFLAGS to compiler"),
  96.         ('rpm3-mode', None,
  97.          "RPM 3 compatibility mode (default)"),
  98.         ('rpm2-mode', None,
  99.          "RPM 2 compatibility mode"),
  100.  
  101.         # Add the hooks necessary for specifying custom scripts
  102.         ('prep-script=', None,
  103.          "Specify a script for the PREP phase of RPM building"),
  104.         ('build-script=', None,
  105.          "Specify a script for the BUILD phase of RPM building"),
  106.  
  107.         ('pre-install=', None,
  108.          "Specify a script for the pre-INSTALL phase of RPM building"),
  109.         ('install-script=', None,
  110.          "Specify a script for the INSTALL phase of RPM building"),
  111.         ('post-install=', None,
  112.          "Specify a script for the post-INSTALL phase of RPM building"),
  113.  
  114.         ('pre-uninstall=', None,
  115.          "Specify a script for the pre-UNINSTALL phase of RPM building"),
  116.         ('post-uninstall=', None,
  117.          "Specify a script for the post-UNINSTALL phase of RPM building"),
  118.  
  119.         ('clean-script=', None,
  120.          "Specify a script for the CLEAN phase of RPM building"),
  121.  
  122.         ('verify-script=', None,
  123.          "Specify a script for the VERIFY phase of the RPM build"),
  124.  
  125.         # Allow a packager to explicitly force an architecture
  126.         ('force-arch=', None,
  127.          "Force an architecture onto the RPM build process"),
  128.        ]
  129.  
  130.     boolean_options = ['keep-temp', 'use-rpm-opt-flags', 'rpm3-mode',
  131.                        'no-autoreq']
  132.  
  133.     negative_opt = {'no-keep-temp': 'keep-temp',
  134.                     'no-rpm-opt-flags': 'use-rpm-opt-flags',
  135.                     'rpm2-mode': 'rpm3-mode'}
  136.  
  137.  
  138.     def initialize_options (self):
  139.         self.bdist_base = None
  140.         self.rpm_base = None
  141.         self.dist_dir = None
  142.         self.python = None
  143.         self.fix_python = None
  144.         self.spec_only = None
  145.         self.binary_only = None
  146.         self.source_only = None
  147.         self.use_bzip2 = None
  148.  
  149.         self.distribution_name = None
  150.         self.group = None
  151.         self.release = None
  152.         self.serial = None
  153.         self.vendor = None
  154.         self.packager = None
  155.         self.doc_files = None
  156.         self.changelog = None
  157.         self.icon = None
  158.  
  159.         self.prep_script = None
  160.         self.build_script = None
  161.         self.install_script = None
  162.         self.clean_script = None
  163.         self.verify_script = None
  164.         self.pre_install = None
  165.         self.post_install = None
  166.         self.pre_uninstall = None
  167.         self.post_uninstall = None
  168.         self.prep = None
  169.         self.provides = None
  170.         self.requires = None
  171.         self.conflicts = None
  172.         self.build_requires = None
  173.         self.obsoletes = None
  174.  
  175.         self.keep_temp = 0
  176.         self.use_rpm_opt_flags = 1
  177.         self.rpm3_mode = 1
  178.         self.no_autoreq = 0
  179.  
  180.         self.force_arch = None
  181.  
  182.     # initialize_options()
  183.  
  184.  
  185.     def finalize_options (self):
  186.         self.set_undefined_options('bdist', ('bdist_base', 'bdist_base'))
  187.         if self.rpm_base is None:
  188.             if not self.rpm3_mode:
  189.                 raise DistutilsOptionError, \
  190.                       "you must specify --rpm-base in RPM 2 mode"
  191.             self.rpm_base = os.path.join(self.bdist_base, "rpm")
  192.  
  193.         if self.python is None:
  194.             if self.fix_python:
  195.                 self.python = sys.executable
  196.             else:
  197.                 self.python = "python"
  198.         elif self.fix_python:
  199.             raise DistutilsOptionError, \
  200.                   "--python and --fix-python are mutually exclusive options"
  201.  
  202.         if os.name != 'posix':
  203.             raise DistutilsPlatformError, \
  204.                   ("don't know how to create RPM "
  205.                    "distributions on platform %s" % os.name)
  206.         if self.binary_only and self.source_only:
  207.             raise DistutilsOptionError, \
  208.                   "cannot supply both '--source-only' and '--binary-only'"
  209.  
  210.         # don't pass CFLAGS to pure python distributions
  211.         if not self.distribution.has_ext_modules():
  212.             self.use_rpm_opt_flags = 0
  213.  
  214.         self.set_undefined_options('bdist', ('dist_dir', 'dist_dir'))
  215.         self.finalize_package_data()
  216.  
  217.     # finalize_options()
  218.  
  219.     def finalize_package_data (self):
  220.         self.ensure_string('group', "Development/Libraries")
  221.         self.ensure_string('vendor',
  222.                            "%s <%s>" % (self.distribution.get_contact(),
  223.                                         self.distribution.get_contact_email()))
  224.         self.ensure_string('packager')
  225.         self.ensure_string_list('doc_files')
  226.         if type(self.doc_files) is ListType:
  227.             for readme in ('README', 'README.txt'):
  228.                 if os.path.exists(readme) and readme not in self.doc_files:
  229.                     self.doc_files.append(readme)
  230.  
  231.         self.ensure_string('release', "1")
  232.         self.ensure_string('serial')   # should it be an int?
  233.  
  234.         self.ensure_string('distribution_name')
  235.  
  236.         self.ensure_string('changelog')
  237.           # Format changelog correctly
  238.         self.changelog = self._format_changelog(self.changelog)
  239.  
  240.         self.ensure_filename('icon')
  241.  
  242.         self.ensure_filename('prep_script')
  243.         self.ensure_filename('build_script')
  244.         self.ensure_filename('install_script')
  245.         self.ensure_filename('clean_script')
  246.         self.ensure_filename('verify_script')
  247.         self.ensure_filename('pre_install')
  248.         self.ensure_filename('post_install')
  249.         self.ensure_filename('pre_uninstall')
  250.         self.ensure_filename('post_uninstall')
  251.  
  252.         # XXX don't forget we punted on summaries and descriptions -- they
  253.         # should be handled here eventually!
  254.  
  255.         # Now *this* is some meta-data that belongs in the setup script...
  256.         self.ensure_string_list('provides')
  257.         self.ensure_string_list('requires')
  258.         self.ensure_string_list('conflicts')
  259.         self.ensure_string_list('build_requires')
  260.         self.ensure_string_list('obsoletes')
  261.  
  262.         self.ensure_string('force_arch')
  263.     # finalize_package_data ()
  264.  
  265.  
  266.     def run (self):
  267.  
  268.         if DEBUG:
  269.             print "before _get_package_data():"
  270.             print "vendor =", self.vendor
  271.             print "packager =", self.packager
  272.             print "doc_files =", self.doc_files
  273.             print "changelog =", self.changelog
  274.  
  275.         # make directories
  276.         if self.spec_only:
  277.             spec_dir = self.dist_dir
  278.             self.mkpath(spec_dir)
  279.         else:
  280.             rpm_dir = {}
  281.             for d in ('SOURCES', 'SPECS', 'BUILD', 'RPMS', 'SRPMS'):
  282.                 rpm_dir[d] = os.path.join(self.rpm_base, d)
  283.                 self.mkpath(rpm_dir[d])
  284.             spec_dir = rpm_dir['SPECS']
  285.  
  286.         # Spec file goes into 'dist_dir' if '--spec-only specified',
  287.         # build/rpm.<plat> otherwise.
  288.         spec_path = os.path.join(spec_dir,
  289.                                  "%s.spec" % self.distribution.get_name())
  290.         self.execute(write_file,
  291.                      (spec_path,
  292.                       self._make_spec_file()),
  293.                      "writing '%s'" % spec_path)
  294.  
  295.         if self.spec_only: # stop if requested
  296.             return
  297.  
  298.         # Make a source distribution and copy to SOURCES directory with
  299.         # optional icon.
  300.         sdist = self.reinitialize_command('sdist')
  301.         if self.use_bzip2:
  302.             sdist.formats = ['bztar']
  303.         else:
  304.             sdist.formats = ['gztar']
  305.         self.run_command('sdist')
  306.  
  307.         source = sdist.get_archive_files()[0]
  308.         source_dir = rpm_dir['SOURCES']
  309.         self.copy_file(source, source_dir)
  310.  
  311.         if self.icon:
  312.             if os.path.exists(self.icon):
  313.                 self.copy_file(self.icon, source_dir)
  314.             else:
  315.                 raise DistutilsFileError, \
  316.                       "icon file '%s' does not exist" % self.icon
  317.  
  318.  
  319.         # build package
  320.         log.info("building RPMs")
  321.         rpm_cmd = ['rpm']
  322.         if os.path.exists('/usr/bin/rpmbuild') or \
  323.            os.path.exists('/bin/rpmbuild'):
  324.             rpm_cmd = ['rpmbuild']
  325.         if self.source_only: # what kind of RPMs?
  326.             rpm_cmd.append('-bs')
  327.         elif self.binary_only:
  328.             rpm_cmd.append('-bb')
  329.         else:
  330.             rpm_cmd.append('-ba')
  331.         if self.rpm3_mode:
  332.             rpm_cmd.extend(['--define',
  333.                              '_topdir %s' % os.path.abspath(self.rpm_base)])
  334.         if not self.keep_temp:
  335.             rpm_cmd.append('--clean')
  336.         rpm_cmd.append(spec_path)
  337.         self.spawn(rpm_cmd)
  338.  
  339.         # XXX this is a nasty hack -- we really should have a proper way to
  340.         # find out the names of the RPM files created; also, this assumes
  341.         # that RPM creates exactly one source and one binary RPM.
  342.         if not self.dry_run:
  343.             if not self.binary_only:
  344.                 srpms = glob.glob(os.path.join(rpm_dir['SRPMS'], "*.rpm"))
  345.                 assert len(srpms) == 1, \
  346.                        "unexpected number of SRPM files found: %s" % srpms
  347.                 self.move_file(srpms[0], self.dist_dir)
  348.  
  349.             if not self.source_only:
  350.                 rpms = glob.glob(os.path.join(rpm_dir['RPMS'], "*/*.rpm"))
  351.                 debuginfo = glob.glob(os.path.join(rpm_dir['RPMS'], \
  352.                                                    "*/*debuginfo*.rpm"))
  353.                 if debuginfo:
  354.                     rpms.remove(debuginfo[0])
  355.                 assert len(rpms) == 1, \
  356.                        "unexpected number of RPM files found: %s" % rpms
  357.                 self.move_file(rpms[0], self.dist_dir)
  358.                 if debuginfo:
  359.                     self.move_file(debuginfo[0], self.dist_dir)
  360.     # run()
  361.  
  362.  
  363.     def _make_spec_file(self):
  364.         """Generate the text of an RPM spec file and return it as a
  365.         list of strings (one per line).
  366.         """
  367.         # definitions and headers
  368.         spec_file = [
  369.             '%define name ' + self.distribution.get_name(),
  370.             '%define version ' + self.distribution.get_version().replace('-','_'),
  371.             '%define release ' + self.release.replace('-','_'),
  372.             '',
  373.             'Summary: ' + self.distribution.get_description(),
  374.             ]
  375.  
  376.         # put locale summaries into spec file
  377.         # XXX not supported for now (hard to put a dictionary
  378.         # in a config file -- arg!)
  379.         #for locale in self.summaries.keys():
  380.         #    spec_file.append('Summary(%s): %s' % (locale,
  381.         #                                          self.summaries[locale]))
  382.  
  383.         spec_file.extend([
  384.             'Name: %{name}',
  385.             'Version: %{version}',
  386.             'Release: %{release}',])
  387.  
  388.         # XXX yuck! this filename is available from the "sdist" command,
  389.         # but only after it has run: and we create the spec file before
  390.         # running "sdist", in case of --spec-only.
  391.         if self.use_bzip2:
  392.             spec_file.append('Source0: %{name}-%{version}.tar.bz2')
  393.         else:
  394.             spec_file.append('Source0: %{name}-%{version}.tar.gz')
  395.  
  396.         spec_file.extend([
  397.             'License: ' + self.distribution.get_license(),
  398.             'Group: ' + self.group,
  399.             'BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot',
  400.             'Prefix: %{_prefix}', ])
  401.  
  402.         if not self.force_arch:
  403.             # noarch if no extension modules
  404.             if not self.distribution.has_ext_modules():
  405.                 spec_file.append('BuildArch: noarch')
  406.         else:
  407.             spec_file.append( 'BuildArch: %s' % self.force_arch )
  408.  
  409.         for field in ('Vendor',
  410.                       'Packager',
  411.                       'Provides',
  412.                       'Requires',
  413.                       'Conflicts',
  414.                       'Obsoletes',
  415.                       ):
  416.             val = getattr(self, string.lower(field))
  417.             if type(val) is ListType:
  418.                 spec_file.append('%s: %s' % (field, string.join(val)))
  419.             elif val is not None:
  420.                 spec_file.append('%s: %s' % (field, val))
  421.  
  422.  
  423.         if self.distribution.get_url() != 'UNKNOWN':
  424.             spec_file.append('Url: ' + self.distribution.get_url())
  425.  
  426.         if self.distribution_name:
  427.             spec_file.append('Distribution: ' + self.distribution_name)
  428.  
  429.         if self.build_requires:
  430.             spec_file.append('BuildRequires: ' +
  431.                              string.join(self.build_requires))
  432.  
  433.         if self.icon:
  434.             spec_file.append('Icon: ' + os.path.basename(self.icon))
  435.  
  436.         if self.no_autoreq:
  437.             spec_file.append('AutoReq: 0')
  438.  
  439.         spec_file.extend([
  440.             '',
  441.             '%description',
  442.             self.distribution.get_long_description()
  443.             ])
  444.  
  445.         # put locale descriptions into spec file
  446.         # XXX again, suppressed because config file syntax doesn't
  447.         # easily support this ;-(
  448.         #for locale in self.descriptions.keys():
  449.         #    spec_file.extend([
  450.         #        '',
  451.         #        '%description -l ' + locale,
  452.         #        self.descriptions[locale],
  453.         #        ])
  454.  
  455.         # rpm scripts
  456.         # figure out default build script
  457.         def_build = "%s setup.py build" % self.python
  458.         if self.use_rpm_opt_flags:
  459.             def_build = 'env CFLAGS="$RPM_OPT_FLAGS" ' + def_build
  460.  
  461.         # insert contents of files
  462.  
  463.         # XXX this is kind of misleading: user-supplied options are files
  464.         # that we open and interpolate into the spec file, but the defaults
  465.         # are just text that we drop in as-is.  Hmmm.
  466.  
  467.         script_options = [
  468.             ('prep', 'prep_script', "%setup"),
  469.             ('build', 'build_script', def_build),
  470.             ('install', 'install_script',
  471.              ("%s setup.py install "
  472.               "--root=$RPM_BUILD_ROOT "
  473.               "--record=INSTALLED_FILES") % self.python),
  474.             ('clean', 'clean_script', "rm -rf $RPM_BUILD_ROOT"),
  475.             ('verifyscript', 'verify_script', None),
  476.             ('pre', 'pre_install', None),
  477.             ('post', 'post_install', None),
  478.             ('preun', 'pre_uninstall', None),
  479.             ('postun', 'post_uninstall', None),
  480.         ]
  481.  
  482.         for (rpm_opt, attr, default) in script_options:
  483.             # Insert contents of file referred to, if no file is referred to
  484.             # use 'default' as contents of script
  485.             val = getattr(self, attr)
  486.             if val or default:
  487.                 spec_file.extend([
  488.                     '',
  489.                     '%' + rpm_opt,])
  490.                 if val:
  491.                     spec_file.extend(string.split(open(val, 'r').read(), '\n'))
  492.                 else:
  493.                     spec_file.append(default)
  494.  
  495.  
  496.         # files section
  497.         spec_file.extend([
  498.             '',
  499.             '%files -f INSTALLED_FILES',
  500.             '%defattr(-,root,root)',
  501.             ])
  502.  
  503.         if self.doc_files:
  504.             spec_file.append('%doc ' + string.join(self.doc_files))
  505.  
  506.         if self.changelog:
  507.             spec_file.extend([
  508.                 '',
  509.                 '%changelog',])
  510.             spec_file.extend(self.changelog)
  511.  
  512.         return spec_file
  513.  
  514.     # _make_spec_file ()
  515.  
  516.     def _format_changelog(self, changelog):
  517.         """Format the changelog correctly and convert it to a list of strings
  518.         """
  519.         if not changelog:
  520.             return changelog
  521.         new_changelog = []
  522.         for line in string.split(string.strip(changelog), '\n'):
  523.             line = string.strip(line)
  524.             if line[0] == '*':
  525.                 new_changelog.extend(['', line])
  526.             elif line[0] == '-':
  527.                 new_changelog.append(line)
  528.             else:
  529.                 new_changelog.append('  ' + line)
  530.  
  531.         # strip trailing newline inserted by first changelog entry
  532.         if not new_changelog[0]:
  533.             del new_changelog[0]
  534.  
  535.         return new_changelog
  536.  
  537.     # _format_changelog()
  538.  
  539. # class bdist_rpm
  540.