#!/usr/bin/env python # ---------------------------------------------------------------------------------------------------- # # Copyright (c) 2018, 2018, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License version 2 only, as # published by the Free Software Foundation. # # This code 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 # version 2 for more details (a copy is included in the LICENSE file that # accompanied this code). # # You should have received a copy of the GNU General Public License version # 2 along with this work; if not, write to the Free Software Foundation, # Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. # # Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA # or visit www.oracle.com if you need additional information or have any # questions. # # ---------------------------------------------------------------------------------------------------- from __future__ import print_function import os, tempfile from argparse import ArgumentParser, REMAINDER from os.path import exists, expanduser, join, isdir, isfile, realpath, dirname, abspath # Temporary imports and (re)definitions while porting mx from Python 2 to Python 3 import sys if sys.version_info[0] < 3: def input(prompt=None): # pylint: disable=redefined-builtin return raw_input(prompt) # pylint: disable=undefined-variable from StringIO import StringIO else: from io import StringIO def is_valid_jdk(jdk): """ Determines if `jdk` looks like a valid JDK directory. :return: True if there's a ``java`` executable in ``jdk/bin`` """ java_exe = join(jdk, 'bin', 'java') if not exists(java_exe): java_exe += '.exe' return isfile(java_exe) and os.access(java_exe, os.X_OK) def find_system_jdks(): """ Returns a set of valid JDK directories by searching standard locations. """ bases = [ '/Library/Java/JavaVirtualMachines', '/usr/lib/jvm', '/usr/java', '/usr/jdk/instances', r'C:\Program Files\Java' ] jdks = set() for base in bases: if isdir(base): for n in os.listdir(base): jdk = join(base, n) mac_jdk = join(jdk, 'Contents', 'Home') if isdir(mac_jdk): jdk = mac_jdk if is_valid_jdk(jdk): jdks.add(realpath(jdk)) return jdks def get_suite_env_file(suite_dir=None): for n in os.listdir(suite_dir or '.'): if n.startswith('mx.'): suite_py = join('.', n, 'suite.py') if exists(suite_py): return abspath(join(suite_dir or '.', n, 'env')) return None def get_setvar_format(shell): if shell == 'csh': return 'setenv %s %s' if shell == 'fish': return 'set -x %s %s' return 'export %s=%s' def get_PATH_sep(shell): if shell == 'fish': return ' ' return os.pathsep def get_shell_commands(args, jdk, extra_jdks): setvar_format = get_setvar_format(args.shell) shell_commands = StringIO() print(setvar_format % ('JAVA_HOME', jdk), file=shell_commands) if extra_jdks: print(setvar_format % ('EXTRA_JAVA_HOMES', os.pathsep.join(extra_jdks)), file=shell_commands) path = os.environ.get('PATH').split(os.pathsep) if path: jdk_bin = join(jdk, 'bin') old_java_home = os.environ.get('JAVA_HOME') replace = join(old_java_home, 'bin') if old_java_home else None if replace in path: path = [e if e != replace else jdk_bin for e in path] else: path = [jdk_bin] + path print(setvar_format % ('PATH', get_PATH_sep(args.shell).join(path)), file=shell_commands) return shell_commands.getvalue().strip() def apply_selection(args, jdk, extra_jdks): print('JAVA_HOME=' + jdk) if extra_jdks: print('EXTRA_JAVA_HOMES=' + os.pathsep.join(extra_jdks)) if args.shell_file: with open(args.shell_file, 'w') as fp: print(get_shell_commands(args, jdk, extra_jdks), file=fp) else: env = get_suite_env_file(args.suite_path) if env: with open(env, 'a') as fp: print('JAVA_HOME=' + jdk, file=fp) if extra_jdks: print('EXTRA_JAVA_HOMES=' + os.pathsep.join(extra_jdks), file=fp) print('Updated', env) else: print() print('To apply the above environment variable settings, eval the following in your shell:') print() print(get_shell_commands(args, jdk, extra_jdks)) if __name__ == '__main__': parser = ArgumentParser(prog='select_jdk', usage='%(prog)s [options] [<primary jdk> [<secondary jdk>...]]' + """ Selects values for the JAVA_HOME, EXTRA_JAVA_HOMES and PATH environment variables based on the explicitly supplied JDKs or on system JDKs plus previously selected JDKs (cached in ~/.mx/jdk_cache). If the -s/--shell-source option is given, settings appropriate for the current shell are written to the given file such that it can be eval'ed in the shell to apply the settings. For example, in ~/.config/fish/config.fish: if test -x (dirname (which mx))/select_jdk.py function select_jdk set tmp_file (mktemp) eval (dirname (which mx))/select_jdk.py -s $tmp_file $argv source $tmp_file rm $tmp_file end end or in ~/.bashrc: if [ -x $(dirname $(which mx))/select_jdk.py ]; then function select_jdk { TMP_FILE=select_jdk.$$ eval $(dirname $(which mx))/select_jdk.py -s $TMP_FILE "$@" source $TMP_FILE rm $TMP_FILE } fi In the absence of -s, if the current directory looks like a suite, the mx.<suite>/env file is created/updated with the selected values for JAVA_HOME and EXTRA_JAVA_HOMES. Otherwise, the settings are printed such that they can applied manually. """) shell_or_env = parser.add_mutually_exclusive_group() shell_or_env.add_argument('-s', '--shell-file', action='store', help='write shell commands for setting env vars to <path>', metavar='<path>') shell_or_env.add_argument('-p', '--suite-path', help='directory of suite whose env file is to be updated', metavar='<path>') parser.add_argument('--shell', action='store', help='shell syntax to use for commands', metavar='<format>', choices=['sh', 'fish', 'csh']) parser.add_argument('jdks', nargs=REMAINDER, metavar='<primary jdk> [<secondary jdk>...]') args = parser.parse_args() if args.shell is None: shell = os.environ.get('SHELL') if shell.endswith('fish'): args.shell = 'fish' elif shell.endswith('csh'): args.shell = 'csh' else: args.shell = 'sh' jdk_cache_path = join(expanduser('~'), '.mx', 'jdk_cache') if len(args.jdks) != 0: invalid_jdks = [a for a in args.jdks if not is_valid_jdk(a)] if invalid_jdks: raise SystemExit('Following JDKs appear to be invalid (java executable not found):\n' + '\n'.join(invalid_jdks)) if not exists(jdk_cache_path): os.makedirs(jdk_cache_path) with open(jdk_cache_path, 'a') as fp: for jdk in args.jdks: print(abspath(jdk), file=fp) apply_selection(args, abspath(args.jdks[0]), [abspath(a) for a in args.jdks[1:]]) else: jdks = find_system_jdks() if exists(jdk_cache_path): with open(jdk_cache_path) as fp: jdks.update((line.strip() for line in fp.readlines() if is_valid_jdk(line.strip()))) sorted_jdks = sorted(jdks) print("Current JDK Settings:") for name in ['JAVA_HOME', 'EXTRA_JAVA_HOMES']: jdk = os.environ.get(name, None) if jdk: if jdk in sorted_jdks: jdk = '{} [{}]'.format(jdk, sorted_jdks.index(jdk)) print('{}={}'.format(name, jdk)) choices = list(enumerate(sorted_jdks)) if choices: _, tmp_cache_path = tempfile.mkstemp(dir=dirname(jdk_cache_path)) with open(tmp_cache_path, 'w') as fp: for index, jdk in choices: print('[{}] {}'.format(index, jdk)) print(jdk, file=fp) os.rename(tmp_cache_path, jdk_cache_path) choices = {str(index):jdk for index, jdk in choices} jdks = [choices[n] for n in input('Select JDK(s) (separate multiple choices by whitespace)> ').split() if n in choices] if jdks: apply_selection(args, jdks[0], jdks[1:])