Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/gh/issues/9897' into release
Browse files Browse the repository at this point in the history
* origin/gh/issues/9897:
  Make errorprone smoke test work with Java 8
  Inject a filtering classloader for java annotation processor classpath
  Reproduce #9897
  • Loading branch information
big-guy committed Jul 9, 2019
2 parents 34abdbc + 96dce94 commit d58bdd6
Show file tree
Hide file tree
Showing 7 changed files with 171 additions and 15 deletions.
Expand Up @@ -26,7 +26,6 @@
import org.gradle.api.internal.tasks.compile.processing.NonIncrementalProcessor;
import org.gradle.api.internal.tasks.compile.processing.SupportedOptionsCollectingProcessor;
import org.gradle.api.internal.tasks.compile.processing.TimeTrackingProcessor;
import org.gradle.internal.classloader.FilteringClassLoader;
import org.gradle.internal.classpath.DefaultClassPath;
import org.gradle.internal.concurrent.CompositeStoppable;

Expand All @@ -39,6 +38,8 @@
import java.util.Locale;
import java.util.Set;

import static org.gradle.api.internal.tasks.compile.filter.AnnotationProcessorFilter.*;

/**
* Wraps another {@link JavaCompiler.CompilationTask} and sets up its annotation processors
* according to the provided processor declarations and processor path. Incremental processors
Expand Down Expand Up @@ -119,23 +120,10 @@ private void setupProcessors() {
private URLClassLoader createProcessorClassLoader() {
return new URLClassLoader(
DefaultClassPath.of(annotationProcessorPath).getAsURLArray(),
new FilteringClassLoader(delegate.getClass().getClassLoader(), getExtraAllowedPackages())
getFilteredClassLoader(delegate.getClass().getClassLoader())
);
}

/**
* Many popular annotation processors like lombok need access to compiler internals
* to do their magic, e.g. to inspect or even change method bodies. This is not valid
* according to the annotation processing spec, but forbidding it would upset a lot of
* our users.
*/
private FilteringClassLoader.Spec getExtraAllowedPackages() {
FilteringClassLoader.Spec spec = new FilteringClassLoader.Spec();
spec.allowPackage("com.sun.tools.javac");
spec.allowPackage("com.sun.source");
return spec;
}

