# HG changeset patch
# User Andrew Janke <andrew@apjanke.net>
# Date 1547259866 18000
#      Fri Jan 11 21:24:26 2019 -0500
# Branch dynamic-jvm-detection-2
# Node ID ab8df2bd404e984c9ea70828c7ca740c3c67f128
# Parent  10f648516d5b83e9001654329cde7fa422df6c95
Add dynamic detection of JVM on macOS

diff --git a/libinterp/corefcn/module.mk b/libinterp/corefcn/module.mk
--- a/libinterp/corefcn/module.mk
+++ b/libinterp/corefcn/module.mk
@@ -63,16 +63,17 @@ COREFCN_INC = \
   %reldir%/oct-handle.h \
   %reldir%/oct-hdf5-types.h \
   %reldir%/oct-hist.h \
   %reldir%/oct-iostrm.h \
   %reldir%/oct-map.h \
   %reldir%/oct-obj.h \
   %reldir%/oct-prcstrm.h \
   %reldir%/oct-procbuf.h \
+  %reldir%/oct-process.h \
   %reldir%/oct-stdstrm.h \
   %reldir%/oct-stream.h \
   %reldir%/oct-strstrm.h \
   %reldir%/oct.h \
   %reldir%/octave-default-image.h \
   %reldir%/octave-link.h \
   %reldir%/pager.h \
   %reldir%/pr-flt-fmt.h \
@@ -200,16 +201,17 @@ COREFCN_SRC = \
   %reldir%/nproc.cc \
   %reldir%/oct-fstrm.cc \
   %reldir%/oct-hdf5-types.cc \
   %reldir%/oct-hist.cc \
   %reldir%/oct-iostrm.cc \
   %reldir%/oct-map.cc \
   %reldir%/oct-prcstrm.cc \
   %reldir%/oct-procbuf.cc \
+  %reldir%/oct-process.cc \
   %reldir%/oct-stream.cc \
   %reldir%/oct-strstrm.cc \
   %reldir%/oct-tex-lexer.ll \
   %reldir%/oct-tex-parser.h \
   %reldir%/oct-tex-parser.yy \
   %reldir%/octave-link.cc \
   %reldir%/ordschur.cc \
   %reldir%/pager.cc \
diff --git a/libinterp/corefcn/oct-process.cc b/libinterp/corefcn/oct-process.cc
new file mode 100644
--- /dev/null
+++ b/libinterp/corefcn/oct-process.cc
@@ -0,0 +1,83 @@
+/*
+
+Copyright (C) 2019 Andrew Janke
+
+This file is part of Octave.
+
+Octave is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Octave is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Octave; see the file COPYING.  If not, see
+<https://www.gnu.org/licenses/>.
+
+*/
+
+#include <iostream>
+#include <sstream>
+
+#include "oct-process.h"
+#include "procstream.h"
+#include "sysdep.h"
+#include "oct-syscalls.h"
+
+OCTINTERP_API
+process_execution_result
+process_execution_result::of_success (int exit_status, std::string stdout_output)
+{
+  return process_execution_result (0, exit_status, stdout_output, "");
+}
+
+OCTINTERP_API
+process_execution_result
+process_execution_result::of_error (int status, std::string err_msg)
+{
+  return process_execution_result (status, -1, "", err_msg);      
+}
+
+// Execute a shell command, returning results as a C++ object
+OCTINTERP_API
+process_execution_result
+run_command_and_return_output (const std::string& cmd_str)
+{
+  iprocstream cmd (cmd_str.c_str ());
+  if (! cmd)
+    return process_execution_result::of_error (-1, std::string ("unable to start subprocess for '")
+      + cmd_str + "'");
+
+  std::ostringstream output_buf;
+
+  char ch;
+
+  for (;;)
+    {
+      if (cmd.get (ch))
+        output_buf.put (ch);
+      else
+        {
+          if (! cmd.eof () && errno == EAGAIN)
+            {
+              cmd.clear ();
+            }
+          else
+            break;
+        }
+    }
+
+  int cmd_status = cmd.close ();
+
+  if (octave::sys::wifexited (cmd_status))
+    cmd_status = octave::sys::wexitstatus (cmd_status);
+  else
+    cmd_status = 127;
+
+  return process_execution_result::of_success (cmd_status, output_buf.str ());
+}
+
diff --git a/libinterp/corefcn/oct-process.h b/libinterp/corefcn/oct-process.h
new file mode 100644
--- /dev/null
+++ b/libinterp/corefcn/oct-process.h
@@ -0,0 +1,79 @@
+/*
+
+Copyright (C) 2019 Andrew Janke
+
+This file is part of Octave.
+
+Octave is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Octave is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Octave; see the file COPYING.  If not, see
+<https://www.gnu.org/licenses/>.
+
+*/
+
+#if ! defined (oct_process_h)
+#define oct_process_h 1
+
+#include "octave-config.h"
+
+#include <string>
+#include <sys/types.h>
+
+OCTINTERP_API
+class
+process_execution_result
+{
+public:
+
+  process_execution_result ()
+    : m_status(-1), m_err_msg(""), m_exit_status(-1), m_stdout_output("") { }
+
+  process_execution_result (int status, int exit_status, std::string stdout_output, std::string err_msg)
+    : m_status(status), m_err_msg(err_msg), m_exit_status(exit_status), m_stdout_output(stdout_output)
+       { }
+
+  static
+  process_execution_result
+  of_success(int exit_status, std::string stdout_output);
+
+  static
+  process_execution_result
+  of_error(int status, std::string err_msg);
+
+  int status (void) const { return m_status; }
+
+  int exit_status (void) const { return m_exit_status; }
+
+  std::string err_msg (void) const { return m_err_msg; }
+
+  std::string stdout_output (void) const { return m_stdout_output; }
+
+private:
+
+  // launch status of the process, 0 for success, nonzero for error
+  int m_status;
+
+  // error message if executing command failed
+  std::string m_err_msg;
+
+  // exit status of the process
+  int m_exit_status;
+
+  // collected stdout output of the process
+  std::string m_stdout_output;
+
+};
+
+extern OCTINTERP_API process_execution_result
+run_command_and_return_output (const std::string& cmd_str);
+
+#endif
\ No newline at end of file
diff --git a/libinterp/octave-value/ov-java.cc b/libinterp/octave-value/ov-java.cc
--- a/libinterp/octave-value/ov-java.cc
+++ b/libinterp/octave-value/ov-java.cc
@@ -51,31 +51,33 @@ along with Octave; see the file COPYING.
 #include "file-ops.h"
 #include "file-stat.h"
 #include "fpucw-wrappers.h"
 #include "interpreter.h"
 #include "interpreter-private.h"
 #include "load-path.h"
 #include "lo-sysdep.h"
 #include "oct-env.h"
