org.sonarsource.scanner.maven.bootstrap.MavenCompilerResolver Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sonar-maven-plugin Show documentation
Show all versions of sonar-maven-plugin Show documentation
Trigger SonarQube analysis on Maven projects
/*
* SonarQube Scanner for Maven
* Copyright (C) 2009-2021 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonarsource.scanner.maven.bootstrap;
import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.SystemUtils;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.lifecycle.LifecycleExecutor;
import org.apache.maven.plugin.MojoExecution;
import org.apache.maven.plugin.PluginParameterExpressionEvaluator;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.project.MavenProject;
import org.apache.maven.toolchain.Toolchain;
import org.apache.maven.toolchain.ToolchainManager;
import org.apache.maven.toolchain.java.DefaultJavaToolChain;
import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
import org.codehaus.plexus.component.configurator.converters.basic.StringConverter;
import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
import org.codehaus.plexus.configuration.PlexusConfiguration;
import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
import org.codehaus.plexus.util.xml.Xpp3Dom;
public class MavenCompilerResolver {
private static final String DEFAULT_COMPILE_EXECUTION_ID = "default-compile";
private static final String TEST_COMPILE_PHASE = "test-compile";
private static final String COMPILE_GOAL = "compile";
private static final String TEST_COMPILE_GOAL = "testCompile";
private static final String MAVEN_COMPILER_PLUGIN = "maven-compiler-plugin";
private final MavenSession session;
private final Log log;
private final ToolchainManager toolchainManager;
private final LifecycleExecutor lifecycleExecutor;
public MavenCompilerResolver(MavenSession session, LifecycleExecutor lifecycleExecutor, Log log, ToolchainManager toolchainManager) {
this.session = session;
this.lifecycleExecutor = lifecycleExecutor;
this.log = log;
this.toolchainManager = toolchainManager;
}
private static int defaultCompileFirstThenCompileFirst(MojoExecution a, MojoExecution b) {
// Favor default-compile, as this is the one likely to best represent the "main" compiler execution
if (DEFAULT_COMPILE_EXECUTION_ID.equals(a.getExecutionId())) {
return -1;
} else if (DEFAULT_COMPILE_EXECUTION_ID.equals(b.getExecutionId())) {
return 1;
} else {
// compile is before testCompile
return a.getGoal().compareTo(b.getGoal());
}
}
public static class MavenCompilerConfiguration {
private Optional release;
private Optional target;
private Optional source;
private Optional jdkHome;
private final String executionId;
private MavenCompilerConfiguration(String executionId) {
this.executionId = executionId;
}
public Optional getRelease() {
return release;
}
public Optional getTarget() {
return target;
}
public Optional getSource() {
return source;
}
public Optional getJdkHome() {
return jdkHome;
}
public String getExecutionId() {
return executionId;
}
public static boolean same(MavenCompilerConfiguration one, MavenCompilerConfiguration two) {
return Objects.equals(one.jdkHome, two.jdkHome)
&& Objects.equals(one.release, two.release)
&& Objects.equals(one.source, two.source)
&& Objects.equals(one.target, two.target);
}
}
public Optional extractConfiguration(MavenProject pom) {
MavenProject oldProject = session.getCurrentProject();
try {
// Switch to the project for which we try to resolve the configuration.
session.setCurrentProject(pom);
List allCompilerExecutions = lifecycleExecutor.calculateExecutionPlan(session, true, TEST_COMPILE_PHASE)
.getMojoExecutions()
.stream()
.filter(MavenCompilerResolver::isMavenCompilerGoal)
.sorted(MavenCompilerResolver::defaultCompileFirstThenCompileFirst)
.collect(Collectors.toList());
if (allCompilerExecutions.isEmpty()) {
return Optional.empty();
}
List allCompilerConfigurations = allCompilerExecutions.stream().map(this::extractConfiguration).collect(Collectors.toList());
MavenCompilerConfiguration first = allCompilerConfigurations.get(0);
if (!allCompilerConfigurations.stream().allMatch(config -> MavenCompilerConfiguration.same(config, first))) {
log.warn("Heterogeneous compiler configuration has been detected. Using compiler configuration from execution: '" + first.getExecutionId() + "'");
}
return Optional.of(first);
} catch (Exception e) {
log.warn("Failed to collect configuration from the maven-compiler-plugin", e);
return Optional.empty();
} finally {
session.setCurrentProject(oldProject);
}
}
private static boolean isMavenCompilerGoal(MojoExecution e) {
return e.getArtifactId().equals(MAVEN_COMPILER_PLUGIN)
&& e.getGroupId().equals(MavenUtils.GROUP_ID_APACHE_MAVEN)
&& (e.getGoal().equals(COMPILE_GOAL) || e.getGoal().equals(TEST_COMPILE_GOAL));
}
private MavenCompilerConfiguration extractConfiguration(MojoExecution compilerExecution) {
MavenCompilerConfiguration result = new MavenCompilerConfiguration(compilerExecution.getExecutionId());
result.release = getStringConfiguration(compilerExecution, "release");
result.target = getStringConfiguration(compilerExecution, "target");
result.source = getStringConfiguration(compilerExecution, "source");
result.jdkHome = getJdkHome(compilerExecution);
return result;
}
private Optional getStringConfiguration(MojoExecution exec, String parameterName) {
Xpp3Dom configuration = exec.getConfiguration();
PlexusConfiguration pomConfiguration = new XmlPlexusConfiguration(configuration);
PlexusConfiguration config = pomConfiguration.getChild(parameterName, false);
if (config == null) {
return Optional.empty();
}
return Optional.ofNullable(convertString(exec, config));
}
private String convertString(MojoExecution exec, PlexusConfiguration config) {
ExpressionEvaluator expressionEvaluator = new PluginParameterExpressionEvaluator(session, exec);
BasicStringConverter converter = new BasicStringConverter();
try {
return converter.fromExpression(config, expressionEvaluator);
} catch (ComponentConfigurationException e) {
log.warn(e.getMessage(), e);
return null;
}
}
private Optional