Skip to content

Commit

Permalink
Merge pull request #40158 from mkouba/qute-native-image-windows-fix
Browse files Browse the repository at this point in the history
Qute: ultimate fix for the problem with registering NativeImageResourceBuildItem correctly on Windows
  • Loading branch information
gsmet authored Apr 22, 2024
2 parents 99d6c7b + 202ea6e commit 1ef8ebe
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 144 deletions.
6 changes: 3 additions & 3 deletions .github/native-tests.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -2124,9 +2126,6 @@ void collectTemplates(ApplicationArchivesBuildItem applicationArchives,
QuteConfig config,
TemplateRootsBuildItem templateRoots)
throws IOException {
Set<ApplicationArchive> allApplicationArchives = applicationArchives.getAllApplicationArchives();
List<ResolvedDependency> 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<String>() {
Expand All @@ -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<ApplicationArchive> allApplicationArchives = applicationArchives.getAllApplicationArchives();
final Set<ArtifactKey> 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<HotDeploymentWatchedFileBuildItem> watchedPaths,
BuildProducer<TemplatePathBuildItem> templatePaths,
BuildProducer<NativeImageResourceBuildItem> nativeImageResources) {
scanRootPath(rootPath, rootPath, config, templateRoots, watchedPaths, templatePaths, nativeImageResources);
}

private void scanRootPath(Path rootPath, Path path, QuteConfig config, TemplateRootsBuildItem templateRoots,
BuildProducer<HotDeploymentWatchedFileBuildItem> watchedPaths,
BuildProducer<TemplatePathBuildItem> templatePaths,
BuildProducer<NativeImageResourceBuildItem> nativeImageResources) {
if (!Files.isDirectory(path)) {
return;
}
try (Stream<Path> 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<NativeImageResourceBuildItem> 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);
});
}
}

Expand Down Expand Up @@ -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<TemplatePathBuildItem> templatePaths,
BuildProducer<HotDeploymentWatchedFileBuildItem> watchedPaths,
BuildProducer<NativeImageResourceBuildItem> nativeImageResources, String osSpecificResourcePath,
BuildProducer<NativeImageResourceBuildItem> 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<HotDeploymentWatchedFileBuildItem> watchedPaths,
BuildProducer<TemplatePathBuildItem> templatePaths,
BuildProducer<NativeImageResourceBuildItem> nativeImageResources,
QuteConfig config)
throws IOException {
try (Stream<Path> files = Files.list(directory)) {
Iterator<Path> 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) {
Expand Down Expand Up @@ -3518,19 +3443,6 @@ private void checkDuplicatePaths(List<TemplatePathBuildItem> templatePaths) {
}
}

private boolean isApplicationArchive(ResolvedDependency dependency, Set<ApplicationArchive> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,8 @@ public void execute(BuildContext context) {
List<NativeImageResourceBuildItem> 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++;
}
}
Expand Down

0 comments on commit 1ef8ebe

Please sign in to comment.