| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| import * as vscode from 'vscode'; |
| import * as path from 'path'; |
| import * as fs from 'fs'; |
| import * as jdkUtils from 'jdk-utils'; |
| |
| |
| const CUSTOM_JAVA_ROOTS_KEY = 'nbls.jdk.customJavaRoots'; |
| |
| let storage: vscode.Memento; |
| |
| export function initialize(context: vscode.ExtensionContext) { |
| storage = context.globalState; |
| } |
| |
| export class Java { |
| |
| readonly javaHome: string; |
| |
| constructor(javaHome: string) { |
| this.javaHome = javaHome; |
| } |
| |
| name(): string { |
| let name = path.basename(this.javaHome); |
| if (isMac() && name === 'Home') { |
| const javaHome = path.resolve(this.javaHome, '..', '..'); |
| name = path.basename(javaHome); |
| } |
| return name; |
| } |
| |
| isJava(): boolean { |
| const java = path.join(this.javaHome, 'bin', isWindows() ? 'java.exe' : 'java'); |
| return fs.existsSync(java); |
| } |
| |
| isJdk(): boolean { |
| const javac = path.join(this.javaHome, 'bin', isWindows() ? 'javac.exe' : 'javac'); |
| return this.isJava() && fs.existsSync(javac); |
| } |
| |
| hasNativeImage(): boolean { |
| const ni = path.join(this.javaHome, 'bin', isWindows() ? 'native-image.cmd' : 'native-image'); |
| return fs.existsSync(ni); |
| } |
| |
| async getVersion(): Promise<{ java_version: string; major: number; } | undefined> { |
| const java = await jdkUtils.getRuntime(this.javaHome, { withVersion: true }); |
| return java?.version; |
| } |
| |
| } |
| |
| export async function findAll(knownJavas?: Java[]): Promise<Java[]> { |
| const javaRoots: string[] = []; |
| function addJavaRoot(javaHome: string) { |
| let javaRoot = path.dirname(javaHome); |
| if (isMac() && path.basename(javaRoot) === 'Contents') { |
| javaHome = path.resolve(javaHome, '..', '..'); |
| javaRoot = path.dirname(javaHome); |
| } |
| const normalizedJavaRoot = normalizePath(javaRoot); |
| if (!javaRoots.includes(normalizedJavaRoot)) { |
| javaRoots.push(normalizedJavaRoot); |
| } |
| } |
| |
| const systemJavas = await jdkUtils.findRuntimes(); |
| for (const sytemJava of systemJavas) { |
| addJavaRoot(sytemJava.homedir); |
| } |
| |
| if (knownJavas) { |
| for (const knownJava of knownJavas) { |
| addJavaRoot(knownJava.javaHome); |
| } |
| } |
| |
| const customJavaRootsArr = await customJavaRoots(); |
| for (const customJavaRoot of customJavaRootsArr) { |
| if (!javaRoots.includes(customJavaRoot)) { |
| javaRoots.push(customJavaRoot); |
| } |
| } |
| |
| const jdks: Java[] = []; |
| for (const javaRoot of javaRoots) { |
| const dirents = fs.readdirSync(javaRoot, { withFileTypes: true }); |
| for (const dirent of dirents) { |
| const javaHome = path.join(javaRoot, dirent.name); |
| // statSync returns true even for symlinked dirs |
| try { |
| if (fs.statSync(javaHome).isDirectory()) { |
| const java = new Java(javaHome); |
| if (java.isJdk()) { |
| jdks.push(java); |
| } else if (isMac()) { |
| const macJavaHome = path.join(javaHome, 'Contents', 'Home'); |
| const macJava = new Java(macJavaHome); |
| if (macJava.isJdk()) { |
| jdks.push(macJava); |
| } |
| } |
| } |
| } catch (statErr : any) { |
| // just ignore |
| } |
| } |
| } |
| |
| return jdks; |
| } |
| |
| async function customJavaRoots(): Promise<string[]> { |
| const customJavaRoots = storage.get<string>(CUSTOM_JAVA_ROOTS_KEY) || ''; |
| const customJavaRootsArr = customJavaRoots.split(path.delimiter); |
| const newCustomJavaRootsArr = []; |
| for (const customJavaRoot of customJavaRootsArr) { |
| if (fs.existsSync(customJavaRoot)) { |
| newCustomJavaRootsArr.push(customJavaRoot); |
| } |
| } |
| if (customJavaRoots.length !== newCustomJavaRootsArr.length) { |
| await storage.update(CUSTOM_JAVA_ROOTS_KEY, newCustomJavaRootsArr.join(path.delimiter)); |
| } |
| return newCustomJavaRootsArr; |
| } |
| |
| export async function registerCustom(java: Java) { |
| let customJavaRoot = path.dirname(java.javaHome); |
| if (isMac() && path.basename(customJavaRoot) === 'Contents') { |
| const javaHome = path.resolve(java.javaHome, '..', '..'); |
| customJavaRoot = path.dirname(javaHome); |
| } |
| const customJavaRootsArr = await customJavaRoots(); |
| if (!customJavaRootsArr.includes(customJavaRoot)) { |
| customJavaRootsArr.push(customJavaRoot); |
| await storage.update(CUSTOM_JAVA_ROOTS_KEY, customJavaRootsArr.join(path.delimiter)); |
| } |
| } |
| |
| function isWindows(): boolean { |
| return process.platform === 'win32'; |
| } |
| |
| function isMac(): boolean { |
| return process.platform === 'darwin'; |
| } |
| |
| function normalizePath(fsPath: string): string { |
| return vscode.Uri.file(path.normalize(fsPath)).fsPath; |
| } |