############################################################################### # Copyright (c) 2022 Marc Schreiber and others. # # This program and the accompanying materials are made available under the # terms of the Eclipse Public License 2.0 which is available at # http://www.eclipse.org/legal/epl-2.0. # # SPDX-License-Identifier: EPL-2.0 # # Contributors: # Marc Schreiber - initial API and implementation ############################################################################### import argparse from hashlib import sha1 import os import platform import re import subprocess from pathlib import Path import tempfile def get_java_executable(known_args): if known_args.java_executable is not None: java_executable = known_args.java_executable else: java_executable = 'java' if 'JAVA_HOME' in os.environ: ext = '.exe' if platform.system() == 'Windows' else '' java_exec_to_test = Path(os.environ['JAVA_HOME']) / 'bin' / f'java{ext}' if java_exec_to_test.is_file(): java_executable = str(java_exec_to_test.resolve()) if not known_args.validate_java_version: return java_executable java_major_version = get_java_major_version(java_executable) if java_major_version < 21: raise Exception("jdtls requires at least Java 21") return java_executable def get_java_major_version (java_executable): out = subprocess.check_output([java_executable, '-version'], stderr = subprocess.STDOUT, universal_newlines=True) matches = re.finditer(r"(?<=version\s\")(?P\d+)(\.\d+\.\d+(_\d+)?)?", out) for match in matches: return int(match.group("major")) raise Exception("Could not determine Java version") def find_equinox_launcher(jdtls_base_directory): plugins_dir = jdtls_base_directory / "plugins" if (plugins_dir / 'org.eclipse.equinox.launcher.jar').is_file(): # mason-registry packaging return str(plugins_dir / 'org.eclipse.equinox.launcher.jar') launchers = plugins_dir.glob('org.eclipse.equinox.launcher_*.jar') for launcher in launchers: return str(plugins_dir / launcher) raise Exception("Cannot find equinox launcher") def get_shared_config_path(jdtls_base_path): system = platform.system() if system in ['Linux', 'FreeBSD']: config_dir = 'config_linux' elif system == 'Darwin': config_dir = 'config_mac' elif system == 'Windows': config_dir = 'config_win' else: raise Exception("Unknown platform {} detected".format(system)) return str(jdtls_base_path / config_dir) def main(args): cwd_name = os.path.basename(os.getcwd()) system = platform.system() if system == 'Windows' and 'APPDATA' in os.environ: cachedir = Path(os.environ['APPDATA']) elif system == 'Darwin' and 'HOME' in os.environ: cachedir = Path(os.environ['HOME']) / 'Library' / 'Caches' elif system == 'Linux' and 'HOME' in os.environ: cachedir = Path(os.environ['HOME']) / '.cache' else: cachedir = Path(tempfile.gettempdir()) cachedir = cachedir / 'jdtls' jdtls_data_path = os.path.join(cachedir, "jdtls-" + sha1(cwd_name.encode()).hexdigest()) parser = argparse.ArgumentParser() parser.add_argument('--validate-java-version', action='store_true', default=True) parser.add_argument('--no-validate-java-version', dest='validate_java_version', action='store_false') parser.add_argument("--java-executable", help="Path to java executable used to start runtime.") parser.add_argument("--jvm-arg", default=[], action="append", help="An additional JVM option (can be used multiple times. Note, use with equal sign. For example: --jvm-arg=-Dlog.level=ALL") parser.add_argument("-data", default=jdtls_data_path) known_args, args = parser.parse_known_args(args) java_executable = get_java_executable(known_args) java_major_version = get_java_major_version(java_executable) jdtls_base_path = Path(__file__).parent.parent shared_config_path = get_shared_config_path(jdtls_base_path) jar_path = find_equinox_launcher(jdtls_base_path) exec_args = ["-Declipse.application=org.eclipse.jdt.ls.core.id1", "-Dosgi.bundles.defaultStartLevel=4", "-Declipse.product=org.eclipse.jdt.ls.core.product", "-Dosgi.checkConfiguration=true", "-Dosgi.sharedConfiguration.area=" + shared_config_path, "-Dosgi.sharedConfiguration.area.readOnly=true", "-Dosgi.configuration.cascaded=true", "-Xms1G", "--add-modules=ALL-SYSTEM", "--add-opens", "java.base/java.util=ALL-UNNAMED", "--add-opens", "java.base/java.lang=ALL-UNNAMED"] \ + known_args.jvm_arg \ + ["-jar", jar_path, "-data", known_args.data] \ + args if (java_major_version >= 24): exec_args = [ '-Djdk.xml.maxGeneralEntitySizeLimit=0', '-Djdk.xml.totalEntitySizeLimit=0' ] + exec_args if os.name == 'posix': os.execvp(java_executable, exec_args) else: subprocess.run([java_executable] + exec_args)