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.model.Exclusion;
023import org.apache.maven.plugin.MojoExecutionException;
024import org.apache.maven.plugins.annotations.Parameter;
025import org.apache.maven.project.MavenProject;
026import org.eclipse.aether.resolution.DependencyResolutionException;
027
028import java.io.File;
029import java.io.IOException;
030import java.util.List;
031
032/**
033 * Abstract Mojo for Micronaut AOT.
034 */
035public abstract class AbstractMicronautAotMojo extends AbstractMicronautMojo {
036
037    protected final CompilerService compilerService;
038
039    protected final MavenProject mavenProject;
040
041    /**
042     * Micronaut AOT runtime. Possible values: <code>jit</code>, <code>native</code>.
043     */
044    @Parameter(property = "micronaut.aot.runtime", required = true, defaultValue = "jit")
045    protected String runtime;
046
047    /**
048     * Micronaut AOT version.
049     */
050    @Parameter(property = "micronaut.aot.version", required = true)
051    protected String micronautAotVersion;
052
053    /**
054     * Whether to enable or disable Micronaut AOT.
055     */
056    @Parameter(property = "micronaut.aot.enabled", defaultValue = "false")
057    protected boolean enabled;
058
059    /**
060     * Directory where compiled application classes are.
061     */
062    @Parameter(defaultValue = "${project.build.outputDirectory}", required = true)
063    protected File outputDirectory;
064
065    /**
066     * Packages that would be excluded from the AOT processing.
067     * @since 4.11.0
068     */
069    @Parameter(property = "exclusions")
070    protected List<Exclusion> aotExclusions;
071
072    public AbstractMicronautAotMojo(CompilerService compilerService, MavenProject mavenProject) {
073        this.compilerService = compilerService;
074        this.mavenProject = mavenProject;
075    }
076
077    abstract void onSuccess(File outputDir) throws MojoExecutionException;
078
079    protected final File getBaseOutputDirectory() {
080        var targetDirectory = new File(mavenProject.getBuild().getDirectory(), "aot");
081        return new File(targetDirectory, runtime);
082    }
083
084    protected final File outputFile(String name) {
085        return new File(getBaseOutputDirectory(), name);
086    }
087
088    @Override
089    public final void execute() throws MojoExecutionException {
090        if (!enabled) {
091            return;
092        }
093        validateRuntime();
094        getLog().info("Running Micronaut AOT " + micronautAotVersion + " " + getName());
095        try {
096            File baseOutputDirectory = getBaseOutputDirectory();
097            clean(baseOutputDirectory);
098            clean(outputDirectory);
099            doExecute();
100            onSuccess(baseOutputDirectory);
101        } catch (DependencyResolutionException | IOException e) {
102            throw new MojoExecutionException("Unable to generate AOT optimizations", e);
103        }
104    }
105
106    private void clean(File baseOutputDirectory) throws IOException {
107        if (baseOutputDirectory.exists()) {
108            getLog().debug("Deleting " + baseOutputDirectory.getAbsolutePath());
109            FileUtils.deleteDirectory(baseOutputDirectory);
110        }
111        baseOutputDirectory.mkdirs();
112    }
113
114    private void validateRuntime() {
115        var packaging = Packaging.of(mavenProject.getPackaging());
116        var aotRuntime = AotRuntime.valueOf(runtime.toUpperCase());
117        switch (packaging) {
118            case JAR, DOCKER_CRAC, DOCKER -> {
119                if (aotRuntime != AotRuntime.JIT) {
120                    warnRuntimeMismatchAndSetCorrectValue(AotRuntime.JIT);
121                }
122            }
123            case NATIVE_IMAGE, DOCKER_NATIVE -> {
124                if (aotRuntime != AotRuntime.NATIVE) {
125                    warnRuntimeMismatchAndSetCorrectValue(AotRuntime.NATIVE);
126                }
127            }
128            default -> throw new IllegalArgumentException("Unsupported packaging: " + packaging);
129        }
130    }
131
132    private void warnRuntimeMismatchAndSetCorrectValue(final AotRuntime correctRuntime) {
133        String correctRuntimeString = correctRuntime.name().toLowerCase();
134        getLog().warn("Packaging is set to [" + mavenProject.getPackaging() + "], but Micronaut AOT runtime is set to [" + runtime + "]. Setting AOT runtime to: [" + correctRuntimeString + "]");
135        this.runtime = correctRuntimeString;
136    }
137
138    protected abstract void doExecute() throws DependencyResolutionException, MojoExecutionException;
139
140    abstract String getName();
141}