RoboVM as a static library

1,018 views
Skip to first unread message

Kurayami Tenshi

unread,
Apr 16, 2014, 3:04:34 AM4/16/14
to rob...@googlegroups.com
Hi folks

After many trials and tribulations I managed to get robovm to compile as, and function as a static library (woohoo!)
I'm not sure how many here are interested in doing it, but I thought I should give back what I've learnt as a brief outline.

Firstly, Simon's work here has been very useful. It made things much easier than it could have been. But it wasn't enough for us as we wanted a robovm app to be a 'real' static library, that is, it doesn't seek to override the xcode app's main method.

To do that, we needed to modify the guts of robovm.
You'll need to add a method initialise robovm on demand.
This method will call a modified version of bc.c's main method. I called it customInitHook

For my setup, I created a simple file called custom_init that took in the app path and called bc's customInitHook.
customInitHook is the same as bc.c's main, except that it calls a special version of rvmRun, which doesn't invoke the Java app's main method.

Once the vm has been initialised, you can use Simon's bridge.cpp to call/pass over your stuff. Also, you can't really use jni in bridge.cpp, as the classloader is not there. There's a workaround, but I am not actively using it, since rvm's own methods (like rvmGetClassMethod) are good enough.

Note that for the static library to function properly, it can't declare bc.c's main mtd, since you can't have 2 main methods defined in an app. So it has to be renamed/commented out.


Another thing to take note of is the "gc.h" includes. Simon noted that it didn't seem to cause any problems, but that wasn't the case here. With "gc.h" commented out, the entire gc mechanism wasn't working at all, and the app would crash at random spots. Lots of time was spent debugging here.

In Simon's post, he also outlined a whole list of linker flags to be included in Xcode. That wouldn't have worked well for us, too many includes and configs for the host app, if you were to package off the static lib to someone else. Furthermore, the host app shouldn't need to know anything about robovm.

So we compiled everything into 1 static library. Robovm's .a files + our app files, and packaged it into a Framework.
You will still need to force_load it, for robovm-rt and native libraries, but it works fine, and our linker flags were cut down drastically, also it fixes the gc.h include issue in Xcode.

To pass data between xcode host app land and javaland, you'll want to convert your NSObject to long long and send it to Java. Then in Javaland, you'll want to use ObjCObject.toObjCObject(NSObject.class, handle).


Well that's about it, it works wonderfully and I'm always impressed when I dive into robovm's guts. Niklas is awesome(!!!). There are some niggling issues like certain javaland crashes would just cause the app to die without any traces, making it hard to debug, but so far so good!




Niklas Therning

unread,
Apr 20, 2014, 5:23:07 AM4/20/14
to Kurayami Tenshi, rob...@googlegroups.com
Thanks for posting this here! Hopefully it will help others who are attempting to use RoboVM in the same way. Using RoboVM in this way is not a use case we're going to support (maybe someday) but if there are any reasonably simple patches that you think would help make the process simpler we may accept them. Just let us know! 


--
You received this message because you are subscribed to the Google Groups "RoboVM" group.
To unsubscribe from this group and stop receiving emails from it, send an email to robovm+un...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Wolfgang Frank

unread,
Jul 29, 2014, 4:10:54 AM7/29/14
to rob...@googlegroups.com
Hello Kurayami,

your approach looks very promising and interesting for me. 
I am trying to accomplish the same goal to have a robovm static library to use from an Xcode project. The information how you succeeded and what the "real" necessary steps are 
to get it all to work is somehow distributed between the different threads in the forum and not fully clear to me.

Do you have an example/tutorial with the necessary steps (in more detail) that you might share with us?

That would be awesome!

Greets,
Wolfgang


Wolfgang Frank

unread,
Jul 29, 2014, 12:07:37 PM7/29/14
to rob...@googlegroups.com
I tried to reproduce the steps described in the two articles (from Simon and Kurayami) in the forum, but got stuck with a runtime error (BAD_ACCESS) trying to call back in my "Java" library from the C++ Bridge using either JNI or the rvmXX functions directly. 

