Skip to content
Sebastian edited this page Jun 28, 2020 · 30 revisions

JPL - Java <-> SWI Prolog Interface

JPL is a set of Java classes and C functions providing a bidirectional interface between Java and Prolog. JPL uses the Java Native Interface (JNI) to connect to a Prolog engine through the Prolog Foreign Language Interface (FLI). JPL is not a pure Java implementation of Prolog; it makes extensive use of native implementations of Prolog on supported platforms.

This is the projct Wiki with information to support development of JPL. This Wiki may provide more information than the basic one in documentation Developing JPL.

For understanding JPL and using it in your embedded application please refer to JPL Documentation page.

For development of JPL, we follow The GitHub Standard Fork & Pull Request Workflow.

Packages and libraries needed

First, let us make sure we have all the necessary libraries and tools to compile the system:

sudo apt-get install \
        build-essential autoconf curl chrpath pkg-config \
        ncurses-dev libreadline-dev libedit-dev \
        libunwind-dev \
        libgmp-dev \
        libssl-dev \
        unixodbc-dev \
        zlib1g-dev libarchive-dev \
        libossp-uuid-dev \
        libxext-dev libice-dev libjpeg-dev libxinerama-dev libxft-dev \
        libxpm-dev libxt-dev \
        libdb-dev \
        libpcre3-dev \
        libyaml-dev \
        openjdk-11-jdk junit \
        make ninja-build \
        cmake

Since we want to get JPL it is mandatory to have:

  • openjdk-11-jdk or any package that can provide java and the compiler javac. (Run javac -version to test).
  • junit, for running Java unit testing.

As explained in fnogatz's swivm repo, if you want to reduce resources, the following packages are optional:

  • libreadline-dev and libedit-devl: Without, you do not have history feature in SWIPL interpreter.
  • unixodbc-dev: Without, you have no ODBC database connectivity (e.g., MySQL)
  • libssl-dev: Without, you have no SSL (and HTTPS) support.
  • libgmp-dev: Without, you lack unbounded integer support, rational numbers, good random number generators, etc.
  • libarchive-dev: Without, you can not unpack and install add-ons.
  • libpcre3-dev: Without, you have no regular expression support (library(pcre)).
  • libyaml-dev: Without, you have no YAML support (library(yaml)).

Environment Variables

There are several environmnet variables that play some role in running SWIPL with JPL:

  • SWI_HOME_DIR: to find the root dir of SWIPL install to be used.
  • LD_LIBRARY_PATH (dynamic linker search path):
    • to find native libraries, including libjpl.so and libswipl.so;
    • to find the JVM shared objects (libjvm.so) when calling Java from Prolog, as explained here.
  • CLASSPATH: to find the jpl.jar file implementing the JPL Java API.
  • LD_PRELOAD: to pre-load certain libraries (e.g., libswipl.so).
  • JAVA_HOME: to find the Java compiler and JNI when compiling JPL.

Find more information on ach in JPL doc here.

Set-up devel repository (first time)

Here is a run on cloning, configuring, compiling, testing, and installing:

git clone https://github.com/SWI-Prolog/swipl-devel.git
cd swipl-devel
git submodule update --init

# Avoid conflicts with existing SWI installs (e.g., Ubuntu distribution)
unset LD_LIBRARY_PATH LD_PRELOAD SWI_HOME_DIR   

mkdir build
cd build

# via make
cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr/local/swipl-git -DINSTALL_DOCUMENTATION=OFF ..
make -j 8

# via ninja  (faster)
cmake -G Ninja -DCMAKE_INSTALL_PREFIX:PATH=/usr/local/swipl-git -DINSTALL_DOCUMENTATION=OFF ..
ninja

ctest -j 8 # run all tests (-j makes it faster!)

sudo make install 
/usr/local/swipl-git/bin/swipl

