Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Language::Java::java_home_cmd for Linux #5333

Merged
merged 1 commit into from Nov 30, 2018

Conversation

sjackman
Copy link
Member

@sjackman sjackman commented Nov 20, 2018

/usr/libexec/java_home is specific to macOS.

  • Have you followed the guidelines in our Contributing document?
  • Have you checked to ensure there aren't other open Pull Requests for the same change?
  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your changes? Here's an example.
  • Have you successfully run brew style with your changes locally?
  • Have you successfully run brew tests with your changes locally?

@sjackman sjackman self-assigned this Nov 20, 2018
@sjackman sjackman added the bug Reproducible Homebrew/brew bug label Nov 20, 2018
@sjackman
Copy link
Member Author

Linuxbrew/brew has been using this fix since 2015-03-07.

Copy link
Member

@MikeMcQuaid MikeMcQuaid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this will work on macOS the generic implementations will be broken. Would be good to have a fallback to something that works e.g. using which "java" or the JavaRequirement. Could be a chance to generally consolidate some logic with the JavaRequirement.

version_flag = " --version #{version}" if version
"/usr/libexec/java_home#{version_flag}"
end
def self.java_home_cmd(_version = nil); end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is a correct implementation. See the uses of this in Homebrew/homebrew-core; they won't work after this. It would be good to use something here that makes sense there.


def self.java_home_env(version = nil)
{ JAVA_HOME: "$(#{java_home_cmd(version)})" }
def self.java_home_env(_version = nil)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is a correct implementation. See the uses of this in Homebrew/homebrew-core; they won't work after this. It would be good to use something here that makes sense there.

end

def self.overridable_java_home_env(version = nil)
{ JAVA_HOME: "${JAVA_HOME:-$(#{java_home_cmd(version)})}" }
def self.overridable_java_home_env(_version = nil)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is a correct implementation. See the uses of this in Homebrew/homebrew-core; they won't work after this. It would be good to use something here that makes sense there.

@sjackman sjackman force-pushed the java-home branch 2 times, most recently from a441821 to 0a36c3e Compare November 26, 2018 19:41
@sjackman sjackman changed the title Do not use /usr/libexec/java_home on Linux Fix Language::Java::java_home_cmd for Linux Nov 26, 2018
@@ -2,7 +2,7 @@ module Language
module Java
def self.java_home_cmd(version = nil)
version_flag = " --version #{version}" if version
"/usr/libexec/java_home#{version_flag}"
"#{HOMEBREW_BREW_FILE} java-home#{version_flag}"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A new documented command seems pretty overkill? Could perhaps do something like raise NotImplementedError on the generic/non-Mac implementation? Is this used in Linuxbrew formulae and needed to work?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

write_jar_script uses Language::Java.java_home_cmd, so for that reason, it does need to work. Making it an option of an existing command doesn't seem as clean to me as a new command.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What was the existing Linuxbrew implementation of this? We make write_jar_script use a more generically named function which used java_home_cmd only on macOS.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing implementation of java_home_cmd returns nil, and the existing implementations of java_home_env and overridable_java_home_env return {}, which was the first version of this PR that I submitted. When JAVA_HOME is not defined, most tools will use the java interpreter found in PATH, which, when there is only a single Java interpreter installed, is good enough.

One place when I've seen this not work is when building a native JNI library, and the build script expects JAVA_HOME to be defined to find $JAVA_HOME/include.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The existing implementation of java_home_cmd returns nil, and the existing implementations of java_home_env and overridable_java_home_env return {}, which was the first version of this PR that I submitted.

I don't think those versions should be empty no-ops but I think a new command is a bit overkill in the opposite direction. The original implementation with raise NotImplementedError scattered around where appropriate feels like it may be a good call.

@sjackman sjackman force-pushed the java-home branch 2 times, most recently from 623cbb0 to fa6c85d Compare November 26, 2018 23:06
@sjackman
Copy link
Member Author

The shell script created by write_jar_script looks like this:

#!/bin/bash
JAVA_HOME="$(/usr/libexec/java_home --version 1.8)" exec "/usr/local/Cellar/igv/2.4.14/libexec/igv.sh" "$@"

rather than being hard coded like for example this:

JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_73.jdk/Contents/Home exec "/usr/local/Cellar/igv/2.4.14/libexec/igv.sh" "$@"

The former is better, because the latter would break when the Java interpreter is upgraded.

I feel that providing a script that is functionally equivalent to macOS's /usr/libexec/java_home is the most straight forward approach to achieving this same functionality on Linux.

@MikeMcQuaid
Copy link
Member

I feel that providing a script that is functionally equivalent to macOS's /usr/libexec/java_home is the most straight forward approach to achieving this same functionality on Linux.

I don't like the idea of having a new Homebrew command that's only relevant to make Linux formulae behave a bit more like Homebrew's. There must be a way that e.g. Debian and Fedora already handle something similar.

@sjackman
Copy link
Member Author

This PR implements equivalent functionality to macOS, which I think is the best possible scenario. If your objection is that brew java-home is user-facing and documented, we can find a way to hide this tool so that it's internal to Homebrew.

@MikeMcQuaid
Copy link
Member

@sjackman My objection is a few-fold:

  • we're emulating an existing macOS utility for Linuxbrew. Our implementation will not be 1:1 compatible without extensive testing (both manual and automated).
  • we're making applications dependent at runtime on executing Homebrew itself. If there's issues with Homebrew's Ruby code at that time: these applications will not run.
  • we're reinventing the wheel for a problem for which Linux package managers already have solutions in order to avoid having to change formulae on Linux
  • this is new code that's not been used or tested on Linuxbrew already

