001/*
002 * Copyright 2017-2022 original authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package io.micronaut.maven.aot;
017
018import io.micronaut.maven.AbstractMicronautMojo;
019import io.micronaut.maven.Packaging;
020import io.micronaut.maven.services.CompilerService;
021import org.apache.commons.io.FileUtils;
022import org.apache.maven.plugin.MojoExecutionException;
023import org.apache.maven.plugins.annotations.Parameter;
024import org.apache.maven.project.MavenProject;
025import org.eclipse.aether.resolution.DependencyResolutionException;
026
027import java.io.File;
028import java.io.IOException;
029
030/**
031 * Abstract Mojo for Micronaut AOT.
032 */
033public abstract class AbstractMicronautAotMojo extends AbstractMicronautMojo {
034
035    protected final CompilerService compilerService;
036
037    protected final MavenProject mavenProject;
038
039    /**
040     * Micronaut AOT runtime. Possible values: <code>jit</code>, <code>native</code>.
041     */
042    @Parameter(property = "micronaut.aot.runtime", required = true, defaultValue = "jit")
043    protected String runtime;
044
045    /**
046     * Micronaut AOT version.
047     */
048    @Parameter(property = "micronaut.aot.version", required = true)
049    protected String micronautAotVersion;
050
051    /**
052     * Whether to enable or disable Micronaut AOT.
053     */
054    @Parameter(property = "micronaut.aot.enabled", defaultValue = "false")
055    protected boolean enabled;
056
057    /**
058     * Directory where compiled application classes are.
059     */
060    @Parameter(defaultValue = "${project.build.outputDirectory}", required = true)
061    protected File outputDirectory;
062
063    public AbstractMicronautAotMojo(CompilerService compilerService, MavenProject mavenProject) {
064        this.compilerService = compilerService;
065        this.mavenProject = mavenProject;
066    }
067
068    abstract void onSuccess(File outputDir) throws MojoExecutionException;
069
070    protected final File getBaseOutputDirectory() {
071        var targetDirectory = new File(mavenProject.getBuild().getDirectory(), "aot");
072        return new File(targetDirectory, runtime);
073    }
074
075    protected final File outputFile(String name) {
076        return new File(getBaseOutputDirectory(), name);
077    }
078
079    @Override
080    public final void execute() throws MojoExecutionException {
081        if (!enabled) {
082            return;
083        }
084        validateRuntime();
085        getLog().info("Running Micronaut AOT " + micronautAotVersion + " " + getName());
086        try {
087            File baseOutputDirectory = getBaseOutputDirectory();
088            clean(baseOutputDirectory);
089            clean(outputDirectory);
090            doExecute();
091            onSuccess(baseOutputDirectory);
092        } catch (DependencyResolutionException | IOException e) {
093            throw new MojoExecutionException("Unable to generate AOT optimizations", e);
094        }
095    }
096
097    private void clean(File baseOutputDirectory) throws IOException {
098        if (baseOutputDirectory.exists()) {
099            getLog().debug("Deleting " + baseOutputDirectory.getAbsolutePath());
100            FileUtils.deleteDirectory(baseOutputDirectory);
101        }
102        baseOutputDirectory.mkdirs();
103    }
104
105    private void validateRuntime() {
106        var packaging = Packaging.of(mavenProject.getPackaging());
107        var aotRuntime = AotRuntime.valueOf(runtime.toUpperCase());
108        switch (packaging) {
109            case JAR, DOCKER_CRAC, DOCKER -> {
110                if (aotRuntime != AotRuntime.JIT) {
111                    warnRuntimeMismatchAndSetCorrectValue(AotRuntime.JIT);
112                }
113            }
114            case NATIVE_IMAGE, DOCKER_NATIVE -> {
115                if (aotRuntime != AotRuntime.NATIVE) {
116                    warnRuntimeMismatchAndSetCorrectValue(AotRuntime.NATIVE);
117                }
118            }
119            default -> throw new IllegalArgumentException("Unsupported packaging: " + packaging);
120        }
121    }
122
123    private void warnRuntimeMismatchAndSetCorrectValue(final AotRuntime correctRuntime) {
124        String correctRuntimeString = correctRuntime.name().toLowerCase();
125        getLog().warn("Packaging is set to [" + mavenProject.getPackaging() + "], but Micronaut AOT runtime is set to [" + runtime + "]. Setting AOT runtime to: [" + correctRuntimeString + "]");
126        this.runtime = correctRuntimeString;
127    }
128
129    protected abstract void doExecute() throws DependencyResolutionException, MojoExecutionException;
130
131    abstract String getName();
132}