google / j2objc

A Java to iOS Objective-C translation tool and runtime.
http://j2objc.org
Apache License 2.0
6k stars 972 forks source link

J2ObjC translation failing with Java 11 #1090

Closed adil-hussain-84 closed 5 years ago

adil-hussain-84 commented 5 years ago

I've been using J2ObjC with Java 1.8 for some time now. I upgraded the version of Java on my development machine to Java 12 today but then encountered an error when running the j2objc command as follows:

JDK not supported. Please set JAVA_HOME to JDK 1.8 or 11.

I managed to find this error message in the source code here and the names of the supported Java versions here. So I proceeded to downgrade my Java version to Java 11 and then retried the j2objc command. This time I got past the Java version pre-check but I then encountered an exception as follows:

Exception in thread "main" java.lang.IllegalArgumentException: /Users/adilhussain/Library/J2ObjC/j2objc-2.5/lib/jre_emul_module
    at jdk.compiler/com.sun.tools.javac.file.Locations$SystemModulesLocationHandler.isCurrentPlatform(Locations.java:1862)
    at jdk.compiler/com.sun.tools.javac.file.Locations$SystemModulesLocationHandler.update(Locations.java:1851)
    at jdk.compiler/com.sun.tools.javac.file.Locations$SystemModulesLocationHandler.handleOption(Locations.java:1798)
    at jdk.compiler/com.sun.tools.javac.file.Locations.handleOption(Locations.java:2062)
    at jdk.compiler/com.sun.tools.javac.file.BaseFileManager.handleOption(BaseFileManager.java:269)
    at jdk.compiler/com.sun.tools.javac.file.BaseFileManager$2.handleFileManagerOption(BaseFileManager.java:222)
    at jdk.compiler/com.sun.tools.javac.main.Option.process(Option.java:1138)
    at jdk.compiler/com.sun.tools.javac.main.Option.handleOption(Option.java:1086)
    at jdk.compiler/com.sun.tools.javac.file.BaseFileManager.handleOption(BaseFileManager.java:232)
    at jdk.compiler/com.sun.tools.javac.main.Arguments.doProcessArgs(Arguments.java:390)
    at jdk.compiler/com.sun.tools.javac.main.Arguments.processArgs(Arguments.java:347)
    at jdk.compiler/com.sun.tools.javac.main.Arguments.init(Arguments.java:246)
    at jdk.compiler/com.sun.tools.javac.api.JavacTool.getTask(JavacTool.java:185)
    at jdk.compiler/com.sun.tools.javac.api.JavacTool.getTask(JavacTool.java:119)
    at jdk.compiler/com.sun.tools.javac.api.JavacTool.getTask(JavacTool.java:68)
    at com.google.devtools.j2objc.javac.JavacParser.createEnvironment(JavacParser.java:264)
    at com.google.devtools.j2objc.javac.JavacParser.parseFiles(JavacParser.java:193)
    at com.google.devtools.j2objc.pipeline.FileProcessor.processBatch(FileProcessor.java:137)
    at com.google.devtools.j2objc.pipeline.FileProcessor.processInputs(FileProcessor.java:67)
    at com.google.devtools.j2objc.pipeline.TranslationProcessor.processInputs(TranslationProcessor.java:82)
    at com.google.devtools.j2objc.J2ObjC.run(J2ObjC.java:127)
    at com.google.devtools.j2objc.J2ObjC.main(J2ObjC.java:176)
Caused by: java.nio.file.NoSuchFileException: /Users/adilhussain/Library/J2ObjC/j2objc-2.5/lib/jre_emul_module
    at java.base/sun.nio.fs.UnixException.translateToIOException(UnixException.java:92)
    at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:111)
    at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:116)
    at java.base/sun.nio.fs.UnixFileSystemProvider.isSameFile(UnixFileSystemProvider.java:333)
    at java.base/java.nio.file.Files.isSameFile(Files.java:1530)
    at jdk.compiler/com.sun.tools.javac.file.Locations$SystemModulesLocationHandler.isCurrentPlatform(Locations.java:1860)
    ... 21 more

