X-Git-Url: http://dolda2000.com/gitweb/?a=blobdiff_plain;f=src%2Fjagi%2Ffs%2FCompiler.java;h=2f0d81ce512bf83c17ac086b5466f7fc00e6cd96;hb=d7e59902e059954a7f764c8b09da2dff76bc5ee4;hp=14b3786d817ff6e82c15e99045d2c5402ccfb0af;hpb=e8892ea03fb37d1f1d362ad82b16a14986c5cd25;p=jagi.git diff --git a/src/jagi/fs/Compiler.java b/src/jagi/fs/Compiler.java index 14b3786..2f0d81c 100644 --- a/src/jagi/fs/Compiler.java +++ b/src/jagi/fs/Compiler.java @@ -3,12 +3,53 @@ package jagi.fs; import jagi.*; import java.util.*; import java.util.regex.*; +import java.util.logging.*; import java.nio.file.*; import java.nio.file.attribute.*; import java.io.*; +import java.net.*; public class Compiler { + private static final Logger log = Logger.getLogger("jagi-fs"); + private static final Path cwd = Paths.get("").toAbsolutePath(); 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,14 +87,17 @@ public class Compiler { } public static class Compilation implements AutoCloseable { - private final Path dir, srcdir, outdir; + public final Path dir, srcdir, outdir, libdir; private final List infiles = new ArrayList<>(); + private final List classpath = new ArrayList<>(); private List output = null; + private boolean haslib; public Compilation() throws IOException { dir = Files.createTempDirectory("javac"); srcdir = dir.resolve("src"); outdir = dir.resolve("out"); + libdir = dir.resolve("lib"); Files.createDirectory(srcdir); Files.createDirectory(outdir); } @@ -62,11 +106,51 @@ public class Compiler { infiles.add(p); } + public void classpath(Path p) { + classpath.add(p); + } + + public void addlib(String clnm, byte[] contents) throws IOException { + if(!haslib) { + Files.createDirectory(libdir); + classpath(libdir); + haslib = true; + } + Path p = libdir; + int p1 = 0; + while(true) { + int p2 = clnm.indexOf('.', p1); + if(p2 < 0) + break; + p = p.resolve(clnm.substring(p1, p2)); + if(!Files.isDirectory(p)) + Files.createDirectory(p); + p1 = p2 + 1; + } + p = p.resolve(clnm.substring(p1) + ".class"); + Files.write(p, contents); + } + + public void addlib(Map classes) throws IOException { + for(Map.Entry ent : classes.entrySet()) + addlib(ent.getKey(), ent.getValue()); + } + 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 +218,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 +233,116 @@ 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) { + try { + Path p = Paths.get(nm); + if(Files.isRegularFile(p)) + return(p); + } catch(InvalidPathException e) { + } + 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+)"); + private static final Pattern incpat = Pattern.compile("\\$include\\s*:\\s*(\\S+)"); + public class Module { public final Path file; - public final ClassLoader code; + public final BufferedClassLoader code; + public final Collection classpath = new ArrayList<>(); + public final Collection include = 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() || !include.isEmpty()) { + Collection libs = new ArrayList<>(); + for(Path cp : classpath) + libs.add(libloader(cp)); + for(Module mod : include) + libs.add(mod.code); + 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 = incpat.matcher(ln); + if(m.find()) { + String nm = m.group(1); + File f= null; + if(f == null) { + Path p = file.resolveSibling(nm); + if(Files.isRegularFile(p)) + f = file(p); + } + if(f == null) { + Path p = cwd.resolve(nm); + if(Files.isRegularFile(p)) + f = file(p); + } + if(f == null) + throw(new CompilationException(file, Arrays.asList("no such file to include: " + nm))); + f.update(); + c.addlib(f.mod().code.contents); + include.add(f.mod()); + } + m = classpat.matcher(ln); if(m.find()) { String clnm = m.group(4); Path sp = c.srcdir.resolve(clnm + ".java"); @@ -191,7 +365,7 @@ public class Compiler { } } - public static class File { + public class File { public final Path name; private FileTime mtime = null; private Module mod = null; @@ -204,8 +378,16 @@ public class Compiler { synchronized(this) { FileTime mtime = Files.getLastModifiedTime(name); if((this.mtime == null) || (this.mtime.compareTo(mtime) < 0)) { - mod = new Module(name); + Module pmod = this.mod; + this.mod = new Module(name); this.mtime = mtime; + if(pmod instanceof AutoCloseable) { + try { + ((AutoCloseable)pmod).close(); + } catch(Exception e) { + log.log(Level.WARNING, String.format("Error when disposing updated module %s", pmod.file), e); + } + } } } }