Notes:

  • The unset step is important if you have a distribution version installed in the system. See this issue if you get Could not find system resources FATAL ERROR.
  • If there are other versions installed (e.g., distribution version) you need to make sure you are running the one you want; and you may need to setup SWI_HOME_DIR and LD_LIBRARY_PATH if the Prolog is embedded into C/Java so that the right version is used. See discussion above on how to keep many SWI versions and how to use these environment variables.
  • SWI provides a script to activate the current install under the build/ dir of CMAKE or the local install. Please refer to script ../scripts/swipl-activate in the CMAKE documentation of SWI.
    • Note this will create three swipl symbolic links in $HOME/bin that you would need to delete if you want to use another install (e.g., the distribution one). So may not be the best way to work on, I prefer to work with the Linux alternative framework as described above.

Run "live" un-installed SWIPL

From build/ directory (where CMAKE compiled SWIPL):

/usr/bin/env "SWI_HOME_DIR=home" "SWIPL_BOOT_FILE=home/boot.prc"  src/swipl

Special tags in commits for changelog

Start relevant commits with [A-Z]+: such that they become entries in the changelog.

The frequent tags are FIXED, ADDED and MODIFIED, but anything matching the regex above is copied to the changelog.

For example, "ADDED: Rational number support".

Running unit testing

As of April 2020 most tests have been ported to JUNIT4.

  • The set of JUNIT test class are in package org.jpl7.junit, but there is a test suit org.jpl7.JPLTestSuite to wrap them all.
  • There are also standalone tests in org.jpl7.standalone.*
    • Each of these testing classes can be run individually and are not part of the test suite.

IMPORTANT: All unit test cases are sub-classes of org.jpl7.junit.JPLTest, which provides a method setUpClass to initialize the Prolog engine with the correct SWIPL in the source development tree. Such class will gather a few environment variables that point to the development tree of SWIPL. While those variables are already set by CMAKE, if one wants to develop and run the tests within an IDE and outside CMAKE for the Java component, one has to set those variables (see below for more details).

There are 3 ways one can run the unit testing (or an application that wants to use the development SWI+JPL):

Using CMAKE from anywhere in the build/ structure:

ctest -V  -R jpl:prolog_in_java
ctest -V  -R jpl:java_in_prolog

In CLI, the tests needs to be run from build/package/jpl.

This is the command to run the prolog_in_java test (which uses SWIPL plunit and a test_lib script defined in packages/cmake/PrologPackage.cmake to collect all test_xxx.pl unit testing files):

ssardina@Thinkpad-X1 jpl]$ /usr/bin/env "CLASSPATH=src/main/java/jpl.jar" "SWI_HOME_DIR=../../home" "SWIPL_BOOT_FILE=../../home/boot.prc" java "-Djava.library.path=." "-classpath" "/usr/share/java/junit4.jar:src/main/java/jpl.jar:src/test/java/jpltest.jar" org.jpl7.junit.Test_Atom
JUnit version 4.12
.Starting test: testAtomToString1 (Test_Atom)
.Starting test: testAtomToString2 (Test_Atom)
.Starting test: testAtomToString3 (Test_Atom)
.Starting test: testAtomEquality1 (Test_Atom)
.Starting test: testAtomEquality2 (Test_Atom)
.Starting test: testAtomIdentity (Test_Atom)
.Starting test: testAtom1 (Test_Atom)
.Starting test: testAtomHasFunctorWrongName (Test_Atom)
.Starting test: testAtomHasFunctorNameZero (Test_Atom)
.Starting test: testAtomArity (Test_Atom)
.Starting test: testAtomName1 (Test_Atom)
.Starting test: testAtomName2 (Test_Atom)
.Starting test: testAtomName3 (Test_Atom)
.Starting test: testAtomHasFunctorWrongArity (Test_Atom)

Time: 0.044

OK (14 tests)

and this is the one for testing java_in_prolog (remember, always from build/package/jpl):

