Building the same code for multiple ISAs 1 Introduction Building both 32-bit and 64-bit variants of the same library is now really easy. What's more, adding more ISAs, for example SSE2 is also easy. This document explains how to change a 32-bit only spec file to support multiple ISAs. 2 Include files There are a bunch of include files in the include subdirectory that define macros useful for building code for various ISAs: base.inc - default macros, used for building 32-bit binaries automatically included by Solaris.inc, but you can include this to reset macros to the defaults after including one of the other includes below. arch64.inc - macros for building 64-bit binaries: amd64 or sparcv9 x86_sse2.inc - macros for building binaries that make use of Intel SSE2 extensions. You need to include Solaris.inc before including any of these files. What they do is, they set macros that define the compiler flags: %gcc_optflags - C compiler flags for building with gcc %suncc_optflags - C compiler flags for building with Sun Studio cc %gcc_cxx_optflags - C++ compiler flags for building with g++ %suncc_cxx_optflags - C++ compiler flags for building with Sun Studio CC %optflags - C compiler flags for the current C compiler ($CC) %cxx_optflags - C++ compiler flags for the current C++ compiler ($CXX) and update the directory macros for the given architecture: %_bindir - set to %{_prefix}/bin/, e.g. /usr/bin/amd64 %_libdir - same with /usr/lib/ %_libexecdir - same as %_libdir %_pkg_config_path - directory that contains the pkgconfig files for this ISA, e.g. /usr/lib/sparcv9/pkgconfig They also define some handy macros: can_isaexec - 1 if multiple ISAs are built, 0 if only 32-bit If 1, we can use isaexec to automatically run the executable that best matches the current system, see details in "Using isaexec" below. gtk_doc_option - always set to --disable-gtk-doc for non-default ISAs so that we only build the gtk docs for the base ISA. In the case of the base ISA, you can continue to the --without-gtk-doc or --with-gtk-doc to control whether or not to build the gtk-doc API documentation 3 Using the ISA specific include files pkgbuild processes the "child" spec files when it reads the %use line. Macros defined in the parent spec file before the %use line are visible in the child spec file, macros defined or redefined after the %use line do not affect the child spec file. This means that changing %{_libdir} to /usr/lib/amd64 using %define before the %use line will cause the libdir of the child spec to be /usr/lib/amd64, if it uses the --libdir=%{_libdir} configure option. We can also control the compiler flags used in the child spec by defining optflags before the %use line and setting CFLAGS="%optflags" in the child spec. So adding a new ISA of a library is as simple as including the appropriate .inc file (which sets up optflags, _libdir, etc.) and then using %use: %include Solaris.inc <- always include before arch64.inc %ifarch amd64 sparcv9 %include arch64.inc <- sets %optflags, %_libdir, etc. %use flac_64 = flac.spec <- process the child spec %endif %include base.inc <- reset %optflags, %_libdir, etc. %use flac = flac.spec <- process the child spec again note that we assign a different label from the 64-bit variant Then we add another section for %prep: %prep rm -rf %name-%version mkdir %name-%version %ifarch amd64 sparcv9 mkdir %name-%version/%_arch64 %flac_64.prep -d %name-%version/%_arch64 %endif mkdir %name-%version/%base_arch %flac.prep -d %name-%version/%base_arch The above sets up the following directory structure under %_topdir/BUILD (considering an amd64 platform for this example): .../packages/BUILD | +-----> SUNWflac-1.1.4 | +-----> i86 | | | +-----> flac-1.1.4 | +-----> amd64 | +-----> flac-1.1.4 Now we need to build both source trees: %build %ifarch amd64 sparcv9 %flac_64.build -d %name-%version/%_arch64 %endif %flac.build -d %name-%version/%base_arch And then install both trees: %install rm -rf $RPM_BUILD_ROOT %ifarch amd64 sparcv9 %flac_64.install -d %name-%version/%_arch64 %endif %flac.install -d %name-%version/%base_arch Finally, update %files to include the 64-bit binaries: %ifarch amd64 sparcv9 %dir %attr (0755, root, bin) %{_bindir}/%{_arch64} %{_bindir}/%{_arch64}/* %dir %attr (0755, root, bin) %{_libdir}/%{_arch64} %{_libdir}/%{_arch64}/lib*.so* %endif Note that we didn't need to touch base-specs/flac.spec for this. We do need to make sure that: - it sets CFLAGS="%optflags" and LDFLAGS="%{_ldflags}" - it passes at least --libdir=%{_libdir} and --bindir=%{_bindir} to configure (for modules using the GNU autotools) 4 Using isaexec There is one more trick we can do: setting up the executables so that the OS will automatically execute the one best suited for the architecture it's running on. To do that, we need to move the base executables into a subdirectory under the bin directory and hard link /usr/lib/isaexec using the name of the executable. isaexec will look for executables with the same name under the ISA-specific subdirectories, in the order printed by isalist, for example: laca@ultra20:~> isalist amd64 pentium_pro+mmx pentium_pro pentium+mmx pentium i486 i386 i86 I.e. the binary in bin/amd64 will be run if it's found, if not then bin/pentium_pro+mmx, etc. finally i86. In the following example we're moving the 32-bit "flac" binary into the i86 subdir. Note that the 64-bit version is automatically installed in the amd64 subdir, because arch64.inc sets _bindir to %{_prefix}/bin/amd64. This goes into %install: %if %can_isaexec mkdir $RPM_BUILD_ROOT%{_bindir}/%{base_isa} cd $RPM_BUILD_ROOT%{_bindir} mv flac metaflac %{base_isa} ln -s ../../usr/lib/isaexec flac ln -s ../../usr/lib/isaexec metaflac %endif In the %file list, %{_bindir}/flac and %{_bindir}/metaflac must be flagged at hard links using the %hard flag. You need pkgbuild 1.1.2 or later for hard links. %hard %{_bindir}/flac %hard %{_bindir}/metaflac