COMMUNITY

How to create a jar file from some Haxe code?

I’d like to use some Haxe code from within an existing Java program — that is, (I think this is the customary way) I want to first compile my Haxe code into a jar file, so I can then use that jar file from my Java program. My Java is a little rusty.

To create the jar file, I’ve made a tiny hello-world Haxe library:

my-haxe-lib/
    README.md
    my_stuff/
        Foo.hx

in Foo.hx:

package my_stuff;

class Foo {
    public static function do_stuff() {
        trace("Hi from Haxe!");
    }
}

How can I create a jar file from this? I’ve installed hxjava (haxelib install hxjava), then tried:

cd my-haxe-lib
haxe -java my_stuff/Foo.hx  # nope
haxe -java my_stuff         # nope

but neither of those seem to be right.

My understanding is that once I have the jar file, I can use it just as I’d use any other jar file from my Java program.

Thanks!

Your “main” function needs to be called main

And then try:

haxe -main my_stuff.Foo -java my_stuff/Foo

The “main” option is your main class. The -java option identifies your target as java with its value being the folder where the jar will appear.

I think you will find everything you need here: https://haxe.org/manual/compiler-usage.html

You can also use hxml to put all the command line flags in a file for convenience.

If the code will be used from a Java app you should build without -main, and you will want to add annotations to types that Java will use, and make sure you expose a usable “API” to the Java side:

Thanks for the link to the manual page. Missed that!

Thank you, Philippe! I’m almost there! Please note, I’m doing this from the command line on a Debian GNU/Linux system with OpenJDK 10 installed:

$ java -version
openjdk version "10.0.2" 2018-07-17
OpenJDK Runtime Environment (build 10.0.2+13-Debian-1)
OpenJDK 64-Bit Server VM (build 10.0.2+13-Debian-1, mixed mode)

$ javac -version
javac 10.0.2

I saw the haxe command as shown in the StackOverflow post. I see that the -cp src tells haxe where to find my Haxe source code, so I changed my library directory to match:

my-haxe-lib/
    README.md
    src/
        my_stuff/
            Foo.hx

I see that the -java my_stuff option tells haxe where to put the output of compilation.

When I run the command from the my-haxe-lib directory:

haxe -cp src -java my_stuff -D no-root my_stuff.Foo

I get a lot of stuff generated in a newly-created my_stuff directory, including a my_stuff.jar. Woot!

I then copied that my_stuff.jar file into the-java-proj directory (which is just my own hello-world Java app). This directory contains a Main.java:

import my_stuff.Foo;

class Main {
    public static void main(String[] args) {
        System.out.println("Hello, Java!");
        Foo.do_stuff();
    }
}

I can build that project via javac -cp my_stuff.jar Main.java, but when I run it (via java Main) I get errors:

$ java Main
Hello, Java!
Exception in thread "main" java.lang.NoClassDefFoundError: my_stuff/Foo
	at Main.main(Main.java:6)
Caused by: java.lang.ClassNotFoundException: my_stuff.Foo
	at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
	at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:190)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:499)
	... 1 more

Any ideas on where I’m going wrong?

Thanks!

Couple extra notes:

When I build the jar, it goes like:

$ haxe -cp src -java my_stuff -D no-root my_stuff.Foo
haxelib run hxjava hxjava_build.txt --haxe-version 3407 --feature-level 1
javac "-sourcepath" "src" "-d" "obj" "-g:none" "@cmd"

Here’s what’s in the jar:

$ jar tf my_stuff.jar 
META-INF/
META-INF/MANIFEST.MF
my_stuff/
my_stuff/Foo.class
haxe/
haxe/lang/
haxe/lang/Function.class
haxe/lang/IHxObject.class
haxe/lang/StringRefl.class
haxe/lang/Enum.class
haxe/lang/Runtime.class
haxe/lang/StringExt.class
haxe/lang/IEquatable.class
haxe/lang/DynamicObject.class
haxe/lang/EmptyObject.class
haxe/lang/Closure.class
haxe/lang/VarArgsBase.class
haxe/lang/HxObject.class
haxe/lang/Exceptions.class
haxe/lang/HaxeException.class
haxe/lang/VarArgsFunction.class
haxe/lang/FieldLookup.class
haxe/lang/ParamEnum.class
haxe/Log_Anon_47__Fun.class
haxe/Log.class
haxe/root/
haxe/root/Reflect.class
haxe/root/Std.class
haxe/root/StringBuf.class
haxe/root/Type.class
haxe/root/Array.class
_Array/
_Array/ArrayIterator.class