private Class<?> loadProcessor(AnnotationProcessorDeclaration declaredProcessor) {
try {
return processorClassloader.loadClass(declaredProcessor.getClassName());
Expand Down
Expand Up @@ -17,6 +17,7 @@

import org.gradle.api.JavaVersion;
import org.gradle.api.internal.tasks.compile.processing.AnnotationProcessorDeclaration;
import org.gradle.api.internal.tasks.compile.reflect.FilteringClassLoaderInjectingProxy;
import org.gradle.api.internal.tasks.compile.reflect.SourcepathIgnoringProxy;
import org.gradle.api.tasks.WorkResult;
import org.gradle.internal.Factory;
Expand Down Expand Up @@ -66,6 +67,7 @@ private JavaCompiler.CompilationTask createCompileTask(JavaCompileSpec spec, Jdk
if (JavaVersion.current().isJava9Compatible() && emptySourcepathIn(options)) {
fileManager = (StandardJavaFileManager) SourcepathIgnoringProxy.proxy(standardFileManager, StandardJavaFileManager.class);
}
fileManager = (StandardJavaFileManager) FilteringClassLoaderInjectingProxy.proxy(fileManager, StandardJavaFileManager.class);
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, options, spec.getClasses(), compilationUnits);

Set<AnnotationProcessorDeclaration> annotationProcessors = spec.getEffectiveAnnotationProcessors();
Expand Down
@@ -0,0 +1,38 @@
/*
* Copyright 2019 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.internal.tasks.compile.filter;

import org.gradle.internal.classloader.FilteringClassLoader;

public class AnnotationProcessorFilter {
public static FilteringClassLoader getFilteredClassLoader(ClassLoader parent) {
return new FilteringClassLoader(parent, getExtraAllowedPackages());
}

/**
* Many popular annotation processors like lombok need access to compiler internals
* to do their magic, e.g. to inspect or even change method bodies. This is not valid
* according to the annotation processing spec, but forbidding it would upset a lot of
* our users.
*/
private static FilteringClassLoader.Spec getExtraAllowedPackages() {
FilteringClassLoader.Spec spec = new FilteringClassLoader.Spec();
spec.allowPackage("com.sun.tools.javac");
spec.allowPackage("com.sun.source");
return spec;
}
}
@@ -0,0 +1,52 @@
/*
* Copyright 2019 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.internal.tasks.compile.reflect;

import javax.tools.StandardLocation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.net.URLClassLoader;

import static org.gradle.api.internal.tasks.compile.filter.AnnotationProcessorFilter.getFilteredClassLoader;

/**
* This class injects a filtering classloader when the compiler uses the standard java annotation processor path.
* This prevents Gradle classes or external libraries from being visible on the annotation processor path.
*/
public class FilteringClassLoaderInjectingInvocationHandler implements InvocationHandler {
public static final String GET_CLASS_LOADER = "getClassLoader";
private final Object proxied;

public FilteringClassLoaderInjectingInvocationHandler(Object proxied) {
this.proxied = proxied;
}

@Override
@SuppressWarnings("unchecked")
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getName().equals(GET_CLASS_LOADER) && args[0] == StandardLocation.ANNOTATION_PROCESSOR_PATH) {
ClassLoader classLoader = (ClassLoader) method.invoke(proxied, args);
if (classLoader instanceof URLClassLoader) {
return new URLClassLoader(((URLClassLoader) classLoader).getURLs(), getFilteredClassLoader(classLoader.getParent()));
} else {
return classLoader;
}
}
return method.invoke(proxied, args);
}
}

@@ -0,0 +1,29 @@
/*
* Copyright 2019 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.internal.tasks.compile.reflect;

import java.lang.reflect.Proxy;

public class FilteringClassLoaderInjectingProxy {
@SuppressWarnings("rawtypes")
public static Object proxy(Object proxied, Class<?> targetInterface) {
return Proxy.newProxyInstance(
FilteringClassLoaderInjectingInvocationHandler.class.getClassLoader(),
new Class[] {targetInterface},
new FilteringClassLoaderInjectingInvocationHandler(proxied));
}
}
Expand Up @@ -102,6 +102,9 @@ abstract class AbstractSmokeTest extends Specification {

// https://plugins.gradle.org/plugin/org.gradle.playframework
static playframework = "0.6"

// https://plugins.gradle.org/plugin/net.ltgt.errorprone
static errorProne = "0.8.1"
}

static class Versions implements Iterable<String> {
Expand Down
Expand Up @@ -384,4 +384,48 @@ class ThirdPartyPluginsSmokeTest extends AbstractSmokeTest {
file('build/reports/spotbugs').isDirectory()
}

@Issue("https://github.com/gradle/gradle/issues/9897")
def 'errorprone plugin'() {
given:
buildFile << """
plugins {
id('java')
id("net.ltgt.errorprone") version "${TestedVersions.errorProne}"
}
${mavenCentralRepository()}
if (JavaVersion.current().java8) {
dependencies {
errorproneJavac("com.google.errorprone:javac:9+181-r4173-1")
}
}
dependencies {
errorprone("com.google.errorprone:error_prone_core:2.3.3")
}
tasks.withType(JavaCompile).configureEach {
options.fork = true
options.errorprone {
check("DoubleBraceInitialization", net.ltgt.gradle.errorprone.CheckSeverity.ERROR)
}
}
"""
file("src/main/java/Test.java") << """
import java.util.HashSet;
import java.util.Set;
public class Test {
public static void main(String[] args) {
}
}
"""

expect:
runner('compileJava').forwardOutput().build()
}

}

0 comments on commit d58bdd6

Please sign in to comment.