[ssardina@Thinkpad-X1 jpl]$ /home/ssardina/git/soft/prolog/swipl-devel.git/build/src/swipl "-p" "foreign=:/home/ssardina/git/soft/prolog/swipl-devel.git/build/packages/plunit" "-f" "none" "--no-packs" "-s" "/home/ssardina/git/soft/prolog/swipl-devel.git/packages/jpl/test_jpl.pl" "-g" "test_jpl" "-t" "halt"
% PL-Unit: jpl .......................................................................................... done
% 3 tests are blocked:
% /home/ssardina/git/soft/prolog/swipl-devel.git/packages/jpl/test_jpl.pl:610:
	test method_static_echo_float_4: we do not yet widen unbounded integers to floats or doubles
% /home/ssardina/git/soft/prolog/swipl-devel.git/packages/jpl/test_jpl.pl:914:
	test set_field_static_shadow_1: we do not yet resolve same-named shadowed fields
% /home/ssardina/git/soft/prolog/swipl-devel.git/packages/jpl/test_jpl.pl:1193:
	test throw_java_exception_1: part of the error term is nondeterministic: we need to match with _
% 90 tests passed

You can also run the Java-side unit testing (or any embedded application) inside an IDE (e.g., IntelliJ), which one would probably be using to develop JPL.

Developing JPL within an IDE

It is usually convenient to develop the Java side of JPL within and IDE. As an example, we will be using IntelliJ, but the same ideas should apply for other IDEs (e.g., ECLIPSE).

The project in the IDE will be under <swi-devel>/package/jpl which is "outside" of the binary generated by CMAKE under <swi-devel>/build.

As of May 2020, the JPL is a Maven project, so this already simplifies many things. However, one still needs to do a few things to make sure the Java being developed accesses the correct SWI development under the CMAKE binary dir <swi-devel>/build, including the compiled libjpl.so library.

So, to run, for example, unit testing classes for Java-calls-Prolog within the IDE:

  1. Compile SWI-Prolog using CMAKE, to compile not only SWI itself but also the C code of JPL <swi-devel>/project/jpl/src/c/jpl.c which will yield library <swi-devel>/build/packages/jpl/libjpl.so.

  2. Create a JUnit Run on some of the testing classes, like org.jpl7.junit.Test_Atom.

  3. Edit the working directory of the JUnit Run created to be <swi-devel>/packages/jpl.

    • Note this is the source code dir of JPL, not the folder within the build/ directory used in step 1.
  4. Edit the environment variables of the JUnit Run created to make sure it is using the current development of SWIPL compiled in step 1::

     ```bash
     LD_LIBRARY_PATH=../../build/packages/jpl;
     SWI_HOME_DIR=../../build/home;
     SWIPL_BOOT_FILE=../../build/home/boot.prc;
     LD_PRELOAD=../../build/src/libswipl.so 
     ```
    

Notes:

  • The LD_PRELOAD variable should not be necessary for the tests.
  • If the tests includes some Prolog code using some Java class in the JPL Java API, then one will need to setup an artifact to produce jpl.jar file, say the target dir for the project is out/ and also modify the CLASSPATH environment variable in the runner. This is needed because some Prolog test files will look for Java classes in that JAR and you want to use the one being developed within the IDE rather than the last generated by CMAKE (which could be old and don't reflect the changes did inside the IDE yet).
  • To get all the warnings add -Xlint to the "Additional commands line parameters" to the Java Compiler (Settings / Build / Compiler / Java Compiler). Check javac options.

Common commands from build/

cmake -DCMAKE_INSTALL_PREFIX:PATH=/usr/local/swipl-git -DINSTALL_DOCUMENTATION=OFF ..

make -C packages/jpl clean  ; make -j 8

ctest -V  -R jpl:prolog_in_java

src/swipl -x home/boot.prc -f none --home=home/ --no-signals

Error handling

For information on SWIPL error handling, check the Error generating support library and the Exception handling in ISO Prolog.

LICENSE

JPL is released under the terms of the Simplified BSD License. See LICENSE file.