I can't make sense of the exception message but I did notice that there is no jre_emul_module file in the lib folder of any of the recent versions of J2ObjC (see line 1 of the exception message above). I'm not sure if it's the cause of the issue but it looks like the path to this jre_emul_module file is being added to the J2OBJC_ARGS variable here if the version of Java on the machine is not Java 1.8.

I should mention that this exception is thrown even when trying to translate the most trivial of Java files.

tomball commented 5 years ago

Thanks for filing this issue, we should be able to address it quickly. It looks like the 2.5 release was built with Java 8, so the module wasn't generated.

FWIW, you don't have to "downgrade" to Java 11, just have either it or Java 8 installed on your system. On Macs, the "/usr/libexec/java_home -V" lists all installed JVMs, and the j2objc.sh script uses that command to find a runtime it can use.

"java_home -v" (lowercase 'v') gives you the path for a specific JVM location; for example (on my system):

$ /usr/libexec/java_home -v 10
/Library/Java/JavaVirtualMachines/jdk-10-latest/Contents/Home

So "java_home -v" can be used to set a default JVM version in your ~/.bash_profile. Use this command to set Java 12 as your default while still having Java 11, 1.8, etc., installed as well:

export JAVA_HOME=$(/usr/libexec/java_home -v 12)

I use Johan Haleby's excellent setjdk() bash function, which makes it trivial to switch versions. In fact, I'll be using it today to investigate this issue!

adil-hussain-84 commented 5 years ago

Hey @tomball, just to point out that the build of J2ObjC that I was using was one built on my machine before I upgraded to Java 11. I'll try rebuilding J2ObjC from the sources with Java 11 and will let you know how I get on with that.

adil-hussain-84 commented 5 years ago

@tomball: So that worked. I rebuilt J2ObjC from the sources with Java 11 and then ran the j2objc command successfully. And thanks for the tip on managing Java versions. I'll try that out.

tomball commented 5 years ago

That's great you're building from source, as we recently upgraded our repository syncing tool so that changes committed internally are updated on GitHub within minutes.

What threw me here was the version number you chose, as "j2objc-2.5" is the last release version. Consider using versions like "adil-j2objc-2.5" or "myemployer-j2objc-2.5", so that it's obvious in stack traces where to start looking.

adil-hussain-84 commented 5 years ago

Hi @tomball, I just tried your suggestion of going back to Java 12 on my machine (by setting the JAVA_HOME environment variable) and I kept Java 11 installed and Java 8 uninstalled so that the result of the /usr/libexec/java_home -V call was the following:

Matching Java Virtual Machines (2):
    12.0.2, x86_64: "OpenJDK 12.0.2"    /Library/Java/JavaVirtualMachines/openjdk-12.0.2.jdk/Contents/Home
    11.0.2, x86_64: "OpenJDK 11.0.2"    /Library/Java/JavaVirtualMachines/openjdk-11.0.2.jdk/Contents/Home

/Library/Java/JavaVirtualMachines/openjdk-12.0.2.jdk/Contents/Home

I then tried the following two experiments:

  1. Building J2ObjC from the source code that was in the last release (2.5).
  2. Trying to run the j2objc command with a version of J2ObjC that was built with Java 11.

Both experiments failed with a "JDK not supported. Please set JAVA_HOME to JDK 1.8 or 11" message.

I had a quick scan of the j2objc.sh file and it looks like the JAVA variable is being initialised as follows:

JAVA_HOME=`/usr/libexec/java_home -v 1.8`
JAVA=${JAVA_HOME}/bin/java

And then later the ${JAVA} -version command is being executed to see if it contains either the "build 1.8" or the "build 11" string as part of initialising the JAVA_VERSION variable.

I might be misunderstanding something here but I think this logic needs to change slightly so that the /usr/libexec/java_home -v {$version} call happens within the for loop that iterates through the supported versions and checking one by one that the exit status of the call (i.e. $?) is 0. Rather than calling /usr/libexec/java_home -v ahead of time with a fixed value of 1.8.

