Search code examples
classpathjavacbootclasspath

Unable to compile unless .jar is included in bootclasspath


I'm trying to build an android project with gradle: it's a library project with ndk flavor, spiced with an external .jar, courtesy of Unity.

I don't really think that the details about the gradle file are important, suffices to say that I'm including the external jar file (provided by Unity) with something like:

  dependencies {
    compile files('libs/unity.jar')
  }

which actually seems to work, as during the java compilation, gradle issues a command like:

BASE="<Some directory>"
javac -d "${BASE}/PluginsSources/Android/libproject/build/intermediates/classes/fat/debug" \
    -g -encoding UTF-8 \
    -bootclasspath /Users/myusername/Applications/adt/sdk/platforms/android-23/android.jar \
    -sourcepath "${BASE}/PluginsSources/Android/libproject/build/tmp/compileFatDebugJavaWithJavac/emptySourcePathRef" \
    -classpath "${BASE}/PluginsSources/Android/libproject/libs/unity.jar" \
    "${BASE}/PluginsSources/Android/libproject/src/main/java/org/example/ScriptBridge/JavaClass.java" \
    "${BASE}/PluginsSources/Android/libproject/build/generated/source/r/fat/debug/org/example/ScriptBridge/R.java" \
    "${BASE}/PluginsSources/Android/libproject/build/generated/source/buildConfig/fat/debug/org/example/ScriptBridge/BuildConfig.java" \
    -XDuseUnsharedTable=true

indentation and BASE variable added by yours truly. Note that unity.jar is a classpath entry.

Problem is, the compilation fails as javac is apparently unable to find a class in unity.jar when compiling, unless that jar is put into the -bootclasspath option (!!).

Why does this happen? AFAIK, the search path for the classes should be hierarchic and after bootclasspath, javac should be searching classpath...

Is it possible to tell gradle to stick some jar into the bootclasspath somhow (this would clumsily solve the issue)?

Is it there a cleaner/better way?

Thank you for any insight!


Solution

  • EDIT:

    After a closer scrutiny, it looks like that classpath works (surprise!) and Unity Technologies didn't create a mutant-classpath-resilient jar as it's first step to take over the world, after all...

    The problem was in the path to the compile statement, after replacing it with the correct relative path to the jar, everything worked as expected.

    The answers to the question's point are:

    Q1: Why does this happen?

    Because java cannot find a class in a jar that doesn't exist

    Q2: Is it possible to tell gradle to stick some jar into the bootclasspath somhow?

    See the answer below

    Q3: is there a cleaner/better way?

    Yeah: making sure that the path to whatever you are linking is correct might be a good start :)

    Note that gradle will not issue any error for missing jars, not even in debug mode (which is an infamy IMHO).


    OLD ANSWER

    I got to solve the issue myself, somehow (I don't think that my workaround is optimal, but given the attention on this question I guess it's as good as it's gonna get...)

    Q1: Why does this happen?

    There is no flipping reason, as far as I understand.

    The document:

    How Classes are Found (Oracle canon)

    clearly specifies that the classes in bootstrap are:

    Bootstrap classes are the classes that implement the Java 2 Platform.

    that is, the classes used to implement the platform. That's why android.jar is there, and unity.jar should not.

    Q2: Is it possible to tell gradle to stick some jar into the bootclasspath somhow?

    Yes! The following link details how:

    Modify bootclasspath in compiler args (gradle forums)

    and that is:

    allprojects {
      gradle.projectsEvaluated {
        tasks.withType(JavaCompile) {
          options.compilerArgs << "Xbootclasspath/a:src/main/libs/unity.jar"
        }
      }
    }
    

    which, in my projects maps to loading a unity.jar archive after the android one, located in the src/main/libs directory of my android library.

    Q3: is there a cleaner/better way?

    I don't think so, at least not without understanding why the jar needs to be in bootstrap in the first place (which would make most of the issues here pointless): changing the bootstrap classpath is not something the high level programmer should do, so I don't expect gradle to provide a cleaner shortcut.

    I'm still waiting for someone with some insights about the issue (I can't sink more of my time on this), but I hope this will help someone anyway.