com.google.code.sbt.compiler.plugin.AbstractSBTCompileMojo.java Source code

Java tutorial

Introduction

Here is the source code for com.google.code.sbt.compiler.plugin.AbstractSBTCompileMojo.java

Source

/*
 * Copyright 2013-2016 Grzegorz Slowikowski (gslowikowski at gmail dot com)
 *
 * 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 com.google.code.sbt.compiler.plugin;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
import org.apache.maven.artifact.repository.ArtifactRepository;
import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
import org.apache.maven.artifact.resolver.ArtifactResolutionException;
import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
import org.apache.maven.artifact.resolver.ArtifactResolver;
import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
import org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;

import org.codehaus.plexus.util.DirectoryScanner;

import com.google.code.sbt.compiler.api.Compiler;
import com.google.code.sbt.compiler.api.CompilerConfiguration;
import com.google.code.sbt.compiler.api.CompilerException;
import com.google.code.sbt.compiler.api.Compilers;
import com.google.code.sbt.compiler.api.SourcePositionMapper;

/**
 * Abstract base class for SBT compilation mojos.
 * 
 * @author <a href="mailto:gslowikowski@gmail.com">Grzegorz Slowikowski</a>
 */
public abstract class AbstractSBTCompileMojo extends AbstractMojo {
    /**
     * Scala artifacts "groupId".
     */
    private static final String SCALA_GROUPID = "org.scala-lang";

    /**
     * Scala library "artifactId".
     */
    private static final String SCALA_LIBRARY_ARTIFACTID = "scala-library";

    /**
     * Scala compiler "artifactId".
     */
    private static final String SCALA_COMPILER_ARTIFACTID = "scala-compiler";

    /**
     * SBT artifacts "groupId".
     */
    private static final String SBT_GROUP_ID = "com.typesafe.sbt";

    /**
     * SBT compile interface "artifactId".
     */
    private static final String COMPILER_INTERFACE_ARTIFACT_ID = "compiler-interface";

    /**
     * SBT compile interface sources "classifier".
     */
    private static final String COMPILER_INTERFACE_CLASSIFIER = "sources";

    /**
     * SBT interface "artifactId".
     */
    private static final String XSBTI_ARTIFACT_ID = "sbt-interface";

    /**
     * Forced Scala version.<br>
     * <br>
     * If specified, this version of Scala compiler is used for compilation.<br>
     * If not specified, version of project's {@code org.scala-lang:scala-library} dependency is used.<br>
     * If there is no {@code org.scala-lang:scala-library} dependency in the project (in Java only projects),
     * selected compiler's {@link Compiler#getDefaultScalaVersion()} is used. 
     * 
     * @since 1.0.0
     */
    @Parameter(property = "scala.version")
    protected String scalaVersion;

    /**
     * Forced SBT version.<br>
     * <br>
     * Used to automatically select one of the "well known" SBT compilers if no compiler added explicitly as plugin's dependency.
     * There are three cases possible:
     * <ul>
     * <li>
     * If {@link #sbtVersion} is specified, compatible {@link Compiler} implementation
     * is selected and configured to use {@link #sbtVersion} SBT version for compilation.
     * </li>
     * <li>
     * If {@link #sbtVersion} is not specified, and {@link #playVersion} is specified
     * {@link #playVersion} parameter value is used to indirectly select compatible {@link Compiler} implementation
     * and it's {@link Compiler#getDefaultSbtVersion()} SBT version used for compilation.
     * </li>
     * <li>
     * If both {@link #sbtVersion} and {@link #playVersion} are not specified
     * the most recent {@link Compiler} implementation is selected
     * and it's {@link Compiler#getDefaultSbtVersion()} SBT version used for compilation.
     * </li>
     * </ul>
     * 
     * @since 1.0.0
     */
    @Parameter(property = "sbt.version")
    protected String sbtVersion;

    /**
     * Forced Play&#33; Framework version.
     * <br>
     * <br>
     * Used to select SBT version in Play&#33; Framework projects based on framework version used.
     * See {@link #sbtVersion} for more details.
     * 
     * @since 1.0.0
     */
    @Parameter(property = "play2.version")
    protected String playVersion;

    /**
     * Scala and Java source files encoding.
     * <br>
     * <br>
     * If not specified, the encoding value will be the value of the {@code file.encoding} system property.
     * <br>
     * <br>
     * {@code -encoding} option in {@link #scalacOptions} or {@link #javacOptions}
     * overrides this parameter for Scala or Java compiler respectively.
     * 
     * @since 1.0.0
     */
    @Parameter(property = "project.build.sourceEncoding")
    protected String sourceEncoding;

    /**
     * Additional parameters for Java compiler.
     * <br>
     * <br>
     * Space-separated list of parameters. Parameters containing spaces can be enclosed in apostrophes or quotes.
     * 
     * @since 1.0.0
     */
    @Parameter(property = "sbt.javacOptions", defaultValue = "-g")
    protected String javacOptions;

    /**
     * Additional parameters for Scala compiler.
     * <br>
     * <br>
     * Space-separated list of parameters. Parameters containing spaces can be enclosed in apostrophes or quotes.
     * 
     * @since 1.0.0
     */
    @Parameter(property = "sbt.scalacOptions", defaultValue = "-deprecation -unchecked")
    protected String scalacOptions;

    /**
     * Additional parameters for Scala compiler.
     * <br>
     * For internal use only.
     */
    @Parameter(property = "sbt._scalacOptions", defaultValue = "", readonly = true)
    protected String _scalacOptions;

    /**
     * Scala compiler plugins.
     * <br>
     * <br>
     * Space-separated list of plugin definitions in {@code groupId:artifactId:version} format.
     * 
     * @since 1.0.0
     */
    @Parameter(property = "sbt.scalacPlugins", defaultValue = "")
    protected String scalacPlugins;

    /**
     * Scala compiler plugins.
     * <br>
     * For internal use only.
     */
    @Parameter(property = "sbt._scalacPlugins", defaultValue = "", readonly = true)
    protected String _scalacPlugins;

    /**
     * Source position mappers.
     * <br>
     * <br>
     * Space-separated list of source position mapper definitions in {@code groupId:artifactId:version} format.
     * 
     * @since 1.0.0
     */
    @Parameter(property = "sbt.sourcePositionMappers", defaultValue = "", readonly = true)
    protected String sourcePositionMappers;

    /**
     * Source position mappers.
     * <br>
     * For internal use only.
     */
    @Parameter(property = "sbt._sourcePositionMappers", defaultValue = "", readonly = true)
    protected String _sourcePositionMappers;

    /**
     * Maven project to interact with.
     */
    @Parameter(defaultValue = "${project}", readonly = true, required = true)
    protected MavenProject project;

    /**
     * All Maven projects in the reactor.
     */
    @Parameter(defaultValue = "${reactorProjects}", required = true, readonly = true)
    protected List<MavenProject> reactorProjects;

    /**
     * Artifact factory used to look up artifacts in the remote repository.
     */
    @Component
    protected ArtifactFactory factory;

    /**
     * Artifact resolver used to resolve artifacts.
     */
    @Component
    protected ArtifactResolver resolver;

    /**
     * Location of the local repository.
     */
    @Parameter(property = "localRepository", readonly = true, required = true)
    protected ArtifactRepository localRepo;

    /**
     * Remote repositories used by the resolver
     */
    @Parameter(property = "project.remoteArtifactRepositories", readonly = true, required = true)
    protected List<ArtifactRepository> remoteRepos;

    /**
     * For retrieval of artifact's metadata.
     */
    @Component
    protected ArtifactMetadataSource metadataSource;

    /**
     * Plugin's groupId used for well known providers resolution
     */
    @Parameter(property = "plugin.groupId", readonly = true, required = true)
    private String pluginGroupId;

    /**
     * Plugin's version used for well known providers resolution
     */
    @Parameter(property = "plugin.version", readonly = true, required = true)
    private String pluginVersion;

    /**
     * Map of compiler implementations. For now only zero or one allowed.
     */
    @Component(role = Compiler.class)
    private Map<String, Compiler> compilers;

    /**
     * Performs compilation.
     * 
     * @throws MojoExecutionException if unexpected problem occurs
     * @throws MojoFailureException if expected problem (such as compilation failure) occurs
     */
    @Override
    public void execute() throws MojoExecutionException, MojoFailureException {
        if (!"pom".equals(project.getPackaging())) {
            long ts = System.currentTimeMillis();
            try {
                internalExecute();
            } finally {
                long te = System.currentTimeMillis();
                getLog().debug(String.format("Mojo execution time: %d ms", te - ts));
            }
        }
    }

    /**
     * Performs compilation.
     * 
     * @throws MojoExecutionException if unexpected problem occurs
     * @throws MojoFailureException if expected problem (such as compilation failure) occurs
     */
    protected void internalExecute() throws MojoExecutionException, MojoFailureException {
        List<String> compileSourceRoots = getCompileSourceRoots();

        if (compileSourceRoots.isEmpty())// ?
        {
            getLog().info("No sources to compile");

            return;
        }

        List<File> sourceRootDirs = new ArrayList<File>(compileSourceRoots.size());
        for (String compileSourceRoot : compileSourceRoots) {
            sourceRootDirs.add(new File(compileSourceRoot));
        }

        List<File> sourceFiles = getSourceFiles(sourceRootDirs);
        if (sourceFiles.isEmpty()) {
            getLog().info("No sources to compile");

            return;
        }

        try {
            Compiler sbtCompiler = getSbtCompiler();

            String resolvedScalaVersion = getScalaVersion(sbtCompiler);

            Artifact scalaLibraryArtifact = getResolvedArtifact(SCALA_GROUPID, SCALA_LIBRARY_ARTIFACTID,
                    resolvedScalaVersion);
            if (scalaLibraryArtifact == null) {
                throw new MojoExecutionException(String.format("Required %s:%s:%s:jar artifact not found",
                        SCALA_GROUPID, SCALA_LIBRARY_ARTIFACTID, resolvedScalaVersion));
            }

            Artifact scalaCompilerArtifact = getResolvedArtifact(SCALA_GROUPID, SCALA_COMPILER_ARTIFACTID,
                    resolvedScalaVersion);
            if (scalaCompilerArtifact == null) {
                throw new MojoExecutionException(String.format("Required %s:%s:%s:jar artifact not found",
                        SCALA_GROUPID, SCALA_COMPILER_ARTIFACTID, resolvedScalaVersion));
            }

            List<File> scalaExtraJars = getCompilerDependencies(scalaCompilerArtifact, scalaLibraryArtifact);

            String resolvedSbtVersion = sbtCompiler.getDefaultSbtVersion();

            Artifact xsbtiArtifact = getResolvedArtifact(SBT_GROUP_ID, XSBTI_ARTIFACT_ID, resolvedSbtVersion);
            if (xsbtiArtifact == null) {
                throw new MojoExecutionException(String.format("Required %s:%s:%s:jar dependency not found",
                        SBT_GROUP_ID, XSBTI_ARTIFACT_ID, resolvedSbtVersion));
            }

            Artifact compilerInterfaceSrc = getResolvedArtifact(SBT_GROUP_ID, COMPILER_INTERFACE_ARTIFACT_ID,
                    resolvedSbtVersion, COMPILER_INTERFACE_CLASSIFIER);
            if (compilerInterfaceSrc == null) {
                throw new MojoExecutionException(
                        String.format("Required %s:%s:%s:%s:jar dependency not found", SBT_GROUP_ID,
                                COMPILER_INTERFACE_ARTIFACT_ID, resolvedSbtVersion, COMPILER_INTERFACE_CLASSIFIER));
            }

            List<String> classpathElements = getClasspathElements();
            classpathElements.remove(getOutputDirectory().getAbsolutePath());
            List<File> classpathFiles = new ArrayList<File>(classpathElements.size());
            for (String path : classpathElements) {
                classpathFiles.add(new File(path));
            }

            String resolvedScalacOptions = scalacOptions.trim();
            if (_scalacOptions != null && _scalacOptions.length() > 0) {
                resolvedScalacOptions = resolvedScalacOptions + ' ' + _scalacOptions;
            }

            if ((scalacPlugins != null && scalacPlugins.trim().length() > 0)
                    || (_scalacPlugins != null && _scalacPlugins.trim().length() > 0)) {
                List<Artifact> resolvedScalaCompilerPluginArtifacts = new ArrayList<Artifact>();
                resolveArtifacts(resolvedScalaCompilerPluginArtifacts, scalacPlugins);
                resolveArtifacts(resolvedScalaCompilerPluginArtifacts, _scalacPlugins);
                for (Artifact artifact : resolvedScalaCompilerPluginArtifacts) {
                    String arg = "-Xplugin:" + artifact.getFile().getAbsolutePath();
                    if (arg.indexOf(' ') >= 0) {
                        arg = '\"' + arg + '\"';
                    }
                    resolvedScalacOptions = resolvedScalacOptions + ' ' + arg;
                }
            }

            SourcePositionMapper sourcePositionMapper = null;
            if (sbtCompiler.supportsSourcePositionMappers()) {
                if ((sourcePositionMappers != null && sourcePositionMappers.trim().length() > 0)
                        || (_sourcePositionMappers != null && _sourcePositionMappers.trim().length() > 0)) {
                    List<Artifact> resolvedSourcePositionMapperArtifacts = new ArrayList<Artifact>();
                    resolveArtifacts(resolvedSourcePositionMapperArtifacts, sourcePositionMappers);
                    resolveArtifacts(resolvedSourcePositionMapperArtifacts, _sourcePositionMappers);
                    if (!resolvedSourcePositionMapperArtifacts.isEmpty()) {
                        Set<Artifact> resolvedSourcePositionMapperArtifactSet = getAllDependencies(
                                new HashSet<Artifact>(resolvedSourcePositionMapperArtifacts), null /* filter */ );
                        List<SourcePositionMapper> resolvedSourcePositionMappers = resolveSourcePositionMappers(
                                resolvedSourcePositionMapperArtifactSet);
                        if (resolvedSourcePositionMappers != null) {
                            sourcePositionMapper = new SourcePositionMapperCollection(
                                    resolvedSourcePositionMappers);
                            sourcePositionMapper.setCharsetName(sourceEncoding);
                        }
                    }
                }
            }

            CompilerConfiguration configuration = new CompilerConfiguration();
            configuration.setSourceFiles(sourceFiles);
            configuration.setScalaLibraryFile(scalaLibraryArtifact.getFile());
            configuration.setScalaCompilerFile(scalaCompilerArtifact.getFile());
            configuration.setScalaExtraJarFiles(scalaExtraJars);
            configuration.setXsbtiFile(xsbtiArtifact.getFile());
            configuration.setCompilerInterfaceSrcFile(compilerInterfaceSrc.getFile());
            configuration.setClasspathFiles(classpathFiles);
            configuration.setLogger(new MavenCompilerLogger(getLog()));
            configuration.setOutputDirectory(getOutputDirectory());
            configuration.setSourceEncoding(sourceEncoding);
            configuration.setJavacOptions(javacOptions);
            configuration.setScalacOptions(resolvedScalacOptions);
            configuration.setAnalysisCacheFile(getAnalysisCacheFile());
            configuration.setAnalysisCacheMap(getAnalysisCacheMap());
            configuration.setSourcePositionMapper(sourcePositionMapper);

            sbtCompiler.performCompile(configuration);
        } catch (CompilerException e) {
            throw new MojoFailureException("Scala compilation failed", e);
        } catch (ArtifactNotFoundException e) {
            throw new MojoExecutionException("Scala compilation failed", e);
        } catch (ArtifactResolutionException e) {
            throw new MojoExecutionException("Scala compilation failed", e);
        } catch (MalformedURLException e) {
            throw new MojoExecutionException("Scala compilation failed", e);
        }
    }

