Repository: quarkusio/quarkus
Issue: ArchiveRootBuildItem should include archive location and the archive root paths

It's currently initialized with a single path, which is practically application classes directory. However, when an instance of `ApplicationArchiveImpl` is created using `ArchiveRootBuildItem`, it expects two different arguments: one representing a path to the archive, the other one - app classes dir. It works in case the app archive is represented with the app classes dir but if the app archive is an actual archive it's not going to work.

NOTE: this is about enabling support for building Quarkus applications from user apps provided as JARs. However, I am being careful here not to imply that it's all that's needed to enable that. This change will definitely allow testing and building native images which is my primary focus at the moment. Building proper runner jars will require a bit more work.

Changed files:
core/deployment/src/main/java/io/quarkus/deployment/QuarkusAugmentor.java
core/deployment/src/main/java/io/quarkus/deployment/builditem/ArchiveRootBuildItem.java
core/deployment/src/main/java/io/quarkus/deployment/steps/SubstrateSystemPropertiesBuildStep.java
core/deployment/src/main/java/io/quarkus/deployment/index/ApplicationArchiveBuildStep.java
extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java
core/deployment/src/main/java/io/quarkus/deployment/steps/ApplicationIndexBuildStep.java

diff --git a/core/deployment/src/main/java/io/quarkus/deployment/QuarkusAugmentor.java b/core/deployment/src/main/java/io/quarkus/deployment/QuarkusAugmentor.java
index a757a6bc13d..5e96a027466 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/QuarkusAugmentor.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/QuarkusAugmentor.java
@@ -1,5 +1,8 @@
 package io.quarkus.deployment;
 
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -53,6 +56,7 @@ public BuildResult run() throws Exception {
         long time = System.currentTimeMillis();
         log.info("Beginning quarkus augmentation");
         ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
+        FileSystem rootFs = null;
         try {
             Thread.currentThread().setContextClassLoader(classLoader);
 
@@ -82,10 +86,13 @@ public BuildResult run() throws Exception {
 
             BuildChain chain = chainBuilder
                     .build();
+            if (!Files.isDirectory(root)) {
+                rootFs = FileSystems.newFileSystem(root, null);
+            }
             BuildExecutionBuilder execBuilder = chain.createExecutionBuilder("main")
                     .produce(QuarkusConfig.INSTANCE)
                     .produce(liveReloadBuildItem)
-                    .produce(new ArchiveRootBuildItem(root))
+                    .produce(new ArchiveRootBuildItem(root, rootFs == null ? root : rootFs.getPath("/")))
                     .produce(new ClassOutputBuildItem(output))
                     .produce(new ShutdownContextBuildItem())
                     .produce(new LaunchModeBuildItem(launchMode))
@@ -106,6 +113,12 @@ public BuildResult run() throws Exception {
             log.info("Quarkus augmentation completed in " + (System.currentTimeMillis() - time) + "ms");
             return buildResult;
         } finally {
+            if (rootFs != null) {
+                try {
+                    rootFs.close();
+                } catch (Exception e) {
+                }
+            }
             Thread.currentThread().setContextClassLoader(originalClassLoader);
         }
     }
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/builditem/ArchiveRootBuildItem.java b/core/deployment/src/main/java/io/quarkus/deployment/builditem/ArchiveRootBuildItem.java
index 08d3d3fdefd..fa935eb1eee 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/builditem/ArchiveRootBuildItem.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/builditem/ArchiveRootBuildItem.java
@@ -1,18 +1,51 @@
 package io.quarkus.deployment.builditem;
 
+import java.nio.file.Files;
 import java.nio.file.Path;
 
 import io.quarkus.builder.item.SimpleBuildItem;
 
 public final class ArchiveRootBuildItem extends SimpleBuildItem {
 
-    private final Path path;
+    private final Path archiveLocation;
+    private final Path archiveRoot;
 
-    public ArchiveRootBuildItem(Path path) {
-        this.path = path;
+    public ArchiveRootBuildItem(Path appClassesDir) {
+        this(appClassesDir, appClassesDir);
     }
 
+    public ArchiveRootBuildItem(Path archiveLocation, Path archiveRoot) {
+        this.archiveLocation = archiveLocation;
+        if (!Files.isDirectory(archiveRoot)) {
+            throw new IllegalArgumentException(archiveRoot + " does not point to the application classes directory");
+        }
+        this.archiveRoot = archiveRoot;
+    }
+
+    /**
+     * Deprecated in favor of getArhiveLocation()
+     */
+    @Deprecated
     public Path getPath() {
-        return path;
+        return getArchiveLocation();
+    }
+
+    /**
+     * If this archive is a jar file it will return the path to the jar file on the file system,
+     * otherwise it will return the directory that this corresponds to.
+     */
+    public Path getArchiveLocation() {
+        return archiveLocation;
+    }
+
+    /**
+     *
+     * Returns a path representing the archive root. Note that if this is a jar archive this is not the path to the
+     * jar, but rather a path to the root of the mounted {@link com.sun.nio.zipfs.ZipFileSystem}
+     *
+     * @return The archive root.
+     */
+    public Path getArchiveRoot() {
+        return archiveRoot;
     }
 }
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/index/ApplicationArchiveBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/index/ApplicationArchiveBuildStep.java
index a0c7b822282..58c21f6b063 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/index/ApplicationArchiveBuildStep.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/index/ApplicationArchiveBuildStep.java
@@ -83,9 +83,9 @@ ApplicationArchivesBuildItem build(ArchiveRootBuildItem root, ApplicationIndexBu
         }
 
         List<ApplicationArchive> applicationArchives = scanForOtherIndexes(Thread.currentThread().getContextClassLoader(),
-                markerFiles, root.getPath(), additionalApplicationArchiveBuildItem, indexCache);
+                markerFiles, root.getArchiveLocation(), additionalApplicationArchiveBuildItem, indexCache);
         return new ApplicationArchivesBuildItem(
-                new ApplicationArchiveImpl(appindex.getIndex(), root.getPath(), null, false, root.getPath()),
+                new ApplicationArchiveImpl(appindex.getIndex(), root.getArchiveRoot(), null, false, root.getArchiveLocation()),
                 applicationArchives);
     }
 
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/ApplicationIndexBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/ApplicationIndexBuildStep.java
index c052add0a5f..8b6c80447d2 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/steps/ApplicationIndexBuildStep.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/ApplicationIndexBuildStep.java
@@ -21,7 +21,7 @@ public class ApplicationIndexBuildStep {
     ApplicationIndexBuildItem build(ArchiveRootBuildItem root) throws IOException {
 
         Indexer indexer = new Indexer();
-        Files.walkFileTree(root.getPath(), new FileVisitor<Path>() {
+        Files.walkFileTree(root.getArchiveRoot(), new FileVisitor<Path>() {
             @Override
             public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                 return FileVisitResult.CONTINUE;
@@ -51,4 +51,4 @@ public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOEx
         return new ApplicationIndexBuildItem(appIndex);
     }
 
-}
+}
\\ No newline at end of file
diff --git a/core/deployment/src/main/java/io/quarkus/deployment/steps/SubstrateSystemPropertiesBuildStep.java b/core/deployment/src/main/java/io/quarkus/deployment/steps/SubstrateSystemPropertiesBuildStep.java
index 1ca0ca2aac2..63c6b6efdbf 100644
--- a/core/deployment/src/main/java/io/quarkus/deployment/steps/SubstrateSystemPropertiesBuildStep.java
+++ b/core/deployment/src/main/java/io/quarkus/deployment/steps/SubstrateSystemPropertiesBuildStep.java
@@ -26,7 +26,7 @@ SubstrateOutputBuildItem writeNativeProps(ArchiveRootBuildItem root,
             }
             properties.put(i.getKey(), i.getValue());
         }
-        try (FileOutputStream os = new FileOutputStream(new File(root.getPath().toFile(), "native-image.properties"))) {
+        try (FileOutputStream os = new FileOutputStream(new File(root.getArchiveRoot().toFile(), "native-image.properties"))) {
             try (OutputStreamWriter osw = new OutputStreamWriter(os, StandardCharsets.UTF_8)) {
                 properties.store(osw, "Generated properties (do not edit)");
             }
diff --git a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java
index 66a0435caaa..968d98460f6 100644
--- a/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java
+++ b/extensions/hibernate-orm/deployment/src/main/java/io/quarkus/hibernate/orm/deployment/HibernateOrmProcessor.java
@@ -431,7 +431,7 @@ private void handleHibernateORMWithNoPersistenceXml(
                 loadScriptPath
                         .filter(path -> !Files.isDirectory(path))
                         .ifPresent(path -> {
-                            String resourceAsString = root.getPath().relativize(loadScriptPath.get()).toString();
+                            String resourceAsString = root.getArchiveRoot().relativize(loadScriptPath.get()).toString();
                             resourceProducer.produce(new SubstrateResourceBuildItem(resourceAsString));
                             desc.getProperties().setProperty(AvailableSettings.HBM2DDL_IMPORT_FILES, importFile);
                         });