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