From 2be6677a7e36d486834ae804d5db08aaea7e32a6 Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Thu, 9 Nov 2023 20:33:46 +0100 Subject: [PATCH] Add classpath management to jagidir compiler. --- src/jagi/fs/Compiler.java | 127 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 120 insertions(+), 7 deletions(-) diff --git a/src/jagi/fs/Compiler.java b/src/jagi/fs/Compiler.java index 14b3786..e7a66a1 100644 --- a/src/jagi/fs/Compiler.java +++ b/src/jagi/fs/Compiler.java @@ -6,9 +6,47 @@ import java.util.regex.*; import java.nio.file.*; import java.nio.file.attribute.*; import java.io.*; +import java.net.*; public class Compiler { private final Map files = new HashMap<>(); + private final Map libs = new HashMap<>(); + private final Collection searchpath = new ArrayList<>(); + + public Compiler() { + String syspath = System.getenv("PATH"); + if(syspath != null) { + String sep = java.io.File.pathSeparator; + int p1 = 0, p2 = syspath.indexOf(sep); + do { + String el; + if(p2 >= 0) { + el = syspath.substring(p1, p2); + p1 = p2 + sep.length(); + p2 = syspath.indexOf(sep, p1); + } else { + el =syspath.substring(p1); + } + try { + Path p = Paths.get(el); + if(p.getParent() != null) + searchpath.add(p.getParent().resolve("share").resolve("java")); + } catch(InvalidPathException e) { + continue; + } + } while(p2 >= 0); + } + String proppath = System.getProperty("jagi.search-path"); + if(proppath != null) { + for(String el : proppath.split(":")) { + try { + searchpath.add(Paths.get(el)); + } catch(InvalidPathException e) { + continue; + } + } + } + } public static class ClassOutput { public final String name; @@ -46,8 +84,9 @@ public class Compiler { } public static class Compilation implements AutoCloseable { - private final Path dir, srcdir, outdir; + public final Path dir, srcdir, outdir; private final List infiles = new ArrayList<>(); + private final List classpath = new ArrayList<>(); private List output = null; public Compilation() throws IOException { @@ -62,11 +101,25 @@ public class Compiler { infiles.add(p); } + public void classpath(Path p) { + classpath.add(p); + } + public boolean compile() throws IOException { List args = new ArrayList<>(); args.add("javac"); args.add("-d"); args.add(outdir.toString()); + if(!classpath.isEmpty()) { + StringBuilder buf = new StringBuilder(); + for(Path cp : classpath) { + if(buf.length() > 0) + buf.append(":"); + buf.append(cp.toString()); + } + args.add("-cp"); + args.add(buf.toString()); + } for(Path p : infiles) args.add(p.toString()); ProcessBuilder cmd = new ProcessBuilder(args); @@ -134,7 +187,8 @@ public class Compiler { public static class BufferedClassLoader extends ClassLoader { public final Map contents; - public BufferedClassLoader(Collection contents) { + public BufferedClassLoader(ClassLoader parent, Collection contents) { + super(parent); this.contents = new HashMap<>(); for(ClassOutput clc : contents) this.contents.put(clc.name, clc.buf.toByteArray()); @@ -148,27 +202,86 @@ public class Compiler { } } - public static class Module { + public static class LibClassLoader extends ClassLoader { + private final ClassLoader[] classpath; + + public LibClassLoader(ClassLoader parent, Collection classpath) { + super(parent); + this.classpath = classpath.toArray(new ClassLoader[0]); + } + + public Class findClass(String name) throws ClassNotFoundException { + for(ClassLoader lib : classpath) { + try { + return(lib.loadClass(name)); + } catch(ClassNotFoundException e) {} + } + throw(new ClassNotFoundException("Could not find " + name + " in any of " + Arrays.asList(classpath).toString())); + } + } + + public ClassLoader libloader(Path p) { + synchronized(libs) { + ClassLoader ret = libs.get(p); + if(ret == null) { + try { + libs.put(p, ret = new URLClassLoader(new URL[] {p.toUri().toURL()})); + } catch(MalformedURLException e) { + throw(new RuntimeException(e)); + } + } + return(ret); + } + } + + private Path findlib(String nm) { + for(Path dir : searchpath) { + Path jar = dir.resolve(nm + ".jar"); + if(Files.isRegularFile(jar)) + return(jar); + } + return(null); + } + + private static final Pattern classpat = Pattern.compile("^((public|abstract)\\s+)*(class|interface)\\s+(\\S+)"); + private static final Pattern libpat = Pattern.compile("\\$use\\s*:\\s*(\\S+)"); + public class Module { public final Path file; public final ClassLoader code; + public final Collection classpath = new ArrayList<>(); public Module(Path file) throws IOException { this.file = file; try(Compilation c = new Compilation()) { split(c); + for(Path cp : classpath) + c.classpath(cp); if(!c.compile()) throw(new CompilationException(file, c.output())); - code = new BufferedClassLoader(c.classes()); + ClassLoader parent = Compiler.class.getClassLoader(); + if(!classpath.isEmpty()) { + Collection libs = new ArrayList<>(); + for(Path cp : classpath) + libs.add(libloader(cp)); + parent = new LibClassLoader(parent, libs); + } + code = new BufferedClassLoader(parent, c.classes()); } } - private static final Pattern classpat = Pattern.compile("^((public|abstract)\\s+)*(class|interface)\\s+(\\S+)"); public void split(Compilation c) throws IOException { StringBuilder head = new StringBuilder(); BufferedWriter cur = null; try(BufferedReader fp = Files.newBufferedReader(file)) { for(String ln = fp.readLine(); ln != null; ln = fp.readLine()) { - Matcher m = classpat.matcher(ln); + Matcher m = libpat.matcher(ln); + if(m.find()) { + Path lib = findlib(m.group(1)); + if(lib == null) + throw(new CompilationException(file, Arrays.asList("no such library: " + m.group(1)))); + classpath.add(lib); + } + m = classpat.matcher(ln); if(m.find()) { String clnm = m.group(4); Path sp = c.srcdir.resolve(clnm + ".java"); @@ -191,7 +304,7 @@ public class Compiler { } } - public static class File { + public class File { public final Path name; private FileTime mtime = null; private Module mod = null; -- 2.11.0