From 6c5fdf39867b5ad5be1e7896af9948ce4ce0d22c Mon Sep 17 00:00:00 2001 From: Joel Rudsberg Date: Thu, 26 Sep 2024 14:27:52 +0200 Subject: [PATCH] Add error handling and useful prints for SBOM feature --- .../buildtools/utils/NativeImageUtils.java | 1 + .../maven/AbstractNativeImageMojo.java | 4 +- .../maven/NativeCompileNoForkMojo.java | 76 +++++++++++++++++-- .../buildtools/maven/sbom/SBOMGenerator.java | 7 +- 4 files changed, 77 insertions(+), 11 deletions(-) diff --git a/common/utils/src/main/java/org/graalvm/buildtools/utils/NativeImageUtils.java b/common/utils/src/main/java/org/graalvm/buildtools/utils/NativeImageUtils.java index 71d235e25..e6d2db8b3 100644 --- a/common/utils/src/main/java/org/graalvm/buildtools/utils/NativeImageUtils.java +++ b/common/utils/src/main/java/org/graalvm/buildtools/utils/NativeImageUtils.java @@ -56,6 +56,7 @@ import static org.graalvm.buildtools.utils.SharedConstants.GRAALVM_EXE_EXTENSION; public class NativeImageUtils { + public static final String ORACLE_GRAALVM_IDENTIFIER = "Oracle GraalVM"; private static final Pattern requiredVersionPattern = Pattern.compile("^([0-9]+)(?:\\.([0-9]+)?)?(?:\\.([0-9]+)?)?$"); diff --git a/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/AbstractNativeImageMojo.java b/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/AbstractNativeImageMojo.java index a86f86fd1..fb2d45405 100644 --- a/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/AbstractNativeImageMojo.java +++ b/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/AbstractNativeImageMojo.java @@ -80,6 +80,8 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import static org.graalvm.buildtools.utils.NativeImageUtils.ORACLE_GRAALVM_IDENTIFIER; + /** * @author Sebastien Deleuze */ @@ -442,7 +444,7 @@ protected void checkRequiredVersionIfNeeded() throws MojoExecutionException { } protected boolean isOracleGraalVM() throws MojoExecutionException { - return getVersionInformation().contains("Oracle GraalVM"); + return getVersionInformation().contains(ORACLE_GRAALVM_IDENTIFIER); } /** diff --git a/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/NativeCompileNoForkMojo.java b/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/NativeCompileNoForkMojo.java index 1c11af7f7..ddb51ba5b 100644 --- a/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/NativeCompileNoForkMojo.java +++ b/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/NativeCompileNoForkMojo.java @@ -54,11 +54,14 @@ import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException; import org.codehaus.plexus.util.xml.Xpp3Dom; import org.graalvm.buildtools.maven.sbom.SBOMGenerator; +import org.graalvm.buildtools.utils.NativeImageUtils; import java.util.Arrays; import java.util.List; import java.util.function.BiFunction; +import static org.graalvm.buildtools.utils.NativeImageUtils.ORACLE_GRAALVM_IDENTIFIER; + /** * This goal runs native builds. It functions the same as the native:compile goal, but it * does not fork the build, so it is suitable for attaching to the build lifecycle. @@ -74,9 +77,9 @@ public class NativeCompileNoForkMojo extends AbstractNativeImageMojo { @Parameter(property = "skipNativeBuildForPom", defaultValue = "false") private boolean skipNativeBuildForPom; - public static final String enableSBOMParamName = "enableSBOM"; - @Parameter(property = enableSBOMParamName, defaultValue = "true") - private boolean enableSBOM; + public static final String enableAugmentedSBOMParamName = "enableAugmentedSBOM"; + @Parameter(property = enableAugmentedSBOMParamName, defaultValue = "false") + private boolean enableAugmentedSBOM; private PluginParameterExpressionEvaluator evaluator; @@ -106,14 +109,73 @@ public void execute() throws MojoExecutionException { maybeSetMainClassFromPlugin(this::consumeConfigurationNodeValue, "org.apache.maven.plugins:maven-jar-plugin", "archive", "manifest", "mainClass"); maybeAddGeneratedResourcesConfig(buildArgs); - if (isOracleGraalVM() && enableSBOM) { - var generator = new SBOMGenerator(mavenProject, mavenSession, pluginManager, repositorySystem, mainClass, logger); - generator.generate(); - } + generateSBOMIfPossible(); buildImage(); } + /** + * Generates an SBOM using the {@link SBOMGenerator} if {@link NativeCompileNoForkMojo#enableAugmentedSBOM} is true + * and the required pre-conditions are met. + * + * @throws IllegalArgumentException when some pre-conditions are not met. + * @throws MojoExecutionException when SBOM generation failed. + */ + private void generateSBOMIfPossible() throws IllegalArgumentException, MojoExecutionException { + String sbomNativeImageFlag = "--enable-sbom"; + boolean sbomEnabledForNativeImage = getBuildArgs().stream().anyMatch(v -> v.contains(sbomNativeImageFlag)); + if (!sbomEnabledForNativeImage && !enableAugmentedSBOM) { + /* Omit showing a hint to use SBOM since the Native Image build output will include such a hint. */ + return; + } + + if (!isOracleGraalVM()) { + if (enableAugmentedSBOM) { + throw new IllegalArgumentException( + String.format("Configuration option %s is only supported in %s.", enableAugmentedSBOMParamName, ORACLE_GRAALVM_IDENTIFIER)); + } + return; + } + + if (!enableAugmentedSBOM) { + if (checkAugmentedSBOMSupportedByVersion(false)) { + logger.info(String.format("Set configuration option %s to true to create an enhanced and more accurate SBOM.", enableAugmentedSBOMParamName)); + } + return; + } + + checkAugmentedSBOMSupportedByVersion(true); + if (!sbomEnabledForNativeImage) { + logger.info(String.format("Automatically added build argument %s with default options to Native Image because configuration option %s was set to true. " + + "An SBOM will be embedded in the image.", sbomNativeImageFlag, enableAugmentedSBOMParamName)); + buildArgs.add(sbomNativeImageFlag); + } + + var sbomGenerator = new SBOMGenerator(mavenProject, mavenSession, pluginManager, repositorySystem, mainClass, logger); + sbomGenerator.generate(); + } + + /** + * Checks if the JDK version supports the {@link NativeCompileNoForkMojo#enableAugmentedSBOM} flag. + * + * @param throwErrorIfNotSupported if true, then an error is thrown if the check failed. + * @return true if the JDK version supports the flag, otherwise false (if {@param throwErrorIfNotSupported} is false). + * @throws MojoExecutionException when {@param throwErrorIfNotSupported} is true and the version information could not be derived. + * @throws IllegalArgumentException when {@param throwErrorIfNotSupported} is true and the version check failed. + */ + private boolean checkAugmentedSBOMSupportedByVersion(boolean throwErrorIfNotSupported) throws IllegalArgumentException, MojoExecutionException { + int detectedJdkVersion = NativeImageUtils.getMajorJDKVersion(getVersionInformation()); + if (detectedJdkVersion < SBOMGenerator.requiredNativeImageVersion) { + if (throwErrorIfNotSupported) { + throw new IllegalArgumentException( + String.format("%s version %s is required to use configuration option %s but major JDK version %s has been detected.", + ORACLE_GRAALVM_IDENTIFIER, SBOMGenerator.requiredNativeImageVersion, enableAugmentedSBOMParamName, detectedJdkVersion)); + } + return false; + } + return true; + } + private String consumeConfigurationNodeValue(String pluginKey, String... nodeNames) { Plugin selectedPlugin = project.getPlugin(pluginKey); if (selectedPlugin == null) { diff --git a/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/sbom/SBOMGenerator.java b/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/sbom/SBOMGenerator.java index 4bbc47d00..be7df2821 100644 --- a/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/sbom/SBOMGenerator.java +++ b/native-maven-plugin/src/main/java/org/graalvm/buildtools/maven/sbom/SBOMGenerator.java @@ -50,8 +50,6 @@ import org.apache.maven.project.MavenProject; import org.codehaus.plexus.logging.Logger; import org.eclipse.aether.RepositorySystem; -import org.graalvm.buildtools.maven.NativeCompileNoForkMojo; - import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -61,6 +59,7 @@ import java.util.Set; import java.util.stream.Collectors; +import static org.graalvm.buildtools.maven.NativeCompileNoForkMojo.enableAugmentedSBOMParamName; import static org.twdata.maven.mojoexecutor.MojoExecutor.*; /** @@ -82,6 +81,8 @@ * * Enhanced Accuracy: Native Image augments and refines the SBOM, potentially significantly improving its accuracy. */ final public class SBOMGenerator { + public static final int requiredNativeImageVersion = 24; + private final MavenProject mavenProject; private final MavenSession mavenSession; private final BuildPluginManager pluginManager; @@ -176,7 +177,7 @@ public void generate() throws MojoExecutionException { } catch (Exception exception) { deleteFileIfExists(sbomPath); String errorMsg = String.format("Failed to create SBOM. Please try again and report this issue if it persists. " + - "To bypass this failure, disable SBOM generation by setting %s to false.", NativeCompileNoForkMojo.enableSBOMParamName); + "To bypass this failure, disable SBOM generation by setting configuration option %s to false.", enableAugmentedSBOMParamName); throw new MojoExecutionException(errorMsg, exception); } }