-
Notifications
You must be signed in to change notification settings - Fork 2
Building JSVM
JSVM embeds Duktape, which is a JavaScript virtual machine, written in C (it actually can be C or C++ depending on build settings. In order to do that, JSVM uses glue code written in C++ that uses the Java Native Interface.
Unfortunately, as a consequence building and distributing JSVM is harder than regular pure Java libraries, because of the need to compile and link C++ into platform specific code.
Thankfully, Android Studio has pretty good tooling for JNI. Also, Google provides the Android NDK, which contains all the compilers and tools you need to build JNI libraries.
In order to build JSVM you just need to install Android Studio, make sure the CMake and NDK packages are installed (both can be installing using the Android SDK repository), open the project and click Build. Everything should work OK.
If you run ./gradlew install
in the console you will get an AAR file in ~/.m2/repository/com/github/ntrrgc/jsvm-lib/unspecified/jsvm-lib-unspecified.aar
which you can use in other projects.
In practice, rather building .aar
files yourself, it's easier to defer this work to JitPack, which will do the same thing behind the scenes but it's much easier to use in dependent projects.
Android Studio is the recommended tool to develop JSVM, for many reasons:
- It can edit both C++ and Java, and does both of them very decently.
- It supports debugging both. You can step in native functions and set breakpoints... and it just works!
- It uses a special JNI wrapper that checks for many types errors and explains them in the Android Monitor.
- Because the library runs in Android (through a virtual machine or a real device connected with adb), which is always a Linux, you don't have to worry about the fact that libraries are handled in very different ways in each operating system.
- Since all the tooling is provided by the Android SDK and NDK, and Android expects libraries to be built in very specific ways there is very little config needed to take advantage of all of the above.
- Android is the main platform for JSVM due to the lack of compelling alternatives. Other solutions such as Nashorn or Rhino, which would be easier to deploy due to being pure Java libraries, are either unavailable or are too big and perform poorly due to the constraints of the Android Java runtimes.
JSVM tests are provided as Android instrumented tests so that they are run in the Android device and take advantage of all the Android tooling.
In order to run them, open Android Studio, right-click jsvm-lib/src/androidTest/java/me/ntrrgc/jsvm/fastTests
and click Run.
The test battery does not use anything Android-specific, so it can be run outside of Android as well even though it is inside androidTest
.
Due to the better tooling, it's recommended to develop JSVM in Android Studio, running the tests in an Android device or virtual machine. But JSVM can run outside Android just fine! The difficult part is getting all the pieces needed to build native code in your operating system.
In order to build and use JSVM, like any JNI library, three steps must be accomplished:
- Build a JAR library containing the Java part of the library. This file is portable across different operating systems, machine platforms and JVM implementations.
- Build a native library containing the C++ part of the library. This part varies heavyly accross operating systems. The file generated may have
.dll
extension (Windows),.so
(Linux) or.dylib
(MacOS). These files are not portable since they contain machine code. So, for instance, a.so
file compiled for a PC running Ubuntu will not work in a Raspberry Pi, even though both use a Linux operating system, because the CPU architectures are incompatible (x86 vs ARM). - Place both parts of the library in such a way they are found and executed by the client application.
This is the easiest part. You just need Gradle and the JDK. Run this command:
./gradlew -p jsvm-lib -b jsvm-lib/standalone.build.gradle jar
The generated JAR is stored in ./jsvm-lib/build/libs/jsvm-lib.jar
.
In order to do this you need a C++ compiler such as gcc or clang. Recent versions of MSVC with C++11 support may work, but have not been tested. You will also need CMake, which is easy to install in most platforms.
Create a directory to store the library file and any temporary files created in the process and configure a build there:
mkdir jsvm-lib/build/native
cmake ../../ -DCMAKE_BUILD_TYPE=Release
Look in the output for mentions to JNI_INCLUDE_DIRS
and JNI_LIBRARIES
. These should point to the locations inside your distribution of Java. If those lines don't appear, the build may fail. Make sure that you have installed the Java Development Kit (JDK).
-- JNI_INCLUDE_DIRS=/usr/lib/jvm/default/include;/usr/lib/jvm/default/include/linux;/usr/lib/jvm/default/include
-- JNI_LIBRARIES=/usr/lib/jvm/default/jre/lib/amd64/libjawt.so;/usr/lib/jvm/default/jre/lib/amd64/server/libjvm.so
Finally, tell cmake to build the library:
cmake --build .
The build should finish without errors and a native library file will be generated in that same directory, e.g. libjsvm.so
.
Unfortunately, much like the format of the native libraries is different across operating systems, the method for getting them found also varies.
In general, the Java runtime will ask the operating system to find a library with a given name (jsvm
). Linux looks for them in a number of system directories like /usr/lib
, but also checks first any directory specified in the LD_LIBRARY_PATH
environment variable. Windows looks for them in the current working directory, but also searches in any directory specified in the PATH
environment variable.
You may also need to configure your IDE so that it finds the native library in debug runs of your application. For instance, IntelliJ allows to add native library locations in the Project structure settings.
In order to run the test battery you need Gradle and the native library. You also need to make sure the native library will be found (e.g. by using an environment variable). Building the JAR is not necessary.
Run the following command from the root of the project
./gradlew -p jsvm-lib -b jsvm-lib/standalone.build.gradle test
The JAR and the native library should come from the same version of JSVM. Mixing recent Java code with old C++ code or vice versa may behave in erratic ways or crash.