    /**
     * Returns compilation classpath elements.
     * 
     * @return classpath elements
     */
    protected abstract List<String> getClasspathElements();

    /**
     * Returns compilation source roots.
     * 
     * @return source roots
     */
    protected abstract List<String> getCompileSourceRoots();

    /**
     * Returns source inclusion filters for the compiler.
     * 
     * @return source inclusion filters
     */
    protected abstract Set<String> getSourceIncludes();

    /**
     * Returns source exclusion filters for the compiler.
     * 
     * @return source exclusion filters
     */
    protected abstract Set<String> getSourceExcludes();

    /**
     * Returns output directory.
     * 
     * @return output directory
     */
    protected abstract File getOutputDirectory();

    /**
     * Returns SBT incremental compilation analysis cache file location for this project.
     * 
     * @return analysis cache file
     */
    protected abstract File getAnalysisCacheFile();

    /**
     * Returns SBT incremental compilation analysis cache file locations map for all reactor projects.
     * 
     * @return analysis cache files map
     */
    protected abstract Map<File, File> getAnalysisCacheMap();

    private Artifact getDependencyArtifact(Collection<?> classPathArtifacts, String groupId, String artifactId,
            String type) {
        Artifact result = null;
        for (Iterator<?> iter = classPathArtifacts.iterator(); iter.hasNext();) {
            Artifact artifact = (Artifact) iter.next();
            if (groupId.equals(artifact.getGroupId()) && artifactId.equals(artifact.getArtifactId())
                    && type.equals(artifact.getType())) {
                result = artifact;
                break;
            }
        }
        return result;
    }

