[SOLVED] Missing JAR in desktop deployment


#6

Including A1.jar without Minie-0.6.5.jar doesn’t work. It seems necessary to include the JAR twice. I don’t understand why.


#7

not sure, i were not looking inside JARs of subprojects that have also dependency on other JAR.

thats correct, i always needed include all JARs anyway, but i did not investigate why.(did not know its included twice)


#8

I’d added all my external JAR dependencies (including Minie) via the project’s Libraries properties. With the exception of Minie, they all worked as expected.

By “overrides” I was referring to classes and resources that are defined both in Maud and in libraries or external JARs that Maud uses.

For example, there’s an issue with the com.jme3.scene.plugins.gltf.GlbLoader class in the jme3-plugins-3.2.2-stable.jar in the jme3-plugins library. The fix is a one-line change. For the Gradle build of Maud, I simply created a src/main/java/com/jme3/scene/plugins/gltf folder in my project and added a corrected GlbLoader.java file to it; the corrected class file overrode the one in the JAR, conveniently resolving the issue.

In Ant builds, however, library classes override those local to the project, so I had to remove the library from AuntMaud, build a jme3-plugins-3.2.2-v3.2-SNAPSHOT.jar that includes the fix, and add that to the build. This is inconvenient, but I don’t see any way around it.


#9

Could it be that this “override” is what ant simply does not support as opposed to gradle?


#10

Yes, Ant doesn’t support overrides. I know. That’s part of why creating an Ant build for Maud was difficult: I had to build custom JARs to eliminate all the overrides in the Gradle build.

I was just explaining to oxplay2 what I meant by “overrides”.

What’s interesting is that after eliminating all the overrides, I discovered the (still unexplained) “missing JAR” issue, which I worked around by trial and error.


#11

I don’t think that’s a normal ANT feature but likely just how JME SDK’s ANT builds are setup. It all depends on classpath ordering in the end and maybe JME SDK’s ANT scripts always add the local classes to the end or something? (broken, in my opinion)


#12

But if override means “I have two classes with the same package and name on my classpath and I want to decide which one the classloader sees” then you must either create your own classloader or actually fix your classpath to not have duplicates.
But maybe you mean something else with “override”?


#13

I haven’t looked at the code, but I imagine Gradle extracts the classes from the JARs and resolves any duplicates. However the trick is accomplished, it’s always worked for me so far.


#14

Java does this. No magic.

jar1 class com.foo.Bar
jar2 class com.foo.Bar

…if jar1 is earlier in the class path then the first one is loaded. If jar2 is earlier in the classpath then the second one is loaded.

And the fun part is that on application servers on different operating systems you might get a different order. Fun tracking down problems where com.foo.Bar from one jar is ancient and only loads first on Linux… caused by rampant ‘mavenitus’, aka: “I need this string util method so I’ll just add this dependency to apache-the-whole-world…”


#15

Oh come on, don’t be so hard on those poor guys. Doing a Base64 encode/decode by hand might take like 20 lines of code or something! :wink:


#16

What’s even worse is when there IDE suggests some util method for them that is from some 9 level deep dependency… now we’re stuck with that until we fix it. (Probably to use the util method that was already in Guava.)

…it’s a constant struggle.


#17

In that case, what I want is simply to put project-defined classes (and assets) before external JARs in all my classpaths. Gradle does this by default; in fact, I don’t know a way around it.

For reasons I don’t understand, the SDK’s BasicGame project template sets up the classpaths like so:

run.classpath=\
    ${javac.classpath}:\
    ${build.classes.dir}:\
    ${assets.folder.name}

putting external JARs and libraries (javac.classpath) before project-defined classes (build.classes.dir) and assets (assets.folder.name). My Ant build, which was based on the BasicGame template, inherited this disease.

Redefining javac.test.classpath and run.classpath to use the desired order enables the use of standard JME libraries in my Ant build, which will simplify the process of deploying Maud in the future.

Desktop deployment is still skipping over Minie-0.6.5.jar, however.

I wonder if that issue relates to the presence of native libraries in the JAR. There’s some code in desktop-deployment-impl.xml that seems intended to extract native libraries from JARs. As far as I know, JME’s libraries never combine classes and native libraries in the same JAR, but Minie-0.6.5.jar does. What if finding new native libraries in a JAR causes desktop deployment to discard the remainder of the JAR? That might explain the behavior I’m seeing.


#18

No it does not, just because it happens to work on your computer does not mean it works on anyone elses. It’s as pspeed says, it is undefined and varies between machines what order the JVM encounters classes on the classpath. There are only two ways to make sure, either write your own classloader hierarchy or get rid of the duplicates on the classpath.


#19

If the jars are specified in the manifest.mf then they should be loaded in the order in the manifest.

The problem with app servers (like JBoss) in my example is that they just expand the lib directory to make the classpath. Then it just depends on what order the files are in the directory… which is OS dependent.

The classpath references in a jar’s manifest should be loaded in order, though.


#20

@jmaasing,

  1. Have you tried building Maud on your computer? And if so, did it work, or didn’t it?
  2. Do you have any insight into why I have to specify 2 copies of Minie-0.6.5.jar in order to deploy it to the desktop?

#21

No I have not tried to build Maud on my computer. My point is that Gradle absolutely does not do anything magic to control the order the JVM finds classes because it can’t.

The reason it can’t is as Paul says. Classloading is machine dependant. In practice you might predict the order (like specifying manifest attributes) but in theory there are no guarantees since classloaders are allowed to do parallel loading.

So maybe that is the difference you experience? The ant and gradle builds maybe sets up the manfest attribute differently?


#22

Just re-reading this post. Is there any possibility of seeing the ant file that you are using?

Looking-down-the-straw from a distance, it sounds like somehow the fileset that copies libraries from dist/lib to the various platform distributions is being set up in a way that excludes or filters the Minie jar, perhaps based on some peculiarity with it’s name?


#23

That’s the most sensible suggestion I’ve heard so far. The XML files are unchanged from JME’s BasicGameTemplate, which can be found at https://github.com/jMonkeyEngine/sdk/tree/v3.2.2-stable-sdk1/BasicGameTemplate

And here is my project.properties file:

annotation.processing.enabled=true
annotation.processing.enabled.in.editor=false
annotation.processing.processors.list=
annotation.processing.run.all.processors=true
application.desc=An editor for jMonkeyEngine3 animated models.
application.homepage=https://github.com/stephengold/Maud
application.splash=assets/Textures/icons/Maud.png
application.title=Maud
application.vendor=sgold
assets.jar.name=assets.jar
assets.excludes=
assets.folder.name=assets
assets.compress=true
build.classes.dir=${build.dir}/classes
build.classes.excludes=**/*.java,**/*.form
# This directory is removed when the project is cleaned:
build.dir=build
build.generated.dir=${build.dir}/generated
build.generated.sources.dir=${build.dir}/generated-sources
# Only compile against the classpath explicitly listed here:
build.sysclasspath=ignore
build.test.classes.dir=${build.dir}/test/classes
build.test.results.dir=${build.dir}/test/results
bundle.jre.enabled=true
compile.on.save=true
# Uncomment to specify the preferred debugger connection transport:
#debug.transport=dt_socket
debug.classpath=\
    ${run.classpath}
debug.test.classpath=\
    ${run.test.classpath}
# This directory is removed when the project is cleaned:
dist.dir=dist
dist.jar=${dist.dir}/${application.title}.jar
dist.javadoc.dir=${dist.dir}/javadoc
endorsed.classpath=
excludes=
file.reference.jME-TTF-v2.12.jar=S:\\saved downloads\\jars\\Google.com\\jME-TTF-v2.12\\jME-TTF-v2.12.jar
file.reference.jme3-utilities-debug-0.9.9.jar=S:\\releases\\debug\\0.9.9\\jme3-utilities-debug-0.9.9.jar
file.reference.jme3-utilities-heart-2.18.0.jar=S:\\releases\\heart\\2.18.0\\jme3-utilities-heart-2.18.0.jar
file.reference.jme3-utilities-nifty-0.9.1.jar=S:\\releases\\nifty\\0.9.1\\jme3-utilities-nifty-0.9.1.jar
file.reference.jme3-utilities-ui-0.7.0.jar=S:\\releases\\ui\\0.7.0\\jme3-utilities-ui-0.7.0.jar
file.reference.jme3_physicsloader-0.5.jar=S:\\saved downloads\\jars\\BinTray.com\\jme3_physicsloader-0.5.jar
file.reference.jme3_xbuf_loader-0.9.1.jar=S:\\saved downloads\\jars\\BinTray.com\\jme3_xbuf_loader-0.9.1.jar
file.reference.jme3_xbuf_rt-0.9.1.jar=S:\\saved downloads\\jars\\BinTray.com\\jme3_xbuf_rt-0.9.1.jar
file.reference.logback-classic-1.2.3.jar=S:\\saved downloads\\jars\\BinTray.com\\logback-classic-1.2.3.jar
file.reference.logback-core-1.2.3.jar=S:\\saved downloads\\jars\\BinTray.com\\logback-core-1.2.3.jar
file.reference.Minie-0.6.5.jar=S:\\releases\\Minie\\0.6.5\\Minie-0.6.5.jar
file.reference.Minie.jar=S:\\releases\\Minie\\0.6.5\\Minie.jar
file.reference.nifty-1.4.2.jar=S:\\saved downloads\\jars\\BinTray.com\\nifty-1.4.2.jar
file.reference.nifty-default-controls-1.4.2.jar=S:\\saved downloads\\jars\\BinTray.com\\nifty-default-controls-1.4.2.jar
file.reference.nifty-style-black-1.4.2.jar=S:\\saved downloads\\jars\\BinTray.com\\nifty-style-black-1.4.2.jar
file.reference.protobuf-java-3.0.0.jar=S:\\saved downloads\\jars\\BinTray.com\\protobuf-java-3.0.0.jar
file.reference.sfntly.jar=S:\\saved downloads\\jars\\Google.com\\jME-TTF-v2.12\\sfntly.jar
file.reference.SkyControl-0.9.16.jar=S:\\releases\\SkyControl\\0.9.16\\SkyControl-0.9.16.jar
file.reference.slf4j-api-1.7.25.jar=S:\\saved downloads\\jars\\BinTray.com\\slf4j-api-1.7.25.jar
file.reference.Wes-0.3.9.jar=S:\\releases\\Wes\\0.3.9\\Wes-0.3.9.jar
file.reference.xbuf-0.9.1.jar=S:\\saved downloads\\jars\\BinTray.com\\xbuf-0.9.1.jar
file.reference.xpp3-1.1.4c.jar=S:\\saved downloads\\jars\\BinTray.com\\xpp3-1.1.4c.jar
includes=**
jar.archive.disabled=${jnlp.enabled}
jar.compress=true
jar.index=${jnlp.enabled}
javac.classpath=\
    ${libs.jme3-core.classpath}:\
    ${libs.jme3-blender.classpath}:\
    ${libs.jme3-desktop.classpath}:\
    ${libs.jme3-effects.classpath}:\
    ${libs.jme3-lwjgl.classpath}:\
    ${libs.jme3-plugins.classpath}:\
    ${libs.jme3-terrain.classpath}:\
    ${file.reference.jme3_physicsloader-0.5.jar}:\
    ${file.reference.jme3_xbuf_rt-0.9.1.jar}:\
    ${file.reference.slf4j-api-1.7.25.jar}:\
    ${file.reference.logback-core-1.2.3.jar}:\
    ${file.reference.logback-classic-1.2.3.jar}:\
    ${file.reference.jme3_xbuf_loader-0.9.1.jar}:\
    ${file.reference.nifty-1.4.2.jar}:\
    ${file.reference.nifty-default-controls-1.4.2.jar}:\
    ${file.reference.nifty-style-black-1.4.2.jar}:\
    ${file.reference.protobuf-java-3.0.0.jar}:\
    ${file.reference.xbuf-0.9.1.jar}:\
    ${file.reference.xpp3-1.1.4c.jar}:\
    ${file.reference.sfntly.jar}:\
    ${file.reference.jME-TTF-v2.12.jar}:\
    ${file.reference.jme3-utilities-heart-2.18.0.jar}:\
    ${file.reference.jme3-utilities-debug-0.9.9.jar}:\
    ${file.reference.Minie.jar}:\
    ${file.reference.Minie-0.6.5.jar}:\
    ${file.reference.jme3-utilities-ui-0.7.0.jar}:\
    ${file.reference.jme3-utilities-nifty-0.9.1.jar}:\
    ${file.reference.SkyControl-0.9.16.jar}:\
    ${file.reference.Wes-0.3.9.jar}
# Space-separated list of extra javac options
javac.compilerargs=-Xlint:unchecked -Xlint:deprecation
javac.deprecation=true
javac.external.vm=false
javac.processorpath=\
    ${javac.classpath}
javac.source=1.8
javac.target=1.8
javac.test.classpath=\
    ${build.classes.dir}:\
    ${javac.classpath}
javadoc.additionalparam=
javadoc.author=false
javadoc.encoding=${source.encoding}
javadoc.noindex=false
javadoc.nonavbar=false
javadoc.notree=false
javadoc.private=false
javadoc.splitindex=true
javadoc.use=true
javadoc.version=false
javadoc.windowtitle=
jaxbwiz.endorsed.dirs="${netbeans.home}/../ide12/modules/ext/jaxb/api"
jme.project.version=3.1
jnlp.codebase.type=local
jnlp.descriptor=application
jnlp.enabled=false
jnlp.offline-allowed=false
jnlp.signed=false
linux-x64.app.enabled=true
linux-x86.app.enabled=true
macosx-x64.app.enabled=true
main.class=maud.Maud
meta.inf.dir=${src.dir}/META-INF
manifest.file=MANIFEST.MF
mkdist.disabled=false
platform.active=default_platform
run.classpath=\
    ${build.classes.dir}:\
    ${assets.folder.name}:\
    ${javac.classpath}
# Space-separated list of JVM arguments used when running the project
# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
# or test-sys-prop.name=value to set system properties for unit tests):
run.jvmargs=-ea
run.test.classpath=\
    ${javac.test.classpath}:\
    ${build.test.classes.dir}
source.encoding=UTF-8
src.dir=src
windows-x64.app.enabled=true
windows-x86.app.enabled=true

