Do not use yum to guess RPM name in py2rpm

This can lead to unpredictable results: nobody knows
what repos and what package names will be available.

Also, removing `yum list` makes py2rpm faster.

Change-Id: I4892d46b9b13ea5f94a9805293381284c388cebe
This commit is contained in:
Alessio Ababilov 2013-06-06 08:00:56 +04:00 committed by Joshua Harlow
parent f016749dd0
commit 10d54505fc
4 changed files with 131 additions and 132 deletions

View File

@ -97,7 +97,7 @@ class Distro(object):
@property @property
def dependency_handler_class(self): def dependency_handler_class(self):
"""Return a dependency handler that will work for this distro.""" """Return a dependency handler that will work for this distro."""
return importer.import_entry_point(self._dependency_handler) return importer.import_entry_point(self._dependency_handler["name"])
def extract_component(self, name, action): def extract_component(self, name, action):
"""Return the class + component info to use for doing the action w/the component.""" """Return the class + component info to use for doing the action w/the component."""

View File

@ -64,10 +64,30 @@ class YumDependencyHandler(base.DependencyHandler):
self.tracereader = tr.TraceReader(trace_fn) self.tracereader = tr.TraceReader(trace_fn)
self.helper = yum_helper.Helper() self.helper = yum_helper.Helper()
def _epoch_list(self): def py2rpm_start_cmdline(self):
return [ cmdline = [
"--epoch-list", self.py2rpm_executable,
] + ["%s==%s" % (name, self.OPENSTACK_EPOCH) for name in self.python_names] "--rpm-base",
self.rpmbuild_dir,
]
if self.python_names:
cmdline += [
"--epoch-map",
] + ["%s==%s" % (name, self.OPENSTACK_EPOCH)
for name in self.python_names]
package_map = self.distro._dependency_handler.get("package_map", {})
if package_map:
cmdline += [
"--package-map",
] + ["%s==%s" % (key, value)
for key, value in package_map.iteritems()]
arch_dependent = self.distro._dependency_handler.get(
"arch_dependent", [])
if arch_dependent:
cmdline += [
"--arch-dependent",
] + arch_dependent
return cmdline
def package(self): def package(self):
super(YumDependencyHandler, self).package() super(YumDependencyHandler, self).package()
@ -229,11 +249,7 @@ BuildArch: noarch
return return
utils.log_iterable(sorted(package_files), logger=LOG, utils.log_iterable(sorted(package_files), logger=LOG,
header="Building RPM packages from files") header="Building RPM packages from files")
cmdline = [ cmdline = self.py2rpm_start_cmdline() + ["--"] + package_files
self.py2rpm_executable,
"--rpm-base",
self.rpmbuild_dir,
] + self._epoch_list() + ["--"] + package_files
out_filename = sh.joinpths(self.deps_dir, "py2rpm.deps.out") out_filename = sh.joinpths(self.deps_dir, "py2rpm.deps.out")
LOG.info("You can watch progress in another terminal with:") LOG.info("You can watch progress in another terminal with:")
LOG.info(" tail -f %s" % out_filename) LOG.info(" tail -f %s" % out_filename)
@ -248,11 +264,7 @@ BuildArch: noarch
def _build_openstack(self): def _build_openstack(self):
utils.log_iterable(sorted(self.package_dirs), logger=LOG, utils.log_iterable(sorted(self.package_dirs), logger=LOG,
header="Building RPM packages for directories") header="Building RPM packages for directories")
cmdline = [ cmdline = self.py2rpm_start_cmdline() + ["--"] + self.package_dirs
self.py2rpm_executable,
"--rpm-base",
self.rpmbuild_dir,
] + self._epoch_list() + ["--"] + self.package_dirs
out_filename = sh.joinpths(self.deps_dir, "py2rpm.openstack.out") out_filename = sh.joinpths(self.deps_dir, "py2rpm.openstack.out")
LOG.info("You can watch progress in another terminal with:") LOG.info("You can watch progress in another terminal with:")
LOG.info(" tail -f %s" % out_filename) LOG.info(" tail -f %s" % out_filename)
@ -282,7 +294,7 @@ BuildArch: noarch
if not self.python_names: if not self.python_names:
return [] return []
cmdline = [self.py2rpm_executable, "--convert"] + python_names cmdline = self.py2rpm_start_cmdline() + ["--convert"] + python_names
rpm_names = [] rpm_names = []
for name in sh.execute(cmdline)[0].splitlines(): for name in sh.execute(cmdline)[0].splitlines():
# name is "Requires: rpm-name" # name is "Requires: rpm-name"

View File

@ -3,7 +3,23 @@
name: rhel name: rhel
platform_pattern: redhat(.*)|centos(.*) platform_pattern: redhat(.*)|centos(.*)
install_helper: anvil.packaging.yum:YumInstallHelper install_helper: anvil.packaging.yum:YumInstallHelper
dependency_handler: anvil.packaging.yum:YumDependencyHandler dependency_handler:
name: anvil.packaging.yum:YumDependencyHandler
package_map:
django: Django
distribute: python-setuptools
mysql-python: MySQL-python
pam: python-pam
pastedeploy: python-paste-deploy
pycrypto: python-crypto
pyflakes: pyflakes
pylint: pylint
pyopenssl: pyOpenSSL
pyparsing: pyparsing
pysendfile: pysendfile
pytz: pytz
arch_dependent:
- selenium
commands: commands:
apache: apache:
name: httpd name: httpd

View File

@ -31,19 +31,8 @@ headers = [
'Group', 'Group',
] ]
package_map = { package_map = {}
"django": "Django", arch_dependent = set()
"distribute": "python-setuptools",
"pam": "python-pam",
"pycrypto": "python-crypto",
}
package_names = {}
arch_dependent = [
"selenium",
]
epoch_map = {} epoch_map = {}
requirements_section_re = re.compile(r'\[(.*?)\]') requirements_section_re = re.compile(r'\[(.*?)\]')
@ -55,7 +44,11 @@ class InstallationError(Exception):
pass pass
def package_name_python2rpm(python_name): def python_name_to_key(name):
return pkg_resources.Requirement.parse(name).key
def python_key_to_rpm(python_name):
python_name = python_name.lower() python_name = python_name.lower()
try: try:
return package_map[python_name] return package_map[python_name]
@ -66,14 +59,6 @@ def package_name_python2rpm(python_name):
prefixed_name = python_name prefixed_name = python_name
else: else:
prefixed_name = "python-%s" % python_name prefixed_name = "python-%s" % python_name
try:
return package_names[prefixed_name]
except:
pass
try:
return package_names[python_name]
except:
pass
return prefixed_name return prefixed_name
@ -179,7 +164,7 @@ def create_parser():
"--arch-dependent", "-a", "--arch-dependent", "-a",
metavar="<Python package name>", metavar="<Python package name>",
nargs="+", nargs="+",
default=arch_dependent, default=[],
help="Known architecture dependent packages") help="Known architecture dependent packages")
parser.add_argument( parser.add_argument(
"--epoch", "-e", "--epoch", "-e",
@ -188,11 +173,17 @@ def create_parser():
default=None, default=None,
help="RPM epoch for generated packages") help="RPM epoch for generated packages")
parser.add_argument( parser.add_argument(
"--epoch-list", "-l", "--epoch-map", "-x",
metavar="<Python package name == epoch number>", metavar="<Python package name == epoch number>",
nargs="+", nargs="+",
default=[], default=[],
help="Forced RPM epochs for packages") help="Forced RPM epochs for packages")
parser.add_argument(
"--package-map", "-p",
metavar="<Python package name == RPM name>",
nargs="+",
default=[],
help="Correspondence between Python and RPM package names")
return parser return parser
@ -224,20 +215,7 @@ def setup_logging(options):
logger.setLevel(level) logger.setLevel(level)
def build_name_map(): def truncate(text, max_len=77):
cmdline = ["yum", "list", "-q"]
try:
yum_list = call_subprocess(cmdline, show_stdout=False)[0]
except Exception as ex:
logging.warning(str(ex))
return
for line in yum_list.split("\n")[1:]:
if line:
line = line.split(None, 1)[0].split(".", 1)[0]
package_names[line.lower()] = line
def truncate(text, max_len):
if max_len <= 0: if max_len <= 0:
return '' return ''
if len(text) < max_len: if len(text) < max_len:
@ -246,17 +224,19 @@ def truncate(text, max_len):
return text return text
def build_epoch_map(options): def build_map(arguments):
for epoch_spec in options.epoch_list: result = {}
for arg in arguments:
try: try:
(name, epoch) = epoch_spec.split("==") (key, value) = arg.split("==")
name = name.strip().lower() key = python_name_to_key(key)
epoch = epoch.strip() value = value.strip()
assert(name and epoch) assert value
except (IndexError, AssertionError): except (IndexError, ValueError, AssertionError):
raise InstallationError("Bad epoch specifier: `%s'" % epoch_spec) raise InstallationError("Bad specifier: `%s'" % value_spec)
else: else:
epoch_map[name] = epoch result[key] = value
return result
def run_egg_info(source_dir, options): def run_egg_info(source_dir, options):
@ -306,7 +286,7 @@ def requires_and_conflicts(req_list, multiline):
req = pkg_resources.Requirement.parse(line) req = pkg_resources.Requirement.parse(line)
except: except:
continue continue
rpm_name = package_name_python2rpm(req.key) rpm_name = python_key_to_rpm(req.key)
if not req.specs: if not req.specs:
if multiline: if multiline:
rpm_requires += "\nRequires:" rpm_requires += "\nRequires:"
@ -335,66 +315,6 @@ def requires_and_conflicts(req_list, multiline):
return rpm_requires, rpm_conflicts return rpm_requires, rpm_conflicts
def adjust_spec(options, spec_file_name, pkg_name, archive_name, build_dir, source_dir):
global epoch_map
rpm_name = package_name_python2rpm(pkg_name)
cmdline = [
"sed", "-i",
"-e", "s/^Name:.*$/Name: %s/" % rpm_name,
"-e", "s/%{name}/%{pkg_name}/g",
"-e", "s/^%%define name.*$/%%define pkg_name %s/" % pkg_name,
]
epoch = epoch_map.get(pkg_name.lower(), options.epoch)
if epoch is not None:
cmdline += [
"-e", "s/^Version:/Epoch: %s\\nVersion:/" % epoch,
]
if pkg_name.lower() in options.arch_dependent:
cmdline += [
"-e", "/^BuildArch/d",
]
if archive_name:
cmdline += [
"-e",
"s/^Source0: .*$/Source0: %s/" % os.path.basename(archive_name)
]
shutil.copy(archive_name, os.path.join(build_dir, "SOURCES"))
call_subprocess(cmdline + [spec_file_name])
cmdline = [
"sed", "-i", "-r",
"-e", "/%doc/s/ man[^ ]+//",
]
call_subprocess(cmdline + [spec_file_name])
# Do any adjustments inside the file.
with open(spec_file_name, "rb") as fh:
contents = fh.read()
# Hook up a changelog if we can.
if not re.search(r"%changelog", contents):
changelog = build_changelog(source_dir, options)
contents += changelog
# Fix how summary is not supposed to be more than 1 line but sometimes
# seems to be (distribute has this problem).
def summary_cleaner(mtch):
summary = mtch.group(1)
summary = summary.strip()
summary = summary.replace("\n", " ")
summary = truncate(summary, 77)
summary = summary.strip()
return summary + "\n" + mtch.group(2)
spec_headers = [h + ":" for h in headers]
spec_headers = "|".join(spec_headers)
contents = re.sub(re.compile(r"(^Summary:.*?)(" + spec_headers+ ")", re.M|re.S),
summary_cleaner, contents)
with open(spec_file_name, "wb") as fh:
fh.write(contents)
def build_rpm(options, filename): def build_rpm(options, filename):
if os.path.isfile(filename): if os.path.isfile(filename):
temp_dir = tempfile.mkdtemp('-unpack', 'py2rpm-') temp_dir = tempfile.mkdtemp('-unpack', 'py2rpm-')
@ -414,6 +334,7 @@ def build_rpm(options, filename):
egg_info_requirements(source_dir), multiline=False) egg_info_requirements(source_dir), multiline=False)
pkg_name = setup_py_one_line(source_dir, "--name") pkg_name = setup_py_one_line(source_dir, "--name")
pkg_key = python_name_to_key(pkg_name)
build_dir = options.rpm_base build_dir = options.rpm_base
cmdline = [ cmdline = [
sys.executable, setup_py, "bdist_rpm", sys.executable, setup_py, "bdist_rpm",
@ -427,7 +348,7 @@ def build_rpm(options, filename):
cmdline += ["--conflicts", rpm_conflicts] cmdline += ["--conflicts", rpm_conflicts]
call_subprocess(cmdline, cwd=source_dir, raise_on_returncode=False) call_subprocess(cmdline, cwd=source_dir, raise_on_returncode=False)
rpm_name = package_name_python2rpm(pkg_name) rpm_name = python_key_to_rpm(pkg_key)
spec_name = os.path.join(build_dir, "SPECS", "%s.spec" % pkg_name) spec_name = os.path.join(build_dir, "SPECS", "%s.spec" % pkg_name)
if not os.path.exists(spec_name): if not os.path.exists(spec_name):
raise InstallationError("`%s' does not exist" % spec_name) raise InstallationError("`%s' does not exist" % spec_name)
@ -435,10 +356,56 @@ def build_rpm(options, filename):
old_name = spec_name old_name = spec_name
spec_name = os.path.join(build_dir, "SPECS", "%s.spec" % rpm_name) spec_name = os.path.join(build_dir, "SPECS", "%s.spec" % rpm_name)
os.rename(old_name, spec_name) os.rename(old_name, spec_name)
cmdline = [
"sed", "-i",
"-e", "s/^Name:.*$/Name: %s/" % rpm_name,
"-e", "s/%{name}/%{pkg_name}/g",
"-e", "s/^%%define name.*$/%%define pkg_name %s/" % pkg_name,
]
epoch = epoch_map.get(pkg_key, options.epoch)
if epoch is not None:
cmdline += [
"-e", "s/^Version:/Epoch: %s\\nVersion:/" % epoch,
]
if pkg_key in arch_dependent:
cmdline += [
"-e", "/^BuildArch/d",
]
if archive_name:
cmdline += [
"-e",
"s/^Source0: .*$/Source0: %s/" % os.path.basename(archive_name)
]
shutil.copy(archive_name,
os.path.join(build_dir, "SOURCES"))
call_subprocess(cmdline + [spec_name])
cmdline = [
"sed", "-i", "-r",
"-e", "/%doc/s/ man[^ ]+//",
]
call_subprocess(cmdline + [spec_name])
# Clean up (or add new things) to the spec file. # Do any adjustments inside the file.
adjust_spec(options, spec_name, pkg_name, archive_name, build_dir, with open(spec_file_name, "rb") as fh:
source_dir) contents = fh.read()
# Fix how summary is not supposed to be more than 1 line but sometimes
# seems to be.
def summary_cleaner(mtch):
summary = mtch.group(1)
summary = summary.strip()
summary = summary.replace("\n", " ")
summary = truncate(summary)
summary = summary.strip()
return summary + "\n" + mtch.group(2)
spec_headers = [h + ":" for h in headers]
spec_headers = "|".join(spec_headers)
contents = re.sub(re.compile(r"(^Summary:.*?)(" + spec_headers+ ")", re.M|re.S),
summary_cleaner, contents)
with open(spec_file_name, "wb") as fh:
fh.write(contents)
if options.source_only: if options.source_only:
rpmbuild_what = "-bs" rpmbuild_what = "-bs"
@ -457,8 +424,13 @@ def main():
parser = create_parser() parser = create_parser()
options = parser.parse_args() options = parser.parse_args()
setup_logging(options) setup_logging(options)
build_name_map() global arch_dependent
build_epoch_map(options) global package_map
global epoch_map
arch_dependent = set(python_name_to_key(pkg)
for pkg in options.arch_dependent)
package_map = build_map(options.package_map)
epoch_map = build_map(options.epoch_map)
if options.convert: if options.convert:
rpm_requires, rpm_conflicts = requires_and_conflicts( rpm_requires, rpm_conflicts = requires_and_conflicts(
@ -499,7 +471,6 @@ mv -f INSTALLED_FILES{.tmp,}
os.close(tmp_install_script[0]) os.close(tmp_install_script[0])
else: else:
tmp_install_script = None tmp_install_script = None
options.arch_dependent = set(pkg.lower() for pkg in options.arch_dependent)
failed_pkgs = [] failed_pkgs = []
for src in (os.path.abspath(sdir) for sdir in options.sources): for src in (os.path.abspath(sdir) for sdir in options.sources):
try: try: