Running JNI based code inside XCode

Sometimes you want to run JNI based code inside XCode – e.g. in case when your C/Objective-C/C++ code is based on some Java library.

It’s both simple and little bit confusing to get JNI running in XCode – you have to make sure to set few things before proceeding.

1. Getting source code

In this article I will use one of mine recipes from JNI Cookbook. Note that source code can be found here: GitHub.

> mkdir -p ~/workspace
> cd ~/workspace
> git clone https://github.com/mkowsiak/jnicookbook

2. Creating new project inside XCode

Start XCode and create new project macOS -> Command Line Tool. Once there, make sure to remove main.m file and replace it with $HOME/workspace/jnicookbook/recipeNo065/c/recipeNo065_main.m. You should have something like this.

3. Adding custom user variable

Make sure to switch to Project -> Build Settings so you can add new, user-defined, variable. I will use JAVA_HOME. It should point to location where your Java is installed. You can get this location by calling java_home

> /usr/libexec/java_home -v 11.0.4
/Library/Java/JavaVirtualMachines/jdk-11.0.4.jdk/Contents/Home

4. Setting up Search Paths

You need to set following search paths: Header Search Path, Library Search Path, and Runpath Search Path. Inside Header Search Path section make sure to add: $(JAVA_HOME)/include/darwin and $(JAVA_HOME)/include. Like this

Inside Library Search Path make sure to set location of libjvm.dylib ($(JAVA_HOME)/lib/server). You can set it following way

Last thing to set here is Runpath Search Path. Make sure to add $(JAVA_HOME)/lib/server there as well

5. Make sure your code is linked with libjvm.dylib

Last thing we have to do (in order to compile the code) is to pass -ljvm to the linker

6. Running the code

Note that JVM uses SIGSEGV to throw exceptions. Make sure to set breakpoint early in your code and ignore SIGSEGV

(lldb) process handle --pass true --stop false --notify true SIGSEGV

7. Continue debugging session

After process handle --pass true --stop false --notify true SIGSEGV is executed inside lldb you can continue with the debugger session.