diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ef0aa9203a..47d65f05e6 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -65,11 +65,11 @@ jobs: - uses: actions/checkout@v2 with: fetch-depth: 50 - - name: 'Set up JDK 11' - uses: actions/setup-java@v2 + - name: 'Set up JDK 17' + uses: actions/setup-java@v3 with: distribution: zulu - java-version: 11 + java-version: 17 - uses: burrunan/gradle-cache-action@v1 name: Run CheckerFramework env: @@ -77,11 +77,11 @@ jobs: S3_BUILD_CACHE_SECRET_KEY: ${{ secrets.S3_BUILD_CACHE_SECRET_KEY }} with: read-only: ${{ matrix.os == 'self-hosted' }} - job-id: checker-jdk11 + job-id: checker-jdk17 arguments: --scan --no-parallel --no-daemon -PenableCheckerframework classes source-distribution-check: - name: 'Source distribution (JDK 11)' + name: 'Source distribution (JDK 17)' runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 @@ -90,18 +90,18 @@ jobs: - name: Start PostgreSQL working-directory: docker/postgres-server run: docker-compose up -d && docker-compose logs - - name: 'Set up JDK 11' - uses: actions/setup-java@v2 + - name: 'Set up JDK 17' + uses: actions/setup-java@v3 with: distribution: zulu - java-version: 11 + java-version: 17 - uses: burrunan/gradle-cache-action@v1 name: Prepare source distribution env: S3_BUILD_CACHE_ACCESS_KEY_ID: ${{ secrets.S3_BUILD_CACHE_ACCESS_KEY_ID }} S3_BUILD_CACHE_SECRET_KEY: ${{ secrets.S3_BUILD_CACHE_SECRET_KEY }} with: - job-id: source-release-jdk11 + job-id: source-release-jdk17 arguments: --scan --no-parallel --no-daemon sourceDistribution -Ppgjdbc.version=1.0 -Prelease - name: Verify source distribution working-directory: pgjdbc/build/distributions @@ -157,23 +157,35 @@ jobs: sed -i -r '/- (543[3-4]):\1/d' docker-compose.yml docker-compose up -d docker-compose logs - - name: 'Get test node ARCH' - run: echo "::set-output name=arch_name::$(uname -i)" - id: get_arch_name - - name: Set up Java ${{ matrix.java_version }}, ${{ matrix.java_distribution }} - if: ${{ steps.get_arch_name.outputs.arch_name != 'aarch64' }} - uses: actions/setup-java@v2 + - name: Set up Java 17 and ${{ matrix.non_ea_java_version }}, ${{ matrix.java_distribution }}, ${{ runner.arch }} + uses: actions/setup-java@v3 with: - java-version: ${{ matrix.java_version }} + # The latest one will be the default, so we use Java 17 for launching Gradle + # oracle-actions/setup-java below requires Java 17, so we must install Java 17 before calling oracle-actions/setup-java + java-version: | + ${{ matrix.non_ea_java_version }} + 17 distribution: ${{ matrix.java_distribution }} - architecture: x64 - - name: 'Setup JDK ${{ matrix.java_version }} on ARM64' - if: ${{ steps.get_arch_name.outputs.arch_name == 'aarch64' }} - uses: AdoptOpenJDK/install-jdk@v1 + # Architecture is explicit to workaround https://github.com/actions/setup-java/issues/559 + architecture: ${{ runner.arch == 'ARM64' && 'aarch64' || 'x64' }} + - name: Set up Java ${{ matrix.java_version }}, oracle + id: setup_ea_java + if: ${{ matrix.oracle_java_website != '' }} + uses: oracle-actions/setup-java@7a0114d66dbd02646abd345c3395b34c148e6126 # v1.3.2 + env: + JAVA_HOME_17_X64: ${{ env.JAVA_HOME_17_AARCH64 || env.JAVA_HOME_17_X64 }} with: - impl: hotspot # or openj9 - version: ${{ matrix.java_version }} - architecture: aarch64 + website: ${{ matrix.oracle_java_website }} + release: ${{ matrix.java_version }} + - name: Set up Java 17 ${{ matrix.java_distribution }} as default + # oracle-actions/setup-java above installs EA java by default, so we need to reinstall Java 17 as the default + if: ${{ matrix.oracle_java_website != '' }} + uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: ${{ matrix.java_distribution }} + # Architecture is explicit to workaround https://github.com/actions/setup-java/issues/559 + architecture: ${{ runner.arch == 'ARM64' && 'aarch64' || 'x64' }} - name: Prepare local properties run: | # See https://github.com/actions/runner/issues/409 @@ -196,6 +208,12 @@ jobs: arguments: --scan --no-parallel --no-daemon jandex test properties: | includeTestTags=${{ matrix.includeTestTags }} + testExtraJvmArgs=${{ matrix.testExtraJvmArgs }} + jdkBuildVersion=17 + jdkTestVersion=${{ matrix.java_version == 'EA' && steps.setup_ea_java.outputs.version || matrix.java_version }} + jdkTestVendor=${{ matrix.java_vendor }} + # We provision JDKs with GitHub Actions for caching purposes, so Gradle should rather fail in case JDK is not found + org.gradle.java.installations.auto-download=false - name: 'Install krb5 for GSS tests' if: ${{ matrix.gss == 'yes' }} diff --git a/.github/workflows/matrix.js b/.github/workflows/matrix.js index 1f551d9c2a..a04e4b28f9 100644 --- a/.github/workflows/matrix.js +++ b/.github/workflows/matrix.js @@ -18,16 +18,20 @@ const matrix = new MatrixBuilder(); matrix.addAxis({ name: 'java_distribution', values: [ - 'zulu', - 'temurin', - 'liberica', - 'microsoft', + {value: 'corretto', vendor: 'amazon', weight: 1}, + {value: 'liberica', vendor: 'bellsoft', weight: 1}, + {value: 'microsoft', vendor: 'microsoft', weight: 1}, + {value: 'oracle', vendor: 'oracle', weight: 1}, + {value: 'semeru', vendor: 'ibm', weight: 4}, + {value: 'temurin', vendor: 'eclipse', weight: 1}, + {value: 'zulu', vendor: 'azul', weight: 1}, ] }); -// TODO: support different JITs (see https://github.com/actions/setup-java/issues/279) -matrix.addAxis({name: 'jit', title: '', values: ['hotspot']}); +// We can't yet use EA here, see https://github.com/oracle-actions/setup-java/issues/65 +const eaJava = '22'; +// Below versions will be used for testing only matrix.addAxis({ name: 'java_version', title: x => 'Java ' + x, @@ -36,6 +40,8 @@ matrix.addAxis({ '8', '11', '17', + '21', + eaJava, ] }); @@ -186,8 +192,8 @@ matrix.addAxis({ ] }); -function isLessThan(pg_version, minVersion){ - return Number(pg_version) < Number(minVersion); +function lessThan(minVersion) { + return value => Number(value) < Number(minVersion); } matrix.setNamePattern([ @@ -196,16 +202,25 @@ matrix.setNamePattern([ 'check_anorm_sbt', 'gss', 'replication', 'slow_tests', ]); -matrix.exclude(row => row.ssl.value === 'yes' && isLessThan(row.pg_version, '9.3')); -matrix.exclude(row => row.scram.value === 'yes' && isLessThan(row.pg_version, '10')); -matrix.exclude(row => row.replication.value === 'yes' && isLessThan(row.pg_version, '9.6')); - -// Microsoft Java has no distribution for 8 -matrix.exclude({java_distribution: 'microsoft', java_version: '8'}); +// We take EA builds from Oracle +matrix.imply({java_version: eaJava}, {java_distribution: {value: 'oracle'}}) +matrix.exclude({ssl: {value: 'yes'}, pg_version: lessThan('9.3')}); +// matrix.exclude(row => row.ssl.value === 'yes' && isLessThan(row.pg_version, '9.3')); +matrix.exclude({scram: {value: 'yes'}, pg_version: lessThan('10')}); +matrix.exclude({replication: {value: 'yes'}, pg_version: lessThan('9.6')}); +//org.postgresql.test.jdbc2.ArrayTest fails using simple mode for versions less than 9.0 with malformed Array literal +matrix.exclude({query_mode: {value: 'simple'}, pg_version: lessThan('9.1')}); +//matrix.exclude({query_mode: {value: 'simple'}, pg_version: '8.4'}); +// Microsoft ships Java 11+ +matrix.imply({java_distribution: 'microsoft'}, {java_version: v => v >= 11}); +// Oracle ships Java 11+ +matrix.imply({java_distribution: 'oracle'}, {java_version: v => v === eaJava || v >= 11}); +// TODO: Semeru does not ship Java 21 builds yet +matrix.exclude({java_distribution: {value: 'semeru'}, java_version: '21'}) matrix.exclude({gss: {value: 'yes'}, os: ['windows-latest', 'macos-latest', 'self-hosted']}) if (process.env.GITHUB_REPOSITORY === 'pgjdbc/pgjdbc') { // PG images below 9.3 are x86_64 only - matrix.exclude(row => row.os === 'self-hosted' && isLessThan(row.pg_version, '9.3')); + matrix.exclude({os: 'self-hosted', pg_version: lessThan('9.3')}); } // The most rare features should be generated the first @@ -214,6 +229,8 @@ if (process.env.GITHUB_REPOSITORY === 'pgjdbc/pgjdbc') { // Ensure at least one job with "same" hashcode exists matrix.generateRow({hash: {value: 'same'}}); matrix.generateRow({scram: {value: 'yes'}}); +// Ensure there's a row for Java EA. It is at the beginning to increase chances of covering cases like ssl=yes below +matrix.generateRow({java_version: eaJava}); // Ensure we have a job with the minimal and maximal PostgreSQL versions matrix.generateRow({pg_version: matrix.axisByName.pg_version.values[0]}); matrix.generateRow({pg_version: matrix.axisByName.pg_version.values.slice(-1)[0]}); @@ -221,8 +238,10 @@ matrix.generateRow({pg_version: matrix.axisByName.pg_version.values.slice(-1)[0] matrix.generateRow({query_mode: {value: 'simple'}}); // Ensure there will be at least one job with minimal supported Java matrix.generateRow({java_version: matrix.axisByName.java_version.values[0]}); -// Ensure there will be at least one job with the latest Java -matrix.generateRow({java_version: matrix.axisByName.java_version.values.slice(-1)[0]}); +// Ensure there will be at least one job with Java 17 +matrix.generateRow({java_version: "17"}); +// Ensure there will be at least one job with the latest Java (excluding EA) +matrix.generateRow({java_version: matrix.axisByName.java_version.values.slice(-2)[0]}); matrix.generateRow({ssl: {value: 'yes'}}); // Ensure at least one Windows and at least one Linux job is present (macOS is almost the same as Linux) // matrix.generateRow({os: 'windows-latest'}); @@ -236,7 +255,26 @@ if (include.length === 0) { } include.sort((a, b) => a.name.localeCompare(b.name, undefined, {numeric: true})); include.forEach(v => { + let gradleArgs = [ + `-Duser.country=${v.locale.country}`, + `-Duser.language=${v.locale.language}`, + ]; + v.extraGradleArgs = gradleArgs.join(' '); +}); +include.forEach(v => { + // Arguments passed to all the JVMs via _JAVA_OPTIONS let jvmArgs = []; + // Extra JVM arguments passed to test execution + let testJvmArgs = []; + jvmArgs.push(`-Duser.country=${v.locale.country}`); + jvmArgs.push(`-Duser.language=${v.locale.language}`); + + v.java_distribution = v.java_distribution.value; + v.java_vendor = v.java_distribution.vendor; + if (v.java_distribution === 'oracle') { + v.oracle_java_website = v.java_version === eaJava ? 'jdk.java.net' : 'oracle.com'; + } + v.non_ea_java_version = v.java_version === eaJava ? '' : v.java_version; v.replication = v.replication.value; v.slow_tests = v.slow_tests.value; v.xa = v.xa.value; @@ -271,7 +309,8 @@ include.forEach(v => { // Gradle does not work in tr_TR locale, so pass locale to test only: https://github.com/gradle/gradle/issues/17361 jvmArgs.push(`-Duser.country=${v.locale.country}`); jvmArgs.push(`-Duser.language=${v.locale.language}`); - if (v.jit === 'hotspot' && Math.random() > 0.5) { + let jit = v.java_distribution === 'semeru' ? 'open9j' : 'hotspot'; + if (jit === 'hotspot' && Math.random() > 0.5) { // The following options randomize instruction selection in JIT compiler // so it might reveal missing synchronization in TestNG code v.name += ', stress JIT'; diff --git a/.github/workflows/matrix_builder.js b/.github/workflows/matrix_builder.js index 15c75b2af3..7dbdc1b114 100644 --- a/.github/workflows/matrix_builder.js +++ b/.github/workflows/matrix_builder.js @@ -18,7 +18,7 @@ class Axis { } if (Array.isArray(filter)) { // e.g. row={os: 'windows'}; filter=[{os: 'linux'}, {os: 'linux'}] - return filter.find(v => Axis.matches(row, v)); + return filter.some(v => Axis.matches(row, v)); } if (typeof filter === 'object') { // e.g. row={jdk: {name: 'openjdk', version: 8}}; filter={jdk: {version: 8}} @@ -68,6 +68,7 @@ class MatrixBuilder { this.duplicates = {}; this.excludes = []; this.includes = []; + this.implications = []; this.failOnUnsatisfiableFilters = false; } @@ -80,13 +81,23 @@ class MatrixBuilder { } /** - * Specifies exclude filter (e.g. exclude a forbidden combination) + * Specifies exclude filter (e.g. exclude a forbidden combination). * @param filter */ exclude(filter) { this.excludes.push(filter); } + /** + * Adds implication like `antecedent -> consequent`. + * In other words, if `antecedent` holds, then `consequent` must also hold. + * @param antecedent + * @param consequent + */ + imply(antecedent, consequent) { + this.implications.push({antecedent: antecedent, consequent: consequent}); + } + addAxis({name, title, values}) { const axis = new Axis({name, title, values}); this.axes.push(axis); @@ -104,8 +115,10 @@ class MatrixBuilder { * @returns {boolean} */ matches(row) { - return (this.excludes.length === 0 || !this.excludes.find(f => Axis.matches(row, f))) && - (this.includes.length === 0 || this.includes.find(f => Axis.matches(row, f))); + return (this.excludes.length === 0 || !this.excludes.some(f => Axis.matches(row, f))) && + (this.includes.length === 0 || this.includes.some(f => Axis.matches(row, f))) && + (this.implications.length === 0 || ( + this.implications.every(i => !Axis.matches(row, i.antecedent) || Axis.matches(row, i.consequent)))); } failOnUnsatisfiableFilters(value) { @@ -125,7 +138,7 @@ class MatrixBuilder { let res; if (filter) { // If matching row already exists, no need to generate more - res = this.rows.find(v => Axis.matches(v, filter)); + res = this.rows.some(v => Axis.matches(v, filter)); if (res) { return res; } @@ -152,7 +165,13 @@ class MatrixBuilder { return title; } const computeTitle = this.axisByName[axisName].title; - return computeTitle ? computeTitle(value) : value; + if (computeTitle) { + return computeTitle(value); + } + if (typeof value === 'object' && value.hasOwnProperty('value')) { + return value.value; + } + return value; }).filter(Boolean).join(", "); this.rows.push(res); return res; diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3b03eca20a..bc7714e0b9 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -76,10 +76,22 @@ Here are a few important things you should know about contributing code: In order to build the source code for PgJDBC you will need the following tools: - - A git client - - A JDK for the JDBC version you'd like to build (JDK8 for JDBC 4.2) + - A Git client + - A JDK for the JDBC version you'd like to build (Java 17 for pgjdbc 42.7.1, Java 8 for older pgjdbc releases) - A running PostgreSQL instance (optional for unit/integration tests) +We use [Gradle's Toolchains for JVM projects](https://docs.gradle.org/current/userguide/toolchains.html) +to separate JDK version used for building and running tests. +This means Gradle will automatically find the required JDK version or download it for you. + +You could control JDK versions with the following Gradle properties: +* `jdkBuildVersion`. Defaults to 17. [JDK version](https://docs.gradle.org/8.4/userguide/toolchains.html#sec:consuming) to use for building $projectName. If the value is 0, then the current Java is used. +* `jdkBuildVendor`. [JDK vendor](https://docs.gradle.org/8.4/userguide/toolchains.html#sec:vendors) to use building +* `jdkBuildImplementation`. Vendor-specific [virtual machine implementation](https://docs.gradle.org/8.4/userguide/toolchains.html#selecting_toolchains_by_virtual_machine_implementation) to use building +* `jdkTestVersion`. Defaults to `jdkBuildVersion`. [JDK version](https://docs.gradle.org/8.4/userguide/toolchains.html#sec:consuming) to use for testing. +* `jdkTestVendor`. Defaults to `jdkBuildVendor`. [JDK vendor](https://docs.gradle.org/8.4/userguide/toolchains.html#sec:vendors) to use testing +* `jdkTestImplementation`. Defaults to `jdkBuildImplementation`. Vendor-specific [virtual machine implementation](https://docs.gradle.org/8.4/userguide/toolchains.html#selecting_toolchains_by_virtual_machine_implementation) to use testing + Additionally, in order to update translations (not typical), you will need the following additional tools: - the gettext package, which contains the commands "msgfmt", "msgmerge", and "xgettext" @@ -109,6 +121,9 @@ on a command line (the outputs are located in the relevant: ./gradlew test # execute tests ./gradlew test --tests org.postgresql.test.ssl.SslTest # execute test by class ./gradlew test -PincludeTestTags=!org.postgresql.test.SlowTests # skip slow tests + ./gradlew test -PjdkTestVersion=21 --tests org.postgresql.test.ssl.SslTest # execute test with Java 21 + + ./gradlew parameters # list most build parameters like jdkTestVersion above Note: `clean` is not required, and the build automatically re-executes the tasks. However, Gradle caches the results of `test` execution as well, so if you want to @@ -218,7 +233,7 @@ is used for releasing artifacts. ## Releasing a new version Prerequisites: -- Java 8 +- Java 17 - a PostgreSQL instance for running tests; it must have a user named `test` as well as a database named `test` - ensure that the RPM packaging CI isn't failing at [copr web page](https://copr.fedorainfracloud.org/coprs/g/pgjdbc/pgjdbc-travis/builds/) diff --git a/TESTING.md b/TESTING.md index 67c202819e..46a593e93c 100644 --- a/TESTING.md +++ b/TESTING.md @@ -10,7 +10,7 @@ guidelines and an example for developers to add new test cases. ## 2 - Installation -Of course, you need to have a [Java 8 JDK](https://www.oracle.com/technetwork/java/javase/downloads/index.html). +Java 11+ is required to build pgjdbc. We recommend installing [Java 17](https://javaalmanac.io/jdk/17/). You need to install and build the PostgreSQL JDBC driver source tree. You can download it from https://github.com/pgjdbc/pgjdbc. See @@ -102,6 +102,14 @@ to see an animated coloured progress bar as the tests are executed, you may want to use one of the GUI versions of the test runner. See the JUnit documentation for more information. +You could specify Java version for testing purposes with `-PjdkTestVersion=8` build parameter: + +```sh +./gradlew -PjdkTestVersion=8 test +``` + +You could lauch `./gradlew parameters` to get the list of available parameters. + If the test suite reports errors or failures that you cannot explain, please post the relevant parts of the output to the mailing list pgsql-jdbc@postgresql.org. diff --git a/appveyor.yml b/appveyor.yml index 17877474f5..20b5b59233 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,16 +1,20 @@ # appveyor.yml -image: Visual Studio 2015 +image: Visual Studio 2022 configuration: Release clone_depth: 1 environment: +<<<<<<< HEAD JAVA_HOME: 'C:\Program Files\Java\jdk1.8.0' PGUSER: postgres PGPASSWORD: Password12! +======= + JAVA_HOME: 'C:\Program Files\Java\jdk17' +>>>>>>> 3a5f1854 (chore: use Java 17 for building pgjdbc, and use --release 8 to target Java 8, add tests with Java 21 and 22 (#3026)) matrix: - pg: 9.6.11-1 PlatformToolset: v120 - - pg: 10.6-1 + - pg: 15.2-1 PlatformToolset: v120 matrix: allow_failures: @@ -27,8 +31,6 @@ skip_commits: - 'docs/**' - '**/*.md' - '.github/**' - - '.travis.yml' - - '.travis/**' init: - set pf=%ProgramFiles%&& set x64=-x64 diff --git a/benchmarks/build.gradle.kts b/benchmarks/build.gradle.kts index 55aae72768..e1fd299eb4 100644 --- a/benchmarks/build.gradle.kts +++ b/benchmarks/build.gradle.kts @@ -1,31 +1,17 @@ /* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to you under the Apache License, Version 2.0 - * (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright (c) 2020, PostgreSQL Global Development Group + * See the LICENSE file in the project root for more information. */ plugins { + id("build-logic.java-library") id("me.champeau.jmh") } dependencies { // Make jmhCompileClasspath resolvable - jmhImplementation(platform(project(":bom"))) jmhImplementation(project(":postgresql")) -// jmhImplementation("com.google.guava:guava") -// jmhImplementation("org.codehaus.janino:commons-compiler") - jmhImplementation("org.openjdk.jmh:jmh-core") - jmhImplementation("org.openjdk.jmh:jmh-generator-annprocess") + jmhImplementation("org.openjdk.jmh:jmh-core:1.12") + jmhImplementation("org.openjdk.jmh:jmh-generator-annprocess:1.12") } // See https://github.com/melix/jmh-gradle-plugin diff --git a/bom/build.gradle.kts b/bom/build.gradle.kts deleted file mode 100644 index 1d1eb78869..0000000000 --- a/bom/build.gradle.kts +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2019, PostgreSQL Global Development Group - * See the LICENSE file in the project root for more information. - */ - -plugins { - `java-platform` -} - -val String.v: String get() = rootProject.extra["$this.version"] as String - -// Note: Gradle allows to declare dependency on "bom" as "api", -// and it makes the contraints to be transitively visible -// However Maven can't express that, so the approach is to use Gradle resolution -// and generate pom files with resolved versions -// See https://github.com/gradle/gradle/issues/9866 - -fun DependencyConstraintHandlerScope.apiv( - notation: String, - versionProp: String = notation.substringAfterLast(':') -) = - "api"(notation + ":" + versionProp.v) - -fun DependencyConstraintHandlerScope.runtimev( - notation: String, - versionProp: String = notation.substringAfterLast(':') -) = - "runtime"(notation + ":" + versionProp.v) - -dependencies { - // Parenthesis are needed here: https://github.com/gradle/gradle/issues/9248 - (constraints) { - // api means "the dependency is for both compilation and runtime" - // runtime means "the dependency is only for runtime, not for compilation" - // In other words, marking dependency as "runtime" would avoid accidental - // dependency on it during compilation - apiv("ch.qos.logback:logback-classic", "logback") - apiv("ch.qos.logback:logback-core", "logback") - apiv("com.github.waffle:waffle-jna") - apiv("com.ongres.scram:client", "com.ongres.scram.client") - apiv("javax:javaee-api") - apiv("junit:junit", "junit4") - apiv("org.apache.felix:org.apache.felix.framework") - apiv("org.checkerframework:checker-qual", "checkerframework") - apiv("org.hamcrest:hamcrest") - apiv("org.hamcrest:hamcrest-core", "hamcrest") - apiv("org.hamcrest:hamcrest-library", "hamcrest") - apiv("org.junit.jupiter:junit-jupiter-api", "junit5") - apiv("uk.org.webcompere:system-stubs-jupiter", "junit5-system-stubs-jupiter") - apiv("org.junit.jupiter:junit-jupiter-params", "junit5") - apiv("org.openjdk.jmh:jmh-generator-annprocess", "jmh") - apiv("org.ops4j.pax.exam:pax-exam-container-native", "org.ops4j.pax.exam") - apiv("org.ops4j.pax.exam:pax-exam-junit4", "org.ops4j.pax.exam") - apiv("org.ops4j.pax.exam:pax-exam-junit4", "org.ops4j.pax.exam") - apiv("org.ops4j.pax.exam:pax-exam-link-mvn", "org.ops4j.pax.exam") - apiv("org.ops4j.pax.url:pax-url-aether") - apiv("org.osgi:org.osgi.core") - apiv("org.osgi:org.osgi.service.jdbc") - apiv("org.slf4j:slf4j-api", "slf4j") - apiv("org.slf4j:slf4j-log4j12", "slf4j") - apiv("se.jiderhamn:classloader-leak-test-framework") - runtimev("org.junit.jupiter:junit-jupiter-engine", "junit5") - runtimev("org.junit.vintage:junit-vintage-engine", "junit5") - runtimev("org.openjdk.jmh:jmh-core", "jmh") - } -} diff --git a/build-logic-commons/.gitignore b/build-logic-commons/.gitignore new file mode 100644 index 0000000000..de8b137467 --- /dev/null +++ b/build-logic-commons/.gitignore @@ -0,0 +1 @@ +/*/build/ diff --git a/build-logic-commons/gradle-plugin/build.gradle.kts b/build-logic-commons/gradle-plugin/build.gradle.kts new file mode 100644 index 0000000000..5e8128c55f --- /dev/null +++ b/build-logic-commons/gradle-plugin/build.gradle.kts @@ -0,0 +1,24 @@ +import org.gradle.kotlin.dsl.support.expectedKotlinDslPluginsVersion + +plugins { + `kotlin-dsl` +} + +group = "org.postgresql.build-logic" + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +dependencies { + // We use precompiled script plugins (== plugins written as src/kotlin/build-logic.*.gradle.kts files, + // and we need to declare dependency on org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin + // to make it work. + // See https://github.com/gradle/gradle/issues/17016 regarding expectedKotlinDslPluginsVersion + implementation("org.gradle.kotlin.kotlin-dsl:org.gradle.kotlin.kotlin-dsl.gradle.plugin:$expectedKotlinDslPluginsVersion") +} + +kotlinDslPluginOptions { + jvmTarget.set("1.8") +} diff --git a/build-logic-commons/gradle-plugin/src/main/kotlin/build-logic.kotlin-dsl-gradle-plugin.gradle.kts b/build-logic-commons/gradle-plugin/src/main/kotlin/build-logic.kotlin-dsl-gradle-plugin.gradle.kts new file mode 100644 index 0000000000..a1e75096e7 --- /dev/null +++ b/build-logic-commons/gradle-plugin/src/main/kotlin/build-logic.kotlin-dsl-gradle-plugin.gradle.kts @@ -0,0 +1,18 @@ +plugins { + id("java-library") + id("org.gradle.kotlin.kotlin-dsl") // this is 'kotlin-dsl' without version +} + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +tasks.validatePlugins { + failOnWarning.set(true) + enableStricterValidation.set(true) +} + +kotlinDslPluginOptions { + jvmTarget.set("1.8") +} diff --git a/build-logic-commons/settings.gradle.kts b/build-logic-commons/settings.gradle.kts new file mode 100644 index 0000000000..e32971bde7 --- /dev/null +++ b/build-logic-commons/settings.gradle.kts @@ -0,0 +1,9 @@ +dependencyResolutionManagement { + repositories { + gradlePluginPortal() + } +} + +rootProject.name = "build-logic-commons" + +include("gradle-plugin") diff --git a/build-logic/.gitignore b/build-logic/.gitignore new file mode 100644 index 0000000000..de8b137467 --- /dev/null +++ b/build-logic/.gitignore @@ -0,0 +1 @@ +/*/build/ diff --git a/build-logic/README.md b/build-logic/README.md new file mode 100644 index 0000000000..ca1a27b6d7 --- /dev/null +++ b/build-logic/README.md @@ -0,0 +1,27 @@ +# Build logic for pgjdbc + +This is a subset of extra plugins for factoring out +the common patterns from the common build logic. + +The recommended approach is to use build composition, so every build script +should list all its prerequisites in the top-most `plugins { ... }` block. + +The use of `allprojects` and `subprojects` is an anti-pattern as it makes it hard to identify +the configuration for a given project. + +Let us consider an example (see `/pgjdbc/build.gradle.kts`): + +```kotlin +plugins { + id("build-logic.java-published-library") + id("build-logic.test-junit5") +} + +... +``` + +It means that we deal with a Java library that will be published to Central, +and which uses JUnit 5 for testing. + +If you want to see what the logic does, you could open `build-logic.java-published-library.gradle.kts` +and `buildlogic.test-junit5.gradle.kts`. diff --git a/build-logic/basics/build.gradle.kts b/build-logic/basics/build.gradle.kts new file mode 100644 index 0000000000..16006a2903 --- /dev/null +++ b/build-logic/basics/build.gradle.kts @@ -0,0 +1,7 @@ +plugins { + id("build-logic.kotlin-dsl-gradle-plugin") +} + +dependencies { + implementation(project(":build-parameters")) +} diff --git a/build-logic/basics/src/main/kotlin/ToolchainProperties.kt b/build-logic/basics/src/main/kotlin/ToolchainProperties.kt new file mode 100644 index 0000000000..ecba65b92d --- /dev/null +++ b/build-logic/basics/src/main/kotlin/ToolchainProperties.kt @@ -0,0 +1,23 @@ +import buildparameters.BuildParametersExtension +import org.gradle.api.JavaVersion + +class ToolchainProperties( + val version: Int, + val vendor: String?, + val implementation: String?, +) + +val BuildParametersExtension.buildJdk: ToolchainProperties? + get() = jdkBuildVersion.takeIf { it != 0 } + ?.let { ToolchainProperties(it, jdkBuildVendor.orNull, jdkBuildImplementation.orNull) } + +val BuildParametersExtension.buildJdkVersion: Int + get() = buildJdk?.version ?: JavaVersion.current().majorVersion.toInt() + +val BuildParametersExtension.testJdk: ToolchainProperties? + get() = jdkTestVersion.orNull?.takeIf { it != 0 } + ?.let { ToolchainProperties(it, jdkTestVendor.orNull, jdkTestImplementation.orNull) } + ?: buildJdk + +val BuildParametersExtension.testJdkVersion: Int + get() = jdkTestVersion.orNull ?: buildJdkVersion diff --git a/build-logic/basics/src/main/kotlin/build-logic.repositories.gradle.kts b/build-logic/basics/src/main/kotlin/build-logic.repositories.gradle.kts new file mode 100644 index 0000000000..b0e41864ff --- /dev/null +++ b/build-logic/basics/src/main/kotlin/build-logic.repositories.gradle.kts @@ -0,0 +1,10 @@ +plugins { + id("build-logic.build-params") +} + +repositories { + if (buildParameters.enableMavenLocal) { + mavenLocal() + } + mavenCentral() +} diff --git a/build-logic/basics/src/main/kotlin/build-logic.reproducible-builds.gradle.kts b/build-logic/basics/src/main/kotlin/build-logic.reproducible-builds.gradle.kts new file mode 100644 index 0000000000..cab1514064 --- /dev/null +++ b/build-logic/basics/src/main/kotlin/build-logic.reproducible-builds.gradle.kts @@ -0,0 +1,7 @@ +tasks.withType().configureEach { + // Ensure builds are reproducible + isPreserveFileTimestamps = false + isReproducibleFileOrder = true + dirMode = "775".toInt(8) + fileMode = "664".toInt(8) +} diff --git a/build-logic/basics/src/main/kotlin/build-logic.toolchains.gradle.kts b/build-logic/basics/src/main/kotlin/build-logic.toolchains.gradle.kts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/build-logic/basics/src/main/kotlin/configureToolchain.kt b/build-logic/basics/src/main/kotlin/configureToolchain.kt new file mode 100644 index 0000000000..c0c533fdcd --- /dev/null +++ b/build-logic/basics/src/main/kotlin/configureToolchain.kt @@ -0,0 +1,24 @@ +import org.gradle.api.provider.Provider +import org.gradle.jvm.toolchain.JavaLanguageVersion +import org.gradle.jvm.toolchain.JavaLauncher +import org.gradle.jvm.toolchain.JavaToolchainService +import org.gradle.jvm.toolchain.JavaToolchainSpec +import org.gradle.jvm.toolchain.JvmImplementation +import org.gradle.jvm.toolchain.JvmVendorSpec + +fun JavaToolchainService.launcherFor(jdk: ToolchainProperties): Provider = launcherFor { + configureToolchain(jdk) +} + +fun JavaToolchainSpec.configureToolchain(jdk: ToolchainProperties?) { + if (jdk == null) { + return + } + languageVersion.set(JavaLanguageVersion.of(jdk.version)) + jdk.vendor?.let { + vendor.set(JvmVendorSpec.matching(it)) + } + if (jdk.implementation.equals("J9", ignoreCase = true)) { + implementation.set(JvmImplementation.J9) + } +} diff --git a/build-logic/build-parameters/build.gradle.kts b/build-logic/build-parameters/build.gradle.kts new file mode 100644 index 0000000000..0a7ec38ea1 --- /dev/null +++ b/build-logic/build-parameters/build.gradle.kts @@ -0,0 +1,95 @@ +plugins { + id("org.gradlex.build-parameters") version "1.4.3" + id("com.github.vlsi.gradle-extensions") version "1.90" + id("build-logic.kotlin-dsl-gradle-plugin") +} + +buildParameters { + // Other plugins can contribute parameters, so below list is not exhaustive + enableValidation.set(false) + pluginId("build-logic.build-params") + bool("enableMavenLocal") { + defaultValue.set(true) + description.set("Add mavenLocal() to repositories") + } + bool("coverage") { + defaultValue.set(false) + description.set("Collect test coverage") + } + integer("targetJavaVersion") { + defaultValue.set(8) + mandatory.set(true) + description.set("Java version for source and target compatibility") + } + val projectName = "pgjdbc" + integer("jdkBuildVersion") { + defaultValue.set(17) + mandatory.set(true) + description.set("JDK version to use for building $projectName. If the value is 0, then the current Java is used. (see https://docs.gradle.org/8.4/userguide/toolchains.html#sec:consuming)") + } + string("jdkBuildVendor") { + description.set("JDK vendor to use building $projectName (see https://docs.gradle.org/8.4/userguide/toolchains.html#sec:vendors)") + } + string("jdkBuildImplementation") { + description.set("Vendor-specific virtual machine implementation to use building $projectName (see https://docs.gradle.org/8.4/userguide/toolchains.html#selecting_toolchains_by_virtual_machine_implementation)") + } + integer("jdkTestVersion") { + description.set("JDK version to use for testing $projectName. If the value is 0, then the current Java is used. (see https://docs.gradle.org/current/userguide/toolchains.html#sec:vendors)") + } + string("jdkTestVendor") { + description.set("JDK vendor to use testing $projectName (see https://docs.gradle.org/8.4/userguide/toolchains.html#sec:vendors)") + } + string("jdkTestImplementation") { + description.set("Vendor-specific virtual machine implementation to use testing $projectName (see https://docs.gradle.org/8.4/userguide/toolchains.html#selecting_toolchains_by_virtual_machine_implementation)") + } + bool("spotbugs") { + defaultValue.set(false) + description.set("Run SpotBugs verifications") + } + bool("enableCheckerframework") { + defaultValue.set(false) + description.set("Run CheckerFramework (nullness) verifications") + } + bool("enableErrorprone") { + defaultValue.set(false) + description.set("Run ErrorProne verifications") + } + bool("skipCheckstyle") { + defaultValue.set(false) + description.set("Skip Checkstyle verifications") + } + bool("skipAutostyle") { + defaultValue.set(false) + description.set("Skip AutoStyle verifications") + } + bool("skipOpenrewrite") { + // For now, we skip OpenRewrite since the -SNAPSHOT version is not very stable + defaultValue.set(true) + description.set("Skip OpenRewrite processing") + } + bool("skipForbiddenApis") { + defaultValue.set(false) + description.set("Skip forbidden-apis verifications") + } + bool("skipJavadoc") { + defaultValue.set(false) + description.set("Skip javadoc generation") + } + bool("failOnJavadocWarning") { + defaultValue.set(true) + description.set("Fail build on javadoc warnings") + } + bool("enableGradleMetadata") { + defaultValue.set(false) + description.set("Generate and publish Gradle Module Metadata") + } + // Note: it does not work in tr_TR locale due to https://github.com/gradlex-org/build-parameters/issues/87 + string("includeTestTags") { + defaultValue.set("") + description.set("Lists tags to include in test execution. For instance -PincludeTestTags=!org.postgresql.test.SlowTests, or or -PincludeTestTags=!replication") + } + bool("useGpgCmd") { + defaultValue.set(false) + description.set("By default use Java implementation to sign artifacts. When useGpgCmd=true, then gpg command line tool is used for signing artifacts") + } +} \ No newline at end of file diff --git a/build-logic/build.gradle.kts b/build-logic/build.gradle.kts new file mode 100644 index 0000000000..6fc4b5012c --- /dev/null +++ b/build-logic/build.gradle.kts @@ -0,0 +1,3 @@ +plugins { + `embedded-kotlin` apply false +} diff --git a/build-logic/java-comment-preprocessor/build.gradle.kts b/build-logic/java-comment-preprocessor/build.gradle.kts new file mode 100644 index 0000000000..d5547dc857 --- /dev/null +++ b/build-logic/java-comment-preprocessor/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + id("build-logic.kotlin-dsl-gradle-plugin") +} + +dependencies { + implementation("com.igormaznitsa:jcp:7.0.2") + implementation("com.github.vlsi.gradle-extensions:com.github.vlsi.gradle-extensions.gradle.plugin:1.86") +} diff --git a/build-logic/java-comment-preprocessor/src/main/kotlin/build-logic.java-comment-preprocessor.gradle.kts b/build-logic/java-comment-preprocessor/src/main/kotlin/build-logic.java-comment-preprocessor.gradle.kts new file mode 100644 index 0000000000..d63eaf22be --- /dev/null +++ b/build-logic/java-comment-preprocessor/src/main/kotlin/build-logic.java-comment-preprocessor.gradle.kts @@ -0,0 +1,27 @@ +import com.github.vlsi.gradle.dsl.configureEach +import com.github.vlsi.gradle.properties.dsl.props + +plugins { + id("com.github.vlsi.gradle-extensions") +} + +tasks.configureEach { + variables.apply { + val jdbcSpec = props.string("jdbc.specification.version") + put("mvn.project.property.postgresql.jdbc.spec", "JDBC$jdbcSpec") + put("jdbc.specification.version", jdbcSpec) + } + + val re = Regex("^(\\d+)\\.(\\d+)(?:\\.(\\d+))?.*") + + val version = project.version.toString() + val matchResult = re.find(version) ?: throw GradleException("Unable to parse major.minor.patch version parts from project.version '$version'") + val (major, minor, patch) = matchResult.destructured + + variables.apply { + put("version", version) + put("version.major", major) + put("version.minor", minor) + put("version.patch", patch.ifBlank { "0" }) + } +} diff --git a/buildSrc/src/main/kotlin/org/postgresql/buildtools/JavaCommentPreprocessorTask.kt b/build-logic/java-comment-preprocessor/src/main/kotlin/buildlogic/JavaCommentPreprocessorTask.kt similarity index 98% rename from buildSrc/src/main/kotlin/org/postgresql/buildtools/JavaCommentPreprocessorTask.kt rename to build-logic/java-comment-preprocessor/src/main/kotlin/buildlogic/JavaCommentPreprocessorTask.kt index 41d093fcb5..22cbb1e008 100644 --- a/buildSrc/src/main/kotlin/org/postgresql/buildtools/JavaCommentPreprocessorTask.kt +++ b/build-logic/java-comment-preprocessor/src/main/kotlin/buildlogic/JavaCommentPreprocessorTask.kt @@ -3,7 +3,7 @@ * See the LICENSE file in the project root for more information. */ -package org.postgresql.buildtools +package buildlogic import com.igormaznitsa.jcp.JcpPreprocessor import com.igormaznitsa.jcp.context.PreprocessorContext diff --git a/build-logic/jvm/build.gradle.kts b/build-logic/jvm/build.gradle.kts new file mode 100644 index 0000000000..777a64eb42 --- /dev/null +++ b/build-logic/jvm/build.gradle.kts @@ -0,0 +1,17 @@ +plugins { + id("build-logic.kotlin-dsl-gradle-plugin") +} + +dependencies { + implementation(project(":build-parameters")) + implementation(project(":verification")) + implementation("com.github.vlsi.crlf:com.github.vlsi.crlf.gradle.plugin:1.86") + implementation("com.github.vlsi.gradle-extensions:com.github.vlsi.gradle-extensions.gradle.plugin:1.86") + implementation("org.jetbrains.kotlin:kotlin-gradle-plugin") + implementation("com.igormaznitsa:jcp:7.0.2") + implementation("org.jetbrains.dokka:org.jetbrains.dokka.gradle.plugin:$embeddedKotlinVersion") + implementation("com.github.autostyle:com.github.autostyle.gradle.plugin:3.2") + implementation("net.ltgt.errorprone:net.ltgt.errorprone.gradle.plugin:3.0.1") + implementation("com.github.vlsi.jandex:com.github.vlsi.jandex.gradle.plugin:1.86") + implementation("de.thetaphi.forbiddenapis:de.thetaphi.forbiddenapis.gradle.plugin:3.3") +} diff --git a/build-logic/jvm/src/main/kotlin/build-logic.dokka-javadoc.gradle.kts b/build-logic/jvm/src/main/kotlin/build-logic.dokka-javadoc.gradle.kts new file mode 100644 index 0000000000..ade4db86ad --- /dev/null +++ b/build-logic/jvm/src/main/kotlin/build-logic.dokka-javadoc.gradle.kts @@ -0,0 +1,18 @@ +plugins { + id("java-base") + id("org.jetbrains.dokka") +} + +java { + // Workaround https://github.com/gradle/gradle/issues/21933, so it adds javadocElements configuration + withJavadocJar() +} + +val dokkaJar by tasks.registering(Jar::class) { + group = LifecycleBasePlugin.BUILD_GROUP + description = "Assembles a jar archive containing javadoc" + from(tasks.dokkaJavadoc) + archiveClassifier.set("javadoc") +} + +configurations[JavaPlugin.JAVADOC_ELEMENTS_CONFIGURATION_NAME].outgoing.artifact(dokkaJar) diff --git a/build-logic/jvm/src/main/kotlin/build-logic.java-library.gradle.kts b/build-logic/jvm/src/main/kotlin/build-logic.java-library.gradle.kts new file mode 100644 index 0000000000..5760723959 --- /dev/null +++ b/build-logic/jvm/src/main/kotlin/build-logic.java-library.gradle.kts @@ -0,0 +1,4 @@ +plugins { + id("build-logic.java") + id("java-library") +} diff --git a/build-logic/jvm/src/main/kotlin/build-logic.java.gradle.kts b/build-logic/jvm/src/main/kotlin/build-logic.java.gradle.kts new file mode 100644 index 0000000000..901e91de02 --- /dev/null +++ b/build-logic/jvm/src/main/kotlin/build-logic.java.gradle.kts @@ -0,0 +1,126 @@ +import com.github.vlsi.gradle.crlf.CrLfSpec +import com.github.vlsi.gradle.crlf.LineEndings +import com.github.vlsi.gradle.dsl.configureEach +import com.github.vlsi.gradle.properties.dsl.props +import java.time.LocalDate + +plugins { + id("java") + id("com.github.vlsi.crlf") + id("com.github.vlsi.gradle-extensions") + id("build-logic.repositories") + id("build-logic.test-base") + id("build-logic.build-params") + id("build-logic.style") + id("com.github.vlsi.jandex") +} + +java { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +sourceSets { + main { + resources { + // TODO: remove when LICENSE is removed (it is used by Maven build for now) + exclude("META-INF/LICENSE") + } + } +} + +project.configure { + skipIndexFileGeneration() +} + +if (!buildParameters.enableGradleMetadata) { + tasks.configureEach { + enabled = false + } +} + +if (buildParameters.coverage || gradle.startParameter.taskNames.any { it.contains("jacoco") }) { + apply(plugin = "build-logic.jacoco") +} + +tasks.configureEach { + inputs.property("java.version", System.getProperty("java.version")) + inputs.property("java.vm.version", System.getProperty("java.vm.version")) + options.apply { + encoding = "UTF-8" + compilerArgs.add("-Xlint:deprecation") + if (JavaVersion.current().isJava9Compatible) { + // See https://bugs.openjdk.org/browse/JDK-8032211 + // Don't issue deprecation warnings on import statements is resolved in Java 9+ + //compilerArgs.add("-Werror") + } + } +} + +tasks.configureEach { + (options as StandardJavadocDocletOptions).apply { + // Please refrain from using non-ASCII chars below since the options are passed as + // javadoc.options file which is parsed with "default encoding" + noTimestamp.value = true + showFromProtected() + if (buildParameters.failOnJavadocWarning) { + // See JDK-8200363 (https://bugs.openjdk.java.net/browse/JDK-8200363) + // for information about the -Xwerror option. + addBooleanOption("Xwerror", true) + } + // There are too many missing javadocs, so failing the build on missing comments seems to be not an option + addBooleanOption("Xdoclint:all,-missing", true) + // javadoc: error - The code being documented uses modules but the packages + // defined in https://docs.oracle.com/javase/9/docs/api/ are in the unnamed module + source = "1.8" + docEncoding = "UTF-8" + charSet = "UTF-8" + encoding = "UTF-8" + docTitle = "PostgreSQL JDBC ${project.name} API version ${project.version}" + windowTitle = "PostgreSQL JDBC ${project.name} API version ${project.version}" + header = "PostgreSQL JDBC" + val lastEditYear = providers.gradleProperty("lastEditYear") + .getOrElse(LocalDate.now().year.toString()) + bottom = + "Copyright © 1997-$lastEditYear PostgreSQL Global Development Group. All Rights Reserved." + if (JavaVersion.current() >= JavaVersion.VERSION_17) { + addBooleanOption("html5", true) + } else if (JavaVersion.current() >= JavaVersion.VERSION_1_9) { + addBooleanOption("html5", true) + links("https://docs.oracle.com/javase/9/docs/api/") + } else { + links("https://docs.oracle.com/javase/8/docs/api/") + } + } +} + +// Add default license/notice when missing (e.g. see :src:config that overrides LICENSE) + +afterEvaluate { + tasks.configureEach { + CrLfSpec(LineEndings.LF).run { + into("META-INF") { + filteringCharset = "UTF-8" + duplicatesStrategy = DuplicatesStrategy.EXCLUDE + from("$rootDir/LICENSE") + from("$rootDir/NOTICE") + } + } + } +} + +tasks.configureEach { + manifest { + attributes["Bundle-License"] = "BSD-2-Clause" + attributes["Implementation-Title"] = "PostgreSQL JDBC Driver" + attributes["Implementation-Version"] = project.version + val jdbcSpec = props.string("jdbc.specification.version") + if (jdbcSpec.isNotBlank()) { + attributes["Specification-Vendor"] = "Oracle Corporation" + attributes["Specification-Version"] = jdbcSpec + attributes["Specification-Title"] = "JDBC" + } + attributes["Implementation-Vendor"] = "PostgreSQL Global Development Group" + attributes["Implementation-Vendor-Id"] = "org.postgresql" + } +} diff --git a/build-logic/jvm/src/main/kotlin/build-logic.kotlin.gradle.kts b/build-logic/jvm/src/main/kotlin/build-logic.kotlin.gradle.kts new file mode 100644 index 0000000000..63f50a8308 --- /dev/null +++ b/build-logic/jvm/src/main/kotlin/build-logic.kotlin.gradle.kts @@ -0,0 +1,30 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + id("java-library") + id("build-logic.java") + id("build-logic.test-base") + id("com.github.vlsi.gradle-extensions") + id("com.github.autostyle") + kotlin("jvm") +} + +java { + withSourcesJar() +} + +autostyle { + kotlin { + file("$rootDir/config/licenseHeaderRaw").takeIf { it.exists() }?.let { + licenseHeader(it.readText()) + } + trimTrailingWhitespace() + endWithNewline() + } +} + +tasks.withType().configureEach { + kotlinOptions { + jvmTarget = "11" + } +} diff --git a/build-logic/jvm/src/main/kotlin/build-logic.test-base.gradle.kts b/build-logic/jvm/src/main/kotlin/build-logic.test-base.gradle.kts new file mode 100644 index 0000000000..2c1ba0698d --- /dev/null +++ b/build-logic/jvm/src/main/kotlin/build-logic.test-base.gradle.kts @@ -0,0 +1,44 @@ +import com.github.vlsi.gradle.dsl.configureEach +import org.gradle.api.tasks.testing.Test + +plugins { + id("build-logic.build-params") +} + +tasks.configureEach { + inputs.file("../build.properties") + if (file("../build.local.properties").exists()) { + inputs.file("../build.local.properties") + } + inputs.file("../ssltest.properties") + if (file("../ssltest.local.properties").exists()) { + inputs.file("../ssltest.local.properties") + } + testLogging { + showStandardStreams = true + } + exclude("**/*Suite*") + jvmArgs("-Xmx1536m") + jvmArgs("-Djdk.net.URLClassPath.disableClassPathURLCheck=true") + // Pass the property to tests + fun passProperty(name: String, default: String? = null) { + val value = System.getProperty(name) ?: default + value?.let { systemProperty(name, it) } + } + passProperty("preferQueryMode") + passProperty("java.awt.headless") + passProperty("user.language", "TR") + passProperty("user.country", "tr") + val props = System.getProperties() + @Suppress("UNCHECKED_CAST") + for (e in props.propertyNames() as `java.util`.Enumeration) { + if (e.startsWith("pgjdbc.") || e.startsWith("java")) { + passProperty(e) + } + } + for (p in listOf("server", "port", "database", "username", "password", + "privilegedUser", "privilegedPassword", + "simpleProtocolOnly", "enable_ssl_tests")) { + passProperty(p) + } +} diff --git a/build-logic/jvm/src/main/kotlin/build-logic.test-junit5.gradle.kts b/build-logic/jvm/src/main/kotlin/build-logic.test-junit5.gradle.kts new file mode 100644 index 0000000000..12993f35bd --- /dev/null +++ b/build-logic/jvm/src/main/kotlin/build-logic.test-junit5.gradle.kts @@ -0,0 +1,39 @@ +import com.github.vlsi.gradle.dsl.configureEach +import com.github.vlsi.gradle.properties.dsl.props + +plugins { + id("java-library") + id("build-logic.build-params") + id("build-logic.test-base") +} + +dependencies { + testImplementation("org.junit.jupiter:junit-jupiter-api:5.8.2") + testImplementation("uk.org.webcompere:system-stubs-jupiter:2.0.1") + testImplementation("org.junit.jupiter:junit-jupiter-params:5.8.2") + testImplementation("org.hamcrest:hamcrest:2.2") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.8.2") + if ((project.findProperty("junit4") ?: "true").toString().toBoolean()) { + // Allow projects to opt-out of junit dependency, so they can be JUnit5-only + testImplementation("junit:junit:4.13.2") + testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.8.2") + } +} + +tasks.configureEach { + useJUnitPlatform { + props.string("includeTestTags") + .takeIf { it.isNotBlank() } + ?.let { includeTags(it) } + } + // Pass the property to tests + fun passProperty(name: String, default: String? = null) { + val value = System.getProperty(name) ?: default + value?.let { systemProperty(name, it) } + } + passProperty("junit.jupiter.execution.parallel.enabled", "true") + // TODO: remove when upgrade to JUnit 5.9+ + // See https://github.com/junit-team/junit5/commit/347e3119d36a5c226cddd7981452f11335fad422 + passProperty("junit.jupiter.execution.parallel.config.strategy", "DYNAMIC") + passProperty("junit.jupiter.execution.timeout.default", "5 m") +} diff --git a/build-logic/publishing/build.gradle.kts b/build-logic/publishing/build.gradle.kts new file mode 100644 index 0000000000..f3a47fd011 --- /dev/null +++ b/build-logic/publishing/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + id("build-logic.kotlin-dsl-gradle-plugin") +} + +dependencies { + implementation(project(":basics")) + implementation(project(":jvm")) + implementation(project(":build-parameters")) + implementation("com.gradle.plugin-publish:com.gradle.plugin-publish.gradle.plugin:1.1.0") + implementation("com.github.vlsi.gradle-extensions:com.github.vlsi.gradle-extensions.gradle.plugin:1.86") + implementation("com.github.vlsi.stage-vote-release:com.github.vlsi.stage-vote-release.gradle.plugin:1.86") +} diff --git a/build-logic/publishing/src/main/kotlin/build-logic.java-published-library.gradle.kts b/build-logic/publishing/src/main/kotlin/build-logic.java-published-library.gradle.kts new file mode 100644 index 0000000000..0d6e4f7036 --- /dev/null +++ b/build-logic/publishing/src/main/kotlin/build-logic.java-published-library.gradle.kts @@ -0,0 +1,31 @@ +import com.github.vlsi.gradle.publishing.dsl.versionFromResolution + +plugins { + id("build-logic.build-params") + id("build-logic.java-library") + id("build-logic.reproducible-builds") + id("build-logic.publish-to-central") +} + +java { + withSourcesJar() + if (!buildParameters.skipJavadoc) { + withJavadocJar() + } +} + +publishing { + publications { + create(project.name) { + from(components["java"]) + + // Gradle feature variants can't be mapped to Maven's pom + suppressAllPomMetadataWarnings() + + // Use the resolved versions in pom.xml + // Gradle might have different resolution rules, so we set the versions + // that were used in Gradle build/test. + versionFromResolution() + } + } +} diff --git a/build-logic/publishing/src/main/kotlin/build-logic.publish-to-central.gradle.kts b/build-logic/publishing/src/main/kotlin/build-logic.publish-to-central.gradle.kts new file mode 100644 index 0000000000..1f980969a2 --- /dev/null +++ b/build-logic/publishing/src/main/kotlin/build-logic.publish-to-central.gradle.kts @@ -0,0 +1,137 @@ +import com.github.vlsi.gradle.dsl.configureEach +import org.gradle.api.publish.internal.PublicationInternal +import com.github.vlsi.gradle.publishing.dsl.simplifyXml + +plugins { + id("java-library") + id("maven-publish") + id("build-logic.publish-to-tmp-maven-repo") + id("com.github.vlsi.gradle-extensions") +} + +publishing { + publications { + // + val extraMavenPublications by configurations.creating { + isVisible = false + isCanBeResolved = false + isCanBeConsumed = false + } + afterEvaluate { + named(project.name) { + extraMavenPublications.outgoing.artifacts.apply { + val keys = mapTo(HashSet()) { + it.classifier.orEmpty() to it.extension + } + artifacts.removeIf { + keys.contains(it.classifier.orEmpty() to it.extension) + } + forEach { artifact(it) } + } + } + } + // + } + publications.configureEach { + // Use the resolved versions in pom.xml + // Gradle might have different resolution rules, so we set the versions + // that were used in Gradle build/test. + versionMapping { + usage(Usage.JAVA_RUNTIME) { + fromResolutionResult() + } + usage(Usage.JAVA_API) { + fromResolutionOf("runtimeClasspath") + } + } + pom { + simplifyXml() + name.set( + (project.findProperty("artifact.name") as? String) ?: "pgdjbc ${project.name.capitalize()}" + ) + description.set(project.description ?: "PostgreSQL JDBC Driver ${project.name.capitalize()}") + inceptionYear.set("1997") + url.set("https://jdbc.postgresql.org") + licenses { + license { + name.set("BSD-2-Clause") + url.set("https://jdbc.postgresql.org/about/license.html") + comments.set("BSD-2-Clause, copyright PostgreSQL Global Development Group") + distribution.set("repo") + } + } + organization { + name.set("PostgreSQL Global Development Group") + url.set("https://jdbc.postgresql.org/") + } + developers { + developer { + id.set("davecramer") + name.set("Dave Cramer") + } + developer { + id.set("jurka") + name.set("Kris Jurka") + } + developer { + id.set("oliver") + name.set("Oliver Jowett") + } + developer { + id.set("ringerc") + name.set("Craig Ringer") + } + developer { + id.set("vlsi") + name.set("Vladimir Sitnikov") + } + developer { + id.set("bokken") + name.set("Brett Okken") + } + } + issueManagement { + system.set("GitHub issues") + url.set("https://github.com/pgjdbc/pgjdbc/issues") + } + mailingLists { + mailingList { + name.set("PostgreSQL JDBC development list") + subscribe.set("https://lists.postgresql.org/") + unsubscribe.set("https://lists.postgresql.org/unsubscribe/") + post.set("pgsql-jdbc@postgresql.org") + archive.set("https://www.postgresql.org/list/pgsql-jdbc/") + } + } + scm { + connection.set("scm:git:https://github.com/pgjdbc/pgjdbc.git") + developerConnection.set("scm:git:https://github.com/pgjdbc/pgjdbc.git") + url.set("https://github.com/pgjdbc/pgjdbc") + tag.set("HEAD") + } + } + } +} + +val createReleaseBundle by tasks.registering(Sync::class) { + description = "This task should be used by github actions to create release artifacts along with a slsa attestation" + val releaseDir = layout.buildDirectory.dir("release") + outputs.dir(releaseDir) + + into(releaseDir) + rename("pom-default.xml", "${project.name}-${project.version}.pom") + rename("module.json", "${project.name}-${project.version}.module") +} + +publishing { + publications.configureEach { + (this as PublicationInternal<*>).allPublishableArtifacts { + val publicationArtifact = this + createReleaseBundle.configure { + dependsOn(publicationArtifact) + from(publicationArtifact.file) + } + } + } +} + diff --git a/build-logic/publishing/src/main/kotlin/build-logic.publish-to-tmp-maven-repo.gradle.kts b/build-logic/publishing/src/main/kotlin/build-logic.publish-to-tmp-maven-repo.gradle.kts new file mode 100644 index 0000000000..673770849a --- /dev/null +++ b/build-logic/publishing/src/main/kotlin/build-logic.publish-to-tmp-maven-repo.gradle.kts @@ -0,0 +1,41 @@ +plugins { + id("java-library") + id("maven-publish") +} + +val localRepoElements by configurations.creating { + isCanBeConsumed = true + isCanBeResolved = false + description = + "Shares local maven repository directory that contains the artifacts produced by the current project" + attributes { + attribute(Category.CATEGORY_ATTRIBUTE, objects.named("maven-repository")) + attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL)) + } +} + +val localRepoDir = layout.buildDirectory.dir("local-maven-repo") + +publishing { + repositories { + maven { + name = "tmp-maven" + url = uri(localRepoDir) + } + } +} + +localRepoElements.outgoing.artifact(localRepoDir) { + builtBy(tasks.named("publishAllPublicationsToTmp-mavenRepository")) +} + +val cleanLocalRepository by tasks.registering(Delete::class) { + description = "Clears local-maven-repo so timestamp-based snapshot artifacts do not consume space" + delete(localRepoDir) +} + +tasks.withType() + .matching { it.name.endsWith("PublicationToTmp-mavenRepository") } + .configureEach { + dependsOn(cleanLocalRepository) + } diff --git a/build-logic/root-build/build.gradle.kts b/build-logic/root-build/build.gradle.kts new file mode 100644 index 0000000000..6fbfddd737 --- /dev/null +++ b/build-logic/root-build/build.gradle.kts @@ -0,0 +1,3 @@ +plugins { + id("build-logic.kotlin-dsl-gradle-plugin") +} diff --git a/build-logic/root-build/src/main/kotlin/build-logic.root-build.gradle.kts b/build-logic/root-build/src/main/kotlin/build-logic.root-build.gradle.kts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/build-logic/settings.gradle.kts b/build-logic/settings.gradle.kts new file mode 100644 index 0000000000..34002de863 --- /dev/null +++ b/build-logic/settings.gradle.kts @@ -0,0 +1,16 @@ +dependencyResolutionManagement { + repositories { + gradlePluginPortal() + } +} + +rootProject.name = "build-logic" + +includeBuild("../build-logic-commons") +include("build-parameters") +include("basics") +include("jvm") +include("java-comment-preprocessor") +include("publishing") +include("root-build") +include("verification") diff --git a/build-logic/verification/build.gradle.kts b/build-logic/verification/build.gradle.kts new file mode 100644 index 0000000000..edb4eca0e6 --- /dev/null +++ b/build-logic/verification/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + id("build-logic.kotlin-dsl-gradle-plugin") +} + +dependencies { + implementation(project(":basics")) + implementation(project(":build-parameters")) + implementation("com.github.autostyle:com.github.autostyle.gradle.plugin:3.2") + implementation("com.github.spotbugs:com.github.spotbugs.gradle.plugin:5.0.13") + implementation("com.github.vlsi.gradle-extensions:com.github.vlsi.gradle-extensions.gradle.plugin:1.86") + implementation("de.thetaphi.forbiddenapis:de.thetaphi.forbiddenapis.gradle.plugin:3.3") + implementation("net.ltgt.errorprone:net.ltgt.errorprone.gradle.plugin:3.0.1") + implementation("org.checkerframework:org.checkerframework.gradle.plugin:0.6.23") +} diff --git a/build-logic/verification/src/main/kotlin/build-logic.autostyle.gradle.kts b/build-logic/verification/src/main/kotlin/build-logic.autostyle.gradle.kts new file mode 100644 index 0000000000..cace7540bc --- /dev/null +++ b/build-logic/verification/src/main/kotlin/build-logic.autostyle.gradle.kts @@ -0,0 +1,36 @@ +plugins { + id("com.github.autostyle") +} + +autostyle { + kotlinGradle { + ktlint() + trimTrailingWhitespace() + endWithNewline() + } + format("markdown") { + target("**/*.md") + endWithNewline() + } +} + +plugins.withId("java") { + autostyle { + java { + // targetExclude("**/test/java/*.java") + // TODO: implement license check (with copyright year) + // licenseHeaderFile(licenseHeaderFile) + importOrder( + "static ", + "org.postgresql.", + "", + "java.", + "javax." + ) + removeUnusedImports() + trimTrailingWhitespace() + indentWithSpaces(4) + endWithNewline() + } + } +} diff --git a/build-logic/verification/src/main/kotlin/build-logic.checkerframework.gradle.kts b/build-logic/verification/src/main/kotlin/build-logic.checkerframework.gradle.kts new file mode 100644 index 0000000000..cf100ba957 --- /dev/null +++ b/build-logic/verification/src/main/kotlin/build-logic.checkerframework.gradle.kts @@ -0,0 +1,45 @@ +import org.gradle.api.JavaVersion +import org.gradle.kotlin.dsl.dependencies + +plugins { + id("org.checkerframework") +} + +dependencies { + providers.gradleProperty("checkerframework.version") + .takeIf { it.isPresent } + ?.let { + val checkerframeworkVersion = it.get() + "checkerFramework"("org.checkerframework:checker:$checkerframeworkVersion") + if (JavaVersion.current() == JavaVersion.VERSION_1_8) { + // only needed for JDK 8 + "checkerFrameworkAnnotatedJDK"("org.checkerframework:jdk8:$checkerframeworkVersion") + } + } ?: run { + val checkerframeworkVersion = "3.30.0" + "checkerFramework"("org.checkerframework:checker:$checkerframeworkVersion") + if (JavaVersion.current() == JavaVersion.VERSION_1_8) { + // only needed for JDK 8 + "checkerFrameworkAnnotatedJDK"("org.checkerframework:jdk8:$checkerframeworkVersion") + } + } +} + +checkerFramework { + skipVersionCheck = true + excludeTests = true + // See https://checkerframework.org/manual/#introduction + checkers.add("org.checkerframework.checker.nullness.NullnessChecker") + checkers.add("org.checkerframework.checker.optional.OptionalChecker") + // checkers.add("org.checkerframework.checker.index.IndexChecker") + checkers.add("org.checkerframework.checker.regex.RegexChecker") + extraJavacArgs.add("-Astubs=" + + fileTree("$rootDir/config/checkerframework") { + include("*.astub") + }.asPath + ) + // Translation classes are autogenerated, and they + extraJavacArgs.add("-AskipDefs=^org\\.postgresql\\.translation\\.") + // The below produces too many warnings :( + // extraJavacArgs.add("-Alint=redundantNullComparison") +} diff --git a/build-logic/verification/src/main/kotlin/build-logic.checkstyle.gradle.kts b/build-logic/verification/src/main/kotlin/build-logic.checkstyle.gradle.kts new file mode 100644 index 0000000000..3213b92f13 --- /dev/null +++ b/build-logic/verification/src/main/kotlin/build-logic.checkstyle.gradle.kts @@ -0,0 +1,32 @@ +import org.gradle.api.plugins.quality.Checkstyle +import org.gradle.kotlin.dsl.withType +import java.io.File + +plugins { + id("checkstyle") +} + +checkstyle { + toolVersion = "9.3" + providers.gradleProperty("checkstyle.version") + .takeIf { it.isPresent } + ?.let { toolVersion = it.get() } + + // Current one is ~8.8 + // https://github.com/julianhyde/toolbox/issues/3 + isShowViolations = true + // TOOD: move to /config + val configDir = File(rootDir, "pgjdbc/src/main/checkstyle") + configDirectory.set(configDir) + configFile = configDir.resolve("checks.xml") +} + +val checkstyleTasks = tasks.withType() +checkstyleTasks.configureEach { + // Checkstyle 8.26 does not need classpath, see https://github.com/gradle/gradle/issues/14227 + classpath = files() +} + +tasks.register("checkstyleAll") { + dependsOn(checkstyleTasks) +} diff --git a/build-logic/verification/src/main/kotlin/build-logic.errorprone.gradle.kts b/build-logic/verification/src/main/kotlin/build-logic.errorprone.gradle.kts new file mode 100644 index 0000000000..662123ac3d --- /dev/null +++ b/build-logic/verification/src/main/kotlin/build-logic.errorprone.gradle.kts @@ -0,0 +1,33 @@ +import com.github.vlsi.gradle.dsl.configureEach +import net.ltgt.gradle.errorprone.errorprone +import org.gradle.kotlin.dsl.apply +import org.gradle.kotlin.dsl.dependencies + +plugins { + id("java") + id("build-logic.repositories") +} + +if (!project.hasProperty("skipErrorprone")) { + apply(plugin = "net.ltgt.errorprone") + + dependencies { + "errorprone"("com.google.errorprone:error_prone_core:2.18.0") + "annotationProcessor"("com.google.guava:guava-beta-checker:1.0") + } + + tasks.configureEach { + if ("Test" in name) { + // Ignore warnings in test code + options.errorprone.isEnabled.set(false) + } else { + options.compilerArgs.addAll(listOf("-Xmaxerrs", "10000", "-Xmaxwarns", "10000")) + options.errorprone { + disableWarningsInGeneratedCode.set(true) + enable( + "PackageLocation" + ) + } + } + } +} diff --git a/build-logic/verification/src/main/kotlin/build-logic.forbidden-apis.gradle.kts b/build-logic/verification/src/main/kotlin/build-logic.forbidden-apis.gradle.kts new file mode 100644 index 0000000000..f0d4573402 --- /dev/null +++ b/build-logic/verification/src/main/kotlin/build-logic.forbidden-apis.gradle.kts @@ -0,0 +1,24 @@ +import com.github.vlsi.gradle.dsl.configureEach +import de.thetaphi.forbiddenapis.gradle.CheckForbiddenApis + +plugins { + id("de.thetaphi.forbiddenapis") +} + +forbiddenApis { + failOnUnsupportedJava = false + signaturesFiles = files("$rootDir/config/forbidden-apis/forbidden-apis.txt") + bundledSignatures.addAll( + listOf( + // "jdk-deprecated", + "jdk-internal", + "jdk-non-portable" + // "jdk-system-out" + // "jdk-unsafe" + ) + ) +} + +tasks.configureEach { + exclude("**/org/postgresql/util/internal/Unsafe.class") +} diff --git a/build-logic/verification/src/main/kotlin/build-logic.jacoco.gradle.kts b/build-logic/verification/src/main/kotlin/build-logic.jacoco.gradle.kts new file mode 100644 index 0000000000..50028ac41a --- /dev/null +++ b/build-logic/verification/src/main/kotlin/build-logic.jacoco.gradle.kts @@ -0,0 +1,51 @@ +plugins { + id("java-base") + id("jacoco") +} + +jacoco { + toolVersion = "0.8.5" + providers.gradleProperty("jacoco.version") + .takeIf { it.isPresent } + ?.let { toolVersion = it.get() } +} + +val testTasks = tasks.withType() +val javaExecTasks = tasks.withType() + +// This configuration must be postponed since JacocoTaskExtension might be added inside +// configure block of a task (== before this code is run) +afterEvaluate { + for (t in arrayOf(testTasks, javaExecTasks)) { + t.configureEach { + extensions.findByType()?.apply { + // We want collect code coverage for org.postgresql classes only + includes?.add("org.postgresql.*") + } + } + } +} + +val jacocoReport by rootProject.tasks.existing(JacocoReport::class) +val mainCode = sourceSets["main"] + +// TODO: rework with provide-consume configurations +jacocoReport { + // Note: this creates a lazy collection + // Some projects might fail to create a file (e.g. no tests or no coverage), + // So we check for file existence. Otherwise, JacocoMerge would fail + val execFiles = + files(testTasks, javaExecTasks).filter { it.exists() && it.name.endsWith(".exec") } + executionData(execFiles) + additionalSourceDirs.from(mainCode.allJava.srcDirs) + sourceDirectories.from(mainCode.allSource.srcDirs) + classDirectories.from(mainCode.output) +} + +// TODO: check which reports do we need +//tasks.configureEach { +// reports { +// html.required.set(reportsForHumans()) +// xml.required.set(!reportsForHumans()) +// } +//} diff --git a/build-logic/verification/src/main/kotlin/build-logic.spotbugs.gradle.kts b/build-logic/verification/src/main/kotlin/build-logic.spotbugs.gradle.kts new file mode 100644 index 0000000000..758a4acd45 --- /dev/null +++ b/build-logic/verification/src/main/kotlin/build-logic.spotbugs.gradle.kts @@ -0,0 +1,41 @@ +import com.github.spotbugs.snom.Confidence +import com.github.spotbugs.snom.SpotBugsExtension +import com.github.spotbugs.snom.SpotBugsTask +import com.github.vlsi.gradle.dsl.configureEach +import org.gradle.language.base.plugins.LifecycleBasePlugin + +plugins { + id("com.github.spotbugs") +} + +spotbugs { + // Below statement is for Renovate Bot since it does not support toolVersion.set("..") pattern yet + val toolVersion = "4.0.0" + this.toolVersion.set(toolVersion) + + providers.gradleProperty("spotbugs.version") + .takeIf { it.isPresent } + ?.let { this.toolVersion.set(it) } + reportLevel.set(Confidence.HIGH) +} + +dependencies { + // Parenthesis are needed here: https://github.com/gradle/gradle/issues/9248 + (constraints) { + providers.gradleProperty("asm.version") + .takeIf { it.isPresent } + ?.let { + val asmVersion = it.get() + spotbugs("org.ow2.asm:asm:$asmVersion") + spotbugs("org.ow2.asm:asm-all:$asmVersion") + spotbugs("org.ow2.asm:asm-analysis:$asmVersion") + spotbugs("org.ow2.asm:asm-commons:$asmVersion") + spotbugs("org.ow2.asm:asm-tree:$asmVersion") + spotbugs("org.ow2.asm:asm-util:$asmVersion") + } + } +} + +tasks.configureEach { + group = LifecycleBasePlugin.VERIFICATION_GROUP +} diff --git a/build-logic/verification/src/main/kotlin/build-logic.style.gradle.kts b/build-logic/verification/src/main/kotlin/build-logic.style.gradle.kts new file mode 100644 index 0000000000..f5657771f5 --- /dev/null +++ b/build-logic/verification/src/main/kotlin/build-logic.style.gradle.kts @@ -0,0 +1,46 @@ +import org.gradle.kotlin.dsl.apply +import org.gradle.language.base.plugins.LifecycleBasePlugin + +plugins { + id("build-logic.build-params") +} + +if (!buildParameters.skipAutostyle) { + apply(plugin = "build-logic.autostyle") +} + +if (!buildParameters.skipCheckstyle) { + apply(plugin = "build-logic.checkstyle") +} + +if (!buildParameters.skipForbiddenApis) { + apply(plugin = "build-logic.forbidden-apis") +} + +plugins.withId("java-base") { + if (buildParameters.enableCheckerframework) { + apply(plugin = "build-logic.checkerframework") + } + if (buildParameters.enableErrorprone) { + apply(plugin = "build-logic.errorprone") + } + if (buildParameters.spotbugs) { + apply(plugin = "build-logic.spotbugs") + } +} + +if (!buildParameters.skipAutostyle || !buildParameters.skipCheckstyle || !buildParameters.skipForbiddenApis) { + tasks.register("style") { + group = LifecycleBasePlugin.VERIFICATION_GROUP + description = "Formats code (license header, import order, whitespace at end of line, ...) and executes Checkstyle verifications" + if (!buildParameters.skipAutostyle) { + dependsOn("autostyleApply") + } + if (!buildParameters.skipCheckstyle) { + dependsOn("checkstyleAll") + } + if (!buildParameters.skipForbiddenApis) { + dependsOn("forbiddenApisAll") + } + } +} diff --git a/build.gradle.kts b/build.gradle.kts index 53e7f06f30..2a1c64bb48 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,81 +3,23 @@ * See the LICENSE file in the project root for more information. */ -import com.github.spotbugs.SpotBugsTask -import com.github.vlsi.gradle.crlf.CrLfSpec -import com.github.vlsi.gradle.crlf.LineEndings -import com.github.vlsi.gradle.dsl.configureEach -import com.github.vlsi.gradle.git.FindGitAttributes -import com.github.vlsi.gradle.properties.dsl.props -import com.github.vlsi.gradle.publishing.dsl.simplifyXml -import com.github.vlsi.gradle.publishing.dsl.versionFromResolution -import de.thetaphi.forbiddenapis.gradle.CheckForbiddenApis -import de.thetaphi.forbiddenapis.gradle.CheckForbiddenApisExtension -import org.postgresql.buildtools.JavaCommentPreprocessorTask - plugins { - publishing - // Verification - checkstyle - jacoco - id("com.github.autostyle") - id("com.github.spotbugs") id("org.owasp.dependencycheck") - id("org.checkerframework") apply false - id("com.github.johnrengelman.shadow") apply false - id("de.thetaphi.forbiddenapis") apply false id("org.nosphere.gradle.github.actions") - id("com.github.vlsi.jandex") apply false // IDE configuration - id("org.jetbrains.gradle.plugin.idea-ext") id("com.github.vlsi.ide") // Release - id("com.github.vlsi.crlf") id("com.github.vlsi.gradle-extensions") - id("com.github.vlsi.license-gather") apply false id("com.github.vlsi.stage-vote-release") } -fun reportsForHumans() = !(System.getenv()["CI"]?.toBoolean() ?: props.bool("CI")) - -val lastEditYear = 2022 // TODO: by extra(lastEditYear("$rootDir/LICENSE")) - -// Do not enable spotbugs by default. Execute it only when -Pspotbugs is present -val enableSpotBugs = props.bool("spotbugs", default = false) -val enableCheckerframework by props() -val skipCheckstyle by props() -val skipAutostyle by props() -val skipJavadoc by props() -val skipForbiddenApis by props() -val enableMavenLocal by props() -val enableGradleMetadata by props() -// For instance -PincludeTestTags=!org.postgresql.test.SlowTests -// or -PincludeTestTags=!org.postgresql.test.Replication -val includeTestTags by props("") -// By default use Java implementation to sign artifacts -// When useGpgCmd=true, then gpg command line tool is used for signing -val useGpgCmd by props() -val jacocoEnabled by extra { - props.bool("coverage") || gradle.startParameter.taskNames.any { it.contains("jacoco") } -} - ide { // TODO: set copyright to PostgreSQL Global Development Group - // copyrightToAsf() ideaInstructionsUri = uri("https://github.com/pgjdbc/pgjdbc") doNotDetectFrameworks("android", "jruby") } -// This task scans the project for gitignore / gitattributes, and that is reused for building -// source/binary artifacts with the appropriate eol/executable file flags -// It enables to automatically exclude patterns from .gitignore -val gitProps by tasks.registering(FindGitAttributes::class) { - // Scanning for .gitignore and .gitattributes files in a task avoids doing that - // when distribution build is not required (e.g. code is just compiled) - root.set(rootDir) -} - val String.v: String get() = rootProject.extra["$this.version"] as String val buildVersion = "pgjdbc".v + releaseParams.snapshotSuffix @@ -86,10 +28,6 @@ println("Building pgjdbc $buildVersion") val isReleaseVersion = rootProject.releaseParams.release.get() -// Configures URLs to SVN and Nexus - -val licenseHeaderFile = file("config/license.header.java") - val jacocoReport by tasks.registering(JacocoReport::class) { group = "Coverage reports" description = "Generates an aggregate report from all subprojects" @@ -119,618 +57,23 @@ releaseParams { allprojects { group = "org.postgresql" version = buildVersion +} - apply(plugin = "com.github.vlsi.gradle-extensions") - - plugins.withId("de.marcphilipp.nexus-publish") { - configure { - clientTimeout.set(java.time.Duration.ofMinutes(15)) - } - } - - plugins.withId("io.codearte.nexus-staging") { - configure { - numberOfRetries = 20 * 60 / 2 - delayBetweenRetriesInMillis = 2000 - } - } - - repositories { - if (enableMavenLocal) { - mavenLocal() - } - mavenCentral() - } - - val javaMainUsed = file("src/main/java").isDirectory - val javaTestUsed = file("src/test/java").isDirectory - val javaUsed = javaMainUsed || javaTestUsed - if (javaUsed) { - apply(plugin = "java-library") - if (jacocoEnabled) { - apply(plugin = "jacoco") - } - } - - plugins.withId("java-library") { - dependencies { - "implementation"(platform(project(":bom"))) - } - } - - val kotlinMainUsed = file("src/main/kotlin").isDirectory - val kotlinTestUsed = file("src/test/kotlin").isDirectory - val kotlinUsed = kotlinMainUsed || kotlinTestUsed - if (kotlinUsed) { - apply(plugin = "java-library") - apply(plugin = "org.jetbrains.kotlin.jvm") - dependencies { - add(if (kotlinMainUsed) "implementation" else "testImplementation", kotlin("stdlib")) - } - } - - val hasTests = javaTestUsed || kotlinTestUsed - if (hasTests) { - // Add default tests dependencies - dependencies { - val testImplementation by configurations - val testRuntimeOnly by configurations - testImplementation("org.junit.jupiter:junit-jupiter-api") - testImplementation("uk.org.webcompere:system-stubs-jupiter") - testImplementation("org.junit.jupiter:junit-jupiter-params") - testImplementation("org.hamcrest:hamcrest") - testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") - if (project.props.bool("junit4", default = true)) { - // Allow projects to opt-out of junit dependency, so they can be JUnit5-only - testImplementation("junit:junit") - testRuntimeOnly("org.junit.vintage:junit-vintage-engine") - } - } - } - - if (!skipAutostyle) { - apply(plugin = "com.github.autostyle") - autostyle { - kotlinGradle { - ktlint() - trimTrailingWhitespace() - endWithNewline() - } - format("markdown") { - target("**/*.md") - endWithNewline() - } - } - } - val skipCheckstyle = skipCheckstyle || props.bool("skipCheckstyle") - if (!skipCheckstyle) { - apply() - dependencies { - checkstyle("com.puppycrawl.tools:checkstyle:${"checkstyle".v}") - } - checkstyle { - // Current one is ~8.8 - // https://github.com/julianhyde/toolbox/issues/3 - isShowViolations = true - // TOOD: move to /config - configDirectory.set(File(rootDir, "pgjdbc/src/main/checkstyle")) - configFile = configDirectory.get().file("checks.xml").asFile - } - - val checkstyleTasks = tasks.withType() - checkstyleTasks.configureEach { - // Checkstyle 8.26 does not need classpath, see https://github.com/gradle/gradle/issues/14227 - classpath = files() - } - - tasks.register("checkstyleAll") { - dependsOn(checkstyleTasks) - } - } - if (!skipAutostyle || !skipCheckstyle) { - tasks.register("style") { - group = LifecycleBasePlugin.VERIFICATION_GROUP - description = "Formats code (license header, import order, whitespace at end of line, ...) and executes Checkstyle verifications" - if (!skipAutostyle) { - dependsOn("autostyleApply") - } - if (!skipCheckstyle) { - dependsOn("checkstyleAll") - } - } - } - - tasks.configureEach { - // Ensure builds are reproducible - isPreserveFileTimestamps = false - isReproducibleFileOrder = true - dirMode = "775".toInt(8) - fileMode = "664".toInt(8) - } - - plugins.withType { - afterEvaluate { - configure { - val release = rootProject.releaseParams.release.get() - // Note it would still try to sign the artifacts, - // however it would fail only when signing a RELEASE version fails - isRequired = release - if (useGpgCmd) { - useGpgCmd() - } - } - } - } - - plugins.withType { - the().toolVersion = "jacoco".v - - val testTasks = tasks.withType() - val javaExecTasks = tasks.withType() - // This configuration must be postponed since JacocoTaskExtension might be added inside - // configure block of a task (== before this code is run). See :src:dist-check:createBatchTask - afterEvaluate { - for (t in arrayOf(testTasks, javaExecTasks)) { - t.configureEach { - extensions.findByType()?.apply { - // Do not collect coverage when not asked (e.g. via jacocoReport or -Pcoverage) - isEnabled = jacocoEnabled - // We don't want to collect coverage for third-party classes - includes?.add("org.postgresql.*") - } - } - } - } - - jacocoReport { - // Note: this creates a lazy collection - // Some of the projects might fail to create a file (e.g. no tests or no coverage), - // So we check for file existence. Otherwise JacocoMerge would fail - val execFiles = - files(testTasks, javaExecTasks).filter { it.exists() && it.name.endsWith(".exec") } - executionData(execFiles) - } - - tasks.configureEach { - reports { - html.required.set(reportsForHumans()) - xml.required.set(!reportsForHumans()) - } - } - } - - tasks { - // - configureEach { - variables.apply { - val jdbcSpec = props.string("jdbc.specification.version") - put("mvn.project.property.postgresql.jdbc.spec", "JDBC$jdbcSpec") - put("jdbc.specification.version", jdbcSpec) - } - - val re = Regex("^(\\d+)\\.(\\d+)(?:\\.(\\d+))?.*") - - val version = project.version.toString() - val matchResult = re.find(version) ?: throw GradleException("Unable to parse major.minor.patch version parts from project.version '$version'") - val (major, minor, patch) = matchResult.destructured - - variables.apply { - put("version", version) - put("version.major", major) - put("version.minor", minor) - put("version.patch", patch.ifBlank { "0" }) - } - } - // - - // - configureEach { - (options as StandardJavadocDocletOptions).apply { - // Please refrain from using non-ASCII chars below since the options are passed as - // javadoc.options file which is parsed with "default encoding" - noTimestamp.value = true - showFromProtected() - if (props.bool("failOnJavadocWarning", default = true)) { - // See JDK-8200363 (https://bugs.openjdk.java.net/browse/JDK-8200363) - // for information about the -Xwerror option. - addBooleanOption("Xwerror", true) - } - // There are too many missing javadocs, so failing the build on missing comments seems to be not an option - addBooleanOption("Xdoclint:all,-missing", true) - // javadoc: error - The code being documented uses modules but the packages - // defined in https://docs.oracle.com/javase/9/docs/api/ are in the unnamed module - source = "1.8" - docEncoding = "UTF-8" - charSet = "UTF-8" - encoding = "UTF-8" - docTitle = "PostgreSQL JDBC ${project.name} API version ${project.version}" - windowTitle = "PostgreSQL JDBC ${project.name} API version ${project.version}" - header = "PostgreSQL JDBC" - bottom = - "Copyright © 1997-$lastEditYear PostgreSQL Global Development Group. All Rights Reserved." - if (JavaVersion.current() >= JavaVersion.VERSION_1_9) { - addBooleanOption("html5", true) - links("https://docs.oracle.com/javase/9/docs/api/") - } else { - links("https://docs.oracle.com/javase/8/docs/api/") - } - } - } - // - } - - plugins.withType { - configure { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 - withSourcesJar() - if (!skipJavadoc) { - withJavadocJar() - } - } - - val sourceSets: SourceSetContainer by project - - apply(plugin = "com.github.vlsi.jandex") - apply(plugin = "maven-publish") - - project.configure { - skipIndexFileGeneration() - } - - if (!enableGradleMetadata) { - tasks.withType { - enabled = false - } - } - - if (!skipForbiddenApis && !props.bool("skipCheckstyle")) { - apply(plugin = "de.thetaphi.forbiddenapis") - configure { - failOnUnsupportedJava = false - signaturesFiles = files("$rootDir/config/forbidden-apis/forbidden-apis.txt") - bundledSignatures.addAll( - listOf( - // "jdk-deprecated", - "jdk-internal", - "jdk-non-portable" - // "jdk-system-out" - // "jdk-unsafe" - ) - ) - } - tasks.configureEach { - exclude("**/org/postgresql/util/internal/Unsafe.class") - } - } - - if (!skipAutostyle) { - autostyle { - java { - // targetExclude("**/test/java/*.java") - // TODO: implement license check (with copyright year) - // licenseHeaderFile(licenseHeaderFile) - importOrder( - "static ", - "org.postgresql.", - "", - "java.", - "javax." - ) - removeUnusedImports() - trimTrailingWhitespace() - indentWithSpaces(4) - endWithNewline() - } - } - } - - if (enableCheckerframework) { - apply(plugin = "org.checkerframework") - dependencies { - "checkerFramework"("org.checkerframework:checker:${"checkerframework".v}") - // CheckerFramework annotations might be used in the code as follows: - // dependencies { - // "compileOnly"("org.checkerframework:checker-qual") - // "testCompileOnly"("org.checkerframework:checker-qual") - // } - if (JavaVersion.current() == JavaVersion.VERSION_1_8) { - // only needed for JDK 8 - "checkerFrameworkAnnotatedJDK"("org.checkerframework:jdk8:${"checkerframework".v}") - } - } - configure { - skipVersionCheck = true - excludeTests = true - // See https://checkerframework.org/manual/#introduction - checkers.add("org.checkerframework.checker.nullness.NullnessChecker") - checkers.add("org.checkerframework.checker.optional.OptionalChecker") - // checkers.add("org.checkerframework.checker.index.IndexChecker") - checkers.add("org.checkerframework.checker.regex.RegexChecker") - extraJavacArgs.add("-Astubs=" + - fileTree("$rootDir/config/checkerframework") { - include("*.astub") - }.asPath - ) - // Translation classes are autogenerated, and they - extraJavacArgs.add("-AskipDefs=^org\\.postgresql\\.translation\\.") - // The below produces too many warnings :( - // extraJavacArgs.add("-Alint=redundantNullComparison") - } - } - - if (jacocoEnabled) { - // Add each project to combined report - val mainCode = sourceSets["main"] - jacocoReport.configure { - additionalSourceDirs.from(mainCode.allJava.srcDirs) - sourceDirectories.from(mainCode.allSource.srcDirs) - classDirectories.from(mainCode.output) - } - } - - if (enableSpotBugs) { - apply(plugin = "com.github.spotbugs") - spotbugs { - toolVersion = "spotbugs".v - reportLevel = "high" - // excludeFilter = file("$rootDir/src/main/config/spotbugs/spotbugs-filter.xml") - // By default spotbugs verifies TEST classes as well, and we do not want that - this.sourceSets = listOf(sourceSets["main"]) - } - dependencies { - // Parenthesis are needed here: https://github.com/gradle/gradle/issues/9248 - (constraints) { - "spotbugs"("org.ow2.asm:asm:${"asm".v}") - "spotbugs"("org.ow2.asm:asm-all:${"asm".v}") - "spotbugs"("org.ow2.asm:asm-analysis:${"asm".v}") - "spotbugs"("org.ow2.asm:asm-commons:${"asm".v}") - "spotbugs"("org.ow2.asm:asm-tree:${"asm".v}") - "spotbugs"("org.ow2.asm:asm-util:${"asm".v}") - } - } - } - - (sourceSets) { - "main" { - resources { - // TODO: remove when LICENSE is removed (it is used by Maven build for now) - exclude("src/main/resources/META-INF/LICENSE") - } - } - } - - tasks { - configureEach { - manifest { - attributes["Bundle-License"] = "BSD-2-Clause" - attributes["Implementation-Title"] = "PostgreSQL JDBC Driver" - attributes["Implementation-Version"] = project.version - val jdbcSpec = props.string("jdbc.specification.version") - if (jdbcSpec.isNotBlank()) { - attributes["Specification-Vendor"] = "Oracle Corporation" - attributes["Specification-Version"] = jdbcSpec - attributes["Specification-Title"] = "JDBC" - } - attributes["Implementation-Vendor"] = "PostgreSQL Global Development Group" - attributes["Implementation-Vendor-Id"] = "org.postgresql" - } - } - - configureEach { - options.encoding = "UTF-8" - } - configureEach { - useJUnitPlatform { - if (includeTestTags.isNotBlank()) { - includeTags.add(includeTestTags) - } - } - inputs.file("../build.properties") - if (file("../build.local.properties").exists()) { - inputs.file("../build.local.properties") - } - inputs.file("../ssltest.properties") - if (file("../ssltest.local.properties").exists()) { - inputs.file("../ssltest.local.properties") - } - testLogging { - showStandardStreams = true - } - exclude("**/*Suite*") - jvmArgs("-Xmx1536m") - jvmArgs("-Djdk.net.URLClassPath.disableClassPathURLCheck=true") - // Pass the property to tests - fun passProperty(name: String, default: String? = null) { - val value = System.getProperty(name) ?: default - value?.let { systemProperty(name, it) } - } - passProperty("preferQueryMode") - passProperty("java.awt.headless") - passProperty("junit.jupiter.execution.parallel.enabled", "true") - // TODO: remove when upgrade to JUnit 5.9+ - // See https://github.com/junit-team/junit5/commit/347e3119d36a5c226cddd7981452f11335fad422 - passProperty("junit.jupiter.execution.parallel.config.strategy", "DYNAMIC") - passProperty("junit.jupiter.execution.timeout.default", "5 m") - passProperty("user.language", "TR") - passProperty("user.country", "tr") - val props = System.getProperties() - for (e in props.propertyNames() as `java.util`.Enumeration) { - if (e.startsWith("pgjdbc.") || e.startsWith("java")) { - passProperty(e) - } - } - for (p in listOf("server", "port", "database", "username", "password", - "privilegedUser", "privilegedPassword", - "simpleProtocolOnly", "enable_ssl_tests")) { - passProperty(p) - } - } - configureEach { - group = LifecycleBasePlugin.VERIFICATION_GROUP - if (enableSpotBugs) { - description = "$description (skipped by default, to enable it add -Dspotbugs)" - } - reports { - html.required.set(reportsForHumans()) - xml.required.set(!reportsForHumans()) - } - enabled = enableSpotBugs - } - - afterEvaluate { - // Add default license/notice when missing - configureEach { - CrLfSpec(LineEndings.LF).run { - into("META-INF") { - filteringCharset = "UTF-8" - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - // Note: we need "generic Apache-2.0" text without third-party items - // So we use the text from $rootDir/config/ since source distribution - // contains altered text at $rootDir/LICENSE - textFrom("$rootDir/src/main/config/licenses/LICENSE") - textFrom("$rootDir/NOTICE") - } - } - } - } - } - - configure { - if (!project.props.bool("nexus.publish", default = true)) { - // Some of the artifacts do not need to be published - return@configure - } - - publications { - // - val extraMavenPublications by configurations.creating { - isVisible = false - isCanBeResolved = false - isCanBeConsumed = false - } - afterEvaluate { - named(project.name) { - extraMavenPublications.outgoing.artifacts.apply { - val keys = mapTo(HashSet()) { - it.classifier.orEmpty() to it.extension - } - artifacts.removeIf { - keys.contains(it.classifier.orEmpty() to it.extension) - } - forEach { artifact(it) } - } - } - } - // - // - create(project.name) { - artifactId = project.name - version = rootProject.version.toString() - from(components["java"]) - - // Gradle feature variants can't be mapped to Maven's pom - suppressAllPomMetadataWarnings() +val parameters by tasks.registering { + group = HelpTasksPlugin.HELP_GROUP + description = "Displays build parameters (i.e. -P flags) that can be used to customize the build" + dependsOn(gradle.includedBuild("build-logic").task(":build-parameters:parameters")) +} - // Use the resolved versions in pom.xml - // Gradle might have different resolution rules, so we set the versions - // that were used in Gradle build/test. - versionFromResolution() - pom { - simplifyXml() - name.set( - (project.findProperty("artifact.name") as? String) ?: "pgdjbc ${project.name.capitalize()}" - ) - description.set(project.description ?: "PostgreSQL JDBC Driver ${project.name.capitalize()}") - inceptionYear.set("1997") - url.set("https://jdbc.postgresql.org") - licenses { - license { - name.set("BSD-2-Clause") - url.set("https://jdbc.postgresql.org/about/license.html") - comments.set("BSD-2-Clause, copyright PostgreSQL Global Development Group") - distribution.set("repo") - } - } - organization { - name.set("PostgreSQL Global Development Group") - url.set("https://jdbc.postgresql.org/") - } - developers { - developer { - id.set("davecramer") - name.set("Dave Cramer") - } - developer { - id.set("jurka") - name.set("Kris Jurka") - } - developer { - id.set("oliver") - name.set("Oliver Jowett") - } - developer { - id.set("ringerc") - name.set("Craig Ringer") - } - developer { - id.set("vlsi") - name.set("Vladimir Sitnikov") - } - developer { - id.set("bokken") - name.set("Brett Okken") - } - } - issueManagement { - system.set("GitHub issues") - url.set("https://github.com/pgjdbc/pgjdbc/issues") - } - mailingLists { - mailingList { - name.set("PostgreSQL JDBC development list") - subscribe.set("https://lists.postgresql.org/") - unsubscribe.set("https://lists.postgresql.org/unsubscribe/") - post.set("pgsql-jdbc@postgresql.org") - archive.set("https://www.postgresql.org/list/pgsql-jdbc/") - } - } - scm { - connection.set("scm:git:https://github.com/pgjdbc/pgjdbc.git") - developerConnection.set("scm:git:https://github.com/pgjdbc/pgjdbc.git") - url.set("https://github.com/pgjdbc/pgjdbc") - tag.set("HEAD") - } - } - } - // - } - } +plugins.withId("de.marcphilipp.nexus-publish") { + configure { + clientTimeout.set(java.time.Duration.ofMinutes(15)) } } -subprojects { - if (project.path.startsWith(":postgresql")) { - plugins.withId("java") { - configure { - val sourceSets: SourceSetContainer by project - registerFeature("sspi") { - usingSourceSet(sourceSets["main"]) - } - registerFeature("osgi") { - usingSourceSet(sourceSets["main"]) - } - } - dependencies { - "sspiImplementation"("com.github.waffle:waffle-jna") - // The dependencies are provided by OSGi container, - // so they should not be exposed as transitive dependencies - "osgiCompileOnly"("org.osgi:org.osgi.core") - "osgiCompileOnly"("org.osgi:org.osgi.service.jdbc") - "testImplementation"("org.osgi:org.osgi.service.jdbc") { - because("DataSourceFactory is needed for PGDataSourceFactoryTest") - } - } - } +plugins.withId("io.codearte.nexus-staging") { + configure { + numberOfRetries = 20 * 60 / 2 + delayBetweenRetriesInMillis = 2000 } } diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts deleted file mode 100644 index 9adadee95e..0000000000 --- a/buildSrc/build.gradle.kts +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2020, PostgreSQL Global Development Group - * See the LICENSE file in the project root for more information. - */ - -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - -plugins { - `kotlin-dsl` - id("com.github.autostyle") -} - -repositories { - mavenCentral() - gradlePluginPortal() -} - -val String.v: String get() = rootProject.extra["$this.version"] as String - -dependencies { - implementation("com.igormaznitsa:jcp:${"jcp".v}") -} - -tasks.withType { - sourceCompatibility = "unused" - targetCompatibility = "unused" - kotlinOptions { - jvmTarget = "1.8" - } -} - -tasks.withType().configureEach { - // Ensure builds are reproducible - isPreserveFileTimestamps = false - isReproducibleFileOrder = true - dirMode = "775".toInt(8) - fileMode = "664".toInt(8) -} - -autostyle { - kotlin { - ktlint { - userData(mapOf("disabled_rules" to "no-wildcard-imports,import-ordering")) - } - trimTrailingWhitespace() - endWithNewline() - } - kotlinGradle { - ktlint() - trimTrailingWhitespace() - endWithNewline() - } -} diff --git a/buildSrc/gradle.properties b/buildSrc/gradle.properties deleted file mode 100644 index 16329b6f04..0000000000 --- a/buildSrc/gradle.properties +++ /dev/null @@ -1,13 +0,0 @@ -org.gradle.parallel=true - -# See https://github.com/gradle/gradle/pull/11358 -# repository.apache.org does not yet support .sha256 and .sha512 checksums -systemProp.org.gradle.internal.publish.checksums.insecure=true - -kotlin.code.style=official - -# Plugins -com.github.autostyle.version=3.2 - -# Dependencies -jcp.version=7.0.2 diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts deleted file mode 100644 index 7d6475e165..0000000000 --- a/buildSrc/settings.gradle.kts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * Copyright (c) 2020, PostgreSQL Global Development Group - * See the LICENSE file in the project root for more information. - */ - -pluginManagement { - plugins { - fun String.v() = extra["$this.version"].toString() - fun PluginDependenciesSpec.idv(id: String, key: String = id) = id(id) version key.v() - - idv("com.github.autostyle") - } -} diff --git a/docs/content/development/_index.md b/docs/content/development/_index.md new file mode 100644 index 0000000000..b210fc60a9 --- /dev/null +++ b/docs/content/development/_index.md @@ -0,0 +1,39 @@ +--- +title: "Development" +date: 2022-06-20T01:17:51+05:30 +draft: false +--- + +## About the Driver + +The PostgreSQL JDBC driver has some unique properties that you should be aware of before starting to develop any code for it. The current development driver supports a number of server versions. This doesn't mean that every feature must work in every combination, but a reasonable behaviour must be provided for non-supported versions. While this extra compatibility sounds like a lot of work, the actual goal is to reduce the amount of work by maintaining only one code base. + +## Tools + +The following tools are required to build and test the driver: + +* Java 11+ for starting Gradle. We recommend to install [Java 17](https://javaalmanac.io/jdk/17/) +* [Git SCM](https://git-scm.com) +* [A PostgreSQL instance](https://www.postgresql.org) to run the tests. + +## Build Process + +After retrieving the source from the [git repository](https://github.com/pgjdbc/pgjdbc). Move into the top level `pgjdbc` directory and simply type `./gradlew build -x test` or `./gradlew assemble` . This will build the driver and place it into `pgjdbc/build/distributions/postgresql-${version}.jar` . + +## Test Suite + +To make sure the driver is working as expected there are a set of JUnit tests that should be run. These require a database to run against that has the plpgsql procedural language installed. The default parameters for username and database are "test", and for password it's "test". So a sample interaction to set this up would look the following, if you enter "password" when asked for it: + +```bash +postgres@host:~$ createuser -d -A test -P +Enter password for user "test": +Enter it again: +CREATE USER + +postgres@host:~$ createdb -U test test +CREATE DATABASE + +postgres@host:~$ createlang plpgsql test +``` + +Now we're ready to run the tests, we simply type `./gradlew clean test` , and it should be off and running. To use non default values to run the regression tests, you can create a `build.local.properties` in the top level directory. This properties file allows you to set values for host, database, user, password, and port with the standard properties "key = value" usage. The ability to set the port value makes it easy to run the tests against a number of different server versions on the same machine. diff --git a/gradle.properties b/gradle.properties index 5097c57b7f..1b87ce79ac 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,6 +15,7 @@ kotlin.code.style=official # Release version can be generated by using -Prelease or -Prc= arguments pgjdbc.version=42.4.4 +lastEditYear=2023 # The options below configures the use of local clone (e.g. testing development versions) # You can pass un-comment it, or pass option -PlocalReleasePlugins, or -PlocalReleasePlugins= # localReleasePlugins=../vlsi-release-plugins @@ -27,43 +28,11 @@ pgjdbc.version=42.4.4 slowTestLogThreshold=2000 slowSuiteLogThreshold=500 -# Plugins -biz.aQute.bnd.builder.version=6.3.1 -com.github.autostyle.version=3.2 -com.github.burrunan.s3-build-cache.version=1.1 -com.github.johnrengelman.shadow.version=5.1.0 -com.github.lburgazzoli.karaf.version=0.5.4 -com.github.spotbugs.version=2.0.0 -com.github.vlsi.vlsi-release-plugins.version=1.82 -de.thetaphi.forbiddenapis.version=3.3 -kotlin.version=1.3.70 -me.champeau.jmh.version=0.6.6 -org.checkerframework.version=0.5.5 -org.jetbrains.gradle.plugin.idea-ext.version=0.7 -org.nosphere.gradle.github.actions.version=1.2.0 -org.owasp.dependencycheck.version=5.3.0 - # Tools -jacoco.version=0.8.5 -checkstyle.version=8.30 -spotbugs.version=4.0.0 - -# Dependencies -checkerframework.version=3.5.0 -classloader-leak-test-framework.version=1.1.1 -com.ongres.scram.client.version=2.1 -hamcrest-date.version=2.0.7 -hamcrest.version=2.2 -jmh.version=1.12 -javaee-api.version=6.0 -junit4.version=4.13 -junit5.version=5.8.2 -junit5-system-stubs-jupiter.version=2.0.1 -logback.version=1.1.2 -org.apache.felix.framework.version=7.0.5 -org.ops4j.pax.exam.version=4.13.5 -org.osgi.core.version=5.0.0 -org.osgi.service.jdbc.version=1.0.0 -pax-url-aether.version=2.6.7 -slf4j.version=1.7.25 -waffle-jna.version=1.9.1 +# The version below are listed for reference only, and they can be overridden by command line +# via -Pcheckstyle.version=... +# If you want to upgrade a version, modify the relevant toolVersion = "..." line in *.gradle.kts +#checkerframework.version=3.30.0 +#checkstyle.version=9.3 +#jacoco.version=0.8.5 +#spotbugs.version=4.0.0 diff --git a/pgjdbc-osgi-test/build.gradle.kts b/pgjdbc-osgi-test/build.gradle.kts index 8c8ebd6c27..e110a883f8 100644 --- a/pgjdbc-osgi-test/build.gradle.kts +++ b/pgjdbc-osgi-test/build.gradle.kts @@ -1,3 +1,7 @@ +plugins { + id("build-logic.java-library") +} + val pgjdbcRepository by configurations.creating { isCanBeConsumed = false isCanBeResolved = true @@ -14,17 +18,17 @@ dependencies { testImplementation(project(":postgresql")) - testImplementation("javax:javaee-api") - testImplementation("org.osgi:org.osgi.service.jdbc") - testImplementation("org.ops4j.pax.exam:pax-exam-container-native") + testImplementation("javax:javaee-api:6.0") + testImplementation("org.osgi:org.osgi.service.jdbc:1.0.0") + testImplementation("org.ops4j.pax.exam:pax-exam-container-native:4.13.5") // pax-exam is not yet compatible with junit5 // see https://github.com/ops4j/org.ops4j.pax.exam2/issues/886 - testImplementation("org.ops4j.pax.exam:pax-exam-junit4") - testImplementation("org.ops4j.pax.exam:pax-exam-link-mvn") - testImplementation("org.ops4j.pax.url:pax-url-aether") - testImplementation("org.apache.felix:org.apache.felix.framework") - testImplementation("ch.qos.logback:logback-core") - testImplementation("ch.qos.logback:logback-classic") + testImplementation("org.ops4j.pax.exam:pax-exam-junit4:4.13.5") + testImplementation("org.ops4j.pax.exam:pax-exam-link-mvn:4.13.5") + testImplementation("org.ops4j.pax.url:pax-url-aether:2.6.7") + testImplementation("org.apache.felix:org.apache.felix.framework:7.0.5") + testImplementation("ch.qos.logback:logback-core:1.1.2") + testImplementation("ch.qos.logback:logback-classic:1.1.2") } // diff --git a/pgjdbc/build.gradle.kts b/pgjdbc/build.gradle.kts index 64e95cfc4e..16238a06ab 100644 --- a/pgjdbc/build.gradle.kts +++ b/pgjdbc/build.gradle.kts @@ -4,7 +4,6 @@ */ import aQute.bnd.gradle.Bundle -import aQute.bnd.gradle.BundleTaskConvention import com.github.autostyle.gradle.AutostyleTask import com.github.vlsi.gradle.dsl.configureEach import com.github.vlsi.gradle.gettext.GettextTask @@ -17,6 +16,9 @@ import com.github.vlsi.gradle.release.dsl.dependencyLicenses import com.github.vlsi.gradle.release.dsl.licensesCopySpec plugins { + id("build-logic.java-published-library") + id("build-logic.test-junit5") + id("build-logic.java-comment-preprocessor") id("biz.aQute.bnd.builder") apply false id("com.github.johnrengelman.shadow") id("com.github.lburgazzoli.karaf") @@ -32,6 +34,21 @@ buildscript { } } +java { + val sourceSets: SourceSetContainer by project + registerFeature("sspi") { + usingSourceSet(sourceSets["main"]) + } + registerFeature("osgi") { + usingSourceSet(sourceSets["main"]) + } +} + +val knows by tasks.existing { + group = null // Hide the task from `./gradlew tasks` output + description = "This is a dummy task, unfortunately the author refuses to remove it: https://github.com/johnrengelman/shadow/issues/122" +} + val shaded by configurations.creating val karafFeatures by configurations.creating { @@ -52,11 +69,24 @@ configurations { val String.v: String get() = rootProject.extra["$this.version"] as String dependencies { - shaded(platform(project(":bom"))) - shaded("com.ongres.scram:client") + constraints { + api("com.github.waffle:waffle-jna:1.9.1") + api("org.osgi:org.osgi.core:5.0.0") + api("org.osgi:org.osgi.service.jdbc:1.0.0") + } - implementation("org.checkerframework:checker-qual") - testImplementation("se.jiderhamn:classloader-leak-test-framework") + "sspiImplementation"("com.github.waffle:waffle-jna") + // The dependencies are provided by OSGi container, + // so they should not be exposed as transitive dependencies + "osgiCompileOnly"("org.osgi:org.osgi.core") + "osgiCompileOnly"("org.osgi:org.osgi.service.jdbc") + "testImplementation"("org.osgi:org.osgi.service.jdbc") { + because("DataSourceFactory is needed for PGDataSourceFactoryTest") + } + shaded("com.ongres.scram:client:2.1") + + implementation("org.checkerframework:checker-qual:3.5.0") + testImplementation("se.jiderhamn:classloader-leak-test-framework:1.1.1") } val skipReplicationTests by props() @@ -75,7 +105,7 @@ tasks.configureEach { } } -val preprocessVersion by tasks.registering(org.postgresql.buildtools.JavaCommentPreprocessorTask::class) { +val preprocessVersion by tasks.registering(buildlogic.JavaCommentPreprocessorTask::class) { baseDir.set(projectDir) sourceFolders.add("src/main/version") } @@ -205,7 +235,7 @@ tasks.shadowJar { val osgiJar by tasks.registering(Bundle::class) { archiveClassifier.set("osgi") from(tasks.shadowJar.map { zipTree(it.archiveFile) }) - withConvention(BundleTaskConvention::class) { + bundle { bnd( """ -exportcontents: !org.postgresql.shaded.*, org.postgresql.* @@ -303,24 +333,20 @@ val sourceDistribution by tasks.registering(Tar::class) { include("README.md") } - val props = listOf( - "pgjdbc.version", - "junit4.version", - "junit5.version", - "junit5-system-stubs-jupiter.version", - "classloader-leak-test-framework.version", - "com.ongres.scram.client.version" - ).associate { propertyName -> - val value = project.findProperty(propertyName) as String - inputs.property(propertyName, project.findProperty(propertyName)) - "%{$propertyName}" to value + val testRuntimeClasspath = configurations.testRuntimeClasspath + dependsOn(testRuntimeClasspath) + + val props by lazy { + // Associate group:module with version, so %{group:module} can be used in reduced-pom.xml + testRuntimeClasspath.get().incoming.resolutionResult.allComponents + .associateBy({ it.moduleVersion!!.module.toString() }, { it.moduleVersion!!.version }) } from("reduced-pom.xml") { rename { "pom.xml" } filter { - it.replace(Regex("%\\{[^}]+\\}")) { - props[it.value] ?: throw GradleException("Unknown property in reduced-pom.xml: $it") + it.replace(Regex("%\\{([^}]+)\\}")) { + props[it.groups[1]!!.value] ?: throw GradleException("Unknown property in reduced-pom.xml: ${it.value}") } } } @@ -383,42 +409,3 @@ val extraMavenPublications by configurations.getting classifier = "features" } } - -// -val localRepoElements by configurations.creating { - isCanBeConsumed = true - isCanBeResolved = false - description = - "Shares local maven repository directory that contains the artifacts produced by the current project" - attributes { - attribute(Category.CATEGORY_ATTRIBUTE, objects.named("maven-repository")) - attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling.EXTERNAL)) - } -} - -val localRepoDir = layout.buildDirectory.dir("local-maven-repo") - -publishing { - repositories { - maven { - name = "local" - url = uri(localRepoDir) - } - } -} - -localRepoElements.outgoing.artifact(localRepoDir) { - builtBy(tasks.named("publishAllPublicationsToLocalRepository")) -} - -val cleanLocalRepository by tasks.registering(Delete::class) { - description = "Clears local-maven-repo so timestamp-based snapshot artifacts do not consume space" - delete(localRepoDir) -} - -tasks.withType() - .matching { it.name.contains("ToLocalRepository") } - .configureEach { - dependsOn(cleanLocalRepository) - } -// diff --git a/pgjdbc/reduced-pom.xml b/pgjdbc/reduced-pom.xml index 084393e116..119a38b6f2 100644 --- a/pgjdbc/reduced-pom.xml +++ b/pgjdbc/reduced-pom.xml @@ -10,7 +10,7 @@ postgresql jar PostgreSQL JDBC Driver - JDBC 4.2 - %{pgjdbc.version} + %{org.postgresql:postgresql} Java JDBC 4.2 (JRE 8+) driver for PostgreSQL database https://github.com/pgjdbc/pgjdbc @@ -29,6 +29,7 @@ 1.8 + 8 UTF-8 ${encoding} ${encoding} @@ -43,47 +44,48 @@ com.ongres.scram client - %{com.ongres.scram.client.version} + %{com.ongres.scram:client} se.jiderhamn classloader-leak-test-framework - %{classloader-leak-test-framework.version} + %{se.jiderhamn:classloader-leak-test-framework} test junit junit - %{junit4.version} + %{junit:junit} test org.junit.jupiter junit-jupiter-api - %{junit5.version} + %{org.junit.jupiter:junit-jupiter-api} test uk.org.webcompere system-stubs-jupiter - %{junit5-system-stubs-jupiter.version} + %{uk.org.webcompere:system-stubs-jupiter} + test org.junit.jupiter junit-jupiter-params - %{junit5.version} + %{org.junit.jupiter:junit-jupiter-params} test org.junit.jupiter junit-jupiter-engine - %{junit5.version} + %{org.junit.jupiter:junit-jupiter-params} test org.junit.vintage junit-vintage-engine - %{junit5.version} + %{org.junit.jupiter:junit-jupiter-params} test @@ -94,10 +96,6 @@ org.apache.maven.plugins maven-compiler-plugin ${maven-compiler-plugin.version} - - ${javac.target} - ${javac.target} - maven-surefire-plugin @@ -123,6 +121,53 @@ + + jdk8 + + 1.8 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${javac.target} + ${javac.target} + + + org/postgresql/test/jdbc2/DriverTest.java + org/postgresql/util/OSUtilTest.java + org/postgresql/util/StubEnvironmentAndProperties.java + org/postgresql/jdbcurlresolver/PgPassParserTest.java + org/postgresql/jdbcurlresolver/PgServiceConfParserTest.java + + + + + + + + + jdkge11 + + [11,) + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + ${java.target.release} + + + + + +