Sunday 24 July 2011

When you need gprbuild

Most of the time, if your code is entirely in Ada, it'll build just fine with gnatmake. You only need gprbuild for mixed-language programming.

However, there's one case in which you need gprbuild, even for Ada-only code; it's when you're building a shared library on Mac OS X (Darwin).

On Linux, a shared library doesn't need to know where it is in the filesystem: it'll either be in a standard location, in a location specified at link time using -rpath, or on LD_LIBRARY_PATH.

On Darwin, it's best if a shared library does know where it is; if it does, programs linked agaist it will know where to find it without needing to have the location specified separately during linking (or via DYLD_LIBRARY_PATH).

This is what a shared library libbc (.dylib, not .so!) looks like if built with gnatmake from GNAT GPL 2011, using otool (the rough Darwin equivalent of the Unix ldd):

$ otool -L lib-release/libbc.dylib
lib-release/libbc.dylib:
/Users/simon/sf/booch95/lib-release//libbc.dylib (compatibility version 0.0.0, current version 0.0.0)
@rpath/libgnarl-2011.dylib (compatibility version 0.0.0, current version 0.0.0)
@rpath/libgnat-2011.dylib (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1105.0.0)
/usr/local/gnat/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.0.0)

and here is the same library built with gprbuild:

$ otool -L lib-release/libbc.dylib:
@rpath/libbc.dylib (compatibility version 0.0.0, current version 0.0.0)
@rpath/libgnarl-2011.dylib (compatibility version 0.0.0, current version 0.0.0)
@rpath/libgnat-2011.dylib (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1105.0.0)
/usr/local/gnat/lib/libgcc_s.1.dylib (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.0.0)

The difference is that instead of the actual location of bc.dylib, it's prefixed by @rpath/. gprbuild does this by passing the -install_name switch to ld:

-install_name name
Sets an internal "install path" (LC_ID_DYLIB) in a dynamic library. Any clients linked against the library will record that path as the way dyld should locate this library. If this option is not specified, then the -o path will be used.
This option is also called -dylib_install_name for compatibility.

When linking against the library, the Darwin ld uses -rpath:

-rpath path
Add path to the runpath search path list for image being created. At runtime, dyld uses the runpath when searching for dylibs whose load path begins with @rpath/.

The shared library is installed at /opt/gnat-gpl-2011/lib/bc/lib-release/.

Linking against the shared library using gnatmake results in a lot of runtime paths being baked in the executable (I don't know if there's a tool which can show these paths, so I've just done a verbose link):

$ gnatmake -p -P shared_libs -largs -Wl,-v
[...]
gnatlink /Users/simon/sf/booch95/doc/.build/shared_libs.ali -shared-libgcc -Wl,-v -L/opt/gnat-gpl-2011/lib/bc/lib-release/ -lbc -Wl,-rpath,/opt/gnat-gpl-2011/lib/bc/lib-release/
[...]
Library search paths:
/opt/gnat-gpl-2011/lib/bc/lib-release/
/Users/simon/sf/booch95/doc/.build/
/opt/gnat-gpl-2011/lib/bc/lib-release/
/opt/gnat-gpl-2011/lib/gcc/x86_64-apple-darwin10.2.0/4.5.3/adalib/
/opt/gnat-gpl-2011/lib/gcc/x86_64-apple-darwin10.2.0/4.5.3
/opt/gnat-gpl-2011/lib/gcc
/opt/gnat-gpl-2011/lib
/usr/lib
/usr/local/lib
Framework search paths:
/Library/Frameworks/
/System/Library/Frameworks/

gprbuild does the same.

There's a useful post (addressing a slightly different problem) here.

No comments:

Post a Comment