Building Packages with OpenPKG

OpenPKG uses a version of RPM modified to work entirely within a package's directory structure. There are some changes in the way RPMs are built, some the result of encouraging non-root users to build the RPMs, others in the way the RPM sources, builds, and packages are handled in the file system.

Changes in RPM

There have been several changes to the ``rpm'' program:

Packages are built by non-root users, limiting the amount of damage done by bad specfiles.

More rational directory structure -- instead of the SOURCES, SPECS, BUILD, RPMS, and SRPMS directories found under the standard RPM directory, OpenPKG's RPMs are all built under the %{l_prefix}/RPM/{PKG,SRC,TMP} directories. The RPM database is under the %{l_prefix}/RPM/DB directory instead of someplace under /var/lib.

Each package's sources, patches and spec files are in one directory, %{l_prefix}/RPM/SRC/%{name} instead of being split between the SOURCES and SPECS directories as done with standard RPM. I find this much easier to use than having spec files and sources in different directories.

The BUILD directory is replaced by the %{l_prefix}/RPM/TMP directory, and by convention the BUILD_ROOT directory is the same as the build directory with a ``-root'' suffix. This convention makes it easy to look at the installed files using $PWD-root when in the build directory.

A second advantage to using the %{l_prefix}/RPM/TMP directory is that this can be easily symlinked to a directory on a UFS file system when running on Apple's OS\ X OS where the default file system is case insensitive.

The specfile handling has been cleaned up so that macros such as %setup and %patch may be indented from the left margin. This permits indentation of all the major specfile sections for easier reading.

The specfiles can contain options which control the build process making it far easier to build complex programs such as ``apache''.

One of the most important features of the OpenPKG system is the ``openpkg'' script that is used to build and maintain OpenPKG instances. This works in conjunction with the user's ~/.openpkg/build file which contains the location of the SRPMS, and various build options to generate scripts to rebuild and install all packages necessary to one or more packages.

``openpkg build -Ua > /tmp/build.all'' creates a script that will rebuild all packages that are installed on the system, and out of date compared to the archive.

``openpkg build apache > /tmp/build.apache'' creates a script that will build apache and any other packages that apache needs based on the options selected in the ~/.openpkg/build file.


# OpenPKG default parameters for ``openpkg build''
# | -r /e/openpkg/SRC
# | -f /e/openpkg/SRC/00INDEX.rdf.bz2
# -f ftp://ftp.celestial.com/mirrors/ftp.openpkg.org/current/00INDEX.rdf
# -f ftp://ftp.celestial.com/mirrors/ftp.openpkg.org/release/1.2/00INDEX.rdf
#-r ftp://ftp.celestial.com/private/ftp.openpkg.org/current/SRC
#-f ftp://ftp.celestial.com/private/ftp.openpkg.org/current/SRC/00INDEX.rdf.bz2
-r ftp://ftp.celestial.com/private/ftp.openpkg.org/release/1.3/SRC
-f ftp://ftp.celestial.com/private/ftp.openpkg.org/release/1.3/SRC/00INDEX.rdf.bz2
#-f ftp://ftp.celestial.com/private/ftp.openpkg.org/release/1.3/SRC/00INDEX.rdf
-P sudo
# -N sudo

# the newest version breaks postfix
# -E openldap
# We're modifying these ourselves
# -E postfix
# -E myodbc
# -E unixodbc
-E smail

-D smail::with_sendmail=no
-D samba::with_docs
-D coreutils::with_legacy
-D with_cvs_badroot
-D gdbm::with_ndbm
-D perl::with_shared
# openssh options
# -D openssh::with_pam
-D openssh::with_pcre
-D openssh::with_tcp_wrappers
-D openssh::with_x11
# This is for vim at least.
-D with_x11
-D vim::with_x11=no
# Apache
-D apache::with_mod_auth_mysql
-D apache::with_mod_php_gettext
-D with_mod_access_referer=no
-D with_mod_auth_ldap
-D with_mod_auth_pam=no
-D with_mod_dav=no
-D apache::with_mod_php_pgsql=yes
-D with_mod_fastcgi=no
-D with_mod_gzip
-D with_mod_layout=no
-D with_mod_macro=no
-D with_mod_perl
-D with_mod_php
-D with_mod_php3=no
-D with_mod_php3_ftp=no
-D with_mod_php3_gd=no
-D with_mod_php3_jpeg=no
-D with_mod_php3_mysql=no
-D with_mod_php3_openssl=no
-D with_mod_php3_zlib=no
-D with_mod_php_bc
-D with_mod_php_bzip2
-D with_mod_php_calendar
-D with_mod_php_curl
-D with_mod_php_db
-D with_mod_php_debug
-D with_mod_php_freetype
-D with_mod_php_ftp
-D with_mod_php_gd=no
-D with_mod_php_imap
-D with_mod_php_java=no
-D with_mod_php_mhash
-D with_mod_php_mm
-D with_mod_php_mysql
-D with_mod_php_oci8=no
-D with_mod_php_openldap
-D with_mod_php_openssl
-D with_mod_php_pcre
-D with_mod_php_pdflib
-D with_mod_php_transsid
-D with_mod_php_xml
-D with_mod_php_zlib
-D with_mod_relocate=no
-D with_mod_roaming=no
-D with_mod_ssl
-D with_mod_throttle=no
# perl dbi
-D perl-dbi::with_dbd_mysql
-D perl-dbi::with_dbd_pg
-D perl-dbi::with_dbd_odbc
# perl-xml
-D with_libxml
-D with_libxslt
# glib
-D with_threads
# postfix
-D postfix::with_tls
-D postfix::with_ldap
-D postfix::with_whoson
# postgres
-D postgresql::with_cxx=yes
-D postgresql::with_perl=yes
-D postgresql::with_odbc=yes
-D postgresql::with_compat=no

Building RPMS

RPM works on the principle of building software packages from pristine sources, applying patches as necessary, compiling, and building binary and source RPM files under the control of a single specification file.


##
##  gcc.spec -- OpenPKG RPM Specification
##  Copyright (c) 2000-2003 The OpenPKG Project <http://www.openpkg.org/>
##  Copyright (c) 2000-2003 Ralf S. Engelschall <rse@engelschall.com>
##  Copyright (c) 2000-2003 Cable & Wireless <http://www.cw.com/>
##
##  Permission to use, copy, modify, and distribute this software for
##  any purpose with or without fee is hereby granted, provided that
##  the above copyright notice and this permission notice appear in all
##  copies.
##
##  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
##  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
##  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
##  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
##  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
##  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
##  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
##  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
##  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
##  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
##  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
##  SUCH DAMAGE.
##

#   package version
%define       V_full         3.3
%define       V_comp         %nil
%define       V_bounds       1.01
%define       V_spp          3

#   package information
Name:         gcc
Summary:      GNU Compiler Collection
URL:          http://gcc.gnu.org/
Vendor:       Free Software Foundation
Packager:     The OpenPKG Project
Distribution: OpenPKG [CORE]
Group:        Language
License:      GPL
Version:      %{V_full}
Release:      1.3.0

#   package options
%option       with_cxx       yes
%option       with_optimize  yes
%option       with_binutils  yes
%option       with_threads   yes
%option       with_bounds    no
%option       with_spp       no
%option       with_gcc       no

#   options sanity check
%if "%{with_bounds}" == "yes" && "%{with_spp}" == "yes"
RPM ERROR: options with_bounds and with_ssp conflict
%endif

#   list of sources
Source0:      ftp://gcc.gnu.org/pub/gcc/releases/gcc-%{version}/gcc-%{version}.tar.bz2
Patch0:       gcc.patch
Patch1:       http://web.inter.nl.net/hcc/Haj.Ten.Brugge/bounds-checking-gcc-%{version}-%{V_bounds}.patch.bz2
Patch2:       http://www.trl.ibm.com/projects/security/ssp/gcc3_3/protector-%{version}-%{V_spp}.tar.gz

#   build information
Prefix:       %{l_prefix}
BuildRoot:    %{l_buildroot}
BuildPreReq:  OpenPKG, openpkg >= 1.3.0, make
PreReq:       OpenPKG, openpkg >= 1.3.0
%if "%{with_binutils}" == "yes"
BuildPreReq:  binutils >= 2.13
PreReq:       binutils >= 2.13
%endif
AutoReq:      no
AutoReqProv:  no
%if "%{with_gcc}" == "yes"
Provides:     gcc = %{version}-%{release}
%endif

