Next: , Previous: , Up: Particular Modules   [Contents][Index]


17.19 Supporting Relocation

It has been a pain for many users of GNU packages for a long time that packages are not relocatable. It means a user cannot copy a program, installed by another user on the same machine, to his home directory, and have it work correctly (including i18n). So many users need to go through configure; make; make install with all its dependencies, options, and hurdles.

Red Hat, Debian, and other binary distributions solve the “ease of installation” problem, but they hardwire path names, usually to /usr or /usr/local. This means that users need root privileges to install a binary package, and prevents installing two different versions of the same binary package.

A relocatable program can be moved or copied to a different location on the file system. It is possible to make symlinks to the installed and moved programs, and invoke them through the symlink. It is possible to do the same thing with a hard link only if the hard link file is in the same directory as the real program.

The relocatable-prog module aims to ease the process of making a GNU program relocatable. It helps overcome two obstacles. First, it aids with relocating the hard-coded references to absolute file names that GNU programs often contain. These references must be fixed up at runtime if a program is to be successfully relocated. The relocatable-prog module provides a function relocate that does this job.

Second, the loader must be able to find shared libraries linked to relocatable executables or referenced by other shared libraries linked to relocatable executables. The relocatable-prog module helps out here in a platform-specific way:

You can make your program relocatable by following these steps:

  1. Import the relocatable-prog module. For libraries, use the relocatable-lib or relocatable-lib-lgpl module, if the libraries are independent. For installing multiple libraries, at least one of which depends on another one, use the relocatable-prog module. If you need more than one module, or you need to use them with different settings, you will need multiple copies of gnulib (see Using Gnulib for both a library and a program).
  2. In every program, add to main as the first statement (even before setting the locale or doing anything related to libintl):
    set_program_name (argv[0]);
    

    The prototype for this function is in progname.h.

  3. If you want your code to be portable to platforms that do not support automatic initialization, call set_relocation_prefix.
  4. Everywhere where you use a constant pathname from installation-time, wrap it in relocate so it gets translated to the run-time situation. Example:
    bindtextdomain (PACKAGE, LOCALEDIR);
    

    becomes:

    bindtextdomain (PACKAGE, relocate (LOCALEDIR));
    

    The prototype for this function is in relocatable.h.

    There is also a variant of this function, named relocate2, that makes it easy to reclaim the memory allocated by the call.

  5. The set_program_name function can also configure some additional libraries to relocate files that they access, by defining corresponding C preprocessor symbols to 1. The libraries for which this is supported and the corresponding preprocessor symbols are:
    libcharset

    DEPENDS_ON_LIBCHARSET

    libiconv

    DEPENDS_ON_LIBICONV

    libintl

    DEPENDS_ON_LIBINTL

    Defining the symbol for a library makes every program in the package depend on that library, whether the program really uses the library or not, so this feature should be used with some caution.

  6. If your package installs shell scripts, also import the relocatable-script module. Then, near the beginning of each shell script that your package installs, add the following:
    @relocatable_sh@
    
    prefix="@prefix@"
    exec_prefix="@exec_prefix@"   # usually needs $prefix.
    datarootdir="@datarootdir@"   # usually needs $prefix.
    
    if test "@RELOCATABLE@" = yes; then
      bindir="@bindir@"
      orig_installdir="$bindir" # see Makefile.am's *_SCRIPTS variables
      func_find_curr_installdir # determine curr_installdir
      func_find_prefixes
      relocate () {
        echo "$1/" \
        | sed -e "s%^${orig_installprefix}/%${curr_installprefix}/%" \
        | sed -e 's,/$,,'
      }
    else
      relocate () {
        echo "$1"
      }
    fi
    
    # Get some relocated directory names.
    sysconfdir=`relocate "@sysconfdir@"`          # usually needs $prefix.
    some_datadir=`relocate "@datadir@/something"` # usually needs $datarootdir.
    bindir=`relocate "@bindir@"`       # usually needs $exec_prefix, hence $prefix.
    

    You must adapt the definition of orig_installdir, depending on where the script gets installed. Also, at the end, instead of sysconfdir and some_datadir, transform those variables that you need.

  7. If your package installs Perl scripts, also import the relocatable-perl module. Then, near the beginning of each Perl script that your package installs, add the following:
    @relocatable_pl@
    if ("@RELOCATABLE@" eq "yes") {
      my $exec_prefix = "@exec_prefix@";
      my $orig_installdir = "@bindir@"; # see Makefile.am's *_SCRIPTS variables
      my ($orig_installprefix, $curr_installprefix) =
        find_prefixes($orig_installdir, find_curr_installdir());
    
      # the subroutine is defined whether or not the enclosing block is executed
      sub relocate {
        my ($dir) = @_;
        if ("@RELOCATABLE@" eq "yes") {
          $dir =~ s%^$orig_installprefix/%$curr_installprefix/%;
          $dir =~ s,/$,,;
        }
        return $dir;
      }
    }
    
    # Get some relocated directory names.
    # (The gnulib module 'configmake' can help with this.)
    $sysconfdir = relocate("@sysconfdir@");
    $some_datadir = relocate(@datadir@/something");
    

    You must adapt the definition of $orig_installdir, depending on where the script gets installed. Also, at the end, instead of sysconfdir and some_datadir, transform those variables that you need.

  8. In your Makefile.am, for every program foo that gets installed in, say, $(bindir), you add:
    foo_CPPFLAGS = -DINSTALLDIR=\"$(bindir)\"
    if RELOCATABLE_VIA_LD
    foo_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(bindir)`
    endif
    

    When building gnulib to use with a relocatable library, you need to define the preprocessor symbol IN_LIBRARY. You may also want to build with ENABLE_COSTLY_RELOCATABLE, in which case you will also need to define INSTALLDIR. The following fragment can be added to an override Makefile.am used to build gnulib (see Modifying the build rules of a Gnulib import directory).

    AM_CPPFLAGS += -DIN_LIBRARY -DENABLE_COSTLY_RELOCATABLE
    
    if SHLIBS_IN_BINDIR
    AM_CPPFLAGS += -DINSTALLDIR=\"$(bindir)\"
    else
    AM_CPPFLAGS += -DINSTALLDIR=\"$(libdir)\"
    endif
    

    SHLIBS_IN_BINDIR is defined in configure.ac as follows:

    AM_CONDITIONAL([SHLIBS_IN_BINDIR],
                   [case "$host_os" in mingw* | cygwin*) true;; *) false;; esac])
    
  9. In your Makefile.am, for every library libfoo that gets installed in, say, $(libdir), you add:
    if RELOCATABLE_VIA_LD
    libfoo_la_LDFLAGS = `$(RELOCATABLE_LDFLAGS) $(libdir)`
    endif
    
  10. Add a couple of variable assignments to your Makefile.am.

    If your package (or any package you rely on, e.g. gettext-runtime) will be relocated together with a set of installed shared libraries, then set RELOCATABLE_LIBRARY_PATH to a colon-separated list of those libraries’ directories, e.g.

    RELOCATABLE_LIBRARY_PATH = $(libdir)
    

    If your config.h is not in $(top_builddir), then set RELOCATABLE_CONFIG_H_DIR to its directory, e.g.

    RELOCATABLE_CONFIG_H_DIR = $(top_builddir)/src
    

Next: func, Previous: Library version handling, Up: Particular Modules   [Contents][Index]