If that makes sense then it looks like there needs to be a similar change to how the JAVA_HOME variable is initialised in the common.mk file.

Let me know if this makes sense and I'm not missing something important and, if so, I can play around with this change later in the week.

tomball commented 5 years ago

That script actually looks like this:

  # java_home is available on all Mac systems.
  if [ -z "${JAVA_HOME}" ]; then
    readonly JAVA_HOME=`/usr/libexec/java_home -v 1.8 2> /dev/null`
  fi
  readonly JAVA=${JAVA_HOME}/bin/java

So you're right that the default is Java 8, but all you need to do to change that is set the JAVA_HOME environment variable in your shell. Just add this line to your ~/.bash_profile:

export JAVA_HOME=`/usr/libexec/java_home -v 11`

And reload the environment variables:

source ~/.bash_profile

As far as build changes, Java 8 is still required to be installed to build the j2objc distribution bundle. That's because we still support Java 8 use as many Android developers still use it.

antonio-cortes-perez commented 5 years ago

A few more notes. As you can observe in: https://github.com/google/j2objc/blob/master/scripts/build_distribution.sh

J2ObjC needs to be build with Java 8 because that's the minimum supported version. To support post-Java 8 features, only the jre_emul_module is built using Java 11. As Tom mentioned, to transpile code that uses post-Java 8 features, you need to override JAVA_HOME.

This was announced in the 2.4 release: https://groups.google.com/forum/#!topic/j2objc-discuss/BmDcAIvaTFs

adil-hussain-84 commented 5 years ago

Thanks for the support @tomball and @antonio-cortes-perez. I appreciate it.

@tomball: My last comment was an experiment I did on the back of your comment (a few comments up) that the j2objc.sh script uses the "/usr/libexec/java_home -V" command (uppercase V) to find a runtime it can use. I was digging around inside of j2objc.sh and it looks like it doesn't behave like this.

@antonio-cortes-perez: I am actually transpiling Java 8 source code with J2ObjC and not post-Java 8 features (since the code needs to run on Android devices). It's just that I had got a new Mac and installed the latest version of the JDK via Homebrew and I was curious to see whether J2ObjC would still work. It might be worth updating the requirements list here and here to say "JDK 1.8 or 11" instead of "JDK 1.8 or higher".

antonio-cortes-perez commented 5 years ago

Thanks for the feedback. I'll update the documentation.

On Mon, Sep 30, 2019 at 5:29 AM Adil Hussain notifications@github.com wrote:

Thanks for the support @tomball https://github.com/tomball and @antonio-cortes-perez https://github.com/antonio-cortes-perez. I appreciate it.

@tomball https://github.com/tomball: My last comment was an experiment I did on the back of your comment (a few comments up) that the j2objc.sh script uses the "/usr/libexec/java_home -V" command (uppercase V) to find a runtime it can use. I was digging around inside of j2objc.sh and it looks like it doesn't behave like this.

@antonio-cortes-perez https://github.com/antonio-cortes-perez: I am actually transpiling Java 8 source code with J2ObjC and not post-Java 8 features. It's just that I had got a new Mac and installed the latest version of the JDK via Homebrew and I was curious to see whether J2ObjC would still work. It might be worth updating the requirements list here https://github.com/google/j2objc and here https://developers.google.com/j2objc/ to say "JDK 1.8 or 11" instead of "JDK 1.8 or higher".

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/google/j2objc/issues/1090?email_source=notifications&email_token=AGYWXVOUOTPDBAT525NPL3DQMHWMDA5CNFSM4IYMDWZKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD75O6WI#issuecomment-536538969, or mute the thread https://github.com/notifications/unsubscribe-auth/AGYWXVO3B46GR2S3MTDCATTQMHWMDANCNFSM4IYMDWZA .

Ben1981 commented 4 years ago

Just to save someones else time, no matter what path you choose to define the JAVA_HOME, REMEMBER TO RESTART XCODE afterwards :)