Skip to content

Commit

Permalink
Record noteworthy build items into extension metadata
Browse files Browse the repository at this point in the history
  • Loading branch information
holly-cummins committed Apr 26, 2024
1 parent a8d3b17 commit 88bdd29
Show file tree
Hide file tree
Showing 16 changed files with 314 additions and 22 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package io.quarkus.builder.item;

public @interface AddToMetadata {
String value();
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
import org.jboss.logging.Logger;
import org.wildfly.common.function.Functions;

import io.quarkus.bootstrap.BootstrapConstants;
import io.quarkus.bootstrap.model.ApplicationModel;
import io.quarkus.builder.BuildChainBuilder;
import io.quarkus.builder.BuildContext;
Expand Down Expand Up @@ -157,7 +158,7 @@ public String getId() {

// the proxy objects used for run time config in the recorders
Map<Class<?>, Object> proxies = new HashMap<>();
for (Class<?> clazz : ServiceUtil.classesNamedIn(classLoader, "META-INF/quarkus-build-steps.list")) {
for (Class<?> clazz : ServiceUtil.classesNamedIn(classLoader, BootstrapConstants.BUILD_STEPS_PATH)) {
try {
result = result.andThen(ExtensionLoader.loadStepsFromClass(clazz, readResult, proxies, bsf));
} catch (Throwable e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.util.HashMap;
import java.util.Map;

import io.quarkus.builder.item.AddToMetadata;
import io.quarkus.builder.item.MultiBuildItem;

/**
Expand All @@ -17,6 +18,7 @@
*
* {@link RunningDevService} helps to manage the lifecycle of the running dev service.
*/
@AddToMetadata("dev-service")
public final class DevServicesResultBuildItem extends MultiBuildItem {

private final String name;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ final public class Constants {
public static final String ANNOTATION_CONFIG_WITH_DEFAULT = "io.smallrye.config.WithDefault";
public static final String ANNOTATION_CONFIG_WITH_UNNAMED_KEY = "io.smallrye.config.WithUnnamedKey";

public static final String ANNOTATION_ADD_BUILD_ITEM_TO_METADATA = "io.quarkus.builder.item.AddToMetadata";

public static final Set<String> SUPPORTED_ANNOTATIONS_TYPES = Set.of(ANNOTATION_BUILD_STEP, ANNOTATION_CONFIG_GROUP,
ANNOTATION_CONFIG_ROOT, ANNOTATION_RECORDER, ANNOTATION_CONFIG_MAPPING);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.quarkus.annotation.processor;

import static io.quarkus.annotation.processor.Constants.ANNOTATION_ADD_BUILD_ITEM_TO_METADATA;
import static io.quarkus.annotation.processor.Constants.ANNOTATION_CONFIG_GROUP;
import static io.quarkus.annotation.processor.Constants.ANNOTATION_CONFIG_MAPPING;
import static javax.lang.model.util.ElementFilter.constructorsIn;
Expand Down Expand Up @@ -54,6 +55,7 @@
import javax.lang.model.type.PrimitiveType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Types;
import javax.tools.Diagnostic;
import javax.tools.FileObject;
import javax.tools.StandardLocation;
Expand Down Expand Up @@ -82,11 +84,13 @@ public class ExtensionAnnotationProcessor extends AbstractProcessor {

private static final Pattern REMOVE_LEADING_SPACE = Pattern.compile("^ ", Pattern.MULTILINE);
private static final String QUARKUS_GENERATED = "io.quarkus.Generated";
public static final String META_INF_NOTEWORTHY_BUILD_ITEMS_LIST = "META-INF/noteworthy-build-items.list";

private final ConfigDocWriter configDocWriter = new ConfigDocWriter();
private final ConfigDocItemScanner configDocItemScanner = new ConfigDocItemScanner();
private final Set<String> generatedAccessors = new ConcurrentHashMap<String, Boolean>().keySet(Boolean.TRUE);
private final Set<String> generatedJavaDocs = new ConcurrentHashMap<String, Boolean>().keySet(Boolean.TRUE);
private final Set<String> noteworthyBuildItems = new ConcurrentHashMap<String, Boolean>().keySet(Boolean.TRUE);
private final boolean generateDocs = !(Boolean.getBoolean("skipDocs") || Boolean.getBoolean("quickly"));

private final Map<String, Boolean> ANNOTATION_USAGE_TRACKER = new ConcurrentHashMap<>();
Expand Down Expand Up @@ -270,6 +274,21 @@ public FileVisitResult postVisitDirectory(final Path dir, final IOException exc)
}
}

if (!noteworthyBuildItems.isEmpty()) {
try {
final FileObject noteworthies = filer.createResource(StandardLocation.CLASS_OUTPUT, Constants.EMPTY,
META_INF_NOTEWORTHY_BUILD_ITEMS_LIST);
Writer writer = noteworthies.openWriter();
for (String o : noteworthyBuildItems) {
writer.append(o);
writer.append("\n");
}
writer.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}

try {
if (generateDocs) {
final Set<ConfigDocGeneratedOutput> outputs = configDocItemScanner
Expand Down Expand Up @@ -357,6 +376,7 @@ private void processBuildStep(RoundEnvironment roundEnv, TypeElement annotation)
.toString();
if (processorClassNames.add(binaryName)) {
validateRecordBuildSteps(clazz);
recordNoteworthyBuildItems(clazz);
recordConfigJavadoc(clazz);
generateAccessor(clazz);
final StringBuilder rbn = getRelativeBinaryName(clazz, new StringBuilder());
Expand Down Expand Up @@ -433,6 +453,64 @@ private void validateRecordBuildSteps(TypeElement clazz) {
}
}

private void recordNoteworthyBuildItems(TypeElement clazz) {
for (Element e : clazz.getEnclosedElements()) {
if (e.getKind() != ElementKind.METHOD) {
continue;
}
ExecutableElement ex = (ExecutableElement) e;
if (!isAnnotationPresent(ex, Constants.ANNOTATION_BUILD_STEP)) {
continue;
}

if (!(e instanceof ExecutableElement)) {
continue;
}

ExecutableElement exel = (ExecutableElement) e;

TypeMirror returned = exel.getReturnType();
if (returned.getKind() != TypeKind.VOID) {
Types typeUtils = processingEnv.getTypeUtils();

List<? extends AnnotationMirror> allAnnotations = typeUtils.asElement(returned)
.getAnnotationMirrors();
Optional<? extends AnnotationMirror> oam = allAnnotations.stream()
.filter(this::isAddMetadataAnnotation)
.findAny();
if (oam.isPresent()) {
AnnotationMirror am = oam.get();

Map<? extends ExecutableElement, ? extends AnnotationValue> elementValues = am.getElementValues();
Optional<? extends Map.Entry<? extends ExecutableElement, ? extends AnnotationValue>> valueEntry = elementValues
.entrySet()
.stream()
.filter(entry -> entry.getKey()
.getSimpleName()
.toString()
.equals("value"))
.findAny();
if (valueEntry.isPresent()) {
String value = valueEntry.get()
.getValue()
.getValue()// First getValue gets from the entry, the second gets from the annotation
.toString();
noteworthyBuildItems.add(value);
}

}
}
}
}

private boolean isAddMetadataAnnotation(AnnotationMirror meth) {
Types typeUtils = processingEnv.getTypeUtils();
TypeElement element = (TypeElement) typeUtils.asElement(meth.getAnnotationType());
String name = element.getQualifiedName()
.toString();
return ANNOTATION_ADD_BUILD_ITEM_TO_METADATA.equals(name);
}

private Name getPackageName(TypeElement clazz) {
return processingEnv.getElementUtils()
.getPackageOf(clazz)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
Expand Down Expand Up @@ -54,9 +55,36 @@ void shouldGenerateABscFile(Results results) throws IOException {
assertEquals("org.acme.examples.ClassWithBuildStep", contents);
}

private String removeLineBreaks(String s) {
return s.replace(System.getProperty("line.separator"), "")
.replace("\n", "");
@Test
@Classpath("org.acme.examples.ClassWithBuildStep")
void shouldNotGenerateANoteworthyBuildItemsFileIfThereAreNoAnnotationsForIt(Results results) throws IOException {
assertNoErrrors(results);
List<JavaFileObject> sources = results.sources;
JavaFileObject buildItemsFile = sources.stream()
.filter(source -> source.getName()
.endsWith("build-items.list"))
.findAny()
.orElse(null);
assertNull(buildItemsFile);

}

@Test
@Classpath("org.acme.examples.ClassWithNoteworthyBuildItem")
void shouldGenerateANoteworthyBuildItemsFile(Results results) throws IOException {
assertNoErrrors(results);
List<JavaFileObject> sources = results.sources;
JavaFileObject buildItemsFile = sources.stream()
.filter(source -> source.getName()
.endsWith("build-items.list"))
.findAny()
.orElse(null);
assertNotNull(buildItemsFile);

String contents = removeLineBreaks(new String(buildItemsFile
.openInputStream()
.readAllBytes(), StandardCharsets.UTF_8));
assertEquals("some-cool-ability", contents);
}

@Test
Expand All @@ -65,6 +93,11 @@ void shouldProcessEmptyClassWithoutErrors(Results results) {
assertNoErrrors(results);
}

private String removeLineBreaks(String s) {
return s.replace(System.getProperty("line.separator"), "")
.replace("\n", "");
}

private static void assertNoErrrors(Results results) {
assertEquals(0, results.find()
.errors()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.acme.examples;

import io.quarkus.deployment.annotations.BuildStep;

public class ClassWithNoteworthyBuildItem {
@BuildStep
NoteworthyBuildItem devService() {
return new NoteworthyBuildItem();
}

// This one shouldn't get recorded for use in the metadata
@BuildStep
ArbitraryBuildItem arbitrary() {
return new ArbitraryBuildItem();
}

// This one shouldn't get recorded, obviously, and it should not cause runtime exceptions
@BuildStep
void boring() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.acme.examples;

import io.quarkus.builder.item.AddToMetadata;
import io.quarkus.builder.item.MultiBuildItem;

@AddToMetadata("some-cool-ability")
public final class NoteworthyBuildItem extends MultiBuildItem {

}
5 changes: 5 additions & 0 deletions docs/src/main/asciidoc/extension-metadata.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -181,3 +181,8 @@ Same as `quarkus-config-roots.list`, this file may appear in a runtime extension
== META-INF/quarkus-build-steps.list

Check warning on line 181 in docs/src/main/asciidoc/extension-metadata.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.Headings] Use sentence-style capitalization in 'META-INF/quarkus-build-steps.list'. Raw Output: {"message": "[Quarkus.Headings] Use sentence-style capitalization in 'META-INF/quarkus-build-steps.list'.", "location": {"path": "docs/src/main/asciidoc/extension-metadata.adoc", "range": {"start": {"line": 181, "column": 4}}}, "severity": "INFO"}

This file may appear in a deployment extension artifact. It contains a list of classes that implement Quarkus build steps (methods annotated with `io.quarkus.deployment.annotations.BuildStep`). This file is generated as part of the extension project build process and must not be edited manually.

Check warning on line 183 in docs/src/main/asciidoc/extension-metadata.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsWarnings] Consider using 'might (for possiblity)' or 'can (for ability)' rather than 'may' unless updating existing content that uses the term. Raw Output: {"message": "[Quarkus.TermsWarnings] Consider using 'might (for possiblity)' or 'can (for ability)' rather than 'may' unless updating existing content that uses the term.", "location": {"path": "docs/src/main/asciidoc/extension-metadata.adoc", "range": {"start": {"line": 183, "column": 11}}}, "severity": "WARNING"}

Check warning on line 183 in docs/src/main/asciidoc/extension-metadata.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsSuggestions] Depending on the context, consider using 'because' or 'while' rather than 'as'. Raw Output: {"message": "[Quarkus.TermsSuggestions] Depending on the context, consider using 'because' or 'while' rather than 'as'.", "location": {"path": "docs/src/main/asciidoc/extension-metadata.adoc", "range": {"start": {"line": 183, "column": 218}}}, "severity": "INFO"}

[[quarkus-build-steps]]
== META-INF/noteworthy-build-items.list

Check warning on line 186 in docs/src/main/asciidoc/extension-metadata.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.Headings] Use sentence-style capitalization in 'META-INF/noteworthy-build-items.list'. Raw Output: {"message": "[Quarkus.Headings] Use sentence-style capitalization in 'META-INF/noteworthy-build-items.list'.", "location": {"path": "docs/src/main/asciidoc/extension-metadata.adoc", "range": {"start": {"line": 186, "column": 4}}}, "severity": "INFO"}

This file may appear in a deployment extension artifact. It contains a list of build items produced by this extension that represent noteworthy function which should be noted in the extension metadata.

Check warning on line 188 in docs/src/main/asciidoc/extension-metadata.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsWarnings] Consider using 'might (for possiblity)' or 'can (for ability)' rather than 'may' unless updating existing content that uses the term. Raw Output: {"message": "[Quarkus.TermsWarnings] Consider using 'might (for possiblity)' or 'can (for ability)' rather than 'may' unless updating existing content that uses the term.", "location": {"path": "docs/src/main/asciidoc/extension-metadata.adoc", "range": {"start": {"line": 188, "column": 11}}}, "severity": "WARNING"}

Check warning on line 188 in docs/src/main/asciidoc/extension-metadata.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsSuggestions] Depending on the context, consider using ', which (non restrictive clause preceded by a comma)' or 'that (restrictive clause without a comma)' rather than 'which'. Raw Output: {"message": "[Quarkus.TermsSuggestions] Depending on the context, consider using ', which (non restrictive clause preceded by a comma)' or 'that (restrictive clause without a comma)' rather than 'which'.", "location": {"path": "docs/src/main/asciidoc/extension-metadata.adoc", "range": {"start": {"line": 188, "column": 153}}}, "severity": "INFO"}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public interface BootstrapConstants {

String DESCRIPTOR_PATH = META_INF + '/' + DESCRIPTOR_FILE_NAME;
String BUILD_STEPS_PATH = META_INF + "/quarkus-build-steps.list";
String NOTEWORTHY_BUILD_ITEMS_PATH = META_INF + "/noteworthy-build-items.list";
String EXTENSION_METADATA_PATH = META_INF + '/' + QUARKUS_EXTENSION_FILE_NAME;

String PROP_DEPLOYMENT_ARTIFACT = "deployment-artifact";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.model.Scm;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
Expand Down Expand Up @@ -82,7 +85,7 @@
* Generates Quarkus extension descriptor for the runtime artifact.
* <p>
* <p/>
* Also generates META-INF/quarkus-extension.json which includes properties of
* Also generates META-INF/quarkus-extension.yaml which includes properties of
* the extension such as name, labels, maven coordinates, etc that are used by
* the tools.
*
Expand Down Expand Up @@ -459,6 +462,7 @@ public void execute() throws MojoExecutionException {

setBuiltWithQuarkusCoreVersion(extObject);
addJavaVersion(extObject);
addNoteworthyItems(extObject);
addCapabilities(extObject);
addSource(extObject);
addExtensionDependencies(extObject);
Expand Down Expand Up @@ -739,6 +743,29 @@ public void addJavaVersion(ObjectNode extObject) {
}
}

private void addNoteworthyItems(ObjectNode extObject) {
try {
Set<String> noteworthies = getNoteworthyList();
if (noteworthies != null) {
for (String noteworthy : noteworthies) {
ObjectNode metadataNode = getMetadataNode(extObject);
// Ignore if already set
String key = "provides-" + noteworthy;
if (!metadataNode.has(key) && noteworthy != null) {
metadataNode.put(key, "true");
}
}

}
} catch (IOException e) {
throw new RuntimeException(e);
} catch (MojoExecutionException e) {
throw new RuntimeException(e);
} catch (MojoFailureException e) {
throw new RuntimeException(e);
}
}

private void addCapabilities(ObjectNode extObject) throws MojoExecutionException {
ObjectNode capsNode = null;
if (!capabilities.getProvides().isEmpty()) {
Expand Down Expand Up @@ -1014,6 +1041,35 @@ private org.eclipse.aether.artifact.Artifact getDeploymentArtifact(org.eclipse.a
return DependencyUtils.toArtifact(deploymentStr);
}

private Set<String> getNoteworthyList() throws IOException, MojoExecutionException, MojoFailureException {

ArtifactCoords deploymentCoords = getDeploymentCoords();

// Ask Maven to resolve the artifact's location, so we can look inside it.
// That could be downloading it from a remote repository, searching the local repository or a cache.

String artifactId = deploymentCoords.getArtifactId();
org.eclipse.aether.artifact.Artifact aetherArtifact = new DefaultArtifact(
deploymentCoords.getGroupId(),
artifactId,
deploymentCoords.getClassifier(),
deploymentCoords.getType(),
deploymentCoords.getVersion());

final LocalProject localProject = workspaceProvider.getProject(deploymentCoords.getGroupId(),
deploymentCoords.getArtifactId());
if (localProject != null) {
Path file = localProject.getClassesDir().resolve(BootstrapConstants.NOTEWORTHY_BUILD_ITEMS_PATH);
if (Files.exists(file)) {
try (Stream<String> lines = Files.lines(file)) {
return lines.collect(Collectors.toSet());
}
}
}

return null;
}

private Properties getExtensionDescriptor(org.eclipse.aether.artifact.Artifact a, boolean packaged) {
final File f;
try {
Expand Down
Loading

0 comments on commit 88bdd29

Please sign in to comment.