There are also some XML files that probably were added by NetBeans. I think the key one is desktop-deployment-impl.xml, which looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<!--desktop-deployment-impl.xml v1.0-->
<project name="desktop-deployment-impl" basedir="..">
    <target name="-desktop-deployment" depends="-extract-native-binaries, -create-launcher-jvmargs, -test-platforms-enabled, -windows-x86-app, -windows-x64-app, -linux-x86-app, -linux-x64-app, -macosx-x64-app"/>
    
    <target name="-windows-x86-app" if="is.windows-x86.app.enabled">
        <echo>Windows 32bit Application Creation</echo>
        <copy file="resources/desktop-deployment/windows-x86/package.cfg" tofile="resources/desktop-deployment/windows-x86/_package.cfg">
            <filterchain>
                <replacestring from="$${main.class}" to="${main.class.launcher}"/>
                <replacestring from="$${launcher.jvmargs}" to="${launcher.jvmargs}"/>
                <replacestring from="$${application.title}" to="${application.title}"/>
                <replacestring from="$${jar.name}" to="${application.title}.jar"/>
            </filterchain>
        </copy>
        
        <zip destfile="${dist.dir}/${application.title}-Windows-x86.zip">
            <zipfileset file="resources/desktop-deployment/windows-x86/stub.exe" filemode="755" fullpath="${application.title}/${application.title}.exe"/>
            <zipfileset file="resources/desktop-deployment/windows-x86/_package.cfg" fullpath="${application.title}/app/package.cfg"/>
            <zipfileset file="resources/desktop-deployment/windows-x86/icon.ico" fullpath="${application.title}/${application.title}.ico"/>
            <zipfileset file="${dist.jar}" prefix="${application.title}/app"/>
            <zipfileset dir="${dist.dir}/lib" excludes="${deployment.jarexcludes}" prefix="${application.title}/app/lib"/>
            <zipfileset dir="${build.dir}/natives/windows-x86" prefix="${application.title}/app" erroronmissingdir="false"/>
        </zip>
        <delete file="resources/desktop-deployment/windows-x86/_package.cfg"/>
        <antcall target="-package-windows-x86-jre"/>
    </target>
    
    <target name="-package-windows-x86-jre" if="is.bundle.jre.enabled">
        <untar src="resources/desktop-deployment/jre-windows-x86.tar.gz" dest="build/jre" compression="gzip"/>
        <dirset dir="build/jre" id="dirId-windows-x86">
            <include name="jre*"/>
        </dirset>
        <property name= "dirName-windows-x86" refid= "dirId-windows-x86"/>
        <zip destfile="${dist.dir}/${application.title}-Windows-x86.zip" update="true">
            <zipfileset dir="build/jre/${dirName-windows-x86}" prefix="${application.title}/runtime/jre"/>
        </zip>
        <delete dir="build/jre"/>
    </target>
    
    <target name="-windows-x64-app" if="is.windows-x64.app.enabled">
        <echo>Windows 64bit Application Creation</echo>
        <copy file="resources/desktop-deployment/windows-x64/package.cfg" tofile="resources/desktop-deployment/windows-x64/_package.cfg">
            <filterchain>
                <replacestring from="$${main.class}" to="${main.class.launcher}"/>
                <replacestring from="$${launcher.jvmargs}" to="${launcher.jvmargs}"/>
                <replacestring from="$${application.title}" to="${application.title}"/>
                <replacestring from="$${jar.name}" to="${application.title}.jar"/>
            </filterchain>
        </copy>
        
        <zip destfile="${dist.dir}/${application.title}-Windows-x64.zip">
            <zipfileset file="resources/desktop-deployment/windows-x64/stub.exe" filemode="755" fullpath="${application.title}/${application.title}.exe"/>
            <zipfileset file="resources/desktop-deployment/windows-x64/_package.cfg" fullpath="${application.title}/app/package.cfg"/>
            <zipfileset file="resources/desktop-deployment/windows-x64/icon.ico" fullpath="${application.title}/${application.title}.ico"/>
            <zipfileset file="${dist.jar}" prefix="${application.title}/app"/>
            <zipfileset dir="${dist.dir}/lib" excludes="${deployment.jarexcludes}" prefix="${application.title}/app/lib"/>
            <zipfileset dir="${build.dir}/natives/windows-x64" prefix="${application.title}/app" erroronmissingdir="false"/>
        </zip>
        <delete file="resources/desktop-deployment/windows-x64/_package.cfg"/>
        <antcall target="-package-windows-x64-jre"/>
    </target>
    
    <target name="-package-windows-x64-jre" if="is.bundle.jre.enabled">
        <untar src="resources/desktop-deployment/jre-windows-x64.tar.gz" dest="build/jre" compression="gzip"/>
        <dirset dir="build/jre" id="dirId-windows-x64">
            <include name="jre*"/>
        </dirset>
        <property name= "dirName-windows-x64" refid= "dirId-windows-x64"/>
        <zip destfile="${dist.dir}/${application.title}-Windows-x64.zip" update="true">
            <zipfileset dir="build/jre/${dirName-windows-x64}" prefix="${application.title}/runtime/jre"/>
        </zip>
        <delete dir="build/jre"/>
    </target>
    
    <target name="-linux-x86-app" if="is.linux-x86.app.enabled">
        <echo>Linux 32bit Application Creation</echo>
        <copy file="resources/desktop-deployment/linux-x86/package.cfg" tofile="resources/desktop-deployment/linux-x86/_package.cfg">
            <filterchain>
                <replacestring from="$${main.class}" to="${main.class.launcher}"/>
                <replacestring from="$${launcher.jvmargs}" to="${launcher.jvmargs}"/>
                <replacestring from="$${application.title}" to="${application.title}"/>
                <replacestring from="$${jar.name}" to="${application.title}.jar"/>
            </filterchain>
        </copy>
        
        <zip destfile="${dist.dir}/${application.title}-Linux-x86.zip">
            <zipfileset file="resources/desktop-deployment/linux-x86/stub" filemode="755" fullpath="${application.title}/${application.title}"/>
            <zipfileset file="resources/desktop-deployment/linux-x86/_package.cfg" fullpath="${application.title}/app/package.cfg"/>
            <zipfileset file="${dist.jar}" prefix="${application.title}/app"/>
            <zipfileset dir="${dist.dir}/lib" excludes="${deployment.jarexcludes}" prefix="${application.title}/app/lib"/>
            <zipfileset dir="${build.dir}/natives/linux-x86" prefix="${application.title}/app" erroronmissingdir="false"/>
        </zip>
        <delete file="resources/desktop-deployment/linux-x86/_package.cfg"/>
        <antcall target="-package-linux-x86-jre"/>
    </target>
    
    <target name="-package-linux-x86-jre" if="is.bundle.jre.enabled">
        <untar src="resources/desktop-deployment/jre-linux-x86.tar.gz" dest="build/jre" compression="gzip"/>
        <dirset dir="build/jre" id="dirId-linux-x86">
            <include name="jre*"/>
        </dirset>
        <property name= "dirName-linux-x86" refid= "dirId-linux-x86"/>
        <zip destfile="${dist.dir}/${application.title}-Linux-x86.zip" update="true">
            <zipfileset dir="build/jre/${dirName-linux-x86}" excludes ="bin/*" prefix="${application.title}/runtime/jre"/>
            <zipfileset dir="build/jre/${dirName-linux-x86}" includes ="bin/*" filemode="755" prefix="${application.title}/runtime/jre"/>
        </zip>
        <delete dir="build/jre"/>
    </target>
    
    <target name="-linux-x64-app" if="is.linux-x64.app.enabled">
        <echo>Linux 64bit Application Creation</echo>
        <copy file="resources/desktop-deployment/linux-x64/package.cfg" tofile="resources/desktop-deployment/linux-x64/_package.cfg">
            <filterchain>
                <replacestring from="$${main.class}" to="${main.class.launcher}"/>
                <replacestring from="$${launcher.jvmargs}" to="${launcher.jvmargs}"/>
                <replacestring from="$${application.title}" to="${application.title}"/>
                <replacestring from="$${jar.name}" to="${application.title}.jar"/>
            </filterchain>
        </copy>
        
        <zip destfile="${dist.dir}/${application.title}-Linux-x64.zip">
            <zipfileset file="resources/desktop-deployment/linux-x64/stub" filemode="755" fullpath="${application.title}/${application.title}"/>
            <zipfileset file="resources/desktop-deployment/linux-x64/_package.cfg" fullpath="${application.title}/app/package.cfg"/>
            <zipfileset file="${dist.jar}" prefix="${application.title}/app"/>
            <zipfileset dir="${dist.dir}/lib" excludes="${deployment.jarexcludes}" prefix="${application.title}/app/lib"/>
            <zipfileset dir="${build.dir}/natives/linux-x64" prefix="${application.title}/app" erroronmissingdir="false"/>
        </zip>
        <delete file="resources/desktop-deployment/linux-x64/_package.cfg"/>
        <antcall target="-package-linux-x64-jre"/>
    </target>
    
    <target name="-package-linux-x64-jre" if="is.bundle.jre.enabled">
        <untar src="resources/desktop-deployment/jre-linux-x64.tar.gz" dest="build/jre" compression="gzip"/>
        <dirset dir="build/jre" id="dirId-linux-x64">
            <include name="jre*"/>
        </dirset>
        <property name= "dirName-linux-x64" refid= "dirId-linux-x64"/>
        <zip destfile="${dist.dir}/${application.title}-Linux-x64.zip" update="true">
            <zipfileset dir="build/jre/${dirName-linux-x64}" excludes ="bin/*" prefix="${application.title}/runtime/jre"/>
            <zipfileset dir="build/jre/${dirName-linux-x64}" includes ="bin/*" filemode="755" prefix="${application.title}/runtime/jre"/>
        </zip>
        <delete dir="build/jre"/>
    </target>

    <target name="-macosx-x64-app" if="is.macosx-x64.app.enabled">
        <echo>MacOSX Application Creation</echo>
        <loadresource property="jfxdeploy.jvmargs">
            <propertyresource name="run.jvmargs"/>
            <filterchain>
                <tokenfilter>
                    <filetokenizer/>
                    <replacestring from=" " to="&lt;/string&gt;&lt;string&gt;"/>
                </tokenfilter>
            </filterchain>
        </loadresource>
        <antcall target="-update-macosx-x64-plist-with-runtime"/>
        <antcall target="-update-macosx-x64-plist-without-runtime"/>
        <zip destfile="${dist.dir}/${application.title}-MacOSX.zip">
            <zipfileset file="resources/desktop-deployment/macosx-x64/stub" filemode="755" fullpath="${application.title}.app/Contents/MacOS/JavaAppLauncher"/>
            <zipfileset file="resources/desktop-deployment/macosx-x64/_Info.plist" fullpath="${application.title}.app/Contents/Info.plist"/>
            <zipfileset file="resources/desktop-deployment/macosx-x64/icon.icns" fullpath="${application.title}.app/Contents/Resources/GenericApp.icns"/>
            <zipfileset file="${dist.jar}" prefix="${application.title}.app/Contents/Java"/>
            <zipfileset dir="${dist.dir}/lib" excludes="${deployment.jarexcludes}" prefix="${application.title}.app/Contents/Java/lib"/>
            <zipfileset dir="${build.dir}/natives/macosx-x64" prefix="${application.title}.app/Contents/Java" erroronmissingdir="false"/>
        </zip>
        <delete file="resources/desktop-deployment/macosx-x64/_Info.plist"/>
        <antcall target="-package-macosx-x64-jre"/>
    </target>
    
    <target name="-update-macosx-x64-plist-with-runtime" if="is.bundle.jre.enabled">
        <!--key>JVMRuntime</key>
        <string>jdk1.8.0_31.jdk</string-->
        <copy file="resources/desktop-deployment/macosx-x64/Info.plist" tofile="resources/desktop-deployment/macosx-x64/_Info.plist">
            <filterchain>
                <replacestring from="$${main.class}" to="${main.class}"/>
                <replacestring from="$${run.jvmargs}" to="${jfxdeploy.jvmargs}"/>
                <replacestring from="$${application.title}" to="${application.title}"/>
                <replacestring from="$${jfxdeploy.jvmargs}" to="-Xnoagent"/>
                <replacestring from="$${jar.name}" to="${application.title}.jar"/>
                <replacestring from="&lt;!--jvmruntime--&gt;" to="&lt;key&gt;JVMRuntime&lt;/key&gt;&#13;  &lt;string&gt;jre.jre&lt;/string&gt;"/>
            </filterchain>
        </copy>
    </target>
    
    <target name="-update-macosx-x64-plist-without-runtime" unless="is.bundle.jre.enabled">
        <copy file="resources/desktop-deployment/macosx-x64/Info.plist" tofile="resources/desktop-deployment/macosx-x64/_Info.plist">
            <filterchain>
                <replacestring from="$${main.class}" to="${main.class}"/>
                <replacestring from="$${run.jvmargs}" to="${jfxdeploy.jvmargs}"/>
                <replacestring from="$${application.title}" to="${application.title}"/>
                <replacestring from="$${jfxdeploy.jvmargs}" to="-Xnoagent"/>
                <replacestring from="$${jar.name}" to="${application.title}.jar"/>
            </filterchain>
        </copy>
    </target>
    
    <target name="-package-macosx-x64-jre" if="is.bundle.jre.enabled">
        <untar src="resources/desktop-deployment/jre-macosx-x64.tar.gz" dest="build/jre" compression="gzip"/>
        <dirset dir="build/jre" id="dirId-macosx-x64">
            <include name="jre*"/>
        </dirset>
        <property name= "dirName-macosx-x64" refid= "dirId-macosx-x64"/>
        <zip destfile="${dist.dir}/${application.title}-MacOSX.zip" update="true">
            <zipfileset dir="build/jre/${dirName-macosx-x64}/Contents/Home" excludes ="bin/*" prefix="${application.title}.app/Contents/PlugIns/jre.jre/Contents/Home/jre"/>
            <zipfileset dir="build/jre/${dirName-macosx-x64}/Contents/Home" includes ="bin/*" filemode="755" prefix="${application.title}.app/Contents/PlugIns/jre.jre/Contents/Home/jre"/>
            <zipfileset file="build/jre/${dirName-macosx-x64}/Contents/Info.plist" fullpath="${application.title}.app/Contents/PlugIns/jre.jre/Contents/Info.plist"/>
            <zipfileset dir="build/jre/${dirName-macosx-x64}/Contents/MacOS" prefix="${application.title}.app/Contents/PlugIns/jre.jre/Contents/MacOS"/>
        </zip>
        <delete dir="build/jre"/>
    </target>
    
    <target name="-test-platforms-enabled">
        <condition property="is.windows-x86.app.enabled">
            <istrue value="${windows-x86.app.enabled}"/>
        </condition>
        <condition property="is.windows-x64.app.enabled">
            <istrue value="${windows-x64.app.enabled}"/>
        </condition>
        <condition property="is.linux-x86.app.enabled">
            <istrue value="${linux-x86.app.enabled}"/>
        </condition>
        <condition property="is.linux-x64.app.enabled">
            <istrue value="${linux-x64.app.enabled}"/>
        </condition>
        <condition property="is.macosx-x64.app.enabled">
            <istrue value="${macosx-x64.app.enabled}"/>
        </condition>
        <condition property="is.bundle.jre.enabled">
            <istrue value="${bundle.jre.enabled}"/>
        </condition>
    </target>

    <target name="-create-launcher-jvmargs">
        <script language="javascript">
            <![CDATA[
            var args = project.getProperty("run.jvmargs");
            var res = args.split(" ");
            var out = "";
            for (var i = 1; i < res.length+1; i++) {
                out = out + "jvmarg." + i + "=" + res[i-1] + "\r\n";
            }
            project.setProperty("launcher.jvmargs", out);
            ]]>
        </script>
        <loadresource property="main.class.launcher">
            <propertyresource name="main.class"/>
            <filterchain>
                <tokenfilter>
                    <filetokenizer/>
                    <replacestring from="." to="/"/>
                </tokenfilter>
            </filterchain>
        </loadresource>
    </target>
    
    <target name="-extract-native-binaries">
        <java outputproperty="deployment.jarexcludes" dir="${basedir}" classname="com.jme3.system.ExtractNativeLibraries" fork="true" failonerror="false" classpath="${dist.jar}">
            <arg value="getjarexcludes"/>
        </java>        
        <java dir="${basedir}" classname="com.jme3.system.ExtractNativeLibraries" fork="true" failonerror="false" classpath="${dist.jar}">
            <arg value="Windows32"/>
            <arg value="${build.dir}/natives/windows-x86"/>
        </java>
        <java dir="${basedir}" classname="com.jme3.system.ExtractNativeLibraries" fork="true" failonerror="false" classpath="${dist.jar}">
            <arg value="Windows64"/>
            <arg value="${build.dir}/natives/windows-x64"/>
        </java>
        <java dir="${basedir}" classname="com.jme3.system.ExtractNativeLibraries" fork="true" failonerror="false" classpath="${dist.jar}">
            <arg value="Linux32"/>
            <arg value="${build.dir}/natives/linux-x86"/>
        </java>
        <java dir="${basedir}" classname="com.jme3.system.ExtractNativeLibraries" fork="true" failonerror="false" classpath="${dist.jar}">
            <arg value="Linux64"/>
            <arg value="${build.dir}/natives/linux-x64"/>
        </java>
        <java dir="${basedir}" classname="com.jme3.system.ExtractNativeLibraries" fork="true" failonerror="false" classpath="${dist.jar}">
            <arg value="MacOSX64"/>
            <arg value="${build.dir}/natives/macosx-x64"/>
        </java>
    </target>
</project>

#24

Right here. the property deployment.jarexcludes is set in calls to the classes

com.jme3.system.ExtractNativeLibraries &
com.jme3.system.NativeLibraryLoader.

Essentially, this entire build system assumes that Native libraries have two components:

  • An ‘API’ jar, which has all of the JNI *.class files, as well as any ‘ergonomics’ wrapper over them, e.g. jme3-bullet
  • A ‘native package’ jar, which includes the actual native libraries, including any JNI callback stubs, e.g. jme3-bullet-native

The ant task copies all native libraries to ${build.dir}/natives/$PLATFORM during the earlier build steps, and then excludes the .jar file from the deployment list during this step.

Your work-around works because the A1 jar is not listed in the places that check for native libraries. It’s therefore ignored, and just copied blindly with the rest of the /lib/*

I’m not sure if this assumption should be treated as a bug, or a (poorly documented) feature. If it’s a bug, there needs to be code to remove the native files from the jar before it is copied to the deployment location, to avoid wasted distribution bandwidth.


#25

Thank you @sailsman63 for digging into this.

In order to conform to the expectations/assumptions of desktop deployment, I could split the Minie library into multiple JARs. My only reluctance is because it would introduce yet another opportunity for version mismatches between JARs.