Yeaaah I can confirm the same thing on my side… It’s been a while so I’m not sure.

Thanks for confirming, Philippe.

Ok. Got it. When running, needed:

java -cp .:my_stuff.jar Main

To help gather up this sort of platform-specific getting-started info, I started https://github.com/uvtc/haxe-getting-started-tutorials .

For these haxe-in-a-java project notes:

1 Like

Makes sense, if you only compile your .java file to .class it doesn’t include the .jar dependency.

I used to try it in an Android project so Android Studio was taking care of embedding the dependencies…

Is there any option to put the haxe/lang stuff in a separate jar file?

In general I’m looking at using Haxe to write and publish various cave survey data routines in multiple output languages.

For example I would publish @speleotica/compass, @speleotica/walls npm packages with the output JS code, and also Maven packages with the output Java code.

But right now the jar files for those two packages would have duplicate haxe/lang stuff that would conflict. Is there an option to keep haxe/lang out of the generated jar file? Is the haxe/lang stuff published to Maven in its own package that packages can depend on?

The goal is for no one to have to deal with Haxe directly to use my libs – I want Node users to be able to install my generated npm package, Java users to be able to install my generated Maven package, C++ users to (somehow) use the generated C++ source, etc.

Seems like this might be a difficulty for most output languages besides JS that have a global package namespace.

This is not only for haxe/lang. It will also happen when two haxe-generated jar using the same haxe libraries.

There are probably ways to do this via Compiler.exclude, but I’d like to provide better support for something like this for the JVM target regardless. If you have any suggestion for how to handle this, please let me know.

It will also happen when two haxe-generated jar using the same haxe libraries.

Ah good point…

If you have any suggestion for how to handle this, please let me know.

I think it would be great to have JARs containing haxe.lang for each version of Haxe published to Maven central, so that packages we publish could declare a dependency on it. And then basically a switch to not bundle the lang packages into the generated JAR.

As far as if I want to publish A and B as Maven packages and also have B depend on A, fortunately that seems not too difficult given that Haxe’s package system is virtually identical to Java. I would basically just need to declare that certain packages are external (maybe that can already be done with --java-lib, I’m not sure)

Of course, I need to figure out if how to avoid duplicate lang class conflicts with the C++ and Python targets as well…

Another thing that would be more appropriate in the JS compiler is to allow the global package tree object to be obtained from outside each generated script so that they would all put their classes onto the same shared package tree.

1 Like

I also just realized that only Haxe standard library features that my code uses get output to the target language, for instance the EReg class isn’t generated unless I use a regular expression. So this will complicate my efforts to cobble together a Haxe core JAR to use for the time being as a dependency of the various libraries I want to create. I don’t guess there’s any switch to output the entire Haxe core?

As far as publishing the generated Haxe core for other languages, I do understand that publishing to Conan for C++ seems very imposing…in fact that’s what got me interested in using Haxe in the first place. My friend had almost convinced me that we should share code written in C++ since it can be used on virtually any platform these days. But publishing packages to Conan looked like such a nightmare that I went back to googling for source-to-source compilers and discovered Haxe :joy:

A combination of disabling dead code elimination and forcing the packages you want to be included should work. Example hxml file:

-dce no

# it's necessary to exclude haxe.macro, otherwise we run
# into some errors for referencing macro API in non-macro context
--macro include("haxe", true, ["haxe.macro"])

# other packages / types you want can simply be listed as dot-paths
# (see https://code.haxe.org/category/compilation/compiling-libraries-without-main-class.html)
sys

# some toplevel types may need to be listed individually if there's
# no references to them within std (generated into haxe.root on JVM)
DateTools
# ...

--jvm haxe-std.jar
1 Like

Awesome, I feel pretty confident this will work for what I’m trying to do now, thanks for the tips!