Skip to content

Commit ac35950

Browse files
authored
Convert muzzle plugin to kotlin (#9439)
* chore: Move muzzle groovy file to its own package * chore: Explode gigantic muzzle groovy file * chore: Make groovy depends on kotlin during transition * chore: Convert MuzzleWorkParameters * chore: Convert MuzzleTask / MuzzleAction to Kotlin * chore: Convert VersionSet to Kotlin * chore: Convert MuzzleExtension to kotlin * chore: Convert MuzzlePlugin to kotlin 1/11 * chore: Generate JVM overloads for groovy compat * chore: Convert MuzzlePlugin to kotlin 2/11 * chore: Convert MuzzlePlugin to kotlin 3/11 * chore: Convert MuzzlePlugin to kotlin 4/11 * chore: Convert MuzzlePlugin to kotlin 5/11 * chore: Convert MuzzlePlugin to kotlin 6/11 * chore: Convert MuzzlePlugin to kotlin 7/11 * chore: Convert MuzzlePlugin to kotlin 8/11 * chore: Convert MuzzlePlugin to kotlin 9/11 * chore: Convert MuzzlePlugin to kotlin 10/11 * chore: Convert MuzzlePlugin to kotlin 11/11 * chore: Cleanups and better use of the Gradle API * style: Fix java.util.* in kt files * fix: Groovy was adding a GStringImpl to the List<String> * fix: task name should have used directive.nameSlug * fix: Don't configure muzzle tasks, when the project don't even have the JavaPlugin applied This can happen on empty parent projects.
1 parent f7aa6d2 commit ac35950

File tree

19 files changed

+1164
-931
lines changed

19 files changed

+1164
-931
lines changed

buildSrc/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ gradlePlugin {
2020
}
2121
create("muzzle-plugin") {
2222
id = "muzzle"
23-
implementationClass = "MuzzlePlugin"
23+
implementationClass = "datadog.gradle.plugin.muzzle.MuzzlePlugin"
2424
}
2525
create("call-site-instrumentation-plugin") {
2626
id = "call-site-instrumentation"

buildSrc/src/main/groovy/MuzzlePlugin.groovy

Lines changed: 0 additions & 809 deletions
This file was deleted.

buildSrc/src/main/groovy/VersionSet.groovy

Lines changed: 0 additions & 111 deletions
This file was deleted.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package datadog.gradle.plugin.muzzle
2+
3+
import org.gradle.api.file.FileCollection
4+
import org.gradle.workers.WorkAction
5+
import java.lang.reflect.Method
6+
import java.net.URLClassLoader
7+
8+
abstract class MuzzleAction : WorkAction<MuzzleWorkParameters> {
9+
companion object {
10+
private val lock = Any()
11+
private var bootCL: ClassLoader? = null
12+
private var toolCL: ClassLoader? = null
13+
@Volatile
14+
private var lastBuildStamp: Long = 0
15+
16+
fun createClassLoader(cp: FileCollection, parent: ClassLoader = ClassLoader.getSystemClassLoader()): ClassLoader {
17+
val urls = cp.map { it.toURI().toURL() }.toTypedArray()
18+
return URLClassLoader(urls, parent)
19+
}
20+
}
21+
22+
override fun execute() {
23+
val buildStamp = parameters.buildStartedTime.get()
24+
if (bootCL == null || toolCL == null || lastBuildStamp < buildStamp) {
25+
synchronized(lock) {
26+
if (bootCL == null || toolCL == null || lastBuildStamp < buildStamp) {
27+
bootCL = createClassLoader(parameters.bootstrapClassPath)
28+
toolCL = createClassLoader(parameters.toolingClassPath, bootCL!!)
29+
lastBuildStamp = buildStamp
30+
}
31+
}
32+
}
33+
val instCL = createClassLoader(parameters.instrumentationClassPath, toolCL!!)
34+
val testCL = createClassLoader(parameters.testApplicationClassPath, bootCL!!)
35+
val assertPass = parameters.assertPass.get()
36+
val muzzleDirective = parameters.muzzleDirective.orNull
37+
val assertionMethod: Method = instCL.loadClass("datadog.trace.agent.tooling.muzzle.MuzzleVersionScanPlugin")
38+
.getMethod(
39+
"assertInstrumentationMuzzled",
40+
ClassLoader::class.java,
41+
ClassLoader::class.java,
42+
Boolean::class.java,
43+
String::class.java
44+
)
45+
assertionMethod.invoke(null, instCL, testCL, assertPass, muzzleDirective)
46+
}
47+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package datadog.gradle.plugin.muzzle
2+
3+
import org.eclipse.aether.repository.RemoteRepository
4+
5+
/**
6+
* A pass or fail directive for a single dependency.
7+
*/
8+
open class MuzzleDirective {
9+
/**
10+
* Name is optional and is used to further define the scope of a directive. The motivation for this is that this
11+
* plugin creates a config for each of the dependencies under test with name '...-<group_id>-<artifact_id>-<version>'.
12+
* The problem is that if we want to test multiple times the same configuration under different conditions, e.g.
13+
* with different extra dependencies, the plugin would throw an error as it would try to create several times the
14+
* same config. This property can be used to differentiate those config names for different directives.
15+
*/
16+
var name: String? = null
17+
var group: String? = null
18+
var module: String? = null
19+
var classifier: String? = null
20+
var versions: String? = null
21+
var skipVersions: MutableSet<String> = HashSet()
22+
var additionalDependencies: MutableList<String> = ArrayList()
23+
internal var additionalRepositories: MutableList<RemoteRepository> = ArrayList()
24+
internal var excludedDependencies: MutableList<String> = ArrayList()
25+
var assertPass: Boolean = false
26+
var assertInverse: Boolean = false
27+
var skipFromReport: Boolean = false
28+
internal var isCoreJdk: Boolean = false
29+
var includeSnapshots: Boolean = false
30+
var javaVersion: String? = null
31+
32+
fun coreJdk(version: String? = null) {
33+
isCoreJdk = true
34+
javaVersion = version
35+
}
36+
37+
/**
38+
* Adds extra dependencies to the current muzzle test.
39+
*
40+
* @param compileString An extra dependency in the gradle canonical form: '<group_id>:<artifact_id>:<version_id>'.
41+
*/
42+
fun extraDependency(compileString: String) {
43+
additionalDependencies.add(compileString)
44+
}
45+
46+
/**
47+
* Adds extra repositories to the current muzzle test.
48+
*
49+
* @param id the repository id
50+
* @param url the url of the repository
51+
* @param type the type of repository, defaults to "default"
52+
*/
53+
fun extraRepository(id: String, url: String, type: String = "default") {
54+
additionalRepositories.add(RemoteRepository.Builder(id, type, url).build())
55+
}
56+
57+
/**
58+
* Adds transitive dependencies to exclude from the current muzzle test.
59+
*
60+
* @param excludeString A dependency in the gradle canonical form: '<group_id>:<artifact_id>'.
61+
*/
62+
fun excludeDependency(excludeString: String) {
63+
excludedDependencies.add(excludeString)
64+
}
65+
66+
/**
67+
* Get the list of repositories to use for this muzzle directive.
68+
*
69+
* @param defaults the default repositories
70+
* @return a list of the default repositories followed by any additional repositories
71+
*/
72+
fun getRepositories(defaults: List<RemoteRepository>): List<RemoteRepository> {
73+
return if (additionalRepositories.isEmpty()) {
74+
defaults
75+
} else {
76+
ArrayList<RemoteRepository>(defaults.size + additionalRepositories.size).apply {
77+
addAll(defaults)
78+
addAll(additionalRepositories)
79+
}
80+
}
81+
}
82+
83+
/**
84+
* Slug of directive name.
85+
*
86+
* @return A slug of the name or an empty string if name is empty. E.g. 'My Directive' --> 'My-Directive'
87+
*/
88+
val nameSlug: String
89+
get() = name?.trim()?.replace(Regex("[^a-zA-Z0-9]+"), "-") ?: ""
90+
91+
override fun toString(): String {
92+
return if (isCoreJdk) {
93+
"${if (assertPass) "Pass" else "Fail"}-core-jdk"
94+
} else {
95+
"${if (assertPass) "pass" else "fail"} $group:$module:$versions"
96+
}
97+
}
98+
}
99+
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package datadog.gradle.plugin.muzzle
2+
3+
import org.eclipse.aether.repository.RemoteRepository
4+
import org.gradle.api.Action
5+
import org.gradle.api.model.ObjectFactory
6+
import javax.inject.Inject
7+
import java.util.Locale
8+
9+
/**
10+
* Muzzle extension containing all pass and fail directives.
11+
*/
12+
abstract class MuzzleExtension @Inject constructor(protected val objectFactory: ObjectFactory) {
13+
val directives: MutableList<MuzzleDirective> = ArrayList()
14+
private val additionalRepositories: MutableList<RemoteRepository> = ArrayList()
15+
16+
fun pass(action: Action<in MuzzleDirective>) {
17+
val pass = objectFactory.newInstance(MuzzleDirective::class.java)
18+
action.execute(pass)
19+
postConstruct(pass)
20+
pass.assertPass = true
21+
directives.add(pass)
22+
}
23+
24+
fun fail(action: Action<in MuzzleDirective>) {
25+
val fail = objectFactory.newInstance(MuzzleDirective::class.java)
26+
action.execute(fail)
27+
postConstruct(fail)
28+
fail.assertPass = false
29+
directives.add(fail)
30+
}
31+
32+
/**
33+
* Adds extra repositories to the current muzzle section. Repositories will only be added to directives
34+
* created after this.
35+
*
36+
* @param id the repository id
37+
* @param url the url of the repository
38+
* @param type the type of repository, defaults to "default"
39+
*/
40+
@JvmOverloads
41+
fun extraRepository(id: String, url: String, type: String = "default") {
42+
additionalRepositories.add(RemoteRepository.Builder(id, type, url).build())
43+
}
44+
45+
private fun postConstruct(directive: MuzzleDirective) {
46+
// Make skipVersions case insensitive.
47+
directive.skipVersions = directive.skipVersions.map { it.lowercase(Locale.ROOT) }.toMutableSet()
48+
// Add existing repositories
49+
directive.additionalRepositories.addAll(additionalRepositories)
50+
}
51+
}

0 commit comments

Comments
 (0)