head 1.1; branch 1.1.1; access ; symbols MAXIMUM_RPM_1_0:1.1.1.1 VENDOR:1.1.1; locks ; strict; comment @# @; 1.1 date 2001.08.28.12.07.10; author rse; state Exp; branches 1.1.1.1; next ; 1.1.1.1 date 2001.08.28.12.07.10; author rse; state Exp; branches ; next ; desc @@ 1.1 log @Initial revision @ text @ Building Packages for Multiple Architectures and Operating Systems multi-platform package building While RPM certainly makes packaging software as easy as possible, it doesn't end there. RPM gives you the tools you need to build a package on different types of computers. More importantly, RPM makes it possible to build packages on different types of computers using a single spec file. Those of you that have developed software for different computers know the importance of maintaining a single set of sources. RPM lets you continue that practice through the package building phase. Before we get into RPM's capabilities, let's do a quick review of what is involved in developing software for different types of computer systems. Architectures and Operating Systems: A Primer multi-platform package building reasons for From a software engineering standpoint, there are only two major differences between any two computer systems: The architecture implemented by the computer's hardware. The system software running on the computer. The first difference is built into the computer. The architecture is the manner in which the computer system was designed. It includes the number and type of registers present in the processor, the number of machine instructions, what operations they perform, and so on. For example, every "PC" today, no matter who built it, is based on the Intel x86 architecture. The second difference is more under our control. The operating system is software that controls how the system operates. Different operating systems have different methods of storing information on disk, different ways of implementing functions used by programs, and different hardware requirements. As far as package building is concerned, two systems with the same architecture running two different operating systems, are as different as two systems with different architectures running the same operating system. In the first case, the software being packaged for different operating systems will differ due to the differences between the operating systems. In the second case, the software being packaged for different architectures will differ due to the underlying differences in hardware. This is a somewhat simplistic view of the matter, as it's common for incompatibilities to crop up between two different implementations of the same operating system on different architectures. RPM supports differences in architecture and operating system equally. If there is a tag, rpmrc file entry, or conditional that is used to support architectural differences, there is a corresponding tag, entry, or conditional that supports operating system differences. Let's Just Call Them Platforms In order to keep the duplication in this chapter to a minimum, we'll refer to a computer of a given architecture running a given operating system as a platform. If another system differs in either aspect, it is considered a different platform. OK, now that we've gotten through the preliminaries, let's look at RPM's multi-platform capabilities. What Does RPM Do To Make Multi-Platform Packaging Easier? multi-platform package building features supporting As we mentioned above, RPM supports multi-platform package building through a set of tags, rpmrc file entries, and conditionals. None of these tools are difficult to use. In fact, the hardest part of multi-platform package building is figuring out how the software needs to be changed to support different platforms. Let's take a look at each multi-platform tool RPM provides. Automatic Detection of Build Platform The first thing necessary for easy multi-platform package building is to identify which platform the package is to be built for. Except in the fairly esoteric case of cross-compilation, the build platform is the platform on which the package is built. RPM does this for you automatically, although it can be overridden at build-time. Automatic Detection of Install Platform The other important platform in package building is the platform on which the package is to be installed. Here again, RPM does this for you, though it's possible to override this when the package is installed. But there is more to multi-platform package building than simply being able to determine the platform during package building and installation. The next component in multi-platform package building is a set of platform-dependent tags. Platform-Dependent Tags RPM uses a number of tags that control which platforms can build a package. These tags make it easier for the package builder to build multiple packages automatically, since the tags will keep RPM from attempting to build packages that are incompatible with the build platform. Platform-Dependent Conditionals While the platform-dependent tags provide a crude level of multi-platform control (i.e., the package will be built or not, depending on the tags and the build platform), RPM's platform-dependent conditionals provide a much finer level of control. By using these conditionals, it's possible to excise those parts of the spec file that are specific to another platform and replace them with one or more lines that are compatible with the build platform. Now that we have a basic idea of RPM's multi-platform support features, let's take a more in-depth look at each one. Build and Install Platform Detection multi-platform package building platform detection As we mentioned above, the first step to multi-platform package building is to identify the build platform. This is done by matching information from the build system's uname output against a number of rpmrc file entries. Normally, it's not necessary to worry too much about the following rpmrc file entries, as RPM comes with a set of entries that support all platforms that currently run RPM. However, when adding support for new platforms, it will be necessary to use the following entries to add support for the new build platform. Platform-Specific <filename>rpmrc</filename> Entries platform-dependent rpmrc file entries Normally, the file /usr/lib/rpmrc contains the following rpmrc file entries. They can be overridden by entries in /etc/rpmrc or ~/.rpmrc. This is discussed more completely in . Because each entry type is available in both architecture and operating system flavors, we'll just use xxx in place of arch and os in the following descriptions. <command><replaceable>xxx</replaceable>_canon</command> — Define Canonical Platform Name and Number rpmrc file entries arch_canon rpmrc file entries os_canon platform-dependent rpmrc file entries arch_canon platform-dependent rpmrc file entries os_canon The xxx_canon entry is used to convert information obtained from the system running RPM into a canonical name and number that RPM will use internally. Here's the format: xxx_canon: <label>: <string> <value> The <label> is compared against information from uname(2). If a match is found, then <string> is used by RPM as the canonical name, and <value> is used as a unique numeric value. Here are two examples: arch_canon: sun4: sparc 3 os_canon: Linux: Linux 1 The arch_canon tag above is used to define the canonical architecture information for Sun Microsystems' SPARC architecture. In this case, the output from uname is compared against sun4. If there's a match, the canonical architecture name is set to sparc and the architecture number is set to 3. The os_canon tag above is used to define the canonical operating system information for the Linux operating system. In this case, the output from uname is compared against Linux. If there's a match, the canonical operating system name is set to Linux, and the operating system number is set to 1. The description above is not 100% complete — There is an additional step performed during the time RPM gets the system information from uname, and compares it against a canonical name. Next, let's look at the rpmrc file entry that comes into play during this intermediate step. <command>build<replaceable>xxx</replaceable>translate</command> — Define Build Platform rpmrc file entries buildarch_translate rpmrc file entries buildos_translate platform-dependent rpmrc file entries buildarch_translate platform-dependent rpmrc file entries buildos_translate The buildxxxtranslate entry is used to define the build platform information. Specifically, these entries are used to create a table that maps information from uname to the appropriate architecture/operating system name. The buildxxxtranslate entry looks like this: buildxxxtranslate: <label>: <string> The <label> is compared against information from uname(2). If a match is found, then <string> is used by RPM as the build platform information, after it has been canonicalized by xxx_canon. Here are two examples: buildarchtranslate: i586: i386 buildostranslate: Linux: Linux The buildarchtranslate tag shown above is used to define the build architecture for an Intel Pentium (or i586 as it's shown here) processor. Any Pentium-based system will, by default, build packages for the Intel 80386 (or i386) architecture. The buildostranslate tag shown above is used to define the build operating system for systems running the Linux operating system. In this case, the build operating system remains unchanged. <command><replaceable>xxx</replaceable>_compat</command> — Define Compatible Architectures rpmrc file entries arch_compat rpmrc file entries os_compat platform-dependent rpmrc file entries arch_compat platform-dependent rpmrc file entries os_compat The xxx_compat entry is used to define which architectures and operating systems are compatible with one another. It is used at install-time only. The format of the entry is: xxx_compat: <label>: <list> The <label> is a name string as defined by an xxx_canon entry. The <list> following it consists of one or more names, also defined by arch_canon. If there is more than one name in the list, they should be separated by a space. The names in the list are considered compatible to the name specified in the label. arch_compat: i586: i486 arch_compat: i486: i386 os_compat: Linux: AIX The arch_compat lines shown above illustrate how a family of upwardly compatible architectures may be represented. For example, if the build architecture was defined as an i586, the compatible architectures would be i486, and i386. However, if the build system was an i486, the only compatible architecture would be an i386. While the os_compat line shown above is entirely fictional, its purpose would be to declare AIX compatible with Linux. If it were only that simple… Overriding Platform Information At Build-Time platform information, overriding at build-time By using the rpmrc file entries discussed above, RPM usually makes the right decisions in selecting the build and install platforms. However, there might be times when RPM's selections aren't the best. Normally the circumstances are unusual, as in the case of cross-compiling software. In these cases, it is nice to have an easy way of overriding the build-time architecture and operating system. The --buildarch and --buildos options can be used to set the build-time architecture and operating system rather than relying on RPM's automatic detection capabilities. These options are added to a normal RPM build command. One important point to remember is that, although RPM does try to find the specified architecture name, it does no checking as to the sanity of the entered architecture or operating system. For example, if you enter an entirely fictional operating system, RPM will issue a warning message, and then happily build a package for it. Why? Wouldn't it make more sense for RPM to perform some sort of sanity check? In a word, no. One of RPM's main design goals was to never get in the way of the package builder. If someone has a need to override their build platform information, they should know what they're doing, and what the full implications of their actions are. Bottom line: Unless you know why you need to use --buildarch or --buildos, you probably don't need to use them. Overriding Platform Information At Install-Time platform information, overriding at install-time It's also possible to direct RPM to ignore platform information while a package is being installed. The --ignorearch and --ignoreos options, when added to any install or upgrade command, will direct RPM to proceed with the install or upgrade, even if the package's platform doesn't match the install platform. Dangerous? Yes. But it can be indispensable in certain circumstances. Like the ability to override platform information at build-time, unless you know why you need to use --ignorearch or --ignoreos, you probably don't need to use them. <command>optflags</command> — The Other <filename>rpmrc</filename> File Entry platform-dependent rpmrc file entries optflags While the optflags entry doesn't play a part in determining the build or install platform, it does play a role in multi-platform package building. The optflags entry is used to define a standard set of options that can be used during the build process, specifically during compilation. The optflags entry looks like this: optflags: <architecture> <value> For example, assume the following optflags entries were placed in an rpmrc file: optflags: i386 -O2 -m486 -fno-strength-reduce optflags: sparc -O2 If RPM was running on an Intel 80386-compatible architecture, the optflags value would be set to -O2 -m486 -fno-strength-reduce. If, however, RPM was running on a Sun SPARC-based system, optflags would be set to -O2. This entry sets the RPM_OPT_FLAGS environment variable, which can be used in the %prep, %build, and %install scripts. Platform-Dependent Tags platform-dependent tags Once RPM has determined the build platform's information, that information can be used in the build process. The first way this information can be used is to determine whether a given package should be built on a given platform. This is done through the use of four tags that can be added to a spec file. There can be many reasons to do this. For example, the software may not build correctly on a given platform. Or the software may be platform-specific, such that packaging the software on any other platform, while technologically possible, would really make no sense. The real world is not always so clear-cut, so there might even be cases where a package should be built on, say, three different platforms, but no others. By carefully using the following tags, any conceivable situation can be covered. Like the rpmrc file entries we've already discussed, there are identical tags for architecture and operating system, so we'll discuss them together. The <command>exclude<replaceable>xxx</replaceable></command> Tag excludearch tag excludeos tag platform-dependent tags excludearch platform-dependent tags excludeos The excludexxx tags are used to direct RPM to insure that the package does not attempt to build on the excluded platforms. One or more platforms may be specified after the excludexxx tags, separated by either spaces or commas. Here are two examples: ExcludeArch: sparc alpha ExcludeOS: Irix The first line prevents systems based on the Sun SPARC and Digital Alpha/AXP architectures from attempting to build the package. The second line insures that the package will not be built for the Silicon Graphics operating system, Irix. If a build is attempted on an excluded architecture or operating system, the following message will be displayed, and the build will fail: # rpm -ba cdplayer-1.0.spec Arch mismatch! cdplayer-1.0.spec doesn't build on this architecture # The excludexxx tags are meant to explicitly prevent a finite set of architectures or operating systems from building a package. If your goal is to insure that a package will only build on one architecture, then you should use the exclusivexxx tags. The <command>exclusive<replaceable>xxx</replaceable></command> Tag exclusivearch tag exclusiveos tag platform-dependent tags exclusivearch platform-dependent tags exclusiveos The exclusivexxx tags are used to direct RPM to only build the package on the specified platforms. These tags insure that, in the future, no brand-new platform will mistakenly attempt to build the package. RPM will build the package on the specified platforms only. The syntax of the exclusivexxx tags is identical to excludexxx: ExclusiveArch: sparc alpha ExclusiveOS: Irix In the first line, the package will only build on a Sun SPARC or Digital Alpha/AXP system. In the second, the package will only be built on the Irix operating system. The exclusivexxx tags are meant to explicitly allow a finite set of architectures or operating systems to build a package. If your goal is to insure that a package will not build on a specific platform, then you should use the excludexxx tag. Platform-Dependent Conditionals platform-dependent conditionals Of course, the control exerted by the excludexxx and exclusivexxx tags over package building is often too coarse. There may be packages, for example, that would build just fine on another platform, if only you could substitute a platform-specific patch file or change some paths in the %files list. The key to exerting this kind of platform-specific control in the spec file is to use RPM's conditionals. The conditionals provide a general-purpose means of constructing a platform-specific version of the spec file during the actual build process. Common Features of All Conditionals platform-dependent conditionals features of There are a few things that are common to each conditional, so let's discuss them first. The first thing is that conditionals are block-structured. The second is that conditionals can be nested. Finally, conditionals can span any part of the spec file. Conditionals Are Block Structured Every conditional is block-structured — in other words, the conditional begins at a certain point within the spec file and continues some number of lines until it is ended. This forms a block that will be used or ignored, depending on the platform the conditional is checking for, as well as the build platform itself. Every conditional starts with a line beginning with the characters %if and is followed by one of four platform-related conditions. Every conditional ends with a line containing the characters %endif. Ignoring the platform-related conditions for a moment, here's an example of a conditional block: %ifos Linux Summary: This is a package for the Linux operating system %endif It's a one-line block, but a block nonetheless. There's also another style of conditional block. As before, it starts with a %if, and ends with a %endif. But there's something new in the middle: %ifos Linux Summary: This is a package for the Linux operating system %else Summary: This is a package for some other operating system %endif Here we've replaced one summary tag with another. Conditionals Can Be Nested platform-dependent conditionals nesting Conditionals can be nested — That is, the block formed by one conditional can enclose another conditional. Here's an example: %ifarch i386 echo "This is an i386" %ifos Linux echo "This is a Linux system" %else echo "This is not a Linux system" %endif %else echo "This is not an i386" %endif In this example, the first conditional block formed by the %ifarch i386 line contains a complete %ifos — %else — %endif conditional. Therefore, if the build system was Intel-based, the %ifos conditional would be tested. If the build system was not Intel-based, the %ifos conditional would not be tested. Conditionals Can Cross Spec File Sections The next thing each conditional has in common is that there is no limit to the number of lines a conditional block can contain. You could enclose the entire spec file within a conditional, if you like. But it's much better to use conditionals to insert only the appropriate platform-specific contents. Now that we have the basics out of the way, let's take a look at each of the conditionals and see how they work. <command>%if<replaceable>xxx</replaceable></command> %ifarch conditional %ifos conditional platform-dependent conditionals %ifarch platform-dependent conditionals %ifos The %ifxxx conditionals are used to control the inclusion of a block, as long as the platform-dependent information is true. Here are two examples: %ifarch i386 alpha In this case, the block following the conditional would be included only if the build architecture was i386 or alpha. %ifos Linux This example would include the block following the conditional only if the operating system was Linux. <command>%ifn<replaceable>xxx</replaceable></command> %ifnarch conditional %ifnos conditional platform-dependent conditionals %ifnarch platform-dependent conditionals %ifnos The %ifnxxx conditionals are used to control the inclusion of a block, as long as the platform-dependent information is not true. Here are two examples: %ifnarch i386 alpha In this case, the block following the conditional would be included only if the build architecture was not i386 or alpha. %ifnos Linux This example would include the block following the conditional only if the operating system was not Linux. Hints and Kinks multi-platform package building hints There isn't much in the way of hard and fast rules when it comes to multi-platform package building. But in general, the following uses of RPM's multi-platform capabilities seem to work the best: The excludexxx and exclusivexxx tags are best used when it's known there's no reason for the package to be built on specific architectures. The %ifxxx and %ifnxxx conditionals are most likely to be used in the following areas: Controlling the inclusion of %patch macros for platform-specific patches. Setting up platform-specific initialization prior to building the software. Tailoring the %files list when the software creates platform-specific files. Given that some software is more easily ported to different platforms than others, this list is far from complete. If there's one thing to remember about multi-platform package building, it's don't be afraid to experiment! @ 1.1.1.1 log @Import book 'Maximum RPM' by Ed Bailey, version 1.0 @ text @@