%description
    The GNU Compiler Collection (GCC) provides a standard conforming and
    highly portable ISO C and ISO C++ compiler.

%prep
    %setup -q -n gcc-%{version}
    %patch0 -p0
%if "%{with_bounds}" == "yes"
    %patch1 -p1
%endif
%if "%{with_spp}" == "yes"
    ( cd gcc
      %{l_gzip} -d -c %{PATCH protector-%{version}-%{V_spp}.tar.gz} | %{l_tar} xf -
      %{l_patch} -p1 <protector.dif ) || exit $?
%endif
    %{l_shtool} subst -v -s \
        -e "s;PREFIX_INCLUDE_DIR;PREFIX_INCLUDE_DIR_DISABLED;g" \
        gcc/configure

%build
    #   create build sub-directory
    mkdir obj
    cd obj

    #   determine ld(1) and as(1) usage
    l_with_gnu_ld_as=""
%if "%{with_binutils}" == "yes"
    l_with_gnu_ld_as="${l_with_gnu_ld_as} --with-gnu-ld --with-ld=%{l_prefix}/bin/ld"
    l_with_gnu_ld_as="${l_with_gnu_ld_as} --with-gnu-as --with-as=%{l_prefix}/bin/as"
%else
    case "%{l_target}" in
       *-linux* | *-freebsd* )
           l_with_gnu_ld_as="${l_with_gnu_ld_as} --with-gnu-as --with-gnu-ld"
           ;;
    esac
%endif

    #   determine threads usage
%if "%{with_threads}" == "yes"
    l_enable_threads="posix"
%else
    l_enable_threads="single"
%endif

    #   determine language usage
    l_enable_languages="c"
%if "%{with_cxx}" == "yes"
    l_enable_languages="${l_enable_languages},c++"
%endif

    #   configure the package
    CC="%{l_cc}" \
    CFLAGS="%{l_cflags}" \
    ../configure \
        --prefix=%{l_prefix} \
        --exec-prefix=%{l_prefix} \
        --includedir=%{l_prefix}/include/gcc%{V_comp} \
        --with-gxx-include-dir=%{l_prefix}/include/g++%{V_comp} \
        --with-local-prefix=%{l_prefix}/lib/gcc%{V_comp}-lib \
        --enable-languages="${l_enable_languages}" \
        --enable-threads="${l_enable_threads}" \
        --disable-maintainer-mode \
        --disable-shared \
        --disable-nls \
        ${l_with_gnu_ld_as}

    #   explicitly redirect remaining gcc-lib directories
    %{l_shtool} subst -v -s \
        -e "s;/gcc-lib/;/gcc%{V_comp}-lib/;" \
        `find . -name Makefile -type f -print`

    #   determine build flags
    l_cflags=""
    l_boot_cflags=""
    l_libcflags="-g"
    l_libcxxflags="-g"
%if "%{with_binutils}" == "yes"
    #   at least GNU as from GNU binutils supports -pipe always
    l_boot_cflags="$l_boot_cflags -pipe"
    l_libcxxflags="$l_libcxxflags -pipe"
%endif
%if "%{with_optimize}" == "yes"
    #   conservatively optimize the generated program code
    #   (also _tune_ for particular CPUs, but _without_ requiring these CPUs!)
    l_cflags="$l_cflags -O"
    l_boot_cflags="$l_boot_cflags -O2 -fomit-frame-pointer -funroll-loops"
    case "%{l_target}" in
        *x86-*     ) l_boot_cflags="$l_boot_cflags -mcpu=pentium3" ;;
        *sparc64-* ) l_boot_cflags="$l_boot_cflags -mtune=v9"      ;;
    esac
    l_libcxxflags="$l_libcxxflags -O2 -fno-implicit-templates"
%else
    #   else do no optimizations at all to reduce problems to minimum in advance
    l_boot_cflags="$l_boot_cflags -O0"
    l_libcxxflags="$l_libcxxflags -O0"
%endif

    #   build the package
    %{l_make} %{l_mflags} \
        MAKE="%{l_make} %{l_mflags}" \
        BOOT_CFLAGS="${l_boot_cflags}" \
        CFLAGS="${l_cflags}" \
        LIBCFLAGS="${l_libcflags}" \
        LIBCXXFLAGS="${l_libcxxflags}" \
        bootstrap-lean

