Shared
Building and Using Shared Ada Libraries on Mac OS X
Download a gzip'd tar file containing the code here.
Here is an example Makefile to build and use shared Ada libraries on Mac OS X.
Building
The make target named shared contains the steps to build a shared library. After deleting any leftover .ali file, the library gets compiled with the -c option, which skips the bind and link phases. Then Apple's libtool creates a dynamic library using the .o file. It also adds the names of any other dynamic libraries needed to resolve references at runtime. Note how the -install_name option allows for a relative path using the @executable_path symbol. After removing the unneeded .o file, the .ali file is marked read-only. This causes subsequent invocations of gnatmake to treat it as a library. Without this step, gnatmake would insist on compiling & binding the file each time, defeating the purpose of the shared library. Finally, the full pathname of each required library is set with install_name_tool. This precludes having to set the environment variable DYLD_LIBRARY_PATH, discussed further below.
Using
Using the shared library is more familiar. Focusing on the target main, the essential step is passing the shared library name among the -largs. Note that -dead_strip is used to strip the object file. It is instructive to compare the stripped executable with the statically linked debug target: 17K v. 584K on ppc; 15K v. 328K on x86. Again, install_name_tool is used to edit the path for both the new shared library, as well as the GNAT run-time shared libraries. The list of required run-time libraries, stored in the variable SHARED may be extended to include other libraries stored in the ADALIB path.
Testing
The test target puts it all together by cleaning, building and running the example. It uses nm to ensure that the shared routines didn't get statically linked by accident, and then uses otool to verify the shared library paths.
Shared run-time
The -shared option of gnatbind may be used to link against the shared GNAT run time libraries, libgnat & libgnarl. The `gnatls -v` command shows the corresponding default object search path, but the shared libraries (libgnat & libgnarl) don't have the correct installed names. The usual approach is to specify the DYLD_LIBRARY_PATH:
$ export ADA_LIB = /usr/local/ada-4.3/lib
$ export DYLD_LIBRARY_PATH = ${ADA_LIB}/gcc/i686-apple-darwin9/4.3.4/adalib
Alternatively, one may change the installed names to match the default path provided automatically in the gnatlink phase of gnatmake:
cd /usr/local/ada-4.3/lib/gcc/i686-apple-darwin9/4.3.4/adalib
install_name_tool -id $(pwd)/libgnat-4.3.dylib libgnat-4.3.dylib
install_name_tool -id $(pwd)/libgnarl-4.3.dylib libgnarl-4.3.dylib
install_name_tool -change libgnat-4.3.dylib $(pwd)/libgnat-4.3.dylib libgnarl.dylib
$ otool -L libgnat-4.3.dylib libgnarl-4.3.dylib | awk '{ print $1 }'
libgnat-4.3.dylib:
/usr/local/ada-4.3/lib/gcc/i686-apple-darwin9/4.3.4/adalib/libgnat-4.3.dylib
/usr/local/ada-4.3/lib/libgcc_s.1.dylib
/usr/lib/libSystem.B.dylib
libgnarl-4.3.dylib:
/usr/local/ada-4.3/lib/gcc/i686-apple-darwin9/4.3.4/adalib/libgnarl-4.3.dylib
/usr/local/ada-4.3/lib/gcc/i686-apple-darwin9/4.3.4/adalib/libgnat-4.3.dylib
/usr/local/ada-4.3/lib/libgcc_s.1.dylib
/usr/lib/libSystem.B.dylib
It may be convenient to link to the shared run-time libraries
cd /usr/local/ada-4.3/lib
ln -s gcc/i686-apple-darwin9/4.3.4/adalib/libgnat-4.3.dylib libgnat-4.3.dylib
ln -s gcc/i686-apple-darwin9/4.3.4/adalib/libgnarl-4.3.dylib libgnarl-4.3.dylib
Finally, one may build a private Framework to be embedded in an application bundle, as discussed here.
Makefile
Here's what the Makefile looks like:
# Example Makefile for building and using shared libraries
#
# Author: John B. Matthews
# Created: 15-Oct-2007
# Modified:
# JBM 16-Oct-2007 Factor main & library source names
# JBM 16-Oct-2007 Handle multiple SHARED libs
# JBM 12-Apr-2009 Revised for x86
# Distribution: GPL
# The target and library to be built
TARGET = main
LIBSRC = greetings
LIBNAME = lib$(LIBSRC).dylib
MYLIB = $(shell pwd)
# A list of other shared libraries that may be needed
SHARED = libgnat-4.3.dylib libgnarl-4.3.dylib
ADALIB = $(ADA_LIB)
CHMOD = chmod
GNATMAKE = gnatmake
CARGS = -cargs -O1 -gnatwu -dynamic
BARGS = -bargs -shared
LARGS = -largs -v -l$(LIBSRC) -dead_strip
LIBTOOL = libtool
RENAME = install_name_tool
.PHONY: shared debug test clean cleaner tar
all:
@echo ""
@echo "Build targets:"
@echo ""
@echo " main Make a program that uses a shared library."
@echo " shared Make a shared Ada library."
@echo " debug Enable debugging."
@echo " test Clean, build & check the results."
@echo ""
@echo "Support targets:"
@echo ""
@echo " clean Remove *.ali *.o b~.*"
@echo " cleaner Remove target & dylib, too."
@echo " tar Archive."
@echo ""
$(TARGET): $(TARGET).adb shared
@echo "# Building program $(TARGET)"
${GNATMAKE} $(TARGET) $(CARGS) $(BARGS) $(LARGS)
${RENAME} -change $(LIBNAME) $(MYLIB)/$(LIBNAME) $(TARGET)
for lib in $(SHARED); do \
${RENAME} -change $$lib $(ADALIB)/$$lib $(TARGET); done
shared: SHAREDLIBS := $(foreach lib,$(SHARED),$(ADALIB)/$(lib))
shared: $(LIBSRC).ad[sb]
@echo "# Building shared library $(LIBNAME)"
${RM} $(LIBSRC).ali
${GNATMAKE} -c $(LIBSRC).adb $(CARGS)
${LIBTOOL} -dynamic \
-install_name @executable_path/$(LIBNAME) \
-o $(LIBNAME) $(LIBSRC).o $(SHAREDLIBS)
${RM} $(LIBSRC).o
${CHMOD} a-w $(LIBSRC).ali
for lib in $(SHARED); do \
${RENAME} -change $$lib $(ADALIB)/$$lib $(LIBNAME); done
debug: cleaner *.ad[sb]
${GNATMAKE} -g $(TARGET)
test: cleaner $(TARGET)
@echo "# Running program $(TARGET)"
@./$(TARGET)
@echo "# These library routines should be U (undefined):"
@nm $(TARGET) | grep $(LIBSRC) | awk '{ print $$1 " " $$2 }'
@echo "# These are the shared libraries used:"
@otool -L $(TARGET) | awk '{ print $$1 }'
@otool -L $(LIBNAME) | awk '{ print $$1 }'
clean:
${RM} *.o *.ali b~*
cleaner: clean
${RM} $(TARGET) *.dylib
tar: cleaner
(export COPYFILE_DISABLE=true; \
tar -zcvf ~/Sites/HomePage/misc/shared.tgz ../shared)
Code
Here's what the sample code looks like (gnatchop format):
with Greetings;
with Text_IO;
procedure Main is
begin
Text_IO.Put_Line(Greetings.Hello);
Text_IO.Put_Line(Greetings.Goodbye);
end Main;
package Greetings is
function Hello return String;
function Goodbye return String;
end Greetings;
package body Greetings is
function Hello return String is
begin
return "Hello, world!";
end Hello;
function Goodbye return String is
begin
return "Goodbye, world!";
end Goodbye;
end Greetings;
Copyright 2007, 2009 John B. Matthews
Distributed without warranty under the terms of the GNU Public License.
Last updated 10-Nov-2009