@sjackman sjackman force-pushed the java-home branch 2 times, most recently from cff9ee4 to 4467f4d Compare November 27, 2018 20:28
@sjackman
Copy link
Member Author

sjackman commented Nov 27, 2018

I've pushed take three of this PR.
Language::Java::java_home_cmd is not implemented on Linux and raises NotImplementedError.
Language::Java::java_home uses java_home_cmd on macOS and JavaRequirement.java_home on Linux.
Language::Java::java_home_env uses Language::Java::java_home.

Copy link
Member

@MikeMcQuaid MikeMcQuaid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm liking this approach 👍. Sorry for all the back and forth.

raise NotImplementedError
end

def self.java_home(version = nil)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you see this being a new method that's called by formulae? If not, could move it somewhere else less public or add a @private at least.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

9/14 uses of java_home_cmd look like…

    cmd = Language::Java.java_home_cmd("1.8")
    ENV["JAVA_HOME"] = Utils.popen_read(cmd).chomp

A function that immediately evaluates to java_home (like the Linux implementation) seems useful.

    ENV["JAVA_HOME"] = Language::Java.java_home("1.8")

That doesn't however work with the current macOS implementation of java_home, which evaluates to the string "$(/usr/libexec/java_home …)", and not the actual home.

I'll take a stab at making a useful java_home function and hiding the private one that possibly evaluates to $(…) on macOS.

Copy link
Member Author

@sjackman sjackman Nov 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On second thought, it's not worth creating a new function for just 9 uses in Homebrew/core. I'll make java_home private.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm game for it being public if it'd be used for that. I think 9 uses is sufficient 👍

Copy link
Member Author

@sjackman sjackman Nov 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm game for implementing it, but the current implementation is not sufficient. On macOS,

ENV["JAVA_HOME"] = Language::Java.java_home("1.8")

would evaluate to

ENV["JAVA_HOME"] = "$(/usr/libexec/java_home --version 1.8)"

which is incorrect. It should evaluate to

ENV["JAVA_HOME"] = "/Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm game for it being public if it'd be used for that. I think 9 uses is sufficient 👍

Language::Java.java_home is now implemented, public, and tested.

@@ -1,7 +1,7 @@
require "language/java"

describe Language::Java do
describe "::java_home_env" do
describe "::java_home_env", :needs_macos do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should work on Linux, no?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current test which searches for the version string (e.g. "1.8+") in java_home does not work. I can modify the test.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, the test also fails unless java is installed. I'll change it to :needs_java.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please 👍. Tests for any new public functions would be good too.

Copy link
Member Author

@sjackman sjackman Nov 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. No, :needs_macos is more correct than :needs_java.

java_home = described_class.java_home_env("blah")
expect(java_home[:JAVA_HOME]).to include("--version blah")

On macOS this test is fine, but on Linux it fails unless Java version blah is installed, because on Linux it uses JavaRequirement to check that that particular version of Java is installed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sjackman Fine with me as long as we have new tests for each of the generic cases that are implemented.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a test for the new Language::Java.java_home method.

@@ -13,7 +13,7 @@
end
end

describe "::overridable_java_home_env" do
describe "::overridable_java_home_env", :needs_macos do
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should work on Linux, no?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto.

@sjackman sjackman force-pushed the java-home branch 3 times, most recently from d241cea to a7a68f0 Compare November 29, 2018 17:33
@sjackman
Copy link
Member Author

@MikeMcQuaid Thanks for the feedback. Good to merge?

version_flag = " --version #{version}" if version
"/usr/libexec/java_home#{version_flag}"
def self.java_home_cmd(_version = nil)
raise NotImplementedError
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A comment might be useful to explain why this is the case.

def self.java_home_cmd(version = nil)
version_flag = " --version #{version}" if version
"/usr/libexec/java_home#{version_flag}"
def self.java_home_cmd(_version = nil)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_ = nil

@@ -85,6 +85,10 @@
skip "Not on macOS." unless OS.mac?
end

config.before(:each, :needs_java) do
skip "Java not installed." unless which("java")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will behave oddly on macOS with the java shim but Java not being installed. Might want to use similar logic to the JavaRequirement.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Fixed.

/usr/libexec/java_home is specific to macOS.
Language::Java::java_home_cmd is not implemented on Linux and raises
NotImplementedError.

Add private Language::Java::java_home_shell and use it instead of java_home_cmd.
Add public Language::Java::java_home for use by formulae.
Copy link
Member

@MikeMcQuaid MikeMcQuaid left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One final comment: feel free to address or merge as-is, don't feel strongly. Thanks for bearing with me here and sorry for all the back and forth. We're getting close 😍

config.before(:each, :needs_java) do
java_installed = if OS.mac?
Utils.popen_read("/usr/libexec/java_home", "--failfast")
$CHILD_STATUS.success?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason to not use system here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used Utils.popen_read so that it has the standard behaviour of silencing stderr by default and showing it when HOMEBREW_STDERR is enabled.

@sjackman sjackman merged commit c79deae into Homebrew:master Nov 30, 2018
@sjackman sjackman deleted the java-home branch November 30, 2018 16:10
@sjackman
Copy link
Member Author

One final comment: feel free to address or merge as-is, don't feel strongly. Thanks for bearing with me here and sorry for all the back and forth. We're getting close 😍

Thanks for the review, Mike. Merged!

@lock lock bot added the outdated PR was locked due to age label Dec 30, 2018
@lock lock bot locked as resolved and limited conversation to collaborators Dec 30, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Reproducible Homebrew/brew bug outdated PR was locked due to age
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants