diff --git a/.evergreen/publish.sh b/.evergreen/publish.sh index bfecc0ae865..e3f9f365d42 100755 --- a/.evergreen/publish.sh +++ b/.evergreen/publish.sh @@ -26,6 +26,6 @@ fi SYSTEM_PROPERTIES="-Dorg.gradle.internal.publish.checksums.insecure=true -Dorg.gradle.internal.http.connectionTimeout=120000 -Dorg.gradle.internal.http.socketTimeout=120000" ./gradlew -version -./gradlew ${SYSTEM_PROPERTIES} --stacktrace --info ${TASK} +./gradlew ${SYSTEM_PROPERTIES} --stacktrace --info ${TASK} # Scala 2.13 is published as result of this gradle execution. ./gradlew ${SYSTEM_PROPERTIES} --stacktrace --info :bson-scala:${TASK} :driver-scala:${TASK} -PdefaultScalaVersions=2.12.12 ./gradlew ${SYSTEM_PROPERTIES} --stacktrace --info :bson-scala:${TASK} :driver-scala:${TASK} -PdefaultScalaVersions=2.11.12 diff --git a/bom/build.gradle.kts b/bom/build.gradle.kts new file mode 100644 index 00000000000..5d1fb81c384 --- /dev/null +++ b/bom/build.gradle.kts @@ -0,0 +1,23 @@ +group = "org.mongodb" +description = "This Bill of Materials POM simplifies dependency management when referencing multiple" + + " MongoDB Java Driver artifacts in projects using Gradle or Maven." + +dependencies { + constraints { + api(project(":mongodb-crypt")) + api(project(":driver-core")) + api(project(":bson")) + api(project(":bson-record-codec")) + + api(project(":driver-sync")) + api(project(":driver-reactive-streams")) + + api(project(":bson-kotlin")) + api(project(":bson-kotlinx")) + api(project(":driver-kotlin-coroutine")) + api(project(":driver-kotlin-sync")) + + api(project(":bson-scala")) + api(project(":driver-scala")) + } +} diff --git a/build.gradle b/build.gradle index 26734755984..4715dfb27ff 100644 --- a/build.gradle +++ b/build.gradle @@ -65,9 +65,10 @@ ext { def configDir = ext.configDir def utilProjects = project(":util").allprojects +def bomProjects = project(":bom") def coreProjects = subprojects - utilProjects -def scalaProjects = subprojects.findAll { it.name.contains('scala') } -def javaProjects = subprojects - scalaProjects +def scalaProjects = subprojects.findAll { it.name.contains('scala') } - bomProjects +def javaProjects = subprojects - scalaProjects - bomProjects def javaMainProjects = javaProjects - utilProjects def javaCodeCheckedProjects = javaMainProjects.findAll { !['driver-benchmarks', 'driver-workload-executor', 'driver-lambda'].contains(it.name) } def javaAndScalaTestedProjects = javaCodeCheckedProjects + scalaProjects diff --git a/gradle.properties b/gradle.properties index 12f1750c442..d3514e32f68 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,6 +16,7 @@ org.gradle.daemon=true org.gradle.jvmargs=-Duser.country=US -Duser.language=en +## NOTE: This property is also used to generate scala compile versions in BOM. scalaVersions=2.11.12,2.12.20,2.13.15 defaultScalaVersions=2.13.15 runOnceTasks=clean,release diff --git a/gradle/publish.gradle b/gradle/publish.gradle index 9add25f9261..fa56f09f138 100644 --- a/gradle/publish.gradle +++ b/gradle/publish.gradle @@ -76,9 +76,10 @@ ext { def projectNamesNotToBePublished = ["driver-benchmarks", "driver-lambda", "driver-workload-executor", "graalvm-native-image-app", "util", "spock", "taglets"] def publishedProjects = subprojects.findAll { !projectNamesNotToBePublished.contains(it.name) } -def scalaProjects = publishedProjects.findAll { it.name.contains('scala') } -def javaProjects = publishedProjects - scalaProjects -def projectsWithManifest = publishedProjects.findAll {it.name != 'driver-legacy' } +def bomProjects = project(":bom") +def scalaProjects = publishedProjects.findAll { it.name.contains('scala') } - bomProjects +def javaProjects = publishedProjects - scalaProjects - bomProjects +def projectsWithManifest = publishedProjects.findAll {it.name != 'driver-legacy' } - bomProjects configure(javaProjects) { project -> apply plugin: 'maven-publish' @@ -169,3 +170,98 @@ configure(projectsWithManifest) { project -> jar configureJarManifestAttributes(project) } } + +configure(bomProjects) { project -> + apply plugin: 'maven-publish' + apply plugin: 'signing' + apply plugin: 'java-platform' + + // Get the Scala versions from the project property. Only major.minor versions. + def scalaVersions = project.findProperty("scalaVersions")?.split(",") + ?.collect { it.split("\\.")[0] + "." + it.split("\\.")[1] } + + assert scalaVersions != null && !scalaVersions.isEmpty() : "Scala versions must be provided as a comma-separated list" + + " in the 'scalaVersions' project property" + + publishing { + publications { + mavenJava(MavenPublication) { + artifactId = "bom".equals(project.archivesBaseName) ? "mongodb-driver-bom" : project.archivesBaseName + from components.javaPlatform + + // Modify the generated POM to add multiple compile versions of driver-scala or bson-scala. + // Scala multi-version support generates only one for BOM. + pom.withXml { + def pomXml = asNode() + + def dependencyManagementNode = pomXml.get("dependencyManagement")?.getAt(0) + assert dependencyManagementNode : " node not found in the generated BOM POM" + + def dependenciesNode = dependencyManagementNode.get("dependencies")?.getAt(0) + assert dependenciesNode : " node not found inside " + + // Check if scala dependencies are present in the BOM. + def existingScalaDeps = dependenciesNode.children().findAll { + it.artifactId.text().contains("scala") + } + + existingScalaDeps.each { existingDep -> + String groupId = existingDep.groupId.text() + String originalArtifactId = existingDep.artifactId.text() + String artifactVersion = existingDep.version.text() + + // Add multiple versions with Scala suffixes for each Scala-related dependency. + scalaVersions.each { scalaVersion -> + // Remove existing Scala version suffix (_2.12, _2.13, etc.) + String baseArtifactId = originalArtifactId.replaceAll("_\\d+\\.\\d+(\\.\\d+)?\$", "") + String newArtifactId = "${baseArtifactId}_${scalaVersion}" + + // Skip if Scala dependency with this scalaVersion already exists in BOM. + if(newArtifactId != originalArtifactId) { + def dependencyNode = dependenciesNode.appendNode("dependency") + dependencyNode.appendNode("groupId", groupId) + dependencyNode.appendNode("artifactId", newArtifactId) + dependencyNode.appendNode("version", artifactVersion) + } + } + } + } + } + } + + repositories configureMavenRepositories(project) + } + + afterEvaluate { + publishing.publications.mavenJava.pom configurePom(project) + signing { + useInMemoryPgpKeys(findProperty("signingKey"), findProperty("signingPassword")) + sign publishing.publications.mavenJava + } + } + + tasks.withType(GenerateModuleMetadata) { + enabled = false + } + + tasks.withType(GenerateMavenPom).configureEach { + doLast { + def xml = file(destination).text + def root = new groovy.xml.XmlSlurper().parseText(xml) + + def dependencies = root.dependencyManagement.dependencies.children() + assert dependencies.children().size() > 1 : "BOM must contain more then one element:\n$destination" + + dependencies.each { dependency -> + def groupId = dependency.groupId.text() + assert groupId.startsWith('org.mongodb') : "BOM must contain only 'org.mongodb' dependencies, but found '$groupId':\n$destination" + /* The and tags should be omitted in BOM dependencies. + This ensures that consuming projects have the flexibility to decide whether a + dependency is optional in their context. The BOM's role is to provide version information, + not to dictate inclusion or exclusion of dependencies. */ + assert dependency.scope.size() == 0 : "BOM must not contain elements in dependency:\n$destination" + assert dependency.optional.size() == 0 : "BOM must not contain elements in dependency:\n$destination" + } + } + } +} diff --git a/settings.gradle b/settings.gradle index c8a32bd7df5..e390791d5d5 100644 --- a/settings.gradle +++ b/settings.gradle @@ -33,6 +33,7 @@ include ':driver-scala' include ':mongodb-crypt' include 'util:spock' include 'util:taglets' +include ':bom' if(hasProperty("includeGraalvm")) { include ':graalvm-native-image-app'