When dealing with multiple RPM packages that provide the same capability but different configurations, you'll encounter situations where you need to ensure proper package replacement during installation. The key issue arises when:
- Multiple packages declare the same Provides capability
- You want automatic removal of the old package when installing a new variant
- The package manager (yum/dnf) doesn't automatically handle the replacement
The solution lies in properly combining three RPM spec file directives:
# Critical directives for package replacement
Provides: %{name} = %{version}
Obsoletes: %{name} <= %{version}
Conflicts: %{name} < %{version}
Here's how to modify your spec file to ensure proper package replacement:
%define version 1234
%define name foo
%define release 1
Name: %{name}
Version: %{version}
Release: %{release}
Summary: Main package for foo service
License: GPLv3+
# Base package provides the capability
Provides: %{name} = %{version}
%package variant-a
Summary: Foo service with configuration A
# Explicitly obsolete any previous version
Obsoletes: %{name} < %{version}
Conflicts: %{name} < %{version}
Provides: %{name} = %{version} variant-a
%description variant-a
This package provides foo service with configuration profile A.
%package variant-b
Summary: Foo service with configuration B
# Force replacement of both base package and other variants
Obsoletes: %{name} < %{version} variant-a < %{version}
Conflicts: %{name} < %{version} variant-a < %{version}
Provides: %{name} = %{version} variant-b
%description variant-b
This package provides foo service with configuration profile B.
Several critical aspects ensure this works correctly:
- Version Comparison: The
<
operator in Obsoletes ensures any older version gets removed - Explicit Variant Handling: Each variant should obsolete both the base package and other variants
- Provides Declaration: All packages must provide the same base capability with identical version
After building your RPMs, verify the metadata with:
rpm -qip foo-variant-a-1.0-1.x86_64.rpm | grep -E 'Provides|Obsoletes'
rpm -qip foo-variant-b-1.0-1.x86_64.rpm | grep -E 'Provides|Obsoletes'
Expected output should show proper Obsoletes relationships between variants.
For more complex cases where you maintain parallel version streams:
%package variant-a
Obsoletes: %{name}-variant-a < %{version}
Conflicts: %{name}-variant-a < %{version}
Provides: %{name}-variant = %{version}
This approach allows for clean upgrades within each variant branch while still preventing concurrent installation.
When dealing with multiple RPM packages providing the same capability but different configurations, you'll encounter situations where multiple versions get installed simultaneously despite mutual exclusivity requirements. The key lies in proper .spec file configuration to enforce package replacement during installation.
Consider these package characteristics:
- Multiple packages built from the same source (foo-one, foo-two)
- Same version but different configurations
- All provide identical capability ("foo")
- Should be mutually exclusive installations
The current implementation has these gaps:
Obsoletes: %{name} <= %{version} # Only handles older versions
Provides: %{name} = %{version} # Doesn't conflict with same version
Here's the corrected spec file approach:
%package one
Summary: Summary for foo-one
Group: %{group}
# Critical changes:
Obsoletes: %{name} < %{version+1} # Handles current and older versions
Conflicts: %{name} = %{version} # Blocks same version installations
Provides: %{name} = %{version}
Obsoletes: Broaden the version range with < %{version+1}
to cover current version
Conflicts: Explicitly prevent same version co-installation
Versioning: Consider adding %{?dist} tag if using distribution-specific builds
Verify the behavior with these commands:
# Verify package metadata
rpm -qp --provides foo-one.rpm
rpm -qp --obsoletes foo-one.rpm
rpm -qp --conflicts foo-one.rpm
# Test installation sequence
yum install foo-one
yum install foo-two # Should now show removal of foo-one
For complex scenarios, consider these additional controls:
# Alternative approach using virtual packages
Provides: virtual-foo-config = %{version}
Obsoletes: virtual-foo-config < %{version+1}
Conflicts: virtual-foo-config = %{version}