« Posts tagged rpm

How to speed up RPM building

If you’ve run into a situation where you wanted to repeatedly test some code in your .spec file after the %build process, you know you have to wait for the application to re-compile every time. For larger RPMs this waiting compounds to a lot of lost time.

During my build for CodeLite I ran into several issues, mostly when cleaning up the rpmlint messages from the resulting package. The average build time was around 16 minutes with the optimal make -j value. It doesn’t sound like much but if you have to compile 5 or 6 or 7 times, it adds up.

The .spec macros will do cleaning of the working directories every time you execute rpmbuild -ba and for good reason. Ideally you should re-compile every time to ensure a repeatable build environment, however if you are impatient like me try the following:

I assume you…

  • have already tested and confirmed your package compiles successfully with rpmbuild -ba
  • are confident the build process can be skipped safely without compromising the rest of the .spec file
  • understand that this is for testing purposes only and the .patch created below will not be included in the final .rpm
  • understand this particular .patch file should only be used on the system it is created

First of all, expand the source tarball to two separate directories: pkg and pkg.compiled.

Navigate to the pkg.compiled directory and go through the configure and make process mirroring the %build process from your working .spec file. You do not need to ‘make install’ and this point.

Change the directory to the pkg.compiled parent and run the following:

$ diff -Naur pkg pkg.compiled > ~/rpmbuild/SOURCES/app-version-skip_make.patch

You will be using this patch file to “trick” the %build process into skipping most of the compiling process. I say most because make may detect inconsistencies in the source directory after the patch and go through some re-linking.

The next step is to add this patch to your .spec file:

...
Patch99:    app-version-skip_make.patch
%prep
%setup -q
%patch99 -p1
...
%build
#%configure
make
...

Notice I’ve commented out %configure because the patch file will take care of this also.

You can now go through the rpmbuild -ba process again and you should see a very significant speed increase.

Using this, I was able to decrease the build time for CodeLite by 400%!

Good luck, and remember to remove this patch from your final RPM.

Creating a .spec-ial RPM for CodeLite 2.7.0

I have decided to make an RPM for codelite since there isn’t one on the Fedora repositories. The source tarball contains a .spec file, however rpmlint produces a bunch of errors and warnings on the resulting .rpm and so I started from scratch.

Having built this package manually already, I knew what the pre-requisites were. These are also listed in the *.tar.gz/BuildInfo.txt. See my previous post, for more information on compiling manually.

The rpm built successfully after making the necessary modifications to the %files directive and all that was left was rpmlint-ing the resulting package and .spec file.

I got a bunch of warnings and errors but the most cryptic was:

codelite.i386: W: unstripped-binary-or-object /usr/lib/codelite/*.so

This was repeated for every object and binary produced by make. Usually if you tell rpmlint to be more informative using the -i switch: rpmlint -i *.rpm, you can follow the instructions or clues to fix each error/warning. In this case the -i switch had no information for that message-id and Google produced no answers. I found a Common Rpmlint Issues page on the Fedora wiki, but there was nothing about “unstripped objects”. I talked to someone on #fedora-devel@Freenode and they pointed me to the strip utility.

I narrowed the *.so and binary objects to 3 strip lines and added them to the %install section as follows:

%install
rm -rf $RPM_BUILD_ROOT
make install DESTDIR=$RPM_BUILD_ROOT
strip -s -v %{buildroot}%{_bindir}/%{name}{,_indexer,_cppcheck}
strip -s -v %{buildroot}%{_libdir}/%{name}/*.so
strip -s -v %{buildroot}%{_libdir}/%{name}/debuggers/*.so

Make sure you add the strip lines after your rm -rf and make commands or the stripping will fail.

I also got a bunch of devel-file-in-non-devel-package messages from rpmlint, but these were aimed at a templates/* folder. They seemed to be example projects and thus not-critical (I should do further testing to ensure they are actually non-critical).

One error I got was the world-writable message on codelite-icons.zip in %{_datadir}/codelite/. The vendor configure script explicitly set the permissions to 0666 on this file. This was interesting because it means that they were either careless when capturing files for the configure script’s initial creation, or the application actually needs access to modify this zip file under non-privileged user context. The latter is more likely, however it seems like a bad design choice as this could cause conflicts between users’ distinct working environments.

There were also a backup files (files that end in ~) left behind in the templates/* folder prompting rpmlint to give backup-file-in-package error messages. If you want to remove all these files from your install add this to %install section of your .spec file:

$ find %{buildroot}%/ -type f -name "*~" -exec rm -rf ’{}’ \;

I, however decided to do away with this problematic templates directory by preventing the Makefile script from copying it during its install phase. To do this I had to modify the original configure script and comment out the appropriate lines (see attached .patch file).

Since modifying the vendor/upstream tarball is against best practices, the only way to do this is to create a patch and let rpmbuild take care of the modifications on the fly.

$ diff -u configure configure.fixed > ~/rpmbuild/SOURCES/codelite-2.7.0.4375-configure_fix.patch

The patch should be unique so as to not conflict with current or future patches. The next step is to modify the .spec file telling it to make the necessary modifications before %configure creates the Makefile:

...
BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
Patch0:        %{name}-%{version}-configure_fix.patch
...
%prep
%setup -q
%patch0

%build
%configure

%patch0 is a macro for the patch command and therefore accepts the usual switches such as -pN (where N is the number of directories to strip from the file being patched, see manpage). I had created the patch in the same directory as the original configure (after %setup happens the working directory is %{_builddir}).

After all this rpmlint was pretty clean. The only outstanding warning was no-manual-page-for-binary, and since the vendor tarball did not contain any manpages, I can safely ignore this.

P.S.: If rpmlint complains about not having a dictionary for en_US (enchant-dictionary-not-found) install aspell and aspell-en.

Download codelite-2.7.0.4375-configure_fix.patch