Image
How to package open source applications as RPMs
Want to use a piece of third-party software but there's no RPM to install it? No problem: You can customize third-party software packages with RPM.
Recently, I wrote about packaging your own software with Red Hat Package Manager (RPM). Another common scenario is that you find a piece of software you want to use, but there is no RPM for it. This article shows you how to create RPMs for third-party applications.
Prerequisites include:
- You have basic knowledge of how to use RPM to query packages and install or delete packages. If not, get familiar with these concepts first and then come back here for some fun.
- You have Make, Git, GCC, and Java installed, as you'll need them to complete the exercises included here. It's not required, but it would be nice if you practice as I move along.
To install Make, GCC, Java 11, and Git using the DNF package manager, run:
$ sudo dnf install \
make git gcc-10 \
java-11-openjdk-headless
For this example, I'll use a Java benchmark I like from NASA: NAS Parallel Benchmarks (NPB3.0). I took this code and made a fork, adding only an improved build using Gradle. Here are the steps.
Step 1: Write a skeleton spec file
$ rpmdev-newspec --output ~/rpmbuild/SPECS/NPB.spec \
--type minimal
/home/josevnz/rpmbuild/SPECS/npb.spec created;
type minimal, rpm version >= 4.16.
The resulting file looks like this:
Name: npb
Version:
Release: 1%{?dist}
Summary:
License:
URL:
Source0:
BuildRequires:
Requires:
%description
%prep
%autosetup
%build
%configure
%make_build
%install
rm -rf $RPM_BUILD_ROOT
%make_install
%files
%license add-license-file-here
%doc add-docs-here
%changelog
* Tue Oct 05 2021 Jose Vicente Nunez <kodegeek.com@protonmail.com>
-
Next, remove the following tags from this skeleton file, as they do not apply to this task:
- %autosetup: You will unpack the software yourself without patches
- %configure and %make_build: You will use Gradle instead
Install the prerequisites, Java and Gradle:
$ sudo dnf install java-11-openjdk
$ sudo -i mkdir -p /opt/gradle
$ sudo -i curl --silent --location --fail \
--output /opt/gradle/gradle.zip \
https://services.gradle.org/distributions/gradle-7.2-bin.zip
$ cd /opt/gradle
$ sudo unzip gradle.zip
$ sudo /bin/rm -f /opt/gradle/gradle.zip
Now you are ready to change the spec
file.
[ Get more tips by downloading the Bash shell scripting cheat sheet. ]
Step 2: Fill in the building blocks for the Java RPM
After several changes, like adding Gradle as part of the build, you have:
Name: NPB
Version: 3.0
Release: 1%{?dist}
Summary: Small set of programs designed to help evaluate the performance of parallel supercomputers
License: NOSA
URL: https://www.nas.nasa.gov/software/npb.html
Source0: https://www.nas.nasa.gov/assets/npb/%{name}%{version}.tar.gz
BuildRequires: java-11-openjdk-headless,tar,gzip,rpmdevtools,rpmlint
Requires: java-11-openjdk-headless
# Custom macros (https://rpm-software-management.github.io/rpm/manual/macros.html)
# If you want to see the value of many of these macros, just run this: /usr/bin/rpm --showrc
%global debug_package %{nil}
%global gradle /opt/gradle/gradle-7.2/bin/gradle
%global curl /bin/curl --location --fail --silent --output
%global JAVA_DIR NPB3_0_JAV
%description
The NAS Parallel Benchmarks (NPB) are a small set of programs designed to help evaluate the performance
of parallel supercomputers. The benchmarks are derived from computational fluid dynamics (CFD)
applications and consist of five kernels and three pseudo-applications in the original "pencil-and-paper"
specification (NPB 1). The benchmark suite has been extended to include new benchmarks for unstructured
adaptive meshes, parallel I/O, multi-zone applications, and computational grids. Problem sizes in NPB are
predefined and indicated as different classes. Reference implementations of NPB are available in
commonly-used programming models like MPI and OpenMP (NPB 2 and NPB 3).
%prep
test ! -x %{gradle} && echo "ERROR: Gradle not installed!" && exit 100
# On a production environment you MOST LIKELY point to your private copy of the build artifacts
/bin/curl --location --fail --silent --output %{_sourcedir}/%{name}%{version}.tar.gz https://www.nas.nasa.gov/assets/npb/%{name}%{version}.tar.gz
%setup -q -n %{name}%{version}
%build
cd %{name}%{version}-JAV
# If you are not familiar with Gradle, you should read the following:
# https://docs.gradle.org/current/userguide/building_java_projects.html#sec:custom_java_source_set_paths
/bin/cat<<GRADLE>build.gradle.kts
// Gradle build file dynamically created for %{name}%{version}
plugins {
\`java-library\`
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(11))
}
}
sourceSets {
main {
java {
setSrcDirs(listOf("%{JAVA_DIR}"))
}
}
test {
java {
setSrcDirs(listOf("test"))
}
}
}
GRADLE
%{gradle} clean java jar
%install
/bin/rm -rf %{buildroot}
/bin/mkdir -v -p %{buildroot}/%{_bindir}
/bin/mkdir -v -p %{buildroot}/%{_libdir}
/bin/mkdir -v -p %{buildroot}/%{_pkgdocdir}
/bin/cp -p -v %{_builddir}/%{name}%{version}/%{name}%{version}-JAV/build/libs/%{name}%{version}-JAV.jar %{buildroot}/%{_libdir}
# On a production environment you MOST LIKELY point to your private copy of the build artifacts
%{curl} %{buildroot}/%{_pkgdocdir}/LICENSE https://raw.githubusercontent.com/josevnz/%{name}%{version}-JAV-FORK/main/LICENSE
%{curl} %{buildroot}/%{_pkgdocdir}/README.md https://github.com/josevnz/%{name}%{version}-JAV-FORK/blob/main/%{name}%{version}-JAV/README.md
%{curl} %{buildroot}/%{_bindir}/testAllS https://raw.githubusercontent.com/josevnz/tutorials/main/testAllS
%{curl} %{buildroot}/%{_bindir}/testAllW https://raw.githubusercontent.com/josevnz/tutorials/main/testAllW
/bin/chmod a+xr %{buildroot}/%{_bindir}/{testAllS,testAllW}
%clean
/bin/rm -rf %{buildroot}
%files
%license %{_pkgdocdir}/LICENSE
%doc %{_pkgdocdir}/README.md
%{_libdir}/%{name}%{version}-JAV.jar
%{_bindir}/testAllS
%{_bindir}/testAllW
%changelog
* Tue Oct 05 2021 Jose Vicente Nunez <kodegeek.com@protonmail.com>
- First RPM
The spec
file is heavily commented, and you can see how I used the original tar.gz
file without any changes and added a new build system on top of that, plus two wrapper scripts (testAIIS and testAIIW) to run the Java code after it is installed.
Next, create the new RPM:
$ rpmbuild -ba ~/rpmbuild/SPECS/npb.spec
Requires: /usr/bin/bash
Checking for unpackaged file(s): /usr/lib/rpm/check-files /home/josevnz/rpmbuild/BUILDROOT/NPB-3.0-1.fc33.x86_64
Wrote: /home/josevnz/rpmbuild/SRPMS/NPB-3.0-1.fc33.src.rpm
Wrote: /home/josevnz/rpmbuild/RPMS/x86_64/NPB-3.0-1.fc33.x86_64.rpm
Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.JGJ4Ky
Step 3: Install your custom RPM
With your RPM built, you can now install it:
$ sudo rpm -ihv ~/rpmbuild/RPMS/x86_64/NPB-3.0-1.fc33.x86_64.rpm
[sudo] password for josevnz:
Verifying... ################## [100%]
Preparing... ################## [100%]
Updating / installing...
1:NPB-3.0-1.fc33 ################## [100%]
The output indicates success:
/usr/bin/testAllS
+ /usr/lib/jvm/java-11-openjdk-11.0.12.0.7-4.fc33.x86_64/bin/java -classpath
[...]rpmbuild/BUILD/NPB3.0/NPB3.0-JAV/build/libs/NPB3.0-JAV.jar NPB3_0_JAV.BT
-np2 CLASS=S
NAS Parallel Benchmarks Java version (NPB3_0_JAV)
Multithreaded Version BT.S np=2
No input file inputbt.data, Using compiled defaults
Size: 12 X 12 X 12
Iterations: 60 dt: 0.01
Time step 1
Time step 20
Time step 40
Time step 60
Verification being performed for class S
accuracy setting for epsilon = 1.0000000000000005E-8
Comparison of RMS-norms of residual
[...]
BT.S: Verification Successful
Learn more
Packaging software with RPM—whether it's your own or someone else's open source application—may look intimidating at first, but with a little bit of patience, you will get there in no time. As you encounter issues, you will also find proper ways to improve your code. Below are some resources and final recommendations:
- Do yourself a big favor and get a copy of the RPM Packaging Guide written by Adam Miller, Maxim Svistunov, and Marie Doleželová. It is very complete and well organized. Seriously, do it now; it is that good.
- The official RPM Packaging Guide and the Fedora RPM guide are also full of details; keep them a bookmark away.
- Use rpmlint. You will be surprised how many little things you can catch and fix before shipping your RPM packages.
- Not enough? Fedora has a list of tricks you can use when packaging software.
- Thirsty for more? You should definitely take a look at RPM Packaging guidelines.
Image
Learn how to compile and package your Linux application for distribution with Red Hat Package Manager.
Image
Uncomfortable working on the command line? This article walks you through customizing your commands, and some examples of how to streamline your filesystem trash handling.
Image
Need to draw attention to something at the command line? Try cowsay, or one of these other terminal tools, to highlight what's important in your scripts.
Jose Vicente Nunez
Proud dad and husband, software developer and sysadmin. Recreational runner and geek. More about me