diff --git a/.github/workflows/deps.yml b/.github/workflows/deps.yml index 39ea7afbc4..e28787f630 100644 --- a/.github/workflows/deps.yml +++ b/.github/workflows/deps.yml @@ -6,11 +6,13 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Set up JDK 11 - uses: actions/setup-java@v4 + - name: Set up JDK 17 + uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: '11' + java-version: | + 8 + 17 - name: Build with Gradle (refresh dependencies) run: ./gradlew clean classes testClasses assemble --refresh-dependencies - name: Build sourceJars diff --git a/.github/workflows/docs-tutorials.yml b/.github/workflows/docs-tutorials.yml index 6d30576e55..ed2a099634 100644 --- a/.github/workflows/docs-tutorials.yml +++ b/.github/workflows/docs-tutorials.yml @@ -7,11 +7,13 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Setup Java 8 - uses: actions/setup-java@v4 + - name: Setup Java 8, 17 + uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: '8' + java-version: | + 8 + 17 - name: Cache Gradle packages uses: actions/cache@v4 with: diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 1838956d5a..5658e210af 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -11,11 +11,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Set up JDK 11 - uses: actions/setup-java@v4 + - name: Set up JDK 17 + uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: '11' + java-version: '17' - name: Cache Gradle packages uses: actions/cache@v4 with: diff --git a/.github/workflows/style.yml b/.github/workflows/style.yml index 61a8ac52a3..c726cb1f21 100644 --- a/.github/workflows/style.yml +++ b/.github/workflows/style.yml @@ -6,11 +6,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Set up JDK 11 - uses: actions/setup-java@v4 + - name: Set up JDK 17 + uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: '11' + java-version: '17' - name: Cache Gradle packages uses: actions/cache@v4 with: @@ -20,5 +20,7 @@ jobs: key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} restore-keys: | ${{ runner.os }}-gradle- - - name: Code Style Check with Gradle and Spotless + - name: build-logic Code Style Check with Gradle and Spotless + run: ./gradlew :build-logic:clean :build-logic:spotlessCheck + - name: netCDF-Java Code Style Check with Gradle and Spotless run: ./gradlew clean spotlessCheck diff --git a/.github/workflows/test-native-compression.yml b/.github/workflows/test-native-compression.yml index ce5227a110..f04c18ca96 100644 --- a/.github/workflows/test-native-compression.yml +++ b/.github/workflows/test-native-compression.yml @@ -15,7 +15,6 @@ jobs: ubuntu-24.04, ubuntu-24.04-arm, windows-2022, - windows-11-arm, macos-13, macos-14, macos-15 @@ -24,15 +23,17 @@ jobs: runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - - name: Set up JDK 11 - if: ${{ matrix.os != 'windows-11-arm' }} - uses: actions/setup-java@v4 + - name: Setup JDK 8, 17 + if: ${{ matrix.os == 'ubuntu-24.04' || matrix.os == 'macos-13' }} + uses: actions/setup-java@v5 with: distribution: 'temurin' - java-version: '11' - - name: Set up JDK 21 - if: ${{ matrix.os == 'windows-11-arm' }} - uses: actions/setup-java@v4 + java-version: | + 8 + 17 + - name: Setup JDK 21 + if: ${{ matrix.os != 'ubuntu-24.04' && matrix.os != 'macos-13' }} + uses: actions/setup-java@v5 with: distribution: 'temurin' java-version: '21' @@ -46,7 +47,13 @@ jobs: restore-keys: | ${{ runner.os }}-gradle- - name: Run libaec JNA tests - run: ./gradlew clean :native-compression:libaec-jna:simpleTests + if: ${{ matrix.os == 'ubuntu-24.04' || matrix.os == 'macos-13' }} + run: ./gradlew -Dorg.gradle.java.installations.auto-detect=true clean :libaec-jna:test + env: + JDK8: /usr/thredds-test-environment/temurin8 + - name: Run libaec JNA tests (JDK 21 tests) + if: ${{ matrix.os != 'ubuntu-24.04' && matrix.os != 'macos-13' }} + run: ./gradlew clean :libaec-jna:test21 - uses: actions/upload-artifact@v4 if: failure() with: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3816a2b06f..c4d086fee0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -11,15 +11,21 @@ jobs: java-vendor: ['zulu', 'temurin', 'corretto'] steps: - uses: actions/checkout@v4 - - name: Build and test with Gradle (${{ matrix.java-vendor }} ${{ matrix.java-version }}) + - name: Build with ${{ matrix.java-vendor }} 17 and run tests with ${{ matrix.java-vendor }} ${{ matrix.java-version }} uses: Unidata/thredds-test-action@v3 with: java-vendor: ${{ matrix.java-vendor }} - java-version: ${{ matrix.java-version }} + java-version: 17 build-tool: 'gradlew' - test-command: '--info --stacktrace testAll' + test-command: '-Dorg.gradle.java.installations.fromEnv=JDK8 --info --stacktrace test' + env: + JDK8: /usr/thredds-test-environment/${{ matrix.java-vendor }}8 + - name: Prep for artifact upload + if: failure() + run: rm -rf build-logic/build/tmp - uses: actions/upload-artifact@v4 if: failure() with: name: netCDF-Java_JUnit_Results_${{ github.sha }}_${{ matrix.java-vendor }}-${{ matrix.java-version }} - path: build/reports/allTests + path: | + ${{ github.workspace }}/**/build/reports/tests/**/* diff --git a/.gitignore b/.gitignore index 6ed39aaaae..8a1cb38332 100644 --- a/.gitignore +++ b/.gitignore @@ -32,8 +32,13 @@ bin/ # Ignore the Gradle cache directory. .gradle/ + +# Ignore .kotlin directories +.kotlin/ + # Ignore the output that Gradle generates in the "build/" directories... build/ + # ...but not the source code in the "edu.ucar.build" package inside buildSrc !**/src/**/edu/ucar/build/ diff --git a/bufr/build.gradle b/bufr/build.gradle deleted file mode 100644 index b783878572..0000000000 --- a/bufr/build.gradle +++ /dev/null @@ -1,25 +0,0 @@ -description = 'Reading BUFR files with the NetCDF-java library.' -ext.title = 'BUFR IOSP' - -apply from: "$rootDir/gradle/any/dependencies.gradle" -apply from: "$rootDir/gradle/any/java-library.gradle" -apply from: "$rootDir/gradle/any/protobuf.gradle" - -dependencies { - api enforcedPlatform(project(':netcdf-java-platform')) - testImplementation enforcedPlatform(project(':netcdf-java-testing-platform')) - - api project(':cdm:cdm-core') - - implementation 'org.jdom:jdom2' - implementation 'com.google.code.findbugs:jsr305' - implementation 'com.beust:jcommander' - implementation 'com.google.protobuf:protobuf-java' - implementation 'com.google.re2j:re2j' - implementation 'org.slf4j:slf4j-api' - implementation 'com.google.guava:guava' - - testImplementation project(':cdm-test-utils') - - testImplementation 'com.google.truth:truth' -} diff --git a/bufr/build.gradle.kts b/bufr/build.gradle.kts new file mode 100644 index 0000000000..9e6a35dd87 --- /dev/null +++ b/bufr/build.gradle.kts @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { + id("java-library-conventions") + id("protobuf-conventions") +} + +description = "Reading BUFR files with the NetCDF-java library." + +extra["project.title"] = "BUFR IOSP" + +dependencies { + implementation(platform(project(":netcdf-java-platform"))) + + api(project(":cdm-core")) + + implementation(libs.beust.jcommander) + implementation(libs.findbugs.jsr305) + implementation(libs.guava) + implementation(libs.jdom2) + implementation(libs.protobuf) + implementation(libs.re2j) + implementation(libs.slf4j.api) + + testImplementation(platform(project(":netcdf-java-testing-platform"))) + + testImplementation(project(":cdm-test-utils")) + + testImplementation(libs.google.truth) + + testCompileOnly(libs.junit4) + + testRuntimeOnly(libs.junit5.platformLauncher) + testRuntimeOnly(libs.junit5.vintageEngine) +} diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts new file mode 100644 index 0000000000..4f728bf2a2 --- /dev/null +++ b/build-logic/build.gradle.kts @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { + `kotlin-dsl` + alias(libs.plugins.protobuf) + alias(libs.plugins.spotless) +} + +dependencies { + implementation(plugin(libs.plugins.protobuf)) + implementation(plugin(libs.plugins.spotless)) +} + +spotless { + kotlinGradle { + target("*.gradle.kts", "**/*.gradle.kts") + ktfmt().googleStyle() + } +} + +// Helper function that transforms a plugin alias from the version catalog +// into a valid dependency notation +fun plugin(plugin: Provider) = + plugin.map { "${it.pluginId}:${it.pluginId}.gradle.plugin:${it.version}" } diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts new file mode 100644 index 0000000000..a3c7e8aedf --- /dev/null +++ b/build-logic/settings.gradle.kts @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +dependencyResolutionManagement { + repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS + repositories { + mavenCentral() + gradlePluginPortal() + } + versionCatalogs { create("libs") { from(files("../gradle/libs.versions.toml")) } } +} + +rootProject.name = "build-logic" diff --git a/build-logic/src/main/kotlin/artifact-publishing-conventions.gradle.kts b/build-logic/src/main/kotlin/artifact-publishing-conventions.gradle.kts new file mode 100644 index 0000000000..40e1971144 --- /dev/null +++ b/build-logic/src/main/kotlin/artifact-publishing-conventions.gradle.kts @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { `maven-publish` } + +publishing { + repositories { + maven { + if ((project.extra.get("project.isRelease") as? Boolean ?: false)) { + name = "releases" + url = uri("https://artifacts.unidata.ucar.edu/repository/unidata-releases/") + } else { + name = "snapshots" + url = uri("https://artifacts.unidata.ucar.edu/repository/unidata-snapshots/") + } + credentials { + username = extra.properties["nexus.username"] as? String + password = extra.properties["nexus.password"] as? String + } + } + } +} + +tasks.withType { enabled = false } + +val augmentPom = + tasks.register("augmentPom") { + publishing.publications.filterIsInstance().forEach { pub -> + pub.pom { + name.set("${project.group}:${project.name}") + url.set("${project.extra.get("project.url")}") + description.set("${project.description}") + licenses { + license { + name.set("BSD 3-Clause License") + url.set("https://github.com/Unidata/netcdf-java/blob/maint-5.x/LICENSE") + } + } + developers { + developer { + name.set("netCDF-Java Developers") + email.set("support-netcdf-java@unidata.ucar.edu") + organization.set("NSF Unidata") + organizationUrl.set("https://unidata.ucar.edu") + } + } + scm { + connection.set("scm:git:https://github.com/unidata/netcdf-java.git") + developerConnection.set("scm:git:https://github.com/unidata/netcdf-java.git") + url.set("https://github.com/unidata/netcdf-java") + } + } + } + } + +tasks.withType().configureEach { dependsOn(augmentPom) } diff --git a/build-logic/src/main/kotlin/base-conventions.gradle.kts b/build-logic/src/main/kotlin/base-conventions.gradle.kts new file mode 100644 index 0000000000..f55008feb7 --- /dev/null +++ b/build-logic/src/main/kotlin/base-conventions.gradle.kts @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { base } + +val catalogs = extensions.getByType() + +group = "edu.ucar" + +version = catalogs.named("libs").findVersion("netcdf-java").get().requiredVersion + +description = "A component to the Unidata netCDF-Java library (aka CDM)." + +extra["project.isRelease"] = !version.toString().endsWith("SNAPSHOT") + +extra["project.title"] = "CDM modules" + +extra["project.vendor"] = "UCAR/Unidata" + +extra["project.url"] = "https://www.unidata.ucar.edu/software/netcdf-java/" + +val docVersionParts = version.toString().split("-")[0].split(".") + +assert(docVersionParts.size == 3) + +extra["docVersion"] = docVersionParts[0] + "." + docVersionParts[1] + +// list of subprojects that are intended for public use and make up the netCDF-Java project +// this is used by the netcdf-java-bom, docs, and code-coverage-report +// subprojects +val publicArtifacts = + listOf( + ":bufr", + ":cdm-core", + ":cdm-image", + ":cdm-mcidas", + ":cdm-misc", + ":cdm-radial", + ":cdm-s3", + ":cdm-vis5d", + ":cdm-zarr", + ":dap4", + ":gcdm", + ":grib", + ":httpservices", + ":legacy", + ":libaec-jna", + ":libaec-native", + ":netcdf4", + ":opendap", + ":udunits", + ":waterml", + ) + +project.extra["public.artifacts"] = publicArtifacts + +// the minimumVersion of java supported +// will be the bytecode produced by the project for all java compilation +// will be used to run the tests (test, not testWithJdkX), generate code coverage reports, etc. +// other versions of java can be used to run the tests, but this is configured in +// testing-conventions.gradle.kts +project.extra["project.minimumJdkVersion"] = "8" diff --git a/build-logic/src/main/kotlin/java-base-conventions.gradle.kts b/build-logic/src/main/kotlin/java-base-conventions.gradle.kts new file mode 100644 index 0000000000..4596a7005f --- /dev/null +++ b/build-logic/src/main/kotlin/java-base-conventions.gradle.kts @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { + id("base-conventions") + id("testing-conventions") +} + +tasks { + // everything gets compiled to the minimum supported version byte code + withType().configureEach { + options.encoding = "UTF-8" + options.release = project.extra["project.minimumJdkVersion"].toString().toInt() + } + + withType().configureEach { + group = "documentation" + options { + encoding = "UTF-8" + // options past here require the standard java doclet + require(this is StandardJavadocDocletOptions) + charSet = "UTF-8" + docEncoding = "UTF-8" + addStringOption("-release", "8") + // so many invalid HTML errors...need to add this for now to + // generate javadocs + addBooleanOption("Xdoclint:none", true) + } + } +} diff --git a/build-logic/src/main/kotlin/java-library-conventions.gradle.kts b/build-logic/src/main/kotlin/java-library-conventions.gradle.kts new file mode 100644 index 0000000000..5dc3d21dbe --- /dev/null +++ b/build-logic/src/main/kotlin/java-library-conventions.gradle.kts @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { + id("java-base-conventions") + `java-library` + id("artifact-publishing-conventions") + id("com.diffplug.spotless") +} + +java { withSourcesJar() } + +tasks.withType().configureEach { + duplicatesStrategy = DuplicatesStrategy.FAIL + manifest { + attributes["Implementation-Title"] = project.extra.get("project.title") + attributes["Implementation-Version"] = "${project.version}" + attributes["Implementation-Vendor-Id"] = "${project.group}" + attributes["Implementation-Vendor"] = project.extra.get("project.vendor") + attributes["Implementation-URL"] = project.extra.get("project.url") + attributes["Created-By"] = "Gradle ${gradle.gradleVersion}" + System.getProperty("java.version")?.let { attributes["Build-Jdk"] = it } + System.getProperty("user.name")?.let { attributes["Built-By"] = it } + } + + from(rootDir.absolutePath) { + include("LICENSE") + into("META-INF/") + } +} + +publishing { + publications { + create("mavenJava") { + from(components["java"]) + versionMapping { + usage("java-api") { fromResolutionOf("runtimeClasspath") } + usage("java-runtime") { fromResolutionResult() } + } + } + } +} + +spotless { + java { + target("src/*/java/**/*.java") + eclipse().configFile("$rootDir/project-files/code-styles/eclipse-style-guide.xml") + encoding("UTF-8") + } +} diff --git a/build-logic/src/main/kotlin/platform-conventions.gradle.kts b/build-logic/src/main/kotlin/platform-conventions.gradle.kts new file mode 100644 index 0000000000..ceb3fc9d6a --- /dev/null +++ b/build-logic/src/main/kotlin/platform-conventions.gradle.kts @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +import org.gradle.api.publish.maven.MavenPublication +import org.gradle.kotlin.dsl.create + +plugins { + id("base-conventions") + `java-platform` + id("artifact-publishing-conventions") +} + +publishing { + publications { create("platform") { from(components["javaPlatform"]) } } +} diff --git a/build-logic/src/main/kotlin/protobuf-conventions.gradle.kts b/build-logic/src/main/kotlin/protobuf-conventions.gradle.kts new file mode 100644 index 0000000000..9e02e2b4e1 --- /dev/null +++ b/build-logic/src/main/kotlin/protobuf-conventions.gradle.kts @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { + id("java-base-conventions") + id("com.google.protobuf") +} + +val libs = extensions.getByType(VersionCatalogsExtension::class.java).named("libs") + +protobuf.protoc { artifact = libs.findLibrary("protobuf-protoc").get().get().toString() } + +tasks.withType(JacocoReport::class.java).configureEach { + classDirectories.setFrom( + sourceSets.main.get().output.asFileTree.matching { exclude("**/generated/**") } + ) +} diff --git a/build-logic/src/main/kotlin/testing-conventions.gradle.kts b/build-logic/src/main/kotlin/testing-conventions.gradle.kts new file mode 100644 index 0000000000..63e708ef0f --- /dev/null +++ b/build-logic/src/main/kotlin/testing-conventions.gradle.kts @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { + id("base-conventions") + java + jacoco +} + +val minimumTestVersion = project.extra["project.minimumJdkVersion"] + +// test tasks will be created for each of the versions listed here (e.g. test11, test17, etc.) +// this will allow the tests to be compiled to and ran with multiple versions of java +val testLtsVersions = listOf(minimumTestVersion, "11", "17", "21", "25") + +project.extra["project.testLtsVersions"] = testLtsVersions + +// Various checks for filtering tests to run +val testdataDirKey = "unidata.testdata.path" +val isCdmUnitTestDirAvailable = isSystemPropertyAValidDirectory(testdataDirKey) + +val jenkinsEnvVar = "JENKINS_URL" +var isJenkins = System.getenv(jenkinsEnvVar) != null + +val pullRequestEnvVar = "GITHUB_ACTIONS" +val isPullRequestCheck = System.getenv(pullRequestEnvVar) != null + +val contentRootKey = "tds.content.root.path" +val isContentRootAvailable = isSystemPropertyAValidDirectory(contentRootKey) + +val runAllTests = isSystemPropertySet("runAllTestsExceptIgnored") +val runSlowTests = isSystemPropertySet("runSlowTests") +val isRdaDataAvailable = isSystemPropertySet("rdaDataAvailable") +val isUcarNetworkAvailable = isSystemPropertySet("ucarNetworkAvailable") +val skipDockerTests = isSystemPropertySet("skipDockerTests") + +fun isSystemPropertyAValidDirectory(sysPropKey: String): Boolean { + val sysPropVal = System.getProperty(sysPropKey) + if (sysPropVal != null) { + val f = File(sysPropVal) + if (f.exists() && f.isDirectory) { + return true + } + } + System.setProperty(sysPropKey, "${layout.buildDirectory}/NO/$sysPropKey/FOUND/") + return false +} + +fun isSystemPropertySet(sysPropKey: String): Boolean { + val sysPropVal = System.getProperty(sysPropKey) + return sysPropVal != null +} + +tasks { + withType().configureEach { + // pass along the location of native libraries, if set + System.getenv("JNA_PATH")?.let { setEnvironment(Pair("JNA_PATH", it)) } + System.getProperty("jna.library.path")?.let { systemProperty("jna.library.path", it) } + jvmArgs("-Xmx1024m") + systemProperties[testdataDirKey] = System.getProperty(testdataDirKey) + useJUnitPlatform { + includeEngines("junit-vintage") + excludeEngines("junit-jupiter") + // Decide how to handle test failures. + // put all log messages inside a doFirst closure so that messages only appear + // during the execution phase + if (isJenkins && !(project.extra.get("project.isRelease") as? Boolean ?: false)) { + // On Jenkins, don't let test failures fail the build unless we are doing we release; we + // want + // the full test report. + ignoreFailures = true + } else { + // Otherwise, fail the build at the first sign of failure. + ignoreFailures = false + } + // Option to run all tests regardless of environment or resource availability + if (runAllTests) { + doFirst { logger.warn("Running all tests except those explicitly annotated with @Ignore.") } + } else { + // Don't skip tests on Jenkins, except NotJenkins ones. + if (!isCdmUnitTestDirAvailable && !isJenkins) { + excludeTags("ucar.unidata.util.test.category.NeedsCdmUnitTest") + doFirst { logger.warn("Skipping all NeedsCdmUnitTest tests.") } + } + + if (!isContentRootAvailable && !isJenkins) { + excludeTags("ucar.unidata.util.test.category.NeedsContentRoot") + doFirst { logger.warn("Skipping all NeedsContentRoot tests.") } + } + + if (isJenkins) { + excludeTags("ucar.unidata.util.test.category.NotJenkins") + doFirst { + logger.warn( + "Skipping all NotJenkins tests: detected that we're running in the Jenkins environment." + ) + } + } + + if (!runSlowTests) { + excludeTags("ucar.unidata.util.test.category.Slow") + doFirst { logger.warn("Skipping all Slow tests.") } + } + + if (isPullRequestCheck) { + excludeTags( + "ucar.unidata.util.test.category.NotPullRequest", + "ucar.unidata.util.test.category.NeedsExternalResource", + ) + doFirst { + logger.warn( + "Skipping all NotPullRequest tests: detected that we're running in the GitHub Actions environment." + ) + } + } + + if (!isRdaDataAvailable) { + excludeTags("ucar.unidata.util.test.category.NeedsRdaData") + doFirst { logger.warn("Skipping all tests that require access to RDA data.") } + } + + if (!isUcarNetworkAvailable) { + excludeTags("ucar.unidata.util.test.category.NeedsUcarNetwork") + doFirst { logger.warn("Skipping all tests that require access to the UCAR Network.") } + } + + if (skipDockerTests) { + // dap4 + exclude("dap4/test/**") + // httpservices + exclude("ucar/httpservices/**") + exclude("ucar/nc2/util/net/**") + // opendap + exclude("opendap/test/**") + exclude("ucar/nc2/dods/**") + doFirst { logger.warn("Skipping all tests that require docker.") } + } + } + } + } +} + +// the basic test task will use the minimumVersion of java to compile and run +tasks.test { + javaLauncher.set( + project.javaToolchains.launcherFor { + languageVersion = JavaLanguageVersion.of(minimumTestVersion.toString().toInt()) + } + ) + // report only generated after test running with minimumTestVersion JDK + finalizedBy(tasks.jacocoTestReport) +} + +tasks.jacocoTestReport { + reports { + xml.required = true + html.required = true + } + dependsOn(tasks.test) // tests are required to run before generating the report +} + +// these test tasks run with a specific version of java, other than the minimum version, +// and are named as testWithJavaX (e.g. testWithJdk11, testWithJdk17, etc.) +testLtsVersions + .filter { it != minimumTestVersion } + .forEach { + tasks.register("testWithJdk${it}") { + group = "verification" + description = "Runs the test task using JDK ${it}." + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + javaLauncher.set( + project.javaToolchains.launcherFor { + languageVersion = JavaLanguageVersion.of(it.toString().toInt()) + } + ) + } + } diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 6144a798a0..0000000000 --- a/build.gradle +++ /dev/null @@ -1,115 +0,0 @@ -// The buildscript {} block is evaluated before anything else in the script (regardless of location in file). -// See http://goo.gl/EO8S1k. So, might as well put it first. -// -buildscript { - // Add the "buildPlugins" ExtraProperty. It should be usable from the rest of this script as well. - // See http://goo.gl/9bixNV - apply from: "$rootDir/gradle/any/shared-mvn-coords.gradle" - - // The buildscript {} block is odd: even though we applied dependencies.gradle above, the repositories therein - // do not get included here. Instead, we must explicitly define the repos again. Yay for duplication. - repositories { - mavenCentral() - gradlePluginPortal() - } - - dependencies { - classpath buildPlugins.shadow - classpath buildPlugins.sonarqube - classpath buildPlugins.spotless - classpath buildPlugins.protobuf - classpath buildPlugins.depcheck - } -} - -allprojects { - // Matches Maven's "project.groupId". Used in MANIFEST.MF for "Implementation-Vendor-Id". - group = 'edu.ucar' - // Matches Maven's "project.version". Used in MANIFEST.MF for "Implementation-Version". - // We try to follow semantic versioning, and thus we use ..- - // may be SNAPSHOT, alphax, betax, etc. - // Note - if bumping to a new major or minor version, be sure to update the docs - version = '5.9.2-SNAPSHOT' - status = 'development' - //status= 'release' -} - -// Matches Maven's "project.description". -description = 'The Unidata netCDF-Java library (aka CDM).' - -import java.text.SimpleDateFormat - -// These will be inherited by subprojects: http://goo.gl/5mvqf7 -// After declaration, they should NOT be referred to using the "ext" namespace, instead preferring e.g. -// "project.title" or simply "title": http://stackoverflow.com/questions/14530901 -// That way, the property will be robustly resolved, as described here: http://goo.gl/UBq0en -// Otherwise, only the one specific ExtraPropertiesExtension will be searched. -ext { - // Matches Maven's "project.name". Used in MANIFEST.MF for "Implementation-Title". - title = 'CDM modules' - - // Matches Maven's "project.organization.name". Used in MANIFEST.MF for "Implementation-Vendor". - vendor = 'UCAR/Unidata' - - // It makes sense to publish major.minor versions of the docs, as - // any patch bumps should be backwards compatible bug fixes only - // To do this, we need to make a special "doc version" string. - // First, drop any dangling snapshot, alpha, beta tags - cleanVersion = "$version".split('-')[0] - - // tokenize version on the '.' character, which gives us a list of [major, minor, patch] - docVersionParts = cleanVersion.tokenize('.') - - // we should always have a major, minor, and patch value in our version - assert docVersionParts.size == 3 - - // keep major and minor parts of the version and use those to version the docs - docVersion = docVersionParts[0] + '.' + docVersionParts[1] - - // Matches Maven's "project.url". Used in MANIFEST.MF for "Implementation-URL". - url = 'https://www.unidata.ucar.edu/software/netcdf-java/' - - SimpleDateFormat iso_8601_format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") - buildTimestamp = iso_8601_format.format(new Date()) - - // Will hold the list of projects that apply the java plugin - // Used by :docs (javadocAll), project wide coverage reports, and sonarqube - javaProjects = [] -} - -gradle.projectsEvaluated { - javaProjects = subprojects.findAll { - subproject -> subproject.plugins.hasPlugin('java') - } -} - -tasks.named('wrapper') { - distributionType = Wrapper.DistributionType.ALL - gradleVersion = '8.14.2' -} - -// Set up properties needed for all testing, adds "testAll" task to root -apply from: "$rootDir/gradle/root/testing.gradle" -// Generates coverage report for testAll -//apply from: "$rootDir/gradle/root/coverage.gradle" -// Attaches fatJar tasks to root project (makes toolsUI, ncIdv, etc.) -apply from: "$rootDir/gradle/root/fatJars.gradle" -// Creates pubs for artifacts created in fatJars.gradle -apply from: "$rootDir/gradle/root/publishing.gradle" -// Adds "sonarqube" task to the root project -apply from: "$rootDir/gradle/root/sonarqube.gradle" -// Adds the spotless tasks to the root project and add check for .gradle files -apply from: "$rootDir/gradle/root/spotless.gradle" -// Adds the owasp dependency-check tasks to the root project (dependencyCheckAggregate for project-wide check) -apply from: "$rootDir/gradle/root/dependency-check.gradle" - -// Modifies Jar tasks created in fatJars.gradle -apply from: "$rootDir/gradle/any/archiving.gradle" -// Modifies pubs created in root/publishing.gradle -apply from: "$rootDir/gradle/any/publishing.gradle" - -// shortcut to run toolsUI -tasks.register("toolsui") { - group = "application" - dependsOn(":uicdm:run") -} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000000..ec5a1d56d5 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { alias(libs.plugins.spotless) } + +description = "The Unidata netCDF-Java library (aka CDM)." + +// To upgrade gradle, update the version and expected checksum values below +// and run ./gradlew wrapper twice +tasks.wrapper { + distributionType = Wrapper.DistributionType.ALL + gradleVersion = "9.2.0" + distributionSha256Sum = "16f2b95838c1ddcf7242b1c39e7bbbb43c842f1f1a1a0dc4959b6d4d68abcac3" +} + +spotless { + // check all gradle build scripts (build-logic has its own formatting check) + kotlinGradle { + target("*.gradle.kts", "**/*.gradle.kts") + targetExclude("build-logic/**/*") + ktfmt().googleStyle() + } +} diff --git a/cdm-test-utils/build.gradle b/cdm-test-utils/build.gradle deleted file mode 100644 index 93d0af646b..0000000000 --- a/cdm-test-utils/build.gradle +++ /dev/null @@ -1,22 +0,0 @@ -description = 'A collection of reusable classes to be used internally for testing across the various THREDDS projects.' -ext.title = 'NetCDF-Java testing utilities' - -apply from: "$rootDir/gradle/any/dependencies.gradle" -apply from: "$rootDir/gradle/any/java-library.gradle" - -dependencies { - api enforcedPlatform(project(':netcdf-java-platform')) - implementation enforcedPlatform(project(':netcdf-java-testing-platform')) - - api project(':cdm:cdm-core') - - implementation project(':httpservices') - - implementation 'junit:junit' - implementation 'org.slf4j:slf4j-api' - implementation 'com.google.re2j:re2j' - implementation 'org.jdom:jdom2' - implementation 'org.testcontainers:testcontainers' - - testRuntimeOnly 'ch.qos.logback:logback-classic' -} diff --git a/cdm-test-utils/build.gradle.kts b/cdm-test-utils/build.gradle.kts new file mode 100644 index 0000000000..df2f799305 --- /dev/null +++ b/cdm-test-utils/build.gradle.kts @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { id("java-library-conventions") } + +description = + "A collection of reusable classes to be used internally for testing across the various THREDDS projects." + +extra["project.title"] = "NetCDF-Java testing utilities" + +dependencies { + implementation(platform(project(":netcdf-java-platform"))) + + api(project(":cdm-core")) + + implementation(project(":httpservices")) + + implementation(libs.jdom2) + implementation(libs.junit4) + implementation(libs.re2j) + implementation(libs.slf4j.api) + implementation(libs.testcontainers) +} + +tasks.withType().configureEach { + from(rootDir.absolutePath) { + include("third-party-licenses/junit/") + into("META-INF/") + } +} diff --git a/cdm-test-utils/src/main/java/ucar/unidata/util/test/DapTestContainer.java b/cdm-test-utils/src/main/java/ucar/unidata/util/test/DapTestContainer.java index 040c38c364..83183f620e 100644 --- a/cdm-test-utils/src/main/java/ucar/unidata/util/test/DapTestContainer.java +++ b/cdm-test-utils/src/main/java/ucar/unidata/util/test/DapTestContainer.java @@ -27,7 +27,7 @@ public abstract class DapTestContainer { static { CONTAINER = new GenericContainer<>( new ImageFromDockerfile().withFileFromClasspath("Dockerfile", "/ucar/unidata/util/test/Dockerfile")) - .withExposedPorts(8080); + .withExposedPorts(8080); CONTAINER.start(); HOST = CONTAINER.getHost(); diff --git a/cdm-test/build.gradle b/cdm-test/build.gradle deleted file mode 100644 index 338dd194a1..0000000000 --- a/cdm-test/build.gradle +++ /dev/null @@ -1,56 +0,0 @@ -description = 'Classes for CDM unit and integration testing. Relies on having access to ' + - 'cdmUnitTest directory, so can which can be obtained at ' + - 'https://github.com/unidata/thredds-test-data.' -ext.title = 'Extended CDM Testing' - -apply from: "$rootDir/gradle/any/dependencies.gradle" -apply from: "$rootDir/gradle/any/test-only-projects.gradle" - -// cdm-test is not published - -dependencies { - testImplementation enforcedPlatform(project(':netcdf-java-platform')) - testImplementation enforcedPlatform(project(':netcdf-java-testing-platform')) - - testImplementation project(':cdm:cdm-core') - testImplementation project(':cdm:cdm-s3') - testImplementation project(':bufr') - testImplementation project(':grib') - testImplementation project(':netcdf4') - testImplementation project(':cdm-test-utils') - testImplementation project(':udunits') - - testImplementation 'commons-io:commons-io' - testImplementation 'org.apache.commons:commons-compress' - testImplementation 'edu.ucar:jj2000' - testImplementation 'org.jdom:jdom2' - testImplementation 'com.google.code.findbugs:jsr305' - testImplementation 'com.google.guava:guava' - testImplementation 'com.google.re2j:re2j' - testImplementation 'software.amazon.awssdk:s3' - testImplementation 'com.google.truth:truth' - testImplementation 'org.slf4j:slf4j-api' - - testRuntimeOnly 'ch.qos.logback:logback-classic' -} - -tasks.register("testIndexCreation", Test) { - group 'Verification' - it.filter { - includeTestsMatching 'ucar.nc2.grib.TestGribIndexCreation' - } - it.dependsOn classes, testClasses -} - -test { - dependsOn 'testIndexCreation' - - // In addition to preventing TestGribIndexCreation from running during cdm-test:test, - // this statement also excludes the results of TestGribIndexCreation from appearing in the cdm-test report: - // "cdm-test/build/reports/tests/index.html". It's not easy to add them back in. Fortunately, those - // results will be included in the allTests aggregate report: "build/reports/allTests/index.html". - // They should also still get picked up by Jenkins. - filter { - excludeTestsMatching 'ucar.nc2.grib.TestGribIndexCreation' - } -} diff --git a/cdm-test/build.gradle.kts b/cdm-test/build.gradle.kts new file mode 100644 index 0000000000..e2bf174aa7 --- /dev/null +++ b/cdm-test/build.gradle.kts @@ -0,0 +1,67 @@ +import kotlin.collections.set + +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { id("java-base-conventions") } + +description = + "Classes for CDM unit and integration testing. Relies on having access to " + + "the cdmUnitTest directory" + +project.extra["project.title"] = "Extended CDM Testing" + +dependencies { + testImplementation(platform(project(":netcdf-java-platform"))) + testImplementation(platform(project(":netcdf-java-testing-platform"))) + + testImplementation(project(":bufr")) + testImplementation(project(":cdm-core")) + testImplementation(project(":cdm-s3")) + testImplementation(project(":cdm-test-utils")) + testImplementation(project(":grib")) + testImplementation(project(":netcdf4")) + testImplementation(project(":udunits")) + + testImplementation(libs.awssdk.s3) + testImplementation(libs.commons.compress) + testImplementation(libs.commons.io) + testImplementation(libs.findbugs.jsr305) + testImplementation(libs.google.truth) + testImplementation(libs.guava) + testImplementation(libs.jdom2) + testImplementation(libs.jj2000) + testImplementation(libs.re2j) + testImplementation(libs.slf4j.api) + testImplementation("org.junit.jupiter:junit-jupiter") + testCompileOnly(libs.junit4) + + testRuntimeOnly(libs.junit5.platformLauncher) + testRuntimeOnly(libs.junit5.vintageEngine) + testRuntimeOnly(libs.logback.classic) +} + +val testVersions = project.extra["project.testLtsVersions"] + +if (testVersions is List<*>) { + testVersions.forEach { + tasks.register( + if (it == project.extra["project.minimumJdkVersion"]) "testIndexCreation" + else "testIndexCreation${it}" + ) { + group = "Verification" + testClassesDirs = sourceSets.test.get().output.classesDirs + classpath = sourceSets.test.get().runtimeClasspath + useJUnitPlatform() + filter { includeTestsMatching("ucar.nc2.grib.TestGribIndexCreation") } + javaLauncher.set( + project.javaToolchains.launcherFor { + languageVersion = JavaLanguageVersion.of(it.toString().toInt()) + } + ) + dependsOn(tasks.classes, tasks.testClasses) + } + } +} diff --git a/cdm-test/src/test/java/ucar/nc2/dataset/TestVertical.java b/cdm-test/src/test/java/ucar/nc2/dataset/TestVertical.java index abba103f47..47a5a2ed22 100644 --- a/cdm-test/src/test/java/ucar/nc2/dataset/TestVertical.java +++ b/cdm-test/src/test/java/ucar/nc2/dataset/TestVertical.java @@ -1,10 +1,11 @@ /* - * Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata + * Copyright (c) 1998-2025 University Corporation for Atmospheric Research/Unidata * See LICENSE for license information. */ + package ucar.nc2.dataset; -import junit.framework.*; +import org.junit.Test; import org.junit.experimental.categories.Category; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,13 +22,11 @@ * Test basic projection methods */ @Category(NeedsCdmUnitTest.class) -public class TestVertical extends TestCase { - private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); +public class TestVertical { - public TestVertical(String name) { - super(name); - } + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + @Test public void testOceanS() throws java.io.IOException, InvalidRangeException { GridDataset gds = ucar.nc2.dt.grid.GridDataset.open(TestDir.cdmUnitTestDir + "transforms/roms_ocean_s_coordinate.nc"); @@ -50,12 +49,14 @@ public void testOceanS() throws java.io.IOException, InvalidRangeException { assert ca.getRank() == 3 : ca.getRank(); int[] shape = ca.getShape(); - for (int i = 0; i < 3; i++) + for (int i = 0; i < 3; i++) { System.out.println(" shape " + i + " = " + shape[i]); + } gds.close(); } + @Test public void testOceanSigma() throws java.io.IOException, InvalidRangeException { GridDataset gds = ucar.nc2.dt.grid.GridDataset.open(TestDir.cdmUnitTestDir + "conventions/cf/gomoos_cf.nc"); @@ -80,12 +81,14 @@ public void testOceanSigma() throws java.io.IOException, InvalidRangeException { assert ca.getRank() == 3 : ca.getRank(); int[] shape = ca.getShape(); - for (int i = 0; i < 3; i++) + for (int i = 0; i < 3; i++) { System.out.println(" shape " + i + " = " + shape[i]); + } } gds.close(); } + @Test public void testAtmSigma() throws java.io.IOException, InvalidRangeException { GridDataset gds = ucar.nc2.dt.grid.GridDataset.open(TestDir.cdmUnitTestDir + "transforms/temperature.nc"); @@ -107,12 +110,14 @@ public void testAtmSigma() throws java.io.IOException, InvalidRangeException { assert ca.getRank() == 3 : ca.getRank(); int[] shape = ca.getShape(); - for (int i = 0; i < 3; i++) + for (int i = 0; i < 3; i++) { System.out.println(" shape " + i + " = " + shape[i]); + } gds.close(); } + @Test public void testAtmHybrid() throws java.io.IOException, InvalidRangeException { GridDataset gds = ucar.nc2.dt.grid.GridDataset.open(TestDir.cdmUnitTestDir + "conventions/cf/ccsm2.nc"); @@ -134,12 +139,14 @@ public void testAtmHybrid() throws java.io.IOException, InvalidRangeException { assert ca.getRank() == 3 : ca.getRank(); int[] shape = ca.getShape(); - for (int i = 0; i < 3; i++) + for (int i = 0; i < 3; i++) { System.out.println(" shape " + i + " = " + shape[i]); + } gds.close(); } + @Test public void testWrfEta() throws java.io.IOException, InvalidRangeException { GridDataset gds = ucar.nc2.dt.grid.GridDataset.open(TestDir.cdmUnitTestDir + "conventions/wrf/wrfout_v2_Lambert.nc"); @@ -162,14 +169,16 @@ public void testWrfEta() throws java.io.IOException, InvalidRangeException { assert ca.getRank() == 3 : ca.getRank(); int[] shape = ca.getShape(); - for (int i = 0; i < 3; i++) + for (int i = 0; i < 3; i++) { System.out.println(" shape " + i + " = " + shape[i]); + } gds.close(); } // TestAll.upcShareDir + /testdata2/grid/netcdf/wrf/wrfout_v2_Lambert.nc + @Test public void testStride() throws java.io.IOException, InvalidRangeException { String filename = TestDir.cdmUnitTestDir + "/conventions/wrf/wrfout_d01_2006-03-08_21-00-00"; GridDataset gds = ucar.nc2.dt.grid.GridDataset.open(filename); @@ -190,8 +199,9 @@ public void testStride() throws java.io.IOException, InvalidRangeException { assert ca.getRank() == 3 : ca.getRank(); int[] shape = ca.getShape(); - for (int i = 0; i < 3; i++) + for (int i = 0; i < 3; i++) { System.out.println(" shape " + i + " = " + shape[i]); + } assert shape[0] == 44; assert shape[1] == 399 / 2 + 1; diff --git a/cdm-test/src/test/java/ucar/nc2/dt/grid/TestGridVerticalTransforms.java b/cdm-test/src/test/java/ucar/nc2/dt/grid/TestGridVerticalTransforms.java index ecb2662d94..ac90fbebd1 100644 --- a/cdm-test/src/test/java/ucar/nc2/dt/grid/TestGridVerticalTransforms.java +++ b/cdm-test/src/test/java/ucar/nc2/dt/grid/TestGridVerticalTransforms.java @@ -1,11 +1,11 @@ /* - * Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata + * Copyright (c) 1998-2025 University Corporation for Atmospheric Research/Unidata * See LICENSE for license information. */ package ucar.nc2.dt.grid; -import junit.framework.TestCase; +import org.junit.Test; import org.junit.experimental.categories.Category; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -22,13 +22,11 @@ import java.lang.invoke.MethodHandles; @Category(NeedsCdmUnitTest.class) -public class TestGridVerticalTransforms extends TestCase { - private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); +public class TestGridVerticalTransforms { - public TestGridVerticalTransforms(String name) { - super(name); - } + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + @Test public void testWRF() throws Exception { testDataset(TestDir.cdmUnitTestDir + "conventions/wrf/wrfout_v2_Lambert.nc"); testDataset(TestDir.cdmUnitTestDir + "conventions/wrf/wrfout_d01_2006-03-08_21-00-00"); @@ -77,13 +75,14 @@ private void testGrid(GeoGrid grid) throws IOException, InvalidRangeException { /* * The 3D coordinate array does not return correct shape and values. Just running this simple code to get z values.. - * + * * url=http://coast-enviro.er.usgs.gov/models/share/erie_test.ncml; * var='temp'; - * + * * z is of shape 20x2x87, it should be 20x87x193. */ + @Test public void testErie() throws IOException, InvalidRangeException { String uri = TestDir.cdmUnitTestDir + "transforms/erie_test.ncml"; String var = "temp"; diff --git a/cdm-test/src/test/java/ucar/nc2/dt/grid/TestStag3D.java b/cdm-test/src/test/java/ucar/nc2/dt/grid/TestStag3D.java index 4eda065afd..a65254a074 100644 --- a/cdm-test/src/test/java/ucar/nc2/dt/grid/TestStag3D.java +++ b/cdm-test/src/test/java/ucar/nc2/dt/grid/TestStag3D.java @@ -1,43 +1,16 @@ /* - * Copyright (c) 1998 - 2009. University Corporation for Atmospheric Research/Unidata - * Portions of this software were developed by the Unidata Program at the - * University Corporation for Atmospheric Research. - * - * Access and use of this software shall impose the following obligations - * and understandings on the user. The user is granted the right, without - * any fee or cost, to use, copy, modify, alter, enhance and distribute - * this software, and any derivative works thereof, and its supporting - * documentation for any purpose whatsoever, provided that this entire - * notice appears in all copies of the software, derivative works and - * supporting documentation. Further, UCAR requests that the user credit - * UCAR/Unidata in any publications that result from the use of this - * software or in any product that includes this software. The names UCAR - * and/or Unidata, however, may not be used in any advertising or publicity - * to endorse or promote any products or commercial entity unless specific - * written permission is obtained from UCAR/Unidata. The user also - * understands that UCAR/Unidata is not obligated to provide the user with - * any support, consulting, training or assistance of any kind with regard - * to the use, operation and performance of this software nor to provide - * the user with any updates, revisions, new versions or "bug fixes." - * - * THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL, - * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE. + * Copyright (c) 2009-2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. */ package ucar.nc2.dt.grid; +import org.junit.Test; import org.junit.experimental.categories.Category; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ucar.nc2.dt.GridCoordSystem; import ucar.ma2.Array; -import junit.framework.TestCase; import ucar.unidata.util.test.category.NeedsCdmUnitTest; import ucar.unidata.util.test.TestDir; import java.lang.invoke.MethodHandles; @@ -50,9 +23,11 @@ * @since Oct 16, 2009 */ @Category(NeedsCdmUnitTest.class) -public class TestStag3D extends TestCase { +public class TestStag3D { + private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + @Test public void testSubset() throws Exception { ucar.nc2.dt.grid.GridDataset dataset = GridDataset.open(TestDir.cdmUnitTestDir + "ft/grid/stag/bora_feb.nc"); @@ -73,8 +48,9 @@ public void testSubset() throws Exception { private String showShape(int[] shape) { Formatter f = new Formatter(); - for (int s : shape) + for (int s : shape) { f.format(" %d", s); + } return f.toString(); } } diff --git a/cdm-test/src/test/java/ucar/nc2/ncml/TestNcMLStrides.java b/cdm-test/src/test/java/ucar/nc2/ncml/TestNcMLStrides.java index 1466ee0b6b..0d5aa1167e 100644 --- a/cdm-test/src/test/java/ucar/nc2/ncml/TestNcMLStrides.java +++ b/cdm-test/src/test/java/ucar/nc2/ncml/TestNcMLStrides.java @@ -1,38 +1,14 @@ /* - * Copyright (c) 1998 - 2011. University Corporation for Atmospheric Research/Unidata - * Portions of this software were developed by the Unidata Program at the - * University Corporation for Atmospheric Research. - * - * Access and use of this software shall impose the following obligations - * and understandings on the user. The user is granted the right, without - * any fee or cost, to use, copy, modify, alter, enhance and distribute - * this software, and any derivative works thereof, and its supporting - * documentation for any purpose whatsoever, provided that this entire - * notice appears in all copies of the software, derivative works and - * supporting documentation. Further, UCAR requests that the user credit - * UCAR/Unidata in any publications that result from the use of this - * software or in any product that includes this software. The names UCAR - * and/or Unidata, however, may not be used in any advertising or publicity - * to endorse or promote any products or commercial entity unless specific - * written permission is obtained from UCAR/Unidata. The user also - * understands that UCAR/Unidata is not obligated to provide the user with - * any support, consulting, training or assistance of any kind with regard - * to the use, operation and performance of this software nor to provide - * the user with any updates, revisions, new versions or "bug fixes." - * - * THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL, - * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING - * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, - * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION - * WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE. + * Copyright (c) 1998-2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. */ + package ucar.nc2.ncml; -import junit.framework.TestCase; +import org.junit.Test; import org.junit.experimental.categories.Category; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import ucar.ma2.ArrayInt; @@ -49,16 +25,13 @@ /** Test netcdf dataset in the JUnit framework. */ @Category(NeedsCdmUnitTest.class) -public class TestNcMLStrides extends TestCase { +public class TestNcMLStrides { private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - public TestNcMLStrides(String name) { - super(name); - } - NetcdfFile ncfile = null; String location = "file:" + TestDir.cdmUnitTestDir + "agg/strides/strides.ncml"; + @BeforeEach public void setUp() { try { ncfile = NcMLReader.readNcML(location, null); @@ -71,10 +44,12 @@ public void setUp() { } } + @AfterEach protected void tearDown() throws IOException { ncfile.close(); } + @Test public void testStride() throws IOException, InvalidRangeException { System.out.println("ncfile opened = " + location + "\n" + ncfile); Variable time = ncfile.findVariable("time"); diff --git a/cdm-test/src/test/java/ucar/nc2/ncml/TestOffAggDirectory.java b/cdm-test/src/test/java/ucar/nc2/ncml/TestOffAggDirectory.java index de0346f3e0..9d0a3a37be 100644 --- a/cdm-test/src/test/java/ucar/nc2/ncml/TestOffAggDirectory.java +++ b/cdm-test/src/test/java/ucar/nc2/ncml/TestOffAggDirectory.java @@ -1,10 +1,11 @@ /* - * Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata + * Copyright (c) 1998-2025 University Corporation for Atmospheric Research/Unidata * See LICENSE for license information. */ + package ucar.nc2.ncml; -import junit.framework.TestCase; +import org.junit.Test; import org.junit.experimental.categories.Category; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -27,9 +28,10 @@ import java.util.List; @Category(NeedsCdmUnitTest.class) -public class TestOffAggDirectory extends TestCase { +public class TestOffAggDirectory { private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); + @Test public void testNcmlDirect() throws IOException { String filename = "file:" + TestDir.cdmUnitTestDir + "ncml/nc/seawifs/aggDirectory.ncml"; @@ -44,6 +46,7 @@ public void testNcmlDirect() throws IOException { ncfile.close(); } + @Test public void testNcmlDataset() throws IOException { String filename = "file:" + TestDir.cdmUnitTestDir + "ncml/nc/seawifs/aggDirectory.ncml"; @@ -58,6 +61,7 @@ public void testNcmlDataset() throws IOException { ncfile.close(); } + @Test public void testNcmlGrid() throws IOException { String filename = "file:" + TestDir.cdmUnitTestDir + "ncml/nc/seawifs/aggDirectory.ncml"; @@ -205,6 +209,7 @@ public void testReadData2(NetcdfFile ncfile) throws IOException { } } + @Test public void testBlanksInDirectory() throws IOException { String dir = TestDir.cdmUnitTestDir + "encoding/"; String ncml = "\n" diff --git a/cdm-test/src/test/java/ucar/nc2/ncml/TestOffAggExistingTimeUnitsChange.java b/cdm-test/src/test/java/ucar/nc2/ncml/TestOffAggExistingTimeUnitsChange.java index d1f2ab08cb..a02fbeb21f 100644 --- a/cdm-test/src/test/java/ucar/nc2/ncml/TestOffAggExistingTimeUnitsChange.java +++ b/cdm-test/src/test/java/ucar/nc2/ncml/TestOffAggExistingTimeUnitsChange.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata + * Copyright (c) 1998-2025 University Corporation for Atmospheric Research/Unidata * See LICENSE for license information. */ @@ -9,7 +9,7 @@ import java.io.IOException; import java.io.StringReader; import java.lang.invoke.MethodHandles; -import junit.framework.TestCase; +import org.junit.Test; import org.junit.experimental.categories.Category; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,15 +30,13 @@ * @author caron */ @Category(NeedsCdmUnitTest.class) -public class TestOffAggExistingTimeUnitsChange extends TestCase { +public class TestOffAggExistingTimeUnitsChange { private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - public TestOffAggExistingTimeUnitsChange(String name) { - super(name); - } // String location = "file:R:/testdata2/ncml/nc/narr/narr.ncml"; + @Test public void testNamExtract() throws IOException { String location = TestDir.cdmUnitTestDir + "ncml/nc/namExtract/test_agg.ncml"; logger.debug(" TestOffAggExistingTimeUnitsChange.open {}", location); @@ -67,6 +65,7 @@ public void testNamExtract() throws IOException { ncfile.close(); } + @Test public void testNarrGrib() throws IOException { String ncml = "\n" + "\n" diff --git a/cdm-test/src/test/java/ucar/nc2/ncml/TestOffAggFmrcNetcdf.java b/cdm-test/src/test/java/ucar/nc2/ncml/TestOffAggFmrcNetcdf.java index 895739c12f..b396c433cc 100644 --- a/cdm-test/src/test/java/ucar/nc2/ncml/TestOffAggFmrcNetcdf.java +++ b/cdm-test/src/test/java/ucar/nc2/ncml/TestOffAggFmrcNetcdf.java @@ -1,10 +1,12 @@ /* - * Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata + * Copyright (c) 1998-2025 University Corporation for Atmospheric Research/Unidata * See LICENSE for license information. */ + package ucar.nc2.ncml; import junit.framework.TestCase; +import org.junit.Test; import org.junit.experimental.categories.Category; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -18,13 +20,10 @@ import java.lang.invoke.MethodHandles; @Category(NeedsCdmUnitTest.class) -public class TestOffAggFmrcNetcdf extends TestCase { +public class TestOffAggFmrcNetcdf { private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - public TestOffAggFmrcNetcdf(String name) { - super(name); - } - + @Test public void testNUWGdatasets() throws IOException, InvalidRangeException { String filename = "file:" + TestDir.cdmUnitTestDir + "ncml/nc/ncmodels/aggFmrcNetcdf.xml"; diff --git a/cdm-test/src/test/java/ucar/nc2/ncml/TestOffAggFmrcNonuniform.java b/cdm-test/src/test/java/ucar/nc2/ncml/TestOffAggFmrcNonuniform.java index 388bad50c1..7881eaebd5 100644 --- a/cdm-test/src/test/java/ucar/nc2/ncml/TestOffAggFmrcNonuniform.java +++ b/cdm-test/src/test/java/ucar/nc2/ncml/TestOffAggFmrcNonuniform.java @@ -1,10 +1,11 @@ /* - * Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata + * Copyright (c) 1998-2025 University Corporation for Atmospheric Research/Unidata * See LICENSE for license information. */ + package ucar.nc2.ncml; -import junit.framework.TestCase; +import org.junit.Test; import org.junit.experimental.categories.Category; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -23,13 +24,10 @@ import java.util.Date; @Category(NeedsCdmUnitTest.class) -public class TestOffAggFmrcNonuniform extends TestCase { +public class TestOffAggFmrcNonuniform { private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - public TestOffAggFmrcNonuniform(String name) { - super(name); - } - + @Test public void testGribNonuniform() throws Exception { String xml = "\n" + "\n" diff --git a/cdm-test/src/test/java/ucar/nc2/ncml/TestOffAggReadGridDataset.java b/cdm-test/src/test/java/ucar/nc2/ncml/TestOffAggReadGridDataset.java index e94cbe97a3..a075f42c15 100644 --- a/cdm-test/src/test/java/ucar/nc2/ncml/TestOffAggReadGridDataset.java +++ b/cdm-test/src/test/java/ucar/nc2/ncml/TestOffAggReadGridDataset.java @@ -2,9 +2,12 @@ * Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata * See LICENSE for license information. */ + package ucar.nc2.ncml; -import junit.framework.*; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; import org.junit.experimental.categories.Category; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -16,16 +19,13 @@ /** Test netcdf dataset in the JUnit framework. */ @Category(NeedsCdmUnitTest.class) -public class TestOffAggReadGridDataset extends TestCase { +public class TestOffAggReadGridDataset { private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); - public TestOffAggReadGridDataset(String name) { - super(name); - } - GridDataset gds = null; String location = "file:" + TestDir.cdmUnitTestDir + "conventions/cf/bora_test_agg.ncml"; + @Before public void setUp() { try { gds = GridDataset.open(location); @@ -37,11 +37,13 @@ public void setUp() { } } - protected void tearDown() throws IOException { + @After + public void tearDown() throws IOException { if (gds != null) gds.close(); } + @Test public void testStructure() { System.out.println("gds opened = " + location + "\n" + gds); } diff --git a/cdm/build.gradle b/cdm/build.gradle deleted file mode 100644 index 8ab8bdef72..0000000000 --- a/cdm/build.gradle +++ /dev/null @@ -1,8 +0,0 @@ -description = 'The NetCDF-Java Library is a Java interface to NetCDF files, as well as to many other types of ' + - 'scientific data formats.' -ext.title = 'CDM library' - -subprojects { - // TODO: Give the subprojects real titles. - ext.title = "CDM: $name" -} diff --git a/cdm/core/build.gradle b/cdm/core/build.gradle deleted file mode 100644 index 6c8fc4aab4..0000000000 --- a/cdm/core/build.gradle +++ /dev/null @@ -1,39 +0,0 @@ -description = 'The Common Data Model (CDM) is a Java interface to NetCDF files, as well as to many other types of ' + - 'scientific data formats.' -ext.title = 'CDM core library' - -apply from: "$rootDir/gradle/any/dependencies.gradle" -apply from: "$rootDir/gradle/any/java-library.gradle" -apply from: "$rootDir/gradle/any/protobuf.gradle" - -dependencies { - api enforcedPlatform(project(':netcdf-java-platform')) - implementation 'org.apache.commons:commons-math3' - - testImplementation enforcedPlatform(project(':netcdf-java-testing-platform')) - - implementation project(':udunits') - implementation project(':httpservices') - - implementation 'com.beust:jcommander' - implementation 'com.google.guava:guava' - implementation 'com.google.protobuf:protobuf-java' - implementation 'com.google.re2j:re2j' - implementation 'joda-time:joda-time' - implementation 'org.jdom:jdom2' - implementation 'org.slf4j:slf4j-api' - - implementation 'com.google.code.findbugs:jsr305' - - testImplementation project(':cdm-test-utils') - - testImplementation 'com.google.truth:truth' - testImplementation 'commons-io:commons-io' - testImplementation 'junit:junit' - testImplementation 'org.mockito:mockito-core' - - testRuntimeOnly 'ch.qos.logback:logback-classic' -} - -// todo: move to ucar.nc2.write.Ncdump in 6? -jar.manifest.attributes 'Main-Class': 'ucar.nc2.NCdumpW' diff --git a/cdm/core/build.gradle.kts b/cdm/core/build.gradle.kts new file mode 100644 index 0000000000..409a9dee47 --- /dev/null +++ b/cdm/core/build.gradle.kts @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { + id("java-library-conventions") + id("protobuf-conventions") +} + +description = + "The Common Data Model (CDM) is a Java interface to NetCDF files, as well as to many other types of" + + "scientific data formats." + +extra["project.title"] = "CDM core library" + +dependencies { + implementation(platform(project(":netcdf-java-platform"))) + + implementation(project(":httpservices")) + implementation(project(":udunits")) + + implementation(libs.beust.jcommander) + implementation(libs.commons.math3) + implementation(libs.findbugs.jsr305) + implementation(libs.guava) + implementation(libs.jdom2) + implementation(libs.jodaTime) + implementation(libs.protobuf) + implementation(libs.re2j) + implementation(libs.slf4j.api) + + testImplementation(platform(project(":netcdf-java-testing-platform"))) + + testImplementation(project(":cdm-test-utils")) + + testImplementation(libs.commons.io) + testImplementation(libs.google.truth) + testImplementation(libs.mockito.core) + + testCompileOnly(libs.junit4) + + testRuntimeOnly(libs.junit5.platformLauncher) + testRuntimeOnly(libs.junit5.vintageEngine) + testRuntimeOnly(libs.logback.classic) +} + +tasks.withType().configureEach { + from(rootDir.absolutePath) { + include("third-party-licenses/edal/") + into("META-INF/") + } +} + +// todo: move to ucar.nc2.write.Ncdump in 6? +tasks.jar { manifest { attributes("Main-Class" to "ucar.nc2.NCdumpW") } } diff --git a/cdm/gcdm/build.gradle b/cdm/gcdm/build.gradle deleted file mode 100644 index 58a2aca191..0000000000 --- a/cdm/gcdm/build.gradle +++ /dev/null @@ -1,88 +0,0 @@ -plugins { - // Provide convenience executables for testing - id 'application' - id 'com.github.psxpaul.execfork' version '0.2.2' -} - -import com.github.psxpaul.task.JavaExecFork - -repositories { - gradlePluginPortal() - maven { - // For jj2000 - url 'https://artifacts.unidata.ucar.edu/repository/unidata-releases/' - } -} - -description = 'gRPC client and server implementation of CDM Remote Procedure Calls (gCDM).' -ext.title = 'CDM Remote Procedure Calls' - -apply from: "$rootDir/gradle/any/java-library.gradle" -apply from: "$rootDir/gradle/any/protobuf.gradle" -apply from: "$rootDir/gradle/any/shared-mvn-coords.gradle" - -dependencies { - api project(':cdm:cdm-core') - - implementation "io.grpc:grpc-protobuf:${depVersion.grpc}" - implementation "io.grpc:grpc-stub:${depVersion.grpc}" - - compileOnly 'org.apache.tomcat:annotations-api:6.0.53' - - runtimeOnly project(':bufr') - runtimeOnly project(':grib') - runtimeOnly "io.grpc:grpc-netty-shaded:${depVersion.grpc}" - - testImplementation project(':netcdf4') - - testImplementation enforcedPlatform(project(':netcdf-java-testing-platform')) - testImplementation project(':cdm-test-utils') - testImplementation 'com.google.truth:truth' - testImplementation 'junit:junit' - testImplementation 'commons-io:commons-io' - testImplementation 'org.mockito:mockito-core' - testImplementation "io.grpc:grpc-testing:${depVersion.grpc}" - - // logging - implementation 'org.slf4j:slf4j-api' - runtimeOnly 'ch.qos.logback:logback-classic' -} - -protobuf { - protoc { artifact = "com.google.protobuf:protoc:${depVersion.protobuf}" } - plugins { - grpc { artifact = "io.grpc:protoc-gen-grpc-java:${depVersion.grpc}" } - } - generateProtoTasks { - all()*.plugins { grpc {} } - } -} - -application { - mainClass = 'ucar.gcdm.server.GcdmServer' -} - -// Inform IDEs like IntelliJ IDEA, Eclipse or NetBeans about the generated code. -sourceSets { - main { - java { - srcDirs 'build/generated/source/proto/main/grpc' - } - } -} - -task startDaemon(type: JavaExecFork) { - classpath = sourceSets.main.runtimeClasspath - main = 'ucar.gcdm.server.GcdmServer' - // To attach the debugger to the gcdm server add to the jvmArgs - // '-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005' - jvmArgs = [ '-Xmx512m', '-Djava.awt.headless=true' ] - standardOutput = new File("$buildDir/gcdm_logs/gcdm.log") - errorOutput = new File("$buildDir/gcdm_logs/gcdm-error.log") - stopAfter = test - waitForPort = 16111 - waitForOutput = 'Server started, listening on 16111' - dependsOn testClasses -} - -test.dependsOn(startDaemon) diff --git a/cdm/gcdm/build.gradle.kts b/cdm/gcdm/build.gradle.kts new file mode 100644 index 0000000000..586ac8e8e6 --- /dev/null +++ b/cdm/gcdm/build.gradle.kts @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +import com.github.psxpaul.task.JavaExecFork +import com.google.protobuf.gradle.id + +plugins { + id("java-library-conventions") + application + id("protobuf-conventions") + alias(libs.plugins.execfork) +} + +description = "gRPC client and server implementation of CDM Remote Procedure Calls (gCDM)." + +project.extra["project.title"] = "CDM Remote Procedure Calls" + +dependencies { + implementation(platform(project(":netcdf-java-platform"))) + + api(project(":cdm-core")) + + implementation(libs.grpc.protobuf) + implementation(libs.grpc.stub) + implementation(libs.slf4j.api) + + compileOnly(libs.tomcat.annotationsApi) + + runtimeOnly(project(":bufr")) + runtimeOnly(project(":grib")) + + runtimeOnly(libs.grpc.nettyShaded) + runtimeOnly(libs.logback.classic) + + testImplementation(platform(project(":netcdf-java-testing-platform"))) + + testImplementation(project(":cdm-test-utils")) + testImplementation(project(":netcdf4")) + + testImplementation(libs.commons.io) + testImplementation(libs.google.truth) + testImplementation(libs.grpc.testing) + testImplementation(libs.mockito.core) + + testCompileOnly(libs.junit4) + + testRuntimeOnly(libs.junit5.platformLauncher) + testRuntimeOnly(libs.junit5.vintageEngine) +} + +val libCatalog = extensions.getByType(VersionCatalogsExtension::class.java).named("libs") + +protobuf { + protoc { artifact = libCatalog.findLibrary("protobuf-protoc").get().get().toString() } + plugins { + id("grpc") { + artifact = libCatalog.findLibrary("grpc-protocGenGrpcJava").get().get().toString() + } + } + generateProtoTasks { + ofSourceSet("main").forEach { + it.plugins { + // Apply the "grpc" plugin whose spec is defined above, without options. + id("grpc") + } + } + } +} + +application { mainClass = "ucar.gcdm.server.GcdmServer" } + +val startDaemon = + tasks.register("startDaemon") { + classpath = sourceSets.main.get().runtimeClasspath + main = "ucar.gcdm.server.GcdmServer" + // To attach the debugger to the gcdm server add to the jvmArgs + // '-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005' + jvmArgs = listOf("-Xmx512m", "-Djava.awt.headless=true") + standardOutput = project.layout.buildDirectory.file("gcdm_logs/gcdm.log") + errorOutput = project.layout.buildDirectory.file("gcdm_logs/gcdm-error.log") + // stopAfter = tasks.named("test") + waitForPort = 16111 + waitForOutput = "Server started, listening on 16111" + dependsOn(tasks.testClasses) + doLast { stopAfter = tasks.named("test") } + } + +tasks.test { dependsOn(startDaemon) } diff --git a/cdm/image/build.gradle b/cdm/image/build.gradle deleted file mode 100644 index ba212c112a..0000000000 --- a/cdm/image/build.gradle +++ /dev/null @@ -1,18 +0,0 @@ -description = 'A collection of utilities needed client-side, including IOSP\'s requiring java.awt.' -ext.title = 'Client-side CDM image library' - -apply from: "$rootDir/gradle/any/dependencies.gradle" -apply from: "$rootDir/gradle/any/java-library.gradle" - -dependencies { - api enforcedPlatform(project(':netcdf-java-platform')) - testImplementation enforcedPlatform(project(':netcdf-java-testing-platform')) - - api project(':cdm:cdm-core') - - implementation 'org.slf4j:slf4j-api' - - testImplementation project(':cdm-test-utils') - testImplementation 'junit:junit' - testImplementation 'com.google.truth:truth' -} diff --git a/cdm/image/build.gradle.kts b/cdm/image/build.gradle.kts new file mode 100644 index 0000000000..b59d4a85ff --- /dev/null +++ b/cdm/image/build.gradle.kts @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { id("java-library-conventions") } + +description = "A collection of utilities needed client-side, including IOSPs requiring java.awt." + +project.extra["project.title"] = "Client-side CDM image library" + +dependencies { + implementation(platform(project(":netcdf-java-platform"))) + + api(project(":cdm-core")) + + implementation(libs.slf4j.api) + + testImplementation(platform(project(":netcdf-java-testing-platform"))) + + testImplementation(project(":cdm-test-utils")) + + testImplementation(libs.google.truth) + + testCompileOnly(libs.junit4) + + testRuntimeOnly(libs.junit5.platformLauncher) + testRuntimeOnly(libs.junit5.vintageEngine) +} diff --git a/cdm/misc/build.gradle b/cdm/misc/build.gradle deleted file mode 100644 index 1375012b3d..0000000000 --- a/cdm/misc/build.gradle +++ /dev/null @@ -1,29 +0,0 @@ -description = 'The Common Data Model (CDM) misc IOSPs.' -ext.title = 'CDM misc iosp library' - -apply from: "$rootDir/gradle/any/dependencies.gradle" -apply from: "$rootDir/gradle/any/java-library.gradle" -apply from: "$rootDir/gradle/any/protobuf.gradle" - -dependencies { - api enforcedPlatform(project(':netcdf-java-platform')) - testImplementation enforcedPlatform(project(':netcdf-java-testing-platform')) - - implementation project(':udunits') - implementation project(':cdm:cdm-core') - - implementation 'com.google.code.findbugs:jsr305' - implementation 'com.google.guava:guava' - implementation 'com.google.protobuf:protobuf-java' - implementation 'com.google.re2j:re2j' - - implementation 'org.slf4j:slf4j-api' - - testImplementation project(':grib') - - testImplementation project(':cdm-test-utils') - - testImplementation 'commons-io:commons-io' - testImplementation 'org.mockito:mockito-core' - testImplementation 'com.google.truth:truth' -} diff --git a/cdm/misc/build.gradle.kts b/cdm/misc/build.gradle.kts new file mode 100644 index 0000000000..4ed8d02942 --- /dev/null +++ b/cdm/misc/build.gradle.kts @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { + id("java-library-conventions") + id("protobuf-conventions") +} + +description = "The Common Data Model (CDM) misc IOSPs." + +project.extra["project.title"] = "CDM misc IOSP library" + +dependencies { + implementation(platform(project(":netcdf-java-platform"))) + + api(project(":cdm-core")) + + implementation(project(":udunits")) + + implementation(libs.findbugs.jsr305) + implementation(libs.guava) + implementation(libs.protobuf) + implementation(libs.re2j) + implementation(libs.slf4j.api) + + testImplementation(platform(project(":netcdf-java-testing-platform"))) + + testImplementation(project(":cdm-test-utils")) + testImplementation(project(":grib")) + + testImplementation(libs.commons.io) + testImplementation(libs.google.truth) + testImplementation(libs.mockito.core) + + testCompileOnly(libs.junit4) + + testRuntimeOnly(libs.junit5.platformLauncher) + testRuntimeOnly(libs.junit5.vintageEngine) +} diff --git a/cdm/misc/src/test/java/ucar/nc2/iosp/dmsp/TestDmspIosp.java b/cdm/misc/src/test/java/ucar/nc2/iosp/dmsp/TestDmspIosp.java index d010359323..754b1f767b 100644 --- a/cdm/misc/src/test/java/ucar/nc2/iosp/dmsp/TestDmspIosp.java +++ b/cdm/misc/src/test/java/ucar/nc2/iosp/dmsp/TestDmspIosp.java @@ -1,11 +1,13 @@ /* - * Copyright (c) 1998-2018 University Corporation for Atmospheric Research/Unidata + * Copyright (c) 2006-2018 University Corporation for Atmospheric Research/Unidata * See LICENSE for license information. */ -// $Id: TestDmspIosp.java 51 2006-07-12 17:13:13Z caron $ + package ucar.nc2.iosp.dmsp; -import junit.framework.TestCase; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; import org.junit.experimental.categories.Category; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -23,7 +25,7 @@ import java.util.Locale; import java.util.TimeZone; -public class TestDmspIosp extends TestCase { +public class TestDmspIosp { private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); private String testFilePath = TestDir.cdmUnitTestDir + "formats/dmsp"; @@ -62,10 +64,7 @@ public class TestDmspIosp extends TestCase { private ucar.unidata.io.RandomAccessFile meRaf = null; private NetcdfFile meNcf = null; - public TestDmspIosp(String name) { - super(name); - } - + @Test public void testDateFormatHandler() { String isoDateFormatString = "yyyy-MM-dd"; String isoTimeFormatString = "HH:mm:ss.SSSz"; @@ -119,6 +118,7 @@ public void testDateFormatHandler() { /** * Test ... */ + @Test @Category(NeedsCdmUnitTest.class) public void testDimAndAtt() throws IOException { setupReadDmspAsNetcdf(this.testFilePath, this.testDataFileName); @@ -204,6 +204,7 @@ public void testDimAndAtt() throws IOException { } + @Test @Category(NeedsCdmUnitTest.class) public void testReadEpoch() throws IOException { setupReadDmspAsNetcdf(this.testFilePath, this.testDataFileName); @@ -270,6 +271,7 @@ public void testReadEpoch() throws IOException { meNcf.close(); } + @Test @Category(NeedsCdmUnitTest.class) public void testLatLonCalcAndCache() throws IOException { setupReadDmspAsNetcdf(this.testFilePath, this.testDataFileName); @@ -342,6 +344,7 @@ public void testLatLonCalcAndCache() throws IOException { // // I.e., if a user requests one point in a particular dimension, that // dimension will be removed. + @Test @Category(NeedsCdmUnitTest.class) public void testSectionVsSectionNoReduce() throws IOException { setupReadDmspAsNetcdf(this.testFilePath, this.testDataFileName); diff --git a/cdm/radial/build.gradle b/cdm/radial/build.gradle deleted file mode 100644 index 9726ef7e43..0000000000 --- a/cdm/radial/build.gradle +++ /dev/null @@ -1,26 +0,0 @@ -description = 'The Common Data Model (CDM) radial data IOSPs.' -ext.title = 'CDM radial library' - -apply from: "$rootDir/gradle/any/dependencies.gradle" -apply from: "$rootDir/gradle/any/java-library.gradle" - -dependencies { - api enforcedPlatform(project(':netcdf-java-platform')) - testImplementation enforcedPlatform(project(':netcdf-java-testing-platform')) - - implementation project(':udunits') - implementation project(':cdm:cdm-core') - - implementation 'org.jdom:jdom2' - implementation 'com.google.code.findbugs:jsr305' - implementation 'com.google.guava:guava' - implementation 'com.google.re2j:re2j' - implementation 'org.slf4j:slf4j-api' - - testImplementation project(':cdm-test-utils') - testImplementation project(':netcdf4') - - testImplementation 'commons-io:commons-io' - testImplementation 'org.mockito:mockito-core' - testImplementation 'com.google.truth:truth' -} diff --git a/cdm/radial/build.gradle.kts b/cdm/radial/build.gradle.kts new file mode 100644 index 0000000000..e98d5bdf68 --- /dev/null +++ b/cdm/radial/build.gradle.kts @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { id("java-library-conventions") } + +description = "The Common Data Model (CDM) radial data IOSPs." + +project.extra["project.title"] = "CDM radial library" + +dependencies { + implementation(platform(project(":netcdf-java-platform"))) + + api(project(":cdm-core")) + + implementation(project(":udunits")) + + implementation(libs.findbugs.jsr305) + implementation(libs.guava) + implementation(libs.jdom2) + implementation(libs.re2j) + implementation(libs.slf4j.api) + + testImplementation(platform(project(":netcdf-java-testing-platform"))) + + testImplementation(project(":cdm-test-utils")) + testImplementation(project(":netcdf4")) + + testImplementation(libs.commons.io) + testImplementation(libs.google.truth) + testImplementation(libs.mockito.core) + + testCompileOnly(libs.junit4) + + testRuntimeOnly(libs.junit5.platformLauncher) + testRuntimeOnly(libs.junit5.vintageEngine) +} diff --git a/cdm/s3/build.gradle b/cdm/s3/build.gradle deleted file mode 100644 index e92debdc38..0000000000 --- a/cdm/s3/build.gradle +++ /dev/null @@ -1,40 +0,0 @@ -description = 'The Common Data Model (CDM) AWS S3 support.' -ext.title = 'CDM S3 support library' - -apply from: "$rootDir/gradle/any/dependencies.gradle" -apply from: "$rootDir/gradle/any/java-library.gradle" - -dependencies { - api enforcedPlatform(project(':netcdf-java-platform')) - testImplementation enforcedPlatform(project(':netcdf-java-testing-platform')) - - implementation project(':cdm:cdm-core') - implementation 'org.slf4j:slf4j-api' - - implementation('software.amazon.awssdk:s3') { - // exclude netty nio client due to open CVEs. See - // https://github.com/aws/aws-sdk-java-v2/issues/1632 - // we don't use the nio http client in our S3 related code, - // so we should be ok here (others may need to add it specifically to - // their code if they are using our S3 stuff, but then it's their - // explicit decision to run it). - exclude group: 'software.amazon.awssdk', module: 'netty-nio-client' - } - - implementation 'software.amazon.awssdk:apache-client' - implementation 'com.google.code.findbugs:jsr305' - implementation 'com.google.guava:guava' - implementation 'com.google.re2j:re2j' - - runtimeOnly('software.amazon.awssdk:sts') { - // see above comment about awssdk and netty-nio-client - exclude group: 'software.amazon.awssdk', module: 'netty-nio-client' - } - - testImplementation project(':cdm:cdm-radial') - testImplementation project(':cdm-test-utils') - - testImplementation 'com.google.truth:truth' - - testRuntimeOnly 'ch.qos.logback:logback-classic' -} diff --git a/cdm/s3/build.gradle.kts b/cdm/s3/build.gradle.kts new file mode 100644 index 0000000000..eaa2df3e95 --- /dev/null +++ b/cdm/s3/build.gradle.kts @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { id("java-library-conventions") } + +description = "The Common Data Model (CDM) AWS S3 support." + +project.extra["project.title"] = "CDM S3 support library" + +dependencies { + implementation(platform(project(":netcdf-java-platform"))) + + api(project(":cdm-core")) + + implementation(libs.awssdk.apacheClient) + implementation(libs.awssdk.s3) { + // exclude netty nio client due to open CVEs. See + // https://github.com/aws/aws-sdk-java-v2/issues/1632 + // we don't use the nio http client in our S3 related code, + // so we should be ok here. Others may need to add it specifically + // to their project if they are using our S3 stuff, but then it's + // their explicit decision to run it. + exclude(group = "software.amazon.awssdk", module = "netty-nio-client") + } + implementation(libs.awssdk.sts) { + // see above comment about awssdk and netty-nio-client + exclude(group = "software.amazon.awssdk", module = "netty-nio-client") + } + implementation(libs.findbugs.jsr305) + implementation(libs.guava) + implementation(libs.re2j) + implementation(libs.slf4j.api) + + testImplementation(platform(project(":netcdf-java-testing-platform"))) + + testImplementation(project(":cdm-radial")) + testImplementation(project(":cdm-test-utils")) + + testImplementation(libs.google.truth) + + testCompileOnly(libs.junit4) + + testRuntimeOnly(libs.junit5.platformLauncher) + testRuntimeOnly(libs.junit5.vintageEngine) + testRuntimeOnly(libs.logback.classic) +} diff --git a/cdm/zarr/build.gradle b/cdm/zarr/build.gradle deleted file mode 100644 index 9e4aea740a..0000000000 --- a/cdm/zarr/build.gradle +++ /dev/null @@ -1,33 +0,0 @@ -description = 'Reading Zarr files with the NetCDF-java library.' -ext.title = 'CDM Zarr support library' - -apply from: "$rootDir/gradle/any/dependencies.gradle" -apply from: "$rootDir/gradle/any/java-library.gradle" - -dependencies { - api enforcedPlatform(project(':netcdf-java-platform')) - testImplementation enforcedPlatform(project(':netcdf-java-testing-platform')) - - api project(':cdm:cdm-core') - - implementation 'org.slf4j:slf4j-api' - implementation 'com.fasterxml.jackson.core:jackson-core' - implementation 'com.fasterxml.jackson.core:jackson-databind' - implementation 'com.google.code.findbugs:jsr305' - implementation 'com.google.guava:guava' - - testImplementation('software.amazon.awssdk:s3') { - // exclude netty nio client due to open CVEs. See - // https://github.com/aws/aws-sdk-java-v2/issues/1632 - // we don't use the nio http client in our S3 related code, - // so we should be ok here (others may need to add it specifically to - // their code if they are using our S3 stuff, but then it's their - // explicit decision to run it). - exclude group: 'software.amazon.awssdk', module: 'netty-nio-client' - } - - testImplementation project(':cdm:cdm-s3') - testImplementation project(':cdm-test-utils') - - testImplementation 'com.google.truth:truth' -} diff --git a/cdm/zarr/build.gradle.kts b/cdm/zarr/build.gradle.kts new file mode 100644 index 0000000000..7171615687 --- /dev/null +++ b/cdm/zarr/build.gradle.kts @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { id("java-library-conventions") } + +description = "Reading Zarr files with the NetCDF-java library." + +project.extra["project.title"] = "CDM Zarr support library" + +dependencies { + implementation(platform(project(":netcdf-java-platform"))) + + api(project(":cdm-core")) + + implementation(libs.findbugs.jsr305) + implementation(libs.guava) + implementation(libs.jackson.core) + implementation(libs.jackson.databind) + implementation(libs.slf4j.api) + + testImplementation(platform(project(":netcdf-java-testing-platform"))) + + testImplementation(project(":cdm-s3")) + testImplementation(project(":cdm-test-utils")) + + testImplementation(libs.awssdk.s3) { + // exclude netty nio client due to open CVEs. See + // https://github.com/aws/aws-sdk-java-v2/issues/1632 + // we don't use the nio http client in our S3 related code, + // so we should be ok here. Others may need to add it specifically + // to their project if they are using our S3 stuff, but then it's + // their explicit decision to run it. + exclude(group = "software.amazon.awssdk", module = "netty-nio-client") + } + testImplementation(libs.google.truth) + + testCompileOnly(libs.junit4) + + testRuntimeOnly(libs.junit5.platformLauncher) + testRuntimeOnly(libs.junit5.vintageEngine) +} diff --git a/code-coverage-report/build.gradle.kts b/code-coverage-report/build.gradle.kts new file mode 100644 index 0000000000..01a7e674c6 --- /dev/null +++ b/code-coverage-report/build.gradle.kts @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +description = + "Generate a project-wide code coverage report for the public artifacts of netCDF-Java." + +extra["project.title"] = "Project-wide code coverage report" + +plugins { + id("java-base-conventions") + alias(libs.plugins.shadow).apply(false) + alias(libs.plugins.cyclonedx.bom).apply(false) + id("jacoco-report-aggregation").apply(true) +} + +dependencies { + val publicArtifacts = project.extra.get("public.artifacts") + if (publicArtifacts is List<*>) { + publicArtifacts.forEach { jacocoAggregation(project(it.toString())) } + jacocoAggregation(project(":cdm-test")) + } else { + logger.error( + "Cannot access the list of public artifacts. The project-wide code coverage report will be incomplete!" + ) + } +} + +reporting { + reports { + val testCodeCoverageReportAgg by + creating(JacocoCoverageReport::class) { testSuiteName = "test" } + } +} diff --git a/dap4/build.gradle b/dap4/build.gradle deleted file mode 100644 index 4704936440..0000000000 --- a/dap4/build.gradle +++ /dev/null @@ -1,37 +0,0 @@ -ext.title = 'Data Access Protocol (DAP) version 4.0' // Will be inherited by subprojects. - -apply from: "$rootDir/gradle/any/dependencies.gradle" -apply from: "$rootDir/gradle/any/java-library.gradle" - -dependencies { - api enforcedPlatform(project(':netcdf-java-platform')) - - implementation project(':httpservices') - implementation project(':cdm:cdm-core') - implementation project(':netcdf4') - testImplementation project(':cdm-test-utils') - - implementation 'org.apache.httpcomponents:httpclient' - compileOnly 'org.apache.httpcomponents:httpcore' - compileOnly 'net.java.dev.jna:jna' - compileOnly 'com.beust:jcommander' - compileOnly 'org.slf4j:slf4j-api' - - compileOnly enforcedPlatform(project(':netcdf-java-platform')) - testImplementation enforcedPlatform(project(':netcdf-java-testing-platform')) - testImplementation 'junit:junit' - testImplementation 'org.slf4j:slf4j-api' - testRuntimeOnly 'ch.qos.logback:logback-classic' -} - -test { - systemProperties['testargs'] = System.getProperty('testargs', '') - - include 'dap4/test/TestParserDMR.class' - include 'dap4/test/TestParserCE.class' - include 'dap4/test/TestRaw.class' - include 'dap4/test/TestDap4Url.class' - include 'dap4/test/TestRemote.class' - include 'dap4/test/TestConstraints.class' - include 'dap4/test/TestHyrax.class' -} diff --git a/dap4/build.gradle.kts b/dap4/build.gradle.kts new file mode 100644 index 0000000000..1e39869bdc --- /dev/null +++ b/dap4/build.gradle.kts @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { id("java-library-conventions") } + +description = "Data Access Protocol (DAP) version 4.0 client." + +project.extra["project.title"] = "DAP4 Client" + +dependencies { + implementation(platform(project(":netcdf-java-platform"))) + + implementation(project(":cdm-core")) + implementation(project(":httpservices")) + + implementation(libs.httpcomponents.api) + implementation(libs.slf4j.api) + + testImplementation(platform(project(":netcdf-java-testing-platform"))) + + testImplementation(project(":cdm-test-utils")) + + testCompileOnly(libs.junit4) + + testRuntimeOnly(libs.junit5.platformLauncher) + testRuntimeOnly(libs.junit5.vintageEngine) +} diff --git a/docs/build.gradle b/docs/build.gradle deleted file mode 100644 index 323f30e337..0000000000 --- a/docs/build.gradle +++ /dev/null @@ -1,201 +0,0 @@ -plugins { - id 'base' // Adds 'assemble', 'check', 'build', and 'clean' tasks. -} - -//////////////////////////////////////////////// Javadoc //////////////////////////////////////////////// -apply from: "$rootDir/gradle/any/dependencies.gradle" -apply from: "$rootDir/gradle/any/javadoc.gradle" -apply from: "$rootDir/gradle/any/testing.gradle" - -dependencies { - testImplementation enforcedPlatform(project(':netcdf-java-testing-platform')) - - testImplementation project(':cdm:cdm-core') - testImplementation project(':cdm:cdm-s3') - testImplementation project(':netcdf4') - testImplementation project(':opendap') - testImplementation project(':cdm-test-utils') - testImplementation project(':bufr') - testImplementation project(':grib') - testImplementation project(':udunits') - - testImplementation 'org.jdom:jdom2' - testImplementation 'org.slf4j:slf4j-api' - testImplementation 'junit:junit' - testImplementation 'com.google.truth:truth' - - testRuntimeOnly 'ch.qos.logback:logback-classic' -} - -gradle.projectsEvaluated { // Several statements below rely upon all subprojects having been evaluated. - - task buildJavadocPublicApi(type: Javadoc) { - description = 'Generate Javadoc for the CDM subproject.' - - title = "NetCDF-Java CDM Public API v${version}" - destinationDir = file("$buildDir/javadoc/") - - options.showFromPublic() - options.noDeprecated() - - SourceSet cdmCoreSourceSet = rootProject.project(':cdm:cdm-core').sourceSets.main - source cdmCoreSourceSet.allJava - - classpath = files([cdmCoreSourceSet.compileClasspath, cdmCoreSourceSet.output]) - - // This is the public interface. Future changes to the API will attempt to remain backwards compatible with it. - include 'thredds/client/catalog/*.java' - include 'thredds/client/catalog/builder/*.java' - include 'ucar/ma2/*.java' - include 'ucar/nc2/*.java' - include 'ucar/nc2/constants/*.java' - include 'ucar/nc2/dataset/*.java' - include 'ucar/nc2/dataset/spi/*.java' - include 'ucar/nc2/iosp/*.java' - include 'ucar/nc2/time/*.java' - include 'ucar/nc2/units/*.java' - include 'ucar/nc2/util/*.java' - include 'ucar/nc2/write/*.java' - include 'ucar/unidata/geoloc/*.java' - include 'ucar/unidata/io/*.java' - include 'ucar/unidata/io/spi/*.java' - } - - task buildJavadocPublicApiWithDeps(type: Javadoc) { - description = 'Generate Javadoc for the CDM subproject - included deprecated classes and methods.' - - title = "NetCDF-Java CDM Public API v${version} - with deprecations" - destinationDir = file("$buildDir/javadoc-with-deprecations/") - - SourceSet cdmCoreSourceSet = rootProject.project(':cdm:cdm-core').sourceSets.main - source cdmCoreSourceSet.allJava - - classpath = files([cdmCoreSourceSet.compileClasspath, cdmCoreSourceSet.output]) - - // This is the public interface. Future changes to the API will attempt to remain backwards compatible with it. - include 'thredds/client/catalog/*.java' - include 'thredds/client/catalog/builder/*.java' - include 'ucar/ma2/*.java' - include 'ucar/nc2/*.java' - include 'ucar/nc2/constants/*.java' - include 'ucar/nc2/dataset/*.java' - include 'ucar/nc2/dataset/spi/*.java' - include 'ucar/nc2/iosp/*.java' - include 'ucar/nc2/time/*.java' - include 'ucar/nc2/units/*.java' - include 'ucar/nc2/util/*.java' - include 'ucar/nc2/write/*.java' - include 'ucar/unidata/geoloc/*.java' - include 'ucar/unidata/io/*.java' - include 'ucar/unidata/io/spi/*.java' - } - - task buildJavadocAll(type: Javadoc) { - description = 'Generate Javadoc for all Java subprojects.' - - title = "NetCDF-Java All API v${version}" - destinationDir = file("$buildDir/javadocAll/") - - source javaProjects.collect { - project -> project.sourceSets.main.allJava - } - - classpath = files(javaProjects.collect {project -> - [project.sourceSets.main.compileClasspath, project.sourceSets.main.output] - }) - } - - tasks.build { - // Aggregates the individual "build*" tasks. - dependsOn buildJekyllSite, buildJavadocPublicApi, buildJavadocPublicApiWithDeps, buildJavadocAll - } -} - -//////////////////////////////////////////////// Nexus //////////////////////////////////////////////// - -apply from: "$rootDir/gradle/any/properties.gradle" // For Nexus credential properties. - -String docTheme = "unidata-jekyll-docs:0.0.6" - -boolean isGitHub = System.getenv('GITHUB_ACTIONS') as boolean -String imageBaseUrl = "docker.unidata.ucar.edu" -if (isGitHub) { - imageBaseUrl = "ghcr.io/unidata" -} - -String dockerImage = "${imageBaseUrl}/${docTheme}" -Provider siteBuildDir = layout.buildDirectory.dir("site") - -tasks.register("buildJekyllSite", Exec) { - group = "documentation" - description = "Build the netCDF-Java documentation." - ConfigurableFileTree buildDocInputs = fileTree(".") - buildDocInputs.exclude("build/", ".gradle", ".jekyll-cache") - inputs.files(buildDocInputs) - outputs.dir(siteBuildDir) - commandLine("docker", "run", "--rm", - "-e", "SRC_DIR=/netcdf-java/docs/src/site", - "-v", "$rootDir:/netcdf-java", - "-v", "./${relativePath(siteBuildDir.get().toString())}:/site", - dockerImage, "build") -} - -class NullOutputStream extends OutputStream { - @Override - void write(int b) throws IOException {} -} - -tasks.register("serveJekyllSite", Exec) { - group = "documentation" - description = "Start a local server to live edit the netCDF-Java documentation." - commandLine("docker", "run", "--rm", "-d", - "--name", "netcdf-java-docs-server", - "-e", "SRC_DIR=/netcdf-java/docs/src/site", - "-v", "$rootDir:/netcdf-java", - "-p", "4005:4005", - dockerImage, "serve", "--livereload") - standardOutput = new NullOutputStream() - doLast { - String msg = "NetCDF-Java documentation available at http://localhost:4005" - String bannerBorder = new String(new char[msg.length() + 4]).replace("\0", "#"); - println() - println(bannerBorder) - println("# $msg #") - println(bannerBorder) - println() - } -} - -tasks.register("stopServe", Exec) { - group = "documentation" - description = "Stop the local server used while live editing the netCDF-Java documentation." - commandLine("docker", "stop", "netcdf-java-docs-server") - delete("$projectDir/src/site/Gemfile") - delete("$projectDir/src/site/Gemfile.lock") -} - -tasks.withType(JavaCompile).configureEach { - options.encoding = 'UTF-8' - // whatever java is being used to run gradle will be used to compile java classes - // in src/main, but this makes sure it outputs bytecode compatible with Java 8 - if (JavaVersion.current() != JavaVersion.VERSION_1_8) { - options.setRelease(8) - } else { - // java 8 does not have a release option, so use source and target compatibility - setSourceCompatibility(JavaVersion.VERSION_1_8) - setTargetCompatibility(JavaVersion.VERSION_1_8) - } - // show deprecation details - options.compilerArgs = ['-Xlint:deprecation'] -} - -apply plugin: 'com.diffplug.spotless' -spotless { - java { - // target java files in the test directory - target 'src/test/java/**/*.java' - eclipse().configFile("$rootDir/project-files/code-styles/eclipse-style-guide.xml", - 'src/test/style/style-override.properties') - encoding 'UTF-8' - } -} diff --git a/docs/build.gradle.kts b/docs/build.gradle.kts new file mode 100644 index 0000000000..1e9a77fbe9 --- /dev/null +++ b/docs/build.gradle.kts @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +import java.io.OutputStream + +plugins { + id("java-base-conventions") + alias(libs.plugins.spotless) +} + +description = "Generate the project documentation, including the various flavors of javadocs." + +extra["project.title"] = "Project documentation" + +dependencies { + testImplementation(platform(project(":netcdf-java-testing-platform"))) + + testImplementation(project(":bufr")) + testImplementation(project(":cdm-core")) + testImplementation(project(":cdm-s3")) + testImplementation(project(":cdm-test-utils")) + testImplementation(project(":grib")) + testImplementation(project(":netcdf4")) + testImplementation(project(":opendap")) + testImplementation(project(":udunits")) + + testImplementation(libs.google.truth) + testImplementation(libs.jdom2) + testImplementation(libs.slf4j.api) + + testCompileOnly(libs.junit4) + + testRuntimeOnly(libs.junit5.platformLauncher) + testRuntimeOnly(libs.junit5.vintageEngine) + testRuntimeOnly(libs.logback.classic) +} + +// This is the public interface. Future changes to the API will attempt to remain backwards +// compatible with it. +val publicApi = + listOf( + "thredds/client/catalog/*.java", + "thredds/client/catalog/builder/*.java", + "ucar/ma2/*.java", + "ucar/nc2/*.java", + "ucar/nc2/constants/*.java", + "ucar/nc2/dataset/*.java", + "ucar/nc2/dataset/spi/*.java", + "ucar/nc2/iosp/*.java", + "ucar/nc2/time/*.java", + "ucar/nc2/units/*.java", + "ucar/nc2/util/*.java", + "ucar/nc2/write/*.java", + "ucar/unidata/geoloc/*.java", + "ucar/unidata/io/*.java", + "ucar/unidata/io/spi/*.java", + ) + +val buildJavadocPublicApi = + tasks.register("buildJavadocPublicApi") { + description = "Generate Javadoc for the public API without deprecations." + title = "NetCDF-Java CDM Public API v${version}" + destinationDir = layout.buildDirectory.dir("javadoc").get().asFile + + options { + showFromPublic() + require(this is StandardJavadocDocletOptions) + noDeprecated() + } + + val cdmCoreSourceSet = rootProject.project("cdm-core").sourceSets.main.get() + source = cdmCoreSourceSet.allJava + classpath = files(arrayOf(cdmCoreSourceSet.compileClasspath, cdmCoreSourceSet.output)) + + publicApi.forEach { include(it) } + } + +val buildJavadocPublicApiWithDeps = + tasks.register("buildJavadocPublicApiWithDeps") { + description = + "Generate Javadoc for the CDM subproject - included deprecated classes and methods." + title = "NetCDF-Java CDM Public API v${version} - with deprecations" + destinationDir = layout.buildDirectory.dir("javadoc-with-deprecations").get().asFile + + options { showFromPublic() } + + val cdmCoreSourceSet = rootProject.project("cdm-core").sourceSets.main.get() + source = cdmCoreSourceSet.allJava + classpath = files(arrayOf(cdmCoreSourceSet.compileClasspath, cdmCoreSourceSet.output)) + + publicApi.forEach { include(it) } + } + +val buildJavadocAll = + tasks.register("buildJavadocAll") { + description = "Generate Javadoc for all Java subprojects." + + title = "NetCDF-Java All API v${version}" + destinationDir = layout.buildDirectory.dir("javadocAll").get().asFile + // list of public artifacts managed by build-logic/src/main/kotlin/base-conventions.gradle.kts + val publicArtifacts = project.extra.get("public.artifacts") + if (publicArtifacts is List<*>) { + publicArtifacts.forEach { + val subprojectSourceSet = rootProject.project("${it}").sourceSets.main.get() + source = subprojectSourceSet.allJava + classpath = files(arrayOf(subprojectSourceSet.compileClasspath, subprojectSourceSet.output)) + } + } else { + logger.error("Cannot access the list of public artifacts. JavadocAll will be incomplete!") + } + } + +// Documentation build using Docker +val catalogs = extensions.getByType() + +val docTheme = + "unidata-jekyll-docs:${catalogs.named("libs").findVersion("unidata-doc-theme").get().requiredVersion}" + +val isGitHub = System.getenv("GITHUB_ACTIONS") != null +val imageBaseUrl = if (isGitHub) "ghcr.io/unidata" else "docker.unidata.ucar.edu" +val dockerImage = "${imageBaseUrl}/${docTheme}" + +val siteBuildDir = layout.buildDirectory.dir("site") + +val buildJekyllSite = + tasks.register("buildJekyllSite") { + group = "documentation" + description = "Build the netCDF-Java documentation." + val buildDocInputs = fileTree(".") + buildDocInputs.exclude("build/", ".gradle", ".jekyll-cache") + inputs.files(buildDocInputs) + outputs.dir(siteBuildDir) + commandLine( + "docker", + "run", + "--rm", + "-e", + "SRC_DIR=/netcdf-java/docs/src/site", + "-v", + "$rootDir:/netcdf-java", + "-v", + "./${relativePath(siteBuildDir.get().toString())}:/site", + dockerImage, + "build", + ) + } + +tasks.register("serveJekyllSite") { + group = "documentation" + description = "Start a local server to live edit the netCDF-Java documentation." + commandLine("ls") + commandLine( + "docker", + "run", + "--rm", + "-d", + "--name", + "netcdf-java-docs-server", + "-e", + "SRC_DIR=/netcdf-java/docs/src/site", + "-v", + "$rootDir:/netcdf-java", + "-p", + "4005:4005", + dockerImage, + "serve", + "--livereload", + ) + standardOutput = OutputStream.nullOutputStream() + doLast { + val msg = "NetCDF-Java documentation available at http://localhost:4005" + val bannerBorder = String(CharArray(msg.length + 4) { '#' }) + println("\n${bannerBorder}\n# ${msg} #\n${bannerBorder}") + } +} + +tasks.register("stopServe") { + group = "documentation" + description = "Stop the local server used while live editing the netCDF-Java documentation." + commandLine("docker", "stop", "netcdf-java-docs-server") + delete("$projectDir/src/site/Gemfile") + delete("$projectDir/src/site/Gemfile.lock") +} + +tasks.withType().configureEach { + // show deprecation warnings + // we have a github action that checks to make sure we are not + // using deprecated code in the tutorial examples + options.compilerArgs.add("-Xlint:deprecation") +} + +spotless { + java { + target("src/test/java/**/*.java") + eclipse() + .configFile( + "$rootDir/project-files/code-styles/eclipse-style-guide.xml", + "src/test/style/style-override.properties", + ) + encoding("UTF-8") + } +} + +tasks.build { + dependsOn(buildJekyllSite, buildJavadocPublicApi, buildJavadocPublicApiWithDeps, buildJavadocAll) +} diff --git a/docs/src/site/_config.yml b/docs/src/site/_config.yml index a151f2d715..db79bf45ea 100644 --- a/docs/src/site/_config.yml +++ b/docs/src/site/_config.yml @@ -2,7 +2,7 @@ theme: unidata-jekyll-theme # this will appear in an HTML meta tag, sidebar, and perhaps elsewhere -docset_version: 5.9 +docset_version: 5.10 # this appears on the top navigation bar next to the home button topnav_title: netCDF-Java diff --git a/docs/src/site/pages/netcdfJava/Upgrade.md b/docs/src/site/pages/netcdfJava/Upgrade.md index 2cb1aac1b6..ed8ab18edf 100644 --- a/docs/src/site/pages/netcdfJava/Upgrade.md +++ b/docs/src/site/pages/netcdfJava/Upgrade.md @@ -1,6 +1,6 @@ --- title: Upgrading to netCDF-Java version 5.x -last_updated: 2025-08-08 +last_updated: 2025-10-30 sidebar: netcdfJavaTutorial_sidebar toc: false permalink: upgrade.html @@ -8,19 +8,11 @@ permalink: upgrade.html ## Requirements -* Java 8 or later is required - -## Overview - -A number of API enhancements have been made to take advantage of evolution in the Java language, for example _try-with-resource_ and _foreach_ constructs. -The use of these make code simpler and more reliable. - -Deprecated classes and methods have been removed, and the module structure and third-party jar use has been improved. - -Java WebStart has been deprecated as of [Java 9](https://www.oracle.com/technetwork/java/javase/9-deprecated-features-3745636.html#JDK-8184998){:target="_blank"}. -As such, we no longer utilize WebStart. +* Java 8 or later is required to use the library. +* Starting with version 5.10.0, Java 17 is required to build the library. ## Quick Navigation +* [Summary of changes for v5.10.x](#netcdf-java-api-changes-510x) * [Summary of changes for v5.9.x](#netcdf-java-api-changes-59x) * [Summary of changes for v5.8.x](#netcdf-java-api-changes-58x) * [Summary of changes for v5.7.x](#netcdf-java-api-changes-57x) @@ -32,13 +24,22 @@ As such, we no longer utilize WebStart. * [Summary of changes for v5.1.x](#netcdf-java-api-changes-51x) * [Summary of changes for v5.0.x](#netcdf-java-api-changes-50x) +## netCDF-Java API Changes (5.10.x) + +Point release notes: +* [5.10.0](https://github.com/Unidata/netcdf-java/releases/tag/v5.10.0){:target="_blank"} (_yyyy-mm-dd, unreleased_) + +Starting with 5.10.x, the netCDF-Java library requires Java 17 to build (although the build will produce Java 8 bytecode, so the minimum supported version is still Java 8). +Note: we are looking to update the minimum version of the JVM we support for the project. +Please consider taking a moment to participate in the [poll on GitHub](https://github.com/Unidata/netcdf-java/discussions/1468){:target="_blank"}. + ## netCDF-Java API Changes (5.9.x) Point release notes: * [5.9.0](https://github.com/Unidata/netcdf-java/releases/tag/v5.9.0){:target="_blank"} (_2025-08-08_) * [5.9.1](https://github.com/Unidata/netcdf-java/releases/tag/v5.9.1){:target="_blank"} (_2025-09-09_) -The 5.9.x includes bug fixes around zarr support, scaling of projection parameters, and setting of the library path for the netCDF-C library. +The 5.9.x release includes bug fixes around zarr support, scaling of projection parameters, and setting of the library path for the netCDF-C library. Additionally, udunits grammar code is now generated at build time, HTTP errors when disambiguating http/https locations has been improved, and scanning for filter providers is done once during initialization. EnhancementProviders are now loaded upon VariableDS instantiation, fixing a critical multithreaded environment related bug. Several ToolsUI improvements have been made as well. @@ -184,6 +185,16 @@ Remove usages of `org.joda.time` outside of `ucar.nc2.time`. ## netCDF-Java API Changes (5.0.x) +### Overview + +A number of API enhancements have been made to take advantage of evolution in the Java language, for example _try-with-resource_ and _foreach_ constructs. +The use of these make code simpler and more reliable. + +Deprecated classes and methods have been removed, and the module structure and third-party jar use has been improved. + +Java WebStart has been deprecated as of [Java 9](https://www.oracle.com/technetwork/java/javase/9-deprecated-features-3745636.html#JDK-8184998){:target="_blank"}. +As such, we no longer utilize WebStart. + Point release notes: * [5.0.0](https://github.com/Unidata/netcdf-java/releases/tag/v5.0.0){:target="_blank"} (_2019-07-29_) diff --git a/docs/src/site/pages/netcdfJava_tutorial/overview/BuildingFromSource.md b/docs/src/site/pages/netcdfJava_tutorial/overview/BuildingFromSource.md index 97027abc19..74e432dbeb 100644 --- a/docs/src/site/pages/netcdfJava_tutorial/overview/BuildingFromSource.md +++ b/docs/src/site/pages/netcdfJava_tutorial/overview/BuildingFromSource.md @@ -1,6 +1,6 @@ --- title: Building From Source -last_updated: 2025-08-08 +last_updated: 2025-10-30 sidebar: netcdfJavaTutorial_sidebar permalink: building_from_source.html toc: false @@ -10,9 +10,12 @@ toc: false The netCDF-Java source code is hosted on GitHub, and — as of v4.6.1 — we use Gradle to build it. Ant and Maven builds are no longer supported. -To build, you need Git and JDK 8, JDK 11, or JDK 14 installed. +To build, you need Git and, as of v5.9.2, Java 17 or higher (required for Gradle 9.0.0 or higher). +Note that the bytecode produced by the Gradle build will be compatible with Java 8, and the test task will run using JDK 8 (thanks to the Gradle Toolchain feature). +If the build cannot find a suitable JDK for testing, you will need to specify one in your `gradle.properties` file (see the [Gradle toolchain documentation](https://docs.gradle.org/current/userguide/toolchains.html#sec:custom_loc){:target="_blank"} for more details). +The tests can be run using other JDK versions using `testX`, where `X` is the version number of one of the LTS releases of Java (currently `11`, `17`, `21`, or `25`). -First, clone the netCDF-Java repository from Github: +First, clone the netCDF-Java repository from GitHub: ~~~bash git clone -o unidata https://github.com/Unidata/netcdf-java.git netcdf-java diff --git a/gradle.properties b/gradle.properties index be6eaee735..6d08f64bf1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,11 +1,11 @@ # This file is useful for adding gradle config options for use in CI environments, where -# there isn't likely a gradle.properties file at a higher priority level -# See https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties -# note that we used to set some things here, specifically turning off the gradle daemon, -# but don't need to currently (used to be recomended in gradle 3.x, but no longer). -# Keeping this for future reference, and maybe we will need it again in the future. +# there isn't likely a gradle.properties file at a higher priority level, or for safer defaults. +# For more information, see +# https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties -# gradle 6 began publishing maven-metadata.xml files using 256 and 512 sha checksums, but nexus 3 does not -# support that yet (although nexus 2 does) - see https://issues.sonatype.org/browse/NEXUS-23603 -# until it does, we need to set this property as our current snapshots are unusable -systemProp.org.gradle.internal.publish.checksums.insecure=true +# netCDF-Java uses gradle toolschains when running the tests. +# Disable auto-detection of java installations by default to avoid surprises. +# If you run into trouble running the tests, configure gradle to look in specific +# locations on your system for installed JDKs following the instructions at +# https://docs.gradle.org/current/userguide/toolchains.html#sec:custom_loc +org.gradle.java.installations.auto-detect=false diff --git a/gradle.properties.old b/gradle.properties.old new file mode 100644 index 0000000000..be6eaee735 --- /dev/null +++ b/gradle.properties.old @@ -0,0 +1,11 @@ +# This file is useful for adding gradle config options for use in CI environments, where +# there isn't likely a gradle.properties file at a higher priority level +# See https://docs.gradle.org/current/userguide/build_environment.html#sec:gradle_configuration_properties +# note that we used to set some things here, specifically turning off the gradle daemon, +# but don't need to currently (used to be recomended in gradle 3.x, but no longer). +# Keeping this for future reference, and maybe we will need it again in the future. + +# gradle 6 began publishing maven-metadata.xml files using 256 and 512 sha checksums, but nexus 3 does not +# support that yet (although nexus 2 does) - see https://issues.sonatype.org/browse/NEXUS-23603 +# until it does, we need to set this property as our current snapshots are unusable +systemProp.org.gradle.internal.publish.checksums.insecure=true diff --git a/gradle/README.md b/gradle/README.md deleted file mode 100644 index 8b37708b02..0000000000 --- a/gradle/README.md +++ /dev/null @@ -1,39 +0,0 @@ -`root/`: Contains script plugins that should be applied only to the root project. - -`any/`: Contains script plugins that can be applied to any project, including the root. -Most of what we need in the `any/` directory can be found in these two script plugins: - - 1. `java-library.gradle`: Provides support for building library projects. - For most of our stuff, this is what we want. - Uses the following plugins: - * [java-library](https://docs.gradle.org/current/userguide/java_library_plugin.html) - * `gradle/any/javadoc.gradle` - * `gradle/any/testing.gradle` - * `gradle/any/coverage.gradle` - * `gradle/any/archiving.gradle` - * `gradle/any/publishing.gradle` - - 2. `test-only-projects.gradle`: Provides support for building test only projects. - Uses the following plugins: - * [java](https://docs.gradle.org/current/userguide/java_plugin.html) - * `gradle/any/testing.gradle` - * `gradle/any/coverage.gradle` - - Currently, two gradle subprojects use this - `dap4:d4tests` and `cdm-test`. - -**TODO:** There are still a few Gradle things left to do, but at least we're fully functional at this point. - -1. Address any issues in our plugin scripts identified by gradle in terms of Gradle 7 compatibility. - - ~~~ - Deprecated Gradle features were used in this build, making it incompatible with Gradle 7.0. - Use '--warning-mode all' to show the individual deprecation warnings. - See https://docs.gradle.org/6.5.1/userguide/command_line_interface.html#sec:command_line_warnings - ~~~ - - This is more about doing proactive maintenance with our gradle infrastructure. - I do not want to be in the position of trying to jump three major versions again :-) - -2. Find new dependency license checker - The license plugin we used in the past does not seem to work with the java-library and java-platform plugins. - Sad times. diff --git a/gradle/any/archiving.gradle b/gradle/any/archiving.gradle deleted file mode 100644 index 4dab9f1269..0000000000 --- a/gradle/any/archiving.gradle +++ /dev/null @@ -1,34 +0,0 @@ -// Safe for root project: no plugins applied. - -tasks.withType(Jar).all { // Applies to Jar, War, Ear and ShadowJar tasks. - // Fails the build when an attempt is made to add a duplicate entry to an archive. - duplicatesStrategy = 'fail' - - manifest { - // Subprojects have not yet been configured, but they may want to reassign these attributes. - // So, we're going to delay evaluation by using lazy GStrings: http://goo.gl/zwWnPh - // Evaluation will happen when these strings are read, which should first occur during the UP-TO-DATE - // check of the execution phase. - attributes 'Implementation-Title': "${-> project.title}", - 'Implementation-Version': "${-> project.version}", - 'Implementation-Vendor-Id': "${-> project.group}", - 'Implementation-Vendor': "${-> project.vendor}", - 'Implementation-URL': "${-> project.url}", - 'Created-By': "Gradle $gradle.gradleVersion", - 'Build-Jdk': System.properties['java.version'], - 'Built-By': System.properties['user.name'] - } - - doFirst { - // We cannot add this attribute during the configuration phase because it'll cause the jar task to never - // be UP-TO-DATE: http://goo.gl/kbVWXR. Adding it during the execution phase removes it as an input to - // Gradle's incremental build machinery. - manifest.attributes 'Built-On': project.buildTimestamp // Defined in root project. - } - - from(rootDir.absolutePath) { - include 'LICENSE' - include 'third-party-licenses/' - into 'META-INF/' - } -} diff --git a/gradle/any/coverage.gradle b/gradle/any/coverage.gradle deleted file mode 100644 index 91a0df2680..0000000000 --- a/gradle/any/coverage.gradle +++ /dev/null @@ -1,53 +0,0 @@ -// The jacoco plugin adds the jacocoTestReport task, but only if the java plugin is already applied. -apply plugin: 'jacoco' - -// Will apply to any task of type Test -Closure isExtendedByJacoco = {Task task -> - task.extensions.findByType(JacocoTaskExtension) -} - -Collection tasksExtendedByJacoco = tasks.matching(isExtendedByJacoco) - -tasksExtendedByJacoco.all { - // Add the execution data that Jacoco generates as a task output. - // The data is a record of all the classes visited during test execution. It is self-contained; we don't need to - // provide any source or class directories for it to "work". Those are only necessary for the report. - outputs.file jacoco.destinationFile -} - -/* - * By default, when the 'jacoco' plugin is applied to a project, a 'jacocoTestReport' task will be added that reports - * on the 'main' SourceSet of that project only. For example, ':cdm:jacocoTestReport' reports on the coverage of code - * in cdm/src/main only. - * - * That's fine for :cdm, but we have subprojects that contain no code in their 'main' source set, namely :it, - * and :cdm-test. They only have code in their 'test' SourceSets. As a result, the default - * 'jacocoTestReport' task added to those tasks will generate an empty report, because there's nothing to report on. - * - * We could remedy this by adding main SourceSets from other projects to report on. This would be particularly - * appropriate for :cdm-test, which touches code in damn near every subproject. However, I don't really like that idea - * because we're already producing an aggregate report in :rootJacocoReport. And frankly, that's the only report - * that we REALLY care about; it gets published to Jenkins and Codecov. - * - * Therefore, I'd like to maintain the default behavior: a subproject only reports on how well its own tests cover its - * own 'main' code. That partitioning of reports promotes TRUE unit tests in our project, which--by definition-- - * have scopes constrained to a single subproject (and often a single class). The addition of more true unit tests - * would be a huge benefit to THREDDS: they execute quickly and are the easiest way to improve our lacking code - * coverage. Right now, most of our tests are of the integration variety, straddling several subprojects. - * - * Furthermore, I don't think that subprojects with no 'main' SourceSet like ':cdm-test' and ':it' are long for this - * world. They are a relic of the old Maven build. The NeedsCdmUnitTest category obviates the need for a separate - * ':cdm-test' module and the separation of tests that need a running TDS could be better done by adding an - * 'integTest' SourceSet to :tds. So in the future, we won't be generating empty Jacoco reports. - */ -tasks.withType(JacocoReport).all { // Will apply to "::jacocoTestReport" and ":rootJacocoReport". - group = 'Reports' - dependsOn tasks.withType(Test) - - // By default, JacocoReport runs onlyIf ALL of the executionData exist. We want it to run if ANY exists. - setOnlyIf { - executionData.any { - it.exists() - } - } -} diff --git a/gradle/any/dependencies.gradle b/gradle/any/dependencies.gradle deleted file mode 100644 index 4bd2093935..0000000000 --- a/gradle/any/dependencies.gradle +++ /dev/null @@ -1,61 +0,0 @@ -// Gradle dependency management. -// This technique was inspired by: http://stackoverflow.com/questions/9547170 -// -// Usage: -// All gradle build scripts will apply this script using: -// apply from: "$rootDir/gradle/any/dependencies.gradle" -// - -// make shared-mvn-coords available to all -// this script plugin specifically needs the depVersion variable -apply from: "$rootDir/gradle/any/shared-mvn-coords.gradle" - -//================================================ Repositories ================================================// - -repositories { - mavenCentral() - - // All of the hosted repositories below could be replaced with: - // url "https://artifacts.unidata.ucar.edu/repository/unidata-all/" - // which is a group repository that contains all other repositories. However, I prefer to list all source - // repos explicitly so that we know where all artifacts ultimately come from. - - // Hosted release repositories. - maven { - // For visad (edu.ucar:cdm-vis5d) and jj2000 (:grib). - // Also for releases of dtswar (dap2) and d4ts (dap4) (used to stand up local servers for tests) - url "https://artifacts.unidata.ucar.edu/repository/unidata-releases/" - } - maven { - // For snapshots of dtswar and d4ts (used to stand up local servers for tests) - url "https://artifacts.unidata.ucar.edu/repository/unidata-snapshots/" - } - - maven { - url 'https://artifacts.unidata.ucar.edu/repository/unidata-3rdparty/' - content { - // this repository *only* used for artifacts with group "org.bounce" (NcML editor in toolsUI) - includeGroup 'org.bounce' - } - } -} - -//////////////////////////// Transitive dependency replacements and exclusions //////////////////////////// -// Executes the given closure against all objects in this collection, and any objects subsequently added to this -// collection. See org.gradle.api.DomainObjectCollection.all(Closure) - -configurations.all { - resolutionStrategy.dependencySubstitution { - // Replace every instance of "commons-logging" in the dependency tree with "jcl-over-slf4j". This effectively - // converts every JCL call to an slf4j call. We can see the first-level dependencies that drag in - // "commons-logging" by commenting-out the rule below and running: - // - // >./gradlew -q :cdm:cdm-core:dependencyInsight --configuration compileClasspath --dependency commons-logging - // - // commons-logging:commons-logging:1.2 - // +--- org.apache.httpcomponents:httpclient:4.5.12 - // | ... - // - substitute module('commons-logging:commons-logging') because 'we want to control JCL logging with slf4j' using module("org.slf4j:jcl-over-slf4j:${depVersion.slf4j}") - } -} diff --git a/gradle/any/java-library.gradle b/gradle/any/java-library.gradle deleted file mode 100644 index 8d231cdb3f..0000000000 --- a/gradle/any/java-library.gradle +++ /dev/null @@ -1,50 +0,0 @@ -apply plugin: 'java-library' -apply from: "$rootDir/gradle/any/javadoc.gradle" -apply from: "$rootDir/gradle/any/testing.gradle" -apply from: "$rootDir/gradle/any/archiving.gradle" -apply from: "$rootDir/gradle/any/coverage.gradle" -apply from: "$rootDir/gradle/any/publishing.gradle" -apply from: "$rootDir/gradle/any/spotless.gradle" - -apply plugin: 'maven-publish' - -def sourceJar = tasks.register('sourceJar', Jar) { - setArchiveClassifier('sources') - from sourceSets.main.allJava -} - -def javadocJar = tasks.register('javadocJar', Jar) { - dependsOn javadoc - setArchiveClassifier('javadoc') - from files(javadoc.destinationDir) -} - -publish.configure { - dependsOn sourceJar - dependsOn javadocJar -} - -publishing { - publications { - mavenSources(MavenPublication) { - from components.java - artifact tasks.sourceJar - artifact tasks.javadocJar - } - } -} - -tasks.withType(JavaCompile).configureEach { - options.encoding = 'UTF-8' - // whatever java is being used to run gradle will be used to compile java classes - // in src/main, but this makes sure it outputs bytecode compatible with Java 8 - if (JavaVersion.current() != JavaVersion.VERSION_1_8) { - options.setRelease(8) - } else { - // java 8 does not have a release option, so use source and target compatibility - setSourceCompatibility(JavaVersion.VERSION_1_8) - setTargetCompatibility(JavaVersion.VERSION_1_8) - } - // show deprecation details - //options.compilerArgs = ['-Xlint:deprecation'] -} diff --git a/gradle/any/javadoc.gradle b/gradle/any/javadoc.gradle deleted file mode 100644 index 49834e5f1d..0000000000 --- a/gradle/any/javadoc.gradle +++ /dev/null @@ -1,21 +0,0 @@ -tasks.withType(Javadoc).all { // Common Javadoc config. - group = 'Documentation' - - options.encoding = 'UTF-8' - options.docEncoding = 'UTF-8' - options.charSet = 'UTF-8' - - // When instances of JDK classes appear in our Javadoc (e.g. "java.lang.String"), create links out of them to - // Oracle's JavaSE 8 Javadoc. - options.links('https://docs.oracle.com/en/java/javase/11/docs/api/') - - if (JavaVersion.current().isJava8Compatible()) { - // doclint="all" (the default) will identify 100s of errors in our docs and cause no Javadoc to be generated. - // So, turn it off. See http://blog.joda.org/2014/02/turning-off-doclint-in-jdk-8-javadoc.html - options.addStringOption('Xdoclint:none', '-quiet') - - // TODO: Actually use doclint and fix the errors it finds. Below are the options that the Gradle project uses. - // At the very least, try 'reference'. - // options.addStringOption 'Xdoclint:syntax,html,reference', '-quiet' - } -} diff --git a/gradle/any/properties.gradle b/gradle/any/properties.gradle deleted file mode 100644 index 1932be4932..0000000000 --- a/gradle/any/properties.gradle +++ /dev/null @@ -1,53 +0,0 @@ -ext { - // Used to publish Maven artifacts to Unidata's Nexus repository, https://artifacts.unidata.ucar.edu - NEXUS_USERNAME_KEY = 'nexus.username' - NEXUS_PASSWORD_KEY = 'nexus.password' - - // Used to publish static analysis report to https://sonarcloud.io/dashboard?id=edu.ucar.unidata:ncj - SONARQUBE_USER_TOKEN_KEY = 'sonarqube.user.token.ncj' - - // Do not propagate these system properties to the Gradle test executors. - systemPropertiesExclude = [ - // Passing this causes "java.lang.ClassNotFoundException: org.jacoco.agent.rt.internal_932a715.PreMain" - // See https://discuss.gradle.org/t/jacoco-related-failure-in-multiproject-build/6216 - 'user.dir' - ] -} - -String getPropertyOrFailBuild(String key) { - if (!hasProperty(key)) { - throw new GradleException("You must define the '$key' property.") - } else { - property(key) as String - } -} - -/** - * Add the system properties set on the Gradle process to the specified map, except for the properties in - * {@code systemPropertiesExclude}. Existing entries in {@code targetProps} are NOT overwritten by system - * properties that have the same key. - * - * @param targetProps the map to add properties to. - * @return {@code targetProps}, now containing system properties. - */ -Map addFilteredSysProps(Map targetProps) { - assert targetProps != null - - System.properties.each {key, value -> - if (!systemPropertiesExclude.contains(key) && - !targetProps.containsKey(key)) { // Don't overrwrite any existing entries. - targetProps[key] = value - } - } - - targetProps -} - -// It isn't possible to share methods, but we can share extra properties containing a closure, which boils down to -// the same thing. We're going to do that by converting our methods to closures. -// See http://stackoverflow.com/questions/18715137/extract-common-methods-from-gradle-build-script -ext { - // Export methods by turning them into closures - getPropertyOrFailBuild = this.&getPropertyOrFailBuild - addFilteredSysProps = this.&addFilteredSysProps -} diff --git a/gradle/any/protobuf.gradle b/gradle/any/protobuf.gradle deleted file mode 100644 index d82f7580b1..0000000000 --- a/gradle/any/protobuf.gradle +++ /dev/null @@ -1,24 +0,0 @@ -apply plugin: 'com.google.protobuf' -apply plugin: 'java' -apply plugin: 'jacoco' - -protobuf { - protoc { - artifact = "com.google.protobuf:protoc:${depVersion.protobuf}" - } -} - -jacocoTestReport { - afterEvaluate { - // Exclude proto generated sources and classes from the coverage reports - sourceDirectories.setFrom(files(sourceDirectories.files.collect { - fileTree(dir: it, excludes:['**/*Proto.java']) - })) - classDirectories.setFrom(files(classDirectories.files.collect { - fileTree(dir: it, excludes:['**/*Proto.class', '**/*Proto$*.class']) - })) - } -} - -compileJava.dependsOn 'generateProto' -sourceJar.dependsOn 'generateProto' diff --git a/gradle/any/publishing.gradle b/gradle/any/publishing.gradle deleted file mode 100644 index 66addd7f6f..0000000000 --- a/gradle/any/publishing.gradle +++ /dev/null @@ -1,25 +0,0 @@ -// safe to apply to root project. -apply plugin: 'maven-publish' - -tasks.withType(GenerateModuleMetadata) { - enabled = false -} - -publishing { - repositories { - String version = rootProject.version as String - if (version.endsWith('SNAPSHOT')) { - maven { - name = 'snapshots' - url = 'https://artifacts.unidata.ucar.edu/repository/unidata-snapshots/' - // Set credentials in root/publishing.gradle. - } - } else { - maven { - name = 'releases' - url = 'https://artifacts.unidata.ucar.edu/repository/unidata-releases/' - // Set credentials in root/publishing.gradle. - } - } - } -} diff --git a/gradle/any/shared-mvn-coords.gradle b/gradle/any/shared-mvn-coords.gradle deleted file mode 100644 index 1d566b1d25..0000000000 --- a/gradle/any/shared-mvn-coords.gradle +++ /dev/null @@ -1,24 +0,0 @@ -// Applied in buildscript blocks that need to declare dependencies directly on the classpath, and in -// gradle/any/dependencies.gradle (which makes this available to every project) -ext { - // Extra properties must be declared in the "ext" namespace. After declaration, they can be used without prefix. - // These will be inherited by any project buildscript that needs to reference a dependency or plugin by its full - // maven coordinates. - - // plugin version management - buildPlugins = [:] - buildPlugins.shadow = 'com.gradleup.shadow:com.gradleup.shadow.gradle.plugin:8.3.5' - buildPlugins.sonarqube = 'org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.0' - buildPlugins.spotless = 'com.diffplug.spotless:spotless-plugin-gradle:5.17.1' - buildPlugins.protobuf = 'com.google.protobuf:protobuf-gradle-plugin:0.9.5' - buildPlugins.depcheck = 'org.owasp:dependency-check-gradle:8.2.1' - - // slf4j version is declared in a place where we cannot use the tds-platform project to handle resolving versions - // (e.g. gradle/any/dependencies.gradle, for transitive dependency replacement purposes) - // best we can do is define the version here, which can then be applied in all of the places we need the full maven - // coords. - depVersion = [:] - depVersion.slf4j = '2.0.17' - depVersion.protobuf = '4.31.1' - depVersion.grpc = '1.73.0' -} diff --git a/gradle/any/spotless.gradle b/gradle/any/spotless.gradle deleted file mode 100644 index 9fa45e9c42..0000000000 --- a/gradle/any/spotless.gradle +++ /dev/null @@ -1,11 +0,0 @@ -apply plugin: 'java' -apply plugin: 'com.diffplug.spotless' - -spotless { - java { - // target java files in source directories (will not pick up generated sources) - target 'src/*/java/**/*.java' - eclipse().configFile "$rootDir/project-files/code-styles/eclipse-style-guide.xml" - encoding 'UTF-8' - } -} diff --git a/gradle/any/test-only-projects.gradle b/gradle/any/test-only-projects.gradle deleted file mode 100644 index 6c47dd3863..0000000000 --- a/gradle/any/test-only-projects.gradle +++ /dev/null @@ -1,19 +0,0 @@ -apply plugin: 'java' -apply from: "$rootDir/gradle/any/testing.gradle" -apply from: "$rootDir/gradle/any/coverage.gradle" -apply from: "$rootDir/gradle/any/spotless.gradle" - -tasks.withType(JavaCompile).configureEach { - options.encoding = 'UTF-8' - // whatever java is being used to run gradle will be used to compile java classes - // in src/main, but this makes sure it outputs bytecode compatible with Java 8 - if (JavaVersion.current() != JavaVersion.VERSION_1_8) { - options.setRelease(8) - } else { - // java 8 does not have a release option, so use source and target compatibility - setSourceCompatibility(JavaVersion.VERSION_1_8) - setTargetCompatibility(JavaVersion.VERSION_1_8) - } -// // show deprecation details -// //options.compilerArgs = ['-Xlint:deprecation'] -} diff --git a/gradle/any/testing.gradle b/gradle/any/testing.gradle deleted file mode 100644 index 17a1867bc0..0000000000 --- a/gradle/any/testing.gradle +++ /dev/null @@ -1,64 +0,0 @@ -apply plugin: 'java' -apply from: "$rootDir/gradle/any/properties.gradle" - -tasks.withType(Test).all { - // Propagates system properties set on the Gradle process to the test executors. - addFilteredSysProps(systemProperties) - jvmArgs = ['-Xmx1024m'] - - if (isJenkins && !isRelease) { - // On Jenkins, don't let test failures fail the build unless we are doing we release; we want - // the full test report. - ignoreFailures = true - } else { - // Otherwise, fail the build at the first sign of failure. - ignoreFailures = false - } - - useJUnit { - // if we are not explicitly trying to run all tests, allow some categories to be ignored - // test tasks named 'simpleTests' do not depend on cdm-test-utils, so do not apply category - // filters (e.g. :native-compression:libaec-jna:simpleTests) - if (!runAllTests && !name.equals('simpleTests')) { - if (isJenkins) { - excludeCategories 'ucar.unidata.util.test.category.NotJenkins' - } - - if (isPullRequestCheck) { - excludeCategories 'ucar.unidata.util.test.category.NotPullRequest' - excludeCategories 'ucar.unidata.util.test.category.NeedsExternalResource' - } - - if (!isContentRootAvailable && !isJenkins) { // Don't skip tests on Jenkins, except NotJenkins ones. - excludeCategories 'ucar.unidata.util.test.category.NeedsContentRoot' - } - - if (!isCdmUnitTestDirAvailable && !isJenkins) { // Don't skip tests on Jenkins, except NotJenkins ones. - excludeCategories 'ucar.unidata.util.test.category.NeedsCdmUnitTest' - } - - if (!isRdaDataAvailable) { - excludeCategories 'ucar.unidata.util.test.category.NeedsRdaData' - } - - if (!runSlowTests) { - excludeCategories 'ucar.unidata.util.test.category.Slow' - } - - if (!isUcarNetworkAvailable) { - excludeCategories 'ucar.unidata.util.test.category.NeedsUcarNetwork' - } - - if (skipDockerTests) { - // dap4 - exclude 'dap4/test/**' - // httpservices - exclude 'ucar/httpservices/**' - exclude 'ucar/nc2/util/net/**' - // opendap - exclude 'opendap/test/**' - exclude 'ucar/nc2/dods/**' - } - } - } -} diff --git a/gradle/gretty/log4j2Config.xml b/gradle/gretty/log4j2Config.xml deleted file mode 100644 index 6a26008fef..0000000000 --- a/gradle/gretty/log4j2Config.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/gradle/gretty/logback.xml b/gradle/gretty/logback.xml deleted file mode 100644 index 791f303e3c..0000000000 --- a/gradle/gretty/logback.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - %highlight([%d{HH:mm:ss.SSS} %-5level %logger{36}]) %message%n - - - - - - - - \ No newline at end of file diff --git a/gradle/gretty/tomcat/README.md b/gradle/gretty/tomcat/README.md deleted file mode 100644 index ff26d2c2fa..0000000000 --- a/gradle/gretty/tomcat/README.md +++ /dev/null @@ -1,24 +0,0 @@ -Keystore was generated with: - -~~~bash -$ keytool -genkey -alias tds -keyalg RSA -validity 3650 -keystore keystore -Enter keystore password: secret666 -Re-enter new password: secret666 -What is your first and last name? - [Unknown]: localhost -What is the name of your organizational unit? - [Unknown]: -What is the name of your organization? - [Unknown]: -What is the name of your City or Locality? - [Unknown]: -What is the name of your State or Province? - [Unknown]: -What is the two-letter country code for this unit? - [Unknown]: -Is CN=localhost, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown correct? - [no]: yes - -Enter key password for - (RETURN if same as keystore password): RETURN -~~~ \ No newline at end of file diff --git a/gradle/gretty/tomcat/keystore b/gradle/gretty/tomcat/keystore deleted file mode 100644 index e291148fea..0000000000 Binary files a/gradle/gretty/tomcat/keystore and /dev/null differ diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000000..8a26d1cdd5 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,120 @@ +[versions] +netcdf-java = "5.10.0-SNAPSHOT" + +unidata-doc-theme = "0.0.6" + +# 3rd party dependencies +awssdk = "2.31.76" +awssdkv1 = "1.12.649" +commons-math3 = "3.6.1" +bounce = "0.18" +grpc = "1.73.0" +guava = "33.4.8-jre" +httpcomponents = "4.5.13" +jackson = "2.19.1" +jcommander = "1.78" +jdom2 = "2.0.6.1" +jfree-jcommon = "1.0.23" +jfree-jfreechart = "1.0.19" +jgoodies-forms = "1.6.0" +jj2000 = "5.4" +joda-time = "2.12.7" +jsr305 = "3.0.2" +jna = "5.17.0" +lgooddatepicker = "10.3.1" +mcidas = "20231121" +protobuf = "4.31.1" +re2j = "1.3" +sensorweb-waterml = "2.7.0" +slf4j = "2.0.17" +tomcat-annotations = "6.0.53" +visad = "2.0-20130124" +xmlbeans = "3.1.0" + +#testing only +commons-compress = "1.12" +commons-io = "2.5" +jsoup = "1.11.2" +junit4 = "4.13.1" +junit5 = "5.14.0" +junitparams = "1.0.5" +mockito-core = "2.28.2" +testcontainers = "1.19.7" +truth = "1.0" + +# runtime only +logback-classic = "1.3.15" + +[libraries] + +amazonaws-s3v1 = { module = "com.amazonaws:aws-java-sdk-s3", version.ref = "awssdkv1" } +awssdk-apacheClient = { module = "software.amazon.awssdk:apache-client", version.ref = "awssdk" } +awssdk-bom = { module = "software.amazon.awssdk:bom", version.ref = "awssdk" } +awssdk-s3 = { module = "software.amazon.awssdk:s3" } +awssdk-sts = { module = "software.amazon.awssdk:sts" } +beust-jcommander = { module = "com.beust:jcommander", version.ref = "jcommander" } +# Used by NcmlEditor. Abandoned, no updates since 2013. https://sourceforge.net/projects/bounce/ +# https://sourceforge.net/p/bounce/svn/HEAD/tree/src/main/java/org/bounce/ +bounce = {module = "org.bounce:bounce", version.ref = "bounce" } +commons-math3 = { module = "org.apache.commons:commons-math3", version.ref = "commons-math3" } +findbugs-jsr305 = { module = "com.google.code.findbugs:jsr305", version.ref = "jsr305" } +grpc-nettyShaded = { module = "io.grpc:grpc-netty-shaded", version.ref = "grpc"} +grpc-protocGenGrpcJava = { module = "io.grpc:protoc-gen-grpc-java", version.ref = "grpc" } +grpc-protobuf = { module = "io.grpc:grpc-protobuf", version.ref = "grpc" } +grpc-stub = { module = "io.grpc:grpc-stub", version.ref = "grpc" } +grpc-testing = { module = "io.grpc:grpc-testing", version.ref = "grpc" } +guava = { module = "com.google.guava:guava", version.ref = "guava" } +httpcomponents-api = { module = "org.apache.httpcomponents:httpclient", version.ref = "httpcomponents" } +httpcomponents-httpmime = { module = "org.apache.httpcomponents:httpmime", version.ref = "httpcomponents" } +jackson-bom = { module = "com.fasterxml.jackson:jackson-bom", version.ref = "jackson" } +jackson-core = { module = "com.fasterxml.jackson.core:jackson-core" } +jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind" } +jdom2 = { module = "org.jdom:jdom2", version.ref = "jdom2" } +# http://www.jfree.org/ +jfree-jcommon = { module = "org.jfree:jcommon", version.ref = "jfree-jcommon" } +jfree-jfreechart = { module = "org.jfree:jfreechart", version.ref = "jfree-jfreechart" } +# http://www.jgoodies.com/. Latest version is 1.9.0, but there is breakage when we try to upgrade. +jgoodies-forms = { module = "com.jgoodies:jgoodies-forms", version.ref = "jgoodies-forms" } +jj2000 = { module = "edu.ucar:jj2000", version.ref = "jj2000" } +jodaTime = { module = "joda-time:joda-time", version.ref = "joda-time" } +jna = { module = "net.java.dev.jna:jna", version.ref = "jna" } +jsoup = { module = "org.jsoup:jsoup", version.ref = "jsoup" } +# LGoodDatePicker - swing calendar widget used in TdsMonitor +lgooddatepicker = { module = "com.github.lgooddatepicker:LGoodDatePicker", version.ref = "lgooddatepicker" } +protobuf = { module = "com.google.protobuf:protobuf-java", version.ref = "protobuf" } +protobuf-protoc = { module = "com.google.protobuf:protoc", version.ref = "protobuf" } +re2j = { module = "com.google.re2j:re2j", version.ref = "re2j" } +sensorweb-xmlGmlV321 = { module = "org.n52.sensorweb:52n-xml-gml-v321", version.ref = "sensorweb-waterml" } +sensorweb-xmlOmV20 = { module = "org.n52.sensorweb:52n-xml-om-v20", version.ref = "sensorweb-waterml" } +sensorweb-xmlSamplingV20 = { module = "org.n52.sensorweb:52n-xml-sampling-v20", version.ref = "sensorweb-waterml" } +sensorweb-xmlSweCommonV20 = { module = "org.n52.sensorweb:52n-xml-sweCommon-v20", version.ref = "sensorweb-waterml" } +sensorweb-xmlWaterMLV20 = { module = "org.n52.sensorweb:52n-xml-waterML-v20", version.ref = "sensorweb-waterml" } +slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } +ssec-visad = { module = "edu.wisc.ssec:visad", version.ref = "visad" } +ssec-visadMcidasSlimUcarNs = { module = "edu.wisc.ssec:visad-mcidas-slim-ucar-ns", version.ref = "mcidas" } +tomcat-annotationsApi = { module="org.apache.tomcat:annotations-api", version.ref = "tomcat-annotations" } +xmlbeans = { module = "org.apache.xmlbeans:xmlbeans", version.ref = "xmlbeans"} + +# test only +commons-compress = { module = "org.apache.commons:commons-compress", version.ref = "commons-compress" } +commons-io = { module = "commons-io:commons-io", version.ref = "commons-io" } +google-truth = { module = "com.google.truth:truth", version.ref = "truth" } +junit4 = { module = "junit:junit", version.ref = "junit4" } +junit5-bom = { module = "org.junit:junit-bom", version.ref = "junit5" } +junit5-vintageEngine = { module = "org.junit.vintage:junit-vintage-engine" } +junit5-platformLauncher = { module = "org.junit.platform:junit-platform-launcher" } # version controlled by gradle? +mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockito-core" } +testcontainers = { module = "org.testcontainers:testcontainers", version.ref = "testcontainers" } +pragmatists-junitparams = { module = "pl.pragmatists:JUnitParams", version.ref = "junitparams" } + +# runtime only +logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "logback-classic" } + +[plugins] +# versions = { id = "com.github.ben-manes.versions", version = "0.45.0" } +cyclonedx-bom = { id = "org.cyclonedx.bom", version = "3.0.1" } +execfork = { id = "com.github.psxpaul.execfork", version = "0.2.2"} +javacc = { id = "org.javacc.javacc", version = "4.0.1" } +protobuf = { id = "com.google.protobuf", version = "0.9.5" } +shadow = { id = "com.gradleup.shadow", version = "9.2.2" } +spotless = { id = "com.diffplug.spotless", version = "8.0.0" } diff --git a/gradle/root/coverage.gradle b/gradle/root/coverage.gradle deleted file mode 100644 index 5d0ac2dd9b..0000000000 --- a/gradle/root/coverage.gradle +++ /dev/null @@ -1,90 +0,0 @@ -if (!name.equals(rootProject.name)) { - throw new GradleException("This script plugin should only be applied to the root project, not '$name'.") -} - -// Without declaring a repository, this plugin script will fail with: -// Execution failed for task ':jacocoMerge'. -// > Could not resolve all files for configuration ':jacocoAnt'. -repositories { - mavenCentral() -} - -apply plugin: 'jacoco' -apply plugin: 'base' // Gives us the "clean" task for removing rootJacocoReport's output. - -// Modified from: -// https://gist.github.com/tsjensen/d8b9ab9e6314ae2f63f4955c44399dad - -def getProjectList() { - // These projects are considered. Replace with a different list as needed. - subprojects + project -} - -// task to merge the exec files produced by subprojects. -task jacocoMerge(type: JacocoMerge) { - group = LifecycleBasePlugin.VERIFICATION_GROUP - description = 'Merge the JaCoCo data files from all subprojects into one' - // Need all projects to be evaluated, otherwise the hasPlugin('jacoco') will not find anything. - gradle.projectsEvaluated { - // an empty FileCollection used to collect exec files from subprojects. - FileCollection execFiles = project.objects.fileCollection() - getProjectList().each {Project subproject -> - if (subproject.plugins.hasPlugin('jacoco')) { - def testTasks = subproject.tasks.withType(Test) - // ensure that .exec files are actually present - dependsOn(testTasks) - - testTasks.each {Test task -> - // The JacocoTaskExtension is the source of truth for the location of the .exec file. - JacocoTaskExtension extension = task.getExtensions().findByType(JacocoTaskExtension.class) - if (extension != null) { - execFiles.from extension.getDestinationFile() - } - } - } - } - - executionData = execFiles - - doFirst { - // .exec files might be missing if a project has no tests. Filter in execution phase. - executionData = executionData.filter { - it.canRead() - } - } - } -} - -def getReportTasks(JacocoReport pRootTask) { - getProjectList().collect { - it.tasks.withType(JacocoReport).findAll { - it != pRootTask - } - }.flatten() -} - -task jacocoRootReport(type: JacocoReport, dependsOn: tasks.jacocoMerge) { - group = LifecycleBasePlugin.VERIFICATION_GROUP - description = 'Generates an aggregate report from all subprojects' - - executionData.from tasks.jacocoMerge.destinationFile - - reports { - xml.enabled = true - html.enabled = true - csv.enabled = false - } - - // subprojects must be evaluated so that we can grab their source and class directories. - gradle.projectsEvaluated { - // The JacocoReport tasks are the source of truth for class files and sources. - def reportTasks = getReportTasks(tasks.jacocoRootReport) - - classDirectories.from project.files({ - reportTasks.collect {it.classDirectories}.findAll {it != null} - }) - sourceDirectories.from project.files({ - reportTasks.collect {it.sourceDirectories}.findAll {it != null} - }) - } -} diff --git a/gradle/root/dependency-check.gradle b/gradle/root/dependency-check.gradle deleted file mode 100644 index 3041778c3a..0000000000 --- a/gradle/root/dependency-check.gradle +++ /dev/null @@ -1,14 +0,0 @@ -apply plugin: 'org.owasp.dependencycheck' - -dependencyCheck { - analyzers { - assemblyEnabled = false - } - data { - directory="$rootDir/project-files/owasp-dependency-check/nvd" - } - scanConfigurations = ['compileClasspath', 'runtimeClasspath'] - suppressionFile = "$rootDir/project-files/owasp-dependency-check/dependency-check-suppression.xml" - // fail the build if any vulnerable dependencies are identified (any CVSS score > 0). - failBuildOnCVSS = 0 -} diff --git a/gradle/root/fatJars.gradle b/gradle/root/fatJars.gradle deleted file mode 100644 index 6868b31de0..0000000000 --- a/gradle/root/fatJars.gradle +++ /dev/null @@ -1,198 +0,0 @@ -// In Gradle, dependencies specified in a parent's buildscript {} block are visible to all children. -// However, that behavior doesn't seem to hold for script plugins (this file) applied from the parent script. -// So we have to repeat ourselves. See the root project's build script for more notes on buildscript {} block weirdness. -buildscript { - // get buildPlugins map - apply from: "$rootDir/gradle/any/shared-mvn-coords.gradle" - - repositories { - mavenCentral() - gradlePluginPortal() - } - dependencies { - classpath buildPlugins.shadow // We want to import the ShadowJar class. - } -} - -if (!name.equals(rootProject.name)) { - throw new GradleException("This script plugin should only be applied to the root project, not '$name'.") -} - -// need this to resolve dependencies defined through the netcdf-java-platform project -apply from: "$rootDir/gradle/any/dependencies.gradle" -apply plugin: 'com.gradleup.shadow' - -// todo: this indicates we need have too much logic in our script plugin -// This script plugin is a candidate for moving into buildSrc. -import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar - -configurations { - // set target attribute to ensure right guava variant is selected - // https://stackoverflow.com/a/77399208/2345036 - ncIdv { - attributes.attribute(TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE, objects.named(TargetJvmEnvironment.class, TargetJvmEnvironment.STANDARD_JVM)) - } - netcdfAll { - attributes.attribute(TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE, objects.named(TargetJvmEnvironment.class, TargetJvmEnvironment.STANDARD_JVM)) - } - toolsUI { - attributes.attribute(TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE, objects.named(TargetJvmEnvironment.class, TargetJvmEnvironment.STANDARD_JVM)) - } -} - -dependencies { - ncIdv enforcedPlatform(project(':netcdf-java-platform')) - ncIdv project(':cdm:cdm-core') - ncIdv project(':cdm:cdm-image') - ncIdv project(':cdm:cdm-radial') - ncIdv project(':cdm:cdm-misc') - ncIdv project(':bufr') - ncIdv project(':grib') - ncIdv project(':opendap') - ncIdv project(':dap4') - ncIdv project(':visad:cdm-mcidas') - ncIdv project(':visad:cdm-vis5d') - ncIdv project(':httpservices') - ncIdv project(':legacy') - ncIdv project(':native-compression:libaec-native') - - netcdfAll enforcedPlatform(project(':netcdf-java-platform')) - netcdfAll project(':cdm:cdm-core') - netcdfAll project(':cdm:cdm-image') - netcdfAll project(':cdm:cdm-radial') - netcdfAll project(':cdm:cdm-misc') - netcdfAll project(':bufr') - netcdfAll project(':grib') - netcdfAll project(':netcdf4') - netcdfAll project(':opendap') - netcdfAll project(':dap4') - netcdfAll project(':httpservices') - netcdfAll project(':visad:cdm-mcidas') - - toolsUI enforcedPlatform(project(':netcdf-java-platform')) - toolsUI project(':uicdm') - toolsUI 'ch.qos.logback:logback-classic' -} - -def fatJarTasks = [] - -fatJarTasks << tasks.create(name: 'buildNcIdv', type: ShadowJar) { - setArchiveBaseName('ncIdv') - - configurations = [project.configurations.ncIdv] - - // Filter out crap from visad - exclude 'edu/wisc/**' - exclude 'nom/**' - exclude 'visad/**' - - manifest.attributes 'Implementation-Title': 'ncIdv Module' -} - -fatJarTasks << tasks.create(name: 'buildNetcdfAll', type: ShadowJar) { - setArchiveBaseName('netcdfAll') - - configurations = [project.configurations.netcdfAll] - - doFirst { - manifest.attributes project(':cdm:cdm-core').tasks.jar.manifest.attributes - } -} - -fatJarTasks << tasks.create(name: 'buildToolsUI', type: ShadowJar) { - setArchiveBaseName('toolsUI') - configurations = [project.configurations.toolsUI] - - doFirst { - manifest.attributes project(':uicdm').tasks.jar.manifest.attributes - } -} - -// Common configuration. -configure(fatJarTasks) { - dependsOn configurations*.buildDependencies - group = 'shadow' - - // Filter out crap from various other packages. - exclude 'AUTHORS' - exclude 'DATE' - exclude 'NOTICE' - exclude '*.txt' - exclude 'META-INF/INDEX.LIST' - exclude 'META-INF/DEPENDENCIES' - exclude 'META-INF/LICENSE' - exclude 'META-INF/third-party-licenses/' - exclude 'META-INF/third-party-licenses/**' - exclude 'META-INF/NOTICE' - exclude 'META-INF/*.SF' - exclude 'META-INF/*.DSA' - exclude 'META-INF/*.RSA' - exclude 'META-INF/*.txt' - exclude 'META-INF/*.xml' - - // Transformations - append('META-INF/spring.handlers') - append('META-INF/spring.schemas') - mergeServiceFiles() -} - -// The base-plugin's "assemble" task automatically creates all artifacts added to the "archives" configuration. -artifacts { - archives buildNcIdv - archives buildNetcdfAll - archives buildToolsUI -} - -import java.nio.file.Files -import java.nio.file.Paths -import java.security.DigestInputStream -import java.security.MessageDigest - -def createChecksumsTask = tasks.register('createChecksums') { - group = 'publishing' - description = 'Create .sha1, .sha256, and .md5 checksum files for the fatJars.' - doLast { - String sourceDir = "${rootProject.getBuildDir()}/distributions" - def files = fileTree(dir: "${sourceDir}", include: '**/*.jar') - def algorithms = ["MD5", "SHA-1", "SHA-256"] - algorithms.each { algorithm -> - MessageDigest md = MessageDigest.getInstance(algorithm) - files.each { File jarFile -> - InputStream is = null - DigestInputStream dis = null - byte[] buffer = new byte[2048] - try { - is = Files.newInputStream(Paths.get(jarFile.absolutePath)) - dis = new DigestInputStream(is, md) - while (dis.read(buffer) != -1) { - // just need to read through the file - } - dis.close() - is.close() - } finally { - if (dis != null) { - dis.close() - } - if (is != null) { - is.close() - } - } - - byte[] digest = md.digest() - StringBuilder sb = new StringBuilder() - for (int b = 0; b < digest.length; b++) { - sb.append(Integer.toString((digest[b] & 0xff) + 0x100, 16).substring(1)) - } - String checksum = sb.toString() - def ext = algorithm.toLowerCase().replace("-", "") - String outputFilename = "${buildDir}/distributions/${jarFile.getName()}.${ext}" - new File(outputFilename).withWriter { writer -> - writer.write checksum - } - } - } - } - dependsOn buildNcIdv, buildNetcdfAll, buildToolsUI -} - -assemble.dependsOn(createChecksumsTask) diff --git a/gradle/root/publishing.gradle b/gradle/root/publishing.gradle deleted file mode 100644 index 6cfc667089..0000000000 --- a/gradle/root/publishing.gradle +++ /dev/null @@ -1,38 +0,0 @@ -buildscript { - // Add the "buildPlugins" ExtraProperty. It should be usable from the rest of this script as well. - // See http://goo.gl/9bixNV - apply from: "$rootDir/gradle/any/shared-mvn-coords.gradle" - - // The buildscript {} block is odd: even though we applied dependencies.gradle above, the repositories therein - // do not get included here. Instead, we must explicitly define the repos again. Yay for duplication. - repositories { - mavenCentral() - gradlePluginPortal() - } -} - -if (!name.equals(rootProject.name)) { - throw new GradleException("This script plugin should only be applied to the root project, not '$name'.") -} - -apply plugin: 'maven-publish' // gives us the publish task even though we are not going to publish anything to maven -apply from: "$rootDir/gradle/any/properties.gradle" // For Nexus credential properties. - - -// The "publish" tasks for all subprojects require credentials for our Nexus server, which they look for in Gradle -// properties. If those properties (i.e. NEXUS_USERNAME_KEY and NEXUS_PASSWORD_KEY) haven't been provided, the build -// will fail. Therefore, we only want to configure credentials when a "publish" task is part of the execution plan. -// Otherwise, unavailable credentials could cause a build to fail even if we aren't doing any publishing. The -// TaskExecutionGraph allows us to do that. -gradle.taskGraph.whenReady {TaskExecutionGraph taskGraph -> - // This won't find any publishToMavenLocal tasks. Those are of type PublishToMavenLocal - Collection mavenPublishTasks = taskGraph.allTasks.findAll { - it instanceof PublishToMavenRepository - } - mavenPublishTasks.each { - it.repository.credentials.with { - username = getPropertyOrFailBuild NEXUS_USERNAME_KEY - password = getPropertyOrFailBuild NEXUS_PASSWORD_KEY - } - } - } diff --git a/gradle/root/sonarqube.gradle b/gradle/root/sonarqube.gradle deleted file mode 100644 index 384ad087ed..0000000000 --- a/gradle/root/sonarqube.gradle +++ /dev/null @@ -1,137 +0,0 @@ -import java.nio.file.Paths - -if (!name.equals(rootProject.name)) { - throw new GradleException("This script plugin should only be applied to the root project, not '$name'.") -} - -// Effectively adds "sonarqube" extension to all projects. -// Adds "sonarqube" task to only the root project. However, the task analyzes the root and all subprojects. -apply plugin: 'org.sonarqube' - -apply from: "$rootDir/gradle/any/properties.gradle" // For SonarQube user token. - -def branchName = 'unknown_branch' -// If this is a git repo, grab the branch name to tag the sonarcloud analysis -def headFile = Paths.get( "$rootDir", ".git", "HEAD" ).toFile() -if(headFile.exists()) { - String head = headFile.getText('UTF-8') - branchName = head.split('refs/heads/').last() -} - -gradle.projectsEvaluated { - // Root project - sonarqube { - properties { - // Global properties. See http://docs.sonarqube.org/display/SONAR/Analysis+Parameters - properties['sonar.verbose'] = 'false' - properties['sonar.projectKey'] = 'edu.ucar.unidata:ncj' - properties['sonar.organization'] = 'unidata_org' - properties['sonar.host.url'] = 'https://sonarcloud.io' - // Defer invocation of getPropertyOrFailBuild() until the execution phase, using lazy GStrings. - properties['sonar.login'] = "${-> getPropertyOrFailBuild SONARQUBE_USER_TOKEN_KEY}" - - /* - * We want to analyze the code in :buildSrc. Unfortunately, that's doesn't happen by default with the - * application of the Sonar plugin because :buildSrc isn't technically part of netcdf-java (Gradle considers - * it a completely separate build). Therefore, sonar isn't including it in the "sonar.modules" property. - * - * Why not just manually edit "sonar.modules" to include :buildSrc? Because the plugin overwrites anything - * that the user has set with a computed value: https://goo.gl/n2EIaI. As a result, "sonar.modules" is - * effectively read-only. - * - * So, given that we can't easily influence the value of the "sonar.modules" property (we'd need to - * physically create another subproject in the Gradle build), the other option is to assign buildSrc's code - * to an existing (Sonar) module. "netcdf-java" (the root project) seems the natural choice, and that's what - * we're trying to do here. Unfortunately, this fails with an error: - * A multi-module project can't have source folders, so - * '/Users/cwardgar/dev/projects/thredds/buildSrc/src' won't be used for the analysis. - * - * See http://stackoverflow.com/questions/39297889. Essentially, only "leaf" modules are allowed to have - * source code. However, as stated in the thread, SonarSource is aware of the problem (SONARGRADL-5) and - * may fix it. I'll leave this here in the event that they do. - */ - properties['sonar.sources'] = file('buildSrc/src') - properties['sonar.java.binaries'] = file('buildSrc/build/classes') - } - } - - allprojects { - sonarqube { - properties { - properties['sonar.inclusions'] = '**/*.java' // Only scan Java files. - properties['sonar.exclusions'] = '**/*Proto.java' // Don't analyze protobuf-generated code. - - // We're already reporting test failures and code coverage in Jenkins; we don't need to do it in Sonar - // too. Setting these properties to the empty string effectively disables the associated functionality. - properties['sonar.junit.reportsPath'] = '' - } - } - } - - - - configure(javaProjects) { // The 'sourceSets' property will only be found on Java projects. - sonarqube { - properties { - /* - * The only code Sonar actually analyzes is in "sonar.sources". It uses "sonar.java.binaries" and - * "sonar.java.libraries" for inspections that require bytecode. The values of these properties are - * all taken from a Java project's "main" SourceSet. - * - * There are analogous properties for the "test" SourceSet: "sonar.tests", "sonar.java.test.binaries", - * and "sonar.java.test.libraries". If we want our test code to be analyzed along with our main code, - * we need to add the values of the "test" properties to their "main" counterparts. - * - * After that, we set the values of those "test" properties to the empty string, which instructs Sonar - * to not do its normal reporting of test successes and failures. As mentioned before, we don't want - * Sonar doing that; we already have Jenkins. - */ - def sources = files(properties['sonar.sources'] ?: [], - properties['sonar.tests'] ?: []).flatten() - properties['sonar.sources'] = nukeDupesAndNonExistent(sources) - properties['sonar.tests'] = '' - - def binaries = files(properties['sonar.java.binaries'] ?: [], - properties['sonar.java.test.binaries'] ?: []).flatten() - properties['sonar.java.binaries'] = nukeDupesAndNonExistent(binaries) - properties['sonar.java.test.binaries'] = '' - - def libraries = files(properties['sonar.java.libraries'] ?: [], - properties['sonar.java.test.libraries'] ?: []).flatten() - properties['sonar.java.libraries'] = nukeDupesAndNonExistent(libraries) - properties['sonar.java.test.libraries'] = '' - properties['sonar.branch.name'] = branchName - } - } - } - - boolean debug = false - - tasks.sonarqube { - // Some inspections require bytecode. 'testClasses' depends on 'classes'. - dependsOn = javaProjects*.tasks*.testClasses - - if (debug) { - doFirst { - // 'properties' is a dynamic property, recomputed every time it is accessed by - // computeSonarProperties(). See https://goo.gl/DkbXfe. This essentially makes it read-only. - properties.each { - k, v -> println "$k:$v" - } - throw new StopExecutionException('Okay, STOP. We just wanted to see the properties.') - } - } - } - -} - -// Returns the provided files, minus any duplicates or files that don't exist. -Set nukeDupesAndNonExistent(Iterable files) { - Set ret = new LinkedHashSet() - files.each { - if (it.exists()) { - ret << it - } - } - ret -} diff --git a/gradle/root/spotless.gradle b/gradle/root/spotless.gradle deleted file mode 100644 index ffdedaaeaa..0000000000 --- a/gradle/root/spotless.gradle +++ /dev/null @@ -1,17 +0,0 @@ -if (!name.equals(rootProject.name)) { - throw new GradleException("This script plugin should only be applied to the root project, not '$name'.") -} - -apply plugin: 'com.diffplug.spotless' - -spotless { - format 'misc', { - // Define the files to apply `misc` - target '*/**/*.gradle', '*.gradle' - - // Things to check - trimTrailingWhitespace() - indentWithSpaces(2) - endWithNewline() - } -} diff --git a/gradle/root/testing.gradle b/gradle/root/testing.gradle deleted file mode 100644 index 8f40eab0f3..0000000000 --- a/gradle/root/testing.gradle +++ /dev/null @@ -1,150 +0,0 @@ -if (!name.equals(rootProject.name)) { - throw new GradleException("This script plugin should only be applied to the root project, not '$name'.") -} - -// The log messages in this ext block will be emitted every time we build, even if no Test task is being run. -// That's a bit spammy, but at least the code is straightforward. -// The alternative is to use "gradle.taskGraph.whenReady{}" and only do this config if a Test is in the task graph, -// but it's not worth the extra complication. -// LOOK: What about if we only logged warnings in "gradle.taskGraph.whenReady{}"? -ext { - // Inspect environment to see what tests we should run - - // These appear to be the only environment variables that Jenkins defines: http://goo.gl/iCh08k - // Is there a better way to detect Jenkins? - jenkinsEnvVar = 'JENKINS_URL' - isJenkins = System.env[jenkinsEnvVar] as boolean // We only care if prop is defined, not its actual value. - - pullRequestEnvVar = 'GITHUB_ACTIONS' - isPullRequestCheck = System.env[pullRequestEnvVar] as boolean // We only care if prop is defined, not its actual value. - - contentRootKey = 'tds.content.root.path' - isContentRootAvailable = isSystemPropertyAValidDirectory contentRootKey - - testdataDirKey = 'unidata.testdata.path' - isCdmUnitTestDirAvailable = isSystemPropertyAValidDirectory testdataDirKey - - // We only care if props are defined, not their actual value. - runSlowTests = System.properties['runSlowTests'] as boolean - isRdaDataAvailable = System.properties['rdaDataAvailable'] as boolean - isUcarNetworkAvailable = System.properties['ucarNetworkAvailable'] as boolean - skipDockerTests = System.properties['skipDockerTests'] as boolean - - // Option to run all tests regardless of environment or resource availability - runAllTests = System.properties['runAllTestExceptIgnored'] as boolean - - // If not a release, the version will contain a -SNAPSHOT, -beta, etc. - isRelease = "${version}".split('-').length == 1 - - if (runAllTests) { - logger.warn 'Running all tests except those explicitly annotated with @Ignore.' - } else { - if (isJenkins) { - logger.warn 'Skipping all NotJenkins tests: detected that we\'re running in the Jenkins environment.' - } - - if (isPullRequestCheck) { - logger.warn 'Skipping all NotPullRequest tests: detected that we\'re running in the GitHub Actions environment.' - } - - if (!isContentRootAvailable && !isJenkins) { // Don't skip tests on Jenkins, except NotJenkins ones. - logger.warn 'Skipping all NeedsContentRoot tests.' - } - - if (!isCdmUnitTestDirAvailable && !isJenkins) { // Don't skip tests on Jenkins, except NotJenkins ones. - logger.warn 'Skipping all NeedsCdmUnitTest tests.' - } - - if (!runSlowTests) { - logger.warn 'Skipping all Slow tests.' - } - - if (!isRdaDataAvailable) { - logger.warn 'Skipping all tests that require access to RDA data.' - } - - if (!isUcarNetworkAvailable) { - logger.warn 'Skipping all tests that require access to the UCAR Network.' - } - - if (skipDockerTests) { - logger.warn 'Skipping all tests that require docker.' - } - } -} - -import java.nio.file.* - -/** - * Returns {@code true} if the given system property is defined and denotes an existing directory. Otherwise, - * {@code false} is returned and the property is set to {@code "$buildDir/NO/$sysPropKey/FOUND/"}. - * - * @param sysPropKey the name of a system property. It'll likely have been set either on the command line or in - * gradle.properties. - * @return {@code true} if the given system property is defined and denotes an existing directory. - */ -boolean isSystemPropertyAValidDirectory(String sysPropKey) { - String sysPropVal = System.properties[sysPropKey] - - if (sysPropVal) { - if (Files.isDirectory(Paths.get(sysPropVal))) { - return true - } else { - logger.warn "$sysPropKey=\"$sysPropVal\"; system property is not a directory." - } - } else { - logger.warn "\"$sysPropKey\" system property not defined." - } - - // Initialization for our tests requires that we define SOME value for 'tds.content.root.path' and - // 'unidata.testdata.path'. To clearly indicate that the property wasn't set properly by the user, we're going to - // create a path containing the segment "NO/$sysPropKey/FOUND". - // - // However, we must exercise some care in our choice of parent directory for that segment. This is because when we - // start the embedded TDS server for :it:integrationTest, log4j will attempt to create the directory - // "${sys:tds.content.root.path}/thredds/logs" (see TDS's log4j.xml). In the event that the user fails to define - // that property, the responsible place to create this logs directory is under the project build directory. - String defaultSysPropVal = Paths.get(buildDir.path, 'NO', sysPropKey, 'FOUND').toAbsolutePath().toString() - - logger.info "Setting default system property: $sysPropKey=\"$defaultSysPropVal\"" - System.properties[sysPropKey] = defaultSysPropVal - return false -} - -gradle.projectsEvaluated { - // By default, subprojects are evaluated AFTER their parents, meaning that the full set of subproject Test - // tasks won't be available until all subprojects have been evaluated. That's why we've delayed the - // configuration of the following two rootProject tasks. - Set subprojectTestTasks = subprojects*.tasks*.withType(Test).flatten() - - task testAll(group: 'Verification') { - description = 'Runs all subproject Test tasks' - dependsOn subprojectTestTasks - // If we run testAll, generate coverage report after all of the tests run - //finalizedBy jacocoRootReport - } - - /** - task rootTestReport(type: TestReport, group: 'Reports') { - description = 'Generates an aggregate test report' - destinationDir = file("$buildDir/reports/allTests") - - // All Test tasks will be finalized by this task. As a result, this task needn't be invoked directly. - subprojectTestTasks*.finalizedBy it - - // We could do "reportOn subprojectTestTasks" here, but that would cause this task to be dependent on - // all subproject Tests. So, we couldn't do something like ":grib:test" and expect only GRIB tests to run - // because: - // ":grib:test" --finalizedBy--> ":rootTestReport" --dependsOn--> "all_subproject_Tests" - // In other words, all subproject tests would get run, no matter what. - // Passing File arguments to reportOn() instead doesn't create that dependency. - reportOn subprojectTestTasks*.binaryResultsDirectory - - // Wait until all Test tasks have run. This creates a task *ordering*, not a dependency. - mustRunAfter subprojectTestTasks - } - **/ -} - - -apply plugin: 'base' // Gives us the "clean" task for removing rootTestReport's output. diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 1b33c55baa..f8e1ee3125 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index be2dc79a8a..b11741a1ad 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-all.zip +distributionSha256Sum=16f2b95838c1ddcf7242b1c39e7bbbb43c842f1f1a1a0dc4959b6d4d68abcac3 +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-all.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 23d15a9367..adff685a03 100755 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -114,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -172,7 +171,6 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -212,7 +210,6 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" diff --git a/gradlew.bat b/gradlew.bat index db3a6ac207..c4bdd3ab8e 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -70,11 +70,10 @@ goto fail :execute @rem Setup the command line -set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell diff --git a/grib/build.gradle b/grib/build.gradle deleted file mode 100644 index 1af91b43ba..0000000000 --- a/grib/build.gradle +++ /dev/null @@ -1,35 +0,0 @@ -description = 'Decoder for the GRIB 1 and 2 formats.' -ext.title = 'GRIB 1 and 2 IOSPs and Feature Collections' - -apply from: "$rootDir/gradle/any/dependencies.gradle" -apply from: "$rootDir/gradle/any/java-library.gradle" -apply from: "$rootDir/gradle/any/protobuf.gradle" - -dependencies { - api enforcedPlatform(project(':netcdf-java-platform')) - testImplementation enforcedPlatform(project(':netcdf-java-testing-platform')) - - api project(':cdm:cdm-core') - - implementation project(':native-compression:libaec-jna') - - implementation 'com.google.protobuf:protobuf-java' - implementation 'org.jdom:jdom2' - implementation 'com.google.code.findbugs:jsr305' - implementation 'edu.ucar:jj2000' - implementation 'com.google.guava:guava' - implementation 'com.beust:jcommander' - implementation 'com.google.re2j:re2j' - implementation 'org.slf4j:slf4j-api' - implementation 'net.java.dev.jna:jna' - - testImplementation project(':cdm-test-utils') - testImplementation project(':udunits') - - testImplementation 'com.google.truth:truth' - testImplementation 'org.jsoup:jsoup' - - testRuntimeOnly project(':native-compression:libaec-native') - - testRuntimeOnly 'ch.qos.logback:logback-classic' -} diff --git a/grib/build.gradle.kts b/grib/build.gradle.kts new file mode 100644 index 0000000000..8a3dff0936 --- /dev/null +++ b/grib/build.gradle.kts @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { + id("java-library-conventions") + id("protobuf-conventions") +} + +description = "Decoder for the GRIB 1 and 2 formats." + +project.extra["project.title"] = "GRIB 1 and 2 IOSPs and Feature Collections" + +dependencies { + implementation(platform(project(":netcdf-java-platform"))) + + api(project(":cdm-core")) + + implementation(project(":libaec-jna")) + + implementation(libs.beust.jcommander) + implementation(libs.findbugs.jsr305) + implementation(libs.guava) + implementation(libs.jdom2) + implementation(libs.jj2000) + implementation(libs.jna) + implementation(libs.protobuf) + implementation(libs.re2j) + implementation(libs.slf4j.api) + + testImplementation(platform(project(":netcdf-java-testing-platform"))) + + testImplementation(project(":cdm-test-utils")) + testImplementation(project(":udunits")) + + testImplementation(libs.google.truth) + testImplementation(libs.jsoup) + + testCompileOnly(libs.junit4) + + testRuntimeOnly(project(":libaec-native")) + + testRuntimeOnly(libs.junit5.platformLauncher) + testRuntimeOnly(libs.junit5.vintageEngine) + testRuntimeOnly(libs.logback.classic) +} diff --git a/httpservices/build.gradle b/httpservices/build.gradle deleted file mode 100755 index 7c94a60b1e..0000000000 --- a/httpservices/build.gradle +++ /dev/null @@ -1,24 +0,0 @@ -ext.title = 'HttpClient Wrappers' - -apply from: "$rootDir/gradle/any/dependencies.gradle" -apply from: "$rootDir/gradle/any/java-library.gradle" - -dependencies { - api enforcedPlatform(project(':netcdf-java-platform')) - testImplementation enforcedPlatform(project(':netcdf-java-testing-platform')) - - api 'com.google.guava:guava' - api 'org.apache.httpcomponents:httpclient' - api 'org.apache.httpcomponents:httpcore' - - implementation 'org.apache.httpcomponents:httpmime' - implementation 'com.google.code.findbugs:jsr305' - implementation 'com.google.re2j:re2j' - implementation 'org.slf4j:slf4j-api' - - testImplementation project(':cdm-test-utils') - - testImplementation 'com.google.truth:truth' - - testRuntimeOnly 'ch.qos.logback:logback-classic' -} diff --git a/httpservices/build.gradle.kts b/httpservices/build.gradle.kts new file mode 100644 index 0000000000..b401fbe7eb --- /dev/null +++ b/httpservices/build.gradle.kts @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { id("java-library-conventions") } + +description = "HTTP Client Wrappers for the NetCDF-Java library." + +extra["project.title"] = "HttpClient Wrappers" + +dependencies { + implementation(platform(project(":netcdf-java-platform"))) + + api(libs.guava) + api(libs.httpcomponents.api) + + implementation(libs.findbugs.jsr305) + implementation(libs.httpcomponents.httpmime) + implementation(libs.re2j) + implementation(libs.slf4j.api) + + testImplementation(platform(project(":netcdf-java-testing-platform"))) + + testImplementation(project(":cdm-test-utils")) + + testImplementation(libs.google.truth) + + testCompileOnly(libs.junit4) + + testRuntimeOnly(libs.junit5.platformLauncher) + testRuntimeOnly(libs.junit5.vintageEngine) + testRuntimeOnly(libs.logback.classic) +} diff --git a/legacy/build.gradle b/legacy/build.gradle deleted file mode 100644 index 39c8a39f91..0000000000 --- a/legacy/build.gradle +++ /dev/null @@ -1,27 +0,0 @@ -description = 'Package that generates a jar file of legacy classes for backwards compatibility' -ext.title = 'legacyJar Module' -ext.url = 'https://www.unidata.ucar.edu/software/tds/v4.6/TDS.html' - -apply from: "$rootDir/gradle/any/dependencies.gradle" -apply from: "$rootDir/gradle/any/java-library.gradle" - -dependencies { - api enforcedPlatform(project(':netcdf-java-platform')) - testImplementation enforcedPlatform(project(':netcdf-java-testing-platform')) - - api project(':cdm:cdm-core') - - implementation project(':grib') - implementation project(':opendap') - implementation project(':udunits') - - implementation 'com.google.guava:guava' - implementation 'org.jdom:jdom2' - implementation 'com.google.code.findbugs:jsr305' - implementation 'org.slf4j:slf4j-api' - - implementation 'com.amazonaws:aws-java-sdk-s3' // For CrawlableDatasetAmazonS3. - - testImplementation project(':cdm-test-utils') - testRuntimeOnly 'ch.qos.logback:logback-classic' -} diff --git a/legacy/build.gradle.kts b/legacy/build.gradle.kts new file mode 100644 index 0000000000..85df97f436 --- /dev/null +++ b/legacy/build.gradle.kts @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { id("java-library-conventions") } + +description = "Package that generates a jar file of legacy classes for backwards compatibility" + +project.extra["project.title"] = "legacyJar" + +dependencies { + implementation(platform(project(":netcdf-java-platform"))) + + api(project(":cdm-core")) + + implementation(project(":grib")) + implementation(project(":opendap")) + implementation(project(":udunits")) + + implementation(libs.amazonaws.s3v1) // For CrawlableDatasetAmazonS3. + implementation(libs.guava) + implementation(libs.jdom2) + implementation(libs.findbugs.jsr305) + implementation(libs.slf4j.api) + + testImplementation(platform(project(":netcdf-java-testing-platform"))) + + testImplementation(project(":cdm-test-utils")) + + testCompileOnly(libs.junit4) + + testRuntimeOnly(libs.junit5.platformLauncher) + testRuntimeOnly(libs.junit5.vintageEngine) + testRuntimeOnly(libs.logback.classic) +} diff --git a/native-compression/build.gradle b/native-compression/build.gradle deleted file mode 100644 index dc2450ee83..0000000000 --- a/native-compression/build.gradle +++ /dev/null @@ -1,2 +0,0 @@ -description = 'Native binding for compression' -ext.title = 'native compression binding' diff --git a/native-compression/libaec-jna/build.gradle b/native-compression/libaec-jna/build.gradle deleted file mode 100644 index bea708b2e3..0000000000 --- a/native-compression/libaec-jna/build.gradle +++ /dev/null @@ -1,37 +0,0 @@ -apply from: "$rootDir/gradle/any/dependencies.gradle" -apply from: "$rootDir/gradle/any/java-library.gradle" - -description = 'Java bindings for decoding libaec compression using JNA' -ext.title = 'libaec compression decoder using JNA' - -sourceSets { - simpleTests { - //resources.srcDirs = [file('src/simpleTests/resources')] - compileClasspath += sourceSets.main.output + configurations.compileClasspath - runtimeClasspath += output + sourceSets.main.output + configurations.runtimeClasspath - } -} - -// unloaded test task using "configuration avoidance" -def simpleTests = tasks.register('simpleTests', Test) { - group = 'verification' - description = 'Runs tests that do not depend on cdm-test-utils (specifically cdm-core)' - testClassesDirs = sourceSets.simpleTests.output.classesDirs - classpath = sourceSets.simpleTests.runtimeClasspath -} - -test.dependsOn(simpleTests) - -dependencies { - api enforcedPlatform(project(':netcdf-java-platform')) - simpleTestsImplementation enforcedPlatform(project(':netcdf-java-platform')) - simpleTestsImplementation enforcedPlatform(project(':netcdf-java-testing-platform')) - - api 'net.java.dev.jna:jna' - implementation 'org.slf4j:slf4j-api' - - simpleTestsImplementation 'com.google.truth:truth' - - simpleTestsRuntimeOnly project(':native-compression:libaec-native') - simpleTestsRuntimeOnly 'ch.qos.logback:logback-classic' -} diff --git a/native-compression/libaec-jna/build.gradle.kts b/native-compression/libaec-jna/build.gradle.kts new file mode 100644 index 0000000000..59035306f1 --- /dev/null +++ b/native-compression/libaec-jna/build.gradle.kts @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { id("java-library-conventions") } + +description = "Java bindings for decoding libaec compression using JNA." + +extra["project.title"] = "libaec compression decoder using JNA" + +dependencies { + implementation(platform(project(":netcdf-java-platform"))) + + api(libs.jna) + + implementation(libs.slf4j.api) + + testImplementation(platform(project(":netcdf-java-testing-platform"))) + + testImplementation(project(":cdm-test-utils")) + + testImplementation(libs.google.truth) + + testCompileOnly(libs.junit4) + + testRuntimeOnly(project(":libaec-native")) + + testRuntimeOnly(libs.junit5.platformLauncher) + testRuntimeOnly(libs.junit5.vintageEngine) + testRuntimeOnly(libs.logback.classic) +} diff --git a/native-compression/libaec-jna/src/simpleTests/java/edu/ucar/unidata/compression/jna/libaec/TestLibAec.java b/native-compression/libaec-jna/src/test/java/edu/ucar/unidata/compression/jna/libaec/TestLibAec.java similarity index 100% rename from native-compression/libaec-jna/src/simpleTests/java/edu/ucar/unidata/compression/jna/libaec/TestLibAec.java rename to native-compression/libaec-jna/src/test/java/edu/ucar/unidata/compression/jna/libaec/TestLibAec.java diff --git a/native-compression/libaec-jna/src/simpleTests/java/edu/ucar/unidata/compression/jna/libaec/TestLibAecMultithreaded.java b/native-compression/libaec-jna/src/test/java/edu/ucar/unidata/compression/jna/libaec/TestLibAecMultithreaded.java similarity index 100% rename from native-compression/libaec-jna/src/simpleTests/java/edu/ucar/unidata/compression/jna/libaec/TestLibAecMultithreaded.java rename to native-compression/libaec-jna/src/test/java/edu/ucar/unidata/compression/jna/libaec/TestLibAecMultithreaded.java diff --git a/native-compression/libaec-jna/src/simpleTests/java/edu/ucar/unidata/compression/jna/libaec/TestLoadLibAec.java b/native-compression/libaec-jna/src/test/java/edu/ucar/unidata/compression/jna/libaec/TestLoadLibAec.java similarity index 100% rename from native-compression/libaec-jna/src/simpleTests/java/edu/ucar/unidata/compression/jna/libaec/TestLoadLibAec.java rename to native-compression/libaec-jna/src/test/java/edu/ucar/unidata/compression/jna/libaec/TestLoadLibAec.java diff --git a/native-compression/libaec-jna/src/simpleTests/resources/logback-test.xml b/native-compression/libaec-jna/src/test/resources/logback-test.xml similarity index 100% rename from native-compression/libaec-jna/src/simpleTests/resources/logback-test.xml rename to native-compression/libaec-jna/src/test/resources/logback-test.xml diff --git a/native-compression/libaec-native/build.gradle b/native-compression/libaec-native/build.gradle deleted file mode 100644 index 793a38c0b6..0000000000 --- a/native-compression/libaec-native/build.gradle +++ /dev/null @@ -1,52 +0,0 @@ -import java.security.DigestInputStream -import java.security.MessageDigest - -apply from: "$rootDir/gradle/any/java-library.gradle" - -description = 'Jar distribution of native libraries for libaec compression.' -ext.title = 'Native libraries for libaec.' - -// zip file produced by GitHub workflow -def libaecNative = 'libaec-native-1.1.3-fec016ecd4b8ff1918877e582898d4257c405168.zip' - -// sha256 checksum from GitHub workflow output -def expectedChecksum = '3db1ba7bc95b48eff74501382b90b0c7d0770a98f369d8c376c8ca4b6003487e' - -def resourceZip = new File("${rootDir}/project-files/native/libaec/${libaecNative}") - -def fetchNativeResources = tasks.register("fetchNativeResources") { - outputs.file resourceZip - doLast { - if (!resourceZip.exists()) { - def actualChecksum = '' - def resourceUrl = "https://downloads.unidata.ucar.edu/netcdf-java/native/libaec/${libaecNative}" - new URL(resourceUrl).withInputStream { - ips -> - DigestInputStream dips = new DigestInputStream(ips, MessageDigest.getInstance('SHA-256')) - resourceZip.withOutputStream { ops -> - ops << dips - } - actualChecksum = dips.getMessageDigest().digest().encodeHex().toString() - } - if (actualChecksum != expectedChecksum) { - resourceZip.delete() - String msg = - String.format('Error: checksum on libaec.zip does not match expected ' + - 'value.\n Expected: %s\n Actual: %s\n', expectedChecksum, actualChecksum) - throw new RuntimeException(msg); - } - } - } -} - -def processNativeResources = tasks.register("processNativeResources", Copy) { - inputs.file resourceZip - from zipTree(resourceZip) - destinationDir(file("$buildDir/resources/main")) - eachFile { details -> - details.path = details.path.replaceFirst("resources/", "") - } - dependsOn(fetchNativeResources) -} - -processResources.dependsOn(processNativeResources) diff --git a/native-compression/libaec-native/build.gradle.kts b/native-compression/libaec-native/build.gradle.kts new file mode 100644 index 0000000000..5e2c7115d3 --- /dev/null +++ b/native-compression/libaec-native/build.gradle.kts @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +import java.net.URL +import java.security.DigestInputStream +import java.security.MessageDigest + +plugins { id("java-library-conventions") } + +description = "Jar distribution of native libraries for libaec compression." + +project.extra["project.title"] = "Native libraries for libaec." + +// zip file produced by GitHub workflow +val libaecNative = "libaec-native-1.1.3-fec016ecd4b8ff1918877e582898d4257c405168.zip" + +// sha256 checksum from GitHub workflow output +val expectedChecksum = "3db1ba7bc95b48eff74501382b90b0c7d0770a98f369d8c376c8ca4b6003487e" + +val resourceZip = file("$rootDir/project-files/native/libaec/$libaecNative") +val fetchNativeResources = + tasks.register("fetchNativeResources") { + outputs.file(resourceZip) + doLast { + if (!resourceZip.exists()) { + logger.info("Fetching native libaec libraries.") + var actualChecksum = "" + val resourceUrl = + "https://downloads.unidata.ucar.edu/netcdf-java/native/libaec/$libaecNative" + URL(resourceUrl).openStream().use { ips -> + val dips = DigestInputStream(ips, MessageDigest.getInstance("SHA-256")) + resourceZip.outputStream().use { ops -> dips.copyTo(ops) } + actualChecksum = dips.messageDigest.digest().toHexString() + } + if (actualChecksum != expectedChecksum) { + throw RuntimeException( + String.format( + "Error: checksum on libaec.zip does not match expected value.\n" + + " Expected: %s\n Actual: %s\n", + expectedChecksum, + actualChecksum, + ) + ) + } + } + } + } + +val processNativeResources = + tasks.register("processNativeResources", Copy::class) { + inputs.file(resourceZip) + from(zipTree(resourceZip)) + eachFile { relativePath = RelativePath(true, *relativePath.segments.drop(1).toTypedArray()) } + destinationDir = layout.buildDirectory.dir("resources/main").get().asFile + dependsOn(fetchNativeResources) + } + +tasks.processResources { dependsOn(processNativeResources) } diff --git a/netcdf-java-bom/build.gradle b/netcdf-java-bom/build.gradle deleted file mode 100644 index bc5524b8f0..0000000000 --- a/netcdf-java-bom/build.gradle +++ /dev/null @@ -1,46 +0,0 @@ -apply plugin: 'java-platform' -apply from: "$rootDir/gradle/any/dependencies.gradle" -apply from: "$rootDir/gradle/any/publishing.gradle" - -// netcdf-java library platform -// responsible for generating a maven bill of materials for the project - -javaPlatform { - allowDependencies() -} - -dependencies { - api enforcedPlatform(project(':netcdf-java-platform')) - constraints { - api project(':bufr') - api project(':cdm:cdm-core') - api project(':cdm:cdm-image') - api project(':cdm:cdm-misc') - api project(':cdm:cdm-radial') - api project(':cdm:cdm-s3') - api project(':cdm:cdm-zarr') - api project(':cdm-test-utils') - api project(':grib') - api project(':httpservices') - api project(':legacy') - api project(':native-compression:libaec-jna') - api project(':native-compression:libaec-native') - api project(':netcdf4') - api project(':opendap') - api project(':dap4') - api project(':udunits') - api project(':uibase') - api project(':uicdm') - api project(':visad:cdm-mcidas') - api project(':visad:cdm-vis5d') - api project(':waterml') - } -} - -publishing { - publications { - netcdfJavaBom(MavenPublication) { - from components.javaPlatform - } - } -} diff --git a/netcdf-java-bom/build.gradle.kts b/netcdf-java-bom/build.gradle.kts new file mode 100644 index 0000000000..9482fc3540 --- /dev/null +++ b/netcdf-java-bom/build.gradle.kts @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { id("platform-conventions") } + +description = "BOM containing the public artifacts that comprise the netCDF-Java Library." + +extra["project.title"] = "netCDF-Java BOM" + +dependencies { + constraints { + // list of public artifacts managed by build-logic/src/main/kotlin/base-conventions.gradle.kts + val publicArtifacts = project.extra.get("public.artifacts") + if (publicArtifacts is List<*>) { + publicArtifacts.forEach { api(project(it.toString())) } + } else { + logger.error( + "Cannot access the list of public artifacts. netcdf-java-bom will be incomplete!" + ) + } + } +} diff --git a/netcdf-java-platform/build.gradle b/netcdf-java-platform/build.gradle deleted file mode 100644 index 2b477a6c05..0000000000 --- a/netcdf-java-platform/build.gradle +++ /dev/null @@ -1,91 +0,0 @@ -apply plugin: 'java-platform' -apply from: "$rootDir/gradle/any/dependencies.gradle" -apply from: "$rootDir/gradle/any/publishing.gradle" - -// All dependencies used by the netCDF-Java library are defined here - -javaPlatform { - allowDependencies() -} - -dependencies { - def awsVersion = '2.31.76' - def jacksonVersion = '2.19.1' - api enforcedPlatform("software.amazon.awssdk:bom:${awsVersion}") - api enforcedPlatform("com.fasterxml.jackson:jackson-bom:${jacksonVersion}") - constraints { - // Note: The depVersion variable is defined in gradle/any/shared-mvn-coords.gradle and is used for dependencies - // that are used in different configurations of a build that need access to the full maven coordinates. - - // general dependencies - api "com.google.protobuf:protobuf-java:${depVersion.protobuf}" - api "com.google.protobuf:protoc:${depVersion.protobuf}" - api 'com.google.guava:guava:33.4.8-jre' - api 'com.google.re2j:re2j:1.3' - api 'org.jdom:jdom2:2.0.6.1' - api 'joda-time:joda-time:2.12.7' - - // netcdf4, dap4 - api 'net.java.dev.jna:jna:5.17.0' - - // Annotations: Nullable - api 'com.google.code.findbugs:jsr305:3.0.2' - - // command line parser - api 'com.beust:jcommander:1.78' - - // cdm-core - api 'org.apache.commons:commons-math3:3.6.1' - - // cdm-grib - api 'edu.ucar:jj2000:5.4' - api 'org.jsoup:jsoup:1.11.2' // HTML scraper used in GRIB - - // cdm-mcidas (GEMPAK and McIDAS IOSPs) - api 'edu.wisc.ssec:visad-mcidas-slim-ucar-ns:20231121' - - // cdm-vis5d (vis5d IOSP) - api 'edu.wisc.ssec:visad:2.0-20130124' - - // netcdfAll (everything else is happy with the Jackson BOM) - api "com.fasterxml.jackson.core:jackson-core:${jacksonVersion}" - api "com.fasterxml.jackson.core:jackson-databind:${jacksonVersion}" - - // toolsUI (everything else is happy with the aws BOM) - api "software.amazon.awssdk:s3:2.17.290:${awsVersion}" - api "software.amazon.awssdk:apache-client:${awsVersion}" - runtime "software.amazon.awssdk:sts:${awsVersion}" - - // apache httpclient - api 'org.apache.httpcomponents:httpclient:4.5.13' - api 'org.apache.httpcomponents:httpmime:4.5.13' - // version of httpcore matches the one used by httpclient - // so if updating httpclient, look in its pom for the version of http core to use - api 'org.apache.httpcomponents:httpcore:4.4.13' - api "org.slf4j:jcl-over-slf4j:${depVersion.slf4j}" - - // waterml xml beans - api 'org.apache.xmlbeans:xmlbeans:3.1.0' - api 'org.n52.sensorweb:52n-xml-waterML-v20:2.7.0' - api 'org.n52.sensorweb:52n-xml-gml-v321:2.7.0' - api 'org.n52.sensorweb:52n-xml-sweCommon-v20:2.7.0' - api 'org.n52.sensorweb:52n-xml-om-v20:2.7.0' - api 'org.n52.sensorweb:52n-xml-sampling-v20:2.7.0' - - // netcdf-java logging - api "org.slf4j:slf4j-api:${depVersion.slf4j}" - runtime 'ch.qos.logback:logback-classic:1.3.15' - - // legacy gradle module - // todo: remove with legacy in 6 - api 'com.amazonaws:aws-java-sdk-s3:1.12.649' - } -} - -publishing { - publications { - netcdfJavaPlatform(MavenPublication) { - from components.javaPlatform - } - } -} diff --git a/netcdf-java-platform/build.gradle.kts b/netcdf-java-platform/build.gradle.kts new file mode 100644 index 0000000000..d091b421c3 --- /dev/null +++ b/netcdf-java-platform/build.gradle.kts @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { id("platform-conventions") } + +description = + "BOM containing the public artifacts, and their third-party dependencies, that comprise the netCDF-Java Library." + +extra["project.title"] = "netCDF-Java BOM with 3rd party libraries" + +// allow references to other BOMs +javaPlatform.allowDependencies() + +dependencies { + api(platform(project(":netcdf-java-bom"))) + // covers libs.awssdk.apacheClient, libs.awssdk.s3, and libs.awssdk.sts + api(platform(libs.awssdk.bom)) + // covers libs.jackson.core, libs.jackson.databind + api(platform(libs.jackson.bom)) + + constraints { + api(libs.amazonaws.s3v1) // legacy subproject + api(libs.beust.jcommander) + api(libs.commons.math3) + api(libs.findbugs.jsr305) + api(libs.grpc.protobuf) + api(libs.grpc.stub) + api(libs.guava) + api(libs.httpcomponents.api) + api(libs.httpcomponents.httpmime) + api(libs.jdom2) + api(libs.jj2000) + api(libs.jna) + api(libs.jodaTime) + api(libs.protobuf) + api(libs.re2j) + api(libs.slf4j.api) + api(libs.sensorweb.xmlGmlV321) + api(libs.sensorweb.xmlOmV20) + api(libs.sensorweb.xmlSamplingV20) + api(libs.sensorweb.xmlSweCommonV20) + api(libs.sensorweb.xmlWaterMLV20) + api(libs.ssec.visad) + api(libs.ssec.visadMcidasSlimUcarNs) + } +} diff --git a/netcdf-java-testing-platform/build.gradle b/netcdf-java-testing-platform/build.gradle deleted file mode 100644 index 5527e0abee..0000000000 --- a/netcdf-java-testing-platform/build.gradle +++ /dev/null @@ -1,41 +0,0 @@ -apply plugin: 'java-platform' -apply from: "$rootDir/gradle/any/dependencies.gradle" -apply from: "$rootDir/gradle/any/publishing.gradle" - -// All dependencies used for testing the netCDF-Java library are defined here. -// Shared with other THREDDS projects and not necessarily intended for external consumption. - -dependencies { - constraints { - - ///////////////////////// - // netcdf-java testing // - ///////////////////////// - - api 'junit:junit:4.13.1' - api 'commons-io:commons-io:2.5' - - // Fluent assertions for Java - api 'com.google.truth:truth:1.0' - - //mockito - api 'org.mockito:mockito-core:2.28.2' - - // opendap - api 'pl.pragmatists:JUnitParams:1.0.5' - - // cdm-test (GRIB related testing) - api 'org.apache.commons:commons-compress:1.12' - - // opendap, dap4, and httpservices - api 'org.testcontainers:testcontainers:1.19.7' - } -} - -publishing { - publications { - netcdfJavaPlatform(MavenPublication) { - from components.javaPlatform - } - } -} diff --git a/netcdf-java-testing-platform/build.gradle.kts b/netcdf-java-testing-platform/build.gradle.kts new file mode 100644 index 0000000000..76a34952d1 --- /dev/null +++ b/netcdf-java-testing-platform/build.gradle.kts @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { id("platform-conventions") } + +description = + "Platform containing the test-only dependencies of the public artifacts that comprise the netCDF-Java Library. Published for use by the THREDDS Data Server." + +extra["project.title"] = "netCDF-Java BOM test-only 3rd party libraries" + +// allow references to other BOMs +javaPlatform.allowDependencies() + +dependencies { + // covers libs.junit5.vintageEngine, libs.junit5.platformLauncher + api(platform(libs.junit5.bom)) + + constraints { + api(libs.commons.compress) + api(libs.commons.io) + api(libs.google.truth) + api(libs.grpc.testing) + api(libs.jsoup) + api(libs.junit4) + api(libs.logback.classic) + api(libs.mockito.core) + api(libs.pragmatists.junitparams) + } +} diff --git a/netcdf4/build.gradle b/netcdf4/build.gradle deleted file mode 100644 index 520d6cc5aa..0000000000 --- a/netcdf4/build.gradle +++ /dev/null @@ -1,89 +0,0 @@ -description = 'An IOSP for NetCDF-4 that loads the C library to read and write files.' -ext.title = 'NetCDF-4 IOSP' -ext.url = 'https://www.unidata.ucar.edu/software/netcdf/' - -apply from: "$rootDir/gradle/any/dependencies.gradle" -apply from: "$rootDir/gradle/any/java-library.gradle" -apply plugin: 'jacoco' - -dependencies { - api enforcedPlatform(project(':netcdf-java-platform')) - testImplementation enforcedPlatform(project(':netcdf-java-testing-platform')) - - implementation project(':cdm:cdm-core') - implementation 'net.java.dev.jna:jna' - implementation 'org.slf4j:slf4j-api' - implementation 'com.google.code.findbugs:jsr305' - implementation 'com.google.guava:guava' - - testImplementation project(':cdm-test-utils') - - testImplementation 'com.google.truth:truth' - testImplementation 'org.jdom:jdom2' - - testRuntimeOnly 'ch.qos.logback:logback-classic' -} - -// Most of the tests in this subproject require that the native C library be loaded. However, there are a handful -// of tests for which it must NOT be loaded. It's tricky for a single Gradle executor to handle both kinds of tests -// because once Java loads a native library, it remains loaded for the duration of the process. So, we must separate -// the tests (using SourceSets) and run them in different tasks. - -sourceSets { - unloadedTest { - resources.srcDirs = [file('src/unloadedTest/resources')] - compileClasspath += sourceSets.main.output + configurations.testCompileClasspath - runtimeClasspath += output + sourceSets.main.output + configurations.testRuntimeClasspath - } -} - -// unloaded test task using "configuration avoidance" -def unloadedTest = tasks.register('unloadedTest', Test) { - group = 'verification' - description = 'Runs tests without the native C library loaded.' - testClassesDirs = sourceSets.unloadedTest.output.classesDirs - classpath = sourceSets.unloadedTest.runtimeClasspath -} - -// unloaded test task using "configuration avoidance" -def jacocoFullTestReport = tasks.register('jacocoFullTestReport', JacocoReport) { - group = 'reports' - description = 'Creates report for tests with and without the native C library loaded.' -} - -jacocoFullTestReport.configure { - dependsOn test, unloadedTest - - // The jacoco plugin adds the "jacocoTestReport" task and it only reports on "test" by default. - // Here we add "unloadedTest" to that report as well. We could create a separate JacocoReport task for - // "unloadedTest", but meh. The report will be saved at "netcdf4/build/reports/jacoco/test/html/index.html". - Closure isExtendedByJacoco = {Task task -> task.extensions.findByType(JacocoTaskExtension) - } - Collection tasksExtendedByJacoco = project.tasks.matching(isExtendedByJacoco).flatten() - Collection subprojectExeData = tasksExtendedByJacoco*.jacoco*.destinationFile - - // because this has two sets of tests, we need to combine their source and class directories - additionalSourceDirs.from - sourceSets.main*.allSource*.srcDirs.flatten() + sourceSets.unloadedTest*.allSource*.srcDirs.flatten() - classDirectories.from = files(sourceSets.main*.output + sourceSets.unloadedTest*.output) - - executionData.from = files(subprojectExeData.findAll { - it.exists() - }) - - // By default, JacocoReport runs onlyIf ALL of the executionData exist. We want it to run if ANY exists. - setOnlyIf { - executionData.any { - it.exists() - } - } -} - -check.configure { - dependsOn test, unloadedTest - finalizedBy jacocoFullTestReport -} - -unloadedTest.configure { - mustRunAfter test -} diff --git a/netcdf4/build.gradle.kts b/netcdf4/build.gradle.kts new file mode 100644 index 0000000000..3b3f6b94e8 --- /dev/null +++ b/netcdf4/build.gradle.kts @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { id("java-library-conventions") } + +description = "An IOSP for NetCDF-4 that loads the C library to read and write files." + +project.extra["project.title"] = "NetCDF-4 IOSP" + +project.extra["project.url"] = "https://www.unidata.ucar.edu/software/netcdf/" + +// Most of the tests in this subproject require that the native C library be loaded. However, there +// are a handful of tests for which it must NOT be loaded. It's tricky for a single Gradle executor +// to handle both kinds of tests because once Java loads a native library, it remains loaded for +// the duration of the process. So, we must separate the tests (using SourceSets) and run them in +// different tasks. +val unloadedTestSourceSet = + sourceSets.create("unloadedTest") { + compileClasspath += sourceSets.main.get().output + runtimeClasspath += sourceSets.main.get().output + } + +val unloadedTestImplementation by + configurations.getting { + extendsFrom(configurations.implementation.get(), configurations.testImplementation.get()) + } + +val unloadedTestRuntimeOnly by configurations.getting + +configurations["unloadedTestRuntimeOnly"].extendsFrom( + configurations.runtimeOnly.get(), + configurations.testRuntimeOnly.get(), +) + +dependencies { + implementation(platform(project(":netcdf-java-platform"))) + + implementation(project(":cdm-core")) + + implementation(libs.findbugs.jsr305) + implementation(libs.guava) + implementation(libs.jna) + implementation(libs.slf4j.api) + + testImplementation(platform(project(":netcdf-java-testing-platform"))) + + testImplementation(project(":cdm-test-utils")) + + testImplementation(libs.google.truth) + testImplementation(libs.jdom2) + + testCompileOnly(libs.junit4) + + testRuntimeOnly(libs.junit5.platformLauncher) + testRuntimeOnly(libs.junit5.vintageEngine) + testRuntimeOnly(libs.logback.classic) +} + +val testVersions = project.extra["project.testLtsVersions"] + +if (testVersions is List<*>) { + testVersions.forEach { + val unloadedTest = + tasks.register( + if (it == project.extra["project.minimumJdkVersion"]) "unloadedTest" + else "unloadedTest${it}" + ) { + description = "Runs tests with netCDF-C unloaded using Java ${it}." + group = "verification" + testClassesDirs = sourceSets["unloadedTest"].output.classesDirs + classpath = sourceSets["unloadedTest"].runtimeClasspath + useJUnitPlatform() + javaLauncher.set( + project.javaToolchains.launcherFor { + languageVersion = JavaLanguageVersion.of(it.toString().toInt()) + } + ) + } + if (it == project.extra["project.minimumJdkVersion"]) { + tasks.test { + mustRunAfter(unloadedTest) + dependsOn(unloadedTest) + } + } else { + tasks.named("testWithJdk${it}") { + mustRunAfter(unloadedTest) + dependsOn(unloadedTest) + } + } + } +} diff --git a/opendap/build.gradle b/opendap/build.gradle deleted file mode 100644 index 0d1bf3f0cb..0000000000 --- a/opendap/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -description = 'Open-source Project for a Network Data Access Protocol, modified for NetCDF purpose. ' + - 'This artifact is a derivative work from the official OPeNDAP library ' + - '(http://opendap.org), modified by UCAR. The packages were renamed from "dods.*" to ' + - '"opendap.*" and the groupID from "org.opendap" to "edu.ucar".' - -ext.title = 'OPeNDAP' -ext.vendor = 'OPeNDAP' -ext.url = 'http://opendap.org/' - -apply from: "$rootDir/gradle/any/dependencies.gradle" -apply from: "$rootDir/gradle/any/java-library.gradle" - -dependencies { - api enforcedPlatform(project(':netcdf-java-platform')) - testImplementation enforcedPlatform(project(':netcdf-java-testing-platform')) - - api project(':cdm:cdm-core') - api project(':httpservices') - - implementation 'com.google.guava:guava' - - implementation 'org.jdom:jdom2' - implementation 'com.google.code.findbugs:jsr305' - implementation 'org.slf4j:slf4j-api' - - testImplementation project(':cdm-test-utils') - testImplementation 'com.google.truth:truth' - testImplementation 'pl.pragmatists:JUnitParams' - - testRuntimeOnly 'ch.qos.logback:logback-classic' -} diff --git a/opendap/build.gradle.kts b/opendap/build.gradle.kts new file mode 100644 index 0000000000..4fac5b7934 --- /dev/null +++ b/opendap/build.gradle.kts @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { id("java-library-conventions") } + +description = + "Open-source Project for a Network Data Access Protocol, modified " + + "for NetCDF purpose. This artifact is a derivative work from the official " + + "OPeNDAP library (https://www.opendap.org/), modified by UCAR. The " + + "packages were renamed from \"dods.*\" to \"opendap.*\" and the groupID " + + "from \"org.opendap\" to \"edu.ucar\"." + +extra["project.title"] = "OPeNDAP (2.0) Client" + +extra["project.vendor"] = "OPeNDAP" + +extra["project.url"] = "https://www.opendap.org/" + +dependencies { + implementation(platform(project(":netcdf-java-platform"))) + + api(project(":cdm-core")) + api(project(":httpservices")) + + implementation(libs.findbugs.jsr305) + implementation(libs.guava) + implementation(libs.jdom2) + implementation(libs.slf4j.api) + + testImplementation(platform(project(":netcdf-java-testing-platform"))) + + testImplementation(project(":cdm-test-utils")) + + testImplementation(libs.google.truth) + testImplementation(libs.pragmatists.junitparams) + + testCompileOnly(libs.junit4) + + testRuntimeOnly(libs.junit5.platformLauncher) + testRuntimeOnly(libs.junit5.vintageEngine) + testRuntimeOnly(libs.logback.classic) +} diff --git a/opendap/src/test/java/opendap/test/TestFiles.java b/opendap/src/test/java/opendap/test/TestFiles.java index 5ce42c8c16..d2bf3a0bff 100644 --- a/opendap/src/test/java/opendap/test/TestFiles.java +++ b/opendap/src/test/java/opendap/test/TestFiles.java @@ -136,7 +136,6 @@ public TestFiles() { //////////////////// static String[] xtestfiles = {}; - /* No longer available */ static String[][] specialtests = - {{".das", "http://dods.mbari.org/cgi-bin/nph-nc/data/ssdsdata/deployments/m1/200810", "OS_M1_20081008_TS.nc"}}; + {{".das", "https://dods.mbari.org/cgi-bin/nph-nc/data/ssdsdata/deployments/m1/200810", "OS_M1_20081008_TS.nc"}}; } diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 17ed36aaa3..0000000000 --- a/settings.gradle +++ /dev/null @@ -1,43 +0,0 @@ -// Explicitly name the root project. See: http://stackoverflow.com/questions/20128416 -rootProject.name = 'netcdf-java' - -// These all refer to subdirectory names. -include 'bufr' -include 'cdm:core' -include 'cdm:gcdm' -include 'cdm:image' -include 'cdm:misc' -include 'cdm:radial' -include 'cdm:s3' -include 'cdm:zarr' -include 'cdm-test' -include 'cdm-test-utils' -include 'docs' -include 'grib' -include 'httpservices' -include 'legacy' -include 'native-compression:libaec-jna' -include 'native-compression:libaec-native' -include 'netcdf4' -include 'netcdf-java-bom' -include 'netcdf-java-platform' -include 'netcdf-java-testing-platform' -include 'opendap' -include 'dap4' -include 'udunits' -include 'uibase' -include 'uicdm' -include 'visad:mcidas' -include 'visad:vis5d' -include 'waterml' - -// Set name of cdm submodules -project(':cdm:core').name = 'cdm-core' -project(':cdm:gcdm').name = 'cdm-gcdm' -project(':cdm:image').name = 'cdm-image' -project(':cdm:radial').name = 'cdm-radial' -project(':cdm:misc').name = 'cdm-misc' -project(':cdm:s3').name = 'cdm-s3' -project(':cdm:zarr').name = 'cdm-zarr' -project(':visad:mcidas').name = 'cdm-mcidas' -project(':visad:vis5d').name = 'cdm-vis5d' diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000000..7dd23118c6 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +dependencyResolutionManagement { + repositoriesMode = RepositoriesMode.FAIL_ON_PROJECT_REPOS + repositories { + mavenCentral() + // be very specific about where non-maven-central-based artifacts are sourced + exclusiveContent { + forRepository { + maven { url = uri("https://artifacts.unidata.ucar.edu/repository/unidata-3rdparty/") } + } + filter { includeModule("org.bounce", "bounce") } + } + exclusiveContent { + forRepository { + maven { url = uri("https://artifacts.unidata.ucar.edu/repository/unidata-releases/") } + } + filter { + includeModule("edu.ucar", "jj2000") + includeModule("edu.wisc.ssec", "visad-mcidas-slim-ucar-ns") + includeModule("edu.wisc.ssec", "visad") + } + } + } +} + +includeBuild("build-logic") + +rootProject.name = "netcdf-java" + +// +// platforms used by all +// +include("netcdf-java-platform") + +include("netcdf-java-testing-platform") + +// +// no subproject dependencies +// +include(":libaec-native") + +project(":libaec-native").projectDir = file("native-compression/libaec-native") + +// +// critical subprojects +// tricky main/test interdependencies...not circular, however +// +include("httpservices") // needed by cdm-test-utils main, cdm-core main + +include("udunits") // needed by cdm-test-utils main, cdm-core main + +include(":cdm-core") + +project(":cdm-core").projectDir = file("cdm/core") + +include("cdm-test-utils") // needed by many for test, its main depends on cdm:core, httpservices + +// +// these subprojects depend on one or more of the critical subprojects +// but otherwise stand alone +// +include("bufr") + +include(":cdm-image") + +project(":cdm-image").projectDir = file("cdm/image") + +include("dap4") + +include(":libaec-jna") + +project(":libaec-jna").projectDir = file("native-compression/libaec-jna") + +include("netcdf4") + +include("opendap") + +include("uibase") + +include("waterml") + +// +// branch of grib dependent subprojects +// +include("grib") // main needs :native-compression:libaec-jna + +include("cdm-image") // runtime depends on grib + +project(":cdm-image").projectDir = file("cdm/image") + +include("cdm-misc") // test depends on grib + +project(":cdm-misc").projectDir = file("cdm/misc") + +include(":gcdm") // runtime depends on grib + +project(":gcdm").projectDir = file("cdm/gcdm") + +include("legacy") // main depends on grib + +include("cdm-mcidas") // main depends on grib + +project(":cdm-mcidas").projectDir = file("visad/mcidas") + +include("cdm-vis5d") // main depends on visad:mcidas + +project(":cdm-vis5d").projectDir = file("visad/vis5d") + +// +// branch of cdm-radial dependent subprojects +// +include("cdm-radial") // test depends on netcdf4 + +project(":cdm-radial").projectDir = file("cdm/radial") + +include("cdm-s3") // test depends on cdm:radial + +project(":cdm-s3").projectDir = file("cdm/s3") + +include("cdm-zarr") // test depends on cdm:s3 + +project(":cdm-zarr").projectDir = file("cdm/zarr") + +include("docs") // test depends on cdm:s3, grib + +// +// many subproject dependencies +// +include(":uicdm") + +include(":cdm-test") + +include(":uber-jars") + +include("code-coverage-report") + +include("netcdf-java-bom") diff --git a/third-party-licenses/gretty/LICENSE b/third-party-licenses/gretty/LICENSE deleted file mode 100644 index e712fcdcb2..0000000000 --- a/third-party-licenses/gretty/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2013-2016 Timur Shakurov, Andrey Hihlovskiy and contributors - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/uber-jars/build.gradle.kts b/uber-jars/build.gradle.kts new file mode 100644 index 0000000000..3436530844 --- /dev/null +++ b/uber-jars/build.gradle.kts @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import java.security.MessageDigest +import org.cyclonedx.gradle.CyclonedxDirectTask + +plugins { + id("java-base-conventions") + alias(libs.plugins.shadow).apply(false) + alias(libs.plugins.cyclonedx.bom).apply(false) +} + +description = + "Manage the creation of uber-jars (netCDFAll, ncIdv, and toolsUI) and their Software Bill of Materials (SBOMs)." + +extra["project.title"] = "Uber-jars and SBOMs" + +// output location for generated artifacts +val artifactOutputLocation = rootProject.layout.buildDirectory.dir("distributions") + +// remove generated artifacts when running clean +tasks.clean { delete(artifactOutputLocation) } + +// configurations for the uber-jars +val toolsUI by configurations.creating { extendsFrom(configurations.implementation.get()) } + +val ncIdv by configurations.creating { extendsFrom(configurations.implementation.get()) } + +val netcdfAll by configurations.creating { extendsFrom(configurations.implementation.get()) } + +dependencies { + implementation(platform(project(":netcdf-java-platform"))) + + // common to all uber-jars + implementation(project(":bufr")) + implementation(project(":dap4")) + implementation(project(":cdm-core")) + implementation(project(":cdm-image")) + implementation(project(":cdm-mcidas")) + implementation(project(":cdm-misc")) + implementation(project(":cdm-radial")) + implementation(project(":grib")) + implementation(project(":opendap")) + implementation(project(":httpservices")) + + // ncIdv specific + ncIdv(project(":cdm-vis5d")) + ncIdv(project(":legacy")) + ncIdv(project(":libaec-native")) + + // netcdfAll specific + netcdfAll(project(":netcdf4")) + + // :uicdm is toolsUI + toolsUI(project(":uicdm")) +} + +// do not build a jar for the subproject +tasks.jar { enabled = false } + +///////////////////////// +// uber-jar generation // +///////////////////////// + +val buildToolsUi = + tasks.register("buildToolsUI") { + archiveBaseName = "toolsUI" + configurations = listOf(toolsUI) + + doFirst { manifest.attributes(project(":uicdm").tasks.jar.get().manifest.attributes) } + } + +val buildNcIdv = + tasks.register("buildNcIdv") { + archiveBaseName = "ncIdv" + configurations = listOf(ncIdv) + + exclude("edu/wisc/**") + exclude("nom/**") + exclude("visad/**") + + doFirst { manifest.attributes["Implementation-Title"] = "ncIdv jar" } + } + +val buildNetcdfAll = + tasks.register("buildNetcdfAll") { + archiveBaseName = "netcdfAll" + configurations = listOf(netcdfAll) + + doFirst { manifest.attributes(project(":cdm-core").tasks.jar.get().manifest.attributes) } + } + +// common configuration for all uber-jars +val uberJarTasks = listOf(buildNcIdv, buildNetcdfAll, buildToolsUi) + +uberJarTasks.forEach { + it { + archiveClassifier = "" + // Transformations + duplicatesStrategy = DuplicatesStrategy.INCLUDE + transform { + resource = "project.properties" + } + transform< + com.github.jengelman.gradle.plugins.shadow.transformers.ApacheLicenseResourceTransformer + >() + transform< + com.github.jengelman.gradle.plugins.shadow.transformers.ApacheNoticeResourceTransformer + >() + mergeServiceFiles() + filesMatching("logback.xml") { + duplicatesStrategy = DuplicatesStrategy.FAIL // Or WARN. + } + destinationDirectory = artifactOutputLocation + from(rootDir.absolutePath) { + include("LICENSE") + into("META-INF/unidata-license") + } + } +} + +///////////////////// +// SBOM generation // +///////////////////// + +val netcdfAllSbom = + tasks.register("netcdfAllSbom") { + group = "build" + includeConfigs = listOf("netcdfAll") + xmlOutput = artifactOutputLocation.get().file("netcdfAll-sbom.xml") + jsonOutput = artifactOutputLocation.get().file("netcdfAll-sbom.json") + } + +val ncIdvSbom = + tasks.register("ncIdvSbom") { + group = "build" + includeConfigs = listOf("ncIdv") + xmlOutput = artifactOutputLocation.get().file("ncIdv-sbom.xml") + jsonOutput = artifactOutputLocation.get().file("ncIdv-sbom.json") + } + +val toolsUISbom = + tasks.register("toolsUISbom") { + group = "build" + includeConfigs = listOf("toolsUI") + xmlOutput = artifactOutputLocation.get().file("toolsUI-sbom.xml") + jsonOutput = artifactOutputLocation.get().file("toolsUI-sbom.json") + } + +val buildSboms = tasks.register("buildSboms") { dependsOn(netcdfAllSbom, ncIdvSbom, toolsUISbom) } + +////////////////////////////////// +// Artifact Checksum generation // +////////////////////////////////// + +val createChecksums = + tasks.register("createChecksums") { + group = "build" + description = "Create .sha1, .sha256, and .md5 checksum files for the uber-jars." + + doLast { + listOf("MD5", "SHA-1", "SHA-256").forEach { algorithm -> + fileTree(artifactOutputLocation) { include("*.jar", "*-sbom.json", "*-sbom.xml") } + .forEach { jarFile -> + MessageDigest.getInstance(algorithm) + .let { md -> + md.digest(file(jarFile).readBytes()).let { + BigInteger(1, it).toString(16).padStart(md.digestLength * 2, '0') + } + } + .let { checksum -> + artifactOutputLocation + .get() + .asFile + .resolve("${jarFile.name}.${algorithm.lowercase().replace("-", "")}") + .writeText(checksum) + } + } + } + } + dependsOn(uberJarTasks, buildSboms) + } + +// aggregate tasks +tasks.assemble { dependsOn(uberJarTasks, buildSboms, createChecksums) } diff --git a/udunits/build.gradle b/udunits/build.gradle deleted file mode 100644 index 2a38b721a5..0000000000 --- a/udunits/build.gradle +++ /dev/null @@ -1,28 +0,0 @@ -plugins { - id "org.javacc.javacc" version "4.0.1" -} - -description = 'The ucar.units Java package is for decoding and encoding formatted unit specifications ' + - '(e.g. "m/s"), converting numeric values between compatible units (e.g. between "m/s" ' + - 'and "knot"), and for performing arithmetic operations on units (e.g. dividing one unit ' + - 'by another, or raising a unit to a power).' - -ext.title = 'UDUNITS' -ext.url = 'https://www.unidata.ucar.edu/software/udunits/' - -apply from: "$rootDir/gradle/any/dependencies.gradle" -apply from: "$rootDir/gradle/any/java-library.gradle" - -dependencies { - api enforcedPlatform(project(':netcdf-java-platform')) - testImplementation enforcedPlatform(project(':netcdf-java-testing-platform')) - - implementation 'com.google.code.findbugs:jsr305' - - testImplementation project(':cdm-test-utils') - - testImplementation 'junit:junit' - testImplementation 'org.slf4j:slf4j-api' - - testRuntimeOnly 'ch.qos.logback:logback-classic' -} diff --git a/udunits/build.gradle.kts b/udunits/build.gradle.kts new file mode 100644 index 0000000000..c2a15e8205 --- /dev/null +++ b/udunits/build.gradle.kts @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { + id("java-library-conventions") + alias(libs.plugins.javacc) +} + +description = + "The ucar.units Java package is for decoding and encoding formatted unit specifications " + + "(e.g. \"m/s\"), converting numeric values between compatible units (e.g. between \"m/s\" " + + "and \"knot\"), and for performing arithmetic operations on units (e.g. dividing one unit " + + "by another, or raising a unit to a power)." + +extra["project.title"] = "UDUNITS" + +extra["project.url"] = "https://www.unidata.ucar.edu/software/udunits/" + +dependencies { + implementation(platform(project(":netcdf-java-platform"))) + + implementation(libs.findbugs.jsr305) + + testImplementation(platform(project(":netcdf-java-testing-platform"))) + + testImplementation(libs.slf4j.api) + + testCompileOnly(libs.junit4) + + testRuntimeOnly(libs.junit5.platformLauncher) + testRuntimeOnly(libs.junit5.vintageEngine) + testRuntimeOnly(libs.logback.classic) +} diff --git a/uibase/build.gradle b/uibase/build.gradle deleted file mode 100644 index 253d75085f..0000000000 --- a/uibase/build.gradle +++ /dev/null @@ -1,45 +0,0 @@ -description = 'UI elements that are independent of the CDM.' -ext.title = 'UI base library' - -apply from: "$rootDir/gradle/any/dependencies.gradle" -apply from: "$rootDir/gradle/any/java-library.gradle" - -dependencies { - implementation enforcedPlatform(project(':netcdf-java-platform')) - testImplementation enforcedPlatform(project(':netcdf-java-testing-platform')) - - implementation 'org.jdom:jdom2' - implementation 'com.google.protobuf:protobuf-java' - implementation 'com.google.guava:guava' - implementation 'com.google.code.findbugs:jsr305' - implementation 'org.slf4j:slf4j-api' - - // Used by NcmlEditor. Abandoned, no updates since 2013. https://sourceforge.net/projects/bounce/ - // https://sourceforge.net/p/bounce/svn/HEAD/tree/src/main/java/org/bounce/ - api 'org.bounce:bounce:0.18' - - // http://www.jfree.org/ - api 'org.jfree:jcommon:1.0.23' - api 'org.jfree:jfreechart:1.0.19' - - // http://www.jgoodies.com/. Latest version is 1.9.0, but there is breakage when we try to upgrade. - api 'com.jgoodies:jgoodies-forms:1.6.0' - - // LGoodDatePicker - swing calendar widget used in TdsMonitor - api 'com.github.lgooddatepicker:LGoodDatePicker:10.3.1' - - testImplementation project(':cdm:cdm-core') - testImplementation project(':cdm-test-utils') - - testImplementation 'commons-io:commons-io' - testImplementation 'org.mockito:mockito-core' - testImplementation 'com.google.truth:truth' - - testRuntimeOnly 'ch.qos.logback:logback-classic' -} - -test { - // Tell java to use ucar.util.prefs.PreferencesExtFactory to generate preference objects - // Important for ucar.util.prefs.TestJavaUtilPreferences - systemProperty 'java.util.prefs.PreferencesFactory', 'ucar.util.prefs.PreferencesExtFactory' -} diff --git a/uibase/build.gradle.kts b/uibase/build.gradle.kts new file mode 100644 index 0000000000..a432bc4448 --- /dev/null +++ b/uibase/build.gradle.kts @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { id("java-base-conventions") } + +description = "UI elements that are independent of the CDM." + +project.extra["project.title"] = "UI base library" + +dependencies { + implementation(platform(project(":netcdf-java-platform"))) + + implementation(libs.bounce) + implementation(libs.findbugs.jsr305) + implementation(libs.guava) + implementation(libs.jfree.jcommon) + implementation(libs.jfree.jfreechart) + implementation(libs.jgoodies.forms) + implementation(libs.jdom2) + implementation(libs.lgooddatepicker) + implementation(libs.protobuf) + implementation(libs.slf4j.api) + + testImplementation(platform(project(":netcdf-java-testing-platform"))) + + testImplementation(project(":cdm-core")) + testImplementation(project(":cdm-test-utils")) + + testImplementation(libs.commons.io) + testImplementation(libs.google.truth) + testImplementation(libs.mockito.core) + + testCompileOnly(libs.junit4) + + testRuntimeOnly(libs.junit5.platformLauncher) + testRuntimeOnly(libs.junit5.vintageEngine) + testRuntimeOnly(libs.logback.classic) +} + +tasks.test { + // Tell java to use ucar.util.prefs.PreferencesExtFactory to generate preference objects + // Important for ucar.util.prefs.TestJavaUtilPreferences + systemProperty("java.util.prefs.PreferencesFactory", "ucar.util.prefs.PreferencesExtFactory") +} diff --git a/uicdm/build.gradle b/uicdm/build.gradle deleted file mode 100644 index 60ca07058e..0000000000 --- a/uicdm/build.gradle +++ /dev/null @@ -1,67 +0,0 @@ -plugins { - id "application" -} - -description = 'Provides a graphical interface to the CDM library.' -ext.title = 'ToolsUI' - -apply from: "$rootDir/gradle/any/dependencies.gradle" -apply from: "$rootDir/gradle/any/java-library.gradle" - -dependencies { - implementation enforcedPlatform(project(':netcdf-java-platform')) - testImplementation enforcedPlatform(project(':netcdf-java-testing-platform')) - - // todo: a lot of these could probably be runtimeOnly - implementation project(':cdm:cdm-core') - implementation project(':cdm:cdm-image') // things that depend on java.awt - implementation project(':cdm:cdm-misc') // misc iosps - implementation project(':cdm:cdm-radial') // radial data - implementation project(':bufr') - implementation project(':grib') - implementation project(':httpservices') - implementation project(':netcdf4') - implementation project(':opendap') - implementation project(':dap4') - implementation project(':udunits') - implementation project(':uibase') - implementation project(':visad:cdm-mcidas') // For Gempak IOSPs. - implementation project(':waterml') - - // constrained by netcdf-java-platform - implementation 'org.apache.xmlbeans:xmlbeans' - implementation 'org.jdom:jdom2' - implementation 'org.apache.httpcomponents:httpclient' - implementation 'com.google.code.findbugs:jsr305' - implementation 'com.google.protobuf:protobuf-java' - implementation 'com.google.re2j:re2j' - implementation 'org.slf4j:slf4j-api' - - testImplementation project(':cdm-test-utils') - testImplementation 'junit:junit' - - runtimeOnly project(':cdm:cdm-s3') - runtimeOnly project(':cdm:cdm-zarr') - runtimeOnly project(':native-compression:libaec-native') - - // constrained by netcdf-java-platform - runtimeOnly 'ch.qos.logback:logback-classic' -} - -application { - mainClass.set("ucar.nc2.ui.ToolsUI") -} - -jar.manifest.attributes 'Main-Class': 'ucar.nc2.ui.ToolsUI' - -def genBuildInfo = tasks.register("generateBuildInfo", WriteProperties) { - destinationFile = getLayout().buildDirectory.file("resources/main/toolsui.properties") - comment = "Application configuration generated by Gradle" - encoding = "UTF-8" - - property("toolsui.version", project.version) - property("toolsui.buildTimestamp", project.buildTimestamp) // Defined in root project. -} - -processResources.finalizedBy(genBuildInfo) -compileJava.dependsOn(genBuildInfo) diff --git a/uicdm/build.gradle.kts b/uicdm/build.gradle.kts new file mode 100644 index 0000000000..3d653f5c4a --- /dev/null +++ b/uicdm/build.gradle.kts @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +import java.text.SimpleDateFormat +import java.util.Date + +plugins { + id("java-base-conventions") + application +} + +description = "Provides a graphical interface to the CDM library." + +project.extra["project.title"] = "ToolsUI" + +dependencies { + implementation(platform(project(":netcdf-java-platform"))) + + implementation(project(":bufr")) + implementation(project(":cdm-core")) + implementation(project(":cdm-image")) + implementation(project(":cdm-misc")) + implementation(project(":grib")) + implementation(project(":httpservices")) + implementation(project(":netcdf4")) + implementation(project(":opendap")) + implementation(project(":udunits")) + implementation(project(":uibase")) + implementation(project(":waterml")) + + implementation(libs.bounce) + implementation(libs.jdom2) + implementation(libs.jgoodies.forms) + implementation(libs.jfree.jfreechart) + implementation(libs.findbugs.jsr305) + implementation(libs.lgooddatepicker) + implementation(libs.protobuf) + implementation(libs.re2j) + implementation(libs.slf4j.api) + implementation(libs.xmlbeans) + + runtimeOnly(project(":cdm-mcidas")) + runtimeOnly(project(":cdm-radial")) + runtimeOnly(project(":cdm-s3")) + runtimeOnly(project(":cdm-zarr")) + runtimeOnly(project(":dap4")) + runtimeOnly(project(":libaec-native")) + + runtimeOnly(libs.logback.classic) + + testImplementation(platform(project(":netcdf-java-testing-platform"))) + + testImplementation(project(":cdm-test-utils")) + + testCompileOnly(libs.junit4) + + testRuntimeOnly(libs.junit5.platformLauncher) + testRuntimeOnly(libs.junit5.vintageEngine) +} + +application { mainClass = "ucar.nc2.ui.ToolsUI" } + +tasks.jar { manifest { attributes["Main-Class"] = "ucar.nc2.ui.ToolsUI" } } + +val getBuildInfo = + tasks.register("getBuildInfo") { + destinationFile = getLayout().buildDirectory.file("resources/main/toolsui.properties") + comment = "Application configuration generated by Gradle" + encoding = "UTF-8" + property("toolsui.version", project.version) + val iso8601Format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ") + property("toolsui.buildTimestamp", iso8601Format.format(Date())) + } + +tasks.processResources { finalizedBy(getBuildInfo) } + +tasks.compileJava { dependsOn(getBuildInfo) } diff --git a/visad/build.gradle b/visad/build.gradle deleted file mode 100644 index fc188288d1..0000000000 --- a/visad/build.gradle +++ /dev/null @@ -1,7 +0,0 @@ -description = 'Visad based IOSPs.' -ext.title = 'Visad based IOSPs' - -subprojects { - // TODO: Give the subprojects real titles. - ext.title = "visad: $name" -} diff --git a/visad/mcidas/build.gradle b/visad/mcidas/build.gradle deleted file mode 100644 index 0f0e85c6b8..0000000000 --- a/visad/mcidas/build.gradle +++ /dev/null @@ -1,25 +0,0 @@ -description = 'Mcidas and Gempak IOSPs.' -ext.title = 'Mcidas and Gempak IOSPs' - -apply from: "$rootDir/gradle/any/dependencies.gradle" -apply from: "$rootDir/gradle/any/java-library.gradle" - -dependencies { - api enforcedPlatform(project(':netcdf-java-platform')) - testImplementation enforcedPlatform(project(':netcdf-java-testing-platform')) - - api project(':cdm:cdm-core') - - implementation project(':grib') - implementation project(':udunits') - - implementation 'edu.wisc.ssec:visad-mcidas-slim-ucar-ns' - implementation 'com.google.guava:guava' - implementation 'com.google.re2j:re2j' - - implementation 'org.slf4j:slf4j-api' - - testImplementation project(':cdm-test-utils') - - testImplementation 'com.google.truth:truth' -} diff --git a/visad/mcidas/build.gradle.kts b/visad/mcidas/build.gradle.kts new file mode 100644 index 0000000000..2311296f85 --- /dev/null +++ b/visad/mcidas/build.gradle.kts @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { id("java-library-conventions") } + +description = "Mcidas and Gempak IOSPs." + +project.extra["project.title"] = "Mcidas and Gempak IOSPs" + +dependencies { + implementation(platform(project(":netcdf-java-platform"))) + + api(project(":cdm-core")) + + implementation(project(":grib")) + implementation(project(":udunits")) + + implementation(libs.guava) + implementation(libs.re2j) + implementation(libs.slf4j.api) + implementation(libs.ssec.visadMcidasSlimUcarNs) + + testImplementation(platform(project(":netcdf-java-testing-platform"))) + + testImplementation(project(":cdm-test-utils")) + + testImplementation(libs.google.truth) + + testCompileOnly(libs.junit4) + + testRuntimeOnly(libs.junit5.platformLauncher) + testRuntimeOnly(libs.junit5.vintageEngine) +} diff --git a/visad/vis5d/build.gradle b/visad/vis5d/build.gradle deleted file mode 100644 index 7f9003ed7b..0000000000 --- a/visad/vis5d/build.gradle +++ /dev/null @@ -1,19 +0,0 @@ -description = 'Vis5D IOSP.' -ext.title = 'Vis5D IOSP' - -apply from: "$rootDir/gradle/any/dependencies.gradle" -apply from: "$rootDir/gradle/any/java-library.gradle" - -dependencies { - api enforcedPlatform(project(':netcdf-java-platform')) - - api project(':cdm:cdm-core') - - implementation project(':visad:cdm-mcidas') - - implementation 'edu.wisc.ssec:visad' - implementation 'com.google.guava:guava' - implementation 'com.google.re2j:re2j' - - implementation 'org.slf4j:slf4j-api' -} diff --git a/visad/vis5d/build.gradle.kts b/visad/vis5d/build.gradle.kts new file mode 100644 index 0000000000..2cb7a901bd --- /dev/null +++ b/visad/vis5d/build.gradle.kts @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { id("java-library-conventions") } + +description = "Vis5D IOSP." + +project.extra["project.title"] = "Vis5D IOSP" + +dependencies { + implementation(platform(project(":netcdf-java-platform"))) + + api(project(":cdm-core")) + + implementation(project(":cdm-mcidas")) + + implementation(libs.guava) + implementation(libs.re2j) + implementation(libs.slf4j.api) + implementation(libs.ssec.visad) + + testImplementation(platform(project(":netcdf-java-testing-platform"))) + + testImplementation(project(":cdm-test-utils")) + + testCompileOnly(libs.junit4) + + testRuntimeOnly(libs.junit5.platformLauncher) + testRuntimeOnly(libs.junit5.vintageEngine) +} diff --git a/waterml/build.gradle b/waterml/build.gradle deleted file mode 100644 index ffe22d638f..0000000000 --- a/waterml/build.gradle +++ /dev/null @@ -1,43 +0,0 @@ -description = 'Converts CDM DSGs to WaterML 2.0 timeseries and vice-versa.' -ext.title = 'NetCDF to WaterML Converter' - -apply from: "$rootDir/gradle/any/dependencies.gradle" -apply from: "$rootDir/gradle/any/java-library.gradle" - -dependencies { - api enforcedPlatform(project(':netcdf-java-platform')) - testImplementation enforcedPlatform(project(':netcdf-java-testing-platform')) - - api project(':cdm:cdm-core') - - implementation('org.n52.sensorweb:52n-xml-waterML-v20') { - exclude group: 'stax', module: 'stax-api' - } - implementation('org.n52.sensorweb:52n-xml-gml-v321') { - exclude group: 'stax', module: 'stax-api' - } - implementation('org.n52.sensorweb:52n-xml-sweCommon-v20') { - exclude group: 'stax', module: 'stax-api' - } - implementation('org.n52.sensorweb:52n-xml-om-v20') { - exclude group: 'stax', module: 'stax-api' - } - implementation('org.n52.sensorweb:52n-xml-sampling-v20') { - exclude group: 'stax', module: 'stax-api' - } - - implementation 'com.google.guava:guava' - implementation 'org.slf4j:slf4j-api' - - testImplementation project(':cdm-test-utils') - - testImplementation 'junit:junit' - - testRuntimeOnly 'ch.qos.logback:logback-classic' -} - -spotless { - java { - targetExclude('src/main/java/ucar/nc2/ogc/erddap/**/*.java') // exclude sources from erddap - } -} diff --git a/waterml/build.gradle.kts b/waterml/build.gradle.kts new file mode 100644 index 0000000000..ea67a8f7c5 --- /dev/null +++ b/waterml/build.gradle.kts @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2025 University Corporation for Atmospheric Research/Unidata + * See LICENSE for license information. + */ + +plugins { id("java-library-conventions") } + +description = "Converts CDM DSGs to WaterML 2.0 timeseries and vice-versa." + +project.extra["project.title"] = "NetCDF to WaterML Converter" + +dependencies { + implementation(platform(project(":netcdf-java-platform"))) + + api(project(":cdm-core")) + + implementation(libs.guava) + implementation(libs.sensorweb.xmlGmlV321) { exclude(group = "stax", module = "stax-api") } + implementation(libs.sensorweb.xmlOmV20) { exclude(group = "stax", module = "stax-api") } + implementation(libs.sensorweb.xmlSamplingV20) { exclude(group = "stax", module = "stax-api") } + implementation(libs.sensorweb.xmlSweCommonV20) { exclude(group = "stax", module = "stax-api") } + implementation(libs.sensorweb.xmlWaterMLV20) { exclude(group = "stax", module = "stax-api") } + implementation(libs.slf4j.api) + + testImplementation(platform(project(":netcdf-java-testing-platform"))) + + testImplementation(project(":cdm-test-utils")) + + testCompileOnly(libs.junit4) + + testRuntimeOnly(libs.junit5.platformLauncher) + testRuntimeOnly(libs.junit5.vintageEngine) + testRuntimeOnly(libs.logback.classic) +} + +spotless { + java { + // exclude sources from erddap + targetExclude("src/main/java/ucar/nc2/ogc/erddap/**/*.java") + } +} + +tasks.withType().configureEach { + from(rootDir.absolutePath) { + include("third-party-licenses/erddap/", "third-party-licenses/NOAA_LICENSE") + into("META-INF/") + } +}