Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support to exclude classes / packages by Regex pattern #121

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
99 changes: 99 additions & 0 deletions src/functionalTest/kotlin/kotlinx/validation/test/IgnoredTests.kt
@@ -0,0 +1,99 @@
/*
* Copyright 2016-2020 JetBrains s.r.o.
* Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file.
*/

package kotlinx.validation.test

import kotlinx.validation.api.*
import kotlinx.validation.api.BaseKotlinGradleTest
import kotlinx.validation.api.assertTaskSuccess
import kotlinx.validation.api.buildGradleKts
import kotlinx.validation.api.kotlin
import kotlinx.validation.api.readFileList
import kotlinx.validation.api.resolve
import kotlinx.validation.api.runner
import kotlinx.validation.api.test
import org.assertj.core.api.Assertions
import org.junit.Test
import kotlin.test.assertTrue

internal class IgnoredTests : BaseKotlinGradleTest() {

@Test
fun `apiCheck should succeed, when given class is not in api-File, but is ignored via ignored pattern`() {
val runner = test {
buildGradleKts {
resolve("examples/gradle/base/withPlugin.gradle.kts")
resolve("examples/gradle/configuration/ignored/oneValidPattern.gradle.kts")
}

kotlin("BuildConfig.kt") {
resolve("examples/classes/BuildConfig.kt")
}

emptyApiFile(projectName = rootProjectDir.name)

runner {
arguments.add(":apiCheck")
}
}

runner.build().apply {
assertTaskSuccess(":apiCheck")
}
}

@Test
fun `apiCheck should succeed, when given class is not in api-File, but is ignored via ignored pattern (based on package)`() {
val runner = test {
buildGradleKts {
resolve("examples/gradle/base/withPlugin.gradle.kts")
resolve("examples/gradle/configuration/ignored/oneValidPackagePattern.gradle.kts")
}

kotlin("BuildConfig.kt") {
resolve("examples/classes/BuildConfig.kt")
}

emptyApiFile(projectName = rootProjectDir.name)

runner {
arguments.add(":apiCheck")
}
}

runner.build().apply {
assertTaskSuccess(":apiCheck")
}
}

@Test
fun `apiDump should not dump ignored classes, when class is excluded via ignored pattern`() {
val runner = test {
buildGradleKts {
resolve("examples/gradle/base/withPlugin.gradle.kts")
resolve("examples/gradle/configuration/ignored/oneValidPatternPackageClass.gradle.kts")
}
kotlin("BuildConfig.kt") {
resolve("examples/classes/BuildConfig.kt")
}
kotlin("AnotherBuildConfig.kt") {
resolve("examples/classes/AnotherBuildConfig.kt")
}

runner {
arguments.add(":apiDump")
}
}

runner.build().apply {
assertTaskSuccess(":apiDump")

assertTrue(rootProjectApiDump.exists(), "api dump file should exist")

val expected = readFileList("examples/classes/AnotherBuildConfig.dump")
Assertions.assertThat(rootProjectApiDump.readText()).isEqualToIgnoringNewLines(expected)
}
}
}
@@ -0,0 +1,8 @@
/*
* Copyright 2016-2020 JetBrains s.r.o.
* Use of this source code is governed by the Apache 2.0 License that can be found in the LICENSE.txt file.
*/

configure<kotlinx.validation.ApiValidationExtension> {
ignored.add("com\\/company\\/.+")
}
@@ -0,0 +1,3 @@
configure<kotlinx.validation.ApiValidationExtension> {
ignored.add(".+\\/BuildConfig")
}
@@ -0,0 +1,3 @@
configure<kotlinx.validation.ApiValidationExtension> {
ignored.add(".+\\/company\\/BuildConfig")
}
8 changes: 8 additions & 0 deletions src/main/kotlin/ApiValidationExtension.kt
Expand Up @@ -34,4 +34,12 @@ open class ApiValidationExtension {
* Example of such a class could be `com.package.android.BuildConfig`.
*/
public var ignoredClasses: MutableSet<String> = HashSet()

/**
* Defines a regex pattern used to define classes and packages that are ignored by the API check.
*
* Example of such a package could be `.+\/internal\/.+`.
* An example of such a class could be `.+\/BuildConfig`
*/
public var ignored: MutableSet<String> = HashSet()
}
9 changes: 8 additions & 1 deletion src/main/kotlin/KotlinApiBuildTask.kt
Expand Up @@ -11,6 +11,7 @@ import org.gradle.api.file.*
import org.gradle.api.tasks.*
import java.io.*
import java.util.jar.JarFile
import java.util.regex.Pattern
import javax.inject.Inject

