Skip to content
Commits on Source (84)
......@@ -24,90 +24,69 @@ archivesBaseName = 'gradle'
buildTypes {
sanityCheck {
tasks "classes", "doc:checkstyleApi", "codeQuality", "docs:check"
}
// The minimum to be run before check-in
preCommitBuild {
tasks "doc:checkstyleApi", "docs:check", "codeQuality", "test"
}
quickCheck {
tasks "doc:checkstyleApi", "docs:check", "codeQuality", "test"
}
// For testing pull requests
pullRequestValidation {
tasks "doc:checkstyleApi", "docs:check", "codeQuality", "core:test", "dependencyManagement:test"
}
// A full (in-process) test
developerBuild {
tasks "check"
tasks "classes", "doc:checkstyleApi", "codeQuality", "docs:check", "distribution:checkBinaryCompatibility", "javadocAll"
projectProperties ignoreIncomingBuildReceipt: true
}
// Used by the first phase of the build pipeline, running only last version on multiversion - tests
quickTest {
tasks "runtimeTests", "runtimeIntegTests"
tasks "test", "integTest", "crossVersionTest"
}
// Used for builds to run all tests, but not necessarily on all platforms
fullTest {
tasks "runtimeTests", "runtimeForkingIntegTests"
projectProperties useIncomingDistributions: true, testAllVersions: true
tasks "test", "forkingIntegTest", "forkingCrossVersionTest"
projectProperties testAllVersions: true
}
// Used for builds to test the code on certain platforms
platformTest {
tasks "runtimeTests", "runtimeForkingIntegTests"
projectProperties useIncomingDistributions: true, testAllVersions: true, testAllPlatforms: true
tasks "test", "forkingIntegTest", "forkingCrossVersionTest"
projectProperties testAllVersions: true, testAllPlatforms: true
}
// Tests using the daemon mode
daemonTest {
tasks "runtimeDaemonIntegTests"
projectProperties useIncomingDistributions: true
// Tests not using the daemon mode
noDaemonTest {
tasks "noDaemonIntegTest"
projectProperties useAllDistribution: true
}
// Run the integration tests using the parallel executer
parallelTest {
tasks "runtimeParallelIntegTests"
projectProperties useIncomingDistributions: true
tasks "parallelIntegTest"
}
performanceTests {
tasks "performance:performanceTest"
projectProperties useIncomingDistributions: true
}
performanceExperiments {
tasks "performance:performanceExperiments"
projectProperties useIncomingDistributions: true
}
fullPerformanceTests {
tasks "performance:fullPerformanceTest"
projectProperties useIncomingDistributions: true
}
distributedPerformanceTests {
tasks "performance:distributedPerformanceTest"
projectProperties useIncomingDistributions: true
}
distributedPerformanceExperiments {
tasks "performance:distributedPerformanceExperiment"
projectProperties useIncomingDistributions: true
}
distributedFullPerformanceTests {
tasks "performance:distributedFullPerformanceTest"
projectProperties useIncomingDistributions: true
}
// Used for cross version tests on CI
crossVersionTest {
tasks "crossVersionIntegTest"
projectProperties useIncomingDistributions: true
allVersionsCrossVersionTest {
tasks "allVersionsCrossVersionTests"
}
quickFeedbackCrossVersionTest {
tasks "quickFeedbackCrossVersionTests"
}
// Used to build production distros and smoke test them
......@@ -115,11 +94,6 @@ buildTypes {
tasks "verifyIsProductionBuildEnvironment", "clean", "buildDists", "distributions:integTest"
}
// Used to build (experimental) production distros and smoke test them
packageBuildExperimental {
tasks "verifyIsProductionBuildEnvironment", "buildDists", "distributions:integTest"
}
// Used to build production distros and smoke test them
promotionBuild {
tasks "verifyIsProductionBuildEnvironment", "clean", "docs:check", "buildDists", "distributions:integTest", "uploadArchives"
......@@ -128,7 +102,10 @@ buildTypes {
//Initial smoke test for java 9
java9Build {
tasks "java9Test", "java9IntegTest"
projectProperties useIncomingDistributions: true
}
java9SmokeTest {
tasks "java9IntegTest"
}
soakTest {
......@@ -137,25 +114,22 @@ buildTypes {
}
}
apply from: "gradle/testGroupings.gradle"
apply from: 'gradle/buildSplits.gradle'
apply from: 'gradle/buildScanUserData.gradle'
ext {
jvm = org.gradle.internal.jvm.Jvm.current()
javaVersion = JavaVersion.current()
isCiServer = System.getenv().containsKey("TEAMCITY_VERSION")
isCiServer = System.getenv().containsKey("CI")
isWindows = org.gradle.internal.os.OperatingSystem.current().windows
if (project.hasProperty("maxParallelForks")) {
project.maxParallelForks = Integer.valueOf(project.maxParallelForks, 10)
} else {
ext.maxParallelForks = 1
ext.maxParallelForks = 4
}
if (project.hasProperty("useIncomingDistributions")) {
project.useIncomingDistributions = true
if (project.hasProperty("useAllDistribution")) {
project.useAllDistribution = true
} else {
ext.useIncomingDistributions = false
ext.useAllDistribution = false
}
agentNum = 1
......@@ -170,19 +144,21 @@ ext {
groovyProjects = subprojects
publicGroovyProjects = groovyProjects - internalProjects
pluginProjects = [
'plugins', 'codeQuality', 'jetty', 'antlr', 'wrapper', 'osgi', 'maven',
'plugins', 'codeQuality', 'antlr', 'wrapper', 'osgi', 'maven',
'ide', 'announce', 'scala', 'signing', 'ear', 'javascript', 'buildComparison',
'diagnostics', 'reporting', 'publish', 'ivy', 'jacoco', 'buildInit', 'platformBase',
'platformJvm', 'languageJvm', 'languageJava', 'languageGroovy', 'languageScala',
'platformNative', 'platformPlay', 'idePlay', 'languageNative', 'ideNative', 'testingBase',
'testingNative', 'testingJvm', 'pluginDevelopment', 'pluginUse', 'resourcesHttp',
'resourcesSftp', 'resourcesS3', 'toolingApiBuilders', 'compositeBuilds', 'buildCacheHttp'
].collect {
project(it)
}
'resourcesSftp', 'resourcesS3', 'resourcesGcs', 'compositeBuilds', 'buildCacheHttp'
].collect { project(it) }
implementationPluginProjects = [
project('toolingApiBuilders')
]
publishedProjects = [
project(':logging'),
project(':core'),
project(':modelCore'),
project(':toolingApi'),
project(':wrapper'),
project(':baseServices'),
......@@ -193,10 +169,10 @@ ext {
project(':processServices'),
project(':resources')
]
removedProjects = ['sonar']
}
apply from: "gradle/incomingDistributions.gradle"
apply from: "gradle/buildScanUserData.gradle"
apply from: "gradle/taskCacheBuildScanUserData.gradle"
apply from: "gradle/versioning.gradle"
apply from: "gradle/dependencies.gradle"
apply from: "gradle/wrapper.gradle"
......@@ -206,9 +182,10 @@ apply from: "gradle/classycle.gradle"
apply from: "gradle/strictCompile.gradle"
apply from: "gradle/noDependencyResolutionDuringConfiguration.gradle"
apply from: "gradle/testSetup.gradle"
apply from: "gradle/taskOrdering.gradle"
apply from: "gradle/fix-GRADLE-2492.gradle"
apply from: 'gradle/customM2Check.gradle'
apply from: "gradle/publicApi.gradle"
apply from: "gradle/updateReleasedVersions.gradle"
allprojects {
group = 'org.gradle'
......@@ -216,16 +193,13 @@ allprojects {
repositories {
maven { url 'https://repo.gradle.org/gradle/libs-releases' }
maven { url 'https://repo.gradle.org/gradle/libs-milestones' }
maven { url "https://repo.gradle.org/gradle/libs-snapshots" }
}
// Force clean tasks to run before any other tasks
tasks.withType(Delete) { cleanTask ->
tasks.all { otherTask ->
if (cleanTask != otherTask &&
!(otherTask instanceof Delete) &&
!(otherTask.name in [ 'killExistingDaemons', 'tasks' ])) {
otherTask.mustRunAfter cleanTask
}
// patchExternalModules lives in the root project - we need to activate normalization there, too.
normalization {
runtimeClasspath {
ignore 'org/gradle/build-receipt.properties'
}
}
}
......@@ -246,16 +220,15 @@ subprojects {
}
apply from: "$rootDir/gradle/codeQuality.gradle"
if (isCiServer) {
reporting.baseDir "$rootProject.reporting.baseDir/${path.replaceFirst(':', '').replaceAll(':', '.')}"
}
}
configurations {
coreRuntime {
visible = false
}
coreRuntimeExtensions {
visible = false
}
externalModules {
visible = false
}
......@@ -284,27 +257,32 @@ def patchedExternalModules = files({ -> fileTree(patchedExternalModulesDir).file
patchedExternalModules.builtBy 'patchExternalModules'
dependencies {
externalModules 'org.gradle:gradle-script-kotlin:0.5.1'
externalModules "org.gradle:gradle-kotlin-dsl:${versions.gradle_kotlin_dsl}"
externalModules "org.gradle:gradle-kotlin-dsl-tooling-builders:${versions.gradle_kotlin_dsl}"
coreRuntime project(':launcher')
coreRuntime project(':runtimeApiInfo')
runtime project(':wrapper')
runtime project(":installationBeacon")
runtime patchedExternalModules
gradlePlugins pluginProjects
gradlePlugins implementationPluginProjects
gradlePlugins project(':workers')
gradlePlugins project(':dependencyManagement')
gradlePlugins project(':testKit')
coreRuntimeExtensions project(':dependencyManagement') //See: DynamicModulesClassPathProvider.GRADLE_EXTENSION_MODULES
coreRuntimeExtensions project(':pluginUse')
coreRuntimeExtensions project(':workers')
coreRuntimeExtensions patchedExternalModules
}
import org.gradle.modules.PatchExternalModules
task patchExternalModules(type: PatchExternalModules) {
// Include any dependency jars that are not available in the core runtime
externalModulesRuntime = configurations.externalModulesRuntime
coreRuntime = configurations.coreRuntime
externalModules = configurations.externalModules
allModules = configurations.externalModulesRuntime
coreModules = configurations.coreRuntime
modulesToPatch = configurations.externalModules
destination = patchedExternalModulesDir
dependsOn configurations.externalModules
}
task verifyIsProductionBuildEnvironment {
......@@ -315,26 +293,12 @@ task verifyIsProductionBuildEnvironment {
}
}
task waitForDaemonsToDie {
if (!project.hasProperty("noWaitForDaemonsToDie")) {
if (isWindows && isCiServer && buildTypes.crossVersionTest.active) {
gradle.startParameter.taskNames.add(0, it.path)
}
doLast {
def mins = 2
println "I'm waiting for $mins mins so that existing daemons can die with honour. It's a workaround until we fix it properly."
sleep mins * 60 * 1000
}
}
}
evaluationDependsOn ":distributions"
task install(type: Install) {
description = 'Installs the minimal distribution into directory $gradle_installPath'
group = 'build'
with project(":distributions").binDistImage
with project(":distributions").versionInfo
installDirPropertyName = 'gradle_installPath'
}
......@@ -345,13 +309,6 @@ task installAll(type: Install) {
installDirPropertyName = 'gradle_installPath'
}
clean {
dependsOn ':killExistingDaemons'
}
apply from: "gradle/intTestImage.gradle"
apply from: 'gradle/pullRequestBuild.gradle'
// Generate a report showing which tests in a subproject are leaving
// files around.
//
......@@ -368,35 +325,11 @@ subprojects { prj ->
}
}
task cleanUpRemovedProjects(type: Delete) {
delete removedProjects.collect { "subprojects/$it" }
}
clean.dependsOn(cleanUpRemovedProjects)
/*
* When run from a Continuous Integration environment HTML test reports will usually only be inspected
* if the build failed e.g. due to failed or flaky tests.
*
* Deletes HTML test reports so they are not archived on TeamCity and use up unnecessary disk space.
*/
if (isCiServer) {
gradle.buildFinished { buildResult ->
if (!buildResult.failure) {
logger.quiet "Deleting HTML test reports..."
subprojects {
def htmlTestReportDirs = tasks.withType(Test).collect { it.reports.html.destination }.unique()
htmlTestReportDirs.each { htmlReportDir ->
logger.info "Deleting test reports directory '$htmlReportDir'."
def success = htmlReportDir.deleteDir()
if (!success) {
logger.warn "Unable to delete test report directory '$htmlReportDir'."
}
}
}
}
gradle.buildFinished { result ->
def rootProject = result.gradle.rootProject
if (rootProject.remoteBuildCacheEnabled && !rootProject.java7HomeSet) {
logger.warn("In order to have cache hits from the remote build cache you need to set 'java7Home' in your gradle.properties!")
}
}
apply from: "gradle/ciReporting.gradle"
......@@ -21,8 +21,14 @@ apply plugin: 'eclipse'
sourceCompatibility = '1.7'
targetCompatibility = '1.7'
// 1.5.3 has a classloader leak (https://github.com/asciidoctor/asciidoctor-gradle-plugin/pull/215)
// 1.5.6 requires Java 8+ (https://github.com/asciidoctor/asciidoctor-gradle-plugin/issues/218)
def asciidoctorPluginVersion = JavaVersion.current() == JavaVersion.VERSION_1_7 ? '1.5.3' : '1.5.6'
repositories {
maven { url 'https://repo.gradle.org/gradle/libs-releases' }
maven { url "https://repo.gradle.org/gradle/libs-snapshots" }
maven { url "https://plugins.gradle.org/m2/" }
}
dependencies {
......@@ -36,20 +42,24 @@ dependencies {
exclude(module: 'xercesImpl')
}
testCompile 'junit:junit:4.12@jar'
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4@jar', 'cglib:cglib-nodep:2.2', 'org.objenesis:objenesis:1.2'
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4@jar', 'cglib:cglib-nodep:3.2.5', 'org.objenesis:objenesis:1.2'
testCompile 'org.hamcrest:hamcrest-core:1.3'
compile "org.pegdown:pegdown:1.6.0"
compile "org.jsoup:jsoup:1.6.3"
compile 'me.champeau.gradle:japicmp-gradle-plugin:0.2.4'
compile "org.asciidoctor:asciidoctor-gradle-plugin:$asciidoctorPluginVersion"
compile 'com.github.javaparser:javaparser-core:2.4.0'
}
ext.isCiServer = System.getenv().containsKey("CI")
apply from: '../gradle/compile.gradle'
apply from: '../gradle/taskOrdering.gradle'
apply from: '../gradle/dependencies.gradle'
apply from: '../gradle/classycle.gradle'
ext.isCiServer = System.getenv().containsKey("TEAMCITY_VERSION")
if (!isCiServer || (isCiServer && Boolean.getBoolean('enableCodeQuality'))) {
apply from: '../gradle/codeQuality.gradle'
}
apply from: "../gradle/ciReporting.gradle"
......@@ -14,7 +14,4 @@
* limitations under the License.
*/
if (gradle.startParameter.systemPropertiesArgs.containsKey("gradle.cache.remote.url")) {
println "Applying remoteHttpCacheInit to buildSrc"
apply from: new File(settingsDir, '../gradle/remoteHttpCacheInit.gradle')
}
apply from: new File(settingsDir, '../gradle/remoteHttpCacheSettings.gradle')
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.binarycompatibility
import groovy.json.JsonOutput
import groovy.json.JsonSlurper
import org.gradle.util.GradleVersion
class AcceptedApiChanges {
GradleVersion baseVersion
Map<ApiChange, String> acceptedChanges
static AcceptedApiChanges parse(String jsonText) {
def json = new JsonSlurper().parseText(jsonText)
def acceptedApiChanges = new AcceptedApiChanges()
acceptedApiChanges.acceptedChanges = json.acceptedApiChanges.collectEntries { jsonChange ->
[(ApiChange.parse(jsonChange)): jsonChange.acceptation]
}
return acceptedApiChanges
}
Map<String, String> toAcceptedChangesMap() {
acceptedChanges.collectEntries { change ->
[(JsonOutput.toJson(change.key)): change.value]
}
}
static Map<ApiChange, String> fromAcceptedChangesMap(Map<String, String> acceptedChanges) {
acceptedChanges.collectEntries { key, value ->
[(ApiChange.parse(new JsonSlurper().parseText(key))): value]
}
}
}
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.binarycompatibility
import groovy.transform.Immutable
@Immutable
class ApiChange {
String type
String member
List<String> changes
static ApiChange parse(Object json) {
new ApiChange(json.type, json.member, json.changes ?: [])
}
String toString() {
def formattedMember = member - "$type."
"$type - $formattedMember - $changes"
}
}
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.binarycompatibility.rules
import com.github.javaparser.JavaParser
import com.github.javaparser.ast.body.MethodDeclaration
import com.github.javaparser.ast.visitor.GenericVisitorAdapter
import groovy.json.JsonOutput
import groovy.transform.CompileStatic
import japicmp.model.JApiClass
import japicmp.model.JApiCompatibility
import japicmp.model.JApiConstructor
import japicmp.model.JApiField
import japicmp.model.JApiHasAnnotations
import japicmp.model.JApiMethod
import me.champeau.gradle.japicmp.report.AbstractContextAwareViolationRule
import me.champeau.gradle.japicmp.report.Violation
import org.gradle.api.Incubating
import org.gradle.binarycompatibility.AcceptedApiChanges
import org.gradle.binarycompatibility.ApiChange
import javax.inject.Inject
@CompileStatic
abstract class AbstractGradleViolationRule extends AbstractContextAwareViolationRule {
private final Map<ApiChange, String> acceptedApiChanges
AbstractGradleViolationRule(Map<String, String> acceptedApiChanges) {
this.acceptedApiChanges = AcceptedApiChanges.fromAcceptedChangesMap(acceptedApiChanges)
}
private static boolean isAnnotatedWithIncubating(JApiHasAnnotations member) {
member.annotations*.fullyQualifiedName.any { it == Incubating.name }
}
private static boolean isAnnotatedWithDeprecated(JApiHasAnnotations member) {
member.annotations*.fullyQualifiedName.any { it == Deprecated.name }
}
private static boolean isAnnotatedWithInject(JApiHasAnnotations member) {
member.annotations*.fullyQualifiedName.any { it == Inject.name }
}
boolean isInject(JApiHasAnnotations member) {
return isAnnotatedWithInject(member)
}
boolean isIncubatingOrDeprecated(JApiHasAnnotations member) {
if (member instanceof JApiClass) {
return isIncubatingOrDeprecated((JApiClass) member)
} else if (member instanceof JApiMethod) {
return isIncubatingOrDeprecatedOrOverride((JApiMethod) member)
} else if (member instanceof JApiField) {
return isIncubatingOrDeprecated((JApiField) member)
} else if (member instanceof JApiConstructor) {
return isIncubatingOrDeprecated((JApiConstructor) member)
}
return isAnnotatedWithIncubating(member)
}
boolean isIncubatingOrDeprecated(JApiClass clazz) {
return isAnnotatedWithIncubating(clazz) || isAnnotatedWithDeprecated(clazz)
}
boolean isIncubatingOrDeprecated(JApiField field) {
return isAnnotatedWithIncubating(field) || isAnnotatedWithIncubating(field.jApiClass) || isAnnotatedWithDeprecated(field) || isAnnotatedWithDeprecated(field.jApiClass)
}
boolean isIncubatingOrDeprecated(JApiConstructor constructor) {
return isAnnotatedWithIncubating(constructor) || isAnnotatedWithIncubating(constructor.jApiClass) || isAnnotatedWithDeprecated(constructor) || isAnnotatedWithDeprecated(constructor.jApiClass)
}
boolean isIncubatingOrDeprecatedOrOverride(JApiMethod method) {
return isAnnotatedWithIncubating(method) || isAnnotatedWithIncubating(method.jApiClass) || isOverride(method) || isAnnotatedWithDeprecated(method) || isAnnotatedWithDeprecated(method.jApiClass)
}
boolean isDeprecated(JApiClass clazz) {
return isAnnotatedWithDeprecated(clazz)
}
boolean isDeprecated(JApiConstructor constructor) {
return isAnnotatedWithDeprecated(constructor) || isAnnotatedWithDeprecated(constructor.jApiClass)
}
boolean isDeprecated(JApiField field) {
return isAnnotatedWithDeprecated(field) || isAnnotatedWithDeprecated(field.jApiClass)
}
boolean isDeprecated(JApiMethod method) {
return isAnnotatedWithDeprecated(method) || isAnnotatedWithDeprecated(method.jApiClass)
}
boolean isOverride(JApiMethod method) {
// @Override has source retention - so we need to peek into the sources
def visitor = new GenericVisitorAdapter<Object, Void>() {
@Override
Object visit(MethodDeclaration declaration, Void arg) {
if (declaration.name == method.name && declaration.annotations.any { it.name.name == Override.simpleName } ) {
return new Object()
}
return null
}
}
return JavaParser.parse(sourceFileFor(method.jApiClass.fullyQualifiedName)).accept(visitor, null) != null
}
File sourceFileFor(String className) {
List<String> sourceFolders = context.userData.get("apiSourceFolders") as List<String>
for (String sourceFolder : sourceFolders) {
def sourceFilePath = className.replace('.', '/').replaceAll('\\$.*', '')
def sourceFile = new File("$sourceFolder/${sourceFilePath}.java")
if (sourceFile.exists()) {
return sourceFile
}
}
throw new RuntimeException("No source file found for: $className")
}
Violation acceptOrReject(JApiCompatibility member, Violation rejection) {
List<String> changes = member.compatibilityChanges.collect { Violation.describe(it) }
return acceptOrReject(member, changes, rejection)
}
Violation acceptOrReject(JApiCompatibility member, List<String> changes, Violation rejection) {
Set<ApiChange> seenApiChanges = (Set<ApiChange>) context.userData["seenApiChanges"]
def change = new ApiChange(
context.className,
Violation.describe(member),
changes
)
String acceptationReason = acceptedApiChanges[change]
if (acceptationReason != null) {
seenApiChanges.add(change)
return Violation.accept(member, "${rejection.getHumanExplanation()}. Reason for accepting this: <b>$acceptationReason</b>")
}
def acceptanceJson = new LinkedHashMap<String, Object>([
type: change.type,
member: change.member,
acceptation: '&lt;ADD YOUR CUSTOM REASON HERE&gt;'
])
if (change.changes) {
acceptanceJson.changes = change.changes
}
def id = "accept" + (change.type + change.member).replaceAll('[^a-zA-Z0-9]', '_')
Violation violation = Violation.error(
member,
rejection.getHumanExplanation() + """. If you did this intentionally, please accept the change and provide an explanation:
<a class="btn btn-info" role="button" data-toggle="collapse" href="#${id}" aria-expanded="false" aria-controls="collapseExample">Accept this change</a>
<div class="collapse" id="${id}">
<div class="well">
In order to accept this change add the following to <code>subprojects/distributions/src/changes/accepted-public-api-changes.json</code>:
<pre>${JsonOutput.prettyPrint(JsonOutput.toJson(acceptanceJson))}</pre>
</div>
</div>""".stripIndent()
)
return violation
}
String getCurrentVersion() {
return context.getUserData().get("currentVersion")
}
}
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.binarycompatibility.rules
import me.champeau.gradle.japicmp.report.Violation
import japicmp.model.JApiClass
import japicmp.model.JApiCompatibility
import javassist.CtClass
import com.google.common.base.Optional
import java.util.regex.Pattern
abstract class AbstractSuperClassChangesRule extends AbstractGradleViolationRule {
private final List<Pattern> publicApiPatterns
AbstractSuperClassChangesRule(Map params) {
super(params['acceptedApiChanges'])
this.publicApiPatterns = params['publicApiPatterns'].collect { Pattern.compile(it) }
}
Violation maybeViolation(final JApiCompatibility member) {
if (!(member instanceof JApiClass)) {
return null
}
if (!changed(member)) {
return null
}
Optional<CtClass> oldClass = member.oldClass
Optional<CtClass> newClass = member.newClass
if (!oldClass.isPresent() || !newClass.isPresent()) {
// breaking change would be reported
return null
}
return checkSuperClassChanges(member, oldClass.get(), newClass.get())
}
protected abstract boolean changed(JApiCompatibility member)
protected abstract Violation checkSuperClassChanges(JApiClass apiClass, CtClass oldClass, CtClass newClass)
protected boolean isInternal(CtClass c) {
if (c.name.contains('.internal.')) {
return true
} else {
return !publicApiPatterns.any { it.matcher(c.name).find() }
}
}
}
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.binarycompatibility.rules;
import me.champeau.gradle.japicmp.report.PostProcessViolationsRule;
import me.champeau.gradle.japicmp.report.ViolationCheckContextWithViolations;
import org.gradle.binarycompatibility.ApiChange;
import org.gradle.util.CollectionUtils;
import java.util.HashSet;
import java.util.Set;
public class AcceptedRegressionsRulePostProcess implements PostProcessViolationsRule {
@SuppressWarnings("unchecked")
public void execute(ViolationCheckContextWithViolations context) {
Set<ApiChange> acceptedApiChanges = (Set<ApiChange>) context.getUserData().get("acceptedApiChanges");
Set<ApiChange> seenApiChanges = (Set<ApiChange>) context.getUserData().get("seenApiChanges");
Set<ApiChange> left = new HashSet<>(acceptedApiChanges);
left.removeAll(seenApiChanges);
if (!left.isEmpty()) {
String formattedLeft = CollectionUtils.join("\n", left);
throw new RuntimeException("The following regressions are declared as accepted, but didn't match any rule:\n\n" + formattedLeft);
}
}
}
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.binarycompatibility.rules;
import me.champeau.gradle.japicmp.report.SetupRule;
import me.champeau.gradle.japicmp.report.ViolationCheckContext;
import org.gradle.binarycompatibility.AcceptedApiChanges;
import org.gradle.binarycompatibility.ApiChange;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class AcceptedRegressionsRuleSetup implements SetupRule {
private final Set<ApiChange> acceptedApiChanges;
public AcceptedRegressionsRuleSetup(Map<String, String> acceptedApiChanges) {
this.acceptedApiChanges = AcceptedApiChanges.fromAcceptedChangesMap(acceptedApiChanges).keySet();
}
@SuppressWarnings("unchecked")
public void execute(ViolationCheckContext context) {
Map<String, Object> userData = (Map<String, Object>) context.getUserData();
userData.put("acceptedApiChanges", acceptedApiChanges);
userData.put("seenApiChanges", new HashSet<ApiChange>());
}
}
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.binarycompatibility.rules;
import com.google.common.collect.ImmutableList;
import japicmp.model.JApiClass;
import japicmp.model.JApiCompatibility;
import japicmp.model.JApiCompatibilityChange;
import japicmp.model.JApiConstructor;
import japicmp.model.JApiHasAnnotations;
import japicmp.model.JApiImplementedInterface;
import me.champeau.gradle.japicmp.report.Violation;
import java.util.List;
import java.util.Map;
public class BinaryBreakingChangesRule extends AbstractGradleViolationRule {
private static final List<JApiCompatibilityChange> IGNORED_CHANGE_TYPES = ImmutableList.of(
JApiCompatibilityChange.METHOD_REMOVED_IN_SUPERCLASS, // the removal of the method will be reported
JApiCompatibilityChange.INTERFACE_REMOVED, // the removed methods will be reported
JApiCompatibilityChange.INTERFACE_ADDED // the added methods will be reported
);
public BinaryBreakingChangesRule(Map<String, String> acceptedApiChanges) {
super(acceptedApiChanges);
}
@Override
@SuppressWarnings("unchecked")
public Violation maybeViolation(final JApiCompatibility member) {
if (!member.isBinaryCompatible()) {
if ((member instanceof JApiClass) && (member.getCompatibilityChanges().isEmpty())) {
// A member of the class breaks binary compatibility.
// That will be handled when the member is passed to `maybeViolation`.
return null;
}
if (member instanceof JApiImplementedInterface) {
// The changes about the interface's methods will be reported already
return null;
}
if (member instanceof JApiConstructor) {
if (isInject((JApiConstructor) member)) {
// We do not consider injecting constructors public API
return null;
}
}
for (JApiCompatibilityChange change : member.getCompatibilityChanges()) {
if (IGNORED_CHANGE_TYPES.contains(change)) {
return null;
}
}
if (member instanceof JApiHasAnnotations) {
if (isIncubatingOrDeprecated((JApiHasAnnotations) member)) {
return Violation.warning(member, "Changed public API (@Incubating)");
}
}
return acceptOrReject(member, Violation.notBinaryCompatible(member));
}
return null;
}
}
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License")
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.binarycompatibility.rules
import me.champeau.gradle.japicmp.report.Violation
import japicmp.model.JApiClass
import japicmp.model.JApiChangeStatus
import japicmp.model.JApiCompatibility
import javassist.CtClass
class IncubatingInternalInterfaceAddedRule extends AbstractSuperClassChangesRule {
IncubatingInternalInterfaceAddedRule(Map params) {
super(params)
}
protected boolean changed(JApiCompatibility member) {
return member.getChangeStatus() == JApiChangeStatus.MODIFIED
}
protected Violation checkSuperClassChanges(JApiClass c, CtClass oldClass, CtClass newClass) {
Map<String, CtClass> oldInterfaces = collectImplementedInterfaces(oldClass)
Map<String, CtClass> newInterfaces = collectImplementedInterfaces(newClass)
newInterfaces.keySet().removeAll(oldInterfaces.keySet())
if (newInterfaces.isEmpty()) {
return null
}
List<String> changes = filterChangesToReport(newClass, newInterfaces)
if (changes.isEmpty()) {
return null
}
return acceptOrReject(c, changes, Violation.error(c, " introduces internal or incubating interfaces"))
}
private Map<String, CtClass> collectImplementedInterfaces(CtClass c) {
Map<String, CtClass> result = [:]
collect(result, c)
return result
}
private void collect(Map<String, CtClass> result, CtClass c) {
c.interfaces.each { result.put(it.name, it) }
if (c.superclass != null) {
collect(result, c.superclass)
}
}
private List<String> filterChangesToReport(CtClass c, Map<String, CtClass> interfaces) {
return interfaces.values().findAll { implementedDirectly(it, c) && addedInterfaceIsIncubatingOrInternal(it, c) }*.name.sort()
}
private boolean implementedDirectly(CtClass interf, CtClass c) {
return c.interfaces.any { it.name == interf.name }
}
private boolean addedInterfaceIsIncubatingOrInternal(CtClass interf, CtClass c) {
return (isIncubating(interf) && !isIncubating(c)) || isInternal(interf)
}
private boolean isIncubating(CtClass c) {
return c.annotations.any { it.annotationType().name == 'org.gradle.api.Incubating' }
}
}
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.binarycompatibility.rules;
import japicmp.model.JApiClass;
import japicmp.model.JApiCompatibility;
import japicmp.model.JApiConstructor;
import japicmp.model.JApiField;
import japicmp.model.JApiHasAnnotations;
import japicmp.model.JApiMethod;
import me.champeau.gradle.japicmp.report.Violation;
import java.util.Map;
public class IncubatingMissingRule extends AbstractGradleViolationRule {
public IncubatingMissingRule(Map<String, String> acceptedViolations) {
super(acceptedViolations);
}
@Override
public Violation maybeViolation(final JApiCompatibility member) {
if (member instanceof JApiMethod || member instanceof JApiField || member instanceof JApiClass || member instanceof JApiConstructor) {
if (!isIncubatingOrDeprecated((JApiHasAnnotations) member)) {
return violationError(member);
}
}
return null;
}
private Violation violationError(JApiCompatibility member) {
return acceptOrReject(member, Violation.error(member, "Is not annotated with @Incubating"));
}
}
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License")
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.binarycompatibility.rules
import me.champeau.gradle.japicmp.report.Violation
import japicmp.model.JApiClass
import japicmp.model.JApiCompatibility
import japicmp.model.JApiCompatibilityChange
import javassist.CtClass
import javassist.CtMethod
import javassist.Modifier
class MethodsRemovedInInternalSuperClassRule extends AbstractSuperClassChangesRule {
MethodsRemovedInInternalSuperClassRule(Map params) {
super(params)
}
protected boolean changed(JApiCompatibility member) {
return member.compatibilityChanges.contains(JApiCompatibilityChange.METHOD_REMOVED_IN_SUPERCLASS)
}
protected Violation checkSuperClassChanges(JApiClass c, CtClass oldClass, CtClass newClass) {
Set<CtMethod> oldMethods = collectAllPublicApiMethods(oldClass)
Set<CtMethod> newMethods = collectAllPublicApiMethods(newClass)
oldMethods.removeAll(newMethods)
if (oldMethods.isEmpty()) {
return null
}
List<String> changes = filterChangesToReport(oldClass, oldMethods)
if (changes.isEmpty()) {
return null
}
return acceptOrReject(c, changes, Violation.error(c, " methods removed in internal super class"))
}
private Set<CtMethod> collectAllPublicApiMethods(CtClass c) {
Set<CtMethod> result = [] as Set
collect(result, c.superclass)
return result
}
private void collect(Set<CtMethod> result, CtClass c) {
if (c == null) {
return
}
result.addAll(c.declaredMethods.findAll { isPublicApi(it) })
collect(result, c.superclass)
}
private boolean isPublicApi(CtMethod method) {
return Modifier.isPublic(method.modifiers) || Modifier.isProtected(method.modifiers)
}
private List<String> filterChangesToReport(CtClass c, Set<CtMethod> methods) {
return methods.findAll { isFirstPublicClassInHierarchy(it, c) }*.longName.sort()
}
private boolean declaredInInternalClass(CtMethod method) {
return isInternal(method.declaringClass)
}
private boolean isFirstPublicClassInHierarchy(CtMethod method, CtClass c) {
List<CtClass> classesContainingMethod = []
CtClass current = c
while (current != null) {
if (containsMethod(current, method)) {
classesContainingMethod.add(current)
} else {
break
}
current = current.getSuperclass()
}
for (int i = classesContainingMethod.size() - 1; i > 0; --i) {
current = classesContainingMethod.get(i)
if (!isInternal(current)) {
// there's another public super class which contains target method
// it would be reported somewhere else
return false
}
}
// I'm the top public class which contains target method
return true
}
private boolean containsMethod(CtClass c, CtMethod method) {
// TODO signature contains return type
// but return type can be overridden
return collectAllPublicApiMethods(c).any { it.signature == method.signature }
}
}
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.binarycompatibility.rules;
import japicmp.model.JApiCompatibility;
import japicmp.model.JApiClass;
import japicmp.model.JApiField;
import japicmp.model.JApiHasAnnotations;
import japicmp.model.JApiMethod;
import me.champeau.gradle.japicmp.report.Violation;
import java.util.Map;
public class NewIncubatingAPIRule extends AbstractGradleViolationRule {
public NewIncubatingAPIRule(Map<String, String> acceptedViolations) {
super(acceptedViolations);
}
@Override
public Violation maybeViolation(final JApiCompatibility member) {
if (member instanceof JApiMethod || member instanceof JApiField || member instanceof JApiClass) {
if (!isIncubatingOrDeprecated((JApiHasAnnotations) member)) {
return null;
}
if (member instanceof JApiMethod && isOverride((JApiMethod) member)) {
return null;
}
return Violation.info(member, "New public API in " + getCurrentVersion() + " (@Incubating)");
}
return null;
}
}
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.binarycompatibility.rules;
import com.github.javaparser.JavaParser;
import com.github.javaparser.ast.body.AnnotationDeclaration;
import com.github.javaparser.ast.body.BodyDeclaration;
import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import com.github.javaparser.ast.body.ConstructorDeclaration;
import com.github.javaparser.ast.body.EnumConstantDeclaration;
import com.github.javaparser.ast.body.EnumDeclaration;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.visitor.GenericVisitorAdapter;
import japicmp.model.JApiClass;
import japicmp.model.JApiCompatibility;
import japicmp.model.JApiConstructor;
import japicmp.model.JApiField;
import japicmp.model.JApiMethod;
import me.champeau.gradle.japicmp.report.Violation;
import java.util.Map;
public class SinceAnnotationMissingRule extends AbstractGradleViolationRule {
public SinceAnnotationMissingRule(Map<String, String> acceptedViolations) {
super(acceptedViolations);
}
@Override
public Violation maybeViolation(final JApiCompatibility member) {
String className = null;
GenericVisitorAdapter<Object, Void> visitor = null;
if (member instanceof JApiMethod && !isOverride((JApiMethod) member)) {
final JApiMethod method = (JApiMethod) member;
if (isDeprecated(method)) {
return null;
}
className = method.getjApiClass().getFullyQualifiedName();
visitor = new GenericVisitorAdapter<Object, Void>() {
@Override
public Object visit(ClassOrInterfaceDeclaration classDeclaration, Void arg) {
if (matchesNameAndContainsAnnotation(classDeclaration.getName(), toSimpleName(method.getjApiClass().getFullyQualifiedName()), classDeclaration)) {
return new Object();
}
return super.visit(classDeclaration, arg);
}
@Override
public Object visit(AnnotationDeclaration annotationDeclaration, Void arg) {
if (matchesNameAndContainsAnnotation(annotationDeclaration.getName(), toSimpleName(method.getjApiClass().getFullyQualifiedName()), annotationDeclaration)) {
return new Object();
}
return super.visit(annotationDeclaration, arg);
}
@Override
public Object visit(EnumDeclaration enumDeclaration, Void arg) {
if (matchesNameAndContainsAnnotation(enumDeclaration.getName(), toSimpleName(method.getjApiClass().getFullyQualifiedName()), enumDeclaration)) {
return new Object();
}
return super.visit(enumDeclaration, arg);
}
@Override
public Object visit(MethodDeclaration methodDeclaration, Void arg) {
if (matchesNameAndContainsAnnotation(methodDeclaration.getName(), method.getName(), methodDeclaration)) {
return new Object();
}
return null;
}
};
} else if (member instanceof JApiField) {
final JApiField field = (JApiField) member;
if (isDeprecated(field)) {
return null;
}
className = field.getjApiClass().getFullyQualifiedName();
visitor = new GenericVisitorAdapter<Object, Void>() {
@Override
public Object visit(ClassOrInterfaceDeclaration classDeclaration, Void arg) {
if (matchesNameAndContainsAnnotation(classDeclaration.getName(), toSimpleName(field.getjApiClass().getFullyQualifiedName()), classDeclaration)) {
return new Object();
}
return super.visit(classDeclaration, arg);
}
@Override
public Object visit(AnnotationDeclaration annotationDeclaration, Void arg) {
if (matchesNameAndContainsAnnotation(annotationDeclaration.getName(), toSimpleName(field.getjApiClass().getFullyQualifiedName()), annotationDeclaration)) {
return new Object();
}
return super.visit(annotationDeclaration, arg);
}
@Override
public Object visit(EnumDeclaration enumDeclaration, Void arg) {
if (matchesNameAndContainsAnnotation(enumDeclaration.getName(), toSimpleName(field.getjApiClass().getFullyQualifiedName()), enumDeclaration)) {
return new Object();
}
return super.visit(enumDeclaration, arg);
}
@Override
public Object visit(FieldDeclaration fieldDeclaration, Void arg) {
if (matchesNameAndContainsAnnotation(fieldDeclaration.getVariables().get(0).getId().getName(), field.getName(), fieldDeclaration)) {
return new Object();
}
return null;
}
@Override
public Object visit(EnumConstantDeclaration enumConstantDeclaration, Void arg) {
if (matchesNameAndContainsAnnotation(enumConstantDeclaration.getName(), field.getName(), enumConstantDeclaration)) {
return new Object();
}
return null;
}
};
} else if (member instanceof JApiConstructor) {
final JApiConstructor constructor = (JApiConstructor) member;
if (isDeprecated(constructor)) {
return null;
}
className = constructor.getjApiClass().getFullyQualifiedName();
visitor = new GenericVisitorAdapter<Object, Void>() {
@Override
public Object visit(ConstructorDeclaration constructorDeclaration, Void arg) {
if (matchesNameAndContainsAnnotation(constructorDeclaration.getName(), toSimpleName(constructor.getjApiClass().getFullyQualifiedName()), constructorDeclaration)) {
return new Object();
}
return super.visit(constructorDeclaration, arg);
}
@Override
public Object visit(ClassOrInterfaceDeclaration classDeclaration, Void arg) {
if (matchesNameAndContainsAnnotation(classDeclaration.getName(), toSimpleName(constructor.getjApiClass().getFullyQualifiedName()), classDeclaration)) {
return new Object();
}
return super.visit(classDeclaration, arg);
}
@Override
public Object visit(AnnotationDeclaration annotationDeclaration, Void arg) {
if (matchesNameAndContainsAnnotation(annotationDeclaration.getName(), toSimpleName(constructor.getjApiClass().getFullyQualifiedName()), annotationDeclaration)) {
return new Object();
}
return super.visit(annotationDeclaration, arg);
}
@Override
public Object visit(EnumDeclaration enumDeclaration, Void arg) {
if (matchesNameAndContainsAnnotation(enumDeclaration.getName(), toSimpleName(constructor.getjApiClass().getFullyQualifiedName()), enumDeclaration)) {
return new Object();
}
return super.visit(enumDeclaration, arg);
}
@Override
public Object visit(FieldDeclaration fieldDeclaration, Void arg) {
if (matchesNameAndContainsAnnotation(fieldDeclaration.getVariables().get(0).getId().getName(), constructor.getName(), fieldDeclaration)) {
return new Object();
}
return null;
}
@Override
public Object visit(EnumConstantDeclaration enumConstantDeclaration, Void arg) {
if (matchesNameAndContainsAnnotation(enumConstantDeclaration.getName(), constructor.getName(), enumConstantDeclaration)) {
return new Object();
}
return null;
}
};
} else if (member instanceof JApiClass) {
final JApiClass clazz = (JApiClass) member;
if (isDeprecated(clazz)) {
return null;
}
className = clazz.getFullyQualifiedName();
visitor = new GenericVisitorAdapter<Object, Void>() {
@Override
public Object visit(ClassOrInterfaceDeclaration classDeclaration, Void arg) {
if (matchesNameAndContainsAnnotation(classDeclaration.getName(), toSimpleName(clazz.getFullyQualifiedName()), classDeclaration)) {
return new Object();
}
return super.visit(classDeclaration, arg);
}
@Override
public Object visit(AnnotationDeclaration annotationDeclaration, Void arg) {
if (matchesNameAndContainsAnnotation(annotationDeclaration.getName(), toSimpleName(clazz.getFullyQualifiedName()), annotationDeclaration)) {
return new Object();
}
return super.visit(annotationDeclaration, arg);
}
@Override
public Object visit(EnumDeclaration enumDeclaration, Void arg) {
if (matchesNameAndContainsAnnotation(enumDeclaration.getName(), toSimpleName(clazz.getFullyQualifiedName()), enumDeclaration)) {
return new Object();
}
return super.visit(enumDeclaration, arg);
}
};
}
if (className == null) {
return null;
}
try {
Object result = JavaParser.parse(sourceFileFor(className)).accept(visitor, null);
if (result == null) {
return acceptOrReject(member, Violation.error(member, "Is not annotated with @since " + getCurrentVersion()));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
return null;
}
private String toSimpleName(String className) {
String[] bits = className.split("\\.");
return bits[bits.length-1];
}
private boolean matchesNameAndContainsAnnotation(String name1, String name2, BodyDeclaration declaration) {
name2 = name2.replaceAll(".*\\$", ""); //strip outer class names
return name1.equals(name2) && declaration.getComment() != null && declaration.getComment().getContent().contains("@since " + getCurrentVersion());
}
}
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.binarycompatibility.rules;
import me.champeau.gradle.japicmp.report.SetupRule;
import me.champeau.gradle.japicmp.report.ViolationCheckContext;
import java.util.Map;
import java.util.Set;
public class SinceAnnotationMissingRuleAPISourceFoldersSetup implements SetupRule {
private final Set<String> apiSourceFolders;
public SinceAnnotationMissingRuleAPISourceFoldersSetup(Map<String, String> sourceFolders) {
this.apiSourceFolders = sourceFolders.keySet();
}
@SuppressWarnings("unchecked")
public void execute(ViolationCheckContext context) {
Map<String, Object> userData = (Map<String, Object>) context.getUserData();
userData.put("apiSourceFolders", apiSourceFolders);
}
}
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.binarycompatibility.rules;
import me.champeau.gradle.japicmp.report.SetupRule;
import me.champeau.gradle.japicmp.report.ViolationCheckContext;
import java.util.Map;
public class SinceAnnotationMissingRuleCurrentGradleVersionSetup implements SetupRule {
private final String currentVersion;
public SinceAnnotationMissingRuleCurrentGradleVersionSetup(Map<String, String> currentVersion) {
this.currentVersion = currentVersion.get("currentVersion");
}
@SuppressWarnings("unchecked")
public void execute(ViolationCheckContext context) {
Map<String, Object> userData = (Map<String, Object>) context.getUserData();
userData.put("currentVersion", currentVersion);
}
}
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.binarycompatibility.transforms
import groovy.transform.CompileStatic
import org.gradle.api.artifacts.transform.ArtifactTransform
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
import java.nio.file.Files
@CompileStatic
class ExplodeZipAndFindJars extends ArtifactTransform {
@Override
List<File> transform(final File file) {
List<File> result = []
if (outputDirectory.exists() && outputDirectory.listFiles().length == 0) {
File gradleJars = new File(outputDirectory, "gradle-jars")
File dependencies = new File(outputDirectory, "gradle-dependencies")
gradleJars.mkdir()
dependencies.mkdir()
result << gradleJars
result << dependencies
ZipInputStream zin = new ZipInputStream(file.newInputStream())
ZipEntry zipEntry
while (zipEntry = zin.nextEntry) {
String shortName = zipEntry.name
if (shortName.contains('/')) {
shortName = shortName.substring(shortName.lastIndexOf('/') + 1)
}
if (shortName.endsWith('.jar')) {
def outputDir = shortName.startsWith('gradle-') ? gradleJars : dependencies
def out = new File(outputDir, shortName)
Files.copy(zin, out.toPath())
zin.closeEntry()
}
}
}
result
}
}
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.binarycompatibility.transforms
import org.gradle.api.artifacts.transform.ArtifactTransform
import javax.inject.Inject
import groovy.transform.CompileStatic
@CompileStatic
class FindGradleClasspath extends ArtifactTransform {
@Inject
FindGradleClasspath() {
}
@Override
List<File> transform(final File file) {
if (file.name == 'gradle-dependencies') {
(file.listFiles() as List<File>).sort { it.name }
} else {
[]
}
}
}
/*
* Copyright 2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.gradle.binarycompatibility.transforms
import org.gradle.api.artifacts.transform.ArtifactTransform
import javax.inject.Inject
import groovy.transform.CompileStatic
@CompileStatic
class FindGradleJar extends ArtifactTransform {
private final String target
@Inject
FindGradleJar(String target) {
this.target = target
}
@Override
List<File> transform(final File file) {
if (file.name == 'gradle-jars') {
(file.listFiles().findAll { it.name.startsWith("gradle-${target}-") } as List<File>).sort { it.name }
} else {
[]
}
}
}