/**
* Copyright (c) 2014 Takari, Inc.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package io.takari.maven.plugins.compile.javac;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.maven.plugin.MojoExecutionException;
import io.takari.incrementalbuild.Resource;
import io.takari.incrementalbuild.ResourceMetadata;
import io.takari.incrementalbuild.ResourceStatus;
import io.takari.maven.plugins.compile.AbstractCompileMojo.AccessRulesViolation;
import io.takari.maven.plugins.compile.AbstractCompileMojo.Debug;
import io.takari.maven.plugins.compile.AbstractCompileMojo.Proc;
import io.takari.maven.plugins.compile.AbstractCompiler;
import io.takari.maven.plugins.compile.CompilerBuildContext;
import io.takari.maven.plugins.compile.ProjectClasspathDigester;
import io.takari.maven.plugins.compile.jdt.CompilerJdt;
public abstract class AbstractCompilerJavac extends AbstractCompiler {
protected static final boolean isJava8orBetter;
static {
boolean _isJava8orBetter = true;
try {
Class.forName("java.util.Base64");
} catch (Exception e) {
_isJava8orBetter = false;
}
isJava8orBetter = _isJava8orBetter;
}
private final ProjectClasspathDigester digester;
private final List<ResourceMetadata<File>> sources = new ArrayList<ResourceMetadata<File>>();
private String classpath;
private String sourcepath = "";
protected AbstractCompilerJavac(CompilerBuildContext context, ProjectClasspathDigester digester) {
super(context);
this.digester = digester;
}
protected List<String> getCompilerOptions() {
List<String> options = new ArrayList<String>();
// output directory
options.add("-d");
options.add(getOutputDirectory().getAbsolutePath());
options.add("-source");
options.add(getSource());
if (getTarget() != null) {
options.add("-target");
options.add(getTarget());
}
options.add("-classpath");
options.add(classpath);
options.add("-Xprefer:source");
options.add("-sourcepath");
options.add(sourcepath);
// http://docs.oracle.com/javase/7/docs/technotes/tools/windows/javac.html#implicit
options.add("-implicit:none");
switch (getProc()) {
case only:
case onlyEX:
options.add("-proc:only");
break;
case proc:
case procEX:
// this is the javac default
break;
case none:
options.add("-proc:none");
break;
}
if (getProc() != Proc.none) {
options.add("-s");
options.add(getGeneratedSourcesDirectory().getAbsolutePath());
if (getAnnotationProcessors() != null) {
options.add("-processor");
StringBuilder processors = new StringBuilder();
for (String processor : getAnnotationProcessors()) {
if (processors.length() > 0) {
processors.append(',');
}
processors.append(processor);
}
options.add(processors.toString());
}
if (getAnnotationProcessorOptions() != null) {
for (Map.Entry<String, String> option : getAnnotationProcessorOptions().entrySet()) {
options.add("-A" + option.getKey() + "=" + option.getValue());
}
}
}
if (isVerbose()) {
options.add("-verbose");
}
if (isParameters()) {
options.add("-parameters");
}
Set<Debug> debug = getDebug();
if (debug == null || debug.contains(Debug.all)) {
options.add("-g");
} else if (debug.contains(Debug.none)) {
options.add("-g:none");
} else {
StringBuilder keywords = new StringBuilder();
for (Debug keyword : debug) {
if (keywords.length() > 0) {
keywords.append(',');
}
keywords.append(keyword.name());
}
options.add("-g:" + keywords.toString());
}
if (isShowWarnings()) {
options.add("-Xlint:all");
} else {
options.add("-Xlint:none");
}
return options;
}
@Override
public boolean setClasspath(List<File> dependencies, File mainClasses, Set<File> directDependencies) throws IOException {
List<File> classpath = new ArrayList<>();
if (mainClasses != null) {
classpath.add(mainClasses);
}
classpath.addAll(dependencies);
if (log.isDebugEnabled()) {
StringBuilder msg = new StringBuilder();
for (File element : classpath) {
msg.append("\nāā ").append(element);
}
log.debug("Compile classpath: {} entries{}", classpath.size(), msg.toString());
}
StringBuilder cp = new StringBuilder();
cp.append(getOutputDirectory().getAbsolutePath());
for (File dependency : classpath) {
if (dependency != null) {
cp.append(File.pathSeparatorChar).append(dependency.getAbsolutePath());
}
}
this.classpath = cp.toString();
return digester.digestClasspath(classpath);
}
@Override
public boolean setSourcepath(List<File> dependencies) throws IOException {
StringBuilder cp = new StringBuilder();
for (File dependency : dependencies) {
if (dependency != null) {
cp.append(File.pathSeparatorChar).append(dependency.getAbsolutePath());
}
}
this.sourcepath = cp.toString();
return digester.digestSourcepath(dependencies);
}
@Override
public boolean setSources(List<ResourceMetadata<File>> sources) {
this.sources.addAll(sources);
List<ResourceMetadata<File>> modifiedSources = new ArrayList<ResourceMetadata<File>>();
List<ResourceMetadata<File>> inputs = new ArrayList<ResourceMetadata<File>>();
for (ResourceMetadata<File> input : sources) {
inputs.add(input);
if (input.getStatus() != ResourceStatus.UNMODIFIED) {
modifiedSources.add(input);
}
}
Collection<ResourceMetadata<File>> deletedSources = context.getRemovedSources();
if (!context.isEscalated() && log.isDebugEnabled()) {
StringBuilder inputsMsg = new StringBuilder("Modified inputs:");
for (ResourceMetadata<File> input : modifiedSources) {
inputsMsg.append("\n ").append(input.getStatus()).append(" ").append(input.getResource());
}
for (ResourceMetadata<File> input : deletedSources) {
inputsMsg.append("\n ").append(input.getStatus()).append(" ").append(input.getResource());
}
log.debug(inputsMsg.toString());
}
return !modifiedSources.isEmpty() || !deletedSources.isEmpty();
}
@Override
public void setPrivatePackageReference(AccessRulesViolation accessRulesViolation) {
if (accessRulesViolation == AccessRulesViolation.error) {
String msg = String.format("Compiler %s does not support privatePackageReference=error, use compilerId=%s", getCompilerId(), CompilerJdt.ID);
throw new IllegalArgumentException(msg);
}
}
@Override
public void setTransitiveDependencyReference(AccessRulesViolation accessRulesViolation) {
if (accessRulesViolation == AccessRulesViolation.error) {
String msg = String.format("Compiler %s does not support transitiveDependencyReference=error, use compilerId=%s", getCompilerId(), CompilerJdt.ID);
throw new IllegalArgumentException(msg);
}
}
@Override
public boolean setProcessorpath(List<File> processorpath) throws IOException {
if (processorpath != null) {
throw new IllegalArgumentException();
}
return false;
}
@Override
public final int compile() throws MojoExecutionException, IOException {
// eagerly delete all outputs.
// otherwise javac may use stale outputs and resolve types that will be deleted at the end of the build
context.deleteOutputs();
// everything is being rebuilt, mark as processed
Map<File, Resource<File>> files = new LinkedHashMap<>(sources.size());
for (ResourceMetadata<File> input : sources) {
files.put(input.getResource(), input.process());
}
return compile(files);
}
protected abstract int compile(Map<File, Resource<File>> sources) throws MojoExecutionException, IOException;
protected abstract String getCompilerId();
}