    private List<File> getSourceFiles(List<File> sourceRootDirs) {
        List<File> sourceFiles = new ArrayList<File>();

        Set<String> sourceIncludes = getSourceIncludes();
        if (sourceIncludes.isEmpty()) {
            sourceIncludes.add("**/*.java");
            sourceIncludes.add("**/*.scala");
        }
        Set<String> sourceExcludes = getSourceExcludes();

        DirectoryScanner scanner = new DirectoryScanner();
        scanner.setIncludes(sourceIncludes.toArray(new String[sourceIncludes.size()]));
        if (!sourceExcludes.isEmpty()) {
            scanner.setExcludes(sourceExcludes.toArray(new String[sourceExcludes.size()]));
        }
        scanner.addDefaultExcludes();

        for (File dir : sourceRootDirs) {
            if (dir.isDirectory()) {
                scanner.setBasedir(dir);
                scanner.scan();
                String[] includedFileNames = scanner.getIncludedFiles();
                for (String includedFileName : includedFileNames) {
                    File tmpAbsFile = new File(dir, includedFileName).getAbsoluteFile(); // ?
                    sourceFiles.add(tmpAbsFile);
                }
            }
        }
        // scalac is sensible to scala file order, file system can't guarantee file order => unreproductible build error
        // across platform
        // to guarantee reproductible command line we order file by path (os dependend).
        // Collections.sort( sourceFiles );
        return sourceFiles;
    }

    private String getScalaVersion(Compiler sbtCompiler) {
        String result = scalaVersion;
        if (result == null || result.length() == 0) {
            Artifact scalaLibraryArtifact = getDependencyArtifact(project.getArtifacts(), SCALA_GROUPID,
                    SCALA_LIBRARY_ARTIFACTID, "jar");
            if (scalaLibraryArtifact != null) {
                result = scalaLibraryArtifact.getVersion();
            } else {
                result = sbtCompiler.getDefaultScalaVersion();
            }
        }
        return result;
    }

    /**
     * Returns SBT incremental main compilation analysis cache file location for a project.
     * 
     * @param p Maven project
     * @return analysis cache file location
     */
    protected File defaultAnalysisCacheFile(MavenProject p) {
        File classesDirectory = new File(p.getBuild().getOutputDirectory());
        return new File(Compilers.getCacheDirectory(classesDirectory), "compile");
    }

    /**
     * Returns SBT incremental test compilation analysis cache file location for a project.
     * 
     * @param p Maven project
     * @return analysis cache file location
     */
    protected File defaultTestAnalysisCacheFile(MavenProject p) {
        File testClassesDirectory = new File(p.getBuild().getTestOutputDirectory());
        return new File(Compilers.getCacheDirectory(testClassesDirectory), "test-compile");
    }

    // Private utility methods

    private Artifact getResolvedArtifact(String groupId, String artifactId, String version)
            throws ArtifactNotFoundException, ArtifactResolutionException {
        Artifact artifact = factory.createArtifact(groupId, artifactId, version, Artifact.SCOPE_RUNTIME, "jar");
        resolver.resolve(artifact, remoteRepos, localRepo);
        return artifact;
    }

