Allow jagidir modules to include other compiled modules.
[jagi.git] / src / jagi / fs / Compiler.java
index e7a66a1..2f0d81c 100644 (file)
@@ -3,12 +3,15 @@ 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<Path, File> files = new HashMap<>();
     private final Map<Path, ClassLoader> libs = new HashMap<>();
     private final Collection<Path> searchpath = new ArrayList<>();
@@ -84,15 +87,17 @@ public class Compiler {
     }
 
     public static class Compilation implements AutoCloseable {
-       public final Path dir, srcdir, outdir;
+       public final Path dir, srcdir, outdir, libdir;
        private final List<Path> infiles = new ArrayList<>();
        private final List<Path> classpath = new ArrayList<>();
        private List<String> 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);
        }
@@ -105,6 +110,32 @@ public class Compiler {
            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<String, byte[]> classes) throws IOException {
+           for(Map.Entry<String, byte[]> ent : classes.entrySet())
+               addlib(ent.getKey(), ent.getValue());
+       }
+
        public boolean compile() throws IOException {
            List<String> args = new ArrayList<>();
            args.add("javac");
@@ -235,6 +266,12 @@ public class Compiler {
     }
 
     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))
@@ -245,10 +282,12 @@ public class Compiler {
 
     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<Path> classpath = new ArrayList<>();
+       public final Collection<Module> include = new ArrayList<>();
 
        public Module(Path file) throws IOException {
            this.file = file;
@@ -259,10 +298,12 @@ public class Compiler {
                if(!c.compile())
                    throw(new CompilationException(file, c.output()));
                ClassLoader parent = Compiler.class.getClassLoader();
-               if(!classpath.isEmpty()) {
+               if(!classpath.isEmpty() || !include.isEmpty()) {
                    Collection<ClassLoader> 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());
@@ -281,6 +322,26 @@ public class Compiler {
                            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);
@@ -317,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);
+                       }
+                   }
                }
            }
        }