/* * Copyright 2009 the original author 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 org.gradle.api.tasks.compile; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import org.gradle.api.Incubating; import org.gradle.api.InvalidUserDataException; import org.gradle.api.JavaVersion; import org.gradle.api.file.FileCollection; import org.gradle.api.file.FileTree; import org.gradle.api.internal.project.ProjectInternal; import org.gradle.api.internal.tasks.JavaToolChainFactory; import org.gradle.api.internal.tasks.compile.AnnotationProcessorDetector; import org.gradle.api.internal.tasks.compile.CleaningGroovyCompiler; import org.gradle.api.internal.tasks.compile.CompilerForkUtils; import org.gradle.api.internal.tasks.compile.DefaultGroovyJavaJointCompileSpec; import org.gradle.api.internal.tasks.compile.DefaultGroovyJavaJointCompileSpecFactory; import org.gradle.api.internal.tasks.compile.GroovyCompilerFactory; import org.gradle.api.internal.tasks.compile.GroovyJavaJointCompileSpec; import org.gradle.api.internal.tasks.compile.JavaCompilerFactory; import org.gradle.api.tasks.CacheableTask; import org.gradle.api.tasks.Classpath; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.Internal; import org.gradle.api.tasks.Nested; import org.gradle.api.tasks.PathSensitive; import org.gradle.api.tasks.PathSensitivity; import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.WorkResult; import org.gradle.jvm.toolchain.JavaToolChain; import org.gradle.language.base.internal.compile.Compiler; import org.gradle.util.GFileUtils; import org.gradle.workers.internal.IsolatedClassloaderWorkerFactory; import org.gradle.workers.internal.WorkerDaemonFactory; import javax.inject.Inject; import java.io.File; import java.util.List; /** * Compiles Groovy source files, and optionally, Java source files. */ @CacheableTask public class GroovyCompile extends AbstractCompile { private Compiler<GroovyJavaJointCompileSpec> compiler; private FileCollection groovyClasspath; private final CompileOptions compileOptions = new CompileOptions(); private final GroovyCompileOptions groovyCompileOptions = new GroovyCompileOptions(); public GroovyCompile() { CompilerForkUtils.doNotCacheIfForkingViaExecutable(compileOptions, getOutputs()); } @Override @TaskAction protected void compile() { checkGroovyClasspathIsNonEmpty(); DefaultGroovyJavaJointCompileSpec spec = createSpec(); WorkResult result = getCompiler(spec).execute(spec); setDidWork(result.getDidWork()); } private Compiler<GroovyJavaJointCompileSpec> getCompiler(GroovyJavaJointCompileSpec spec) { if (compiler == null) { ProjectInternal projectInternal = (ProjectInternal) getProject(); WorkerDaemonFactory workerDaemonFactory = getServices().get(WorkerDaemonFactory.class); IsolatedClassloaderWorkerFactory inProcessWorkerFactory = getServices().get(IsolatedClassloaderWorkerFactory.class); JavaCompilerFactory javaCompilerFactory = getServices().get(JavaCompilerFactory.class); GroovyCompilerFactory groovyCompilerFactory = new GroovyCompilerFactory(projectInternal, javaCompilerFactory, workerDaemonFactory, inProcessWorkerFactory); Compiler<GroovyJavaJointCompileSpec> delegatingCompiler = groovyCompilerFactory.newCompiler(spec); compiler = new CleaningGroovyCompiler(delegatingCompiler, getOutputs()); } return compiler; } private DefaultGroovyJavaJointCompileSpec createSpec() { DefaultGroovyJavaJointCompileSpec spec = new DefaultGroovyJavaJointCompileSpecFactory(compileOptions).create(); spec.setSource(getSource()); spec.setDestinationDir(getDestinationDir()); spec.setWorkingDir(getProject().getProjectDir()); spec.setTempDir(getTemporaryDir()); spec.setCompileClasspath(ImmutableList.copyOf(getClasspath())); spec.setSourceCompatibility(getSourceCompatibility()); spec.setTargetCompatibility(getTargetCompatibility()); spec.setAnnotationProcessorPath(calculateAnnotationProcessorClasspath()); spec.setGroovyClasspath(Lists.newArrayList(getGroovyClasspath())); spec.setCompileOptions(compileOptions); spec.setGroovyCompileOptions(groovyCompileOptions); if (spec.getGroovyCompileOptions().getStubDir() == null) { File dir = new File(getTemporaryDir(), "groovy-java-stubs"); GFileUtils.mkdirs(dir); spec.getGroovyCompileOptions().setStubDir(dir); } return spec; } private List<File> calculateAnnotationProcessorClasspath() { AnnotationProcessorDetector annotationProcessorDetector = getServices().get(AnnotationProcessorDetector.class); FileCollection processorClasspath = annotationProcessorDetector.getEffectiveAnnotationProcessorClasspath(compileOptions, getClasspath()); return Lists.newArrayList(processorClasspath); } private void checkGroovyClasspathIsNonEmpty() { if (getGroovyClasspath().isEmpty()) { throw new InvalidUserDataException("'" + getName() + ".groovyClasspath' must not be empty. If a Groovy compile dependency is provided, " + "the 'groovy-base' plugin will attempt to configure 'groovyClasspath' automatically. Alternatively, you may configure 'groovyClasspath' explicitly."); } } /** * We need to track the Java version of the JVM the Groovy compiler is running on, since the Groovy compiler produces different results depending on it. * * This should be replaced by a property on the Groovy toolchain as soon as we model these. * * @since 4.0 */ @Incubating @Input protected String getGroovyCompilerJvmVersion() { return JavaVersion.current().getMajorVersion(); } /** * We need to track the toolchain used by the Groovy compiler to compile Java sources. * * @since 4.0 */ @Nested @Incubating protected JavaToolChain getJavaToolChain() { return getJavaToolChainFactory().forCompileOptions(getOptions()); } /** * {@inheritDoc} */ @Override @PathSensitive(PathSensitivity.NAME_ONLY) // Java source files are supported, too. Therefore we should care about the names. public FileTree getSource() { return super.getSource(); } /** * Gets the options for the Groovy compilation. To set specific options for the nested Java compilation, use {@link * #getOptions()}. * * @return The Groovy compile options. Never returns null. */ @Nested public GroovyCompileOptions getGroovyOptions() { return groovyCompileOptions; } /** * Returns the options for Java compilation. * * @return The Java compile options. Never returns null. */ @Nested public CompileOptions getOptions() { return compileOptions; } /** * Returns the classpath containing the version of Groovy to use for compilation. * * @return The classpath. */ @Classpath public FileCollection getGroovyClasspath() { return groovyClasspath; } /** * Sets the classpath containing the version of Groovy to use for compilation. * * @param groovyClasspath The classpath. Must not be null. */ public void setGroovyClasspath(FileCollection groovyClasspath) { this.groovyClasspath = groovyClasspath; } @Internal public Compiler<GroovyJavaJointCompileSpec> getCompiler() { return getCompiler(createSpec()); } public void setCompiler(Compiler<GroovyJavaJointCompileSpec> compiler) { this.compiler = compiler; } @Inject protected JavaToolChainFactory getJavaToolChainFactory() { throw new UnsupportedOperationException(); } }