From dc4059a145dc2d7896c84d4a4d65a8e7e2a7fe1f Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sat, 5 Sep 2015 13:33:25 +0200 Subject: [PATCH 01/40] First empty commit of Gradle task, plugin still missing; completely untested! --- build.xml | 1 + ivy-settings.xml | 32 ++++ ivy.xml | 6 +- .../forbiddenapis/gradle/GradleTask.java | 167 ++++++++++++++++++ 4 files changed, 205 insertions(+), 1 deletion(-) create mode 100644 ivy-settings.xml create mode 100644 src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java diff --git a/build.xml b/build.xml index f5a111cc..c4d1dec7 100644 --- a/build.xml +++ b/build.xml @@ -153,6 +153,7 @@ + + + + + + + + + + + + + + + + + + diff --git a/ivy.xml b/ivy.xml index cc54519d..dc195b2c 100644 --- a/ivy.xml +++ b/ivy.xml @@ -27,6 +27,11 @@ + + + + + @@ -39,7 +44,6 @@ - diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java new file mode 100644 index 00000000..3330114a --- /dev/null +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java @@ -0,0 +1,167 @@ +package de.thetaphi.forbiddenapis.gradle; + +/* + * (C) Copyright Uwe Schindler (Generics Policeman) and others. + * + * 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. + */ + +import java.io.File; +import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; + +import org.gradle.api.DefaultTask; +import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.Optional; +import org.gradle.api.tasks.TaskAction; + +/** + * Forbiddenapis Gradle Task + * @since 1.9 + */ +public class GradleTask extends DefaultTask { + + /** + * Lists all files, which contain signatures and comments for forbidden API calls. + * The signatures are resolved against the compile classpath. + * @since 1.0 + */ + @Optional + @InputFiles + public List signaturesFiles; + + /** + * Gives a multiline list of signatures, inline in the pom.xml. Use an XML CDATA section to do that! + * The signatures are resolved against the compile classpath. + * @since 1.0 + */ + @Optional + @Input + public String signatures; + + /** + * Specifies built-in signatures files (e.g., deprecated APIs for specific Java versions, + * unsafe method calls using default locale, default charset,...) + * @since 1.0 + */ + @Optional + @Input + public List bundledSignatures; + + /** + * Forbids calls to classes from the internal java runtime (like sun.misc.Unsafe) + * @since 1.0 + */ + @Input + public boolean internalRuntimeForbidden = false; + + /** + * Fail the build, if the bundled ASM library cannot read the class file format + * of the runtime library or the runtime library cannot be discovered. + * @since 1.0 + */ + @Input + public boolean failOnUnsupportedJava = false; + + /** + * Fail the build, if a class referenced in the scanned code is missing. This requires + * that you pass the whole classpath including all dependencies to this Mojo + * (Maven does this by default). + * @since 1.0 + */ + @Input + public boolean failOnMissingClasses = true; + + /** + * Fail the build if a signature is not resolving. If this parameter is set to + * to false, then such signatures are silently ignored. This is useful in multi-module Maven + * projects where only some modules have the dependency to which the signature file(s) apply. + * @since 1.4 + */ + @Input + public boolean failOnUnresolvableSignatures = true; + + /** + * Fail the build if violations have been found. Defaults to {@code true}. + * @since 1.9 + */ + @Input + public boolean failOnViolation = true; + + /** + * The default compiler target version used to expand references to bundled JDK signatures. + * E.g., if you use "jdk-deprecated", it will expand to this version. + * This setting should be identical to the target version used in the compiler plugin. + * @since 1.0 + */ + @Optional + @Input + public String targetVersion = null; + + /** + * List of patterns matching all class files to be parsed from the classesDirectory. + * Can be changed to e.g. exclude several files (using excludes). + * The default is a single include with pattern '**/*.class' + * @see #excludes + * @since 1.0 + */ + @Input + public List includes = new ArrayList(); + + /** + * List of patterns matching class files to be excluded from checking. + * @see #includes + * @since 1.0 + */ + @Optional + @Input + public List excludes = new ArrayList(); + + /** + * List of a custom Java annotations (full class names) that are used in the checked + * code to suppress errors. Those annotations must have at least + * {@link RetentionPolicy#CLASS}. They can be applied to classes, their methods, + * or fields. By default, {@code @de.thetaphi.forbiddenapis.SuppressForbidden} + * can always be used, but needs the {@code forbidden-apis.jar} file in classpath + * of compiled project, which may not be wanted. + * Instead of a full class name, a glob pattern may be used (e.g., + * {@code **.SuppressForbidden}). + * @since 1.8 + */ + @Optional + @Input + public List suppressAnnotations; + + /** + * Skip entire check. Most useful on the command line via "-Dforbiddenapis.skip=true". + * @since 1.6 + */ + @Input + public boolean skip = false; + + public GradleTask() { + includes.add("**/*.class"); + } + + private String getTargetVersion() { + return (targetVersion != null) ? targetVersion : getProject().property("targetCompatibility").toString(); + } + + @TaskAction + public void checkForbidden() { + // TODO + } + +} \ No newline at end of file From 769a75a45bbcf86a74d8db20c8e958de190ac596 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sat, 5 Sep 2015 13:36:27 +0200 Subject: [PATCH 02/40] Use type safety for targetVersion --- .../java/de/thetaphi/forbiddenapis/gradle/GradleTask.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java index 3330114a..ec8aa2d2 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java @@ -22,6 +22,7 @@ import java.util.List; import org.gradle.api.DefaultTask; +import org.gradle.api.JavaVersion; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.Optional; @@ -108,7 +109,7 @@ public class GradleTask extends DefaultTask { */ @Optional @Input - public String targetVersion = null; + public JavaVersion targetVersion = null; /** * List of patterns matching all class files to be parsed from the classesDirectory. @@ -156,7 +157,8 @@ public GradleTask() { } private String getTargetVersion() { - return (targetVersion != null) ? targetVersion : getProject().property("targetCompatibility").toString(); + return (targetVersion != null) ? + targetVersion.toString() : getProject().property("targetCompatibility").toString(); } @TaskAction From fd6ad30b44744e7846ccf4eb3057d5462a74579e Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sat, 5 Sep 2015 15:07:55 +0200 Subject: [PATCH 03/40] Commit current state --- .../forbiddenapis/gradle/GradlePlugin.java | 73 +++++++++++++++++++ .../forbiddenapis/gradle/GradleTask.java | 14 ++++ 2 files changed, 87 insertions(+) create mode 100644 src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java new file mode 100644 index 00000000..8eaf88a3 --- /dev/null +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java @@ -0,0 +1,73 @@ +package de.thetaphi.forbiddenapis.gradle; + +/* + * (C) Copyright Uwe Schindler (Generics Policeman) and others. + * + * 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. + */ + +import java.io.File; + +import org.gradle.api.Action; +import org.gradle.api.Plugin; +import org.gradle.api.Project; +import org.gradle.api.Task; +import org.gradle.api.artifacts.ConfigurationContainer; +import org.gradle.api.tasks.TaskContainer; + +/** + * Forbiddenapis Gradle Plugin + * @since 1.9 + */ +public class GradlePlugin implements Plugin { + + static final String FORBIDDEN_APIS_CONFIGURATION_NAME = "forbiddenApis"; + static final String FORBIDDEN_APIS_TASK_NAME = "forbiddenApis"; + static final String TEST_FORBIDDEN_APIS_CONFIGURATION_NAME = "testForbiddenApis"; + static final String TEST_FORBIDDEN_APIS_TASK_NAME = "testForbiddenApis"; + + public void apply(final Project project) { + final ConfigurationContainer configurations = project.getConfigurations(); + configurations.create(FORBIDDEN_APIS_CONFIGURATION_NAME); + configurations.create(TEST_FORBIDDEN_APIS_CONFIGURATION_NAME); + + final TaskContainer tasks = project.getTasks(); + + // TODO: How to import the JavaPlugin and its tasks from Maven!? + final Task classesJavaTask = tasks.getByName("classes"), + testClassesJavaTask = tasks.getByName("testClasses"), + jarJavaTask = tasks.getByName("jar"), + testJavaTask = tasks.getByName("test"); + + // Create the tasks of the plugin: + final Task forbiddenTask = tasks.create(FORBIDDEN_APIS_TASK_NAME, GradleTask.class, new Action() { + public void execute(GradleTask task) { + task.javaClassesDir = (File) project.property("sourceSets.main.output.classesDir"); + task.javaClasspath.add(configurations.getByName("compile")); + task.dependsOn(classesJavaTask); + } + }); + final Task testForbiddenTask = tasks.create(TEST_FORBIDDEN_APIS_TASK_NAME, GradleTask.class, new Action() { + public void execute(GradleTask task) { + task.javaClassesDir = (File) project.property("sourceSets.test.output.classesDir"); + task.javaClasspath.add(configurations.getByName("testCompile")); + task.dependsOn(testClassesJavaTask); + } + }); + + // make the jar task dependent upon it so that code cannot be bundled without checking if it's forbidden: + jarJavaTask.dependsOn(forbiddenTask); + // the same goes for the test task, but this is so that it fails before wasting time with tests: + testJavaTask.dependsOn(forbiddenTask, testForbiddenTask); + } +} \ No newline at end of file diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java index ec8aa2d2..f84cf921 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java @@ -23,7 +23,9 @@ import org.gradle.api.DefaultTask; import org.gradle.api.JavaVersion; +import org.gradle.api.file.FileCollection; import org.gradle.api.tasks.Input; +import org.gradle.api.tasks.InputDirectory; import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.Optional; import org.gradle.api.tasks.TaskAction; @@ -34,6 +36,18 @@ */ public class GradleTask extends DefaultTask { + /** + * If the code changed, then it needs to be re-run. + */ + @InputDirectory + public File javaClassesDir; + + /** + * The {@link Configuration}(s) used to configure the classpath. + */ + @InputFiles + public List javaClasspath; + /** * Lists all files, which contain signatures and comments for forbidden API calls. * The signatures are resolved against the compile classpath. From 4e29e6c730b75660aeb6c459deda5c43c5d52736 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sat, 5 Sep 2015 18:31:38 +0200 Subject: [PATCH 04/40] Remove additional signature configurations for now, fix NPE --- .../forbiddenapis/gradle/GradlePlugin.java | 18 +++--------------- .../forbiddenapis/gradle/GradleTask.java | 2 +- 2 files changed, 4 insertions(+), 16 deletions(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java index 8eaf88a3..5e568f83 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java @@ -31,43 +31,31 @@ */ public class GradlePlugin implements Plugin { - static final String FORBIDDEN_APIS_CONFIGURATION_NAME = "forbiddenApis"; static final String FORBIDDEN_APIS_TASK_NAME = "forbiddenApis"; - static final String TEST_FORBIDDEN_APIS_CONFIGURATION_NAME = "testForbiddenApis"; static final String TEST_FORBIDDEN_APIS_TASK_NAME = "testForbiddenApis"; public void apply(final Project project) { final ConfigurationContainer configurations = project.getConfigurations(); - configurations.create(FORBIDDEN_APIS_CONFIGURATION_NAME); - configurations.create(TEST_FORBIDDEN_APIS_CONFIGURATION_NAME); - final TaskContainer tasks = project.getTasks(); // TODO: How to import the JavaPlugin and its tasks from Maven!? final Task classesJavaTask = tasks.getByName("classes"), - testClassesJavaTask = tasks.getByName("testClasses"), - jarJavaTask = tasks.getByName("jar"), - testJavaTask = tasks.getByName("test"); + testClassesJavaTask = tasks.getByName("testClasses"); // Create the tasks of the plugin: - final Task forbiddenTask = tasks.create(FORBIDDEN_APIS_TASK_NAME, GradleTask.class, new Action() { + tasks.create(FORBIDDEN_APIS_TASK_NAME, GradleTask.class, new Action() { public void execute(GradleTask task) { task.javaClassesDir = (File) project.property("sourceSets.main.output.classesDir"); task.javaClasspath.add(configurations.getByName("compile")); task.dependsOn(classesJavaTask); } }); - final Task testForbiddenTask = tasks.create(TEST_FORBIDDEN_APIS_TASK_NAME, GradleTask.class, new Action() { + tasks.create(TEST_FORBIDDEN_APIS_TASK_NAME, GradleTask.class, new Action() { public void execute(GradleTask task) { task.javaClassesDir = (File) project.property("sourceSets.test.output.classesDir"); task.javaClasspath.add(configurations.getByName("testCompile")); task.dependsOn(testClassesJavaTask); } }); - - // make the jar task dependent upon it so that code cannot be bundled without checking if it's forbidden: - jarJavaTask.dependsOn(forbiddenTask); - // the same goes for the test task, but this is so that it fails before wasting time with tests: - testJavaTask.dependsOn(forbiddenTask, testForbiddenTask); } } \ No newline at end of file diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java index f84cf921..3501a26b 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java @@ -46,7 +46,7 @@ public class GradleTask extends DefaultTask { * The {@link Configuration}(s) used to configure the classpath. */ @InputFiles - public List javaClasspath; + public List javaClasspath = new ArrayList(); /** * Lists all files, which contain signatures and comments for forbidden API calls. From 62bb081ab1795da241b8c9e95ceea32476858655 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sat, 5 Sep 2015 18:44:41 +0200 Subject: [PATCH 05/40] Rename some properties, add docs --- .../forbiddenapis/gradle/GradlePlugin.java | 8 ++++---- .../thetaphi/forbiddenapis/gradle/GradleTask.java | 15 +++++---------- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java index 5e568f83..7641ae74 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java @@ -45,15 +45,15 @@ public void apply(final Project project) { // Create the tasks of the plugin: tasks.create(FORBIDDEN_APIS_TASK_NAME, GradleTask.class, new Action() { public void execute(GradleTask task) { - task.javaClassesDir = (File) project.property("sourceSets.main.output.classesDir"); - task.javaClasspath.add(configurations.getByName("compile")); + task.classesDir = (File) project.property("sourceSets.main.output.classesDir"); + task.classpath.add(configurations.getByName("compile")); task.dependsOn(classesJavaTask); } }); tasks.create(TEST_FORBIDDEN_APIS_TASK_NAME, GradleTask.class, new Action() { public void execute(GradleTask task) { - task.javaClassesDir = (File) project.property("sourceSets.test.output.classesDir"); - task.javaClasspath.add(configurations.getByName("testCompile")); + task.classesDir = (File) project.property("sourceSets.test.output.classesDir"); + task.classpath.add(configurations.getByName("testCompile")); task.dependsOn(testClassesJavaTask); } }); diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java index 3501a26b..263e8c08 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java @@ -40,13 +40,13 @@ public class GradleTask extends DefaultTask { * If the code changed, then it needs to be re-run. */ @InputDirectory - public File javaClassesDir; + public File classesDir; /** - * The {@link Configuration}(s) used to configure the classpath. + * The {@link FileCollection}(s) used to configure the classpath. */ @InputFiles - public List javaClasspath = new ArrayList(); + public List classpath = new ArrayList(); /** * Lists all files, which contain signatures and comments for forbidden API calls. @@ -119,6 +119,8 @@ public class GradleTask extends DefaultTask { * The default compiler target version used to expand references to bundled JDK signatures. * E.g., if you use "jdk-deprecated", it will expand to this version. * This setting should be identical to the target version used in the compiler plugin. + *

+ * If undefined, it is taken from the project property {@code targetCompatibility}. * @since 1.0 */ @Optional @@ -159,13 +161,6 @@ public class GradleTask extends DefaultTask { @Input public List suppressAnnotations; - /** - * Skip entire check. Most useful on the command line via "-Dforbiddenapis.skip=true". - * @since 1.6 - */ - @Input - public boolean skip = false; - public GradleTask() { includes.add("**/*.class"); } From 38596b54f937d92525b737d890a1ed0a4949e227 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sat, 5 Sep 2015 19:04:59 +0200 Subject: [PATCH 06/40] Add plugin descriptor --- .../META-INF/gradle-plugins/de.thetaphi.forbiddenapis.properties | 1 + 1 file changed, 1 insertion(+) create mode 100644 src/main/resources/META-INF/gradle-plugins/de.thetaphi.forbiddenapis.properties diff --git a/src/main/resources/META-INF/gradle-plugins/de.thetaphi.forbiddenapis.properties b/src/main/resources/META-INF/gradle-plugins/de.thetaphi.forbiddenapis.properties new file mode 100644 index 00000000..83975594 --- /dev/null +++ b/src/main/resources/META-INF/gradle-plugins/de.thetaphi.forbiddenapis.properties @@ -0,0 +1 @@ +implementation-class=de.thetaphi.forbiddenapis.gradle.GradlePlugin From 3ff24df0d00282c051689aabe071ae4c6787c94f Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sat, 5 Sep 2015 19:41:09 +0200 Subject: [PATCH 07/40] Add first implementation of the Gradle plugin --- ivy.xml | 1 + .../gradle/GradleForbiddenApiException.java | 28 +++ .../forbiddenapis/gradle/GradleTask.java | 166 +++++++++++++++++- 3 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 src/main/java/de/thetaphi/forbiddenapis/gradle/GradleForbiddenApiException.java diff --git a/ivy.xml b/ivy.xml index dc195b2c..a23b42fe 100644 --- a/ivy.xml +++ b/ivy.xml @@ -30,6 +30,7 @@ + diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleForbiddenApiException.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleForbiddenApiException.java new file mode 100644 index 00000000..21c7a6bc --- /dev/null +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleForbiddenApiException.java @@ -0,0 +1,28 @@ +package de.thetaphi.forbiddenapis.gradle; + +/* + * (C) Copyright Uwe Schindler (Generics Policeman) and others. + * + * 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. + */ + +import org.gradle.api.GradleException; + +@SuppressWarnings("serial") +public final class GradleForbiddenApiException extends GradleException { + + public GradleForbiddenApiException(String msg) { + super(msg); + } + +} diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java index 263e8c08..82c75a31 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java @@ -16,25 +16,50 @@ * limitations under the License. */ +import static de.thetaphi.forbiddenapis.Checker.Option.FAIL_ON_MISSING_CLASSES; +import static de.thetaphi.forbiddenapis.Checker.Option.FAIL_ON_UNRESOLVABLE_SIGNATURES; +import static de.thetaphi.forbiddenapis.Checker.Option.FAIL_ON_VIOLATION; +import static de.thetaphi.forbiddenapis.Checker.Option.INTERNAL_RUNTIME_FORBIDDEN; + +import java.io.Closeable; import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; import java.lang.annotation.RetentionPolicy; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; import java.util.ArrayList; +import java.util.Collection; +import java.util.EnumSet; import java.util.List; +import java.util.Locale; +import org.codehaus.plexus.util.DirectoryScanner; import org.gradle.api.DefaultTask; +import org.gradle.api.GradleException; +import org.gradle.api.InvalidUserDataException; import org.gradle.api.JavaVersion; import org.gradle.api.file.FileCollection; +import org.gradle.api.resources.ResourceException; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputDirectory; import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.Optional; import org.gradle.api.tasks.TaskAction; +import de.thetaphi.forbiddenapis.Checker; +import de.thetaphi.forbiddenapis.ForbiddenApiException; +import de.thetaphi.forbiddenapis.Logger; +import de.thetaphi.forbiddenapis.ParseException; + /** * Forbiddenapis Gradle Task * @since 1.9 */ public class GradleTask extends DefaultTask { + + private static final String[] EMPTY_STRING_ARRAY = new String[0]; /** * If the code changed, then it needs to be re-run. @@ -46,7 +71,7 @@ public class GradleTask extends DefaultTask { * The {@link FileCollection}(s) used to configure the classpath. */ @InputFiles - public List classpath = new ArrayList(); + public FileCollection classpath; /** * Lists all files, which contain signatures and comments for forbidden API calls. @@ -172,7 +197,144 @@ private String getTargetVersion() { @TaskAction public void checkForbidden() { - // TODO + final Logger log = new Logger() { + public void error(String msg) { + getLogger().error(msg); + } + + public void warn(String msg) { + getLogger().warn(msg); + } + + public void info(String msg) { + getLogger().info(msg); + } + }; + final URL[] urls; + try { + final Collection elements = classpath.getFiles(); + urls = new URL[elements.size()]; + int i = 0; + for (final File cpElement : elements) { + urls[i++] = cpElement.toURI().toURL(); + } + assert i == urls.length; + } catch (MalformedURLException e) { + throw new InvalidUserDataException("Failed to build classpath URLs.", e); + } + + URLClassLoader urlLoader = null; + final ClassLoader loader = (urls.length > 0) ? + (urlLoader = URLClassLoader.newInstance(urls, ClassLoader.getSystemClassLoader())) : + ClassLoader.getSystemClassLoader(); + + try { + final EnumSet options = EnumSet.noneOf(Checker.Option.class); + if (internalRuntimeForbidden) options.add(INTERNAL_RUNTIME_FORBIDDEN); + if (failOnMissingClasses) options.add(FAIL_ON_MISSING_CLASSES); + if (failOnViolation) options.add(FAIL_ON_VIOLATION); + if (failOnUnresolvableSignatures) options.add(FAIL_ON_UNRESOLVABLE_SIGNATURES); + final Checker checker = new Checker(log, loader, options); + + if (!checker.isSupportedJDK) { + final String msg = String.format(Locale.ENGLISH, + "Your Java runtime (%s %s) is not supported by the forbiddenapis MOJO. Please run the checks with a supported JDK!", + System.getProperty("java.runtime.name"), System.getProperty("java.runtime.version")); + if (failOnUnsupportedJava) { + throw new GradleException(msg); + } else { + log.warn(msg); + return; + } + } + + if (suppressAnnotations != null) { + for (String a : suppressAnnotations) { + checker.addSuppressAnnotation(a); + } + } + + log.info("Scanning for classes to check..."); + if (!classesDir.exists()) { + log.warn("Classes directory does not exist, forbiddenapis check skipped: " + classesDir); + return; + } + final DirectoryScanner ds = new DirectoryScanner(); + ds.setBasedir(classesDir); + ds.setCaseSensitive(true); + ds.setIncludes(includes.toArray(EMPTY_STRING_ARRAY)); + ds.setExcludes(excludes.toArray(EMPTY_STRING_ARRAY)); + ds.addDefaultExcludes(); + ds.scan(); + final String[] files = ds.getIncludedFiles(); + if (files.length == 0) { + log.warn(String.format(Locale.ENGLISH, + "No classes found in '%s' (includes=%s, excludes=%s), forbiddenapis check skipped.", + classesDir, includes, excludes)); + return; + } + + try { + final String sig = (signatures != null) ? signatures.trim() : null; + if (sig != null && sig.length() != 0) { + log.info("Reading inline API signatures..."); + checker.parseSignaturesString(sig); + } + if (bundledSignatures != null) { + String targetVersion = getTargetVersion(); + if ("".equals(targetVersion)) targetVersion = null; + if (targetVersion == null) { + log.warn("The 'targetVersion' parameter or '${maven.compiler.target}' property is missing. " + + "Trying to read bundled JDK signatures without compiler target. " + + "You have to explicitely specify the version in the resource name."); + } + for (String bs : bundledSignatures) { + log.info("Reading bundled API signatures: " + bs); + checker.parseBundledSignatures(bs, targetVersion); + } + } + if (signaturesFiles != null) for (final File f : signaturesFiles) { + log.info("Reading API signatures: " + f); + checker.parseSignaturesFile(new FileInputStream(f)); + } + } catch (IOException ioe) { + throw new ResourceException("IO problem while reading files with API signatures.", ioe); + } catch (ParseException pe) { + throw new InvalidUserDataException("Parsing signatures failed: " + pe.getMessage()); + } + + if (checker.hasNoSignatures()) { + if (failOnUnresolvableSignatures) { + throw new InvalidUserDataException("No API signatures found; use parameters 'signatures', 'bundledSignatures', and/or 'signaturesFiles' to define those!"); + } else { + log.info("Skipping execution because no API signatures are available."); + return; + } + } + + log.info("Loading classes to check..."); + try { + for (String f : files) { + checker.addClassToCheck(new FileInputStream(new File(classesDir, f))); + } + } catch (IOException ioe) { + throw new ResourceException("Failed to load one of the given class files.", ioe); + } + + log.info("Scanning for API signatures and dependencies..."); + try { + checker.run(); + } catch (ForbiddenApiException fae) { + throw new GradleForbiddenApiException(fae.getMessage()); + } + } finally { + // Java 7 supports closing URLClassLoader, so check for Closeable interface: + if (urlLoader instanceof Closeable) try { + ((Closeable) urlLoader).close(); + } catch (IOException ioe) { + // ignore + } + } } } \ No newline at end of file From ef781fbcaaeb5bedfb5ae2c2fffc5f5591a7367a Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sat, 5 Sep 2015 19:45:38 +0200 Subject: [PATCH 08/40] Fix wrong name in docs --- .../java/de/thetaphi/forbiddenapis/gradle/GradleTask.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java index 82c75a31..cd6c1dad 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java @@ -117,8 +117,8 @@ public class GradleTask extends DefaultTask { /** * Fail the build, if a class referenced in the scanned code is missing. This requires - * that you pass the whole classpath including all dependencies to this Mojo - * (Maven does this by default). + * that you pass the whole classpath including all dependencies to this task + * (Gradle does this by default). * @since 1.0 */ @Input @@ -238,7 +238,7 @@ public void info(String msg) { if (!checker.isSupportedJDK) { final String msg = String.format(Locale.ENGLISH, - "Your Java runtime (%s %s) is not supported by the forbiddenapis MOJO. Please run the checks with a supported JDK!", + "Your Java runtime (%s %s) is not supported by the forbiddenapis plugin. Please run the checks with a supported JDK!", System.getProperty("java.runtime.name"), System.getProperty("java.runtime.version")); if (failOnUnsupportedJava) { throw new GradleException(msg); @@ -337,4 +337,4 @@ public void info(String msg) { } } -} \ No newline at end of file +} From 18a1d0014826f46abd7f332cbc960f87331c78c2 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sat, 5 Sep 2015 20:01:50 +0200 Subject: [PATCH 09/40] Docs cleanup; NPE fixes; type change to file collections; inline signatures as array --- .../forbiddenapis/gradle/GradleTask.java | 33 ++++++++++--------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java index cd6c1dad..cc63ac33 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java @@ -30,6 +30,7 @@ import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.EnumSet; import java.util.List; @@ -62,34 +63,35 @@ public class GradleTask extends DefaultTask { private static final String[] EMPTY_STRING_ARRAY = new String[0]; /** - * If the code changed, then it needs to be re-run. + * Directory with the class files to check. */ @InputDirectory public File classesDir; /** - * The {@link FileCollection}(s) used to configure the classpath. + * A {@link FileCollection} used to configure the classpath. */ @InputFiles public FileCollection classpath; /** - * Lists all files, which contain signatures and comments for forbidden API calls. + * {@link FileCollection} containing all files, which contain signatures and comments for forbidden API calls. * The signatures are resolved against the compile classpath. * @since 1.0 */ @Optional @InputFiles - public List signaturesFiles; + public FileCollection signaturesFiles; /** - * Gives a multiline list of signatures, inline in the pom.xml. Use an XML CDATA section to do that! + * Gives multiple API signatures that are joined with newlines and + * parsed like a single {@link #signaturesFiles}. * The signatures are resolved against the compile classpath. * @since 1.0 */ @Optional @Input - public String signatures; + public List signatures; /** * Specifies built-in signatures files (e.g., deprecated APIs for specific Java versions, @@ -160,7 +162,7 @@ public class GradleTask extends DefaultTask { * @since 1.0 */ @Input - public List includes = new ArrayList(); + public List includes = new ArrayList(Arrays.asList("**/*.class")); /** * List of patterns matching class files to be excluded from checking. @@ -186,10 +188,6 @@ public class GradleTask extends DefaultTask { @Input public List suppressAnnotations; - public GradleTask() { - includes.add("**/*.class"); - } - private String getTargetVersion() { return (targetVersion != null) ? targetVersion.toString() : getProject().property("targetCompatibility").toString(); @@ -262,8 +260,8 @@ public void info(String msg) { final DirectoryScanner ds = new DirectoryScanner(); ds.setBasedir(classesDir); ds.setCaseSensitive(true); - ds.setIncludes(includes.toArray(EMPTY_STRING_ARRAY)); - ds.setExcludes(excludes.toArray(EMPTY_STRING_ARRAY)); + ds.setIncludes(includes == null ? null : includes.toArray(EMPTY_STRING_ARRAY)); + ds.setExcludes(excludes == null ? null : excludes.toArray(EMPTY_STRING_ARRAY)); ds.addDefaultExcludes(); ds.scan(); final String[] files = ds.getIncludedFiles(); @@ -275,10 +273,13 @@ public void info(String msg) { } try { - final String sig = (signatures != null) ? signatures.trim() : null; - if (sig != null && sig.length() != 0) { + if (signatures != null && !signatures.isEmpty()) { log.info("Reading inline API signatures..."); - checker.parseSignaturesString(sig); + final StringBuilder sb = new StringBuilder(); + for (String line : signatures) { + sb.append(line).append('\n'); + } + checker.parseSignaturesString(sb.toString()); } if (bundledSignatures != null) { String targetVersion = getTargetVersion(); From d0c8297a89f07b2a15294a7ab1d5f190c2b464c2 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sat, 5 Sep 2015 20:10:36 +0200 Subject: [PATCH 10/40] Fix bugs, add classes output directory to classpath (otherwise scanning may fail when excludes are there) --- .../thetaphi/forbiddenapis/gradle/GradleTask.java | 13 +++++++------ .../forbiddenapis/maven/AbstractCheckMojo.java | 5 ++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java index cc63ac33..77d043e9 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java @@ -208,17 +208,18 @@ public void info(String msg) { getLogger().info(msg); } }; - final URL[] urls; + + final Collection cpElements = classpath.getFiles(); + final URL[] urls = new URL[cpElements.size() + 1]; try { - final Collection elements = classpath.getFiles(); - urls = new URL[elements.size()]; int i = 0; - for (final File cpElement : elements) { + for (final File cpElement : cpElements) { urls[i++] = cpElement.toURI().toURL(); } + urls[i++] = classesDir.toURI().toURL(); assert i == urls.length; - } catch (MalformedURLException e) { - throw new InvalidUserDataException("Failed to build classpath URLs.", e); + } catch (MalformedURLException mfue) { + throw new InvalidUserDataException("Failed to build classpath URLs.", mfue); } URLClassLoader urlLoader = null; diff --git a/src/main/java/de/thetaphi/forbiddenapis/maven/AbstractCheckMojo.java b/src/main/java/de/thetaphi/forbiddenapis/maven/AbstractCheckMojo.java index c0f73788..6bfb1795 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/maven/AbstractCheckMojo.java +++ b/src/main/java/de/thetaphi/forbiddenapis/maven/AbstractCheckMojo.java @@ -195,10 +195,9 @@ public void execute() throws MojoExecutionException, MojoFailureException { // set default param: if (includes == null) includes = new String[] {"**/*.class"}; - final URL[] urls; + final List cp = getClassPathElements(); + final URL[] urls = new URL[cp.size()]; try { - final List cp = getClassPathElements(); - urls = new URL[cp.size()]; int i = 0; for (final String cpElement : cp) { urls[i++] = new File(cpElement).toURI().toURL(); From 395921fd75b78dd64035962a630316143651e3ed Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sat, 5 Sep 2015 20:11:44 +0200 Subject: [PATCH 11/40] add newline --- .../java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java index 7641ae74..88e6ec7c 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java @@ -58,4 +58,4 @@ public void execute(GradleTask task) { } }); } -} \ No newline at end of file +} From acb65964ffc1d28f08b95870147b2a2a72ad500c Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sat, 5 Sep 2015 20:15:42 +0200 Subject: [PATCH 12/40] fix more bugs and improve targetVersion checks --- .../de/thetaphi/forbiddenapis/gradle/GradleTask.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java index 77d043e9..6e27c2a0 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java @@ -188,9 +188,9 @@ public class GradleTask extends DefaultTask { @Input public List suppressAnnotations; - private String getTargetVersion() { + private JavaVersion getTargetVersion() { return (targetVersion != null) ? - targetVersion.toString() : getProject().property("targetCompatibility").toString(); + targetVersion : (JavaVersion) getProject().property("targetCompatibility"); } @TaskAction @@ -283,8 +283,7 @@ public void info(String msg) { checker.parseSignaturesString(sb.toString()); } if (bundledSignatures != null) { - String targetVersion = getTargetVersion(); - if ("".equals(targetVersion)) targetVersion = null; + JavaVersion targetVersion = getTargetVersion(); if (targetVersion == null) { log.warn("The 'targetVersion' parameter or '${maven.compiler.target}' property is missing. " + "Trying to read bundled JDK signatures without compiler target. " + @@ -292,7 +291,7 @@ public void info(String msg) { } for (String bs : bundledSignatures) { log.info("Reading bundled API signatures: " + bs); - checker.parseBundledSignatures(bs, targetVersion); + checker.parseBundledSignatures(bs, targetVersion == null ? null : targetVersion.toString()); } } if (signaturesFiles != null) for (final File f : signaturesFiles) { From 29a5aa538d6e462f0fe81d868275ecd6af0ffddb Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sat, 5 Sep 2015 20:17:17 +0200 Subject: [PATCH 13/40] fix error message --- src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java index 6e27c2a0..2ba5c21a 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java @@ -285,7 +285,7 @@ public void info(String msg) { if (bundledSignatures != null) { JavaVersion targetVersion = getTargetVersion(); if (targetVersion == null) { - log.warn("The 'targetVersion' parameter or '${maven.compiler.target}' property is missing. " + + log.warn("The 'targetVersion' parameter or 'targetCompatibility' project property is missing. " + "Trying to read bundled JDK signatures without compiler target. " + "You have to explicitely specify the version in the resource name."); } From cca129197ad892600db8ef53d7f568c7af33845a Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sat, 5 Sep 2015 20:33:07 +0200 Subject: [PATCH 14/40] fix NPE with configurations --- .../java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java index 88e6ec7c..c053b752 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java @@ -46,14 +46,14 @@ public void apply(final Project project) { tasks.create(FORBIDDEN_APIS_TASK_NAME, GradleTask.class, new Action() { public void execute(GradleTask task) { task.classesDir = (File) project.property("sourceSets.main.output.classesDir"); - task.classpath.add(configurations.getByName("compile")); + task.classpath = configurations.getByName("compile"); task.dependsOn(classesJavaTask); } }); tasks.create(TEST_FORBIDDEN_APIS_TASK_NAME, GradleTask.class, new Action() { public void execute(GradleTask task) { task.classesDir = (File) project.property("sourceSets.test.output.classesDir"); - task.classpath.add(configurations.getByName("testCompile")); + task.classpath = configurations.getByName("testCompile"); task.dependsOn(testClassesJavaTask); } }); From 2d2648da0152a5fa8bcfe9b81cc2156b3e4fa091 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sat, 5 Sep 2015 23:53:57 +0200 Subject: [PATCH 15/40] Add more task dependencies, based on dependency graph of Gradle Java Plugin --- .../forbiddenapis/gradle/GradlePlugin.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java index c053b752..d40f9a70 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java @@ -23,6 +23,7 @@ import org.gradle.api.Project; import org.gradle.api.Task; import org.gradle.api.artifacts.ConfigurationContainer; +import org.gradle.api.plugins.PluginInstantiationException; import org.gradle.api.tasks.TaskContainer; /** @@ -35,27 +36,39 @@ public class GradlePlugin implements Plugin { static final String TEST_FORBIDDEN_APIS_TASK_NAME = "testForbiddenApis"; public void apply(final Project project) { + if (project.getPlugins().findPlugin("java") == null) { + throw new PluginInstantiationException("forbiddenapis only works in projects using the 'java' plugin."); + } + final ConfigurationContainer configurations = project.getConfigurations(); final TaskContainer tasks = project.getTasks(); - // TODO: How to import the JavaPlugin and its tasks from Maven!? + // Get the tasks we depend on or the other one should depends on us (to insert us into the chain): final Task classesJavaTask = tasks.getByName("classes"), - testClassesJavaTask = tasks.getByName("testClasses"); + testClassesJavaTask = tasks.getByName("testClasses"), + jarJavaTask = tasks.getByName("jar"), + testJavaTask = tasks.getByName("test"), + checkJavaTask = tasks.getByName("check"); // Create the tasks of the plugin: - tasks.create(FORBIDDEN_APIS_TASK_NAME, GradleTask.class, new Action() { + final Task forbiddenTask = tasks.create(FORBIDDEN_APIS_TASK_NAME, GradleTask.class, new Action() { public void execute(GradleTask task) { task.classesDir = (File) project.property("sourceSets.main.output.classesDir"); task.classpath = configurations.getByName("compile"); task.dependsOn(classesJavaTask); } }); - tasks.create(TEST_FORBIDDEN_APIS_TASK_NAME, GradleTask.class, new Action() { + final Task testForbiddenTask = tasks.create(TEST_FORBIDDEN_APIS_TASK_NAME, GradleTask.class, new Action() { public void execute(GradleTask task) { task.classesDir = (File) project.property("sourceSets.test.output.classesDir"); task.classpath = configurations.getByName("testCompile"); task.dependsOn(testClassesJavaTask); } }); + + // Add dependencies + jarJavaTask.dependsOn(forbiddenTask); + testJavaTask.dependsOn(forbiddenTask, testForbiddenTask); + checkJavaTask.dependsOn(forbiddenTask, testForbiddenTask); } } From 45dd6474d2b281031f2f5765f04b515df14266b0 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sun, 6 Sep 2015 00:03:48 +0200 Subject: [PATCH 16/40] Remove targetVersion property. Gradle does not allow to set it on compileJava tasks, so we also don't allow it. --- .../forbiddenapis/gradle/GradleTask.java | 25 +++++-------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java index 2ba5c21a..537bb19f 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java @@ -142,18 +142,6 @@ public class GradleTask extends DefaultTask { @Input public boolean failOnViolation = true; - /** - * The default compiler target version used to expand references to bundled JDK signatures. - * E.g., if you use "jdk-deprecated", it will expand to this version. - * This setting should be identical to the target version used in the compiler plugin. - *

- * If undefined, it is taken from the project property {@code targetCompatibility}. - * @since 1.0 - */ - @Optional - @Input - public JavaVersion targetVersion = null; - /** * List of patterns matching all class files to be parsed from the classesDirectory. * Can be changed to e.g. exclude several files (using excludes). @@ -188,13 +176,12 @@ public class GradleTask extends DefaultTask { @Input public List suppressAnnotations; - private JavaVersion getTargetVersion() { - return (targetVersion != null) ? - targetVersion : (JavaVersion) getProject().property("targetCompatibility"); - } - @TaskAction public void checkForbidden() { + if (classesDir == null || classpath == null) { + throw new InvalidUserDataException("Missing 'classesDir' or 'classpath' property."); + } + final Logger log = new Logger() { public void error(String msg) { getLogger().error(msg); @@ -283,9 +270,9 @@ public void info(String msg) { checker.parseSignaturesString(sb.toString()); } if (bundledSignatures != null) { - JavaVersion targetVersion = getTargetVersion(); + final JavaVersion targetVersion = (JavaVersion) getProject().property("targetCompatibility"); if (targetVersion == null) { - log.warn("The 'targetVersion' parameter or 'targetCompatibility' project property is missing. " + + log.warn("The 'targetCompatibility' project property is missing. " + "Trying to read bundled JDK signatures without compiler target. " + "You have to explicitely specify the version in the resource name."); } From f68673beb83d4d2d0e8a228366819a17b2cd292d Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sun, 6 Sep 2015 02:14:44 +0200 Subject: [PATCH 17/40] Fix lookup of classesDir (horrible in plain Java); add main classesDir to test classpath --- .../forbiddenapis/gradle/GradlePlugin.java | 50 ++++++++++++------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java index d40f9a70..05ffc91f 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java @@ -19,6 +19,7 @@ import java.io.File; import org.gradle.api.Action; +import org.gradle.api.NamedDomainObjectCollection; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.Task; @@ -32,43 +33,58 @@ */ public class GradlePlugin implements Plugin { - static final String FORBIDDEN_APIS_TASK_NAME = "forbiddenApis"; - static final String TEST_FORBIDDEN_APIS_TASK_NAME = "testForbiddenApis"; + public static final String FORBIDDEN_APIS_TASK_NAME = "forbiddenApis"; + public static final String TEST_FORBIDDEN_APIS_TASK_NAME = "testForbiddenApis"; public void apply(final Project project) { if (project.getPlugins().findPlugin("java") == null) { - throw new PluginInstantiationException("forbiddenapis only works in projects using the 'java' plugin."); + throw new PluginInstantiationException("Forbiddenapis only works in projects using the 'java' plugin."); } final ConfigurationContainer configurations = project.getConfigurations(); final TaskContainer tasks = project.getTasks(); // Get the tasks we depend on or the other one should depends on us (to insert us into the chain): - final Task classesJavaTask = tasks.getByName("classes"), - testClassesJavaTask = tasks.getByName("testClasses"), - jarJavaTask = tasks.getByName("jar"), - testJavaTask = tasks.getByName("test"), - checkJavaTask = tasks.getByName("check"); + final Task classesTask = tasks.getByName("classes"), + testClassesTask = tasks.getByName("testClasses"), + jarTask = tasks.getByName("jar"), + testTask = tasks.getByName("test"), + checkTask = tasks.getByName("check"); + + // Get classes directories for main and test + final File mainClassesDir = getClassesDirByName(project, "main"), + testClassesDir = getClassesDirByName(project, "test"); // Create the tasks of the plugin: final Task forbiddenTask = tasks.create(FORBIDDEN_APIS_TASK_NAME, GradleTask.class, new Action() { public void execute(GradleTask task) { - task.classesDir = (File) project.property("sourceSets.main.output.classesDir"); + task.classesDir = mainClassesDir; task.classpath = configurations.getByName("compile"); - task.dependsOn(classesJavaTask); + task.dependsOn(classesTask); } }); final Task testForbiddenTask = tasks.create(TEST_FORBIDDEN_APIS_TASK_NAME, GradleTask.class, new Action() { public void execute(GradleTask task) { - task.classesDir = (File) project.property("sourceSets.test.output.classesDir"); - task.classpath = configurations.getByName("testCompile"); - task.dependsOn(testClassesJavaTask); + task.classesDir = testClassesDir; + task.classpath = configurations.getByName("testCompile").plus(project.files(mainClassesDir)); + task.dependsOn(testClassesTask); } }); - // Add dependencies - jarJavaTask.dependsOn(forbiddenTask); - testJavaTask.dependsOn(forbiddenTask, testForbiddenTask); - checkJavaTask.dependsOn(forbiddenTask, testForbiddenTask); + // Add our tasks as dependencies to chain + jarTask.dependsOn(forbiddenTask); + testTask.dependsOn(forbiddenTask, testForbiddenTask); + checkTask.dependsOn(forbiddenTask, testForbiddenTask); } + + private File getClassesDirByName(Project project, String sourceSetName) { + final Object sourceSet = ((NamedDomainObjectCollection) project.property("sourceSets")).getByName(sourceSetName); + try { + final Object output = sourceSet.getClass().getMethod("getOutput").invoke(sourceSet); + return (File) output.getClass().getMethod("getClassesDir").invoke(output); + } catch (Exception e) { + throw new PluginInstantiationException("Forbiddenapis was not able to initialize classesDir.", e); + } + } + } From 4b1430c1163d94a8a8d4b0977bbdcd3e0e61abb1 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sun, 6 Sep 2015 12:07:57 +0200 Subject: [PATCH 18/40] Insert the tasks into the chain in a better way: We run before classes or testClasses is done and require only the compileJava tasks before. By this all task that require compiled classes now get "checked" classes. This is now similar to Maven. --- .../forbiddenapis/gradle/GradlePlugin.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java index 05ffc91f..67e093ea 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java @@ -47,9 +47,8 @@ public void apply(final Project project) { // Get the tasks we depend on or the other one should depends on us (to insert us into the chain): final Task classesTask = tasks.getByName("classes"), testClassesTask = tasks.getByName("testClasses"), - jarTask = tasks.getByName("jar"), - testTask = tasks.getByName("test"), - checkTask = tasks.getByName("check"); + compileJavaTask = tasks.getByName("compileJava"), + compileTestJavaTask = tasks.getByName("compileTestJava"); // Get classes directories for main and test final File mainClassesDir = getClassesDirByName(project, "main"), @@ -60,21 +59,20 @@ public void apply(final Project project) { public void execute(GradleTask task) { task.classesDir = mainClassesDir; task.classpath = configurations.getByName("compile"); - task.dependsOn(classesTask); + task.dependsOn(compileJavaTask); } }); final Task testForbiddenTask = tasks.create(TEST_FORBIDDEN_APIS_TASK_NAME, GradleTask.class, new Action() { public void execute(GradleTask task) { task.classesDir = testClassesDir; task.classpath = configurations.getByName("testCompile").plus(project.files(mainClassesDir)); - task.dependsOn(testClassesTask); + task.dependsOn(compileTestJavaTask); } }); // Add our tasks as dependencies to chain - jarTask.dependsOn(forbiddenTask); - testTask.dependsOn(forbiddenTask, testForbiddenTask); - checkTask.dependsOn(forbiddenTask, testForbiddenTask); + classesTask.dependsOn(forbiddenTask); + testClassesTask.dependsOn(testForbiddenTask); } private File getClassesDirByName(Project project, String sourceSetName) { From 9680b103253dcf3343c1ec10a0edf4d1025b82a3 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sun, 6 Sep 2015 12:17:05 +0200 Subject: [PATCH 19/40] Code simplifications --- .../forbiddenapis/gradle/GradlePlugin.java | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java index 67e093ea..e4b0981e 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java @@ -44,12 +44,6 @@ public void apply(final Project project) { final ConfigurationContainer configurations = project.getConfigurations(); final TaskContainer tasks = project.getTasks(); - // Get the tasks we depend on or the other one should depends on us (to insert us into the chain): - final Task classesTask = tasks.getByName("classes"), - testClassesTask = tasks.getByName("testClasses"), - compileJavaTask = tasks.getByName("compileJava"), - compileTestJavaTask = tasks.getByName("compileTestJava"); - // Get classes directories for main and test final File mainClassesDir = getClassesDirByName(project, "main"), testClassesDir = getClassesDirByName(project, "test"); @@ -59,20 +53,20 @@ public void apply(final Project project) { public void execute(GradleTask task) { task.classesDir = mainClassesDir; task.classpath = configurations.getByName("compile"); - task.dependsOn(compileJavaTask); + task.dependsOn(tasks.getByName("compileJava")); } }); final Task testForbiddenTask = tasks.create(TEST_FORBIDDEN_APIS_TASK_NAME, GradleTask.class, new Action() { public void execute(GradleTask task) { task.classesDir = testClassesDir; task.classpath = configurations.getByName("testCompile").plus(project.files(mainClassesDir)); - task.dependsOn(compileTestJavaTask); + task.dependsOn(tasks.getByName("compileTestJava")); } }); // Add our tasks as dependencies to chain - classesTask.dependsOn(forbiddenTask); - testClassesTask.dependsOn(testForbiddenTask); + tasks.getByName("classes").dependsOn(forbiddenTask); + tasks.getByName("testClasses").dependsOn(testForbiddenTask); } private File getClassesDirByName(Project project, String sourceSetName) { From 4a04324f82b61f14457aa90b8413bb1ab06fcd35 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sun, 6 Sep 2015 13:58:51 +0200 Subject: [PATCH 20/40] Make the Inputs/Outputs work correctly (plain fields don't work, although docs tells otherwise - still open bug in Gradle). Change boolean options to use Option's EnumSet. --- .../forbiddenapis/gradle/GradlePlugin.java | 8 +- .../forbiddenapis/gradle/GradleTask.java | 171 +++++++++++++++--- 2 files changed, 145 insertions(+), 34 deletions(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java index e4b0981e..bd79a8ba 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java @@ -51,15 +51,15 @@ public void apply(final Project project) { // Create the tasks of the plugin: final Task forbiddenTask = tasks.create(FORBIDDEN_APIS_TASK_NAME, GradleTask.class, new Action() { public void execute(GradleTask task) { - task.classesDir = mainClassesDir; - task.classpath = configurations.getByName("compile"); + task.setClassesDir(mainClassesDir); + task.setClasspath(configurations.getByName("compile")); task.dependsOn(tasks.getByName("compileJava")); } }); final Task testForbiddenTask = tasks.create(TEST_FORBIDDEN_APIS_TASK_NAME, GradleTask.class, new Action() { public void execute(GradleTask task) { - task.classesDir = testClassesDir; - task.classpath = configurations.getByName("testCompile").plus(project.files(mainClassesDir)); + task.setClassesDir(testClassesDir); + task.setClasspath(configurations.getByName("testCompile").plus(project.files(mainClassesDir))); task.dependsOn(tasks.getByName("compileTestJava")); } }); diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java index 537bb19f..5b3e1e16 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java @@ -47,6 +47,8 @@ import org.gradle.api.tasks.InputDirectory; import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.Optional; +import org.gradle.api.tasks.OutputDirectory; +import org.gradle.api.tasks.SkipWhenEmpty; import org.gradle.api.tasks.TaskAction; import de.thetaphi.forbiddenapis.Checker; @@ -61,27 +63,64 @@ public class GradleTask extends DefaultTask { private static final String[] EMPTY_STRING_ARRAY = new String[0]; + + private File classesDir; + private FileCollection classpath, signaturesFiles; + private List signatures; + private List bundledSignatures, suppressAnnotations; + private final EnumSet options = EnumSet.of(FAIL_ON_MISSING_CLASSES, FAIL_ON_UNRESOLVABLE_SIGNATURES, FAIL_ON_VIOLATION); + private boolean failOnUnsupportedJava = false; + + private List includes = new ArrayList(Arrays.asList("**/*.class")), + excludes = new ArrayList(); + + private void setOption(Checker.Option opt, boolean value) { + options.remove(opt); + if (value) options.add(opt); + } /** * Directory with the class files to check. */ @InputDirectory - public File classesDir; + @SkipWhenEmpty + public File getClassesDir() { + return classesDir; + } + + /** @see #getClassesDir */ + public void setClassesDir(File classesDir) { + this.classesDir = classesDir; + } /** - * A {@link FileCollection} used to configure the classpath. + * A {@link FileCollection} containing all files, which contain signatures and comments for forbidden API calls. + * The signatures are resolved against the compile classpath. + * @since 1.0 */ @InputFiles - public FileCollection classpath; + public FileCollection getClasspath() { + return classpath; + } + + /** @see #getClasspath */ + public void setClasspath(FileCollection classpath) { + this.classpath = classpath; + } /** - * {@link FileCollection} containing all files, which contain signatures and comments for forbidden API calls. - * The signatures are resolved against the compile classpath. - * @since 1.0 + * A {@link FileCollection} used to configure the classpath. */ - @Optional @InputFiles - public FileCollection signaturesFiles; + @Optional + public FileCollection getSignaturesFiles() { + return signaturesFiles; + } + + /** @see #getSignaturesFiles */ + public void setSignaturesFiles(FileCollection signaturesFiles) { + this.signaturesFiles = signaturesFiles; + } /** * Gives multiple API signatures that are joined with newlines and @@ -89,25 +128,46 @@ public class GradleTask extends DefaultTask { * The signatures are resolved against the compile classpath. * @since 1.0 */ - @Optional @Input - public List signatures; + @Optional + public List getSignatures() { + return signatures; + } + + /** @see #getSignatures */ + public void setSignatures(List signatures) { + this.signatures = signatures; + } /** * Specifies built-in signatures files (e.g., deprecated APIs for specific Java versions, * unsafe method calls using default locale, default charset,...) * @since 1.0 */ - @Optional @Input - public List bundledSignatures; + @Optional + public List getBundledSignatures() { + return bundledSignatures; + } + + /** @see #getBundledSignatures */ + public void setBundledSignatures(List bundledSignatures) { + this.bundledSignatures = bundledSignatures; + } /** * Forbids calls to classes from the internal java runtime (like sun.misc.Unsafe) * @since 1.0 */ @Input - public boolean internalRuntimeForbidden = false; + public boolean getInternalRuntimeForbidden() { + return options.contains(INTERNAL_RUNTIME_FORBIDDEN); + } + + /** @see #getInternalRuntimeForbidden */ + public void setInternalRuntimeForbidden(boolean internalRuntimeForbidden) { + setOption(INTERNAL_RUNTIME_FORBIDDEN, internalRuntimeForbidden); + } /** * Fail the build, if the bundled ASM library cannot read the class file format @@ -115,8 +175,15 @@ public class GradleTask extends DefaultTask { * @since 1.0 */ @Input - public boolean failOnUnsupportedJava = false; - + public boolean getFailOnUnsupportedJava() { + return failOnUnsupportedJava; + } + + /** @see #getFailOnUnsupportedJava */ + public void setFailOnUnsupportedJava(boolean failOnUnsupportedJava) { + this.failOnUnsupportedJava = failOnUnsupportedJava; + } + /** * Fail the build, if a class referenced in the scanned code is missing. This requires * that you pass the whole classpath including all dependencies to this task @@ -124,8 +191,15 @@ public class GradleTask extends DefaultTask { * @since 1.0 */ @Input - public boolean failOnMissingClasses = true; - + public boolean getFailOnMissingClasses() { + return options.contains(FAIL_ON_MISSING_CLASSES); + } + + /** @see #getFailOnMissingClasses */ + public void setFailOnMissingClasses(boolean failOnMissingClasses) { + setOption(FAIL_ON_MISSING_CLASSES, failOnMissingClasses); + } + /** * Fail the build if a signature is not resolving. If this parameter is set to * to false, then such signatures are silently ignored. This is useful in multi-module Maven @@ -133,14 +207,28 @@ public class GradleTask extends DefaultTask { * @since 1.4 */ @Input - public boolean failOnUnresolvableSignatures = true; + public boolean getFailOnUnresolvableSignatures() { + return options.contains(FAIL_ON_UNRESOLVABLE_SIGNATURES); + } + + /** @see #getFailOnUnresolvableSignatures */ + public void setFailOnUnresolvableSignatures(boolean failOnUnresolvableSignatures) { + setOption(FAIL_ON_UNRESOLVABLE_SIGNATURES, failOnUnresolvableSignatures); + } /** * Fail the build if violations have been found. Defaults to {@code true}. * @since 1.9 */ @Input - public boolean failOnViolation = true; + public boolean getFailOnViolation() { + return options.contains(FAIL_ON_VIOLATION); + } + + /** @see #getFailOnViolation */ + public void setFailOnViolation(boolean failOnViolation) { + setOption(FAIL_ON_VIOLATION, failOnViolation); + } /** * List of patterns matching all class files to be parsed from the classesDirectory. @@ -150,16 +238,29 @@ public class GradleTask extends DefaultTask { * @since 1.0 */ @Input - public List includes = new ArrayList(Arrays.asList("**/*.class")); + public List getIncludes() { + return includes; + } + + /** @see #getFailOnViolation */ + public void getIncludes(List includes) { + this.includes = includes; + } /** * List of patterns matching class files to be excluded from checking. * @see #includes * @since 1.0 */ - @Optional @Input - public List excludes = new ArrayList(); + public List getExcludes() { + return excludes; + } + + /** @see #getExcludes */ + public void setExcludes(List excludes) { + this.excludes = excludes; + } /** * List of a custom Java annotations (full class names) that are used in the checked @@ -172,9 +273,24 @@ public class GradleTask extends DefaultTask { * {@code **.SuppressForbidden}). * @since 1.8 */ - @Optional @Input - public List suppressAnnotations; + @Optional + public List getSuppressAnnotations() { + return suppressAnnotations; + } + + /** @see #getSuppressAnnotations */ + public void setSuppressAnnotations(List suppressAnnotations) { + this.suppressAnnotations = suppressAnnotations; + } + + /** Returns the classes dir as output, although we don't change anything + * @see #getClassesDir() + */ + @OutputDirectory + public File getOutputDir() { + return classesDir; + } @TaskAction public void checkForbidden() { @@ -215,11 +331,6 @@ public void info(String msg) { ClassLoader.getSystemClassLoader(); try { - final EnumSet options = EnumSet.noneOf(Checker.Option.class); - if (internalRuntimeForbidden) options.add(INTERNAL_RUNTIME_FORBIDDEN); - if (failOnMissingClasses) options.add(FAIL_ON_MISSING_CLASSES); - if (failOnViolation) options.add(FAIL_ON_VIOLATION); - if (failOnUnresolvableSignatures) options.add(FAIL_ON_UNRESOLVABLE_SIGNATURES); final Checker checker = new Checker(log, loader, options); if (!checker.isSupportedJDK) { @@ -292,7 +403,7 @@ public void info(String msg) { } if (checker.hasNoSignatures()) { - if (failOnUnresolvableSignatures) { + if (options.contains(FAIL_ON_UNRESOLVABLE_SIGNATURES)) { throw new InvalidUserDataException("No API signatures found; use parameters 'signatures', 'bundledSignatures', and/or 'signaturesFiles' to define those!"); } else { log.info("Skipping execution because no API signatures are available."); From 5c1c8d00e7270d178fd375caa04483af2bb4e30f Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sun, 6 Sep 2015 15:00:02 +0200 Subject: [PATCH 21/40] Make the include/exclude pattern handling according to Gradle's guidelines (see SourceTask Javadocs) --- .../forbiddenapis/gradle/GradleTask.java | 165 ++++++++++-------- 1 file changed, 94 insertions(+), 71 deletions(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java index 5b3e1e16..c61ffe50 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java @@ -21,6 +21,8 @@ import static de.thetaphi.forbiddenapis.Checker.Option.FAIL_ON_VIOLATION; import static de.thetaphi.forbiddenapis.Checker.Option.INTERNAL_RUNTIME_FORBIDDEN; +import groovy.lang.Closure; + import java.io.Closeable; import java.io.File; import java.io.FileInputStream; @@ -29,27 +31,29 @@ import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.EnumSet; import java.util.List; import java.util.Locale; +import java.util.Set; -import org.codehaus.plexus.util.DirectoryScanner; import org.gradle.api.DefaultTask; import org.gradle.api.GradleException; 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.file.FileTreeElement; import org.gradle.api.resources.ResourceException; +import org.gradle.api.specs.Spec; import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.InputDirectory; import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.Optional; import org.gradle.api.tasks.OutputDirectory; import org.gradle.api.tasks.SkipWhenEmpty; import org.gradle.api.tasks.TaskAction; +import org.gradle.api.tasks.util.PatternFilterable; +import org.gradle.api.tasks.util.PatternSet; import de.thetaphi.forbiddenapis.Checker; import de.thetaphi.forbiddenapis.ForbiddenApiException; @@ -60,19 +64,16 @@ * Forbiddenapis Gradle Task * @since 1.9 */ -public class GradleTask extends DefaultTask { - - private static final String[] EMPTY_STRING_ARRAY = new String[0]; +public class GradleTask extends DefaultTask implements PatternFilterable { private File classesDir; private FileCollection classpath, signaturesFiles; private List signatures; private List bundledSignatures, suppressAnnotations; - private final EnumSet options = EnumSet.of(FAIL_ON_MISSING_CLASSES, FAIL_ON_UNRESOLVABLE_SIGNATURES, FAIL_ON_VIOLATION); private boolean failOnUnsupportedJava = false; - private List includes = new ArrayList(Arrays.asList("**/*.class")), - excludes = new ArrayList(); + private final EnumSet options = EnumSet.of(FAIL_ON_MISSING_CLASSES, FAIL_ON_UNRESOLVABLE_SIGNATURES, FAIL_ON_VIOLATION); + private final PatternFilterable patternSet = new PatternSet().include("**/*.class"); private void setOption(Checker.Option opt, boolean value) { options.remove(opt); @@ -82,8 +83,7 @@ private void setOption(Checker.Option opt, boolean value) { /** * Directory with the class files to check. */ - @InputDirectory - @SkipWhenEmpty + @OutputDirectory public File getClassesDir() { return classesDir; } @@ -230,38 +230,6 @@ public void setFailOnViolation(boolean failOnViolation) { setOption(FAIL_ON_VIOLATION, failOnViolation); } - /** - * List of patterns matching all class files to be parsed from the classesDirectory. - * Can be changed to e.g. exclude several files (using excludes). - * The default is a single include with pattern '**/*.class' - * @see #excludes - * @since 1.0 - */ - @Input - public List getIncludes() { - return includes; - } - - /** @see #getFailOnViolation */ - public void getIncludes(List includes) { - this.includes = includes; - } - - /** - * List of patterns matching class files to be excluded from checking. - * @see #includes - * @since 1.0 - */ - @Input - public List getExcludes() { - return excludes; - } - - /** @see #getExcludes */ - public void setExcludes(List excludes) { - this.excludes = excludes; - } - /** * List of a custom Java annotations (full class names) that are used in the checked * code to suppress errors. Those annotations must have at least @@ -284,12 +252,87 @@ public void setSuppressAnnotations(List suppressAnnotations) { this.suppressAnnotations = suppressAnnotations; } - /** Returns the classes dir as output, although we don't change anything - * @see #getClassesDir() + // PatternFilterable implementation: + + /** + * {@inheritDoc} + *

+ * Set of patterns matching all class files to be parsed from the classesDirectory. + * Can be changed to e.g. exclude several files (using excludes). + * The default is a single include with pattern '**/*.class' + * @since 1.0 */ - @OutputDirectory - public File getOutputDir() { - return classesDir; + @Input + public Set getIncludes() { + return patternSet.getIncludes(); + } + + public GradleTask setIncludes(Iterable includes) { + patternSet.setIncludes(includes); + return this; + } + + /** + * {@inheritDoc} + *

+ * Set of patterns matching class files to be excluded from checking. + * @since 1.0 + */ + @Input + public Set getExcludes() { + return patternSet.getExcludes(); + } + + public GradleTask setExcludes(Iterable excludes) { + patternSet.setExcludes(excludes); + return this; + } + + public GradleTask exclude(String... arg0) { + patternSet.exclude(arg0); + return this; + } + + public GradleTask exclude(Iterable arg0) { + patternSet.exclude(arg0); + return this; + } + + public GradleTask exclude(Spec arg0) { + patternSet.exclude(arg0); + return this; + } + + public GradleTask exclude(@SuppressWarnings("rawtypes") Closure arg0) { + patternSet.exclude(arg0); + return this; + } + + public GradleTask include(String... arg0) { + patternSet.include(arg0); + return this; + } + + public GradleTask include(Iterable arg0) { + patternSet.include(arg0); + return this; + } + + public GradleTask include(Spec arg0) { + patternSet.include(arg0); + return this; + } + + public GradleTask include(@SuppressWarnings("rawtypes") Closure arg0) { + patternSet.include(arg0); + return this; + } + + /** Returns the classes to check. */ + @InputFiles + @SkipWhenEmpty + public FileTree getClassFiles() { + return getProject().files(classesDir).getAsFileTree().matching(patternSet); } @TaskAction @@ -351,26 +394,6 @@ public void info(String msg) { } } - log.info("Scanning for classes to check..."); - if (!classesDir.exists()) { - log.warn("Classes directory does not exist, forbiddenapis check skipped: " + classesDir); - return; - } - final DirectoryScanner ds = new DirectoryScanner(); - ds.setBasedir(classesDir); - ds.setCaseSensitive(true); - ds.setIncludes(includes == null ? null : includes.toArray(EMPTY_STRING_ARRAY)); - ds.setExcludes(excludes == null ? null : excludes.toArray(EMPTY_STRING_ARRAY)); - ds.addDefaultExcludes(); - ds.scan(); - final String[] files = ds.getIncludedFiles(); - if (files.length == 0) { - log.warn(String.format(Locale.ENGLISH, - "No classes found in '%s' (includes=%s, excludes=%s), forbiddenapis check skipped.", - classesDir, includes, excludes)); - return; - } - try { if (signatures != null && !signatures.isEmpty()) { log.info("Reading inline API signatures..."); @@ -413,8 +436,8 @@ public void info(String msg) { log.info("Loading classes to check..."); try { - for (String f : files) { - checker.addClassToCheck(new FileInputStream(new File(classesDir, f))); + for (File f : getClassFiles()) { + checker.addClassToCheck(new FileInputStream(f)); } } catch (IOException ioe) { throw new ResourceException("Failed to load one of the given class files.", ioe); From 36ecd69ae2bd252cbb6b718ce938e35023923894 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sun, 6 Sep 2015 15:05:20 +0200 Subject: [PATCH 22/40] Add File-taking methods to Checker --- src/main/java/de/thetaphi/forbiddenapis/Checker.java | 11 +++++++++++ .../java/de/thetaphi/forbiddenapis/cli/CliMain.java | 5 ++--- .../de/thetaphi/forbiddenapis/gradle/GradleTask.java | 5 ++--- .../forbiddenapis/maven/AbstractCheckMojo.java | 5 ++--- 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/Checker.java b/src/main/java/de/thetaphi/forbiddenapis/Checker.java index 4a0d6cc3..b0e00530 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/Checker.java +++ b/src/main/java/de/thetaphi/forbiddenapis/Checker.java @@ -23,6 +23,7 @@ import org.objectweb.asm.commons.Method; import java.io.BufferedReader; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; @@ -351,6 +352,11 @@ public final void parseSignaturesFile(InputStream in) throws IOException,ParseEx parseSignaturesFile(in, false); } + /** Reads a list of API signatures from the given file. */ + public final void parseSignaturesFile(File f) throws IOException,ParseException { + parseSignaturesFile(new FileInputStream(f)); + } + /** Reads a list of API signatures from a String. */ public final void parseSignaturesString(String signatures) throws IOException,ParseException { parseSignaturesFile(new StringReader(signatures), false); @@ -405,6 +411,11 @@ public final void addClassToCheck(final InputStream in) throws IOException { classesToCheck.put(reader.getClassName(), new ClassSignature(reader, false, true)); } + /** Parses and adds a class from the given file to the list of classes to check. */ + public final void addClassToCheck(File f) throws IOException { + addClassToCheck(new FileInputStream(f)); + } + public final boolean hasNoSignatures() { return forbiddenMethods.isEmpty() && forbiddenFields.isEmpty() && forbiddenClasses.isEmpty() && forbiddenClassPatterns.isEmpty() && (!options.contains(Option.INTERNAL_RUNTIME_FORBIDDEN)); } diff --git a/src/main/java/de/thetaphi/forbiddenapis/cli/CliMain.java b/src/main/java/de/thetaphi/forbiddenapis/cli/CliMain.java index 3322036f..673d50f3 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/cli/CliMain.java +++ b/src/main/java/de/thetaphi/forbiddenapis/cli/CliMain.java @@ -20,7 +20,6 @@ import java.io.Closeable; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.util.Arrays; import java.util.EnumSet; @@ -266,7 +265,7 @@ public void run() throws ExitException { if (signaturesFiles != null) for (String sf : signaturesFiles) { final File f = new File(sf).getAbsoluteFile(); LOG.info("Reading API signatures: " + f); - checker.parseSignaturesFile(new FileInputStream(f)); + checker.parseSignaturesFile(f); } } catch (IOException ioe) { throw new ExitException(EXIT_ERR_OTHER, "IO problem while reading files with API signatures: " + ioe); @@ -284,7 +283,7 @@ public void run() throws ExitException { LOG.info("Loading classes to check..."); try { for (String f : files) { - checker.addClassToCheck(new FileInputStream(new File(classesDirectory, f))); + checker.addClassToCheck(new File(classesDirectory, f)); } } catch (IOException ioe) { throw new ExitException(EXIT_ERR_OTHER, "Failed to load one of the given class files: " + ioe); diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java index c61ffe50..e9eecb80 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java @@ -25,7 +25,6 @@ import java.io.Closeable; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.lang.annotation.RetentionPolicy; import java.net.MalformedURLException; @@ -417,7 +416,7 @@ public void info(String msg) { } if (signaturesFiles != null) for (final File f : signaturesFiles) { log.info("Reading API signatures: " + f); - checker.parseSignaturesFile(new FileInputStream(f)); + checker.parseSignaturesFile(f); } } catch (IOException ioe) { throw new ResourceException("IO problem while reading files with API signatures.", ioe); @@ -437,7 +436,7 @@ public void info(String msg) { log.info("Loading classes to check..."); try { for (File f : getClassFiles()) { - checker.addClassToCheck(new FileInputStream(f)); + checker.addClassToCheck(f); } } catch (IOException ioe) { throw new ResourceException("Failed to load one of the given class files.", ioe); diff --git a/src/main/java/de/thetaphi/forbiddenapis/maven/AbstractCheckMojo.java b/src/main/java/de/thetaphi/forbiddenapis/maven/AbstractCheckMojo.java index 6bfb1795..7a44009d 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/maven/AbstractCheckMojo.java +++ b/src/main/java/de/thetaphi/forbiddenapis/maven/AbstractCheckMojo.java @@ -32,7 +32,6 @@ import java.io.Closeable; import java.io.File; -import java.io.FileInputStream; import java.io.IOException; import java.lang.annotation.RetentionPolicy; import java.net.URLClassLoader; @@ -293,7 +292,7 @@ public void info(String msg) { } if (signaturesFiles != null) for (final File f : signaturesFiles) { log.info("Reading API signatures: " + f); - checker.parseSignaturesFile(new FileInputStream(f)); + checker.parseSignaturesFile(f); } } catch (IOException ioe) { throw new MojoExecutionException("IO problem while reading files with API signatures: " + ioe); @@ -313,7 +312,7 @@ public void info(String msg) { log.info("Loading classes to check..."); try { for (String f : files) { - checker.addClassToCheck(new FileInputStream(new File(classesDirectory, f))); + checker.addClassToCheck(new File(classesDirectory, f)); } } catch (IOException ioe) { throw new MojoExecutionException("Failed to load one of the given class files: " + ioe); From ed14d03286737aa604da8a88d78052c5407c3e26 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sun, 6 Sep 2015 15:31:40 +0200 Subject: [PATCH 23/40] Rename some class files so the API is more useable in Gradle scripts. Also remove useless Exception. --- ...radleTask.java => CheckForbiddenApis.java} | 30 ++++++++----------- ...lePlugin.java => ForbiddenApisPlugin.java} | 10 +++---- .../gradle/GradleForbiddenApiException.java | 28 ----------------- .../de.thetaphi.forbiddenapis.properties | 2 +- 4 files changed, 19 insertions(+), 51 deletions(-) rename src/main/java/de/thetaphi/forbiddenapis/gradle/{GradleTask.java => CheckForbiddenApis.java} (94%) rename src/main/java/de/thetaphi/forbiddenapis/gradle/{GradlePlugin.java => ForbiddenApisPlugin.java} (90%) delete mode 100644 src/main/java/de/thetaphi/forbiddenapis/gradle/GradleForbiddenApiException.java diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java similarity index 94% rename from src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java rename to src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java index e9eecb80..dfcb5dff 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleTask.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java @@ -63,7 +63,7 @@ * Forbiddenapis Gradle Task * @since 1.9 */ -public class GradleTask extends DefaultTask implements PatternFilterable { +public class CheckForbiddenApis extends DefaultTask implements PatternFilterable { private File classesDir; private FileCollection classpath, signaturesFiles; @@ -266,7 +266,7 @@ public Set getIncludes() { return patternSet.getIncludes(); } - public GradleTask setIncludes(Iterable includes) { + public CheckForbiddenApis setIncludes(Iterable includes) { patternSet.setIncludes(includes); return this; } @@ -282,47 +282,47 @@ public Set getExcludes() { return patternSet.getExcludes(); } - public GradleTask setExcludes(Iterable excludes) { + public CheckForbiddenApis setExcludes(Iterable excludes) { patternSet.setExcludes(excludes); return this; } - public GradleTask exclude(String... arg0) { + public CheckForbiddenApis exclude(String... arg0) { patternSet.exclude(arg0); return this; } - public GradleTask exclude(Iterable arg0) { + public CheckForbiddenApis exclude(Iterable arg0) { patternSet.exclude(arg0); return this; } - public GradleTask exclude(Spec arg0) { + public CheckForbiddenApis exclude(Spec arg0) { patternSet.exclude(arg0); return this; } - public GradleTask exclude(@SuppressWarnings("rawtypes") Closure arg0) { + public CheckForbiddenApis exclude(@SuppressWarnings("rawtypes") Closure arg0) { patternSet.exclude(arg0); return this; } - public GradleTask include(String... arg0) { + public CheckForbiddenApis include(String... arg0) { patternSet.include(arg0); return this; } - public GradleTask include(Iterable arg0) { + public CheckForbiddenApis include(Iterable arg0) { patternSet.include(arg0); return this; } - public GradleTask include(Spec arg0) { + public CheckForbiddenApis include(Spec arg0) { patternSet.include(arg0); return this; } - public GradleTask include(@SuppressWarnings("rawtypes") Closure arg0) { + public CheckForbiddenApis include(@SuppressWarnings("rawtypes") Closure arg0) { patternSet.include(arg0); return this; } @@ -335,7 +335,7 @@ public FileTree getClassFiles() { } @TaskAction - public void checkForbidden() { + public void checkForbidden() throws ForbiddenApiException { if (classesDir == null || classpath == null) { throw new InvalidUserDataException("Missing 'classesDir' or 'classpath' property."); } @@ -443,11 +443,7 @@ public void info(String msg) { } log.info("Scanning for API signatures and dependencies..."); - try { - checker.run(); - } catch (ForbiddenApiException fae) { - throw new GradleForbiddenApiException(fae.getMessage()); - } + checker.run(); } finally { // Java 7 supports closing URLClassLoader, so check for Closeable interface: if (urlLoader instanceof Closeable) try { diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java similarity index 90% rename from src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java rename to src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java index bd79a8ba..4ffc1e3a 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradlePlugin.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java @@ -31,7 +31,7 @@ * Forbiddenapis Gradle Plugin * @since 1.9 */ -public class GradlePlugin implements Plugin { +public class ForbiddenApisPlugin implements Plugin { public static final String FORBIDDEN_APIS_TASK_NAME = "forbiddenApis"; public static final String TEST_FORBIDDEN_APIS_TASK_NAME = "testForbiddenApis"; @@ -49,15 +49,15 @@ public void apply(final Project project) { testClassesDir = getClassesDirByName(project, "test"); // Create the tasks of the plugin: - final Task forbiddenTask = tasks.create(FORBIDDEN_APIS_TASK_NAME, GradleTask.class, new Action() { - public void execute(GradleTask task) { + final Task forbiddenTask = tasks.create(FORBIDDEN_APIS_TASK_NAME, CheckForbiddenApis.class, new Action() { + public void execute(CheckForbiddenApis task) { task.setClassesDir(mainClassesDir); task.setClasspath(configurations.getByName("compile")); task.dependsOn(tasks.getByName("compileJava")); } }); - final Task testForbiddenTask = tasks.create(TEST_FORBIDDEN_APIS_TASK_NAME, GradleTask.class, new Action() { - public void execute(GradleTask task) { + final Task testForbiddenTask = tasks.create(TEST_FORBIDDEN_APIS_TASK_NAME, CheckForbiddenApis.class, new Action() { + public void execute(CheckForbiddenApis task) { task.setClassesDir(testClassesDir); task.setClasspath(configurations.getByName("testCompile").plus(project.files(mainClassesDir))); task.dependsOn(tasks.getByName("compileTestJava")); diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleForbiddenApiException.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleForbiddenApiException.java deleted file mode 100644 index 21c7a6bc..00000000 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/GradleForbiddenApiException.java +++ /dev/null @@ -1,28 +0,0 @@ -package de.thetaphi.forbiddenapis.gradle; - -/* - * (C) Copyright Uwe Schindler (Generics Policeman) and others. - * - * 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. - */ - -import org.gradle.api.GradleException; - -@SuppressWarnings("serial") -public final class GradleForbiddenApiException extends GradleException { - - public GradleForbiddenApiException(String msg) { - super(msg); - } - -} diff --git a/src/main/resources/META-INF/gradle-plugins/de.thetaphi.forbiddenapis.properties b/src/main/resources/META-INF/gradle-plugins/de.thetaphi.forbiddenapis.properties index 83975594..dfd15d10 100644 --- a/src/main/resources/META-INF/gradle-plugins/de.thetaphi.forbiddenapis.properties +++ b/src/main/resources/META-INF/gradle-plugins/de.thetaphi.forbiddenapis.properties @@ -1 +1 @@ -implementation-class=de.thetaphi.forbiddenapis.gradle.GradlePlugin +implementation-class=de.thetaphi.forbiddenapis.gradle.ForbiddenApisPlugin From e00b3725a1296473d7cda6aa759f4c2b98b52c1a Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sun, 6 Sep 2015 20:11:41 +0200 Subject: [PATCH 24/40] Refactor plugin initialization to use a Groovy script, executed by GroovyShell. The synatx is otherwise unmaintainable. Also add the dependency on our tasks to "check" --- .../gradle/ForbiddenApisPlugin.java | 70 +++++++------------ .../forbiddenapis/gradle/plugin-init.groovy | 32 +++++++++ 2 files changed, 58 insertions(+), 44 deletions(-) create mode 100644 src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java index 4ffc1e3a..ac4226a1 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java @@ -16,16 +16,18 @@ * limitations under the License. */ -import java.io.File; +import groovy.lang.Binding; +import groovy.lang.GroovyShell; -import org.gradle.api.Action; -import org.gradle.api.NamedDomainObjectCollection; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +import org.codehaus.groovy.control.CompilerConfiguration; +import org.codehaus.groovy.control.customizers.ImportCustomizer; import org.gradle.api.Plugin; import org.gradle.api.Project; -import org.gradle.api.Task; -import org.gradle.api.artifacts.ConfigurationContainer; import org.gradle.api.plugins.PluginInstantiationException; -import org.gradle.api.tasks.TaskContainer; /** * Forbiddenapis Gradle Plugin @@ -33,50 +35,30 @@ */ public class ForbiddenApisPlugin implements Plugin { + private static final String PLUGIN_INIT_SCRIPT = "plugin-init.groovy"; + public static final String FORBIDDEN_APIS_TASK_NAME = "forbiddenApis"; public static final String TEST_FORBIDDEN_APIS_TASK_NAME = "testForbiddenApis"; public void apply(final Project project) { - if (project.getPlugins().findPlugin("java") == null) { - throw new PluginInstantiationException("Forbiddenapis only works in projects using the 'java' plugin."); - } - - final ConfigurationContainer configurations = project.getConfigurations(); - final TaskContainer tasks = project.getTasks(); - - // Get classes directories for main and test - final File mainClassesDir = getClassesDirByName(project, "main"), - testClassesDir = getClassesDirByName(project, "test"); - - // Create the tasks of the plugin: - final Task forbiddenTask = tasks.create(FORBIDDEN_APIS_TASK_NAME, CheckForbiddenApis.class, new Action() { - public void execute(CheckForbiddenApis task) { - task.setClassesDir(mainClassesDir); - task.setClasspath(configurations.getByName("compile")); - task.dependsOn(tasks.getByName("compileJava")); + try { + final InputStream in = ForbiddenApisPlugin.class.getResourceAsStream(PLUGIN_INIT_SCRIPT); + if (in == null) { + throw new PluginInstantiationException("Cannot find resource with plugin init script."); } - }); - final Task testForbiddenTask = tasks.create(TEST_FORBIDDEN_APIS_TASK_NAME, CheckForbiddenApis.class, new Action() { - public void execute(CheckForbiddenApis task) { - task.setClassesDir(testClassesDir); - task.setClasspath(configurations.getByName("testCompile").plus(project.files(mainClassesDir))); - task.dependsOn(tasks.getByName("compileTestJava")); + try { + final ImportCustomizer importCustomizer = new ImportCustomizer().addStarImports(ForbiddenApisPlugin.class.getPackage().getName()); + final CompilerConfiguration configuration = new CompilerConfiguration().addCompilationCustomizers(importCustomizer); + final Binding binding = new Binding(); + binding.setVariable("plugin", this); + binding.setVariable("project", project); + new GroovyShell(ForbiddenApisPlugin.class.getClassLoader(), binding, configuration).evaluate(new InputStreamReader(in, "UTF-8"), PLUGIN_INIT_SCRIPT); + } finally { + in.close(); } - }); - - // Add our tasks as dependencies to chain - tasks.getByName("classes").dependsOn(forbiddenTask); - tasks.getByName("testClasses").dependsOn(testForbiddenTask); - } - - private File getClassesDirByName(Project project, String sourceSetName) { - final Object sourceSet = ((NamedDomainObjectCollection) project.property("sourceSets")).getByName(sourceSetName); - try { - final Object output = sourceSet.getClass().getMethod("getOutput").invoke(sourceSet); - return (File) output.getClass().getMethod("getClassesDir").invoke(output); - } catch (Exception e) { - throw new PluginInstantiationException("Forbiddenapis was not able to initialize classesDir.", e); - } + } catch (IOException ioe) { + throw new PluginInstantiationException("Cannot execute plugin init script.", ioe); + } } } diff --git a/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy b/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy new file mode 100644 index 00000000..c874d7e2 --- /dev/null +++ b/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy @@ -0,0 +1,32 @@ +// initializes the plugin and binds it to the lifecycle + +import org.gradle.api.Task; +import org.gradle.api.artifacts.ConfigurationContainer; +import org.gradle.api.plugins.PluginInstantiationException; +import org.gradle.api.tasks.TaskContainer; + +if (!project.plugins.findPlugin("java")) { + throw new PluginInstantiationException("Forbiddenapis only works in projects using the 'java' plugin."); +} + +ConfigurationContainer configurations = project.getConfigurations(); +TaskContainer tasks = project.getTasks(); + +// Get classes directories for main and test +File mainClassesDir = project.sourceSets.main.output.classesDir; +File testClassesDir = project.sourceSets.test.output.classesDir; + +// Create the tasks of the plugin: +def forbiddenTask = tasks.create(plugin.FORBIDDEN_APIS_TASK_NAME, CheckForbiddenApis.class) { + it.setClassesDir(mainClassesDir); + it.setClasspath(configurations.getByName("compile")); + it.dependsOn(tasks.getByName("classes")); +} +def testForbiddenTask = tasks.create(plugin.TEST_FORBIDDEN_APIS_TASK_NAME, CheckForbiddenApis.class) { + it.setClassesDir(testClassesDir); + it.setClasspath(configurations.getByName("testCompile").plus(project.files(mainClassesDir))); + it.dependsOn(tasks.getByName("testClasses")); +} + +// Add our tasks as dependencies to chain +tasks.getByName("check").dependsOn(forbiddenTask, testForbiddenTask); From 70dca88fe16db3bb47a5327a23e3efad243acd1f Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sun, 6 Sep 2015 20:13:15 +0200 Subject: [PATCH 25/40] Improve error message --- .../de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java index ac4226a1..00f9f8ff 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java @@ -57,7 +57,7 @@ public void apply(final Project project) { in.close(); } } catch (IOException ioe) { - throw new PluginInstantiationException("Cannot execute plugin init script.", ioe); + throw new PluginInstantiationException("Cannot execute " + PLUGIN_INIT_SCRIPT + " script.", ioe); } } From 2d2e50a16675a59f66d7f4cf6c7ea159bd1c9706 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sun, 6 Sep 2015 20:55:20 +0200 Subject: [PATCH 26/40] Make it work with all SourceSets --- .../gradle/ForbiddenApisPlugin.java | 3 +-- .../forbiddenapis/gradle/plugin-init.groovy | 22 +++++++------------ 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java index 00f9f8ff..ab5c7311 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java @@ -37,8 +37,7 @@ public class ForbiddenApisPlugin implements Plugin { private static final String PLUGIN_INIT_SCRIPT = "plugin-init.groovy"; - public static final String FORBIDDEN_APIS_TASK_NAME = "forbiddenApis"; - public static final String TEST_FORBIDDEN_APIS_TASK_NAME = "testForbiddenApis"; + public static final String FORBIDDEN_APIS_TASK_NAME_VERB = "forbiddenApis"; public void apply(final Project project) { try { diff --git a/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy b/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy index c874d7e2..d76e269a 100644 --- a/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy +++ b/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy @@ -12,21 +12,15 @@ if (!project.plugins.findPlugin("java")) { ConfigurationContainer configurations = project.getConfigurations(); TaskContainer tasks = project.getTasks(); -// Get classes directories for main and test -File mainClassesDir = project.sourceSets.main.output.classesDir; -File testClassesDir = project.sourceSets.test.output.classesDir; +def forbiddenTasks = []; -// Create the tasks of the plugin: -def forbiddenTask = tasks.create(plugin.FORBIDDEN_APIS_TASK_NAME, CheckForbiddenApis.class) { - it.setClassesDir(mainClassesDir); - it.setClasspath(configurations.getByName("compile")); - it.dependsOn(tasks.getByName("classes")); -} -def testForbiddenTask = tasks.create(plugin.TEST_FORBIDDEN_APIS_TASK_NAME, CheckForbiddenApis.class) { - it.setClassesDir(testClassesDir); - it.setClasspath(configurations.getByName("testCompile").plus(project.files(mainClassesDir))); - it.dependsOn(tasks.getByName("testClasses")); +project.sourceSets.each { sourceSet -> + forbiddenTasks += tasks.create(sourceSet.getTaskName(plugin.FORBIDDEN_APIS_TASK_NAME_VERB, null), CheckForbiddenApis.class) { task -> + task.setClassesDir(sourceSet.output.classesDir); + task.setClasspath(sourceSet.compileClasspath); + task.dependsOn(sourceSet.output); + } } // Add our tasks as dependencies to chain -tasks.getByName("check").dependsOn(forbiddenTask, testForbiddenTask); +tasks.getByName("check").dependsOn(forbiddenTasks); From c6bed964ac6380e5f9c3703d6dbc77ce0aefcad5 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sun, 6 Sep 2015 21:10:43 +0200 Subject: [PATCH 27/40] More code cleanup in task-init.groovy --- .../thetaphi/forbiddenapis/gradle/plugin-init.groovy | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy b/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy index d76e269a..a022e03c 100644 --- a/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy +++ b/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy @@ -1,7 +1,5 @@ // initializes the plugin and binds it to the lifecycle -import org.gradle.api.Task; -import org.gradle.api.artifacts.ConfigurationContainer; import org.gradle.api.plugins.PluginInstantiationException; import org.gradle.api.tasks.TaskContainer; @@ -9,13 +7,10 @@ if (!project.plugins.findPlugin("java")) { throw new PluginInstantiationException("Forbiddenapis only works in projects using the 'java' plugin."); } -ConfigurationContainer configurations = project.getConfigurations(); -TaskContainer tasks = project.getTasks(); +def tasks = project.getTasks(); -def forbiddenTasks = []; - -project.sourceSets.each { sourceSet -> - forbiddenTasks += tasks.create(sourceSet.getTaskName(plugin.FORBIDDEN_APIS_TASK_NAME_VERB, null), CheckForbiddenApis.class) { task -> +def forbiddenTasks = project.sourceSets.collect { sourceSet -> + tasks.create(sourceSet.getTaskName(plugin.FORBIDDEN_APIS_TASK_NAME_VERB, null), CheckForbiddenApis.class) { task -> task.setClassesDir(sourceSet.output.classesDir); task.setClasspath(sourceSet.compileClasspath); task.dependsOn(sourceSet.output); From 6dd1eeee5c005c7e479e41d784a71f7394754878 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sun, 6 Sep 2015 22:06:34 +0200 Subject: [PATCH 28/40] Add descriptions to task (not yet the group); make "this" correct in init script (refer to Plugin). --- .../gradle/ForbiddenApisPlugin.java | 18 ++++++++++-------- .../forbiddenapis/gradle/plugin-init.groovy | 12 ++++++++---- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java index ab5c7311..87630efc 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java @@ -18,10 +18,12 @@ import groovy.lang.Binding; import groovy.lang.GroovyShell; +import groovy.util.DelegatingScript; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.util.Collections; import org.codehaus.groovy.control.CompilerConfiguration; import org.codehaus.groovy.control.customizers.ImportCustomizer; @@ -35,23 +37,23 @@ */ public class ForbiddenApisPlugin implements Plugin { - private static final String PLUGIN_INIT_SCRIPT = "plugin-init.groovy"; - - public static final String FORBIDDEN_APIS_TASK_NAME_VERB = "forbiddenApis"; + public static final String PLUGIN_INIT_SCRIPT = "plugin-init.groovy"; + public static final String FORBIDDEN_APIS_TASK_NAME_PREFIX = "forbiddenApis"; public void apply(final Project project) { try { final InputStream in = ForbiddenApisPlugin.class.getResourceAsStream(PLUGIN_INIT_SCRIPT); if (in == null) { - throw new PluginInstantiationException("Cannot find resource with plugin init script."); + throw new PluginInstantiationException("Cannot find resource with " + PLUGIN_INIT_SCRIPT + " script."); } try { final ImportCustomizer importCustomizer = new ImportCustomizer().addStarImports(ForbiddenApisPlugin.class.getPackage().getName()); final CompilerConfiguration configuration = new CompilerConfiguration().addCompilationCustomizers(importCustomizer); - final Binding binding = new Binding(); - binding.setVariable("plugin", this); - binding.setVariable("project", project); - new GroovyShell(ForbiddenApisPlugin.class.getClassLoader(), binding, configuration).evaluate(new InputStreamReader(in, "UTF-8"), PLUGIN_INIT_SCRIPT); + configuration.setScriptBaseClass(DelegatingScript.class.getName()); + final GroovyShell shell = new GroovyShell(ForbiddenApisPlugin.class.getClassLoader(), new Binding(Collections.singletonMap("project", project)), configuration); + final DelegatingScript script = (DelegatingScript) shell.parse(new InputStreamReader(in, "UTF-8"), PLUGIN_INIT_SCRIPT); + script.setDelegate(this); + script.run(); } finally { in.close(); } diff --git a/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy b/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy index a022e03c..e3ade071 100644 --- a/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy +++ b/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy @@ -8,14 +8,18 @@ if (!project.plugins.findPlugin("java")) { } def tasks = project.getTasks(); +def checkTask = tasks.getByName("check"); +// Define our tasks (one for each SourceSet): def forbiddenTasks = project.sourceSets.collect { sourceSet -> - tasks.create(sourceSet.getTaskName(plugin.FORBIDDEN_APIS_TASK_NAME_VERB, null), CheckForbiddenApis.class) { task -> - task.setClassesDir(sourceSet.output.classesDir); - task.setClasspath(sourceSet.compileClasspath); + tasks.create(sourceSet.getTaskName(FORBIDDEN_APIS_TASK_NAME_PREFIX, null), CheckForbiddenApis.class) { task -> + task.classesDir = sourceSet.output.classesDir; + task.classpath = sourceSet.compileClasspath; + task.description = "Runs forbiddenApis checks on '" + sourceSet.name + "' classes."; + // task.group = checkTask.group; task.dependsOn(sourceSet.output); } } // Add our tasks as dependencies to chain -tasks.getByName("check").dependsOn(forbiddenTasks); +checkTask.dependsOn(forbiddenTasks); From 81c7daf247afcee06692ad72bae98464f68455c8 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sun, 6 Sep 2015 23:06:15 +0200 Subject: [PATCH 29/40] Add a task to execute on all sourcesets with one call --- .../gradle/ForbiddenApisPlugin.java | 2 +- .../forbiddenapis/gradle/plugin-init.groovy | 17 ++++++++++++----- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java index 87630efc..c6361259 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java @@ -38,7 +38,7 @@ public class ForbiddenApisPlugin implements Plugin { public static final String PLUGIN_INIT_SCRIPT = "plugin-init.groovy"; - public static final String FORBIDDEN_APIS_TASK_NAME_PREFIX = "forbiddenApis"; + public static final String FORBIDDEN_APIS_TASK_NAME = "forbiddenApis"; public void apply(final Project project) { try { diff --git a/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy b/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy index e3ade071..a95093c8 100644 --- a/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy +++ b/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy @@ -1,5 +1,6 @@ // initializes the plugin and binds it to the lifecycle +import org.gradle.api.DefaultTask; import org.gradle.api.plugins.PluginInstantiationException; import org.gradle.api.tasks.TaskContainer; @@ -12,14 +13,20 @@ def checkTask = tasks.getByName("check"); // Define our tasks (one for each SourceSet): def forbiddenTasks = project.sourceSets.collect { sourceSet -> - tasks.create(sourceSet.getTaskName(FORBIDDEN_APIS_TASK_NAME_PREFIX, null), CheckForbiddenApis.class) { task -> + tasks.create(sourceSet.getTaskName(FORBIDDEN_APIS_TASK_NAME, null), CheckForbiddenApis.class) { task -> task.classesDir = sourceSet.output.classesDir; task.classpath = sourceSet.compileClasspath; - task.description = "Runs forbiddenApis checks on '" + sourceSet.name + "' classes."; - // task.group = checkTask.group; + task.description = "Runs forbidden-apis checks on '" + sourceSet.name + "' classes."; task.dependsOn(sourceSet.output); } } -// Add our tasks as dependencies to chain -checkTask.dependsOn(forbiddenTasks); +// Create a task for all checks +def forbiddenTask = tasks.create(FORBIDDEN_APIS_TASK_NAME, DefaultTask.class) { task -> + task.description = "Runs forbidden-apis checks."; + task.group = checkTask.group; + task.dependsOn(forbiddenTasks); +} + +// Add our task as dependency to chain +checkTask.dependsOn(forbiddenTask); From 69359a4b270546475ed07a7dbba259b0021f0a72 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sun, 6 Sep 2015 23:08:45 +0200 Subject: [PATCH 30/40] Small cleanup by using GString --- .../de/thetaphi/forbiddenapis/gradle/plugin-init.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy b/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy index a95093c8..e86a436b 100644 --- a/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy +++ b/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy @@ -16,7 +16,7 @@ def forbiddenTasks = project.sourceSets.collect { sourceSet -> tasks.create(sourceSet.getTaskName(FORBIDDEN_APIS_TASK_NAME, null), CheckForbiddenApis.class) { task -> task.classesDir = sourceSet.output.classesDir; task.classpath = sourceSet.compileClasspath; - task.description = "Runs forbidden-apis checks on '" + sourceSet.name + "' classes."; + task.description = "Runs forbidden-apis checks on '${sourceSet.name}' classes."; task.dependsOn(sourceSet.output); } } From 0cba435b1970213960aed366fec6ca358c0633a7 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sun, 6 Sep 2015 23:47:38 +0200 Subject: [PATCH 31/40] Minor cleanups & constants --- .../forbiddenapis/gradle/ForbiddenApisPlugin.java | 4 ++++ .../thetaphi/forbiddenapis/gradle/plugin-init.groovy | 11 +++++------ 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java index c6361259..655e661c 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java @@ -37,9 +37,13 @@ */ public class ForbiddenApisPlugin implements Plugin { + /** Resource with Groovy script that initializes the plugin. */ public static final String PLUGIN_INIT_SCRIPT = "plugin-init.groovy"; + + /** Name of the base task that depends on one for every SourceSet */ public static final String FORBIDDEN_APIS_TASK_NAME = "forbiddenApis"; + // Not before Java 6: @Override public void apply(final Project project) { try { final InputStream in = ForbiddenApisPlugin.class.getResourceAsStream(PLUGIN_INIT_SCRIPT); diff --git a/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy b/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy index e86a436b..b2f26fe9 100644 --- a/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy +++ b/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy @@ -1,15 +1,14 @@ // initializes the plugin and binds it to the lifecycle import org.gradle.api.DefaultTask; +import org.gradle.api.plugins.JavaBasePlugin; import org.gradle.api.plugins.PluginInstantiationException; -import org.gradle.api.tasks.TaskContainer; -if (!project.plugins.findPlugin("java")) { - throw new PluginInstantiationException("Forbiddenapis only works in projects using the 'java' plugin."); +if (project.plugins.withType(JavaBasePlugin.class).isEmpty()) { + throw new PluginInstantiationException('Forbidden-apis only works in projects using the java plugin.'); } def tasks = project.getTasks(); -def checkTask = tasks.getByName("check"); // Define our tasks (one for each SourceSet): def forbiddenTasks = project.sourceSets.collect { sourceSet -> @@ -24,9 +23,9 @@ def forbiddenTasks = project.sourceSets.collect { sourceSet -> // Create a task for all checks def forbiddenTask = tasks.create(FORBIDDEN_APIS_TASK_NAME, DefaultTask.class) { task -> task.description = "Runs forbidden-apis checks."; - task.group = checkTask.group; + task.group = JavaBasePlugin.VERIFICATION_GROUP; task.dependsOn(forbiddenTasks); } // Add our task as dependency to chain -checkTask.dependsOn(forbiddenTask); +tasks.getByName(JavaBasePlugin.CHECK_TASK_NAME).dependsOn(forbiddenTask); From ba997958f67eb5f01ae44b4c06c940c023f851a5 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Sun, 6 Sep 2015 23:53:47 +0200 Subject: [PATCH 32/40] Add missing license header --- .../forbiddenapis/gradle/plugin-init.groovy | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy b/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy index b2f26fe9..7e9776fb 100644 --- a/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy +++ b/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy @@ -1,4 +1,20 @@ -// initializes the plugin and binds it to the lifecycle +/* + * (C) Copyright Uwe Schindler (Generics Policeman) and others. + * + * 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. + */ + +/** Initializes the plugin and binds it to project lifecycle. */ import org.gradle.api.DefaultTask; import org.gradle.api.plugins.JavaBasePlugin; From 6268bb1e96a1b2de3e10e623565d26b86c52d60f Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Mon, 7 Sep 2015 00:50:48 +0200 Subject: [PATCH 33/40] Code cleanups, also implement VerificationTask --- .../gradle/CheckForbiddenApis.java | 18 +++++++++++++++++- .../forbiddenapis/gradle/plugin-init.groovy | 19 +++++++++---------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java index dfcb5dff..798bc4c6 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java @@ -51,6 +51,7 @@ import org.gradle.api.tasks.OutputDirectory; import org.gradle.api.tasks.SkipWhenEmpty; import org.gradle.api.tasks.TaskAction; +import org.gradle.api.tasks.VerificationTask; import org.gradle.api.tasks.util.PatternFilterable; import org.gradle.api.tasks.util.PatternSet; @@ -63,7 +64,7 @@ * Forbiddenapis Gradle Task * @since 1.9 */ -public class CheckForbiddenApis extends DefaultTask implements PatternFilterable { +public class CheckForbiddenApis extends DefaultTask implements PatternFilterable,VerificationTask { private File classesDir; private FileCollection classpath, signaturesFiles; @@ -229,6 +230,21 @@ public void setFailOnViolation(boolean failOnViolation) { setOption(FAIL_ON_VIOLATION, failOnViolation); } + /** + * @{inheritDoc} + *

+ * This setting is to conform with {@link VerificationTask} interface. + * It is the negation of {@link #getFailOnViolation}. + * @see #getFailOnViolation + */ + public boolean getIgnoreFailures() { + return !getFailOnViolation(); + } + + public void setIgnoreFailures(boolean ignore) { + setFailOnViolation(!ignore); + } + /** * List of a custom Java annotations (full class names) that are used in the checked * code to suppress errors. Those annotations must have at least diff --git a/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy b/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy index 7e9776fb..d04f3d6f 100644 --- a/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy +++ b/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy @@ -16,7 +16,6 @@ /** Initializes the plugin and binds it to project lifecycle. */ -import org.gradle.api.DefaultTask; import org.gradle.api.plugins.JavaBasePlugin; import org.gradle.api.plugins.PluginInstantiationException; @@ -28,19 +27,19 @@ def tasks = project.getTasks(); // Define our tasks (one for each SourceSet): def forbiddenTasks = project.sourceSets.collect { sourceSet -> - tasks.create(sourceSet.getTaskName(FORBIDDEN_APIS_TASK_NAME, null), CheckForbiddenApis.class) { task -> - task.classesDir = sourceSet.output.classesDir; - task.classpath = sourceSet.compileClasspath; - task.description = "Runs forbidden-apis checks on '${sourceSet.name}' classes."; - task.dependsOn(sourceSet.output); + tasks.create(sourceSet.getTaskName(FORBIDDEN_APIS_TASK_NAME, null), CheckForbiddenApis.class) { + classesDir = sourceSet.output.classesDir; + classpath = sourceSet.compileClasspath; + description = "Runs forbidden-apis checks on '${sourceSet.name}' classes."; + dependsOn(sourceSet.output); } } // Create a task for all checks -def forbiddenTask = tasks.create(FORBIDDEN_APIS_TASK_NAME, DefaultTask.class) { task -> - task.description = "Runs forbidden-apis checks."; - task.group = JavaBasePlugin.VERIFICATION_GROUP; - task.dependsOn(forbiddenTasks); +def forbiddenTask = tasks.create(FORBIDDEN_APIS_TASK_NAME) { + description = "Runs forbidden-apis checks."; + group = JavaBasePlugin.VERIFICATION_GROUP; + dependsOn(forbiddenTasks); } // Add our task as dependency to chain From e35cf6de40d86954e451556b9138ebe11f347761 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Mon, 7 Sep 2015 12:30:20 +0200 Subject: [PATCH 34/40] Delay initialization of task until whole project is parsed. --- .../forbiddenapis/gradle/plugin-init.groovy | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy b/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy index d04f3d6f..5ff195f4 100644 --- a/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy +++ b/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy @@ -28,10 +28,19 @@ def tasks = project.getTasks(); // Define our tasks (one for each SourceSet): def forbiddenTasks = project.sourceSets.collect { sourceSet -> tasks.create(sourceSet.getTaskName(FORBIDDEN_APIS_TASK_NAME, null), CheckForbiddenApis.class) { - classesDir = sourceSet.output.classesDir; - classpath = sourceSet.compileClasspath; description = "Runs forbidden-apis checks on '${sourceSet.name}' classes."; - dependsOn(sourceSet.output); + // We don't use conventions for delayed configuration, because this is internal feature. + // We use closure that executes after the project was completely evaulated and then sets + // classesDir and classpath if not specified by user otherwise. + project.afterEvaluate { + if (classesDir == null) { + classesDir = sourceSet.output.classesDir; + dependsOn(sourceSet.output); + } + if (classpath == null) { + classpath = sourceSet.compileClasspath; + } + } } } From 974e441b29bcf1cec981ed5471459b012ecc5bdd Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Mon, 7 Sep 2015 23:02:45 +0200 Subject: [PATCH 35/40] Allow use of Extension to define task defaults. The extension name is 'forbiddenApis', it supports all the properties which are supported by the task (excluding classpath and classesDir) --- .../gradle/CheckForbiddenApis.java | 112 ++++++++---------- .../gradle/CheckForbiddenApisExtension.java | 56 +++++++++ .../forbiddenapis/gradle/plugin-init.groovy | 11 +- 3 files changed, 112 insertions(+), 67 deletions(-) create mode 100644 src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApisExtension.java diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java index 798bc4c6..9622d867 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java @@ -53,7 +53,6 @@ import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.VerificationTask; import org.gradle.api.tasks.util.PatternFilterable; -import org.gradle.api.tasks.util.PatternSet; import de.thetaphi.forbiddenapis.Checker; import de.thetaphi.forbiddenapis.ForbiddenApiException; @@ -61,24 +60,14 @@ import de.thetaphi.forbiddenapis.ParseException; /** - * Forbiddenapis Gradle Task + * ForbiddenApis Gradle Task * @since 1.9 */ public class CheckForbiddenApis extends DefaultTask implements PatternFilterable,VerificationTask { + private final CheckForbiddenApisExtension data = new CheckForbiddenApisExtension(); private File classesDir; - private FileCollection classpath, signaturesFiles; - private List signatures; - private List bundledSignatures, suppressAnnotations; - private boolean failOnUnsupportedJava = false; - - private final EnumSet options = EnumSet.of(FAIL_ON_MISSING_CLASSES, FAIL_ON_UNRESOLVABLE_SIGNATURES, FAIL_ON_VIOLATION); - private final PatternFilterable patternSet = new PatternSet().include("**/*.class"); - - private void setOption(Checker.Option opt, boolean value) { - options.remove(opt); - if (value) options.add(opt); - } + private FileCollection classpath; /** * Directory with the class files to check. @@ -114,12 +103,12 @@ public void setClasspath(FileCollection classpath) { @InputFiles @Optional public FileCollection getSignaturesFiles() { - return signaturesFiles; + return data.signaturesFiles; } /** @see #getSignaturesFiles */ public void setSignaturesFiles(FileCollection signaturesFiles) { - this.signaturesFiles = signaturesFiles; + data.signaturesFiles = signaturesFiles; } /** @@ -131,12 +120,12 @@ public void setSignaturesFiles(FileCollection signaturesFiles) { @Input @Optional public List getSignatures() { - return signatures; + return data.signatures; } /** @see #getSignatures */ public void setSignatures(List signatures) { - this.signatures = signatures; + data.signatures = signatures; } /** @@ -147,12 +136,12 @@ public void setSignatures(List signatures) { @Input @Optional public List getBundledSignatures() { - return bundledSignatures; + return data.bundledSignatures; } /** @see #getBundledSignatures */ public void setBundledSignatures(List bundledSignatures) { - this.bundledSignatures = bundledSignatures; + data.bundledSignatures = bundledSignatures; } /** @@ -161,12 +150,12 @@ public void setBundledSignatures(List bundledSignatures) { */ @Input public boolean getInternalRuntimeForbidden() { - return options.contains(INTERNAL_RUNTIME_FORBIDDEN); + return data.internalRuntimeForbidden; } /** @see #getInternalRuntimeForbidden */ public void setInternalRuntimeForbidden(boolean internalRuntimeForbidden) { - setOption(INTERNAL_RUNTIME_FORBIDDEN, internalRuntimeForbidden); + data.internalRuntimeForbidden = internalRuntimeForbidden; } /** @@ -176,12 +165,12 @@ public void setInternalRuntimeForbidden(boolean internalRuntimeForbidden) { */ @Input public boolean getFailOnUnsupportedJava() { - return failOnUnsupportedJava; + return data.failOnUnsupportedJava; } /** @see #getFailOnUnsupportedJava */ public void setFailOnUnsupportedJava(boolean failOnUnsupportedJava) { - this.failOnUnsupportedJava = failOnUnsupportedJava; + data.failOnUnsupportedJava = failOnUnsupportedJava; } /** @@ -192,12 +181,12 @@ public void setFailOnUnsupportedJava(boolean failOnUnsupportedJava) { */ @Input public boolean getFailOnMissingClasses() { - return options.contains(FAIL_ON_MISSING_CLASSES); + return data.failOnMissingClasses; } /** @see #getFailOnMissingClasses */ public void setFailOnMissingClasses(boolean failOnMissingClasses) { - setOption(FAIL_ON_MISSING_CLASSES, failOnMissingClasses); + data.failOnMissingClasses = failOnMissingClasses; } /** @@ -208,41 +197,27 @@ public void setFailOnMissingClasses(boolean failOnMissingClasses) { */ @Input public boolean getFailOnUnresolvableSignatures() { - return options.contains(FAIL_ON_UNRESOLVABLE_SIGNATURES); + return data.failOnUnresolvableSignatures; } /** @see #getFailOnUnresolvableSignatures */ public void setFailOnUnresolvableSignatures(boolean failOnUnresolvableSignatures) { - setOption(FAIL_ON_UNRESOLVABLE_SIGNATURES, failOnUnresolvableSignatures); - } - - /** - * Fail the build if violations have been found. Defaults to {@code true}. - * @since 1.9 - */ - @Input - public boolean getFailOnViolation() { - return options.contains(FAIL_ON_VIOLATION); - } - - /** @see #getFailOnViolation */ - public void setFailOnViolation(boolean failOnViolation) { - setOption(FAIL_ON_VIOLATION, failOnViolation); + data.failOnUnresolvableSignatures = failOnUnresolvableSignatures; } /** * @{inheritDoc} *

* This setting is to conform with {@link VerificationTask} interface. - * It is the negation of {@link #getFailOnViolation}. - * @see #getFailOnViolation + * Other ForbiddenApis implementations use another name: {@code failOnViolation} + * Default is {@code false}. */ public boolean getIgnoreFailures() { - return !getFailOnViolation(); + return data.ignoreFailures; } - public void setIgnoreFailures(boolean ignore) { - setFailOnViolation(!ignore); + public void setIgnoreFailures(boolean ignoreFailures) { + data.ignoreFailures = ignoreFailures; } /** @@ -259,12 +234,12 @@ public void setIgnoreFailures(boolean ignore) { @Input @Optional public List getSuppressAnnotations() { - return suppressAnnotations; + return data.suppressAnnotations; } /** @see #getSuppressAnnotations */ public void setSuppressAnnotations(List suppressAnnotations) { - this.suppressAnnotations = suppressAnnotations; + data.suppressAnnotations = suppressAnnotations; } // PatternFilterable implementation: @@ -279,11 +254,11 @@ public void setSuppressAnnotations(List suppressAnnotations) { */ @Input public Set getIncludes() { - return patternSet.getIncludes(); + return data.getIncludes(); } public CheckForbiddenApis setIncludes(Iterable includes) { - patternSet.setIncludes(includes); + data.setIncludes(includes); return this; } @@ -295,51 +270,51 @@ public CheckForbiddenApis setIncludes(Iterable includes) { */ @Input public Set getExcludes() { - return patternSet.getExcludes(); + return data.getExcludes(); } public CheckForbiddenApis setExcludes(Iterable excludes) { - patternSet.setExcludes(excludes); + data.setExcludes(excludes); return this; } public CheckForbiddenApis exclude(String... arg0) { - patternSet.exclude(arg0); + data.exclude(arg0); return this; } public CheckForbiddenApis exclude(Iterable arg0) { - patternSet.exclude(arg0); + data.exclude(arg0); return this; } public CheckForbiddenApis exclude(Spec arg0) { - patternSet.exclude(arg0); + data.exclude(arg0); return this; } public CheckForbiddenApis exclude(@SuppressWarnings("rawtypes") Closure arg0) { - patternSet.exclude(arg0); + data.exclude(arg0); return this; } public CheckForbiddenApis include(String... arg0) { - patternSet.include(arg0); + data.include(arg0); return this; } public CheckForbiddenApis include(Iterable arg0) { - patternSet.include(arg0); + data.include(arg0); return this; } public CheckForbiddenApis include(Spec arg0) { - patternSet.include(arg0); + data.include(arg0); return this; } public CheckForbiddenApis include(@SuppressWarnings("rawtypes") Closure arg0) { - patternSet.include(arg0); + data.include(arg0); return this; } @@ -347,11 +322,13 @@ public CheckForbiddenApis include(@SuppressWarnings("rawtypes") Closure arg0) { @InputFiles @SkipWhenEmpty public FileTree getClassFiles() { - return getProject().files(classesDir).getAsFileTree().matching(patternSet); + return getProject().files(getClassesDir()).getAsFileTree().matching(data); } @TaskAction public void checkForbidden() throws ForbiddenApiException { + final File classesDir = getClassesDir(); + final FileCollection classpath = getClasspath(); if (classesDir == null || classpath == null) { throw new InvalidUserDataException("Missing 'classesDir' or 'classpath' property."); } @@ -389,13 +366,18 @@ public void info(String msg) { ClassLoader.getSystemClassLoader(); try { + final EnumSet options = EnumSet.noneOf(Checker.Option.class); + if (getInternalRuntimeForbidden()) options.add(INTERNAL_RUNTIME_FORBIDDEN); + if (getFailOnMissingClasses()) options.add(FAIL_ON_MISSING_CLASSES); + if (!getIgnoreFailures()) options.add(FAIL_ON_VIOLATION); + if (getFailOnUnresolvableSignatures()) options.add(FAIL_ON_UNRESOLVABLE_SIGNATURES); final Checker checker = new Checker(log, loader, options); if (!checker.isSupportedJDK) { final String msg = String.format(Locale.ENGLISH, "Your Java runtime (%s %s) is not supported by the forbiddenapis plugin. Please run the checks with a supported JDK!", System.getProperty("java.runtime.name"), System.getProperty("java.runtime.version")); - if (failOnUnsupportedJava) { + if (getFailOnUnsupportedJava()) { throw new GradleException(msg); } else { log.warn(msg); @@ -403,6 +385,7 @@ public void info(String msg) { } } + final List suppressAnnotations = getSuppressAnnotations(); if (suppressAnnotations != null) { for (String a : suppressAnnotations) { checker.addSuppressAnnotation(a); @@ -410,6 +393,7 @@ public void info(String msg) { } try { + final List signatures = getSignatures(); if (signatures != null && !signatures.isEmpty()) { log.info("Reading inline API signatures..."); final StringBuilder sb = new StringBuilder(); @@ -418,6 +402,7 @@ public void info(String msg) { } checker.parseSignaturesString(sb.toString()); } + final List bundledSignatures = getBundledSignatures(); if (bundledSignatures != null) { final JavaVersion targetVersion = (JavaVersion) getProject().property("targetCompatibility"); if (targetVersion == null) { @@ -430,6 +415,7 @@ public void info(String msg) { checker.parseBundledSignatures(bs, targetVersion == null ? null : targetVersion.toString()); } } + final FileCollection signaturesFiles = getSignaturesFiles(); if (signaturesFiles != null) for (final File f : signaturesFiles) { log.info("Reading API signatures: " + f); checker.parseSignaturesFile(f); diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApisExtension.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApisExtension.java new file mode 100644 index 00000000..b8bd1eb2 --- /dev/null +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApisExtension.java @@ -0,0 +1,56 @@ +package de.thetaphi.forbiddenapis.gradle; + +/* + * (C) Copyright Uwe Schindler (Generics Policeman) and others. + * + * 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. + */ + +import java.util.Arrays; +import java.util.List; + +import org.gradle.api.file.FileCollection; +import org.gradle.api.tasks.util.PatternSet; + +/** + * Extension for the ForbiddenApis Gradle Task to store defaults. + * For description of the properties refer to the {@link CheckForbiddenApis} + * task documentation. + */ +public class CheckForbiddenApisExtension extends PatternSet { + + public CheckForbiddenApisExtension() { + include("**/*.class"); + } + + public static final List PROPS = Arrays.asList( + "signaturesFiles", + "signatures", + "bundledSignatures", + "suppressAnnotations", + "internalRuntimeForbidden", + "failOnUnsupportedJava", + "failOnMissingClasses", + "failOnUnresolvableSignatures", + "ignoreFailures" + ); + + public FileCollection signaturesFiles; + public List signatures, bundledSignatures, suppressAnnotations; + public boolean internalRuntimeForbidden = false, + failOnUnsupportedJava = false, + failOnMissingClasses = true, + failOnUnresolvableSignatures = true, + ignoreFailures = false; + +} diff --git a/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy b/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy index 5ff195f4..5ea65ddc 100644 --- a/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy +++ b/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy @@ -23,15 +23,18 @@ if (project.plugins.withType(JavaBasePlugin.class).isEmpty()) { throw new PluginInstantiationException('Forbidden-apis only works in projects using the java plugin.'); } -def tasks = project.getTasks(); +def tasks = project.tasks; + +// create Extension for defaults: +def extension = project.extensions.create(FORBIDDEN_APIS_TASK_NAME, CheckForbiddenApisExtension.class); // Define our tasks (one for each SourceSet): def forbiddenTasks = project.sourceSets.collect { sourceSet -> tasks.create(sourceSet.getTaskName(FORBIDDEN_APIS_TASK_NAME, null), CheckForbiddenApis.class) { description = "Runs forbidden-apis checks on '${sourceSet.name}' classes."; - // We don't use conventions for delayed configuration, because this is internal feature. - // We use closure that executes after the project was completely evaulated and then sets - // classesDir and classpath if not specified by user otherwise. + CheckForbiddenApisExtension.PROPS.each { key -> + conventionMapping.map(key, { extension[key] }); + } project.afterEvaluate { if (classesDir == null) { classesDir = sourceSet.output.classesDir; From a230dede114c31c87721b999448738df4fe8ee0a Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Mon, 7 Sep 2015 23:50:34 +0200 Subject: [PATCH 36/40] Code cleanups --- .../gradle/CheckForbiddenApisExtension.java | 11 ++++++----- .../forbiddenapis/gradle/ForbiddenApisPlugin.java | 3 +++ .../thetaphi/forbiddenapis/gradle/plugin-init.groovy | 10 ++++------ 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApisExtension.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApisExtension.java index b8bd1eb2..ef7e584c 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApisExtension.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApisExtension.java @@ -29,11 +29,8 @@ */ public class CheckForbiddenApisExtension extends PatternSet { - public CheckForbiddenApisExtension() { - include("**/*.class"); - } - - public static final List PROPS = Arrays.asList( + /** Fields used for the convention mapping, keep up-to-date with class members! */ + static final List PROPS = Arrays.asList( "signaturesFiles", "signatures", "bundledSignatures", @@ -45,6 +42,10 @@ public CheckForbiddenApisExtension() { "ignoreFailures" ); + public CheckForbiddenApisExtension() { + include("**/*.class"); + } + public FileCollection signaturesFiles; public List signatures, bundledSignatures, suppressAnnotations; public boolean internalRuntimeForbidden = false, diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java index 655e661c..a6ddc8ec 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java @@ -43,6 +43,9 @@ public class ForbiddenApisPlugin implements Plugin { /** Name of the base task that depends on one for every SourceSet */ public static final String FORBIDDEN_APIS_TASK_NAME = "forbiddenApis"; + /** Name of the base task that depends on one for every SourceSet */ + public static final String FORBIDDEN_APIS_EXTENSION_NAME = "forbiddenApis"; + // Not before Java 6: @Override public void apply(final Project project) { try { diff --git a/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy b/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy index 5ea65ddc..35935697 100644 --- a/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy +++ b/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy @@ -23,14 +23,12 @@ if (project.plugins.withType(JavaBasePlugin.class).isEmpty()) { throw new PluginInstantiationException('Forbidden-apis only works in projects using the java plugin.'); } -def tasks = project.tasks; - // create Extension for defaults: -def extension = project.extensions.create(FORBIDDEN_APIS_TASK_NAME, CheckForbiddenApisExtension.class); +def extension = project.extensions.create(FORBIDDEN_APIS_EXTENSION_NAME, CheckForbiddenApisExtension.class); // Define our tasks (one for each SourceSet): def forbiddenTasks = project.sourceSets.collect { sourceSet -> - tasks.create(sourceSet.getTaskName(FORBIDDEN_APIS_TASK_NAME, null), CheckForbiddenApis.class) { + project.tasks.create(sourceSet.getTaskName(FORBIDDEN_APIS_TASK_NAME, null), CheckForbiddenApis.class) { description = "Runs forbidden-apis checks on '${sourceSet.name}' classes."; CheckForbiddenApisExtension.PROPS.each { key -> conventionMapping.map(key, { extension[key] }); @@ -48,11 +46,11 @@ def forbiddenTasks = project.sourceSets.collect { sourceSet -> } // Create a task for all checks -def forbiddenTask = tasks.create(FORBIDDEN_APIS_TASK_NAME) { +def forbiddenTask = project.tasks.create(FORBIDDEN_APIS_TASK_NAME) { description = "Runs forbidden-apis checks."; group = JavaBasePlugin.VERIFICATION_GROUP; dependsOn(forbiddenTasks); } // Add our task as dependency to chain -tasks.getByName(JavaBasePlugin.CHECK_TASK_NAME).dependsOn(forbiddenTask); +project.tasks[JavaBasePlugin.CHECK_TASK_NAME].dependsOn(forbiddenTask); From 9f57dbbf85f120c595178699172309dbdf150347 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Tue, 8 Sep 2015 00:29:31 +0200 Subject: [PATCH 37/40] Add missing patterns to Extension, simplify loading of plugin-init script --- .../gradle/CheckForbiddenApisExtension.java | 33 +++++++++++-------- .../gradle/ForbiddenApisPlugin.java | 28 ++++++++-------- 2 files changed, 32 insertions(+), 29 deletions(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApisExtension.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApisExtension.java index ef7e584c..1ca83d00 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApisExtension.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApisExtension.java @@ -31,15 +31,18 @@ public class CheckForbiddenApisExtension extends PatternSet { /** Fields used for the convention mapping, keep up-to-date with class members! */ static final List PROPS = Arrays.asList( - "signaturesFiles", - "signatures", - "bundledSignatures", - "suppressAnnotations", - "internalRuntimeForbidden", - "failOnUnsupportedJava", - "failOnMissingClasses", - "failOnUnresolvableSignatures", - "ignoreFailures" + "signaturesFiles", + "signatures", + "bundledSignatures", + "suppressAnnotations", + "internalRuntimeForbidden", + "failOnUnsupportedJava", + "failOnMissingClasses", + "failOnUnresolvableSignatures", + "ignoreFailures", + // patterns + "includes", + "excludes" ); public CheckForbiddenApisExtension() { @@ -47,11 +50,13 @@ public CheckForbiddenApisExtension() { } public FileCollection signaturesFiles; - public List signatures, bundledSignatures, suppressAnnotations; + public List signatures, + bundledSignatures, + suppressAnnotations; public boolean internalRuntimeForbidden = false, - failOnUnsupportedJava = false, - failOnMissingClasses = true, - failOnUnresolvableSignatures = true, - ignoreFailures = false; + failOnUnsupportedJava = false, + failOnMissingClasses = true, + failOnUnresolvableSignatures = true, + ignoreFailures = false; } diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java index a6ddc8ec..73da8168 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java @@ -21,12 +21,12 @@ import groovy.util.DelegatingScript; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; +import java.net.URL; import java.util.Collections; import org.codehaus.groovy.control.CompilerConfiguration; import org.codehaus.groovy.control.customizers.ImportCustomizer; +import org.codehaus.groovy.runtime.ResourceGroovyMethods; import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.plugins.PluginInstantiationException; @@ -48,25 +48,23 @@ public class ForbiddenApisPlugin implements Plugin { // Not before Java 6: @Override public void apply(final Project project) { + final String scriptText; try { - final InputStream in = ForbiddenApisPlugin.class.getResourceAsStream(PLUGIN_INIT_SCRIPT); - if (in == null) { + final URL scriptUrl = ForbiddenApisPlugin.class.getResource(PLUGIN_INIT_SCRIPT); + if (scriptUrl == null) { throw new PluginInstantiationException("Cannot find resource with " + PLUGIN_INIT_SCRIPT + " script."); } - try { - final ImportCustomizer importCustomizer = new ImportCustomizer().addStarImports(ForbiddenApisPlugin.class.getPackage().getName()); - final CompilerConfiguration configuration = new CompilerConfiguration().addCompilationCustomizers(importCustomizer); - configuration.setScriptBaseClass(DelegatingScript.class.getName()); - final GroovyShell shell = new GroovyShell(ForbiddenApisPlugin.class.getClassLoader(), new Binding(Collections.singletonMap("project", project)), configuration); - final DelegatingScript script = (DelegatingScript) shell.parse(new InputStreamReader(in, "UTF-8"), PLUGIN_INIT_SCRIPT); - script.setDelegate(this); - script.run(); - } finally { - in.close(); - } + scriptText = ResourceGroovyMethods.getText(scriptUrl, "UTF-8"); } catch (IOException ioe) { throw new PluginInstantiationException("Cannot execute " + PLUGIN_INIT_SCRIPT + " script.", ioe); } + final ImportCustomizer importCustomizer = new ImportCustomizer().addStarImports(ForbiddenApisPlugin.class.getPackage().getName()); + final CompilerConfiguration configuration = new CompilerConfiguration().addCompilationCustomizers(importCustomizer); + configuration.setScriptBaseClass(DelegatingScript.class.getName()); + final GroovyShell shell = new GroovyShell(ForbiddenApisPlugin.class.getClassLoader(), new Binding(Collections.singletonMap("project", project)), configuration); + final DelegatingScript script = (DelegatingScript) shell.parse(scriptText, PLUGIN_INIT_SCRIPT); + script.setDelegate(this); + script.run(); } } From 33745873d17969e52f9b57507fdf2cbc0a0bdf48 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Tue, 8 Sep 2015 00:45:02 +0200 Subject: [PATCH 38/40] Use convention mapping also for classesDir and classpath --- .../forbiddenapis/gradle/plugin-init.groovy | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy b/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy index 35935697..00f29c68 100644 --- a/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy +++ b/src/main/resources/de/thetaphi/forbiddenapis/gradle/plugin-init.groovy @@ -30,17 +30,18 @@ def extension = project.extensions.create(FORBIDDEN_APIS_EXTENSION_NAME, CheckFo def forbiddenTasks = project.sourceSets.collect { sourceSet -> project.tasks.create(sourceSet.getTaskName(FORBIDDEN_APIS_TASK_NAME, null), CheckForbiddenApis.class) { description = "Runs forbidden-apis checks on '${sourceSet.name}' classes."; - CheckForbiddenApisExtension.PROPS.each { key -> - conventionMapping.map(key, { extension[key] }); + conventionMapping.with { + CheckForbiddenApisExtension.PROPS.each { key -> + map(key, { extension[key] }); + } + classesDir = { sourceSet.output.classesDir } + classpath = { sourceSet.compileClasspath } } + // add dependency to compile task after evaluation, if the classesDir is from our SourceSet: project.afterEvaluate { - if (classesDir == null) { - classesDir = sourceSet.output.classesDir; + if (classesDir == sourceSet.output.classesDir) { dependsOn(sourceSet.output); } - if (classpath == null) { - classpath = sourceSet.compileClasspath; - } } } } From 7c98f72141f96932b474bf8773828b8f33988da7 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Tue, 8 Sep 2015 15:23:18 +0200 Subject: [PATCH 39/40] Fix patternset in extension --- .../gradle/CheckForbiddenApis.java | 13 +++- .../gradle/CheckForbiddenApisExtension.java | 76 +++++++++++++++++-- 2 files changed, 80 insertions(+), 9 deletions(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java index 9622d867..5a79d0b4 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java @@ -53,6 +53,7 @@ import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.VerificationTask; import org.gradle.api.tasks.util.PatternFilterable; +import org.gradle.api.tasks.util.PatternSet; import de.thetaphi.forbiddenapis.Checker; import de.thetaphi.forbiddenapis.ForbiddenApiException; @@ -241,7 +242,7 @@ public List getSuppressAnnotations() { public void setSuppressAnnotations(List suppressAnnotations) { data.suppressAnnotations = suppressAnnotations; } - + // PatternFilterable implementation: /** @@ -318,11 +319,19 @@ public CheckForbiddenApis include(@SuppressWarnings("rawtypes") Closure arg0) { return this; } + public PatternSet getPatternSet() { + return data.patternSet; + } + + public void setPatternSet(PatternSet patternSet) { + data.patternSet = patternSet; + } + /** Returns the classes to check. */ @InputFiles @SkipWhenEmpty public FileTree getClassFiles() { - return getProject().files(getClassesDir()).getAsFileTree().matching(data); + return getProject().files(getClassesDir()).getAsFileTree().matching(getPatternSet()); } @TaskAction diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApisExtension.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApisExtension.java index 1ca83d00..3282659d 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApisExtension.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApisExtension.java @@ -16,10 +16,16 @@ * limitations under the License. */ +import groovy.lang.Closure; + import java.util.Arrays; import java.util.List; +import java.util.Set; import org.gradle.api.file.FileCollection; +import org.gradle.api.file.FileTreeElement; +import org.gradle.api.specs.Spec; +import org.gradle.api.tasks.util.PatternFilterable; import org.gradle.api.tasks.util.PatternSet; /** @@ -27,7 +33,7 @@ * For description of the properties refer to the {@link CheckForbiddenApis} * task documentation. */ -public class CheckForbiddenApisExtension extends PatternSet { +public class CheckForbiddenApisExtension implements PatternFilterable { /** Fields used for the convention mapping, keep up-to-date with class members! */ static final List PROPS = Arrays.asList( @@ -40,14 +46,10 @@ public class CheckForbiddenApisExtension extends PatternSet { "failOnMissingClasses", "failOnUnresolvableSignatures", "ignoreFailures", - // patterns - "includes", - "excludes" + "patternSet" ); - public CheckForbiddenApisExtension() { - include("**/*.class"); - } + public PatternSet patternSet = new PatternSet().include("**/*.class"); public FileCollection signaturesFiles; public List signatures, @@ -59,4 +61,64 @@ public CheckForbiddenApisExtension() { failOnUnresolvableSignatures = true, ignoreFailures = false; + // PatternFilterable implementation: + + public Set getIncludes() { + return patternSet.getIncludes(); + } + + public CheckForbiddenApisExtension setIncludes(Iterable includes) { + patternSet.setIncludes(includes); + return this; + } + + public Set getExcludes() { + return patternSet.getExcludes(); + } + + public CheckForbiddenApisExtension setExcludes(Iterable excludes) { + patternSet.setExcludes(excludes); + return this; + } + + public CheckForbiddenApisExtension exclude(String... arg0) { + patternSet.exclude(arg0); + return this; + } + + public CheckForbiddenApisExtension exclude(Iterable arg0) { + patternSet.exclude(arg0); + return this; + } + + public CheckForbiddenApisExtension exclude(Spec arg0) { + patternSet.exclude(arg0); + return this; + } + + public CheckForbiddenApisExtension exclude(@SuppressWarnings("rawtypes") Closure arg0) { + patternSet.exclude(arg0); + return this; + } + + public CheckForbiddenApisExtension include(String... arg0) { + patternSet.include(arg0); + return this; + } + + public CheckForbiddenApisExtension include(Iterable arg0) { + patternSet.include(arg0); + return this; + } + + public CheckForbiddenApisExtension include(Spec arg0) { + patternSet.include(arg0); + return this; + } + + public CheckForbiddenApisExtension include(@SuppressWarnings("rawtypes") Closure arg0) { + patternSet.include(arg0); + return this; + } + } From d32180430a4e71d79ac20a354616976f8e853a57 Mon Sep 17 00:00:00 2001 From: Uwe Schindler Date: Wed, 9 Sep 2015 14:55:01 +0200 Subject: [PATCH 40/40] Fix clone problems caused by PatternFilterable interface in the extension. --- .../gradle/CheckForbiddenApis.java | 41 +++++----- .../gradle/CheckForbiddenApisExtension.java | 81 ++----------------- .../gradle/ForbiddenApisPlugin.java | 2 +- 3 files changed, 29 insertions(+), 95 deletions(-) diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java index 5a79d0b4..84d19498 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApis.java @@ -20,7 +20,6 @@ import static de.thetaphi.forbiddenapis.Checker.Option.FAIL_ON_UNRESOLVABLE_SIGNATURES; import static de.thetaphi.forbiddenapis.Checker.Option.FAIL_ON_VIOLATION; import static de.thetaphi.forbiddenapis.Checker.Option.INTERNAL_RUNTIME_FORBIDDEN; - import groovy.lang.Closure; import java.io.Closeable; @@ -67,9 +66,10 @@ public class CheckForbiddenApis extends DefaultTask implements PatternFilterable,VerificationTask { private final CheckForbiddenApisExtension data = new CheckForbiddenApisExtension(); + private final PatternSet patternSet = new PatternSet().include("**/*.class"); private File classesDir; private FileCollection classpath; - + /** * Directory with the class files to check. */ @@ -126,7 +126,8 @@ public List getSignatures() { /** @see #getSignatures */ public void setSignatures(List signatures) { - data.signatures = signatures; + data.signatures.clear(); + data.signatures.addAll(signatures); } /** @@ -142,7 +143,8 @@ public List getBundledSignatures() { /** @see #getBundledSignatures */ public void setBundledSignatures(List bundledSignatures) { - data.bundledSignatures = bundledSignatures; + data.bundledSignatures.clear(); + data.bundledSignatures.addAll(bundledSignatures); } /** @@ -240,7 +242,8 @@ public List getSuppressAnnotations() { /** @see #getSuppressAnnotations */ public void setSuppressAnnotations(List suppressAnnotations) { - data.suppressAnnotations = suppressAnnotations; + data.suppressAnnotations.clear(); + data.suppressAnnotations.addAll(suppressAnnotations); } // PatternFilterable implementation: @@ -255,11 +258,11 @@ public void setSuppressAnnotations(List suppressAnnotations) { */ @Input public Set getIncludes() { - return data.getIncludes(); + return getPatternSet().getIncludes(); } public CheckForbiddenApis setIncludes(Iterable includes) { - data.setIncludes(includes); + getPatternSet().setIncludes(includes); return this; } @@ -271,60 +274,60 @@ public CheckForbiddenApis setIncludes(Iterable includes) { */ @Input public Set getExcludes() { - return data.getExcludes(); + return getPatternSet().getExcludes(); } public CheckForbiddenApis setExcludes(Iterable excludes) { - data.setExcludes(excludes); + getPatternSet().setExcludes(excludes); return this; } public CheckForbiddenApis exclude(String... arg0) { - data.exclude(arg0); + getPatternSet().exclude(arg0); return this; } public CheckForbiddenApis exclude(Iterable arg0) { - data.exclude(arg0); + getPatternSet().exclude(arg0); return this; } public CheckForbiddenApis exclude(Spec arg0) { - data.exclude(arg0); + getPatternSet().exclude(arg0); return this; } public CheckForbiddenApis exclude(@SuppressWarnings("rawtypes") Closure arg0) { - data.exclude(arg0); + getPatternSet().exclude(arg0); return this; } public CheckForbiddenApis include(String... arg0) { - data.include(arg0); + getPatternSet().include(arg0); return this; } public CheckForbiddenApis include(Iterable arg0) { - data.include(arg0); + getPatternSet().include(arg0); return this; } public CheckForbiddenApis include(Spec arg0) { - data.include(arg0); + getPatternSet().include(arg0); return this; } public CheckForbiddenApis include(@SuppressWarnings("rawtypes") Closure arg0) { - data.include(arg0); + getPatternSet().include(arg0); return this; } public PatternSet getPatternSet() { - return data.patternSet; + return patternSet; } public void setPatternSet(PatternSet patternSet) { - data.patternSet = patternSet; + patternSet.copyFrom(patternSet); } /** Returns the classes to check. */ diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApisExtension.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApisExtension.java index 3282659d..36b29244 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApisExtension.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/CheckForbiddenApisExtension.java @@ -16,24 +16,18 @@ * limitations under the License. */ -import groovy.lang.Closure; - +import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Set; import org.gradle.api.file.FileCollection; -import org.gradle.api.file.FileTreeElement; -import org.gradle.api.specs.Spec; -import org.gradle.api.tasks.util.PatternFilterable; -import org.gradle.api.tasks.util.PatternSet; /** * Extension for the ForbiddenApis Gradle Task to store defaults. * For description of the properties refer to the {@link CheckForbiddenApis} * task documentation. */ -public class CheckForbiddenApisExtension implements PatternFilterable { +public class CheckForbiddenApisExtension { /** Fields used for the convention mapping, keep up-to-date with class members! */ static final List PROPS = Arrays.asList( @@ -45,80 +39,17 @@ public class CheckForbiddenApisExtension implements PatternFilterable { "failOnUnsupportedJava", "failOnMissingClasses", "failOnUnresolvableSignatures", - "ignoreFailures", - "patternSet" + "ignoreFailures" ); - public PatternSet patternSet = new PatternSet().include("**/*.class"); - public FileCollection signaturesFiles; - public List signatures, - bundledSignatures, - suppressAnnotations; + public List signatures = new ArrayList(), + bundledSignatures = new ArrayList(), + suppressAnnotations = new ArrayList(); public boolean internalRuntimeForbidden = false, failOnUnsupportedJava = false, failOnMissingClasses = true, failOnUnresolvableSignatures = true, ignoreFailures = false; - // PatternFilterable implementation: - - public Set getIncludes() { - return patternSet.getIncludes(); - } - - public CheckForbiddenApisExtension setIncludes(Iterable includes) { - patternSet.setIncludes(includes); - return this; - } - - public Set getExcludes() { - return patternSet.getExcludes(); - } - - public CheckForbiddenApisExtension setExcludes(Iterable excludes) { - patternSet.setExcludes(excludes); - return this; - } - - public CheckForbiddenApisExtension exclude(String... arg0) { - patternSet.exclude(arg0); - return this; - } - - public CheckForbiddenApisExtension exclude(Iterable arg0) { - patternSet.exclude(arg0); - return this; - } - - public CheckForbiddenApisExtension exclude(Spec arg0) { - patternSet.exclude(arg0); - return this; - } - - public CheckForbiddenApisExtension exclude(@SuppressWarnings("rawtypes") Closure arg0) { - patternSet.exclude(arg0); - return this; - } - - public CheckForbiddenApisExtension include(String... arg0) { - patternSet.include(arg0); - return this; - } - - public CheckForbiddenApisExtension include(Iterable arg0) { - patternSet.include(arg0); - return this; - } - - public CheckForbiddenApisExtension include(Spec arg0) { - patternSet.include(arg0); - return this; - } - - public CheckForbiddenApisExtension include(@SuppressWarnings("rawtypes") Closure arg0) { - patternSet.include(arg0); - return this; - } - } diff --git a/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java b/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java index 73da8168..b5dc856c 100644 --- a/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java +++ b/src/main/java/de/thetaphi/forbiddenapis/gradle/ForbiddenApisPlugin.java @@ -56,7 +56,7 @@ public void apply(final Project project) { } scriptText = ResourceGroovyMethods.getText(scriptUrl, "UTF-8"); } catch (IOException ioe) { - throw new PluginInstantiationException("Cannot execute " + PLUGIN_INIT_SCRIPT + " script.", ioe); + throw new PluginInstantiationException("Cannot load " + PLUGIN_INIT_SCRIPT + " script.", ioe); } final ImportCustomizer importCustomizer = new ImportCustomizer().addStarImports(ForbiddenApisPlugin.class.getPackage().getName()); final CompilerConfiguration configuration = new CompilerConfiguration().addCompilationCustomizers(importCustomizer);