Chapter 26. Linking With Whole Libraries

Table of Contents

26.1. Whole Library Example

In C and C++, most environments create library archives that consist of a collection of object files. Most linkers only link object files from libraries into executables if there is at least one function in the object file that is in the calling chain of the executable. In other words, if an object file in a library appears not to contain any code that is ever accessed, that object file is not included in the final executable. Abuild provides a way to force inclusion of all object files in a given library for underlying systems in which this is supported.

26.1. Whole Library Example

There are some instances in which it may be desirable to tell the linker to include all the object files from a library. Common examples include times when static libraries are converted into shared libraries or when an object file is self-contained but contains a static initializer whose side effects are important. The doc/example/whole-library directory contains an example of doing this. The lib1 and lib2 directories both contain self-contained classes and have static variables that call those classes' constructors:

whole-library/lib1/thing1.hh

#ifndef __THING1_HH__
#define __THING1_HH__

class Thing1
{
  public:
    Thing1();
    virtual ~Thing1();
};

#endif // __THING1_HH__

whole-library/lib1/thing1.cc

#include "thing1.hh"

#include <iostream>

static Thing1* static_thing = new Thing1;

Thing1::Thing1()
{
    std::cout << "in thing1 constructor" << std::endl;
}

Thing1::~Thing1()
{
    std::cout << "in thing1 destructor" << std::endl;
}

whole-library/lib2/thing2.hh

#ifndef __THING2_HH__
#define __THING2_HH__

class Thing2
{
  public:
    Thing2();
    virtual ~Thing2();
};

#endif // __THING2_HH__

whole-library/lib2/thing2.cc

#include "thing2.hh"

#include <iostream>

static Thing2* static_thing = new Thing2;

Thing2::Thing2()
{
    std::cout << "in thing2 constructor" << std::endl;
}

Thing2::~Thing2()
{
    std::cout << "in thing2 destructor" << std::endl;
}

Neither library is referenced by main.cc (in bin):

whole-library/bin/Abuild.conf

name: main
platform-types: native
deps: thing1 thing2

whole-library/bin/main.cc

#include <iostream>

int main()
{
    std::cout << "In main" << std::endl;
    return 0;
}

Therefore, the linker would not ordinarily link them in even with the dependency on both library build items.

In this example, we force lib1 to be linked in but not lib2. This is done by adding the variable WHOLE_lib_thing1 (since thing1 is the name of the library) to lib1's Abuild.interface:

whole-library/lib1/Abuild.interface

INCLUDES = .
LIBDIRS = $(ABUILD_OUTPUT_DIR)
declare WHOLE_lib_thing1 boolean
WHOLE_lib_thing1 = 1
LIBS = thing1

On systems that support this, defining this variable causes the corresponding library to be linked in its entirety into any executables that use the library. This facility may not be supported by all compilers. In particular, it is not supported for Microsoft Visual C++ in versions at least through .NET 2005, in which case setting this variable has cause an error.

For cases in which some users of a library may want to link in the whole library and others may not, it is also possible to set the WHOLE_lib_libname variable in an Abuild.mk. For example, if you were converting a static library to a shared library, you might want to do this in the shared library build item's Abuild.mk rather than the static library's Abuild.interface file. That would prevent other users of the static library from needlessly linking with the whole library.

We do not set this variable for lib2:

whole-library/lib2/Abuild.interface

INCLUDES = .
LIBDIRS = $(ABUILD_OUTPUT_DIR)
LIBS = thing2

This means that its static initializer will not be linked in on any system. On a system that supports whole-library linking, the main program generates this output:

whole-library.out

in thing1 constructor
In main

This output includes the static initializer from Thing1 but not from Thing2.

Note that, in order to be truly portable, an application would have to contain explicit code that accessed the static initializers. We illustrate this in some Java code in Section 25.2, “Mixed Classification Example”. The same technique used for that example would work in C or C++ code.