I'm trying to figure out how to build and link dynamic libraries with Ada, and I've encountered an issue I can't seem to figure out. Might just be a bug on MacOS.
Here is my setup: I've created a dynamic library called FCS Middleware
I would like to use in my main project Flight Control Software
. Both the library and the main project with the library included compile ok (more on my setup in detail later). I can even execute the main project in my destination folder.
The problem is when I move my destination folder (containing the executable and the dynamic library, see below) to another folder. In this case my main program no longer works, and I get this error message:
dyld[15596]: Library not loaded: @rpath/libgnat-2020.dylib
Referenced from: <9DD7BB9F-4BC0-3F80-9642-36D5228B5867> /Users/robert/Downloads/dist/fcs_software
Reason: tried: '/Users/robert/Downloads/dist//lib/libgnat-2020.dylib' (no such file), '/Users/robert/Downloads//.obj/libgnat-2020.dylib' (no such file), '/Users/../../..//opt/gnat/2020/lib/gcc/x86_64-apple-darwin17.7.0/8.4.1/adalib/libgnat-2020.dylib' (no such file), '/Users/../../..//opt/gnat/2020/lib/libgnat-2020.dylib' (no such file), '/Users/robert/Downloads/dist//lib/libgnat-2020.dylib' (no such file), '/Users/robert/Downloads//.obj/libgnat-2020.dylib' (no such file), '/Users/../../..//opt/gnat/2020/lib/gcc/x86_64-apple-darwin17.7.0/8.4.1/adalib/libgnat-2020.dylib' (no such file), '/Users/../../..//opt/gnat/2020/lib/libgnat-2020.dylib' (no such file), '/usr/local/lib/libgnat-2020.dylib' (no such file), '/usr/lib/libgnat-2020.dylib' (no such file, not in dyld cache)
zsh: abort ./fcs_software
Any ideas on why this happens, and how to fix it?
Thanks,
Robert
If needed here are the details of my setup:
.
├── FCS_Middleware
│ ├── dist
│ │ ├── fcsm-math.ads
│ │ ├── fcsm-math.ali
│ │ ├── fcsm.ads
│ │ ├── fcsm.ali
│ │ └── libfcs_middleware.dylib
│ ├── fcs_middleware.gpr
│ └── src
│ ├── fcsm
│ │ ├── fcsm-math.adb
│ │ ├── hidden_package.adb
│ │ └── hidden_package.ads
│ ├── fcsm-math.ads
│ └── fcsm.ads
└── FlightControlSoftware
├── dist
│ ├── fcs_software **executable**
│ └── lib
│ ├── fcsm-math.ali
│ ├── fcsm.ali
│ └── libfcs_middleware.dylib
├── fcs_middleware.gpr
├── flight_control_software.gpr
├── include
│ ├── fcsm-math.ads
│ └── fcsm.ads
└── src
└── main.adb
It is only when I move the FlightControlSoftware/dist
folder to i.e. my downloads folder is when the problem occurs. Executing FlightControlSoftware/dist/fcs_software
works.
project FCS_Middleware is
for Source_Dirs use ("src/**");
for Object_Dir use ".obj";
for Library_Dir use "dist";
for Library_Src_Dir use "dist";
for Library_Name use "fcs_middleware";
for Library_Kind use "dynamic";
for Library_Interface use ("FCSM", "FCSM.Math");
end FCS_Middleware;
with "fcs_middleware.gpr";
project Flight_Control_Software is
for Source_Dirs use ("src/**");
for Object_Dir use ".obj";
for Exec_Dir use "dist";
for Main use ("main.adb");
package Binder is
for Switches ("ada") use ("-Es");
end Binder;
package Builder is
for Executable ("main.adb") use "fcs_software";
end Builder;
end Flight_Control_Software;
project FCS_Middleware is
for Source_Dirs use ("include");
for Library_Name use "fcs_middleware";
for Library_Dir use "dist/lib";
for Library_Kind use "dynamic";
for Externally_Built use "true";
end FCS_Middleware;
Focusing on MacOS,
Use gnatls -v
to see your compiler's configured search paths; note that the current directory is searched first.
Use a library project, illustrated here, which offers "a system- and language-independent way of building…dynamic libraries." Alternatively, use gcc -dynamiclib
to create the library manually; it "produces a dynamic library instead of an executable when linking, using the Darwin libtool
command."
Use the binder switch -shared
to "Link against a shared GNAT run time."
Use otool -L
to "Display the names and version numbers of the shared libraries that the object file uses." This indicates where the executable expects to find shared libraries.
Use install_name_tool
to "change dynamic shared library install names" as needed.
Why do I need this library anyway? The Ada run-time library is provided by libgnat
; you may also need libgnarl
.
Is this a portable solution across other macs? Generally, yes, with some caveats:
In the example below, both libgnat
and libgnarl
require libSystem.B.dylib
, with is virtual in recent versions of MacOS.
Compatibility is good going forward; less so going backward; test to verify.
Check the license that came with your implementation for details of distributing the run-time library.
$ otool -L $ADA_LIB/libgnat.dylib $ADA_LIB/libgnarl.dylib
/opt/gcc-13.1.0/lib/gcc/x86_64-apple-darwin21/13.1.0/adalib/libgnat.dylib:
@rpath/libgnat-13.dylib (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1319.0.0)
/opt/gcc-13.1.0/lib/gcc/x86_64-apple-darwin21/13.1.0/adalib/libgnarl.dylib:
@rpath/libgnarl-13.dylib (compatibility version 0.0.0, current version 0.0.0)
@rpath/libgnat-13.dylib (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1319.0.0)
See also How to properly set run paths, search paths, and install names? and Understanding dyld @executable_path, @loader_path and @rpath.