Boost logo

Boost-Build :

From: Zack Weinberg (zackw_at_[hidden])
Date: 2006-08-19 19:34:33


Please bear with me while I explain my problem.

I'm involved in the development of Monotone (http://venge.net/monotone) which
uses several Boost libraries, including boost::regex. We do not ship our own
copy of Boost; on systems like Debian Linux with a rich prepackaged set of
libraries we rely on the distributor's version. We've noticed that on many such
systems the prepackaged boost::regex includes ICU support, which we do not use.
 We care because the ICU libraries drag in libpthread, and therefore, even if we
use the non-threadsafe variants of the Boost libraries, we get thread-safe
behavior from the C++ runtime, which hurts performance to the tune of 15% of
runtime on some loads(!) [1]

A logical solution to this problem is to split libboost_regex.so into two shared
objects, let's call them libboost_regex_core.so and libboost_regex_icu.so, and
arrange that the latter is only loaded at runtime if the program actually uses
the ICU support. Looking at the source code, I see that ICU is well-isolated in
its own source file, so it *could* be as simple as compiling
libs/regex/src/icu.cpp into libboost_regex_icu.so and everything else into
libboost_regex_core.so ... except that without further cleverness, this would
break both build-time and runtime compatibility. Everyone currently expects
that they can just do -lboost_regex at build time, and that a single DT_NEEDED
for libboost_regex_<suffix>.so.NNN is sufficient at runtime.

Now, at least with some linkers (notably GNU ld 2.17 or later) cleverness is
possible: I append to this message a demonstration shell script which does it
for a pair of mocked-up shared libraries. The key is the two variations of
libfoobar.so -- the runtime version that exports no symbols but brings in both
the 'base' libraries with DT_NEEDEDs, and the build-time version that is really
a script telling GNU ld to pick up only those 'base' libraries that are needed.
 With this arrangement, existing programs linked against libboost_regex continue
to work (and continue to drag in libraries they may not need); freshly compiled
programs get only what they need. Don't take my word for it; run the script,
then do ldd on the *_xdeps and *_better executables, and see the difference for
yourselves.

So my actual question to you, is how do I implement all of this in a Jamfile.
Naturally I want to do this only when it's going to work (i.e. when the system
has a toolset that understands the cleverness) and only when ICU support has
been enabled to begin with. Also, as the goal is to get something into the
Debian packages of boost 1.33.1, I need a solution that works there, not just on
CVS HEAD.

Any help would be appreciated. Note I'm reading the list via gmane, so cc:ing
me directly will get my attention sooner.

zw

[1] http://lists.gnu.org/archive/html/monotone-devel/2006-08/msg00022.html

#! /bin/sh
set -x
set -e

# create source files ...
cat > empty.c <<EOF
EOF
cat > foo.c <<EOF
extern int foo(void) { return 1; }
EOF
cat > bar.c <<EOF
extern int bar(void) { return 2; }
EOF
cat > foobar.h <<EOF
extern int foo(void);
extern int bar(void);
EOF
cat > uses_bar.c <<EOF
#include "foobar.h"
int main(void)
{
  return bar();
}
EOF
cat > uses_foo_and_bar.c <<EOF
#include "foobar.h"
int main(void)
{
  return foo() + bar();
}
EOF
cat > uses_foo.c <<EOF
#include "foobar.h"
int main(void)
{
  return foo();
}
EOF

# compile the base libraries and the programs that use them ...
gcc -fPIC -O2 foo.c -shared -Wl,-soname,libfoo.so.1 -o libfoo.so.1
gcc -fPIC -O2 bar.c -shared -Wl,-soname,libbar.so.1 -o libbar.so.1
gcc -c -O2 uses_foo.c -o uses_foo.o
gcc -c -O2 uses_bar.c -o uses_bar.o
gcc -c -O2 uses_foo_and_bar.c -o uses_foo_and_bar.o

# make it possible to link to the base libraries ...
ln -sf libfoo.so.1 libfoo.so
ln -sf libbar.so.1 libbar.so

# create a library which exposes no symbols itself, but has DT_NEEDED
# entries for both the base libraries. for demonstration purposes,
# make it possible to link against that library (in the real scenario,
# this library would be used for backward compatibility only, there
# would be no need to make it linkable). Note that --no-as-needed is
# currently the default, but in the future --as-needed will be the
# default, hence it should be explicitly set (also, it is more
# self-documenting this way).
gcc -fPIC -O2 empty.c -shared -Wl,-soname,libfoobar.so.1 -L. -Wl,-rpath,$PWD \
    -Wl,--no-as-needed -lfoo -lbar -o libfoobar.so.1
ln -sf libfoobar.so.1 libfoobar_xdeps.so

# create a stub which tells (GNU) ld to link whichever of the base libraries
# are actually necessary, and not others; for demonstration purposes this
# has a marked name, in the real scenario, it would be simply libfoobar.so
# (i.e. libboost_regex_<suffix>.so)
echo 'GROUP ( AS_NEEDED ( libfoo.so libbar.so ) )' > libfoobar_better.so

# now link the demonstration programs. these are the legacy versions
# that drag in both libfoo and libbar, whether they need them both or
# not... (xdeps = extra dependencies)
gcc uses_foo.o -o uses_foo_xdeps -L. -Wl,-rpath,$PWD -lfoobar_xdeps
gcc uses_bar.o -o uses_bar_xdeps -L. -Wl,-rpath,$PWD -lfoobar_xdeps
gcc uses_foo_and_bar.o -o uses_foo_and_bar_xdeps -L. -Wl,-rpath,$PWD \
    -lfoobar_xdeps

# these are the improved versions that refer only to the libs they need.
gcc uses_foo.o -o uses_foo_better -L. -Wl,-rpath,$PWD -lfoobar_better
gcc uses_bar.o -o uses_bar_better -L. -Wl,-rpath,$PWD -lfoobar_better
gcc uses_foo_and_bar.o -o uses_foo_and_bar_better -L. -Wl,-rpath,$PWD \
    -lfoobar_better


Boost-Build list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk