From: Mathieu Baudier Date: Sun, 9 Oct 2022 12:09:14 +0000 (+0200) Subject: Incremental build working X-Git-Tag: v2.3.1~20 X-Git-Url: https://git.argeo.org/?a=commitdiff_plain;h=6cdb72cc5012c50becedffc5fb932eab4f6676dc;p=cc0%2Fargeo-build.git Incremental build working --- diff --git a/.classpath b/.classpath index d706197..3628e33 100644 --- a/.classpath +++ b/.classpath @@ -6,6 +6,6 @@ - + diff --git a/build.properties b/build.properties index 31bbddc..f8373a3 100644 --- a/build.properties +++ b/build.properties @@ -1,4 +1,4 @@ additional.bundles = org.slf4j.api bin.includes = META-INF/,\ - java/org/ -source.. = java \ No newline at end of file + src/org/ +source.. = src diff --git a/java/org/argeo/build/Make.java b/java/org/argeo/build/Make.java deleted file mode 100644 index f0ce443..0000000 --- a/java/org/argeo/build/Make.java +++ /dev/null @@ -1,325 +0,0 @@ -package org.argeo.build; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.PrintWriter; -import java.nio.file.FileVisitResult; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.PathMatcher; -import java.nio.file.Paths; -import java.nio.file.SimpleFileVisitor; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Properties; -import java.util.StringJoiner; -import java.util.StringTokenizer; -import java.util.jar.JarEntry; -import java.util.jar.JarOutputStream; -import java.util.jar.Manifest; - -import org.eclipse.jdt.core.compiler.CompilationProgress; - -import aQute.bnd.osgi.Analyzer; -import aQute.bnd.osgi.Jar; - -public class Make { - private final static String SDK_MK = "sdk.mk"; - - final Path execDirectory; - final Path sdkSrcBase; - final Path argeoBuildBase; - final Path sdkBuildBase; - final Path buildBase; - final Path a2Output; - - public Make() throws IOException { - execDirectory = Paths.get(System.getProperty("user.dir")); - 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); - } - - sdkSrcBase = Paths.get(context.computeIfAbsent("SDK_SRC_BASE", (key) -> { - throw new IllegalStateException(key + " not found"); - })).toAbsolutePath(); - argeoBuildBase = sdkSrcBase.resolve("sdk/argeo-build"); - - sdkBuildBase = Paths.get(context.computeIfAbsent("SDK_BUILD_BASE", (key) -> { - throw new IllegalStateException(key + " not found"); - })).toAbsolutePath(); - buildBase = sdkBuildBase.resolve(sdkSrcBase.getFileName()); - a2Output = sdkBuildBase.resolve("a2"); - } - - /* - * ACTIONS - */ - - @SuppressWarnings("restriction") - void compile(Map> options) throws IOException { - List bundles = options.get("--bundles"); - Objects.requireNonNull(bundles, "--bundles argument must be set"); - - List a2Categories = options.getOrDefault("--dep-categories", new ArrayList<>()); - List a2Bases = options.getOrDefault("--a2-bases", new ArrayList<>()); - if (a2Bases.isEmpty()) { - a2Bases.add(a2Output.toString()); - } - - List compilerArgs = new ArrayList<>(); - - Path ecjArgs = argeoBuildBase.resolve("ecj.args"); - compilerArgs.add("@" + ecjArgs); - - // classpath - if (!a2Categories.isEmpty()) { - compilerArgs.add("-cp"); - StringJoiner classPath = new StringJoiner(File.pathSeparator); - for (String a2Base : a2Bases) { - for (String a2Category : a2Categories) { - Path a2Dir = Paths.get(a2Base).resolve(a2Category); - for (Path jarP : Files.newDirectoryStream(a2Dir, - (p) -> p.getFileName().toString().endsWith(".jar"))) { - classPath.add(jarP.toString()); - } - } - } - compilerArgs.add(classPath.toString()); - } - - // sources - for (String bundle : bundles) { - StringBuilder sb = new StringBuilder(); - sb.append(sdkSrcBase.resolve(bundle).resolve("src")); - sb.append("[-d"); - compilerArgs.add(sb.toString()); - sb = new StringBuilder(); - sb.append(buildBase.resolve(bundle).resolve("bin")); - sb.append("]"); - compilerArgs.add(sb.toString()); - } - - // System.out.println(compilerArgs); - - // Use Main instead of BatchCompiler to workaround the fact that - // org.eclipse.jdt.core.compiler.batch is not exported - org.eclipse.jdt.internal.compiler.batch.Main.compile(compilerArgs.toArray(new String[compilerArgs.size()]), - new PrintWriter(System.out), new PrintWriter(System.err), (CompilationProgress) null); - } - - void bundle(Map> options) throws IOException { - List bundles = options.get("--bundles"); - Objects.requireNonNull(bundles, "--bundles argument must be set"); - - List categories = options.get("--category"); - Objects.requireNonNull(bundles, "--bundles argument must be set"); - if (categories.size() != 1) - throw new IllegalArgumentException("One and only one category must be specified"); - String category = categories.get(0); - - // create jars - for (String bundle : bundles) { - Path source = sdkSrcBase.resolve(bundle); - Path compiled = buildBase.resolve(bundle); - createBundle(source, compiled, category); - } - } - - /* - * JAR PACKAGING - */ - void createBundle(Path source, Path compiled, String category) throws IOException { - String bundleSymbolicName = source.getFileName().toString(); - - // Metadata - Properties properties = new Properties(); - Path argeoBnd = argeoBuildBase.resolve("argeo.bnd"); - try (InputStream in = Files.newInputStream(argeoBnd)) { - properties.load(in); - } - // FIXME make it configurable - Path branchBnd = sdkSrcBase.resolve("cnf/unstable.bnd"); - try (InputStream in = Files.newInputStream(branchBnd)) { - properties.load(in); - } - - Path bndBnd = source.resolve("bnd.bnd"); - try (InputStream in = Files.newInputStream(bndBnd)) { - properties.load(in); - } - - // Normalise - properties.put("Bundle-SymbolicName", bundleSymbolicName); - - // Calculate MANIFEST - Path binP = compiled.resolve("bin"); - Manifest manifest; - try (Analyzer bndAnalyzer = new Analyzer()) { - bndAnalyzer.setProperties(properties); - Jar jar = new Jar(bundleSymbolicName, binP.toFile()); - bndAnalyzer.setJar(jar); - manifest = bndAnalyzer.calcManifest(); - -// keys: for (Object key : manifest.getMainAttributes().keySet()) { -// System.out.println(key + ": " + manifest.getMainAttributes().getValue(key.toString())); -// } - } catch (Exception e) { - 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"); - - // Write manifest -// Path manifestP = compiled.resolve("META-INF/MANIFEST.MF"); -// Files.createDirectories(manifestP.getParent()); -// try (OutputStream out = Files.newOutputStream(manifestP)) { -// manifest.write(out); -// } -// -// // Load manifest -// Path manifestP = compiled.resolve("META-INF/MANIFEST.MF"); -// if (!Files.exists(manifestP)) -// throw new IllegalStateException("Manifest " + manifestP + " not found"); -// Manifest manifest; -// try (InputStream in = Files.newInputStream(manifestP)) { -// manifest = new Manifest(in); -// } catch (IOException e) { -// throw new IllegalStateException("Cannot read manifest " + manifestP, e); -// } - - // Load excludes - List excludes = new ArrayList<>(); - Path excludesP = argeoBuildBase.resolve("excludes.txt"); - for (String line : Files.readAllLines(excludesP)) { - PathMatcher pathMatcher = excludesP.getFileSystem().getPathMatcher("glob:" + line); - excludes.add(pathMatcher); - } - - Path jarP = a2Output.resolve(category).resolve(compiled.getFileName() + "." + major + "." + minor + ".jar"); - Files.createDirectories(jarP.getParent()); - - try (JarOutputStream jarOut = new JarOutputStream(Files.newOutputStream(jarP), manifest)) { - // add all classes first -// Path binP = compiled.resolve("bin"); - Files.walkFileTree(binP, new SimpleFileVisitor() { - - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - jarOut.putNextEntry(new JarEntry(binP.relativize(file).toString())); - Files.copy(file, jarOut); - return FileVisitResult.CONTINUE; - } - }); - - // add resources - Files.walkFileTree(source, new SimpleFileVisitor() { - - @Override - public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { - Path relativeP = source.relativize(dir); - for (PathMatcher exclude : excludes) - if (exclude.matches(relativeP)) - return FileVisitResult.SKIP_SUBTREE; - - return FileVisitResult.CONTINUE; - } - - @Override - public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { - Path relativeP = source.relativize(file); - for (PathMatcher exclude : excludes) - if (exclude.matches(relativeP)) - return FileVisitResult.CONTINUE; - JarEntry entry = new JarEntry(relativeP.toString()); - jarOut.putNextEntry(entry); - 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); - return FileVisitResult.CONTINUE; - } - }); - - } - - } - - private Path findSdkMk(Path directory) { - Path sdkMkP = directory.resolve(SDK_MK); - if (Files.exists(sdkMkP)) { - return sdkMkP.toAbsolutePath(); - } - if (directory.getParent() == null) - return null; - return findSdkMk(directory.getParent()); - - } - - public static void main(String... args) throws IOException { - 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]); - } - } - - Make argeoMake = new Make(); - switch (action) { - case "compile" -> argeoMake.compile(options); - case "bundle" -> argeoMake.bundle(options); - case "all" -> { - argeoMake.compile(options); - argeoMake.bundle(options); - } - default -> throw new IllegalArgumentException("Unkown action: " + action); - } - - } -} diff --git a/osgi.mk b/osgi.mk index 45f313e..cef7609 100644 --- a/osgi.mk +++ b/osgi.mk @@ -9,47 +9,56 @@ ECJ_JAR := $(A2_BASE)/org.argeo.tp.sdk/org.eclipse.jdt.core.compiler.batch.3.29. BNDLIB_JAR := $(A2_BASE)/org.argeo.tp.sdk/biz.aQute.bndlib.5.3.jar SLF4J_API_JAR := $(A2_BASE)/org.argeo.tp/org.slf4j.api.1.7.jar -ARGEO_MAKE := $(JVM) -cp $(ECJ_JAR):$(BNDLIB_JAR):$(SLF4J_API_JAR) $(SDK_SRC_BASE)/sdk/argeo-build/java/org/argeo/build/Make.java +#ARGEO_MAKE = $(JVM) -cp $(ECJ_JAR):$(BNDLIB_JAR):$(SLF4J_API_JAR):$(BUILD_BASE)/bin org/argeo/build/Make +ARGEO_MAKE := $(JVM) -cp $(ECJ_JAR):$(BNDLIB_JAR):$(SLF4J_API_JAR) $(SDK_SRC_BASE)/sdk/argeo-build/src/org/argeo/build/Make.java #BND_TOOL := /usr/bin/bnd BUILD_BASE = $(SDK_BUILD_BASE)/$(shell basename $(SDK_SRC_BASE)) -WORKSPACE_BNDS := $(shell cd $(SDK_SRC_BASE) && find cnf -name '*.bnd') sdk/argeo-build/argeo.bnd -BUILD_WORKSPACE_BNDS := $(WORKSPACE_BNDS:%=$(BUILD_BASE)/%) +#WORKSPACE_BNDS := $(shell cd $(SDK_SRC_BASE) && find cnf -name '*.bnd') sdk/argeo-build/argeo.bnd +#BUILD_WORKSPACE_BNDS := $(WORKSPACE_BNDS:%=$(BUILD_BASE)/%) -A2_JARS = $(foreach category, $(DEP_CATEGORIES), $(shell find $(A2_BASE)/$(category) -name '*.jar')) -A2_CLASSPATH = $(subst $(space),$(pathsep),$(strip $(A2_JARS))) +#A2_JARS = $(foreach category, $(DEP_CATEGORIES), $(shell find $(A2_BASE)/$(category) -name '*.jar')) +#A2_CLASSPATH = $(subst $(space),$(pathsep),$(strip $(A2_JARS))) -A2_BUNDLES = $(foreach bundle, $(BUNDLES),$(A2_OUTPUT)/$(A2_CATEGORY)/$(shell basename $(bundle)).$(MAJOR).$(MINOR).jar) +#A2_BUNDLES = $(foreach bundle, $(BUNDLES),$(A2_OUTPUT)/$(A2_CATEGORY)/$(shell basename $(bundle)).$(MAJOR).$(MINOR).jar) -JAVA_SRCS = $(foreach bundle, $(BUNDLES), $(shell find $(bundle) -name '*.java')) -BNDS = $(foreach bundle, $(BUNDLES), $(BUILD_BASE)/$(shell basename $(bundle))/bnd.bnd) -ECJ_SRCS = $(foreach bundle, $(BUNDLES), $(bundle)/src[-d $(BUILD_BASE)/$(bundle)/bin]) +#JAVA_SRCS = $(foreach bundle, $(BUNDLES), $(shell find $(bundle) -name '*.java')) +#BNDS = $(foreach bundle, $(BUNDLES), $(BUILD_BASE)/$(shell basename $(bundle))/bnd.bnd) +#ECJ_SRCS = $(foreach bundle, $(BUNDLES), $(bundle)/src[-d $(BUILD_BASE)/$(bundle)/bin]) JAVADOC_SRCS = $(foreach bundle, $(JAVADOC_BUNDLES),$(bundle)/src) -osgi: $(BUILD_BASE)/jars-built +osgi: $(BUILD_BASE)/built javadoc: $(BUILD_BASE)/java-compiled $(JAVADOC) -d $(BUILD_BASE)/api --source-path $(subst $(space),$(pathsep),$(strip $(JAVADOC_SRCS))) -subpackages $(JAVADOC_PACKAGES) -# SDK level -#$(BUILD_BASE)/cnf/%.bnd: cnf/%.bnd -# mkdir -p $(dir $@) -# cp $< $@ - -#$(BUILD_BASE)/sdk/argeo-build/%.bnd: sdk/argeo-build/%.bnd -# mkdir -p $(dir $@) -# cp $< $@ - -#$(A2_OUTPUT)/$(A2_CATEGORY)/%.$(MAJOR).$(MINOR).jar : $(BUILD_BASE)/jars-built: -# touch $@ -# mkdir -p $(dir $@) -# cp $< $@ +TARGET_BUNDLES := $(abspath $(foreach bundle, $(BUNDLES),$(A2_OUTPUT)/$(shell dirname $(bundle))/$(A2_CATEGORY)/$(shell basename $(bundle)).$(MAJOR).$(MINOR).jar)) +TODOS := $(foreach bundle, $(BUNDLES),$(BUILD_BASE)/$(bundle)/to-build) + +.SECONDEXPANSION: + +$(BUILD_BASE)/built : BUNDLES_TO_BUILD = $(subst $(abspath $(BUILD_BASE))/,, $(subst to-build,, $?)) +$(BUILD_BASE)/built : $(TODOS) + $(ARGEO_MAKE) all --a2-bases $(A2_BASE) --dep-categories $(DEP_CATEGORIES) --category $(A2_CATEGORY) --bundles $(BUNDLES_TO_BUILD) + touch $(BUILD_BASE)/built + +$(A2_OUTPUT)/%.$(MAJOR).$(MINOR).jar : $(BUILD_BASE)/$$(subst $(A2_CATEGORY)/,,$$*)/to-build + $(ARGEO_MAKE) all --a2-bases $(A2_BASE) --dep-categories $(DEP_CATEGORIES) --category $(A2_CATEGORY) --bundles $(subst $(A2_CATEGORY)/,,$*) + +$(BUILD_BASE)/%/to-build : $$(shell find $(SDK_SRC_BASE)/% -type f -not -path 'bin/*' -not -path '*/MANIFEST.MF') + @rm -rf $(dir $@) + @mkdir -p $(dir $@) + @touch $@ -#$(BUILD_BASE)/%.jar: $(BUILD_BASE)/jars-built - #mv $(basename $@)/generated/*.jar $(basename $@).jar +$(BUILD_BASE)/%/sources-modified : $$(shell find $(SDK_SRC_BASE)/$$* -name '*.java') + touch $@ + +$(BUILD_BASE)/%/java-compiled: $(BUILD_BASE)/%/sources-modified + $(ARGEO_MAKE) compile --a2-bases $(A2_BASE) --dep-categories $(DEP_CATEGORIES) --bundles $(BUNDLES) + touch $@ # Build level $(BUILD_BASE)/jars-built: $(BUILD_BASE)/java-compiled @@ -59,14 +68,20 @@ $(BUILD_BASE)/jars-built: $(BUILD_BASE)/java-compiled $(BUILD_BASE)/java-compiled : $(JAVA_SRCS) $(ARGEO_MAKE) compile --a2-bases $(A2_BASE) --dep-categories $(DEP_CATEGORIES) --bundles $(BUNDLES) touch $@ - -argeo-all: - $(ARGEO_MAKE) all --a2-bases $(A2_BASE) --dep-categories $(DEP_CATEGORIES) --category $(A2_CATEGORY) --bundles $(BUNDLES) + +clean-a2 : + rm -rf $(TARGET_BUNDLES) + rm -rf $(BUILD_BASE)/built # Local manifests manifests : osgi - $(foreach bundle, $(BUNDLES), mkdir -p $(bundle)/META-INF/;) - $(foreach bundle, $(BUNDLES), cp -v $(BUILD_BASE)/$(shell basename $(bundle))/META-INF/MANIFEST.MF $(bundle)/META-INF/MANIFEST.MF;) + @mkdir -p $(foreach bundle, $(BUNDLES), $(bundle)/META-INF/); + @$(foreach bundle, $(BUNDLES), cp -v $(BUILD_BASE)/$(bundle)/META-INF/MANIFEST.MF $(bundle)/META-INF/MANIFEST.MF;) + +builder: $(BUILD_BASE)/bin/org/argeo/build/Make.class + +$(BUILD_BASE)/bin/org/argeo/build/Make.class : $(SDK_SRC_BASE)/sdk/argeo-build/java/org/argeo/build/Make.java + $(JVM) -jar $(ECJ_JAR) -cp $(ECJ_JAR):$(BNDLIB_JAR):$(SLF4J_API_JAR) @$(SDK_SRC_BASE)/sdk/argeo-build/ecj.args $(SDK_SRC_BASE)/sdk/argeo-build/src[-d $(BUILD_BASE)/bin] null := space := $(null) # diff --git a/src/org/argeo/build/Make.java b/src/org/argeo/build/Make.java new file mode 100644 index 0000000..7e29e14 --- /dev/null +++ b/src/org/argeo/build/Make.java @@ -0,0 +1,399 @@ +package org.argeo.build; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.lang.management.ManagementFactory; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.PathMatcher; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Properties; +import java.util.StringJoiner; +import java.util.StringTokenizer; +import java.util.jar.JarEntry; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; + +import org.eclipse.jdt.core.compiler.CompilationProgress; + +import aQute.bnd.osgi.Analyzer; +import aQute.bnd.osgi.Jar; + +public class Make { + private final static String SDK_MK = "sdk.mk"; + + final Path execDirectory; + final Path sdkSrcBase; + final Path argeoBuildBase; + final Path sdkBuildBase; + final Path buildBase; + final Path a2Output; + + public Make() throws IOException { + execDirectory = Paths.get(System.getProperty("user.dir")); + 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); + } + + sdkSrcBase = Paths.get(context.computeIfAbsent("SDK_SRC_BASE", (key) -> { + throw new IllegalStateException(key + " not found"); + })).toAbsolutePath(); + argeoBuildBase = sdkSrcBase.resolve("sdk/argeo-build"); + + sdkBuildBase = Paths.get(context.computeIfAbsent("SDK_BUILD_BASE", (key) -> { + throw new IllegalStateException(key + " not found"); + })).toAbsolutePath(); + buildBase = sdkBuildBase.resolve(sdkSrcBase.getFileName()); + a2Output = sdkBuildBase.resolve("a2"); + } + + /* + * ACTIONS + */ + + void all(Map> options) throws IOException { +// List a2Bundles = options.get("--a2-bundles"); +// if (a2Bundles == null) +// throw new IllegalArgumentException("--a2-bundles must be specified"); +// List bundles = new ArrayList<>(); +// for (String a2Bundle : a2Bundles) { +// Path a2BundleP = Paths.get(a2Bundle); +// Path bundleP = a2Output.relativize(a2BundleP.getParent().getParent().resolve(a2BundleP.getFileName())); +// bundles.add(bundleP.toString()); +// } +// options.put("--bundles", bundles); + compile(options); + bundle(options); + } + + @SuppressWarnings("restriction") + void compile(Map> options) throws IOException { + List bundles = options.get("--bundles"); + Objects.requireNonNull(bundles, "--bundles argument must be set"); + if (bundles.isEmpty()) + return; + + List a2Categories = options.getOrDefault("--dep-categories", new ArrayList<>()); + List a2Bases = options.getOrDefault("--a2-bases", new ArrayList<>()); + if (a2Bases.isEmpty()) { + a2Bases.add(a2Output.toString()); + } + + List compilerArgs = new ArrayList<>(); + + Path ecjArgs = argeoBuildBase.resolve("ecj.args"); + compilerArgs.add("@" + ecjArgs); + + // classpath + if (!a2Categories.isEmpty()) { + compilerArgs.add("-cp"); + StringJoiner classPath = new StringJoiner(File.pathSeparator); + for (String a2Base : a2Bases) { + for (String a2Category : a2Categories) { + Path a2Dir = Paths.get(a2Base).resolve(a2Category); + for (Path jarP : Files.newDirectoryStream(a2Dir, + (p) -> p.getFileName().toString().endsWith(".jar"))) { + classPath.add(jarP.toString()); + } + } + } + compilerArgs.add(classPath.toString()); + } + + // sources + for (String bundle : bundles) { + StringBuilder sb = new StringBuilder(); + sb.append(sdkSrcBase.resolve(bundle).resolve("src")); + sb.append("[-d"); + compilerArgs.add(sb.toString()); + sb = new StringBuilder(); + sb.append(buildBase.resolve(bundle).resolve("bin")); + sb.append("]"); + compilerArgs.add(sb.toString()); + } + + // System.out.println(compilerArgs); + + CompilationProgress compilationProgress = new CompilationProgress() { + int totalWork; + long currentChunk = 0; + + long chunksCount = 80; + + @Override + public void worked(int workIncrement, int remainingWork) { + long chunk = ((totalWork - remainingWork) * chunksCount) / totalWork; + if (chunk != currentChunk) { + currentChunk = chunk; + for (long i = 0; i < currentChunk; i++) { + System.out.print("#"); + } + for (long i = currentChunk; i < chunksCount; i++) { + System.out.print("-"); + } + System.out.print("\r"); + } + if (remainingWork == 0) + System.out.print("\n"); + } + + @Override + public void setTaskName(String name) { + } + + @Override + public boolean isCanceled() { + return false; + } + + @Override + public void done() { + } + + @Override + public void begin(int remainingWork) { + this.totalWork = remainingWork; + } + }; + // Use Main instead of BatchCompiler to workaround the fact that + // org.eclipse.jdt.core.compiler.batch is not exported + boolean success = org.eclipse.jdt.internal.compiler.batch.Main.compile( + compilerArgs.toArray(new String[compilerArgs.size()]), new PrintWriter(System.out), + new PrintWriter(System.err), (CompilationProgress) compilationProgress); + if (!success) { + System.exit(1); + } + } + + void bundle(Map> options) throws IOException { + List bundles = options.get("--bundles"); + Objects.requireNonNull(bundles, "--bundles argument must be set"); + if (bundles.isEmpty()) + return; + + List categories = options.get("--category"); + Objects.requireNonNull(bundles, "--bundles argument must be set"); + if (categories.size() != 1) + 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); + } + } + + /* + * JAR PACKAGING + */ + void createBundle(String bundle, String category) throws IOException { + Path source = sdkSrcBase.resolve(bundle); + Path compiled = buildBase.resolve(bundle); + String bundleSymbolicName = source.getFileName().toString(); + + // Metadata + Properties properties = new Properties(); + Path argeoBnd = argeoBuildBase.resolve("argeo.bnd"); + try (InputStream in = Files.newInputStream(argeoBnd)) { + properties.load(in); + } + // FIXME make it configurable + Path branchBnd = sdkSrcBase.resolve("cnf/unstable.bnd"); + try (InputStream in = Files.newInputStream(branchBnd)) { + properties.load(in); + } + + Path bndBnd = source.resolve("bnd.bnd"); + try (InputStream in = Files.newInputStream(bndBnd)) { + properties.load(in); + } + + // Normalise + properties.put("Bundle-SymbolicName", bundleSymbolicName); + + // Calculate MANIFEST + Path binP = compiled.resolve("bin"); + Manifest manifest; + try (Analyzer bndAnalyzer = new Analyzer()) { + bndAnalyzer.setProperties(properties); + Jar jar = new Jar(bundleSymbolicName, binP.toFile()); + bndAnalyzer.setJar(jar); + manifest = bndAnalyzer.calcManifest(); + +// keys: for (Object key : manifest.getMainAttributes().keySet()) { +// System.out.println(key + ": " + manifest.getMainAttributes().getValue(key.toString())); +// } + } catch (Exception e) { + 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"); + + // Write manifest + Path manifestP = compiled.resolve("META-INF/MANIFEST.MF"); + Files.createDirectories(manifestP.getParent()); + try (OutputStream out = Files.newOutputStream(manifestP)) { + manifest.write(out); + } +// +// // Load manifest +// Path manifestP = compiled.resolve("META-INF/MANIFEST.MF"); +// if (!Files.exists(manifestP)) +// throw new IllegalStateException("Manifest " + manifestP + " not found"); +// Manifest manifest; +// try (InputStream in = Files.newInputStream(manifestP)) { +// manifest = new Manifest(in); +// } catch (IOException e) { +// throw new IllegalStateException("Cannot read manifest " + manifestP, e); +// } + + // Load excludes + List excludes = new ArrayList<>(); + Path excludesP = argeoBuildBase.resolve("excludes.txt"); + for (String line : Files.readAllLines(excludesP)) { + PathMatcher pathMatcher = excludesP.getFileSystem().getPathMatcher("glob:" + line); + excludes.add(pathMatcher); + } + + Path bundleParent = Paths.get(bundle).getParent(); + Path a2JarDirectory = bundleParent != null ? a2Output.resolve(bundleParent).resolve(category) + : a2Output.resolve(category); + Path jarP = a2JarDirectory.resolve(compiled.getFileName() + "." + major + "." + minor + ".jar"); + Files.createDirectories(jarP.getParent()); + + try (JarOutputStream jarOut = new JarOutputStream(Files.newOutputStream(jarP), manifest)) { + // add all classes first +// Path binP = compiled.resolve("bin"); + Files.walkFileTree(binP, new SimpleFileVisitor() { + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + jarOut.putNextEntry(new JarEntry(binP.relativize(file).toString())); + Files.copy(file, jarOut); + return FileVisitResult.CONTINUE; + } + }); + + // add resources + Files.walkFileTree(source, new SimpleFileVisitor() { + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + Path relativeP = source.relativize(dir); + for (PathMatcher exclude : excludes) + if (exclude.matches(relativeP)) + return FileVisitResult.SKIP_SUBTREE; + + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Path relativeP = source.relativize(file); + for (PathMatcher exclude : excludes) + if (exclude.matches(relativeP)) + return FileVisitResult.CONTINUE; + JarEntry entry = new JarEntry(relativeP.toString()); + jarOut.putNextEntry(entry); + 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); + return FileVisitResult.CONTINUE; + } + }); + + } + + } + + private Path findSdkMk(Path directory) { + Path sdkMkP = directory.resolve(SDK_MK); + if (Files.exists(sdkMkP)) { + return sdkMkP.toAbsolutePath(); + } + if (directory.getParent() == null) + return null; + return findSdkMk(directory.getParent()); + + } + + 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]); + } + } + + Make argeoMake = new Make(); + switch (action) { + case "compile" -> argeoMake.compile(options); + case "bundle" -> argeoMake.bundle(options); + case "all" -> argeoMake.all(options); + + default -> throw new IllegalArgumentException("Unkown action: " + action); + } + + long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime(); + System.out.println("Completed after " + jvmUptime + " ms"); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } +}