Skip to content

Commit

Permalink
Initial work to modernize the Gradle build (micronaut-projects#6067)
Browse files Browse the repository at this point in the history
* Remove unused variables

* Introduce a version catalog

This commit replaces the "dependencyVersion" and "dependencyModuleVersion"
calls with Gradle's version catalogs. In the process, I tried to clarify
what belongs to the BOM (as "managed" dependencies") vs what is not.

Currently this breaks BOM verification module, this will be fixed in
another commit. It will also probably break the automatic upgrading of
dependencies. However, the result is much cleaner: there are no duplicate
coordinates anymore in build files.

* Publish a version catalog alongside the BOM

This commit introduces a new artifact published alongside the BOM:
a Gradle _version catalog_. A version catalog is not a replacement
for a BOM. It can be seen as an alternative, or a complement to a
BOM file.

For example, a user can import the Micronaut version catalog in
their settings file by doing:

```gradle
dependencyResolutionManagement {
    versionCatalogs {
        create("mn") {
            from("io.micronaut:micronaut-bom:3.0.1-SNAPSHOT")
        }
    }
}
```

Gradle will then generate _version accessors_, which allows
replacing dependency notations from:

```gradle
implementation "ch.qos.logback:logback-classic"
```

to:

```gradle
implementation mn.logback
```

Those accessors are _type safe_. Version catalogs should
really be seen as "recommendations". Without applying a
BOM in addition, the versions declared in a catalog have
_no impact_ on dependency resolution. Therefore, it is
often recommended to apply both the BOM _and_ use catalogs
to declare dependencies.

It comes with an advantage, which is that versions declared
in the BOM can be overridden by the user:

```gradle
dependencyResolutionManagement {
    versionCatalogs {
        create("mn") {
            from("io.micronaut:micronaut-bom:3.0.1-SNAPSHOT")
            version("snakeyaml") {
                strictly("1.28")
            }
        }
    }
}
```

* Workaround JSON ordering issue under JDK 15 on CI

* Integrate Jackson to the BOM

It looks like it was a mistake that it wasn't present in the BOM already.
  • Loading branch information
melix authored Sep 8, 2021
1 parent 5266c37 commit de34e4d
Show file tree
Hide file tree
Showing 52 changed files with 1,320 additions and 1,311 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ target/
.gradle/
.idea/
build/
!buildSrc/src/main/groovy/io/micronaut/build/
classes/
out/
*.db
Expand Down
4 changes: 2 additions & 2 deletions aop/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ dependencies {
api project(':inject')
api project(':core')
compileOnly project(':core-reactive')
compileOnly "org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinCoroutinesVersion"
compileOnly libs.kotlinx.coroutines.core
}

compileKotlin {
kotlinOptions.jvmTarget = "1.8"
kotlinOptions.languageVersion = "1.3"
}
}
7 changes: 3 additions & 4 deletions benchmarks/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ plugins {
dependencies {
annotationProcessor project(":inject-java")
jmhAnnotationProcessor project(":inject-java")
jmhAnnotationProcessor "org.ow2.asm:asm:$asmVersion"
jmhAnnotationProcessor "org.ow2.asm:asm-commons:$asmVersion"
jmhAnnotationProcessor libs.bundles.asm

annotationProcessor project(":validation")
compileOnly project(":validation")
Expand All @@ -17,8 +16,8 @@ dependencies {
api project(":router")
api project(":runtime")

jmh 'org.openjdk.jmh:jmh-core:1.29'
jmh 'org.openjdk.jmh:jmh-generator-annprocess:1.29'
jmh libs.jmh
jmh libs.jmh.generator.annprocess
}
jmh {
includes = ['io.micronaut.http.server.StartupBenchmark']
Expand Down
90 changes: 5 additions & 85 deletions bom-check/build.gradle
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import groovy.xml.XmlSlurper

plugins {
id 'java'
id 'java-library'
id 'io.micronaut.build.internal.bom-checker'
}

repositories {
Expand All @@ -11,86 +8,9 @@ repositories {

boolean micronautSnapshot = rootProject.version.toString().endsWith("-SNAPSHOT")

// checks all the BOMs are resolvable from central
task("checkBom") {
doLast {
// verify BOMs
def centralURL = repositories.findByName("MavenRepo").url
List<String> errors = []
for (dep in bomVersions) {
def info = dep.value
def versionExpr = info.version

def pom = new groovy.xml.XmlSlurper()
.parse("$centralURL${info.group.replace('.', '/')}/${info.name}/$versionExpr/${info.name}-${versionExpr}.pom")
if (!info.group.startsWith("io.micronaut")) {
pom.dependencyManagement.dependencies.dependency.each {

String groupId = it.groupId.text()
groupId = groupId.replace('${project.groupId}', info.group)

if (!groupId.startsWith(info.group)) {
errors << "Error validating BOM [${info.name}]: includes the dependency [${it.groupId}:${it.artifactId}:${it.version}] that doesn't belong to the group id of the BOM: [${info.group}]".toString()
}

}
}

}

// verify dependencies
for (dep in dependencyVersions) {
def info = dep.value
// don't include snapshots
if (info.version.toString().endsWith("-SNAPSHOT") && !micronautSnapshot) {
continue
}

def versionExpr = dep.value.version
def moduleName = info.name
if (moduleName) {
validatePom(centralURL, info, moduleName, versionExpr, errors)
}
info.modules?.each {
validatePom(centralURL, info, it, versionExpr, errors)
}

}

if (errors) {
throw new GradleException("Bom Check Failed: " + errors.join("\n"))
}
}
}

check.dependsOn(checkBom)

private validatePom(centralURL, info, moduleName, versionExpr, List errors) {
def pom = null

try {
pom = new XmlSlurper()
.parse("$centralURL${info.group.replace('.', '/')}/${moduleName}/$versionExpr/${moduleName}-${versionExpr}.pom")
} catch (e) {
println "WARNING: Dependency ${moduleName} is not in Maven Central or has an invalid POM: $e.message"
}

// try other repos
if (pom == null) {
for (repo in repositories) {
try {
def url = "${repo.url}${info.group.replace('.', '/')}/${moduleName}/$versionExpr/${moduleName}-${versionExpr}.pom"
pom = new XmlSlurper()
.parse(url)
if (pom != null) {
break
}
} catch (e) {

}
}
}
if (pom == null) {
errors << "Error validating POM for dependency [${moduleName}]: POM Not Found or not Parseable".toString()
tasks.withType(io.micronaut.build.internal.pom.PomChecker).configureEach {
onlyIf {
// We only perform validation on releases
!micronautSnapshot || providers.gradleProperty("force.check.bom").isPresent()
}
}
165 changes: 82 additions & 83 deletions bom/build.gradle
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import io.micronaut.build.internal.pom.VersionCatalogConverter
import org.gradle.api.plugins.catalog.CatalogPluginExtension
import org.gradle.api.plugins.catalog.VersionCatalogPlugin

plugins {
id 'java-platform'
id 'version-catalog'
id 'maven-publish'
id 'io.micronaut.build.internal.publishing'
}

group projectGroupId
version projectVersion


apply plugin: 'maven-publish'

boolean micronautSnapshot = rootProject.version.toString().endsWith("-SNAPSHOT")

def excludedProjects = [
"benchmarks",
"inject-test-utils",
Expand All @@ -20,107 +22,104 @@ def excludedProjects = [
"test-utils"
]

publishing {
afterEvaluate {
publications {
maven(MavenPublication) {
artifactId("micronaut-bom")
from components.javaPlatform

pom.withXml {
def xml = asNode()

xml.children().find {
it.name().localPart == 'packaging'
} + pomInfo
def libsCatalog = project.extensions.findByType(VersionCatalogsExtension).named("libs")

// This map defines the names of the properties found in the POM file
// which cannot be directly inferred from the version name in the catalog.
// This is a backwards compatibility fix.
def legacyVersionNames = [
'jakarta.annotation.api': 'jakarta.annotation-api',
'javax.annotation.api': 'javax.annotation-api',
'methvin.directory.watcher': 'methvin.directory-watcher',
'paho.v3': 'pahov3',
'paho.v5': 'pahov5',
'graal.sdk': 'graalSdk',
'neo4j.java.driver': 'neo4j.bolt',
]

for(dep in dependencyVersions) {
setBomVersion(dep, xml)
}
for(dep in bomVersions) {
setBomVersion(dep, xml)
}
}
}
}
}
String toPropertyName(String alias) {
alias.split("(?=[A-Z])").collect { it.toLowerCase(Locale.US) }.join("-").replace((char)'-', (char)'.')
}

private void setBomVersion(Map.Entry<Object, Object> dep, xml) {
String property = "\${${dep.key}.version}"
def info = dep.value
def version = info.version
def group = info.group
if (group && version) {
def modules = []
if (info.name) {
modules << info.name
}
if (info.modules) {
modules.addAll(info.modules)
}
for (module in modules) {
components.javaPlatform.addVariantsFromConfiguration(
configurations.getByName(VersionCatalogPlugin.VERSION_CATALOG_ELEMENTS)
) { details ->
details.mapToMavenScope("compile")
details.mapToOptional()
}

def pomDep = xml.dependencyManagement.dependencies.dependency.find {
it.artifactId.text() == module &&
it.groupId.text() == group
}
if (pomDep != null) {
pomDep.version.first().setValue(property)
}
}
def modelConverter = new VersionCatalogConverter(
file("../gradle/libs.versions.toml"),
project.extensions.findByType(CatalogPluginExtension)
)

}
tasks.named("generateCatalogAsToml") {
modelConverter.populateModel()
}

ext.extraPomInfo = {
delegate.properties {
for(dep in dependencyVersions) {
"${dep.key}.version"(dep.value.version)
}
for(dep in bomVersions) {
"${dep.key}.version"(dep.value.version)
publishing {
publications {
publication(MavenPublication) {
artifactId("micronaut-bom")
from components.javaPlatform

libsCatalog.versionAliases.each { alias ->
if (alias.startsWith('managed.')) {
libsCatalog.findVersion(alias).ifPresent { version ->
alias = alias - 'managed.'
String baseName = legacyVersionNames[alias] ?: toPropertyName(alias)
String propertyName = "${baseName}.version"
pom.properties.put(propertyName, version.requiredVersion)
}
}
}
}
}
}

javaPlatform {
allowDependencies()
}

dependencies {
for (dep in bomVersions) {
def info = dep.value
def versionExpr = info.version
api platform("$info.group:$info.name:$versionExpr")
libsCatalog.dependencyAliases.each { alias ->
if (alias.startsWith("boms.")) {
api platform(libsCatalog.findDependency(alias).map {
it.get()
}.orElseThrow { new RuntimeException("Unexpected missing alias in catalog") })
}
}

constraints {
for (Project p : rootProject.subprojects) {
if (!p.subprojects.empty) continue
if (p.name.contains("bom")) continue
if (excludedProjects.contains(p.name)) continue

api "$p.group:micronaut-$p.name:$p.version"
}

for (dep in dependencyVersions) {
def info = dep.value
// don't include snapshots
if (info.version.toString().endsWith("-SNAPSHOT") && !micronautSnapshot) {
if (!p.subprojects.empty) {
continue
}
if (p.name.contains("bom")) {
continue
}
if (excludedProjects.contains(p.name)) {
continue
}

def versionExpr = dep.value.version
String moduleGroup = p.group
String moduleName = "micronaut-${p.name}"
String moduleVersion = p.version

if (info.name) {
api "$info.group:$info.name:$versionExpr"
}
if (info.modules) {
for (m in info.modules) {
api "$info.group:$m:$versionExpr"
}
api "$moduleGroup:$moduleName:$moduleVersion"

modelConverter.extraVersions.put(moduleName, moduleVersion)
modelConverter.extraLibraries.put(moduleName, VersionCatalogConverter.library(moduleGroup, moduleName, moduleName))

}

libsCatalog.dependencyAliases.each { alias ->
if (alias.startsWith("managed.")) {
api libsCatalog.findDependency(alias).map {
it.get()
}.orElseThrow { new RuntimeException("Unexpected missing alias in catalog") }
}
}

}
}

apply plugin: "io.micronaut.build.internal.publishing"
4 changes: 2 additions & 2 deletions buffer-netty/build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
dependencies {
api project(":core")
api project(":inject")
api dependencyModuleVersion("netty", "netty-buffer")
api libs.managed.netty.buffer

annotationProcessor project(":inject-java")
}
}
Loading

0 comments on commit de34e4d

Please sign in to comment.