]> git.argeo.org Git - cc0/argeo-build.git/blobdiff - src/org/argeo/build/Make.java
Argeo Build can build itself
[cc0/argeo-build.git] / src / org / argeo / build / Make.java
index 56230ea5fb9444634b5b0c9ab843b002b65983c4..78310597c15a4982f03dccbaa2b2ecf35965adb2 100644 (file)
@@ -1,7 +1,9 @@
 package org.argeo.build;
 
+import static java.lang.System.Logger.Level.DEBUG;
 import static java.lang.System.Logger.Level.ERROR;
 import static java.lang.System.Logger.Level.INFO;
+import static java.lang.System.Logger.Level.WARNING;
 
 import java.io.File;
 import java.io.IOException;
@@ -21,12 +23,14 @@ import java.nio.file.attribute.BasicFileAttributes;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
+import java.util.Iterator;
 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.TreeMap;
 import java.util.concurrent.CompletableFuture;
 import java.util.jar.Attributes;
 import java.util.jar.JarEntry;
@@ -52,10 +56,17 @@ public class Make {
        private final static Logger logger = System.getLogger(Make.class.getName());
 
        /**
-        * Environment properties on whether sources should be packaged separately or
+        * Environment variable on whether sources should be packaged separately or
         * integrated in the bundles.
         */
-       private final static String ENV_BUILD_SOURCE_BUNDLES = "BUILD_SOURCE_BUNDLES";
+       private final static String ENV_SOURCE_BUNDLES = "SOURCE_BUNDLES";
+
+       /**
+        * Environment variable to override the default location for the Argeo Build
+        * configuration files. Typically used if Argeo Build has been compiled and
+        * packaged separately.
+        */
+       private final static String ENV_ARGEO_BUILD_CONFIG = "ARGEO_BUILD_CONFIG";
 
        /** Name of the local-specific Makefile (sdk.mk). */
        final static String SDK_MK = "sdk.mk";
@@ -77,13 +88,15 @@ public class Make {
        final Path buildBase;
        /** The base of the a2 output for all layers. */
        final Path a2Output;
+       /** The base of the a2 sources when packages separately. */
+       final Path a2srcOutput;
 
        /** Whether sources should be packaged separately */
        final boolean sourceBundles;
 
        /** Constructor initialises the base directories. */
        public Make() throws IOException {
-               sourceBundles = Boolean.parseBoolean(System.getenv(ENV_BUILD_SOURCE_BUNDLES));
+               sourceBundles = Boolean.parseBoolean(System.getenv(ENV_SOURCE_BUNDLES));
                if (sourceBundles)
                        logger.log(Level.INFO, "Sources will be packaged separately");
 
@@ -95,13 +108,26 @@ public class Make {
                sdkSrcBase = Paths.get(context.computeIfAbsent("SDK_SRC_BASE", (key) -> {
                        throw new IllegalStateException(key + " not found");
                })).toAbsolutePath();
-               argeoBuildBase = sdkSrcBase.resolve("sdk/argeo-build");
+
+               Path argeoBuildBaseT = sdkSrcBase.resolve("sdk/argeo-build");
+               if (!Files.exists(argeoBuildBaseT)) {
+                       String fromEnv = System.getenv(ENV_ARGEO_BUILD_CONFIG);
+                       if (fromEnv != null)
+                               argeoBuildBaseT = Paths.get(fromEnv);
+                       if (fromEnv == null || !Files.exists(argeoBuildBaseT)) {
+                               throw new IllegalStateException(
+                                               "Argeo Build not found. Did you initialise the git submodules or set the "
+                                                               + ENV_ARGEO_BUILD_CONFIG + " environment variable?");
+                       }
+               }
+               argeoBuildBase = argeoBuildBaseT;
 
                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");
+               a2srcOutput = sdkBuildBase.resolve("a2.src");
        }
 
        /*
@@ -123,7 +149,7 @@ public class Make {
 
                List<String> a2Categories = options.getOrDefault("--dep-categories", new ArrayList<>());
                List<String> a2Bases = options.getOrDefault("--a2-bases", new ArrayList<>());
-               if (a2Bases.isEmpty()) {
+               if (a2Bases.isEmpty() || !a2Bases.contains(a2Output.toString())) {
                        a2Bases.add(a2Output.toString());
                }
 
@@ -134,20 +160,39 @@ public class Make {
 
                // classpath
                if (!a2Categories.isEmpty()) {
-                       StringJoiner classPath = new StringJoiner(File.pathSeparator);
-                       StringJoiner modulePath = new StringJoiner(File.pathSeparator);
+                       // We will keep only the highest major.minor
+                       // and order by bundle name, for predictability
+                       Map<String, A2Jar> a2Jars = new TreeMap<>();
+
+//                     StringJoiner modulePath = new StringJoiner(File.pathSeparator);
                        for (String a2Base : a2Bases) {
-                               for (String a2Category : a2Categories) {
+                               categories: for (String a2Category : a2Categories) {
                                        Path a2Dir = Paths.get(a2Base).resolve(a2Category);
                                        if (!Files.exists(a2Dir))
-                                               Files.createDirectories(a2Dir);
-                                       modulePath.add(a2Dir.toString());
+                                               continue categories;
+//                                     modulePath.add(a2Dir.toString());
                                        for (Path jarP : Files.newDirectoryStream(a2Dir,
                                                        (p) -> p.getFileName().toString().endsWith(".jar"))) {
-                                               classPath.add(jarP.toString());
+                                               A2Jar a2Jar = new A2Jar(jarP);
+                                               if (a2Jars.containsKey(a2Jar.name)) {
+                                                       A2Jar current = a2Jars.get(a2Jar.name);
+                                                       if (a2Jar.major > current.major)
+                                                               a2Jars.put(a2Jar.name, a2Jar);
+                                                       else if (a2Jar.major == current.major //
+                                                                       // if minor equals, we take the last one
+                                                                       && a2Jar.minor >= current.minor)
+                                                               a2Jars.put(a2Jar.name, a2Jar);
+                                               } else {
+                                                       a2Jars.put(a2Jar.name, a2Jar);
+                                               }
                                        }
                                }
                        }
+
+                       StringJoiner classPath = new StringJoiner(File.pathSeparator);
+                       for (Iterator<A2Jar> it = a2Jars.values().iterator(); it.hasNext();)
+                               classPath.add(it.next().path.toString());
+
                        compilerArgs.add("-cp");
                        compilerArgs.add(classPath.toString());
 //                     compilerArgs.add("--module-path");
@@ -158,8 +203,14 @@ public class Make {
                for (String bundle : bundles) {
                        StringBuilder sb = new StringBuilder();
                        Path bundlePath = execDirectory.resolve(bundle);
-                       if (!Files.exists(bundlePath))
-                               throw new IllegalArgumentException("Bundle " + bundle + " not found in " + execDirectory);
+                       if (!Files.exists(bundlePath)) {
+                               if (bundles.size() == 1) {
+                                       logger.log(WARNING, "Bundle " + bundle + " not found in " + execDirectory
+                                                       + ", assuming this is this directory, as only one bundle was requested.");
+                                       bundlePath = execDirectory;
+                               } else
+                                       throw new IllegalArgumentException("Bundle " + bundle + " not found in " + execDirectory);
+                       }
                        sb.append(bundlePath.resolve("src"));
                        sb.append("[-d");
                        compilerArgs.add(sb.toString());
@@ -172,8 +223,11 @@ public class Make {
                if (logger.isLoggable(INFO))
                        compilerArgs.add("-time");
 
-//             for (String arg : compilerArgs)
-//                     System.out.println(arg);
+               if (logger.isLoggable(DEBUG)) {
+                       logger.log(DEBUG, "Compiler arguments:");
+                       for (String arg : compilerArgs)
+                               logger.log(DEBUG, arg);
+               }
 
                boolean success = org.eclipse.jdt.core.compiler.batch.BatchCompiler.compile(
                                compilerArgs.toArray(new String[compilerArgs.size()]), new PrintWriter(System.out),
@@ -222,12 +276,17 @@ public class Make {
                logger.log(INFO, "Packaging took " + duration + " ms");
        }
 
-       /*
-        * UTILITIES
-        */
        /** Package a single bundle. */
        void createBundle(String branch, String bundle, String category) throws IOException {
-               Path source = execDirectory.resolve(bundle);
+               final Path source;
+               if (!Files.exists(execDirectory.resolve(bundle))) {
+                       logger.log(WARNING,
+                                       "Bundle " + bundle + " not found in " + execDirectory + ", assuming this is this directory.");
+                       source = execDirectory;
+               } else {
+                       source = execDirectory.resolve(bundle);
+               }
+
                Path compiled = buildBase.resolve(bundle);
                String bundleSymbolicName = source.getFileName().toString();
 
@@ -352,7 +411,11 @@ public class Make {
                        // TODO add effective BND, Eclipse project file, etc., in order to be able to
                        // repackage
                        if (sourceBundles) {
-                               Path srcJarP = a2JarDirectory.resolve(compiled.getFileName() + "." + major + "." + minor + ".src.jar");
+                               Path a2srcJarDirectory = bundleParent != null ? a2srcOutput.resolve(bundleParent).resolve(category)
+                                               : a2srcOutput.resolve(category);
+                               Files.createDirectories(a2srcJarDirectory);
+                               Path srcJarP = a2srcJarDirectory
+                                               .resolve(compiled.getFileName() + "." + major + "." + minor + ".src.jar");
                                Manifest srcManifest = new Manifest();
                                srcManifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
                                srcManifest.getMainAttributes().putValue("Bundle-SymbolicName", bundleSymbolicName + ".src");
@@ -370,6 +433,10 @@ public class Make {
                }
        }
 
+       /*
+        * UTILITIES
+        */
+       /** Add sources to a jar file */
        void copySourcesToJar(Path srcP, JarOutputStream srcJarOut, String prefix) throws IOException {
                Files.walkFileTree(srcP, new SimpleFileVisitor<Path>() {
                        @Override
@@ -462,6 +529,23 @@ public class Make {
                }
        }
 
+       static class A2Jar {
+               final Path path;
+               final String name;
+               final int major;
+               final int minor;
+
+               A2Jar(Path path) {
+                       this.path = path;
+                       String fileName = path.getFileName().toString();
+                       fileName = fileName.substring(0, fileName.lastIndexOf('.'));
+                       minor = Integer.parseInt(fileName.substring(fileName.lastIndexOf('.') + 1));
+                       fileName = fileName.substring(0, fileName.lastIndexOf('.'));
+                       major = Integer.parseInt(fileName.substring(fileName.lastIndexOf('.') + 1));
+                       name = fileName.substring(0, fileName.lastIndexOf('.'));
+               }
+       }
+
        /**
         * An ECJ {@link CompilationProgress} printing a progress bar while compiling.
         */