ada

Missing libgnat-2020.dylib when using runtime library (MacOS)


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:

  1. Project structure:
.
├── 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.

  1. FCS_Middleware Main Project File
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;
  1. Flight Control Software Project File
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;
  1. FCS_Middleware Project File within the FCS package
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;

Solution

  • 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:

    $ 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.