X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=src%2Forg%2Fargeo%2Fbuild%2FMake.java;h=9593e0df00d01b38126b387e377e7a23bf1e6127;hb=8a5bc1878eb62e28f2cecbf610c61c456d1f1f89;hp=78895104d4b87a7672b5b2a3f61862e87e8dce9d;hpb=e1b29992391fdf4effd04ab7a13ecb6b31786b2c;p=cc0%2Fargeo-build.git diff --git a/src/org/argeo/build/Make.java b/src/org/argeo/build/Make.java index 7889510..9593e0d 100644 --- a/src/org/argeo/build/Make.java +++ b/src/org/argeo/build/Make.java @@ -1,10 +1,15 @@ package org.argeo.build; +import static java.lang.System.Logger.Level.ERROR; +import static java.lang.System.Logger.Level.INFO; + import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; import java.lang.management.ManagementFactory; import java.nio.file.FileVisitResult; import java.nio.file.Files; @@ -22,9 +27,11 @@ import java.util.Objects; import java.util.Properties; import java.util.StringJoiner; import java.util.StringTokenizer; +import java.util.concurrent.CompletableFuture; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; +import java.util.zip.Deflater; import org.eclipse.jdt.core.compiler.CompilationProgress; @@ -41,8 +48,12 @@ import aQute.bnd.osgi.Jar; * java -cp "/path/to/ECJ jar:/path/to/bndlib jar:/path/to/SLF4J jar" /path/to/cloned/argeo-build/src/org/argeo/build/Make.java action --option1 argument1 argument2 --option2 argument3 */ public class Make { + private final static Logger logger = System.getLogger(Make.class.getName()); + /** Name of the local-specific Makefile (sdk.mk). */ final static String SDK_MK = "sdk.mk"; + /** Name of the branch definition Makefile (branch.mk). */ + final static String BRANCH_MK = "branch.mk"; /** The execution directory (${user.dir}). */ final Path execDirectory; @@ -66,19 +77,7 @@ public class Make { Path sdkMkP = findSdkMk(execDirectory); Objects.requireNonNull(sdkMkP, "No " + SDK_MK + " found under " + execDirectory); - Map context = new HashMap<>(); - List sdkMkLines = Files.readAllLines(sdkMkP); - lines: for (String line : sdkMkLines) { - StringTokenizer st = new StringTokenizer(line, " :="); - if (!st.hasMoreTokens()) - continue lines; - String key = st.nextToken(); - if (!st.hasMoreTokens()) - continue lines; - String value = st.nextToken(); - context.put(key, value); - } - + Map context = readeMakefileVariables(sdkMkP); sdkSrcBase = Paths.get(context.computeIfAbsent("SDK_SRC_BASE", (key) -> { throw new IllegalStateException(key + " not found"); })).toAbsolutePath(); @@ -121,26 +120,33 @@ public class Make { // classpath if (!a2Categories.isEmpty()) { - compilerArgs.add("-cp"); StringJoiner classPath = new StringJoiner(File.pathSeparator); + StringJoiner modulePath = new StringJoiner(File.pathSeparator); for (String a2Base : a2Bases) { for (String a2Category : a2Categories) { Path a2Dir = Paths.get(a2Base).resolve(a2Category); if (!Files.exists(a2Dir)) Files.createDirectories(a2Dir); + modulePath.add(a2Dir.toString()); for (Path jarP : Files.newDirectoryStream(a2Dir, (p) -> p.getFileName().toString().endsWith(".jar"))) { classPath.add(jarP.toString()); } } } + compilerArgs.add("-cp"); compilerArgs.add(classPath.toString()); +// compilerArgs.add("--module-path"); +// compilerArgs.add(modulePath.toString()); } // sources for (String bundle : bundles) { StringBuilder sb = new StringBuilder(); - sb.append(execDirectory.resolve(bundle).resolve("src")); + Path bundlePath = execDirectory.resolve(bundle); + if (!Files.exists(bundlePath)) + throw new IllegalArgumentException("Bundle " + bundle + " not found in " + execDirectory); + sb.append(bundlePath.resolve("src")); sb.append("[-d"); compilerArgs.add(sb.toString()); sb = new StringBuilder(); @@ -149,11 +155,17 @@ public class Make { compilerArgs.add(sb.toString()); } + if (logger.isLoggable(INFO)) + compilerArgs.add("-time"); + +// for (String arg : compilerArgs) +// System.out.println(arg); + boolean success = org.eclipse.jdt.core.compiler.batch.BatchCompiler.compile( compilerArgs.toArray(new String[compilerArgs.size()]), new PrintWriter(System.out), new PrintWriter(System.err), new MakeCompilationProgress()); if (!success) // kill the process if compilation failed - System.exit(1); + throw new IllegalStateException("Compilation failed"); } /** Package the bundles. */ @@ -165,21 +177,40 @@ public class Make { return; List categories = options.get("--category"); - Objects.requireNonNull(bundles, "--bundles argument must be set"); + Objects.requireNonNull(bundles, "--category argument must be set"); if (categories.size() != 1) - throw new IllegalArgumentException("One and only one category must be specified"); + throw new IllegalArgumentException("One and only one --category must be specified"); String category = categories.get(0); - // create jars - for (String bundle : bundles) - createBundle(bundle, category); + Path branchMk = sdkSrcBase.resolve(BRANCH_MK); + if (!Files.exists(branchMk)) + throw new IllegalStateException("No " + branchMk + " file available"); + Map branchVariables = readeMakefileVariables(branchMk); + + String branch = branchVariables.get("BRANCH"); + + long begin = System.currentTimeMillis(); + // create jars in parallel + List> toDos = new ArrayList<>(); + for (String bundle : bundles) { + toDos.add(CompletableFuture.runAsync(() -> { + try { + createBundle(branch, bundle, category); + } catch (IOException e) { + throw new RuntimeException("Packaging of " + bundle + " failed", e); + } + })); + } + CompletableFuture.allOf(toDos.toArray(new CompletableFuture[toDos.size()])).join(); + long duration = System.currentTimeMillis() - begin; + logger.log(INFO, "Packaging took " + duration + " ms"); } /* * UTILITIES */ /** Package a single bundle. */ - void createBundle(String bundle, String category) throws IOException { + void createBundle(String branch, String bundle, String category) throws IOException { Path source = execDirectory.resolve(bundle); Path compiled = buildBase.resolve(bundle); String bundleSymbolicName = source.getFileName().toString(); @@ -190,8 +221,8 @@ public class Make { try (InputStream in = Files.newInputStream(argeoBnd)) { properties.load(in); } - // FIXME make it configurable - Path branchBnd = sdkSrcBase.resolve("cnf/unstable.bnd"); + + Path branchBnd = sdkSrcBase.resolve("sdk/branches/" + branch + ".bnd"); try (InputStream in = Files.newInputStream(branchBnd)) { properties.load(in); } @@ -218,10 +249,10 @@ public class Make { throw new RuntimeException("Bnd analysis of " + compiled + " failed", e); } - String major = properties.getProperty("MAJOR"); - Objects.requireNonNull(major, "MAJOR must be set"); - String minor = properties.getProperty("MINOR"); - Objects.requireNonNull(minor, "MINOR must be set"); + String major = properties.getProperty("major"); + Objects.requireNonNull(major, "'major' must be set"); + String minor = properties.getProperty("minor"); + Objects.requireNonNull(minor, "'minor' must be set"); // Write manifest Path manifestP = compiled.resolve("META-INF/MANIFEST.MF"); @@ -245,9 +276,9 @@ public class Make { Files.createDirectories(jarP.getParent()); try (JarOutputStream jarOut = new JarOutputStream(Files.newOutputStream(jarP), manifest)) { + jarOut.setLevel(Deflater.DEFAULT_COMPRESSION); // add all classes first Files.walkFileTree(binP, new SimpleFileVisitor() { - @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { jarOut.putNextEntry(new JarEntry(binP.relativize(file).toString())); @@ -258,7 +289,6 @@ public class Make { // add resources Files.walkFileTree(source, new SimpleFileVisitor() { - @Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { Path relativeP = source.relativize(dir); @@ -280,32 +310,44 @@ public class Make { Files.copy(file, jarOut); return FileVisitResult.CONTINUE; } + }); + Path srcP = source.resolve("src"); + // Add all resources from src/ + Files.walkFileTree(srcP, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + if (file.getFileName().toString().endsWith(".java") + || file.getFileName().toString().endsWith(".class")) + return FileVisitResult.CONTINUE; + jarOut.putNextEntry(new JarEntry(srcP.relativize(file).toString())); + if (!Files.isDirectory(file)) + Files.copy(file, jarOut); + return FileVisitResult.CONTINUE; + } }); // add sources // TODO add effective BND, Eclipse project file, etc., in order to be able to // repackage - Path srcP = source.resolve("src"); Files.walkFileTree(srcP, new SimpleFileVisitor() { - @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { jarOut.putNextEntry(new JarEntry("OSGI-OPT/src/" + srcP.relativize(file).toString())); - Files.copy(file, jarOut); + if (!Files.isDirectory(file)) + Files.copy(file, jarOut); return FileVisitResult.CONTINUE; } }); } - } /** * Recursively find the base source directory (which contains the * {@value #SDK_MK} file). */ - private Path findSdkMk(Path directory) { + Path findSdkMk(Path directory) { Path sdkMkP = directory.resolve(SDK_MK); if (Files.exists(sdkMkP)) { return sdkMkP.toAbsolutePath(); @@ -313,33 +355,54 @@ public class Make { if (directory.getParent() == null) return null; return findSdkMk(directory.getParent()); + } + /** + * Reads Makefile variable assignments of the form =, :=, or ?=, ignoring white + * spaces. To be used with very simple included Makefiles only. + */ + Map readeMakefileVariables(Path path) throws IOException { + Map context = new HashMap<>(); + List sdkMkLines = Files.readAllLines(path); + lines: for (String line : sdkMkLines) { + StringTokenizer st = new StringTokenizer(line, " :=?"); + if (!st.hasMoreTokens()) + continue lines; + String key = st.nextToken(); + if (!st.hasMoreTokens()) + continue lines; + String value = st.nextToken(); + if (st.hasMoreTokens()) // probably not a simple variable assignment + continue lines; + context.put(key, value); + } + return context; } /** Main entry point, interpreting actions and arguments. */ public static void main(String... args) { - try { - if (args.length == 0) - throw new IllegalArgumentException("At least an action must be provided"); - int actionIndex = 0; - String action = args[actionIndex]; - if (args.length > actionIndex + 1 && !args[actionIndex + 1].startsWith("-")) - throw new IllegalArgumentException( - "Action " + action + " must be followed by an option: " + Arrays.asList(args)); - - Map> options = new HashMap<>(); - String currentOption = null; - for (int i = actionIndex + 1; i < args.length; i++) { - if (args[i].startsWith("-")) { - currentOption = args[i]; - if (!options.containsKey(currentOption)) - options.put(currentOption, new ArrayList<>()); - - } else { - options.get(currentOption).add(args[i]); - } + if (args.length == 0) + throw new IllegalArgumentException("At least an action must be provided"); + int actionIndex = 0; + String action = args[actionIndex]; + if (args.length > actionIndex + 1 && !args[actionIndex + 1].startsWith("-")) + throw new IllegalArgumentException( + "Action " + action + " must be followed by an option: " + Arrays.asList(args)); + + Map> options = new HashMap<>(); + String currentOption = null; + for (int i = actionIndex + 1; i < args.length; i++) { + if (args[i].startsWith("-")) { + currentOption = args[i]; + if (!options.containsKey(currentOption)) + options.put(currentOption, new ArrayList<>()); + + } else { + options.get(currentOption).add(args[i]); } + } + try { Make argeoMake = new Make(); switch (action) { case "compile" -> argeoMake.compile(options); @@ -350,9 +413,12 @@ public class Make { } long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime(); - System.out.println("Completed after " + jvmUptime + " ms"); + logger.log(INFO, "Make.java action '" + action + "' succesfully completed after " + (jvmUptime / 1000) + "." + + (jvmUptime % 1000) + " s"); } catch (Exception e) { - e.printStackTrace(); + long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime(); + logger.log(ERROR, "Make.java action '" + action + "' failed after " + (jvmUptime / 1000) + "." + + (jvmUptime % 1000) + " s", e); System.exit(1); } } @@ -360,14 +426,15 @@ public class Make { /** * An ECJ {@link CompilationProgress} printing a progress bar while compiling. */ - class MakeCompilationProgress extends CompilationProgress { - int totalWork; - long currentChunk = 0; - - long chunksCount = 80; + static class MakeCompilationProgress extends CompilationProgress { + private int totalWork; + private long currentChunk = 0; + private long chunksCount = 80; @Override public void worked(int workIncrement, int remainingWork) { + if (!logger.isLoggable(Level.INFO)) // progress bar only at INFO level + return; long chunk = ((totalWork - remainingWork) * chunksCount) / totalWork; if (chunk != currentChunk) { currentChunk = chunk; @@ -400,7 +467,5 @@ public class Make { public void begin(int remainingWork) { this.totalWork = remainingWork; } - } - }