Created 4 years ago
a strong libtool implementation, written in C
midipix committed 4 months ago
# slibtool: a strong libtool implementation, written in C
`slibtool` is an independent reimplementation of the widely used libtool,
written in C. `slibtool` is designed to be a clean, fast, easy-to-use
libtool drop-in replacement, and is accordingly aimed at package authors,
distro developers, and system integrators. `slibtool` maintains compatibility
with libtool in nearly every aspect of the tool's functionality as well as
semantics, leaving out (or turning into a no-op) only a small number of
features that are no longer needed on modern systems.
Being a compiled binary, and although not primarily written for the sake of
performance, building a package with `slibtool` is often faster than with its
script-based counterpart. The resulting performance gain would normally vary
between packages, and is most noticeable in builds that invoke libtool a large
number of times, and which are characterized by the short compilation duration
of individual translation units.
## why reimplement libtool?
Midipix targets use the PE binary format, and so require import library
integration on the one hand, yet follow ELF naming conventions
(i.e. libfoo.so.x.y.z) on the other. In consequence, midipix shared
libraries are not fully supported by existing libtool implementations.
As an interim solution used for the porting of gcc, a libtool script
generated by ltmain.sh was hand-edited so that it would correctly produce
the host's shared libraries as part of the toolchain build process.
Bypassing the libtool script generated by a package's ltmain.sh requires
overriding the `LIBTOOL` variable as part of invoking `make(1)`. In that
sense, and unless midipix support is added to every single ltmain.sh in
every single package that uses autotools, it does not truly matter whether
the surrogate libtool that one chooses be a script, a program written in C,
or something else.
At the time of this writing, an average libtool script consists of about
`10K lines of code` with `several hundred case-esac statements` and a similar
number of `if-then blocks`. In view of the length of the generated script,
and in light of its challenging logic and overall complexity, a rewrite of
the tool in C seemed to be the right choice not only with respect to quality,
readability, and anticipated performance, but also in terms of development
effort, ease of customization, and long-term maintenance.
## Requirements for building slibtool
- a C toolchain, consisting of
- a C compiler such as gcc, clang, or [cparser];
- the compiler should support -std=c99;
- the system's libc should support -D_XOPEN_SOURCE=700.
If building for (native) mingw development, please cross-build slibtool
under your posix environment of choice (e.g. midipix, cygwin, msys2) and
install it alongside the rest of your (cross-built) mingw toolchain.
If your system libc only supports -D_XOPEN_SOURCE=600 then you should be
able to build slibtool after slightly tweaking the code and/or setting
a few config-time environment variables (such as CFLAGS and LDFLAGS).
## Usage
With most packages, simply adding `LIBTOOL=slibtool` to the `make` invocation
should suffice. To have `slibtool` operate in debug mode (outputting a colorized
raw argument vector), use `LIBTOOL=dlibtool` instead, or alternatively add
`--debug` to the libtool flags inside of your Makefile.
### Corner cases
Some programs which make heavy use of recursive `make` are known to drop the
`LIBTOOL` value at some point in the process, and as such you may additionally
need to export the environment variable; `export MAKE="make LIBTOOL=slibtool"`.
`slibtool` additionally installs two symlinks that are the equivalent of
`-disable-shared` and `-disable-static`, named `slibtool-static` and
`slibtool-shared`, respectively. These symlinks should be used when building
packages that have the above switches hard-coded into the generated libtool
script; two examples of such packages are `binutils` and `gdb`, which both
have shared library builds disabled by default. A quick way to determine
whether this invocation form is appropriate with a specific package is to
run `./libtool --features` from the build directory, then check whether the
output has either shared or static library builds disabled. When slibtool
is invoked as a basic tool (i.e. slibtool, dlibtool, etc.), then building
of both a shared library and a static archive is enabled by default.
## Differences from GNU libtool
While `slibtool` aims to be compatible with all common usages of libtool at the
build system level, there exist several differences at the implementation level
that should be noted.
- `.la wrappers` are always generated, but by default are never installed;
`.la wrappers` contain key information that is provided to libtool when
generating a shared library or static archive, and which is needed when
passing the archive or library as an input argument in .la form to the
compiler driver in subsequent link steps. Since `slibtool` is entirely
independent of the above wrappers with respect to its own functionality,
and given its announced goal to remain compatible with the script-based
libtool as much as possible, `slibtool`'s behavior is to always produce
`.la wrappers` on the one hand, yet fully ignore their content on the
other.
Despite their internal nature, installed .la wrappers are often [ab]used
in strange and mysterious ways by distro-related tools other than libtool
itself. For the sake of distributions that depend on the aforementioned
wrappers, slibtool comes with three special symlinks named `clibtool`,
`clibtool-shared`, and `clibtool-static`, respectively. The `'c'` in
`clibtool` stands for `compatible`, and accordingly indicates an end-user's
preference towards perfect emulation of legacy behavior.
- `-rpath` argument values are passed to the compiler and linker as needed only;
`-rpath` is often [mis]used by libtool to add redundant paths to a program's
linker search path. When using slibtool, `-rpath` argument values are only
forwarded to the compiler and linker when pointing to non-default linker
directories, and are accordingly filtered out when pointing to default
library locations (i.e. `/usr/lib`, `/lib`, and so on).
- no-ops
- `-R`, which adds a path to the generated program's run-time search path;
this switch is currently not needed, and is accordingly ignored.
- `-no-install`, which considers the executable wrapper to be optional;
this switch is not needed on modern systems, and is accordingly ignored.
## Development
Major changes to slibtool should be discussed on [#slibtool] prior to pursuing
a considerable effort, and patches should be sent, gpg-signed, to the project
maintainer; before hacking on slibtool, please take a moment of your time and
read the [CONTRIB] document.
As you finalize your changes to the code, please consider building at least
once with `cparser` as the compiler. cparser is excellent at catching logical
errors and semantic flaws, and as of the time of this writing is capable of
spotting bugs that go unnoticed by other major compilers. For a few examples
of such hidden bugs, see commits [94d109f], [1142bf2], and [55c95a8].
## License
`slibtool` is distributed under a permissive [MIT license].
[#slibtool]: ircs://irc.oftc.net/#slibtool
[MIT license]: https://git.foss21.org/slibtool/tree/COPYING.SLIBTOOL
[CONTRIB]: https://git.foss21.org/slibtool/tree/CONTRIB
[libltdl]: https://www.gnu.org/software/libtool/manual/html_node/Using-libltdl.html#Using-libltdl
[libtool]: https://www.gnu.org/software/libtool/
[cparser]: https://pp.ipd.kit.edu/git/cparser/
[dlopen()]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/dlopen.html
[94d109f]: https://git.foss21.org/slibtool/commit/?id=94d109fa418c024c214a50d645624e2e2935e6d1
[1142bf2]: https://git.foss21.org/slibtool/commit/?id=1142bf2e13f411cf967c1ed8b4060d7829eb13bb
[55c95a8]: https://git.foss21.org/slibtool/commit/?id=55c95a829928ae9f053678a58a2145276cad9c08