/* * $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); } }