Using swipl-ld on Linux and MacOS

Hello, and please be patient :slight_smile: I struggled a bit with using swipl-ld, here are my observations.

Linux

On my Linux installation, with SWI-Prolog compiled from source and not “installed” (runs from the build directory), using swipl-ld works fine as advertised in the docs:

swipl-ld -o mylib file.{c,o,cc,C} ...

However, this will create mylib.so, not mylib. This is OK but I couldn’t then make the example few lines lower in the same docs work. The example says:

Now write a file mylib.pl:

:- module(mylib, [ say_hello/1 ]).
:- use_foreign_library(foreign(mylib)).

But this won’t work for me. I had to use the fully qualified name of the foreign library, with the .so at the end:

:- use_foreign_library(foreign('mylib.so')).

MacOS

On my (Intel, ~ 5 year old hardware) MacOS I failed to convince swipl-ld to work for me. I have compiled from source and installed in my $HOME:

cmake \
    -DUSE_GMP=OFF \
    -DUSE_TCMALLOC=OFF \
    -DSWIPL_PACKAGES_JAVA=OFF \
    -DSWIPL_PACKAGES_X=OFF \
    -DCMAKE_INSTALL_PREFIX=$HOME \
    -G Ninja ..
ninja
ninja install

(I am not sure about all the -D flags but this works now for development purposes)

No matter what I tried, using swipl-ld would end up saying that

ld: library '-lswipl' not found

for example:

$ swipl-ld -shared -L"$HOME"/lib/swipl/lib/x86_64-darwin -o swiplite -l sqlite3 swiplite.o
ld: library '-lswipl' not found
clang: error: linker command failed with exit code 1 (use -v to see invocation)
cc returned code 256
*** swipl-ld exit status 1

This however worked:

gcc -shared -L"$HOME"/lib/swipl/lib/x86_64-darwin -o swiplite -l sqlite3 -l swipl swiplite.o

What could have I possibly mis-configured?

The name is correct. ‘.so’ is the correct extension for MacOS for modules. True, MacOS can load the object regardless of the extensions, but not all OSes are like that so we want to give it the right extension.

Using foreign(mylib) looks (on MacOS) for mylib.so on the foreign search path. See absolute_file_name/3 and file_search_path/2 for dealing with file search paths. As ‘.’ is not on the path, this does not work. I’m wondering a bit why it works without extension. Probably because if it cannot find the file using Prolog’s search it just passes the raw name to the dynamic linker, hoping the linker will find the file. That seems to work for plain files.

So, for a local install, just use use_foreign_library(mylib) to find it in the current directory. Or, use

 swipl -p foreign=. ...

Hmm. Just tried on MacOS, both running from the build dir and installed. Both work as advertised. You can add -v to see what it is running. That may give some clues. swipl-ld does its work by running

swipl --dump-runtime-variables

So, that may give a hint as well.

The more robust way is probably by using CMake, but that comes with its own learning curve. SWI-Prolog provides swipl.cmake, which may be included into your project, providing several external targets. The environ demo project provides a complete example.

1 Like

Hi, following the pointers in your reply I managed to figure out how to use CMake to build a foreign code module that uses sqlite3 as a C library :pray:. For posterity:

I did use the environ demo project. It was a trivial copy-paste job, basically s/environ/swiplite for CMakeLists.txt. Extremely useful!

I did not climb the CMake learning curve, I just hammered it until it “worked”. Looking at swipl.cmake did help. I ended up with following 3 lines before the install:

find_package(SQLite3 REQUIRED)
target_link_libraries(swiplite PRIVATE ${SQLite3_LIBRARIES})
target_include_directories(swiplite PRIVATE ${SQLite3_INCLUDE_DIRS})

Here is the documentation for FindSQLite3. While this did “work” I have no idea what it means or how it could break.

With this, I was able to follow the instructions on packs/add-ons to end up with a pack that is installed from a local file using

?- pack_install('swiplite-0.1.tgz').

It ends up in ~/.local/share/swi-prolog/pack, runs the tests and so on. I can now do:

?- use_module(library(sqlite)).

With the proper pack structure and swipl.cmake, I can also build it outside of SWI-Prolog with CMake and ninja. I only need to provide the library location:

mkdir -p build \
    && ( cd build \
        && SWIPL_HOME_DIR=~/lib/swipl cmake -G Ninja .. \
        && ninja )

To use it straight from the build directory, I can use -p foreign and -p library as you suggested above:

$ swipl -p foreign=./build -p library=./prolog
?- use_module(library(sqlite)).
?- use_module(test/test_sqlite).
?- run_tests.

All in all, very smooth experience, :star: :star: :star: :slight_smile:

2 Likes