    private Artifact getResolvedArtifact(String groupId, String artifactId, String version, String classifier)
            throws ArtifactNotFoundException, ArtifactResolutionException {
        Artifact artifact = factory.createArtifactWithClassifier(groupId, artifactId, version, "jar", classifier);
        resolver.resolve(artifact, remoteRepos, localRepo);
        return artifact;
    }

    private List<File> getCompilerDependencies(Artifact scalaCompilerArtifact, Artifact scalaLibraryArtifact)
            throws ArtifactNotFoundException, ArtifactResolutionException {
        ArtifactFilter scalaLibraryFilter = new ExcludesArtifactFilter(Collections
                .singletonList(scalaLibraryArtifact.getGroupId() + ":" + scalaLibraryArtifact.getArtifactId()));
        List<File> d = new ArrayList<File>();
        for (Artifact artifact : getAllDependencies(scalaCompilerArtifact, scalaLibraryFilter)) {
            if (!scalaCompilerArtifact.getGroupId().equals(artifact.getGroupId())
                    || !scalaCompilerArtifact.getArtifactId().equals(artifact.getArtifactId())) {
                d.add(artifact.getFile()); // don't add scalaCompilerArtifact file
            }
        }
        return d;
    }

    private Set<Artifact> getAllDependencies(Artifact artifact, ArtifactFilter filter)
            throws ArtifactNotFoundException, ArtifactResolutionException {
        return getAllDependencies(Collections.singleton(artifact), filter);
    }

    private Set<Artifact> getAllDependencies(Set<Artifact> artifacts, ArtifactFilter filter)
            throws ArtifactNotFoundException, ArtifactResolutionException {
        Artifact originatingArtifact = factory.createBuildArtifact("dummy", "dummy", "1.0", "jar");
        ArtifactResolutionResult resolutionResult = resolver.resolveTransitively(artifacts, originatingArtifact,
                localRepo, remoteRepos, metadataSource, filter);
        return resolutionResult.getArtifacts();
    }

    // Cached classloaders
    private static final ConcurrentHashMap<String, ClassLoader> cachedClassLoaders = new ConcurrentHashMap<String, ClassLoader>(
            2);

    private static ClassLoader getCachedClassLoader(String compilerId) {
        return cachedClassLoaders.get(compilerId);
    }

    private static void setCachedClassLoader(String compilerId, ClassLoader classLoader) {
        cachedClassLoaders.put(compilerId, classLoader);
    }

    private Compiler getSbtCompiler() throws MojoExecutionException {
        Compiler sbtCompiler = null;
        if (!compilers.isEmpty()) {
            sbtCompiler = getDeclaredSbtCompiler();
        } else {
            sbtCompiler = getWellKnownSbtCompiler();
        }
        return sbtCompiler;
    }

    private Compiler getDeclaredSbtCompiler() throws MojoExecutionException {
        if (compilers.size() > 1) {
            throw new MojoExecutionException("Too many compiles defined. A maximum of one allowed.");
        }

        Map.Entry<String, Compiler> compilerEntry = compilers.entrySet().iterator().next();
        String compilerId = compilerEntry.getKey();
        Compiler sbtCompiler = compilerEntry.getValue();

        getLog().debug(String.format("Using declared compiler \"%s\".", compilerId));

        return sbtCompiler;
    }