%install
    rm -rf $RPM_BUILD_ROOT

    #   fetch GNU platform triple
    triple=`./config.guess`
    triple=`./config.sub ${triple}`

    #   perform the standard installation procedure
    ( cd obj
      %{l_make} %{l_mflags} install DESTDIR=$RPM_BUILD_ROOT
    ) || exit $?

    #   cleanup installation tree
    mv $RPM_BUILD_ROOT%{l_prefix}/lib/lib*.a \
       $RPM_BUILD_ROOT%{l_prefix}/lib/gcc%{V_comp}-lib/${triple}/%{V_full}/
    for multilib in `$RPM_BUILD_ROOT%{l_prefix}/bin/gcc --print-multi-lib`; do
        subdir=`echo "$multilib" | sed -e 's/;.*$//'`
        [ ".$subdir" = .. ] && continue
        mv $RPM_BUILD_ROOT%{l_prefix}/lib/$subdir/lib*.a \
           $RPM_BUILD_ROOT%{l_prefix}/lib/gcc%{V_comp}-lib/${triple}/%{V_full}/$subdir/
        rm -rf $RPM_BUILD_ROOT%{l_prefix}/lib/$subdir
    done
    mv $RPM_BUILD_ROOT%{l_prefix}/${triple}/include/* \
       $RPM_BUILD_ROOT%{l_prefix}/lib/gcc%{V_comp}-lib/${triple}/%{V_full}/include/ \
           >/dev/null 2>&1 || true

    #   strip installation tree
    rm -rf $RPM_BUILD_ROOT%{l_prefix}/${triple}
    rm -rf $RPM_BUILD_ROOT%{l_prefix}/man/man7   >/dev/null 2>&1 || true
    rm -f  $RPM_BUILD_ROOT%{l_prefix}/lib/*.la   >/dev/null 2>&1 || true
    rm -f  $RPM_BUILD_ROOT%{l_prefix}/bin/*-gcc* >/dev/null 2>&1 || true
%if "%{with_cxx}" == "yes"
    rm -f  $RPM_BUILD_ROOT%{l_prefix}/bin/*-c++  >/dev/null 2>&1 || true
    rm -f  $RPM_BUILD_ROOT%{l_prefix}/bin/*-g++  >/dev/null 2>&1 || true
%endif
    strip $RPM_BUILD_ROOT%{l_prefix}/bin/* >/dev/null 2>&1 || true
    for prog in cc1 cc1plus collect2 cpp; do
        strip $RPM_BUILD_ROOT%{l_prefix}/lib/gcc%{V_comp}-lib/${triple}/%{V_full}/${prog} \
            >/dev/null 2>&1 || true
    done

    #   bump up installation tree
    ln $RPM_BUILD_ROOT%{l_prefix}/bin/gcc \
       $RPM_BUILD_ROOT%{l_prefix}/bin/cc
    ln $RPM_BUILD_ROOT%{l_prefix}/man/man1/gcc.1 \
       $RPM_BUILD_ROOT%{l_prefix}/man/man1/cc.1
%if "%{with_cxx}" == "yes"
    ln $RPM_BUILD_ROOT%{l_prefix}/man/man1/g++.1 \
       $RPM_BUILD_ROOT%{l_prefix}/man/man1/c++.1
%endif

    #   resolve filename conflicts
%if "%{with_gcc}" != "yes" && "%{V_comp}" != ""
    ( cd $RPM_BUILD_ROOT%{l_prefix}/bin
      for file in *; do
          mv ${file} ${file}%{V_comp}
      done
    ) || exit $?
    ( cd $RPM_BUILD_ROOT%{l_prefix}/info
      for file in *; do
          mv ${file} `echo ${file} | sed -e 's;^\([^.]*\)\(\..*\)$;\1%{V_comp}\2;'`
      done
    ) || exit $?
    ( cd $RPM_BUILD_ROOT%{l_prefix}/man/man1
      for file in *; do
          mv ${file} `echo ${file} | sed -e 's;^\([^.]*\)\(\..*\)$;\1%{V_comp}\2;'`
      done
    ) || exit $?
%endif

    #   determine installation file list
    %{l_rpmtool} files -v -ofiles -r$RPM_BUILD_ROOT %{l_files_std}

%files -f files

%clean
    rm -rf $RPM_BUILD_ROOT