diff --git a/src/main/java/org/javacs/InferConfig.java b/src/main/java/org/javacs/InferConfig.java index 8cc004c5..180851fd 100644 --- a/src/main/java/org/javacs/InferConfig.java +++ b/src/main/java/org/javacs/InferConfig.java @@ -13,6 +13,20 @@ import java.util.logging.Logger; import java.util.regex.Pattern; import java.util.stream.Stream; +import java.net.URI; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.logging.Logger; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.net.URI; +import java.util.Collection; +import java.util.Set; +import java.util.HashSet; +import java.util.stream.Collectors; class InferConfig { private static final Logger LOG = Logger.getLogger("main"); @@ -49,11 +63,23 @@ private static Path defaultGradleHome() { return Paths.get(System.getProperty("user.home")).resolve(".gradle"); } + + static boolean firstVisitClassPath = true; + static final String classPathSerName = ".jls/classes.ser"; + /** Find .jar files for external dependencies, for examples maven dependencies in ~/.m2 or jars in bazel-genfiles */ Set classPath() { // externalDependencies + Set restored = restorePaths(classPathSerName); + LOG.info("restored object " + restored); + if (firstVisitClassPath && restored != null) { + firstVisitClassPath = false; + return restored; + } + + Set result = Collections.emptySet(); if (!externalDependencies.isEmpty()) { - var result = new HashSet(); + result = new HashSet(); for (var id : externalDependencies) { var a = Artifact.parse(id); var found = findAnyJar(a, false); @@ -63,22 +89,37 @@ Set classPath() { } result.add(found); } - return result; + //return result; + } else { + + // Maven + var pomXml = workspaceRoot.resolve("pom.xml"); + if (Files.exists(pomXml)) { + Set deps = mvnDependencies(pomXml, "dependency:list"); + result = deps; + } else { + + // Bazel + var bazelWorkspaceRoot = bazelWorkspaceRoot(); + if (Files.exists(bazelWorkspaceRoot.resolve("WORKSPACE"))) { + result = bazelClasspath(bazelWorkspaceRoot); + } + } } - // Maven - var pomXml = workspaceRoot.resolve("pom.xml"); - if (Files.exists(pomXml)) { - return mvnDependencies(pomXml, "dependency:list"); + var classpathEnvVar = System.getenv("CLASSPATH"); + LOG.fine(() -> "CLASSPATH=" + classpathEnvVar); + if (classpathEnvVar != null) { + var paths = Stream.of(classpathEnvVar.split(System.getProperty("path.separator"))).map(Path::of).collect(Collectors.toSet()); + LOG.fine(() -> "Paths from CLASSPATH = " + paths); + result = paths; } - // Bazel - var bazelWorkspaceRoot = bazelWorkspaceRoot(); - if (Files.exists(bazelWorkspaceRoot.resolve("WORKSPACE"))) { - return bazelClasspath(bazelWorkspaceRoot); + if (restored == null) { + savePaths(result, classPathSerName); } - - return Collections.emptySet(); + LOG.info("class path saved"); + return result; } private Path bazelWorkspaceRoot() { @@ -90,11 +131,23 @@ private Path bazelWorkspaceRoot() { return workspaceRoot; } + static boolean firstVisitDocPath = true; + static final String docPathSerName = ".jls/doc.ser"; + /** Find source .jar files in local maven repository. */ Set buildDocPath() { // externalDependencies + Set restored = restorePaths(docPathSerName); + LOG.info("restored object " + restored); + if (firstVisitDocPath && restored != null) { + firstVisitDocPath = false; + LOG.info("doc path restored"); + return restored; + } + + Set result = Collections.emptySet(); if (!externalDependencies.isEmpty()) { - var result = new HashSet(); + result = new HashSet(); for (var id : externalDependencies) { var a = Artifact.parse(id); var found = findAnyJar(a, true); @@ -104,22 +157,24 @@ Set buildDocPath() { } result.add(found); } - return result; - } + } else { - // Maven - var pomXml = workspaceRoot.resolve("pom.xml"); - if (Files.exists(pomXml)) { - return mvnDependencies(pomXml, "dependency:sources"); - } + // Maven + var pomXml = workspaceRoot.resolve("pom.xml"); + if (Files.exists(pomXml)) { + result = mvnDependencies(pomXml, "dependency:sources"); + } - // Bazel - var bazelWorkspaceRoot = bazelWorkspaceRoot(); - if (Files.exists(bazelWorkspaceRoot.resolve("WORKSPACE"))) { - return bazelSourcepath(bazelWorkspaceRoot); + // Bazel + var bazelWorkspaceRoot = bazelWorkspaceRoot(); + if (Files.exists(bazelWorkspaceRoot.resolve("WORKSPACE"))) { + result = bazelSourcepath(bazelWorkspaceRoot); + } } - - return Collections.emptySet(); + if (restored == null) { + savePaths(result, docPathSerName); + } + return result; } private Path findAnyJar(Artifact artifact, boolean source) { @@ -173,9 +228,20 @@ private String fileName(Artifact artifact, boolean source) { return artifact.artifactId + '-' + artifact.version + (source ? "-sources" : "") + ".jar"; } + static boolean firstVisitMvn = true; + static final String mvnSerName = ".jls/deps.ser"; + static Set mvnDependencies(Path pomXml, String goal) { Objects.requireNonNull(pomXml, "pom.xml path is null"); try { + Set restored = restorePaths(mvnSerName); + LOG.info("restored object " + restored); + if (firstVisitMvn && restored != null) { + firstVisitMvn = false; + LOG.info("mvn path restored"); + return restored; + } + var dependencies = new HashSet(); // TODO consider using mvn valide dependency:copy-dependencies -DoutputDirectory=??? instead // Run maven as a subprocess String[] command = { @@ -203,13 +269,15 @@ static Set mvnDependencies(Path pomXml, String goal) { return Set.of(); } // Read output - var dependencies = new HashSet(); for (var line : Files.readAllLines(output)) { var jar = readDependency(line); if (jar != NOT_FOUND) { dependencies.add(jar); } } + if (restored == null) { + savePaths(dependencies, mvnSerName); + } return dependencies; } catch (InterruptedException | IOException e) { throw new RuntimeException(e); @@ -497,5 +565,42 @@ private static Path fork(Path workspaceRoot, String[] command) { } } + static void saveObject(Object obj, String name) { + try (FileOutputStream fos = new FileOutputStream(name); + ObjectOutputStream oos = new ObjectOutputStream(fos)) { + oos.writeObject(obj); + } catch (Exception e) { + // ignore + LOG.severe(e.getMessage()); + } + } + + static public Object restoreObject(String name) { + try (FileInputStream fis = new FileInputStream(name); + ObjectInputStream ois = new ObjectInputStream(fis)) { + return ois.readObject(); + } catch (Exception e) { + LOG.severe(e.getMessage()); + return null; + } + } + + static void savePaths(Collection paths, String name) { + saveObject(paths.stream().map(p -> p.toUri()).toList(), name); + } + + static Set restorePaths(String name) { + Object obj = restoreObject(name); + if (obj != null) { + Set result = new HashSet<>(); + for (URI uri : (Collection)obj) { + result.add(Paths.get(uri)); + } + return result; + } + return null; + } + + private static final Path NOT_FOUND = Paths.get(""); } diff --git a/src/main/java/org/javacs/Main.java b/src/main/java/org/javacs/Main.java index a908804a..df8cb9e9 100644 --- a/src/main/java/org/javacs/Main.java +++ b/src/main/java/org/javacs/Main.java @@ -4,6 +4,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import org.javacs.lsp.*; +import java.io.File; public class Main { private static final Logger LOG = Logger.getLogger("main"); @@ -19,6 +20,12 @@ public static void setRootFormat() { public static void main(String[] args) { boolean quiet = Arrays.stream(args).anyMatch("--quiet"::equals); + // define hidden workspace directory + File directory = new File(".jls"); + if (!directory.exists()) { + directory.mkdirs(); + } + if (quiet) { LOG.setLevel(Level.OFF); }