5dollarwhitebox.org - theboxownsyou

  • blog
  • projects
  • articles
  • tech wiki
  • about
  • login
Home › RPM Packaging Reference › RPM Packaging Illuminated

RSS Feed

If Conditions, the Bit Operator, and Passing command line options

drks — Wed, 2007-03-28 01:34

The RPM Spec file provides a number of ways to deal with differing conditions that alter the way the package should build. I myself use condition statements heavily in order to use a single spec file that builds properly on multiple Operating Systems (Redhat EL3, EL4, etc).

Consider the following situation:

When building a spec for PHP, one of the requirements for php-imap is '/usr/include/imap/c-client.h'. Now, on a Redhat EL3 box this file is provided by the package 'imap-devel'. However, on Redhat EL4 the packages changed and this particular file is now provided by 'libc-client-devel'.

The reason Redhat doesn't maintain a single spec is because the version of software on Rhel3 is never the same as the versions of software on Rhel4 because they lock in version numbers before official release. However, for me I want to build the latest version of PHP for both Rhel3 and Rhel4 the same. Therefore, it wouldn't really make any sense to maintain two different spec files. If I did, you would have two entries like so:

Rhel3:

BuildRequires: imap-devel

Rhel4:

BuildRequires: libc-client-devel



Now, you decide "he's right... If I can do this all in a single spec, why not?". Exactly. So now, lets make some changes to our spec and that allow for this. At the top of your spec, add the following:

%define _with_rhel3 1
%define rhel3 %{?_with_rhel3:1}%{!?_with_rhel3:0}
%define rhel4 %{?_with_rhel4:1}%{!?_with_rhel4:0}
%define os_ver_tag %{?_with_rhel3:.rhel3}%{?_with_rhel4:.rhel4}



The first line defines the variable '_with_rhel3' and sets it to 1. If you think of this logically, we are saying 'This spec is for a Rhel3 box'. The second and third lines read that definition and set the appropriate variables to '1' as well.

Now this might seem redundant, but there is a reason for this. In the future it should be clear, however the simple reason is that you can't call a '%if' statement on a variable that hasn't been define, however with the bit operator '%{?}' you can. For our example above, we would not be able to do the following:

%if _with_rhel4
...do something
%endif



We can't do it, because rpmbuild will complain that '_with_rhel4' has not been defined. Now, what we can do is this:

%if rhel4
...do something for rhel4
%endif



The difference is that we have defined 'rhe4' to equal zero, but at least it is define. Back to our example, lets pick apart the following line to better understand it:

%define rhel3 %{?_with_rhel3:1}%{!?_with_rhel3:0}



Broken up in sections:

  • %define rhel3
  • %{?_with_rhel3:1}
  • %{!?_with_rhel3:0}



You can see that we are defining the variable 'rhel3'. The second section displays our 'bit operator'. The bit operator works with '1' and '0' values. On of Off. The following statements are equivalent:

%{?_with_rhel3:1}

If the variable '_with_rhel3' is '1' then output the value after the colon ':' (in this case is 1 also... which is confusing I know).



I understand the use of 1s and 0s in this example is confusing, but it is something you will see. Lets take our os_ver_tag line for a better example:

%define os_ver_tag %{?_with_rhel3:.rhel3}%{?_with_rhel4:.rhel4}



This is saying:

  • If the variable '_with_rhel3' is true (or equal to 1) then output '.rhel3'.
  • if the variable '_with_rhel4' is true (or equal to 1) then output '.rhel4'



So, again the following are the same:

%define _with_rhel3 1
%define rhel3 %{?_with_rhel3:1}%{!?_with_rhel3:0}
%define rhel4 %{?_with_rhel4:1}%{!?_with_rhel4:0}
%define os_ver_tag %{?_with_rhel3:.rhel3}%{?_with_rhel4:.rhel4}

%define _with_rhel3 1
%define rhel3 1
%define os_ver_tag .rhel3

%define _with_rhel4 1
%define rhel4 1
%define os_ver_tag .rhel4



You can see how we can hard code definitions, or using our first example have a dynamic configuration that changes with a single definition. Now you can add '%{os_ver_tag}' to you Release line like this:

Release: 1%{os_ver_tag}



The output will be:

Release: 1.rhel3



This is helpful when you are building the same software for different OS versions. Now back to our Requires definition where we use our changes. With the OS definitions in place, we can now have the following replace our two separate files:

%if %{rhel3}
BuildRequires: imap-devel
%elseif %{rhel4}
BuildRequires: libc-client-devel
%endif



Here we see the use of our if/elseif/else standard condition statement. This literally says:

  • If building for rhel3, then output 'BuildRequires: imap-devel'
  • elseif we are building for rhel4, then output 'BuildRequires: libc-client-devel



You could also change '%elseif %{rhel4}' to '%else' if you want a generic setting if nothing else matches in the condition statement previously.

Note: I have found that the if/elseif/else condition statement does not always act as expected when you go above 2 elseif/else conditions. Remember that if you have no way of explaining why your condition isn't working the way it logically should.



You have now seen the condition and bit operators in action a bit. Take some time to look through this PHP spec file to see how it is also used for adding optional additional modules (pay close attention to the configure lines). Searching for 'mhash' and 'mcrypt' will be helpful to find the conditions in action:

  • php5.bjd.spec


One more thing I want to go over is passing command line options. A simple example is defining a variable at command line like this:

rpmbuild -ba example.spec --define="_installpath /usr/local/mypath"



This is essentially the same as having the following definition in the spec file itself:

%define _installpath /usr/local/mypath



This can be handy for making changes for one-off build. I often use this when building in a non-standard directory like this:

rpmbuild -ba example.spec --define="_topdir /home/wdierkes/usr/src/redhat"



You can define anything this way that you can in the spec itself.

Another command line option is the '--with' and '--without' flags. These flags are used to enable or disable configure options for the build. Say I wanted to remove the following line from my example above:

%define _with_rhel3 1



If I want to specify 'rhel3' or 'rhel4' add build time, I can simply pass in that option like this:

rpmbuild -ba example.spec --with rhel3



Which is the same as defining it in the spec file:

%define _with_rhel3 1



Additionally, I could disable the option with '--without rhel3'. Now, that doesn't make much sense for our os_ver_tag example, but it does for optional additional modules in a package such as 'mhash' or 'mcrypt' in the PHP spec linked above.

This is a rough draft for this section, so I will hopefully be cleaning it up soon. Please send any comments to wdierkes [at] 5dollarwhitebox [dot] org.

I will be adding more sections soon to hopefully cover the following:

  • Separate packages within a single RPM
  • Troubleshooting issues
  • Signing RPM Packages with GPG Keys

Take care!

‹ Macros and Variables up Reference / Links ›
  • Printer-friendly version



Who's online

There are currently 0 users and 1 guest online.
  • blog
  • projects
  • articles
  • tech wiki
  • about
  • login

5dollarwhitebox.org is not responsible in anyway for actions performed based on information found on this site.