Table of Contents
In most cases, development efforts consisting of large amounts of dynamic and evolving code will be best served by sticking with static libraries. Sometimes, however, it may be desirable or necessary to create shared library object files. Abuild provides support for creating shared libraries on UNIX-based systems and DLLs on Windows systems. Note that there are many ancillary concerns one must keep in mind when using shared libraries such as binary interface compatibility. We touch lightly on these topics here, but a full discussion is out of scope for this document.
Building shared libraries with abuild is essentially identical
to building static libraries. You still set up your shared
library targets using TARGETS_lib
in
Abuild.mk
just as you would for static
libraries. In order to tell abuild that a library should be
created as a shared library, you must set the additional variable
SHLIB_
where
libname
libname
is the name of the library
target. The value of this variable consists of up to three
numbers: major version
, minor
version
, and revision
. These values
tell potential users of your library when the library has
changed. In general, you should only modify these values when
you are releasing versions of your library. During development,
it's best to just leave them alone or else your version numbers
will get very large and you will lose all the advantages of using
shared libraries because of the need to relink everything all the
time. Before a release, the major version number should be
incremented if the shared library has had interfaces removed or
modified since the last release as those operations would make
old binaries that linked with the shared library fail to work
with the new version. The minor version should be incremented if
no interfaces were changed or removed but new interfaces were
added. This indicates that old binaries would work with new
libraries but new binaries may not work with old libraries. The
revision number should be incremented if any changes were made to
the shared library code that did not affect the interfaces. This
just tells the user that the library has changed relative to
another version that may be installed. Abuild will build
executables that link against shared libraries in such a way that
they will fail to locate shared libraries whose major version
numbers do not match what they linked against. On UNIX
platforms, the unversioned .so
file and the
.so
file with only the major version will be
symbolic links to the fully versioned file name. (For example,
if the actual shared library file were
libmoo.so.1.2.3
, both
libmoo.so
and
libmoo.so.1
would be symbolic links to it.)
On Windows, the DLL will contain the major version in its name
(e.g., moo1.dll
) while the companion static
library remains unversioned.
Note that all the version number parameters are optional.
Although they should always be used when creating actual shared
libraries that you intend to link programs against, they may be
omitted in some other cases. For example, if you are building a
shared library object file that will be loaded at runtime or used
as a plugin (such as with Java native code), then it may be
appropriate to omit the version numbers altogether. Even if the
SHLIB_
variable is set to an empty string, abuild will still make a
shared library instead of a static library. There is no way to
create both a shared and a static version of the same library at
the same time, but it is possible to create a shared library that
links against a static library, which can be used to achieve the
same effect in many circumstances.
libname
When abuild builds shared libraries, it links them with any
libraries in the LIBS
variable. Although this
is generally the correct behavior for systems that support
linking of shared libraries, it can cause unpleasant side effects
if you mix shared libraries with static libraries. When mixing
shared libraries and static libraries, you should make sure that
you don't include two copies of the same symbols in more than one
place (two shared libraries or a shared library and an
executable). Some systems handle this case acceptably, and
others don't. Even in the best case, doing this is wasteful and
potentially confusing. If you need to prevent abuild from
linking certain static libraries into shared libraries, you may
manually manipulate the contents of the LIBS
variable in your Abuild.mk
file. However,
if you find that you need to do this, you should probably rethink
how you have your build configured. If you have static libraries
that are intended to be linked into shared libraries and then not
used again for other purposes, you should reset the value of
LIBS
in an after-build file that is included
by your shared library build item's
Abuild.interface
file. This is discussed in
Section 21.2, “Shared Library Example”.
[42]
Abuild allows you to mix executables, shared libraries, and
static libraries in the same build item. If you do this, all
executable targets will link with all shared and all static
library targets, and all shared library targets will link with
all static library targets. The shared library targets will not
link with each other. There are few, if any, good reasons to mix
shared and static library targets in the same build item, as
doing so can only lead to confusion. When they are mixed, it is
probably appropriate to avoid adding the static libraries to
LIBS
in the
Abuild.interface
file.
In order to allow static libraries to be linked into shared
libraries, abuild compiles all library object files as
position-independent code. In some extremely rare cases, you may
wish to avoid doing this as there is a very minor performance
cost to do it. If you wish to prevent a specific source file
from being compiled as position-independent code, set the
variable
NOPIC_
to
filename
1
where filename
is
the name of the source file. For example, the code
NOPIC_File.cc := 1
in your
Abuild.mk
file would prevent
File.cc
from being compiled as
position-independent code. Note that abuild does not check to
make sure that code compiled in this way is not eventually linked
into a shared library. If you try to link
non-position-independent code into a shared library, it may not
link at all, or it may cause undefined and hard-to-trace
behavior. Use of this feature is not recommended unless
absolutely needed to fix some specific problem.
In order to run a program that is linked with shared libraries,
the operating system will have to know where to find the shared
library. Abuild does not include library run path information
in the executables as doing so is inherently dangerous and
non-portable. Even if abuild were to ignore this danger and
include run path information, doing so would potentially preclude
the ability to swap out shared libraries at runtime, which is
often the main reason for wanting to use them in the first place.
[43]
Instead, you will need to make sure that, one way or another, the
shared libraries you need are located in a directory that is in
your shared library path. On most UNIX systems, you can set the
LD_LIBRARY_PATH
environment variable or install
the shared libraries into certain system-defined locations. On
some systems (like Linux), you can also add directories to
/etc/ld.so.conf
. On Windows, you can
colocate the DLL files with the executables, or you can add the
directories containing the DLL files to your path. When building
DLLs and executables with MSVC versions greater than or equal to
.NET 2005, abuild automatically embeds the manifest file in the
DLL or executable with the mt command.
[42] Versions of abuild prior to 1.0.3 behaved somewhat differently with respect to linking of shared libraries. See the changes for version 1.0.3 in the release notes for details.
[43] The way the runtime loader behaves when shared library location information is compiled into an executable (as run path data) varies from system to system. In most systems, if the shared library doesn't exist at the compiled-in location, the system will fall back to its standard rules for locating shared libraries. In some systems, if the shared library does exist in the compiled-in location, that copy of the shared library will be used with no way to override it. This may have undesired implications. For example, suppose you were to create an executable that linked with a shared library and included run path information to the development version of the shared library. If you installed that executable and shared library in standard locations on a system without a copy of the development environment, everything would work fine. Then suppose you put a development environment on that system and built a newer version of the same shared library. Your installed executable would actually use the new development copy of the library because it still has that path compiled into it! This is almost certainly not what would be intended. Abuild avoids this issue entirely but not including support for specifying run path data.
For another approach to using shared libraries, look at libtool. The libtool program gets around this problem by creating wrappers around executables and shared libraries in the development tree. Although abuild is not integrated with libtool, such an integration would be possible. The possibility of including support for libtool is actually one of the motivations behind allowing library object files and non-library object files to have different extensions.