/*
* $Id$
*
* SARL is an general-purpose agent programming language.
* More details on http://www.sarl.io
*
* Copyright (C) 2014-2017 the original authors or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.sarl.maven.compiler;
import java.io.File;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import com.google.common.base.Strings;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.ArtifactUtils;
import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
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.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.arakhne.afc.vmutil.locale.Locale;
import io.sarl.lang.SARLVersion;
/** Mojo for compiling SARL.
*
* @author $Author: sgalland$
* @version $FullVersion$
* @mavengroupid $GroupId$
* @mavenartifactid $ArtifactId$
*/
@Mojo(name = "compile", defaultPhase = LifecyclePhase.COMPILE,
requiresDependencyResolution = ResolutionScope.COMPILE)
public class CompileMojo extends AbstractSarlBatchCompilerMojo {
private static final String STUB_FOLDER = "sarl-temp"; //$NON-NLS-1$
/** Version of the Java specification used for the source files.
*/
@Parameter(defaultValue = SARLVersion.MINIMAL_JDK_VERSION, required = false)
private String source;
/** Encoding.
*/
@Parameter(required = false)
private String encoding;
/**
* Location of the temporary compiler directory.
*/
@Parameter
private File tempDirectory;
/** Indicates if the Java compiler must be invoked by the SARL maven plugin.
*/
@Parameter(defaultValue = "true", required = false)
private boolean runJavaCompiler;
/** Indicates if the inline annotations must be generated by the SARL maven plugin.
*/
@Parameter(defaultValue = "true", required = false)
private boolean generateInlines;
/** Indicates if the trace files should be generated.
*/
@Parameter(defaultValue = "true", required = false)
private boolean generateTraceFiles;
/** Indicates if the storage files should be generated.
*/
@Parameter(defaultValue = "true", required = false)
private boolean generateStorageFiles;
/** Indicates if the classpath is provided by Tycho.
*/
@Parameter(defaultValue = "false", required = false)
private boolean tycho;
@Override
protected String getSourceVersion() {
return this.source;
}
@Override
protected String getEncoding() {
return this.encoding == null || this.encoding.isEmpty() ? Charset.defaultCharset().displayName() : this.encoding;
}
@Override
protected File getTempDirectory() {
if (this.tempDirectory == null) {
final File targetDir = new File(getProject().getBuild().getDirectory());
return makeAbsolute(new File(targetDir, STUB_FOLDER));
}
return makeAbsolute(this.tempDirectory);
}
@Override
protected boolean getPostRunningOfJavaCompiler() {
return this.runJavaCompiler;
}
@Override
protected boolean getGenerateInlines() {
return this.generateInlines;
}
@Override
protected boolean getGenerateTraceFiles() {
return this.generateTraceFiles;
}
@Override
protected boolean getGenerateStorageFiles() {
return this.generateStorageFiles;
}
@Override
protected void buildPropertyString(StringBuilder buffer) {
super.buildPropertyString(buffer);
buffer.append("source = ").append(this.source).append("\n"); //$NON-NLS-1$//$NON-NLS-2$
buffer.append("encoding = ").append(this.encoding).append("\n"); //$NON-NLS-1$//$NON-NLS-2$
buffer.append("tempDirectory = ").append(this.tempDirectory).append("\n"); //$NON-NLS-1$//$NON-NLS-2$
buffer.append("runJavaCompiler = ").append(this.runJavaCompiler).append("\n"); //$NON-NLS-1$//$NON-NLS-2$
buffer.append("generateInlines = ").append(this.generateInlines).append("\n"); //$NON-NLS-1$//$NON-NLS-2$
buffer.append("generateTraceFiles = ").append(this.generateTraceFiles).append("\n"); //$NON-NLS-1$//$NON-NLS-2$
buffer.append("generateStorageFiles = ").append(this.generateStorageFiles).append("\n"); //$NON-NLS-1$//$NON-NLS-2$
}
@Override
protected void ensureDefaultParameterValues() {
super.ensureDefaultParameterValues();
if (Strings.isNullOrEmpty(this.encoding)) {
final Properties properties = this.mavenHelper.getSession().getCurrentProject().getProperties();
this.encoding = properties.getProperty("project.build.sourceEncoding", null); //$NON-NLS-1$
if (Strings.isNullOrEmpty(this.encoding)) {
this.encoding = Charset.defaultCharset().name();
}
}
}
@Override
protected void executeMojo() throws MojoExecutionException, MojoFailureException {
ensureSARLVersions();
validateDependencyVersions();
compileSARL();
}
@SuppressWarnings("unchecked")
private static boolean containsVersion(ArtifactVersion version, ArtifactVersion rangeMin, ArtifactVersion rangeMax) {
return (version.compareTo(rangeMin) >= 0) && (version.compareTo(rangeMax) < 0);
}
private void ensureSARLVersions() throws MojoExecutionException, MojoFailureException {
final String compilerVersionString = this.mavenHelper.getConfig("plugin.version"); //$NON-NLS-1$
final ArtifactVersion compilerVersion = new DefaultArtifactVersion(compilerVersionString);
final ArtifactVersion maxCompilerVersion = new DefaultArtifactVersion(
compilerVersion.getMajorVersion() + "." //$NON-NLS-1$
+ (compilerVersion.getMinorVersion() + 1)
+ ".0"); //$NON-NLS-1$
getLog().info(Locale.getString(CompileMojo.class, "CHECK_SARL_SDK", compilerVersionString, //$NON-NLS-1$
maxCompilerVersion));
final StringBuilder classpath = new StringBuilder();
final Set<String> foundVersions = findSARLLibrary(compilerVersion, maxCompilerVersion, classpath,
this.tycho);
if (foundVersions.isEmpty()) {
throw new MojoFailureException(Locale.getString(CompileMojo.class, "NO_SARL_LIBRARY", //$NON-NLS-1$
classpath.toString()));
}
final StringBuilder versions = new StringBuilder();
for (final String version : foundVersions) {
if (versions.length() > 0) {
versions.append(", "); //$NON-NLS-1$
}
versions.append(version);
}
if (foundVersions.size() > 1) {
getLog().info(Locale.getString(CompileMojo.class, "TOO_MUCH_SARL_VERSIONS", versions)); //$NON-NLS-1$
} else {
getLog().info(Locale.getString(CompileMojo.class, "DETECTED_SARL_VERSION", versions)); //$NON-NLS-1$
}
}
private Set<String> findSARLLibrary(ArtifactVersion compilerVersion, ArtifactVersion maxCompilerVersion,
StringBuilder classpath, boolean enableTycho) throws MojoExecutionException, MojoFailureException {
final String sarlLibGroupId = this.mavenHelper.getConfig("sarl-lib.groupId"); //$NON-NLS-1$
final String sarlLibArtifactId = this.mavenHelper.getConfig("sarl-lib.artifactId"); //$NON-NLS-1$
final String sarlLibGroupIdTycho = "p2.eclipse-plugin"; //$NON-NLS-1$
final String sarlLibArtifactIdTycho = this.mavenHelper.getConfig("sarl-lib.osgiBundleId"); //$NON-NLS-1$
final Set<String> foundVersions = new TreeSet<>();
for (final Artifact dep : this.mavenHelper.getSession().getCurrentProject().getArtifacts()) {
getLog().debug(Locale.getString(CompileMojo.class, "SCANNING_DEPENDENCY", //$NON-NLS-1$
dep.getGroupId(), dep.getArtifactId(), dep.getVersion()));
if (classpath.length() > 0) {
classpath.append(":"); //$NON-NLS-1$
}
classpath.append(ArtifactUtils.versionlessKey(dep));
String gid = null;
String aid = null;
if (sarlLibGroupId.equals(dep.getGroupId())
&& sarlLibArtifactId.equals(dep.getArtifactId())) {
gid = sarlLibGroupId;
aid = sarlLibArtifactId;
} else if (enableTycho
&& sarlLibGroupIdTycho.equals(dep.getGroupId())
&& sarlLibArtifactIdTycho.equals(dep.getArtifactId())) {
gid = sarlLibGroupIdTycho;
aid = sarlLibArtifactIdTycho;
}
if (gid != null && aid != null) {
final ArtifactVersion dependencyVersion = new DefaultArtifactVersion(dep.getVersion());
if (!containsVersion(dependencyVersion, compilerVersion, maxCompilerVersion)) {
final String shortMessage = Locale.getString(CompileMojo.class,
"INCOMPATIBLE_VERSION_SHORT", //$NON-NLS-1$
gid, aid, dependencyVersion.toString(),
compilerVersion.toString(), maxCompilerVersion.toString());
final String longMessage = Locale.getString(CompileMojo.class,
"INCOMPATIBLE_VERSION_LONG", //$NON-NLS-1$
sarlLibGroupId, sarlLibArtifactId, dependencyVersion.toString(),
compilerVersion.toString(), maxCompilerVersion.toString());
throw new MojoFailureException(this, shortMessage, longMessage);
}
foundVersions.add(dep.getVersion());
}
}
return foundVersions;
}
@SuppressWarnings("unchecked")
private void validateDependencyVersions() throws MojoExecutionException, MojoFailureException {
getLog().info(Locale.getString(CompileMojo.class, "CHECK_DEPENDENCY_VERSIONS")); //$NON-NLS-1$
final String sarlSdkGroupId = this.mavenHelper.getConfig("sarl-sdk.groupId"); //$NON-NLS-1$
final String sarlSdkArtifactId = this.mavenHelper.getConfig("sarl-sdk.artifactId"); //$NON-NLS-1$
boolean hasError = false;
final Map<String, Artifact> artifacts = this.mavenHelper.getSession().getCurrentProject().getArtifactMap();
final String sdkArtifactKey = ArtifactUtils.versionlessKey(sarlSdkGroupId, sarlSdkArtifactId);
final Artifact sdkArtifact = artifacts.get(sdkArtifactKey);
if (sdkArtifact != null) {
final Map<String, ArtifactVersion> versions = new TreeMap<>();
final Set<Artifact> dependencies = this.mavenHelper.resolveDependencies(sdkArtifactKey, false);
for (final Artifact dependency : dependencies) {
final ArtifactVersion dependencyVersion = new DefaultArtifactVersion(dependency.getVersion());
final String dependencyKey = ArtifactUtils.versionlessKey(dependency);
final ArtifactVersion currentVersion = versions.get(dependencyKey);
if (currentVersion == null || dependencyVersion.compareTo(currentVersion) > 0) {
versions.put(dependencyKey, dependencyVersion);
}
}
for (final Entry<String, ArtifactVersion> entry : versions.entrySet()) {
final Artifact dependencyArtifact = artifacts.get(entry.getKey());
if (dependencyArtifact != null) {
final ArtifactVersion dependencyVersion = new DefaultArtifactVersion(dependencyArtifact.getVersion());
if (entry.getValue().compareTo(dependencyVersion) > 0) {
final String message = Locale.getString(CompileMojo.class,
"INVALID_SARL_SDK_DEPENDENCY_VERSION", //$NON-NLS-1$
dependencyArtifact.getGroupId(), dependencyArtifact.getArtifactId(),
dependencyArtifact.getVersion(), entry.getValue().toString());
getLog().error(message);
hasError = true;
}
}
}
}
if (hasError) {
throw new MojoFailureException(Locale.getString(CompileMojo.class, "INVALID_SARL_SDK_DEPENDENCY_VERSION_TITLE")); //$NON-NLS-1$
}
}
private void compileSARL() throws MojoExecutionException, MojoFailureException {
final Log log = getLog();
File outputDirectory = getOutput();
log.info(Locale.getString(CompileMojo.class, "COMPILING_SARL")); //$NON-NLS-1$
if (log.isDebugEnabled()) {
final StringBuilder properties = new StringBuilder();
buildPropertyString(properties);
log.debug(properties.toString());
}
// If output is not explicitly set try to read SARL prefs from eclipse .settings folder
if (getDefaultOutput().equals(getOutput())) {
final String settingsValue = readSarlEclipseSetting(getProject().getBuild().getSourceDirectory());
if (settingsValue != null && !settingsValue.isEmpty()) {
outputDirectory = new File(settingsValue);
getLog().info(Locale.getString(CompileMojo.class, "OUTPUT_DIR_UPDATE", outputDirectory)); //$NON-NLS-1$
}
}
final MavenProject project = getProject();
final List<File> compileSourceRoots = new ArrayList<>();
for (final String filename : project.getCompileSourceRoots()) {
final File file = new File(filename);
if (!file.equals(outputDirectory)) {
compileSourceRoots.add(file);
}
}
final List<File> classPath = getClassPath();
project.addCompileSourceRoot(outputDirectory.getAbsolutePath());
compile(classPath, compileSourceRoots, outputDirectory);
}
}