open class KotlinApiBuildTask @Inject constructor(
Expand Down Expand Up @@ -53,6 +54,12 @@ open class KotlinApiBuildTask @Inject constructor(
get() = _ignoredClasses ?: extension?.ignoredClasses ?: emptySet()
set(value) { _ignoredClasses = value }

private var _ignored: Set<Pattern>? = null
@get:Input
var ignored : Set<Pattern>
get() = _ignored ?: extension?.ignored?.map { Pattern.compile(it) }?.toSet() ?: emptySet()
set(value) { _ignored = value }

@get:Internal
internal val projectName = project.name

Expand All @@ -79,7 +86,7 @@ open class KotlinApiBuildTask @Inject constructor(


val filteredSignatures = signatures
.filterOutNonPublic(ignoredPackages, ignoredClasses)
.filterOutNonPublic(ignored, ignoredPackages, ignoredClasses)
.filterOutAnnotated(nonPublicMarkers.map { it.replace(".", "/") }.toSet())

outputApiDir.resolve("$projectName.api").bufferedWriter().use { writer ->
Expand Down
25 changes: 18 additions & 7 deletions src/main/kotlin/api/KotlinSignaturesLoading.kt
Expand Up @@ -5,13 +5,20 @@

package kotlinx.validation.api

import kotlinx.metadata.jvm.*
import kotlinx.validation.*
import org.objectweb.asm.*
import org.objectweb.asm.tree.*
import java.io.*
import kotlinx.metadata.jvm.JvmFieldSignature
import kotlinx.metadata.jvm.JvmMethodSignature
import kotlinx.metadata.jvm.KotlinClassMetadata
import kotlinx.validation.ExternalApi
import org.objectweb.asm.ClassReader
import org.objectweb.asm.Opcodes
import org.objectweb.asm.tree.AnnotationNode
import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.FieldNode
import org.objectweb.asm.tree.MethodNode
import java.io.InputStream
import java.util.*
import java.util.jar.*
import java.util.jar.JarFile
import java.util.regex.Pattern

@ExternalApi
@Suppress("unused")
Expand Down Expand Up @@ -148,6 +155,7 @@ public fun List<ClassBinarySignature>.filterOutAnnotated(targetAnnotations: Set<

@ExternalApi
public fun List<ClassBinarySignature>.filterOutNonPublic(
nonPublic: Collection<Pattern> = emptyList(),
nonPublicPackages: Collection<String> = emptyList(),
nonPublicClasses: Collection<String> = emptyList()
): List<ClassBinarySignature> {
Expand All @@ -157,6 +165,8 @@ public fun List<ClassBinarySignature>.filterOutNonPublic(

val classByName = associateBy { it.name }

fun ClassBinarySignature.isNonPublic() = nonPublic.any { it.matcher(name).matches() }

fun ClassBinarySignature.isInNonPublicPackage() =
nonPublicPackagePaths.any { name.startsWith(it) }

Expand Down Expand Up @@ -189,7 +199,8 @@ public fun List<ClassBinarySignature>.filterOutNonPublic(
)
}

return filter { !it.isInNonPublicPackage() && !it.isInExcludedClasses() && it.isPublicAndAccessible() }

return filter { !it.isInNonPublicPackage() && !it.isInExcludedClasses() && !it.isNonPublic() && it.isPublicAndAccessible() }
.map { it.flattenNonPublicBases() }
.filterNot { it.isNotUsedWhenEmpty && it.memberSignatures.isEmpty() }
}
Expand Down