X-Git-Url: http://dolda2000.com/gitweb/?a=blobdiff_plain;f=src%2Fjagi%2Ffs%2FCompiler.java;h=60a1c24e70b8c7a9112ed0327315dd83d8dd584d;hb=9c4d8fd978f0d398f25b70b4f9016631a44a1777;hp=dcfee03dcf021d3a0365dc7c3760bb454c70dad6;hpb=49ccd711f15e0fbb64afdef0e6698aca14ecbc79;p=jagi.git diff --git a/src/jagi/fs/Compiler.java b/src/jagi/fs/Compiler.java index dcfee03..60a1c24 100644 --- a/src/jagi/fs/Compiler.java +++ b/src/jagi/fs/Compiler.java @@ -1,99 +1,67 @@ package jagi.fs; +import jagi.*; import java.util.*; import java.util.regex.*; import java.nio.file.*; import java.nio.file.attribute.*; import java.io.*; import java.net.*; -import javax.tools.*; public class Compiler { - private final Map modules = new HashMap<>(); + private final Map files = new HashMap<>(); + private final Map libs = new HashMap<>(); + private final Collection searchpath = new ArrayList<>(); - public static class FilePart extends SimpleJavaFileObject { - public final Path file; - public final String clnm; - public final CharSequence src; - - private static URI dummyuri(Path file, String clnm) { - String clp = clnm.replace('.', '/') + Kind.SOURCE.extension; - return(URI.create(file.toUri().toString() + "!/" + clp)); - } - - public FilePart(Path file, String clnm, CharSequence src) { - super(dummyuri(file, clnm), Kind.SOURCE); - this.file = file; - this.clnm = clnm; - this.src = src; - } - - public CharSequence getCharContent(boolean ice) { - return(src); + 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); } - - private static final Pattern classpat = Pattern.compile("^((public|abstract)\\s+)*(class|interface)\\s+(\\S+)"); - public static Collection split(Path file) throws IOException { - Collection ret = new ArrayList<>(); - StringBuilder head = new StringBuilder(); - StringBuilder cur = null; - String clnm = null; - try(BufferedReader fp = Files.newBufferedReader(file)) { - for(String ln = fp.readLine(); ln != null; ln = fp.readLine()) { - Matcher m = classpat.matcher(ln); - if(m.find()) { - if(cur != null) - ret.add(new FilePart(file, clnm, cur)); - clnm = m.group(4); - cur = new StringBuilder(); - cur.append(head); - } - if(cur != null) { - cur.append(ln); cur.append('\n'); - } else { - head.append(ln); head.append('\n'); - } + 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; } - if(cur != null) - ret.add(new FilePart(file, clnm, cur)); } - return(ret); } } - public static class ClassOutput extends SimpleJavaFileObject { + public static class ClassOutput { public final String name; - private final ByteArrayOutputStream buf = new ByteArrayOutputStream(); + public final ByteArrayOutputStream buf = new ByteArrayOutputStream(); public ClassOutput(String name) { - super(URI.create("mem://" + name), Kind.CLASS); this.name = name; } - - public OutputStream openOutputStream() { - return(buf); - } - } - - public static class FileContext extends ForwardingJavaFileManager { - public final Collection output = new ArrayList<>(); - - public FileContext(JavaCompiler javac) { - super(javac.getStandardFileManager(null, null, null)); - } - - public JavaFileObject getJavaFileForOutput(Location location, String name, JavaFileObject.Kind kind, FileObject sibling) { - ClassOutput cl = new ClassOutput(name); - output.add(cl); - return(cl); - } } public static class CompilationException extends RuntimeException { public final Path file; - private final List> messages; + private final List messages; - public CompilationException(Path file, List> messages) { + public CompilationException(Path file, List messages) { this.file = file; this.messages = messages; } @@ -104,7 +72,7 @@ public class Compiler { public String messages() { StringBuilder buf = new StringBuilder(); - for(Diagnostic msg : messages) + for(Object msg : messages) buf.append(msg.toString() + "\n"); return(buf.toString()); } @@ -115,25 +83,112 @@ public class Compiler { } } - public static Collection compile(Path file) throws IOException { - List opt = Arrays.asList(); - JavaCompiler javac = ToolProvider.getSystemJavaCompiler(); - if(javac == null) - throw(new RuntimeException("no javac present")); - Collection files; - files = FilePart.split(file); - DiagnosticCollector out = new DiagnosticCollector<>(); - FileContext fs = new FileContext(javac); - JavaCompiler.CompilationTask job = javac.getTask(null, fs, out, opt, null, files); - if(!job.call()) - throw(new CompilationException(file, out.getDiagnostics())); - return(fs.output); + public static class Compilation implements AutoCloseable { + 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 { + dir = Files.createTempDirectory("javac"); + srcdir = dir.resolve("src"); + outdir = dir.resolve("out"); + Files.createDirectory(srcdir); + Files.createDirectory(outdir); + } + + public void add(Path p) { + 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); + cmd.redirectErrorStream(true); + cmd.redirectOutput(ProcessBuilder.Redirect.PIPE); + Process proc = cmd.start(); + List output = new ArrayList<>(); + BufferedReader fp = new BufferedReader(new InputStreamReader(proc.getInputStream(), Utils.UTF8)); + while(true) { + String ln = fp.readLine(); + if(ln == null) + break; + output.add(ln); + } + int status; + try { + status = proc.waitFor(); + } catch(InterruptedException e) { + Thread.currentThread().interrupt(); + throw(new IOException("compilation interrupted")); + } + this.output = output; + return(status == 0); + } + + public List output() { + if(output == null) + throw(new IllegalStateException()); + return(output); + } + + public Collection classes() throws IOException { + Collection ret = new ArrayList<>(); + for(Path p : (Iterable)Files.walk(outdir)::iterator) { + Path rel = outdir.relativize(p); + String fn = rel.getName(rel.getNameCount() - 1).toString(); + if(!Files.isRegularFile(p) || !fn.endsWith(".class")) + continue; + StringBuilder clnm = new StringBuilder(); + for(int i = 0; i < rel.getNameCount() - 1; i++) { + clnm.append(rel.getName(i)); + clnm.append('.'); + } + clnm.append(fn.substring(0, fn.length() - 6)); + ClassOutput cls = new ClassOutput(clnm.toString()); + Files.copy(p, cls.buf); + ret.add(cls); + } + return(ret); + } + + private static void remove(Path p) throws IOException { + if(Files.isDirectory(p)) { + for(Path ent : (Iterable)Files.list(p)::iterator) + remove(ent); + } + Files.delete(p); + } + + public void close() throws IOException { + remove(dir); + } } 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()); @@ -147,37 +202,145 @@ 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+)"); + public class Module { public final Path file; - private FileTime mtime = null; - private ClassLoader code = null; + public final ClassLoader code; + public final Collection classpath = new ArrayList<>(); - private Module(Path file) { + 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())); + 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()); + } + } + + 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 = 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"); + c.add(sp); + if(cur != null) + cur.close(); + cur = Files.newBufferedWriter(sp); + cur.append(head); + } + if(cur != null) { + cur.append(ln); cur.append('\n'); + } else { + head.append(ln); head.append('\n'); + } + } + } finally { + if(cur != null) + cur.close(); + } + } + } + + public class File { + public final Path name; + private FileTime mtime = null; + private Module mod = null; + + private File(Path name) { + this.name = name; } public void update() throws IOException { synchronized(this) { - FileTime mtime = Files.getLastModifiedTime(file); + FileTime mtime = Files.getLastModifiedTime(name); if((this.mtime == null) || (this.mtime.compareTo(mtime) < 0)) { - code = new BufferedClassLoader(compile(file)); + mod = new Module(name); this.mtime = mtime; } } } - public ClassLoader code() { - if(code == null) - throw(new RuntimeException("module has not yet been updated")); - return(code); + public Module mod() { + if(mod == null) + throw(new RuntimeException("file has not yet been updated")); + return(mod); } } - public Module module(Path file) { - synchronized(modules) { - Module ret = modules.get(file); + public File file(Path name) { + synchronized(files) { + File ret = files.get(name); if(ret == null) - modules.put(file, ret = new Module(file)); + files.put(name, ret = new File(name)); return(ret); } }