+#include "oct-process.h"
 #include "oct-shlib.h"
 #include "ov-java.h"
 #include "parse.h"
 #include "variables.h"
 
 #if defined (HAVE_JAVA)
 #include <jni.h>
 #endif
 
 #if defined (HAVE_JAVA)
 
 #if defined (OCTAVE_USE_WINDOWS_API)
 #  define LIBJVM_FILE_NAME "jvm.dll"
 #elif defined (__APPLE__)
 #  define LIBJVM_FILE_NAME "libjvm.dylib"
+#  define JAVA_HOME_CMD "/usr/libexec/java_home"
 #else
 #  define LIBJVM_FILE_NAME "libjvm.so"
 #endif
 
 #define TO_JOBJECT(obj) reinterpret_cast<jobject> (obj)
 #define TO_JCLASS(obj) reinterpret_cast<jclass> (obj)
 
 #define TO_JNIENV(env) reinterpret_cast<JNIEnv *> (env)
@@ -589,16 +591,17 @@ get_jvm_lib_path_from_registry ()
   if (jvm_lib_path.empty ())
     error ("unable to find Java Runtime Environment: %s::%s",
            key.c_str (), value.c_str ());
 
   return jvm_lib_path;
 }
 #endif
 
+
 //! Initialize the java virtual machine (jvm) and field #jvm if necessary.
 //!
 //! If the jvm exists and is initialized, #jvm points to it, i.e. is not 0
 //! and there is nothing to do.
 //!
 //! If #jvm is 0 and if at least one jvm exists, attach the current thread to
 //! it by setting #jvm_attached.  Otherwise, create a #jvm with some hard-
 //! coded options:
@@ -627,38 +630,60 @@ initialize_jvm (void)
   if (jvm)
     return;
 
   JNIEnv *current_env;
   const char *static_locale = setlocale (LC_ALL, nullptr);
   const std::string locale (static_locale);
 
   octave::dynamic_library lib ("");
-  std::string jvm_lib_path = "linked in or loaded libraries";
+  std::string jvm_lib_path = "";
 
   // Check whether the Java VM library is already loaded or linked in.
   JNI_CreateJavaVM_t create_vm = reinterpret_cast<JNI_CreateJavaVM_t>
                                  (lib.search ("JNI_CreateJavaVM"));
   JNI_GetCreatedJavaVMs_t get_vm = reinterpret_cast<JNI_GetCreatedJavaVMs_t>
                                    (lib.search ("JNI_GetCreatedJavaVMs"));
 
-  if (! create_vm || ! get_vm)
+  if (create_vm && get_vm)
+    jvm_lib_path = "linked in or loaded libraries";
+  else
     {
       // JAVA_HOME environment variable takes precedence
       std::string java_home_env = octave::sys::env::getenv ("JAVA_HOME");
       if (! java_home_env.empty ())
         {
           jvm_lib_path = get_jvm_lib_path_in_subdir (java_home_env);
 
           // If JAVA_HOME does not look like a Java directory, use it anyway
           // to fail with a useful error message indicating the directory
           if (jvm_lib_path.empty ())
             jvm_lib_path = java_home_env + "/" LIBJVM_FILE_NAME;
         }
-      else
+#  if defined (__APPLE__)
+      // Use standard /usr/libexec/java_home if available
+      if (jvm_lib_path.empty ())
+        {
+          octave::sys::file_stat libexec_java_home_exists (JAVA_HOME_CMD);
+          if (libexec_java_home_exists)
+            {
+              process_execution_result rslt = run_command_and_return_output (
+                  JAVA_HOME_CMD " --failfast --version 1.6+ 2>/dev/null");
+              if (rslt.exit_status () == 0)
+                {
+                  std::string output = rslt.stdout_output ();
+                  std::string found_path = output.substr (0, output.length() - 1);
+                  std::string jvm_lib_found = get_jvm_lib_path_in_subdir (found_path);
+                  if (!jvm_lib_found.empty ())
+                    jvm_lib_path = jvm_lib_found;
+                }
+            }
+        }
+#  endif
+      if (jvm_lib_path.empty ())
         {
 #if defined (OCTAVE_USE_WINDOWS_API)
           jvm_lib_path = get_jvm_lib_path_from_registry ();
 #else
           // Fall back to JAVA_LDPATH, determined by the build system
           jvm_lib_path = std::string (JAVA_LDPATH) + "/" LIBJVM_FILE_NAME;
 #endif
         }
