Pages

Wednesday, January 30, 2013

RPM example - Application packaging

This post is about something quite different than the others.
It concerns packaging your software as an RPM file making it ready for delivery.
If you have never used RPM before and/or you're not an experienced Linux user, this post is intended for you.

I'm far from being an expert in this area, and I would guess there is probably a better way of achieving the goal I want to show here. Please share your thoughts and experience if you have any.

I assume you already heard of RPM, and understand it's benefits and so on.
This post is not to convince you to use RPM, only how to create such RPM file which you could use.

What's in this example?
I created a "Hello World" Java application, and using Ant, I compile it, and Jar it.
This application could be anything you want - this is just a "place-holder" for you, to replace with
your real stuff.

So, after having an RPM file, we'll be able to install it to the system and remove it afterwards.
The path which the program will be installed into will contain the "Hello World" application Jar.

RPM Structure quick overview

As you probably know by now, RPM files are created using a ".spec" file.
This file tells the rpmbuild tool which we will use how to create the RPM package.
The .spec file contains several sections:

  1. Headers - Headers of the spec file contain stuff like a Summary of what the package is,
    Name of the package, Version & Release information, License & Package and an
    attribute named BuildRoot. BuildRoot is important - it specifies the location
    of where your package will be temporarily installed.
    What I mean by temporarily, we'll get to later.
  2. %description step - This is where you can type just about anything you want describing
    your awesome application.
  3. %prep step - In the step, preparing your Source files to be built (using Ant in our example).
    "What's to prepare?" - you ask? - Well, in order to build an RPM package, you must provide
    RPM a .tar file or a zipped file of some kind to it's SOURCES directory, (I'll explain the
    structure of RPM further down), so what you need to do in this step is to extract your
    archived sources.
  4. %build step - This is where the actual build of your application happens. In our case, we'll
    execute ant on our build.xml file, that compiles and Jars the application - real simple.
  5. %install step - This is the step that happens after the %build step, and will determine how
    your installed package will look like. We will copy the necessary binaries (Jar in our case) to a relevant directory.
  6. %files section - This section determines which files from the %install step to pack, and with what access rights.
  7. %clean section - This section runs after packaging was done, and cleans up all the leftovers from your build and your install steps. It's important to delete all the old files, especially if you build several RPM packages, you don't want to have files of another RPM build process in your way.

Okay. I know this still doesn't tell you how to do stuff, but this introduction is quite important in my opinion.
So take a deep breathe one last time, and we'll go over the rpmbuild directory structure and understand the steps we need to take in order to accomplish our mission.

"rpmbuild" Directory structure

An important part of the way RPM works, is its directory structure where work is being done.
You should be able to locate your rpmbuild directory on your machine, which usually should
show under your home directory.

The rpmbuild directory contains the following sub-directories:
  1. BUILD - This is the directory where your sources will be built, and where the artifacts of your build will be located. The %build step, reads from the directory and writes to it. 
  2. BUILDROOT - This is the directory where you will "temporarily" install your binaries to.
    You can think of it as the "INSTALL" directory. After the build step, the BUILDROOT
    serves as a directory to contain the files in a given directory structure. Those files in this
    specified directory structure will be installed afterwards using RPM.
    To clarify: If you place under BUILDROOT a directory called my-app-123 then on the
    real install of the RPM package, your package will be installed under /my-app-123.
    This step reads from the BUILD directory and writes to the BUILDROOT directory.
  3. RPMS - This is the directory that will contain your RPM package in the end.
  4. SOURCES - This is the directory where you should place your archived sources.
  5. SPECS - This is the directory where you should place your .spec file which will create
    the RPM package.
  6. SRPMS - This is a directory where source RPMs are created and stored. 

Each of those directories, can be referenced in the .spec file using RPM variables.
Variables
%_specdir~/rpmbuild/SPECS
%_sourcedir~/rpmbuild/SOURCES
%_builddir~/rpmbuild/BUILD
%_buildrootdir~/rpmbuild/BUILDROOT
%_rpmdir~/rpmbuild/RPMS
%_srcrpmdir~/rpmbuild/SRPMS

Now that we finished our overview. Let's go over our 8 steps to create a packaged application with RPM.
  1. Create an application (write source code) - in our case, a simple "Hello World" application.
  2. Create the build file for the application - Ant's build.xml file in our case.
  3. Create an archive from our application's sources.
  4. Place the archive (tar) at ~/rpmbuild/SOURCES directory.
  5. Copy the .spec file into the ~/rpmbuild/SPECS directory.
  6. Create the RPM package using: rpmbuild -ba <spec_file>
  7. Install the package.
  8. Uninstall the package.
Now, lets go a bit into details:
  1. You can download the pre-made application entirely from GitHub: rpm-example-project.zip
    in order to skip steps 1,2 and 3.
    (You can also access the repository - http://github.com/nirgit/RPM-Project-Example).
  2. After downloading the zipped application, and extracting it somewhere on your machine,
    you can take a closer look into its hierarchy. You will find under the Application directory,
    a directory called src which contains the sources of our application, and you will find
    the build.xml file which builds the application.
    You will also find 2 other files - the application.tar containing the application sources,
    and the project.spec file.
  3. You need now to copy the .spec file into the ~/rpmbuild/SPECS directory, and the
    application.tar file into the ~/rpmbuild/SOURCES directory.
  4. In order to create the package now, you're required to run the rpmbuild tool, by
    executing: > rpmbuild -ba ~/rpmbuild/SPECS/project.spec
  5. Since I use Ubuntu, I also use alien to install the package. In case you're running on
    a Linux flavor such as CentOS, I think you can simply run the regular RPM install.
    With alien: > sudo alien -i ~/rpmbuild/RPMS/i386/Example-RPM-Project-1.0-1.i386.rpm
    Without: > rpm -i ~/rpmbuild/RPMS/i386/Example-RPM-Project-1.0-1.i386.rpm
  6. After the install you should be able to find the package installed under the root:
    /Example-RPM-Project-1.0-1
    Under it, you can find the app.jar.
  7. You can uninstall the package using RPM's command: rpm -e <package_name> 
    or (in my case using Ubuntu): sudo dpkg --remove example-rpm-project

The SPEC file
This is the SPEC file associated with the RPM package.
I hope it will serve you as a good starting point for your own SPEC file.


################################################################
#
# This is an example of a simple RPM spec file.
#
################################################################

Summary: An RPM Spec example
Name: Example-RPM-Project
Version: 1.0
Release: 1
License: Apache 2.0
Group: Applications/Sample
URL: http://www.mycompany.com
Packager: Nir Moav <getnirm@gmail.com>
BuildRoot: %{_buildrootdir}/%{name}-%{version}-%{release}

%description
This is a sample SPEC file for the RPM project
demonstrating how to build, package, install(deploy)


%prep
# extract the tar file containing all the sources, to the build directory.
tar -xvf %{_sourcedir}/*.tar -C %{_builddir}


%build
echo "Building the project..."
cd Application
# running ant to build the java project (could be make/maven/gradle or anything else).
ant


%install
# This is the hierarchy which is going to be inside the package (RPM/Deb) eventually.
echo "Install phase..."
mkdir -p %{buildroot}/%{name}-%{version}-%{release}
cp -R %{_builddir}/Application/output/jars/* %{buildroot}/%{name}-%{version}-%{release}


%post
#This runs post the install - maybe you want to execute the application already then
echo "Post install.."


%postun
#this runs after the uninstall
echo "Post Uninstall..."


%files
# tells which files to contain in the package and with what access rights
# the triplet contains of (<file mode>, <user>, <group>). Make the necessary changes.
%defattr(-,nir,nir)
/*


%clean
# Clean up! Must run this! after build and install steps execute, this will make
# sure that the directories are remained clean, so in case you're building another
# package, you don't want to pack the previous build's artifacts.
rm -rf %{_builddir}/*
rm -rf %{_buildrootdir}/*



That's all folks!
I hope you managed to read through this long post, and found it useful.

Enjoy,
Nir

3 comments:

  1. Great post - thanks. Do you have an example using gradle instead of ant?

    ReplyDelete
  2. Thanks!
    You can simply execute anything you want in the %build step.
    So if you want to execute gradle instead of ant, then just change it to "gradle"
    in the %build step.
    (You can run anything that would run on the shell basically.)
    Good luck

    ReplyDelete
  3. Hi, I am learning to build RPMs and I am working with your example.

    I ran into this error when running buildrpm -ba /.... Happens at Install step in spec file.

    error: File not found by glob: /home/username/rpmbuild/BUILDROOT/Example-RPM-Project-1.0-1.x86_64/*

    mkdir -p ~/Desktop/rpmbuild/BUILD/Example-RPM-Project-1.0-1/

    cp -R ~/Desktop/rpmbuild/BUILD/Application/output/jars/* ~/Desktop/rpmbuild/BUILD/Example-RPM-Project-1.0-1/

    Where is the missing file coming from?

    ReplyDelete