I summarized the issue in an stack overflow post

Hopefully someone has succeeded already and can give me a hint. Unfortunately I don't have a deep knowledge of the robovm internals yet so I am not sure if the runtime needs some additional initialization?

Thanks,
Wolfgang




Wolfgang Frank

unread,
Jul 30, 2014, 3:49:51 AM7/30/14
to rob...@googlegroups.com
Hi,

especially this part is not clear to me:

>> For my setup, I created a simple file called custom_init that took in the app path and called bc's customInitHook.
>> customInitHook is the same as bc.c's main, except that it calls a special version of rvmRun, which doesn't invoke the Java app's main >> method.

Can you describe a little more in detail what you did or post an example?

Thanks!

Rene Ravenel

unread,
Oct 1, 2014, 2:18:22 PM10/1/14
to rob...@googlegroups.com
Hi,

I'm working thru these instructions and those linked from Simon.

I've succeeded in producing a native binary from my Java code, and calling in to C++ code from ObjC in an iOS app, but I've been unable to get the RoboVM JNI support to build w/ the iOS app.

I'm getting several errors in vm/core/include/robovm/types.h:

ln 45: Elaborated type refers to a typedef
ln 157: Definition of type 'Class' conflicts with typedef of the same name
ln 293: Definition of type 'Boolean' conflicts with typedef of the same name
ln 298: Definition of type 'Byte' conflicts with typedef of the same name

(source version: commit 07e9b7314a0dcf1aa0447df051f10230eaae352f)

So far, all my C++ does is  #include "robovm.h" in the header.  I'm not making any JNI calls. (I have commented out the gc.h include in robovm.h.)


Has anybody encountered this or similar problems?  Any suggestions on a solution?

Thanks in advance,
-Rene

Rene Ravenel

unread,
Oct 1, 2014, 2:40:16 PM10/1/14
to rob...@googlegroups.com
<Novice Xcode user>

Traced the conflict to usr/include/MacTypes.h in the iPhoneSimulator SDK (iPhoneOS SDK is the same).

The code in question in types.h dates from 2010 to 2013, so it was most likely in Kurayami's build when he started this thread.

I'm at a loss on how to resolve this conflict.  Any help appreciated.

Simon Robinson

unread,
Oct 2, 2014, 3:06:14 AM10/2/14
to rob...@googlegroups.com
Rene, this sounds familar..........
Fuzzy memories suggest I encountered this - its the result of putting #include 'robovm.h' in a header file that is along some path also included at the same time as the rest of apple's regular headers. e.g. a regular objective-c++ class doing objective-c things so needs to include normal cocoa things, but also includes a path to the robovm headers.

I didn't find any 'correct' way to solve it, so I worked around it by introducing another 'bridge' between proper iOS code and the robovm-jni code. So iOS code relying on Cocoa never has to see robovm.h

1) MyBridge.h : Includes actually nothing, so no dependency on Robovm or on iOS. Uses regular C types to declare functions going iOS-to-Java direction and the reverse Java-to-iOS
2) RoboJniCalls.cpp : includes robovm.h, includes MyBridge.h - defines those functions calling into java, and defines functions called from java and passes them on to something declared in MyBridge.h
3) MyBridge.mm : includes MyBridge.h and other regular iOS headers, i.e. can call Cocoa apis. So for the sake of some consistency, all my calls in both directions go through little trampoline functions here with occasional argument conversion (e.g. from NSArray to char**)

As I think again about this approach, its pretty yuck :) is there a more clever way to solve it yet?

Wolfgang Frank

unread,
Oct 2, 2014, 7:01:33 PM10/2/14
to rob...@googlegroups.com
I had to go down the same path to make it work and couldn't find a much better solution... Too

Rene Ravenel

