diff --git a/.github/native-tests.json b/.github/native-tests.json index dbe32b8def327..bf7de09a2cc2b 100644 --- a/.github/native-tests.json +++ b/.github/native-tests.json @@ -133,9 +133,9 @@ "os-name": "ubuntu-latest" }, { - "category": "Windows - RESTEasy Jackson", - "timeout": 25, - "test-modules": "resteasy-jackson", + "category": "Windows support", + "timeout": 50, + "test-modules": "resteasy-jackson, qute", "os-name": "windows-latest" }, { diff --git a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java index 7353f30506eaa..43cb4659a1634 100644 --- a/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java +++ b/extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/QuteProcessor.java @@ -38,7 +38,6 @@ import java.util.function.Predicate; import java.util.regex.Pattern; import java.util.stream.Collectors; -import java.util.stream.Stream; import jakarta.inject.Singleton; @@ -93,12 +92,15 @@ import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem; import io.quarkus.deployment.pkg.NativeConfig; import io.quarkus.deployment.pkg.builditem.CurateOutcomeBuildItem; -import io.quarkus.fs.util.ZipUtils; import io.quarkus.gizmo.ClassOutput; import io.quarkus.gizmo.MethodDescriptor; -import io.quarkus.maven.dependency.Dependency; +import io.quarkus.maven.dependency.ArtifactKey; +import io.quarkus.maven.dependency.DependencyFlags; import io.quarkus.maven.dependency.ResolvedDependency; import io.quarkus.panache.common.deployment.PanacheEntityClassesBuildItem; +import io.quarkus.paths.FilteredPathTree; +import io.quarkus.paths.PathFilter; +import io.quarkus.paths.PathTree; import io.quarkus.qute.CheckedTemplate; import io.quarkus.qute.Engine; import io.quarkus.qute.EngineBuilder; @@ -2124,9 +2126,6 @@ void collectTemplates(ApplicationArchivesBuildItem applicationArchives, QuteConfig config, TemplateRootsBuildItem templateRoots) throws IOException { - Set allApplicationArchives = applicationArchives.getAllApplicationArchives(); - List extensionArtifacts = curateOutcome.getApplicationModel().getDependencies().stream() - .filter(Dependency::isRuntimeExtensionArtifact).collect(Collectors.toList()); // Make sure the new templates are watched as well watchedPaths.produce(HotDeploymentWatchedFileBuildItem.builder().setLocationPredicate(new Predicate() { @@ -2141,80 +2140,39 @@ public boolean test(String path) { } }).build()); - for (ResolvedDependency artifact : extensionArtifacts) { - if (isApplicationArchive(artifact, allApplicationArchives)) { - // Skip extension archives that are also application archives - continue; - } - for (Path resolvedPath : artifact.getResolvedPaths()) { - if (Files.isDirectory(resolvedPath)) { - scanRootPath(resolvedPath, config, templateRoots, watchedPaths, templatePaths, - nativeImageResources); - } else { - try (FileSystem artifactFs = ZipUtils.newFileSystem(resolvedPath)) { - // Iterate over template roots, such as "templates", and collect the included templates - for (String templateRoot : templateRoots) { - Path artifactBasePath = artifactFs.getPath(templateRoot); - if (Files.exists(artifactBasePath)) { - LOGGER.debugf("Found template root in extension artifact: %s", resolvedPath); - scanDirectory(artifactBasePath, artifactBasePath, templateRoot + "/", watchedPaths, - templatePaths, - nativeImageResources, - config); - } - } - } catch (IOException e) { - LOGGER.warnf(e, "Unable to create the file system from the path: %s", resolvedPath); - } - } + final Set allApplicationArchives = applicationArchives.getAllApplicationArchives(); + final Set appArtifactKeys = new HashSet<>(allApplicationArchives.size()); + for (var archive : allApplicationArchives) { + appArtifactKeys.add(archive.getKey()); + } + for (ResolvedDependency artifact : curateOutcome.getApplicationModel() + .getDependencies(DependencyFlags.RUNTIME_EXTENSION_ARTIFACT)) { + // Skip extension archives that are also application archives + if (!appArtifactKeys.contains(artifact.getKey())) { + scanPathTree(artifact.getContentTree(), templateRoots, watchedPaths, templatePaths, nativeImageResources, + config); } } for (ApplicationArchive archive : allApplicationArchives) { - archive.accept(tree -> { - for (Path root : tree.getRoots()) { - // Note that we cannot use ApplicationArchive.getChildPath(String) here because we would not be able to detect - // a wrong directory name on case-insensitive file systems - scanRootPath(root, config, templateRoots, watchedPaths, templatePaths, nativeImageResources); - } - }); + archive.accept( + tree -> scanPathTree(tree, templateRoots, watchedPaths, templatePaths, nativeImageResources, config)); } } - private void scanRootPath(Path rootPath, QuteConfig config, TemplateRootsBuildItem templateRoots, + private void scanPathTree(PathTree pathTree, TemplateRootsBuildItem templateRoots, BuildProducer watchedPaths, BuildProducer templatePaths, - BuildProducer nativeImageResources) { - scanRootPath(rootPath, rootPath, config, templateRoots, watchedPaths, templatePaths, nativeImageResources); - } - - private void scanRootPath(Path rootPath, Path path, QuteConfig config, TemplateRootsBuildItem templateRoots, - BuildProducer watchedPaths, - BuildProducer templatePaths, - BuildProducer nativeImageResources) { - if (!Files.isDirectory(path)) { - return; - } - try (Stream paths = Files.list(path)) { - for (Path file : paths.collect(Collectors.toList())) { - if (Files.isDirectory(file)) { - // Iterate over the directories in the root - // "/io", "/META-INF", "/templates", "/web", etc. - Path relativePath = rootPath.relativize(file); - if (templateRoots.isRoot(relativePath)) { - LOGGER.debugf("Found templates root dir: %s", file); - // The base path is an OS-specific template root path relative to the scanned root path - String basePath = relativePath.toString() + relativePath.getFileSystem().getSeparator(); - scanDirectory(file, file, basePath, watchedPaths, templatePaths, - nativeImageResources, - config); - } else if (templateRoots.maybeRoot(relativePath)) { - // Scan the path recursively because the template root may be nested, for example "/web/public" - scanRootPath(rootPath, file, config, templateRoots, watchedPaths, templatePaths, nativeImageResources); - } + BuildProducer nativeImageResources, + QuteConfig config) { + for (String templateRoot : templateRoots) { + pathTree.accept(templateRoot, visit -> { + if (visit != null) { + // if template root is found in this tree then walk over its subtree + scanTemplateRootSubtree( + new FilteredPathTree(pathTree, PathFilter.forIncludes(List.of(templateRoot + "/**"))), + visit.getPath(), watchedPaths, templatePaths, nativeImageResources, config); } - } - } catch (IOException e) { - throw new UncheckedIOException(e); + }); } } @@ -3397,85 +3355,52 @@ public static String getName(InjectionPointInfo injectionPoint) { * @param templatePaths * @param watchedPaths * @param nativeImageResources - * @param osSpecificResourcePath The OS-specific resource path, i.e. templates\nested\foo.html + * @param resourcePath The relative resource path, including the template root * @param templatePath The path relative to the template root; using the {@code /} path separator * @param originalPath * @param config */ private static void produceTemplateBuildItems(BuildProducer templatePaths, BuildProducer watchedPaths, - BuildProducer nativeImageResources, String osSpecificResourcePath, + BuildProducer nativeImageResources, String resourcePath, String templatePath, Path originalPath, QuteConfig config) { if (templatePath.isEmpty()) { return; } - // OS-agnostic full path, i.e. templates/foo.html - String osAgnosticResourcePath = toOsAgnosticPath(osSpecificResourcePath, originalPath.getFileSystem()); LOGGER.debugf("Produce template build items [templatePath: %s, osSpecificResourcePath: %s, originalPath: %s", templatePath, - osSpecificResourcePath, + resourcePath, originalPath); boolean restartNeeded = true; if (config.devMode.noRestartTemplates.isPresent()) { - restartNeeded = !config.devMode.noRestartTemplates.get().matcher(osAgnosticResourcePath).matches(); + restartNeeded = !config.devMode.noRestartTemplates.get().matcher(resourcePath).matches(); } - watchedPaths.produce(new HotDeploymentWatchedFileBuildItem(osAgnosticResourcePath, restartNeeded)); - nativeImageResources.produce(new NativeImageResourceBuildItem(osSpecificResourcePath)); + watchedPaths.produce(new HotDeploymentWatchedFileBuildItem(resourcePath, restartNeeded)); + nativeImageResources.produce(new NativeImageResourceBuildItem(resourcePath)); templatePaths.produce( new TemplatePathBuildItem(templatePath, originalPath, readTemplateContent(originalPath, config.defaultCharset))); } - /** - * - * @param root - * @param directory - * @param basePath OS-specific template root path relative to the scanned root path, e.g. {@code templates/} - * @param watchedPaths - * @param templatePaths - * @param nativeImageResources - * @param config - * @throws IOException - */ - private void scanDirectory(Path root, Path directory, String basePath, + private void scanTemplateRootSubtree(PathTree pathTree, Path templateRoot, BuildProducer watchedPaths, BuildProducer templatePaths, BuildProducer nativeImageResources, - QuteConfig config) - throws IOException { - try (Stream files = Files.list(directory)) { - Iterator iter = files.iterator(); - while (iter.hasNext()) { - Path filePath = iter.next(); - /* - * Fix for https://github.com/quarkusio/quarkus/issues/25751 where running tests in Eclipse - * sometimes produces `/templates/tags` (absolute) files listed for `templates` (relative) - * directories, so we work around this - */ - if (!directory.isAbsolute() - && filePath.isAbsolute() - && filePath.getRoot() != null) { - filePath = filePath.getRoot().relativize(filePath); - } - if (Files.isRegularFile(filePath)) { - LOGGER.debugf("Found template: %s", filePath); - Path relativePath = root.relativize(filePath); - String templatePath = toOsAgnosticPath(relativePath); - if (config.templatePathExclude.matcher(templatePath).matches()) { - LOGGER.debugf("Template file excluded: %s", filePath); - continue; - } - produceTemplateBuildItems(templatePaths, watchedPaths, nativeImageResources, - basePath + relativePath.toString(), - templatePath, - filePath, config); - } else if (Files.isDirectory(filePath)) { - LOGGER.debugf("Scan directory: %s", filePath); - scanDirectory(root, filePath, basePath, watchedPaths, templatePaths, nativeImageResources, config); - } + QuteConfig config) { + pathTree.walk(visit -> { + if (Files.isRegularFile(visit.getPath())) { + LOGGER.debugf("Found template: %s", visit.getPath()); + String templatePath = toOsAgnosticPath(templateRoot.relativize(visit.getPath())); + if (config.templatePathExclude.matcher(templatePath).matches()) { + LOGGER.debugf("Template file excluded: %s", visit.getPath()); + return; + } + produceTemplateBuildItems(templatePaths, watchedPaths, nativeImageResources, + visit.getRelativePath("/"), + templatePath, visit.getPath(), config); } - } + }); } private static String toOsAgnosticPath(String path, FileSystem fs) { @@ -3518,19 +3443,6 @@ private void checkDuplicatePaths(List templatePaths) { } } - private boolean isApplicationArchive(ResolvedDependency dependency, Set applicationArchives) { - for (ApplicationArchive archive : applicationArchives) { - if (archive.getKey() == null) { - continue; - } - if (dependency.getGroupId().equals(archive.getKey().getGroupId()) - && dependency.getArtifactId().equals(archive.getKey().getArtifactId())) { - return true; - } - } - return false; - } - static String readTemplateContent(Path path, Charset defaultCharset) { try { return Files.readString(path, defaultCharset); diff --git a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/templateroot/AdditionalTemplateRootTest.java b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/templateroot/AdditionalTemplateRootTest.java index 9095a01599387..37b3606d7a096 100644 --- a/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/templateroot/AdditionalTemplateRootTest.java +++ b/extensions/qute/deployment/src/test/java/io/quarkus/qute/deployment/templateroot/AdditionalTemplateRootTest.java @@ -51,11 +51,8 @@ public void execute(BuildContext context) { List items = context.consumeMulti(NativeImageResourceBuildItem.class); for (NativeImageResourceBuildItem item : items) { if (item.getResources().contains("web/public/hello.txt") - || item.getResources().contains("web\\public\\hello.txt") || item.getResources().contains("templates/hi.txt") - || item.getResources().contains("templates\\hi.txt") - || item.getResources().contains("templates/nested/hoho.txt") - || item.getResources().contains("templates\\nested\\hoho.txt")) { + || item.getResources().contains("templates/nested/hoho.txt")) { found++; } }