    private Compiler getWellKnownSbtCompiler() throws MojoExecutionException {
        try {
            String compilerId = Compilers.getDefaultCompilerId(sbtVersion, playVersion);
            ClassLoader compilerClassLoader = getCachedClassLoader(compilerId);
            if (compilerClassLoader == null) {
                getLog().debug(String.format("Cached classloader for compiler \"%s\" not available.", compilerId));
            } else {
                if (compilerClassLoader.getParent() == Thread.currentThread().getContextClassLoader()) {
                    getLog().debug(String.format("Using cached classloader for compiler \"%s\".", compilerId));
                } else {
                    getLog().debug(String.format(
                            "Invalidated cached classloader for compiler \"%s\". Parent classloader changed from %d to %d.",
                            compilerId, Integer.valueOf(compilerClassLoader.getParent().hashCode()),
                            Integer.valueOf(Thread.currentThread().getContextClassLoader().hashCode())));
                    compilerClassLoader = null;
                }
            }
            if (compilerClassLoader == null) {
                Artifact compilerArtifact = getResolvedArtifact(pluginGroupId, "sbt-compiler-" + compilerId,
                        pluginVersion);

                Set<Artifact> compilerDependencies = getAllDependencies(compilerArtifact, null);
                List<File> classPathFiles = new ArrayList<File>(compilerDependencies.size() + 1);
                //                classPathFiles.add( compilerArtifact.getFile() );
                for (Artifact dependencyArtifact : compilerDependencies) {
                    classPathFiles.add(dependencyArtifact.getFile());
                }
                String javaHome = System.getProperty("java.home");
                classPathFiles.add(new File(javaHome, "../lib/tools.jar"));

                List<URL> classPathUrls = new ArrayList<URL>(classPathFiles.size());
                for (File classPathFile : classPathFiles) {
                    classPathUrls.add(new URL(classPathFile.toURI().toASCIIString()));
                }

                compilerClassLoader = new URLClassLoader(classPathUrls.toArray(new URL[classPathUrls.size()]),
                        Thread.currentThread().getContextClassLoader());
                getLog().debug(
                        String.format("Setting cached classloader for compiler \"%s\" with parent classloader %d",
                                compilerId, Integer.valueOf(compilerClassLoader.getParent().hashCode())));
                setCachedClassLoader(compilerId, compilerClassLoader);
            }

            ServiceLoader<Compiler> compilerServiceLoader = ServiceLoader.load(Compiler.class, compilerClassLoader);
            // get first (there should be exactly one)
            Compiler sbtCompiler = compilerServiceLoader.iterator().next();

            getLog().debug(String.format("Using autodetected compiler \"%s\".", compilerId));

            return sbtCompiler;
        } catch (ArtifactNotFoundException e) {
            throw new MojoExecutionException("Compiler autodetection failed", e);
        } catch (ArtifactResolutionException e) {
            throw new MojoExecutionException("Compiler autodetection failed", e);
        } catch (MalformedURLException e) {
            throw new MojoExecutionException("Compiler autodetection failed", e);
        }
    }

    // "artifactGAVs" in format "groupId1:artifactId1:version1 groupId2:artifactId2:version2"
    private void resolveArtifacts(List<Artifact> artifacts, String artifactGAVs)
            throws ArtifactNotFoundException, ArtifactResolutionException {
        if (artifactGAVs != null && artifactGAVs.trim().length() > 0) {
            String[] scalacPluginsGAVs = artifactGAVs.trim().split(" ");
            for (String scalacPluginGAV : scalacPluginsGAVs) {
                String[] gav = scalacPluginGAV.split(":");
                String groupId = gav[0];
                String artifactId = gav[1];
                String version = gav[2];
                Artifact scalacPluginArtifact = getResolvedArtifact(groupId, artifactId, version);
                if (scalacPluginArtifact != null) {
                    artifacts.add(scalacPluginArtifact);
                }
            }
        }
    }

    private List<SourcePositionMapper> resolveSourcePositionMappers(
            Set<Artifact> resolvedSourcePositionMapperArtifacts) throws MalformedURLException {
        List<SourcePositionMapper> result = null;

        List<URL> classPathUrls = new ArrayList<URL>(resolvedSourcePositionMapperArtifacts.size());
        for (Artifact artifact : resolvedSourcePositionMapperArtifacts) {
            classPathUrls.add(new URL(artifact.getFile().toURI().toASCIIString()));
        }
        ClassLoader tmpMappersClassLoader = new URLClassLoader(classPathUrls.toArray(new URL[classPathUrls.size()]),
                Thread.currentThread().getContextClassLoader());
        ServiceLoader<SourcePositionMapper> spmServiceLoader = ServiceLoader.load(SourcePositionMapper.class,
                tmpMappersClassLoader);
        Iterator<SourcePositionMapper> spmIterator = spmServiceLoader.iterator();
        if (spmIterator.hasNext()) {
            result = new ArrayList<SourcePositionMapper>();
            while (spmIterator.hasNext()) {
                result.add(spmIterator.next());
            }
        }

        return result;
    }

}