added jpms-check plugin
This commit is contained in:
@@ -1,56 +1,31 @@
|
|||||||
repositories {
|
|
||||||
jcenter()
|
|
||||||
mavenLocal()
|
|
||||||
}
|
|
||||||
|
|
||||||
group = "net.woggioni.plugins"
|
|
||||||
version = 0.1
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
`java-gradle-plugin`
|
id("org.jetbrains.kotlin.jvm") version "1.3.72" apply false
|
||||||
`maven-publish`
|
id("com.gradle.plugin-publish") version "0.10.1" apply false
|
||||||
id("org.jetbrains.kotlin.jvm") version("1.3.71")
|
|
||||||
id("com.gradle.plugin-publish") version("0.10.1")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
configure<ExtraPropertiesExtension> {
|
allprojects {
|
||||||
set("junit_jupiter_version", "5.5.2")
|
apply<JavaLibraryPlugin>()
|
||||||
}
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
mavenCentral()
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
group = "net.woggioni.plugins"
|
||||||
|
version = 0.1
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
add("testImplementation", create(group="org.junit.jupiter", name="junit-jupiter-api", version=project["version.junitJupiter"]))
|
||||||
|
add("testRuntimeOnly", create(group="org.junit.jupiter", name="junit-jupiter-engine", version=project["version.junitJupiter"]))
|
||||||
|
add("testImplementation", gradleTestKit())
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named<Test>("test") {
|
||||||
// Align versions of all Kotlin components
|
useJUnitPlatform()
|
||||||
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
|
|
||||||
|
|
||||||
// Use the Kotlin JDK 8 standard library.
|
|
||||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
|
||||||
|
|
||||||
testImplementation("org.junit.jupiter:junit-jupiter-api:5.5.2")
|
|
||||||
testImplementation(gradleTestKit())
|
|
||||||
|
|
||||||
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.5.2")
|
|
||||||
}
|
|
||||||
|
|
||||||
gradlePlugin {
|
|
||||||
val dependencyExportPlugin by plugins.creating {
|
|
||||||
id = "net.woggioni.plugins.dependency-export"
|
|
||||||
implementationClass = "net.woggioni.plugins.DependencyExportPlugin"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType<Test>().configureEach {
|
|
||||||
useJUnitPlatform()
|
|
||||||
systemProperty("plugin.build.dir", tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().first().destinationDir)
|
|
||||||
systemProperty("java.io.tmpdir", buildDir.absolutePath)
|
|
||||||
systemProperty("test.gradle.user.home", project.gradle.gradleUserHomeDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
tasks.withType<Wrapper>().configureEach {
|
tasks.withType<Wrapper>().configureEach {
|
||||||
gradleVersion = "6.3"
|
gradleVersion = "6.6"
|
||||||
distributionType = Wrapper.DistributionType.ALL
|
distributionType = Wrapper.DistributionType.ALL
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
|
|
||||||
kotlinOptions.jvmTarget = "1.8"
|
|
||||||
}
|
|
||||||
7
buildSrc/build.gradle.kts
Normal file
7
buildSrc/build.gradle.kts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
repositories {
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
`kotlin-dsl`
|
||||||
|
}
|
||||||
5
buildSrc/src/main/kotlin/Build.kt
Normal file
5
buildSrc/src/main/kotlin/Build.kt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import org.gradle.api.Project
|
||||||
|
|
||||||
|
operator fun Project.get(key : String) : String? {
|
||||||
|
return property(key) as String?
|
||||||
|
}
|
||||||
21
dependency-export/build.gradle.kts
Normal file
21
dependency-export/build.gradle.kts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
plugins {
|
||||||
|
`java-gradle-plugin`
|
||||||
|
`maven-publish`
|
||||||
|
id("org.jetbrains.kotlin.jvm")
|
||||||
|
id("com.gradle.plugin-publish")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// Align versions of all Kotlin components
|
||||||
|
implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
|
||||||
|
|
||||||
|
// Use the Kotlin JDK 8 standard library.
|
||||||
|
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
|
||||||
|
}
|
||||||
|
|
||||||
|
gradlePlugin {
|
||||||
|
val dependencyExportPlugin by plugins.creating {
|
||||||
|
id = "net.woggioni.plugins.dependency-export"
|
||||||
|
implementationClass = "net.woggioni.plugins.dependency.export.DependencyExportPlugin"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
package net.woggioni.plugins
|
package net.woggioni.plugins.dependency.export
|
||||||
|
|
||||||
import org.gradle.api.GradleException
|
import org.gradle.api.GradleException
|
||||||
import org.gradle.api.Plugin
|
import org.gradle.api.Plugin
|
||||||
import org.gradle.api.Project
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.artifacts.ResolvedArtifact
|
||||||
|
import org.gradle.api.artifacts.component.ComponentIdentifier
|
||||||
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
|
import org.gradle.api.artifacts.component.ModuleComponentIdentifier
|
||||||
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
|
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
|
||||||
import org.gradle.api.artifacts.result.ResolvedComponentResult
|
import org.gradle.api.artifacts.result.ResolvedComponentResult
|
||||||
@@ -19,6 +21,7 @@ import kotlin.reflect.KMutableProperty0
|
|||||||
open class ExportDependenciesPluginExtension(project: Project) {
|
open class ExportDependenciesPluginExtension(project: Project) {
|
||||||
var configurationName: String = "default"
|
var configurationName: String = "default"
|
||||||
var outputFile = project.buildDir.toPath().resolve("dependencies.dot")
|
var outputFile = project.buildDir.toPath().resolve("dependencies.dot")
|
||||||
|
var showArtifacts = false
|
||||||
}
|
}
|
||||||
|
|
||||||
open class RenderDependenciesPluginExtension(project: Project) {
|
open class RenderDependenciesPluginExtension(project: Project) {
|
||||||
@@ -32,8 +35,7 @@ private class Overrider(private val properties: Map<String, Any?>, private val p
|
|||||||
return arg
|
return arg
|
||||||
}
|
}
|
||||||
|
|
||||||
fun overrideProperty(
|
fun overrideProperty(property: KMutableProperty0<String>) {
|
||||||
property: KMutableProperty0<String>) {
|
|
||||||
overrideProperty(property, ::identity)
|
overrideProperty(property, ::identity)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,10 +52,13 @@ private class Overrider(private val properties: Map<String, Any?>, private val p
|
|||||||
|
|
||||||
object DependencyExporter {
|
object DependencyExporter {
|
||||||
|
|
||||||
|
private fun quote(s : String) = "\"" + s + "\""
|
||||||
|
|
||||||
fun exportDot(project: Project, ext: ExportDependenciesPluginExtension) {
|
fun exportDot(project: Project, ext: ExportDependenciesPluginExtension) {
|
||||||
val overrider = Overrider(project.properties, "exportDependencies")
|
val overrider = Overrider(project.properties, "exportDependencies")
|
||||||
overrider.overrideProperty(ext::configurationName)
|
overrider.overrideProperty(ext::configurationName)
|
||||||
overrider.overrideProperty(ext::outputFile) { value -> Paths.get(value) }
|
overrider.overrideProperty(ext::outputFile) { value -> Paths.get(value) }
|
||||||
|
overrider.overrideProperty(ext::showArtifacts) { value -> value.toBoolean() }
|
||||||
|
|
||||||
var sequence = 0
|
var sequence = 0
|
||||||
val map = HashMap<ResolvedComponentResult, Int>()
|
val map = HashMap<ResolvedComponentResult, Int>()
|
||||||
@@ -81,31 +86,73 @@ object DependencyExporter {
|
|||||||
writer.newLine()
|
writer.newLine()
|
||||||
writer.write(" #rankdir=\"LR\";")
|
writer.write(" #rankdir=\"LR\";")
|
||||||
writer.newLine()
|
writer.newLine()
|
||||||
|
|
||||||
|
val artifactMap = if(ext.showArtifacts) {
|
||||||
|
requestedConfiguration.resolvedConfiguration.resolvedArtifacts.asSequence().map {
|
||||||
|
it.id.componentIdentifier to it
|
||||||
|
}.groupBy(
|
||||||
|
Pair<ComponentIdentifier, ResolvedArtifact>::first,
|
||||||
|
Pair<ComponentIdentifier, ResolvedArtifact>::second
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
for (component in resolutionResult.allComponents) {
|
for (component in resolutionResult.allComponents) {
|
||||||
map.computeIfAbsent(component) {
|
map.computeIfAbsent(component) {
|
||||||
sequence++
|
sequence++
|
||||||
}
|
}
|
||||||
|
val artifacts = artifactMap?.let { it[component.id] }
|
||||||
val (shape, color) = when (component.id) {
|
val (shape, color) = when (component.id) {
|
||||||
is ProjectComponentIdentifier -> "box" to "#88ff88"
|
is ProjectComponentIdentifier -> (artifacts?.let { "none" } ?: "box") to "#88ff88"
|
||||||
is ModuleComponentIdentifier -> "oval" to "#ffff88"
|
is ModuleComponentIdentifier -> (artifacts?.let { "none" } ?: "oval") to "#ffff88"
|
||||||
else -> throw NotImplementedError("${component.id::class}")
|
else -> throw NotImplementedError("${component.id::class}")
|
||||||
}
|
}
|
||||||
val attrs = mapOf(
|
|
||||||
"label" to component.id.displayName,
|
|
||||||
"shape" to shape,
|
val componentName = component.id.displayName
|
||||||
"style" to "filled",
|
val label = artifacts?.let {
|
||||||
"fillcolor" to color
|
val rows = it.asSequence().map { resolvedArtifact ->
|
||||||
)
|
val artifactDescription = sequenceOf(
|
||||||
|
"type" to resolvedArtifact.type,
|
||||||
|
"classifier" to resolvedArtifact.classifier,
|
||||||
|
"extension" to resolvedArtifact.extension.takeIf {
|
||||||
|
resolvedArtifact.extension != resolvedArtifact.type
|
||||||
|
}
|
||||||
|
).mapNotNull { pair ->
|
||||||
|
when {
|
||||||
|
pair.second == null || pair.second.isEmpty() -> null
|
||||||
|
else -> "${pair.first}: ${pair.second}"
|
||||||
|
}
|
||||||
|
}.joinToString(", ")
|
||||||
|
"<TR><TD BGCOLOR=\"lightgrey\">$artifactDescription</TD></TR>"
|
||||||
|
}.joinToString()
|
||||||
|
"""
|
||||||
|
<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="2">
|
||||||
|
<TR>
|
||||||
|
<TD>${component.id.displayName}</TD>
|
||||||
|
</TR>
|
||||||
|
$rows
|
||||||
|
</TABLE>>
|
||||||
|
""".trimIndent()
|
||||||
|
} ?: quote(componentName)
|
||||||
|
|
||||||
|
val attrs = sequenceOf(
|
||||||
|
"label" to label,
|
||||||
|
"shape" to quote(shape),
|
||||||
|
"style" to quote("filled"),
|
||||||
|
artifacts?.let { "margin" to quote(0.toString()) },
|
||||||
|
"fillcolor" to quote(color)
|
||||||
|
).mapNotNull { it }.toMap()
|
||||||
writer.write(" node_${map[component]} [" +
|
writer.write(" node_${map[component]} [" +
|
||||||
attrs.entries
|
attrs.entries
|
||||||
.asSequence()
|
.asSequence()
|
||||||
.map { "${it.key}=\"${it.value}\"" }.joinToString(", ") +
|
.map { "${it.key}=${it.value}" }.joinToString(", ") +
|
||||||
"];")
|
"];")
|
||||||
writer.newLine()
|
writer.newLine()
|
||||||
}
|
}
|
||||||
|
|
||||||
for (component in resolutionResult.allComponents) {
|
for (component in resolutionResult.allComponents) {
|
||||||
|
|
||||||
component.dependencies.map { dependency ->
|
component.dependencies.map { dependency ->
|
||||||
when (dependency) {
|
when (dependency) {
|
||||||
is ResolvedDependencyResult -> dependency
|
is ResolvedDependencyResult -> dependency
|
||||||
@@ -163,7 +210,7 @@ class DependencyExportPlugin : Plugin<Project> {
|
|||||||
|
|
||||||
val renderDependenciesPluginExtension = RenderDependenciesPluginExtension(project)
|
val renderDependenciesPluginExtension = RenderDependenciesPluginExtension(project)
|
||||||
project.extensions.add(RenderDependenciesPluginExtension::class.java, "renderDependencies", renderDependenciesPluginExtension)
|
project.extensions.add(RenderDependenciesPluginExtension::class.java, "renderDependencies", renderDependenciesPluginExtension)
|
||||||
val renderDependenciesTask = project.tasks.register("renderDependencies") {
|
project.tasks.register("renderDependencies") {
|
||||||
it.dependsOn(exportDependenciesTask)
|
it.dependsOn(exportDependenciesTask)
|
||||||
it.doLast {
|
it.doLast {
|
||||||
DependencyRenderer.render(project, renderDependenciesPluginExtension, dependencyExportExtension.outputFile)
|
DependencyRenderer.render(project, renderDependenciesPluginExtension, dependencyExportExtension.outputFile)
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* This Kotlin source file was generated by the Gradle 'init' task.
|
* This Kotlin source file was generated by the Gradle 'init' task.
|
||||||
*/
|
*/
|
||||||
package net.woggioni.plugins
|
package net.woggioni.plugins.dependency.export
|
||||||
|
|
||||||
import org.gradle.testkit.runner.GradleRunner
|
import org.gradle.testkit.runner.GradleRunner
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.BeforeEach
|
||||||
@@ -12,6 +12,9 @@ import java.nio.file.Path
|
|||||||
|
|
||||||
class DependencyExportPluginTest {
|
class DependencyExportPluginTest {
|
||||||
|
|
||||||
|
@TempDir
|
||||||
|
lateinit var testGradleHomeDir : Path
|
||||||
|
|
||||||
@TempDir
|
@TempDir
|
||||||
lateinit var testProjectDir : Path
|
lateinit var testProjectDir : Path
|
||||||
lateinit var buildFile : Path
|
lateinit var buildFile : Path
|
||||||
@@ -25,7 +28,7 @@ class DependencyExportPluginTest {
|
|||||||
return GradleRunner.create()
|
return GradleRunner.create()
|
||||||
.withDebug(true)
|
.withDebug(true)
|
||||||
.withProjectDir(testProjectDir.toFile())
|
.withProjectDir(testProjectDir.toFile())
|
||||||
.withArguments(taskName, "-s", "--info", "-g", System.getProperty("test.gradle.user.home", "."))
|
.withArguments(taskName, "-s", "--info", "-g", testGradleHomeDir.toString())
|
||||||
.withPluginClasspath()
|
.withPluginClasspath()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,7 +38,7 @@ class DependencyExportPluginTest {
|
|||||||
installResource("settings.gradle.kts", testProjectDir)
|
installResource("settings.gradle.kts", testProjectDir)
|
||||||
installResource("gradle.properties", testProjectDir)
|
installResource("gradle.properties", testProjectDir)
|
||||||
val runner = getStandardGradleRunnerFor("exportDependencies")
|
val runner = getStandardGradleRunnerFor("exportDependencies")
|
||||||
val result = runner.build()
|
runner.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -44,6 +47,6 @@ class DependencyExportPluginTest {
|
|||||||
installResource("settings.gradle.kts", testProjectDir)
|
installResource("settings.gradle.kts", testProjectDir)
|
||||||
installResource("gradle.properties", testProjectDir)
|
installResource("gradle.properties", testProjectDir)
|
||||||
val runner = getStandardGradleRunnerFor("exportDependencies")
|
val runner = getStandardGradleRunnerFor("exportDependencies")
|
||||||
val result = runner.build()
|
runner.build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package net.woggioni.plugins
|
package net.woggioni.plugins.dependency.export
|
||||||
|
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
2
gradle.properties
Normal file
2
gradle.properties
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
version.junitJupiter=5.7.0
|
||||||
|
version.junitPlatform=1.7.0
|
||||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Binary file not shown.
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,5 +1,5 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-all.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
2
gradlew
vendored
2
gradlew
vendored
@@ -82,6 +82,7 @@ esac
|
|||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
# Determine the Java command to use to start the JVM.
|
# Determine the Java command to use to start the JVM.
|
||||||
if [ -n "$JAVA_HOME" ] ; then
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
@@ -129,6 +130,7 @@ fi
|
|||||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
|
||||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
# We build the pattern for arguments to be converted via cygpath
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
|||||||
1
gradlew.bat
vendored
1
gradlew.bat
vendored
@@ -84,6 +84,7 @@ set CMD_LINE_ARGS=%*
|
|||||||
|
|
||||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
@rem Execute Gradle
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||||
|
|
||||||
|
|||||||
93
jpms-check/README.md
Normal file
93
jpms-check/README.md
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
## Overview
|
||||||
|
This plugin will add a new task to your gradle projects, the `jpms-check` task, to help you to check how easily
|
||||||
|
whether you can start using [JPMS](http://openjdk.java.net/projects/jigsaw/) in your project and which one of your dependencies you will need to update or patch
|
||||||
|
to make them JPMS-friendly
|
||||||
|
|
||||||
|
### The *jpms-check* task
|
||||||
|
The `jpms-check` task will scan through all of your project dependencies and create a document where, for
|
||||||
|
each of them, it will be reported whether they contain a module descriptor (aka `module-info.class`) or
|
||||||
|
an `Automatic-Module-Name` entry in the manifest or if the jar is a
|
||||||
|
[Multi-Release JAR file](https://openjdk.java.net/jeps/238).
|
||||||
|
Note that artifacts that do not meet any of these requirements can still be used with JPMS,
|
||||||
|
but, using the standard module finder, the module's name will be inferred from the jar file name
|
||||||
|
(with [some tweaks](https://docs.oracle.com/javase/9/docs/api/java/lang/module/ModuleFinder.html#automatic-modules)
|
||||||
|
to try exclude the version number and the artifact classifier) which is not a very reliable option.
|
||||||
|
|
||||||
|
## Install the plugin
|
||||||
|
|
||||||
|
Checkout this project and in the root folder run
|
||||||
|
```bash
|
||||||
|
./gradlew publishToMavenLocal
|
||||||
|
```
|
||||||
|
to install it to your local machine's Maven repository.
|
||||||
|
|
||||||
|
## Add the plugin to your project
|
||||||
|
|
||||||
|
Add this plugin to you project simply adding it to your projects `build.gradle`
|
||||||
|
```groovy
|
||||||
|
plugins {
|
||||||
|
id "net.woggioni.plugins.jpms-check" version "0.1"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
or to your `build.gradle.kts` if you prefer the Gradle Kotlin DSL
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
plugins {
|
||||||
|
id("net.woggioni.plugins.jpms-check") version "0.1"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also enable it globally on your machine just create a `~/.gradle/init.gradle` file with this content
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
initscript {
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
mavenCentral()
|
||||||
|
jcenter()
|
||||||
|
}
|
||||||
|
dependencies {
|
||||||
|
classpath "net.woggioni.plugins:jpms-check:0.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
apply plugin: net.woggioni.plugins.jpms.check.JPMSCheckPlugin
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
this means that the plugin will be automatically enabled for all your Gradle projects,
|
||||||
|
note that it doesn't alter the build process in any way except for creating the
|
||||||
|
`jpms-check` task, so it is relatively safe to do so.
|
||||||
|
|
||||||
|
## Configure the plugin
|
||||||
|
|
||||||
|
The `jpms-check` will by default create a `jpms-report.html` file in the build directory of the Gradle project
|
||||||
|
where it is invoked. that is a fully self contained HTML document that can easily be opened wit ha web browser without
|
||||||
|
the need of a web server. The behaviour can be customized using some Gradle properties.
|
||||||
|
|
||||||
|
### Parameters description
|
||||||
|
|
||||||
|
- `jpms-check.outputFormat` will change the format of the generated report,
|
||||||
|
at the moment only `html` and `json` are supported (the json report contains the same data of the html report,
|
||||||
|
but instead of an HTML table, it generates an array of JSON objects containing the same data).
|
||||||
|
|
||||||
|
- `jpms-check.outputFile` will set the path and the name of the generated output file.
|
||||||
|
The default is a file in the build directory named `jpms-report.html` if the `html` format is chosen or
|
||||||
|
`jpms-report.json` otherwise.
|
||||||
|
|
||||||
|
- `jpms-check.recursive` if this property is set to `true` the plugin will recursively analyze all the subprojects
|
||||||
|
of the project where it is invoked and merge the results in a single report file (the default value of this property is
|
||||||
|
`false`)
|
||||||
|
|
||||||
|
- `jpms-check.configurationName` the name of the Gradle configuration whose dependencies will be analyzed,
|
||||||
|
it defaults to the `default` configuration.
|
||||||
|
|
||||||
|
|
||||||
|
#### Example usage
|
||||||
|
Call the `jpms-check` task to analyze all of the dependencies in the current project and all of its
|
||||||
|
subproject and create a JSON report with the result in `/tmp/report.json`
|
||||||
|
```bash
|
||||||
|
gradle -Pjpms-check.recursive=true -Pjpms-check.outputFormat=json -Pjpms-check.outputFile=/tmp/report.json jpms-check
|
||||||
|
```
|
||||||
16
jpms-check/build.gradle.kts
Normal file
16
jpms-check/build.gradle.kts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
plugins {
|
||||||
|
`java-gradle-plugin`
|
||||||
|
`maven-publish`
|
||||||
|
groovy
|
||||||
|
id("com.gradle.plugin-publish")
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
}
|
||||||
|
|
||||||
|
gradlePlugin {
|
||||||
|
val dependencyExportPlugin by plugins.creating {
|
||||||
|
id = "net.woggioni.plugins.jpms-check"
|
||||||
|
implementationClass = "net.woggioni.plugins.jpms.check.JPMSCheckPlugin"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,203 @@
|
|||||||
|
package net.woggioni.plugins.jpms.check
|
||||||
|
|
||||||
|
import groovy.json.JsonBuilder
|
||||||
|
import groovy.transform.Canonical
|
||||||
|
import groovy.xml.MarkupBuilder
|
||||||
|
import org.gradle.api.GradleException
|
||||||
|
import org.gradle.api.Plugin
|
||||||
|
import org.gradle.api.Project
|
||||||
|
import org.gradle.api.artifacts.Configuration
|
||||||
|
import org.gradle.api.artifacts.result.ResolvedArtifactResult
|
||||||
|
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.nio.file.Paths
|
||||||
|
import java.util.jar.JarFile
|
||||||
|
import java.util.stream.Collectors
|
||||||
|
import java.util.zip.ZipFile
|
||||||
|
import java.util.stream.Stream
|
||||||
|
|
||||||
|
class JPMSCheckPlugin implements Plugin<Project> {
|
||||||
|
|
||||||
|
@Canonical
|
||||||
|
private class CheckResult {
|
||||||
|
ResolvedArtifactResult dep
|
||||||
|
String automaticModuleName
|
||||||
|
boolean multiReleaseJar
|
||||||
|
boolean moduleInfo
|
||||||
|
|
||||||
|
boolean getJpmsFriendly() {
|
||||||
|
return automaticModuleName != null || moduleInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean equals(Object other) {
|
||||||
|
if(other == null) {
|
||||||
|
return false
|
||||||
|
} else if(other.class != CheckResult.class) {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return dep?.id?.componentIdentifier == other.dep?.id?.componentIdentifier
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
int hashCode() {
|
||||||
|
return dep.id.componentIdentifier.hashCode()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Stream<CheckResult> computeResults(Stream<ResolvedArtifactResult> artifacts) {
|
||||||
|
return artifacts.filter { ResolvedArtifactResult res ->
|
||||||
|
res.file.exists() && res.file.name.endsWith(".jar")
|
||||||
|
}.map { resolvedArtifact ->
|
||||||
|
JarFile jarFile = new JarFile(resolvedArtifact.file).with {
|
||||||
|
if (it.isMultiRelease()) {
|
||||||
|
new JarFile(
|
||||||
|
resolvedArtifact.file,
|
||||||
|
false,
|
||||||
|
ZipFile.OPEN_READ,
|
||||||
|
Runtime.version()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
String automaticModuleName = jarFile.manifest?.with {it.mainAttributes.getValue("Automatic-Module-Name") }
|
||||||
|
def moduleInfoEntry = jarFile.getJarEntry("module-info.class")
|
||||||
|
new CheckResult(
|
||||||
|
dep: resolvedArtifact,
|
||||||
|
moduleInfo: moduleInfoEntry != null,
|
||||||
|
automaticModuleName: automaticModuleName,
|
||||||
|
multiReleaseJar: jarFile.isMultiRelease()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createHtmlReport(Project project, Stream<CheckResult> checkResults, Writer writer) {
|
||||||
|
def builder = new MarkupBuilder(writer)
|
||||||
|
int friendly = 0
|
||||||
|
int total = 0
|
||||||
|
def results = checkResults.peek { CheckResult res ->
|
||||||
|
total += 1
|
||||||
|
if(res.jpmsFriendly) friendly += 1
|
||||||
|
}.collect(Collectors.toList())
|
||||||
|
builder.html {
|
||||||
|
head {
|
||||||
|
meta name: "viewport", content: "width=device-width, initial-scale=1"
|
||||||
|
getClass().classLoader.getResourceAsStream('net/woggioni/plugins/jpms/check/github-markdown.css').withReader { Reader reader ->
|
||||||
|
style reader.text
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
article(class: 'markdown-body') {
|
||||||
|
h1 "Project ${project.group}:${project.name}:${project.version}", style: "text-align: center;"
|
||||||
|
div {
|
||||||
|
table {
|
||||||
|
thead {
|
||||||
|
tr {
|
||||||
|
th "JPMS friendly"
|
||||||
|
th "Not JPMS friendly", colspan: 2
|
||||||
|
th "Total", colspan: 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tbody {
|
||||||
|
tr {
|
||||||
|
td friendly, style: "text-align: center;"
|
||||||
|
td total - friendly, style: "text-align: center;", colspan: 2
|
||||||
|
td total, style: "text-align: center;", colspan: 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
thead {
|
||||||
|
th "Name"
|
||||||
|
th "Multi-release jar"
|
||||||
|
th "Automatic-Module-Name"
|
||||||
|
th "Module descriptor"
|
||||||
|
th "JPMS friendly"
|
||||||
|
}
|
||||||
|
tbody {
|
||||||
|
results.forEach {res ->
|
||||||
|
String color = res.jpmsFriendly ? "#dfd" : "fdd"
|
||||||
|
tr(style: "background-color:$color;") {
|
||||||
|
td res.dep.id.displayName
|
||||||
|
td style: "text-align: center;", res.multiReleaseJar ? "✓" : "✕"
|
||||||
|
td style: "text-align: center;", res.automaticModuleName ?: "n/a"
|
||||||
|
td style: "text-align: center;", res.moduleInfo ? "✓" : "✕"
|
||||||
|
td style: "text-align: center;", res.jpmsFriendly ? "✓" : "✕"
|
||||||
|
}
|
||||||
|
total += 1
|
||||||
|
if(res.jpmsFriendly) friendly += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private createJsonReport(Stream<CheckResult> checkResults, Writer writer) {
|
||||||
|
def builder = new JsonBuilder()
|
||||||
|
builder (checkResults.map {
|
||||||
|
[
|
||||||
|
name: it.dep.id.componentIdentifier.displayName,
|
||||||
|
automaticModuleName: it.automaticModuleName,
|
||||||
|
isMultiReleaseJar: it.multiReleaseJar,
|
||||||
|
hasModuleInfo: it.moduleInfo,
|
||||||
|
jpmsFriendly: it.jpmsFriendly
|
||||||
|
]
|
||||||
|
}.collect(Collectors.toList()))
|
||||||
|
builder.writeTo(writer)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void apply(Project project) {
|
||||||
|
project.tasks.register("jpms-check") {task ->
|
||||||
|
boolean recursive = project.properties["jpms-check.recursive"]?.with(Boolean.&parseBoolean) ?: false
|
||||||
|
String cfgName = project.properties["jpms-check.configurationName"] ?: "default"
|
||||||
|
String outputFormat = project.properties["jpms-check.outputFormat"] ?: "html"
|
||||||
|
Path outputFile
|
||||||
|
switch(outputFormat) {
|
||||||
|
case "html":
|
||||||
|
outputFile = Paths.get(project.properties["jpms-check.outputFile"]) ?: Paths.get(project.buildDir.path, "jpms-report.html")
|
||||||
|
break
|
||||||
|
case "json":
|
||||||
|
outputFile = Paths.get(project.properties["jpms-check.outputFile"]) ?: Paths.get(project.buildDir.path, "jpms-report.json")
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Unsupported output format: $outputFormat")
|
||||||
|
}
|
||||||
|
doLast {
|
||||||
|
Set<CheckResult> results = (recursive ? project.subprojects.stream() : Stream.of(project)).flatMap {
|
||||||
|
Configuration requestedConfiguration = project.configurations.find { Configuration cfg ->
|
||||||
|
cfg.canBeResolved && cfg.name == cfgName
|
||||||
|
} ?: ({
|
||||||
|
def resolvableConfigurations = "[" + project.configurations
|
||||||
|
.grep { Configuration cfg -> cfg.canBeResolved }
|
||||||
|
.collect { "'${it.name}'" }
|
||||||
|
.join(",") + "]"
|
||||||
|
throw new GradleException("Configuration '$cfgName' doesn't exist or cannot be resolved, " +
|
||||||
|
"resolvable configurations in this project are " + resolvableConfigurations)
|
||||||
|
} as Configuration)
|
||||||
|
computeResults(requestedConfiguration.incoming.artifacts.artifacts.stream())
|
||||||
|
}.collect(Collectors.toSet())
|
||||||
|
Files.createDirectories(outputFile.parent)
|
||||||
|
Files.newBufferedWriter(outputFile).withWriter {
|
||||||
|
Stream<CheckResult> resultStream = results.stream().sorted(Comparator.comparing { CheckResult res ->
|
||||||
|
res.dep.id.componentIdentifier.displayName
|
||||||
|
})
|
||||||
|
switch(outputFormat) {
|
||||||
|
case "html":
|
||||||
|
createHtmlReport(project, resultStream, it)
|
||||||
|
break
|
||||||
|
case "json":
|
||||||
|
createJsonReport(resultStream, it)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Unsupported output format: $outputFormat")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,710 @@
|
|||||||
|
@font-face {
|
||||||
|
font-family: octicons-link;
|
||||||
|
src: url(data:font/woff;charset=utf-8;base64,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) format('woff');
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body {
|
||||||
|
-ms-text-size-adjust: 100%;
|
||||||
|
-webkit-text-size-adjust: 100%;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: #24292e;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.5;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-c {
|
||||||
|
color: #6a737d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-c1,
|
||||||
|
.markdown-body .pl-s .pl-v {
|
||||||
|
color: #005cc5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-e,
|
||||||
|
.markdown-body .pl-en {
|
||||||
|
color: #6f42c1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-smi,
|
||||||
|
.markdown-body .pl-s .pl-s1 {
|
||||||
|
color: #24292e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-ent {
|
||||||
|
color: #22863a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-k {
|
||||||
|
color: #d73a49;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-s,
|
||||||
|
.markdown-body .pl-pds,
|
||||||
|
.markdown-body .pl-s .pl-pse .pl-s1,
|
||||||
|
.markdown-body .pl-sr,
|
||||||
|
.markdown-body .pl-sr .pl-cce,
|
||||||
|
.markdown-body .pl-sr .pl-sre,
|
||||||
|
.markdown-body .pl-sr .pl-sra {
|
||||||
|
color: #032f62;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-v,
|
||||||
|
.markdown-body .pl-smw {
|
||||||
|
color: #e36209;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-bu {
|
||||||
|
color: #b31d28;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-ii {
|
||||||
|
color: #fafbfc;
|
||||||
|
background-color: #b31d28;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-c2 {
|
||||||
|
color: #fafbfc;
|
||||||
|
background-color: #d73a49;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-c2::before {
|
||||||
|
content: "^M";
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-sr .pl-cce {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #22863a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-ml {
|
||||||
|
color: #735c0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-mh,
|
||||||
|
.markdown-body .pl-mh .pl-en,
|
||||||
|
.markdown-body .pl-ms {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #005cc5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-mi {
|
||||||
|
font-style: italic;
|
||||||
|
color: #24292e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-mb {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #24292e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-md {
|
||||||
|
color: #b31d28;
|
||||||
|
background-color: #ffeef0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-mi1 {
|
||||||
|
color: #22863a;
|
||||||
|
background-color: #f0fff4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-mc {
|
||||||
|
color: #e36209;
|
||||||
|
background-color: #ffebda;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-mi2 {
|
||||||
|
color: #f6f8fa;
|
||||||
|
background-color: #005cc5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-mdr {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #6f42c1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-ba {
|
||||||
|
color: #586069;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-sg {
|
||||||
|
color: #959da5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-corl {
|
||||||
|
text-decoration: underline;
|
||||||
|
color: #032f62;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .octicon {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: text-top;
|
||||||
|
fill: currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body a {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body a:active,
|
||||||
|
.markdown-body a:hover {
|
||||||
|
outline-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body strong {
|
||||||
|
font-weight: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body strong {
|
||||||
|
font-weight: bolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
margin: 0.67em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body img {
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body code,
|
||||||
|
.markdown-body kbd,
|
||||||
|
.markdown-body pre {
|
||||||
|
font-family: monospace, monospace;
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body hr {
|
||||||
|
box-sizing: content-box;
|
||||||
|
height: 0;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body input {
|
||||||
|
font: inherit;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body input {
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body [type="checkbox"] {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body * {
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body input {
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body a {
|
||||||
|
color: #0366d6;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body strong {
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body hr {
|
||||||
|
height: 0;
|
||||||
|
margin: 15px 0;
|
||||||
|
overflow: hidden;
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
border-bottom: 1px solid #dfe2e5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body hr::before {
|
||||||
|
display: table;
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body hr::after {
|
||||||
|
display: table;
|
||||||
|
clear: both;
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body table {
|
||||||
|
border-spacing: 0;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body td,
|
||||||
|
.markdown-body th {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body h1,
|
||||||
|
.markdown-body h2,
|
||||||
|
.markdown-body h3,
|
||||||
|
.markdown-body h4,
|
||||||
|
.markdown-body h5,
|
||||||
|
.markdown-body h6 {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body h1 {
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body h2 {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body h3 {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body h4 {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body h5 {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body h6 {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body p {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body blockquote {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body ul,
|
||||||
|
.markdown-body ol {
|
||||||
|
padding-left: 0;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body ol ol,
|
||||||
|
.markdown-body ul ol {
|
||||||
|
list-style-type: lower-roman;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body ul ul ol,
|
||||||
|
.markdown-body ul ol ol,
|
||||||
|
.markdown-body ol ul ol,
|
||||||
|
.markdown-body ol ol ol {
|
||||||
|
list-style-type: lower-alpha;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body dd {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body code {
|
||||||
|
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body pre {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .octicon {
|
||||||
|
vertical-align: text-bottom;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-0 {
|
||||||
|
padding-left: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-1 {
|
||||||
|
padding-left: 4px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-2 {
|
||||||
|
padding-left: 8px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-3 {
|
||||||
|
padding-left: 16px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-4 {
|
||||||
|
padding-left: 24px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-5 {
|
||||||
|
padding-left: 32px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .pl-6 {
|
||||||
|
padding-left: 40px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body::before {
|
||||||
|
display: table;
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body::after {
|
||||||
|
display: table;
|
||||||
|
clear: both;
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body>*:first-child {
|
||||||
|
margin-top: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body>*:last-child {
|
||||||
|
margin-bottom: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body a:not([href]) {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .anchor {
|
||||||
|
float: left;
|
||||||
|
padding-right: 4px;
|
||||||
|
margin-left: -20px;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .anchor:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body p,
|
||||||
|
.markdown-body blockquote,
|
||||||
|
.markdown-body ul,
|
||||||
|
.markdown-body ol,
|
||||||
|
.markdown-body dl,
|
||||||
|
.markdown-body table,
|
||||||
|
.markdown-body pre {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body hr {
|
||||||
|
height: 0.25em;
|
||||||
|
padding: 0;
|
||||||
|
margin: 24px 0;
|
||||||
|
background-color: #e1e4e8;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body blockquote {
|
||||||
|
padding: 0 1em;
|
||||||
|
color: #6a737d;
|
||||||
|
border-left: 0.25em solid #dfe2e5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body blockquote>:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body blockquote>:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body kbd {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 3px 5px;
|
||||||
|
font-size: 11px;
|
||||||
|
line-height: 10px;
|
||||||
|
color: #444d56;
|
||||||
|
vertical-align: middle;
|
||||||
|
background-color: #fafbfc;
|
||||||
|
border: solid 1px #c6cbd1;
|
||||||
|
border-bottom-color: #959da5;
|
||||||
|
border-radius: 3px;
|
||||||
|
box-shadow: inset 0 -1px 0 #959da5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body h1,
|
||||||
|
.markdown-body h2,
|
||||||
|
.markdown-body h3,
|
||||||
|
.markdown-body h4,
|
||||||
|
.markdown-body h5,
|
||||||
|
.markdown-body h6 {
|
||||||
|
margin-top: 24px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.25;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body h1 .octicon-link,
|
||||||
|
.markdown-body h2 .octicon-link,
|
||||||
|
.markdown-body h3 .octicon-link,
|
||||||
|
.markdown-body h4 .octicon-link,
|
||||||
|
.markdown-body h5 .octicon-link,
|
||||||
|
.markdown-body h6 .octicon-link {
|
||||||
|
color: #1b1f23;
|
||||||
|
vertical-align: middle;
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body h1:hover .anchor,
|
||||||
|
.markdown-body h2:hover .anchor,
|
||||||
|
.markdown-body h3:hover .anchor,
|
||||||
|
.markdown-body h4:hover .anchor,
|
||||||
|
.markdown-body h5:hover .anchor,
|
||||||
|
.markdown-body h6:hover .anchor {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body h1:hover .anchor .octicon-link,
|
||||||
|
.markdown-body h2:hover .anchor .octicon-link,
|
||||||
|
.markdown-body h3:hover .anchor .octicon-link,
|
||||||
|
.markdown-body h4:hover .anchor .octicon-link,
|
||||||
|
.markdown-body h5:hover .anchor .octicon-link,
|
||||||
|
.markdown-body h6:hover .anchor .octicon-link {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body h1 {
|
||||||
|
padding-bottom: 0.3em;
|
||||||
|
font-size: 2em;
|
||||||
|
border-bottom: 1px solid #eaecef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body h2 {
|
||||||
|
padding-bottom: 0.3em;
|
||||||
|
font-size: 1.5em;
|
||||||
|
border-bottom: 1px solid #eaecef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body h3 {
|
||||||
|
font-size: 1.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body h4 {
|
||||||
|
font-size: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body h5 {
|
||||||
|
font-size: 0.875em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body h6 {
|
||||||
|
font-size: 0.85em;
|
||||||
|
color: #6a737d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body ul,
|
||||||
|
.markdown-body ol {
|
||||||
|
padding-left: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body ul ul,
|
||||||
|
.markdown-body ul ol,
|
||||||
|
.markdown-body ol ol,
|
||||||
|
.markdown-body ol ul {
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body li {
|
||||||
|
word-wrap: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body li>p {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body li+li {
|
||||||
|
margin-top: 0.25em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body dl {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body dl dt {
|
||||||
|
padding: 0;
|
||||||
|
margin-top: 16px;
|
||||||
|
font-size: 1em;
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body dl dd {
|
||||||
|
padding: 0 16px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body table {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body table th {
|
||||||
|
font-weight: 600;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body table th,
|
||||||
|
.markdown-body table td {
|
||||||
|
padding: 6px 13px;
|
||||||
|
border: 1px solid #dfe2e5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body table tr {
|
||||||
|
background-color: #fff;
|
||||||
|
border-top: 1px solid #c6cbd1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body table tr:nth-child(2n) {
|
||||||
|
background-color: #f6f8fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body img {
|
||||||
|
max-width: 100%;
|
||||||
|
box-sizing: content-box;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body img[align=right] {
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body img[align=left] {
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body code {
|
||||||
|
padding: 0.2em 0.4em;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 85%;
|
||||||
|
background-color: rgba(27,31,35,0.05);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body pre {
|
||||||
|
word-wrap: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body pre>code {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
font-size: 100%;
|
||||||
|
word-break: normal;
|
||||||
|
white-space: pre;
|
||||||
|
background: transparent;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .highlight {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .highlight pre {
|
||||||
|
margin-bottom: 0;
|
||||||
|
word-break: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .highlight pre,
|
||||||
|
.markdown-body pre {
|
||||||
|
padding: 16px;
|
||||||
|
overflow: auto;
|
||||||
|
font-size: 85%;
|
||||||
|
line-height: 1.45;
|
||||||
|
background-color: #f6f8fa;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body pre code {
|
||||||
|
display: inline;
|
||||||
|
max-width: auto;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
overflow: visible;
|
||||||
|
line-height: inherit;
|
||||||
|
word-wrap: normal;
|
||||||
|
background-color: transparent;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .full-commit .btn-outline:not(:disabled):hover {
|
||||||
|
color: #005cc5;
|
||||||
|
border-color: #005cc5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body kbd {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 3px 5px;
|
||||||
|
font: 11px "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||||
|
line-height: 10px;
|
||||||
|
color: #444d56;
|
||||||
|
vertical-align: middle;
|
||||||
|
background-color: #fafbfc;
|
||||||
|
border: solid 1px #d1d5da;
|
||||||
|
border-bottom-color: #c6cbd1;
|
||||||
|
border-radius: 3px;
|
||||||
|
box-shadow: inset 0 -1px 0 #c6cbd1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body :checked+.radio-label {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
border-color: #0366d6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .task-list-item {
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .task-list-item+.task-list-item {
|
||||||
|
margin-top: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body .task-list-item input {
|
||||||
|
margin: 0 0.2em 0.25em -1.6em;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body hr {
|
||||||
|
border-bottom-color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body {
|
||||||
|
box-sizing: border-box;
|
||||||
|
min-width: 200px;
|
||||||
|
max-width: 1920px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.markdown-body {
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,4 +7,6 @@
|
|||||||
* in the user manual at https://docs.gradle.org/6.1.1/userguide/multi_project_builds.html
|
* in the user manual at https://docs.gradle.org/6.1.1/userguide/multi_project_builds.html
|
||||||
*/
|
*/
|
||||||
|
|
||||||
rootProject.name = "dependency-export"
|
rootProject.name = "my-gradle-plugins"
|
||||||
|
include("dependency-export")
|
||||||
|
include("jpms-check")
|
||||||
Reference in New Issue
Block a user