unread,
Oct 2, 2014, 7:20:02 PM10/2/14
to rob...@googlegroups.com
I see what you're getting at in manipulating the include paths, but I'm having trouble seeing how to do so and still having a working app.  I'm coming from a Java/Android background, haven't touched C++ in over a decade and am new to ObjC, so I could be missing something obvious.

For the sake of simplicity, let's just consider calling from ObjC in to Java and receiving a result.

- RoboJNI.h includes robovm.h and declares some methods 
- RoboJNI.cpp implements calls to Java via JNI
- Bridge.h declares some methods
- Bridge.mm includes RoboJNI.h and calls RoboJNI methods
- ViewController.m includes Bridge.h and calls Bridge methods (to ultimately reach Java)

Including RoboJNI.h in Bridge.mm gives me conflicts, but if I don't, how can Bridge.mm access methods in RoboJNI?

Am I misunderstanding how all your pieces fit together?

Simon Robinson

unread,
Oct 3, 2014, 3:26:50 AM10/3/14
to rob...@googlegroups.com
You're almost there!

You need to try to #include fewer things from your .h files... I would argue this is a good idea in general, if there are any sort of conflicts or mixing of different frameworks/libraries etc
More precisely
- don't include robovm.h from RoboJNI.h, include it only from RoboJNI.cpp
- if this causes errors because you refer to some type in RoboJNI.h, then you could make it a more fundamental/common type, e.g. char* instead of jstring. Or you might be able to get away with forward declarations sometimes. Requires some c/c++ knowledge though.


Your JNI cpp file cannot know about cocoa, and your cocoa .mm files can not know about robovm, to avoid this conflict :)


>> Including RoboJNI.h in Bridge.mm gives me conflicts, but if I don't, how can Bridge.mm access methods in RoboJNI?
Basically, declare the methods that Bridge.mm needs somewhere else. But make sure they don't have any JNI related types as arguments, they will have to be c++ things like char*, void*.. int32_t not jint


..........something like:


MyBridge.h
=====

// Nothing included here

// Called from Bridge (obj-c) towards JAVA
void callJavaLogin(const char* u, const char* p);

// Called from Java/JNI towards the bridge
MyBridge_sendNotification(void* cbID);


RoboJNI.cpp
======

#include “MyBridge.h”
#include “robovm.h”

extern “C”
{
void Java_com_blah_MyJavaType_sendNotification(Env* env, Class* c, jlong cbID, jobjectArray lines, jboolean expectMore)
{
// Java called us, pass to bridge using c++ types
MyBridge_sendNotification((void*)cbID);
}
}


void callJavaLogin(const char* u, const char* p) {
// Call out to JNI/Java e.g. CallStaticObjectMethod(….)
}



MyBridge.mm
=====

#include <stdio.h>
#include "MyBridge.h"

#import “SomeRegularObjCStuff.h”

void MyBridge_sendNotification(void* cbID)
{
// Fire back towards objective-c classes
NSLog(@“Got sentNotification %p”, cbID);
}

void invokeJavaLogin(NSString *username, NSString *password)
{
// Take my arguments from objectiveC and send them on to java
char* u = …
char* p = …
callJavaLogin(u, p);
}

Wayne Shelley

unread,
Aug 12, 2015, 10:17:20 AM8/12/15
to RoboVM
Hello and thanks for all this documentation. I've been trying to do this for a while but I've been unable to get Xcode to compile robovm/jni. The method you describe sounded promising but was wondering if there was a project example that you could share/recommend?

Cheers,
Wayne.

Niklas Therning

unread,
Aug 12, 2015, 11:27:51 AM8/12/15
to Wayne Shelley, RoboVM
Have a look at the MyJavaFramework sample [1]. It compiles Java code into a dynamic framework which you can then use from your ObjC/Swift code via JNI. It's not a static lib though so your app will only work on iOS 8+.


Reply all
Reply to author
Forward
0 new messages