diff --git a/.github/workflows/ci-packagedpg.yml b/.github/workflows/ci-packagedpg.yml new file mode 100644 index 000000000..a8feac535 --- /dev/null +++ b/.github/workflows/ci-packagedpg.yml @@ -0,0 +1,322 @@ +# This workflow will build and test PL/Java against versions of PostgreSQL +# installed from prebuilt packages. + +name: PL/Java CI with PostgreSQL prebuilt packaged versions + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + if: false + + runs-on: ${{ matrix.oscc.os }} + strategy: + matrix: + oscc: + - os: ubuntu-latest + cc: gcc + - os: macos-latest + cc: clang + - os: windows-latest + cc: msvc + - os: windows-latest + cc: mingw + java: [9, 11, 12, 14, 15-ea] + pgsql: [12, 11, 10, 9.6, 9.5] + + steps: + + - name: Check out PL/Java + uses: actions/checkout@v2 + with: + path: pljava + + - name: Install PostgreSQL + shell: bash + run: echo here a miracle occurs + + - name: Set up JDK + uses: actions/setup-java@v1 + with: + java-version: ${{ matrix.java }} + + - name: Report Java, Maven, and PostgreSQL versions (Linux, macOS) + if: ${{ 'Windows' != runner.os }} + run: | + java -version + mvn --version + pg_config # might need attention to the path + + - name: Report Java, Maven, and PostgreSQL versions (Windows) + if: ${{ 'Windows' == runner.os }} + run: | + java -version + mvn --version + & "$Env:PGBIN\pg_config" # might need attention to the path + + - name: Build PL/Java (Linux, macOS) + if: ${{ 'Windows' != runner.os }} + working-directory: pljava + run: | + pgConfig=pg_config # might need attention to the path + mvn clean install --batch-mode \ + -Dpgsql.pgconfig="$pgConfig" \ + -Psaxon-examples -Ppgjdbc-ng \ + -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn + + - name: Build PL/Java (Windows MinGW-w64) + if: ${{ 'Windows' == runner.os && 'mingw' == matrix.oscc.cc }} + working-directory: pljava + # + # GitHub Actions will allow 'bash' as a shell choice, even on a Windows + # runner, in which case it's the bash from Git for Windows. That isn't the + # same as the msys64\usr\bin\bash that we want; what's more, while both + # rely on a cygwin DLL, they don't rely on the same one, and an attempt + # to exec one from the other leads to a "fatal error - cygheap base + # mismatch". So, the bash we want has to be started by something other + # than the bash we've got. In this case, set shell: to a command that + # will use cmd to start the right bash. + # + # Some of the MinGW magic is set up by the bash profile run at "login", so + # bash must be started with -l. That profile ends with a cd $HOME, so to + # avoid changing the current directory, set HOME=. first (credit for that: + # https://superuser.com/a/806371). As set above, . is really the pljava + # working-directory, so the bash script should start by resetting HOME to + # the path of its parent. + # + # The runner is provisioned with a very long PATH that includes separate + # bin directories for pre-provisioned packages. The MinGW profile replaces + # that with a much shorter path, so mvn and pg_config below must be given + # as absolute paths (using M2 and PGBIN supplied in the environment) or + # they won't be found. As long as mvn itself can be found, it is able + # to find java without difficulty, using the JAVA_HOME that is also in + # the environment. + # + # Those existing variables in the environment are all spelled in Windows + # style with drive letters, colons, and backslashes, rather than the MinGW + # unixy style, but the mingw bash doesn't seem to object. + # + # If you use the runner-supplied bash to examine the environment, you will + # see MSYSTEM=MINGW64 already in it, but that apparently is something the + # runner-supplied bash does. It must be set here before invoking the MinGW + # bash directly. + # + env: + HOME: . + MSYSTEM: MINGW64 + shell: 'cmd /C "c:\msys64\usr\bin\bash -l "{0}""' + run: | + HOME=$( (cd .. && pwd) ) + pgConfig="$PGBIN"'\pg_config' # might need attention to the path + "$M2"/mvn clean install --batch-mode \ + -Dpgsql.pgconfig="$pgConfig' \ + -Psaxon-examples -Ppgjdbc-ng \ + -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn + + - name: Install and test PL/Java + if: ${{ '9' != matrix.java || 'Windows' != runner.os }} + working-directory: pljava + shell: bash + run: | + pgConfig=pg_config # might need attention to the path + + packageJar=$(find pljava-packaging -name pljava-pg*.jar -print) + + mavenRepo="$HOME/.m2/repository" + + saxonVer=$( + find "$mavenRepo/net/sf/saxon/Saxon-HE" \ + -name 'Saxon-HE-*.jar' -print | + sort | + tail -n 1 + ) + saxonVer=${saxonVer%/*} + saxonVer=${saxonVer##*/} + + jdbcJar=$( + find "$mavenRepo/com/impossibl/pgjdbc-ng/pgjdbc-ng-all" \ + -name 'pgjdbc-ng-all-*.jar' -print | + sort | + tail -n 1 + ) + + # + # The runner on a Unix-like OS is running as a non-privileged user, but + # has passwordless sudo available (needed to install the PL/Java files + # into the system directories where the supplied PostgreSQL lives). By + # contrast, on Windows the runner has admin privilege, and can install + # the files without any fuss (but later below, pg_ctl will have to be + # used when starting PostgreSQL; pg_ctl has a Windows-specific ability + # to drop admin privs so postgres will not refuse to start). + # + # The Windows runner seems to have an extra pg_config somewhere on the + # path, that reports it was built with MinGW and installed in paths + # containing Strawberry that don't really exist. $PGBIN\pg_config refers + # to a different build made with MSVC, and those directories really + # exist, so specify that one explicitly when running on Windows. + # + # The Git for Windows bash environment includes a find command, and the + # things found have unixy paths returned. Make them Windowsy here, with + # a hardcoded assumption they start with /c which should become c: (as + # appears to be the case in the Windows runner currently). + # + if [[ $RUNNER_OS == Windows ]] + then + pathSep=';' + pgConfig="$PGBIN"'\pg_config' + java -Dpgconfig="$pgConfig" -jar "$packageJar" + function toWindowsPath() { + local p + p="c:${1#/c}" + printf "%s" "${p//\//\\}" + } + jdbcJar="$(toWindowsPath "$jdbcJar")" + mavenRepo="$(toWindowsPath "$mavenRepo")" + else + pathSep=':' + sudo "$JAVA_HOME"/bin/java -Dpgconfig="$pgConfig" -jar "$packageJar" + fi + + jshell \ + -execution local \ + "-J--class-path=$packageJar$pathSep$jdbcJar" \ + "--class-path=$packageJar" \ + "-J--add-modules=java.sql,java.sql.rowset" \ + "-J-Dpgconfig=$pgConfig" \ + "-J-Dcom.impossibl.shadow.io.netty.noUnsafe=true" \ + "-J-DmavenRepo=$mavenRepo" \ + "-J-DsaxonVer=$saxonVer" - <<\ENDJSHELL + + boolean succeeding = false; // begin pessimistic + + import static java.nio.file.Paths.get + import java.sql.Connection + import org.postgresql.pljava.packaging.Node + import static org.postgresql.pljava.packaging.Node.q + import static org.postgresql.pljava.packaging.Node.stateMachine + import static org.postgresql.pljava.packaging.Node.isVoidResultSet + import static org.postgresql.pljava.packaging.Node.s_isWindows + + Path javaLibDir = + get(System.getProperty("java.home"), s_isWindows ? "bin" : "lib") + + Path libjvm = ( + "Mac OS X".equals(System.getProperty("os.name")) + ? Stream.of("libjli.dylib", "jli/libjli.dylib") + .map(s -> javaLibDir.resolve(s)) + .filter(Files::exists).findFirst().get() + : javaLibDir.resolve(s_isWindows ? "jvm.dll" : "server/libjvm.so") + ); + + String vmopts = "-enableassertions:org.postgresql.pljava... -Xcheck:jni" + + Node n1 = Node.get_new_node("TestNode1") + + if ( s_isWindows ) + n1.use_pg_ctl(true) + + /* + * Keep a tally of the three types of diagnostic notices that may be + * received, and, independently, how many represent no-good test results + * (error always, but also warning if seen from the tests in the + * examples.jar deployment descriptor). + */ + Map results = + Stream.of("info", "warning", "error", "ng").collect( + LinkedHashMap::new, + (m,k) -> m.put(k, 0), (r,s) -> {}) + + boolean isDiagnostic(Object o, Set whatIsNG) + { + if ( ! ( o instanceof Throwable ) ) + return false; + String[] parts = Node.classify((Throwable)o); + String type = parts[0]; + results.compute(type, (k,v) -> 1 + v); + if ( whatIsNG.contains(type) ) + results.compute("ng", (k,v) -> 1 + v); + return true; + } + + try ( + AutoCloseable t1 = n1.initialized_cluster(); + AutoCloseable t2 = n1.started_server(Map.of( + "client_min_messages", "info", + "pljava.vmoptions", vmopts, + "pljava.libjvm_location", libjvm.toString() + )); + ) + { + try ( Connection c = n1.connect() ) + { + succeeding = true; // become optimistic, will be using &= below + + succeeding &= stateMachine( + "create extension no result", + null, + + q(c, "create extension pljava") + .flatMap(Node::semiFlattenDiagnostics) + .peek(Node::peek), + + // state 1: consume any diagnostics, or to state 2 with same item + (o,p,q) -> isDiagnostic(o, Set.of("error")) ? 1 : -2, + + // state 2: must be end of input + (o,p,q) -> null == o + ); + } + + /* + * Get a new connection; 'create extension' always sets a near-silent + * logging level, and PL/Java only checks once at VM start time, so in + * the same session where 'create extension' was done, logging is + * somewhat suppressed. + */ + try ( Connection c = n1.connect() ) + { + succeeding &= stateMachine( + "saxon path examples path", + null, + + Node.installSaxonAndExamplesAndPath(c, + System.getProperty("mavenRepo"), + System.getProperty("saxonVer"), + true) + .flatMap(Node::semiFlattenDiagnostics) + .peek(Node::peek), + + // states 1,2: diagnostics* then a void result set (saxon install) + (o,p,q) -> isDiagnostic(o, Set.of("error")) ? 1 : -2, + (o,p,q) -> isVoidResultSet(o, 1, 1) ? 3 : false, + + // states 3,4: diagnostics* then a void result set (set classpath) + (o,p,q) -> isDiagnostic(o, Set.of("error")) ? 3 : -4, + (o,p,q) -> isVoidResultSet(o, 1, 1) ? 5 : false, + + // states 5,6: diagnostics* then void result set (example install) + (o,p,q) -> isDiagnostic(o, Set.of("error", "warning")) ? 5 : -6, + (o,p,q) -> isVoidResultSet(o, 1, 1) ? 7 : false, + + // states 7,8: diagnostics* then a void result set (set classpath) + (o,p,q) -> isDiagnostic(o, Set.of("error")) ? 7 : -8, + (o,p,q) -> isVoidResultSet(o, 1, 1) ? 9 : false, + + // state 9: must be end of input + (o,p,q) -> null == o + ); + } + } catch ( Throwable t ) + { + succeeding = false; + throw t; + } + + System.out.println(results); + succeeding &= (0 == results.get("ng")); + System.exit(succeeding ? 0 : 1) + ENDJSHELL diff --git a/.github/workflows/ci-runnerpg.yml b/.github/workflows/ci-runnerpg.yml new file mode 100644 index 000000000..8dbffe7f2 --- /dev/null +++ b/.github/workflows/ci-runnerpg.yml @@ -0,0 +1,321 @@ +# This workflow will build and test PL/Java against the version of PostgreSQL +# preinstalled in the GitHub Actions runner environment. Naturally, this one +# does not have a PostgreSQL version in the build matrix. The version that's +# preinstalled is the version you get. + +name: PL/Java CI with PostgreSQL version supplied by the runner + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + if: true + + runs-on: ${{ matrix.oscc.os }} + continue-on-error: true + strategy: + matrix: + oscc: + - os: ubuntu-latest + cc: gcc + - os: macos-latest + cc: clang +# - os: windows-latest +# cc: msvc + - os: windows-latest + cc: mingw + java: [9, 11] # , 12, 14, 15-ea] + + steps: + + - name: Check out PL/Java + uses: actions/checkout@v2 + with: + path: pljava + + - name: Set up JDK + uses: actions/setup-java@v1 + with: + java-version: ${{ matrix.java }} + + - name: Report Java, Maven, and PostgreSQL versions (Linux, macOS) + if: ${{ 'Windows' != runner.os }} + run: | + java -version + mvn --version + pg_config + + - name: Report Java, Maven, and PostgreSQL versions (Windows) + if: ${{ 'Windows' == runner.os }} + run: | + java -version + mvn --version + & "$Env:PGBIN\pg_config" + + - name: Obtain PG development files (Ubuntu, PGDG) + if: ${{ 'Linux' == runner.os }} + run: sudo apt-get install postgresql-server-dev-12 libkrb5-dev + + - name: Build PL/Java (Linux, macOS) + if: ${{ 'Windows' != runner.os }} + working-directory: pljava + run: | + mvn clean install --batch-mode \ + -Psaxon-examples -Ppgjdbc-ng \ + -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn + + - name: Build PL/Java (Windows MinGW-w64) + if: ${{ 'Windows' == runner.os && 'mingw' == matrix.oscc.cc }} + working-directory: pljava + # + # GitHub Actions will allow 'bash' as a shell choice, even on a Windows + # runner, in which case it's the bash from Git for Windows. That isn't the + # same as the msys64\usr\bin\bash that we want; what's more, while both + # rely on a cygwin DLL, they don't rely on the same one, and an attempt + # to exec one from the other leads to a "fatal error - cygheap base + # mismatch". So, the bash we want has to be started by something other + # than the bash we've got. In this case, set shell: to a command that + # will use cmd to start the right bash. + # + # Some of the MinGW magic is set up by the bash profile run at "login", so + # bash must be started with -l. That profile ends with a cd $HOME, so to + # avoid changing the current directory, set HOME=. first (credit for that: + # https://superuser.com/a/806371). As set above, . is really the pljava + # working-directory, so the bash script should start by resetting HOME to + # the path of its parent. + # + # The runner is provisioned with a very long PATH that includes separate + # bin directories for pre-provisioned packages. The MinGW profile replaces + # that with a much shorter path, so mvn and pg_config below must be given + # as absolute paths (using M2 and PGBIN supplied in the environment) or + # they won't be found. As long as mvn itself can be found, it is able + # to find java without difficulty, using the JAVA_HOME that is also in + # the environment. + # + # Those existing variables in the environment are all spelled in Windows + # style with drive letters, colons, and backslashes, rather than the MinGW + # unixy style, but the mingw bash doesn't seem to object. + # + # If you use the runner-supplied bash to examine the environment, you will + # see MSYSTEM=MINGW64 already in it, but that apparently is something the + # runner-supplied bash does. It must be set here before invoking the MinGW + # bash directly. + # + env: + HOME: . + MSYSTEM: MINGW64 + shell: 'cmd /C "c:\msys64\usr\bin\bash -l "{0}""' + run: | + HOME=$( (cd .. && pwd) ) + "$M2"/mvn clean install --batch-mode \ + -Dpgsql.pgconfig="$PGBIN"'\pg_config' \ + -Psaxon-examples -Ppgjdbc-ng \ + -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn + + - name: Install and test PL/Java + if: ${{ '9' != matrix.java || 'Windows' != runner.os }} + working-directory: pljava + shell: bash + run: | + pgConfig=pg_config # runner-supplied, just get it from the PATH + + packageJar=$(find pljava-packaging -name pljava-pg*.jar -print) + + mavenRepo="$HOME/.m2/repository" + + saxonVer=$( + find "$mavenRepo/net/sf/saxon/Saxon-HE" \ + -name 'Saxon-HE-*.jar' -print | + sort | + tail -n 1 + ) + saxonVer=${saxonVer%/*} + saxonVer=${saxonVer##*/} + + jdbcJar=$( + find "$mavenRepo/com/impossibl/pgjdbc-ng/pgjdbc-ng-all" \ + -name 'pgjdbc-ng-all-*.jar' -print | + sort | + tail -n 1 + ) + + # + # The runner on a Unix-like OS is running as a non-privileged user, but + # has passwordless sudo available (needed to install the PL/Java files + # into the system directories where the supplied PostgreSQL lives). By + # contrast, on Windows the runner has admin privilege, and can install + # the files without any fuss (but later below, pg_ctl will have to be + # used when starting PostgreSQL; pg_ctl has a Windows-specific ability + # to drop admin privs so postgres will not refuse to start). + # + # The Windows runner seems to have an extra pg_config somewhere on the + # path, that reports it was built with MinGW and installed in paths + # containing Strawberry that don't really exist. $PGBIN\pg_config refers + # to a different build made with MSVC, and those directories really + # exist, so specify that one explicitly when running on Windows. + # + # The Git for Windows bash environment includes a find command, and the + # things found have unixy paths returned. Make them Windowsy here, with + # a hardcoded assumption they start with /c which should become c: (as + # appears to be the case in the Windows runner currently). + # + if [[ $RUNNER_OS == Windows ]] + then + pathSep=';' + pgConfig="$PGBIN"'\pg_config' + java -Dpgconfig="$pgConfig" -jar "$packageJar" + function toWindowsPath() { + local p + p="c:${1#/c}" + printf "%s" "${p//\//\\}" + } + jdbcJar="$(toWindowsPath "$jdbcJar")" + mavenRepo="$(toWindowsPath "$mavenRepo")" + else + pathSep=':' + sudo "$JAVA_HOME"/bin/java -Dpgconfig="$pgConfig" -jar "$packageJar" + fi + + jshell \ + -execution local \ + "-J--class-path=$packageJar$pathSep$jdbcJar" \ + "--class-path=$packageJar" \ + "-J--add-modules=java.sql,java.sql.rowset" \ + "-J-Dpgconfig=$pgConfig" \ + "-J-Dcom.impossibl.shadow.io.netty.noUnsafe=true" \ + "-J-DmavenRepo=$mavenRepo" \ + "-J-DsaxonVer=$saxonVer" - <<\ENDJSHELL + + boolean succeeding = false; // begin pessimistic + + import static java.nio.file.Paths.get + import java.sql.Connection + import org.postgresql.pljava.packaging.Node + import static org.postgresql.pljava.packaging.Node.q + import static org.postgresql.pljava.packaging.Node.stateMachine + import static org.postgresql.pljava.packaging.Node.isVoidResultSet + import static org.postgresql.pljava.packaging.Node.s_isWindows + + Path javaLibDir = + get(System.getProperty("java.home"), s_isWindows ? "bin" : "lib") + + Path libjvm = ( + "Mac OS X".equals(System.getProperty("os.name")) + ? Stream.of("libjli.dylib", "jli/libjli.dylib") + .map(s -> javaLibDir.resolve(s)) + .filter(Files::exists).findFirst().get() + : javaLibDir.resolve(s_isWindows ? "jvm.dll" : "server/libjvm.so") + ); + + String vmopts = "-enableassertions:org.postgresql.pljava... -Xcheck:jni" + + Node n1 = Node.get_new_node("TestNode1") + + if ( s_isWindows ) + n1.use_pg_ctl(true) + + /* + * Keep a tally of the three types of diagnostic notices that may be + * received, and, independently, how many represent no-good test results + * (error always, but also warning if seen from the tests in the + * examples.jar deployment descriptor). + */ + Map results = + Stream.of("info", "warning", "error", "ng").collect( + LinkedHashMap::new, + (m,k) -> m.put(k, 0), (r,s) -> {}) + + boolean isDiagnostic(Object o, Set whatIsNG) + { + if ( ! ( o instanceof Throwable ) ) + return false; + String[] parts = Node.classify((Throwable)o); + String type = parts[0]; + results.compute(type, (k,v) -> 1 + v); + if ( whatIsNG.contains(type) ) + results.compute("ng", (k,v) -> 1 + v); + return true; + } + + try ( + AutoCloseable t1 = n1.initialized_cluster(); + AutoCloseable t2 = n1.started_server(Map.of( + "client_min_messages", "info", + "pljava.vmoptions", vmopts, + "pljava.libjvm_location", libjvm.toString() + )); + ) + { + try ( Connection c = n1.connect() ) + { + succeeding = true; // become optimistic, will be using &= below + + succeeding &= stateMachine( + "create extension no result", + null, + + q(c, "create extension pljava") + .flatMap(Node::semiFlattenDiagnostics) + .peek(Node::peek), + + // state 1: consume any diagnostics, or to state 2 with same item + (o,p,q) -> isDiagnostic(o, Set.of("error")) ? 1 : -2, + + // state 2: must be end of input + (o,p,q) -> null == o + ); + } + + /* + * Get a new connection; 'create extension' always sets a near-silent + * logging level, and PL/Java only checks once at VM start time, so in + * the same session where 'create extension' was done, logging is + * somewhat suppressed. + */ + try ( Connection c = n1.connect() ) + { + succeeding &= stateMachine( + "saxon path examples path", + null, + + Node.installSaxonAndExamplesAndPath(c, + System.getProperty("mavenRepo"), + System.getProperty("saxonVer"), + true) + .flatMap(Node::semiFlattenDiagnostics) + .peek(Node::peek), + + // states 1,2: diagnostics* then a void result set (saxon install) + (o,p,q) -> isDiagnostic(o, Set.of("error")) ? 1 : -2, + (o,p,q) -> isVoidResultSet(o, 1, 1) ? 3 : false, + + // states 3,4: diagnostics* then a void result set (set classpath) + (o,p,q) -> isDiagnostic(o, Set.of("error")) ? 3 : -4, + (o,p,q) -> isVoidResultSet(o, 1, 1) ? 5 : false, + + // states 5,6: diagnostics* then void result set (example install) + (o,p,q) -> isDiagnostic(o, Set.of("error", "warning")) ? 5 : -6, + (o,p,q) -> isVoidResultSet(o, 1, 1) ? 7 : false, + + // states 7,8: diagnostics* then a void result set (set classpath) + (o,p,q) -> isDiagnostic(o, Set.of("error")) ? 7 : -8, + (o,p,q) -> isVoidResultSet(o, 1, 1) ? 9 : false, + + // state 9: must be end of input + (o,p,q) -> null == o + ); + } + } catch ( Throwable t ) + { + succeeding = false; + throw t; + } + + System.out.println(results); + succeeding &= (0 == results.get("ng")); + System.exit(succeeding ? 0 : 1) + ENDJSHELL diff --git a/.github/workflows/ci-sourcepg.yml b/.github/workflows/ci-sourcepg.yml new file mode 100644 index 000000000..a33a5c449 --- /dev/null +++ b/.github/workflows/ci-sourcepg.yml @@ -0,0 +1,329 @@ +# This workflow will build and test PL/Java against versions of PostgreSQL +# built from source. + +name: PL/Java CI with PostgreSQL versions built from source + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + if: false + + runs-on: ${{ matrix.oscc.os }} + strategy: + matrix: + oscc: + - os: ubuntu-latest + cc: gcc + - os: macos-latest + cc: clang + - os: windows-latest + cc: msvc + - os: windows-latest + cc: mingw + java: [9, 11, 12, 14, 15-ea] + pgsql: [REL_12_4, REL_11_9, REL_10_14, REL9_6_19, REL9_5_23] + + steps: + + - name: Check out PL/Java + uses: actions/checkout@v2 + with: + path: pljava + + - name: Check out PostgreSQL + uses: actions/checkout@v2 + with: + path: postgresql + repository: postgres/postgres + ref: ${{ matrix.pgsql }} + + - name: Configure and build PostgreSQL + shell: bash + run: echo here a miracle occurs + + - name: Set up JDK + uses: actions/setup-java@v1 + with: + java-version: ${{ matrix.java }} + + - name: Report Java, Maven, and PostgreSQL versions (Linux, macOS) + if: ${{ 'Windows' != runner.os }} + run: | + java -version + mvn --version + pg_config # might need attention to the path + + - name: Report Java, Maven, and PostgreSQL versions (Windows) + if: ${{ 'Windows' == runner.os }} + run: | + java -version + mvn --version + & "$Env:PGBIN\pg_config" # might need attention to the path + + - name: Build PL/Java (Linux, macOS) + if: ${{ 'Windows' != runner.os }} + working-directory: pljava + run: | + pgConfig=pg_config # might need attention to the path + mvn clean install --batch-mode \ + -Dpgsql.pgconfig="$pgConfig" \ + -Psaxon-examples -Ppgjdbc-ng \ + -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn + + - name: Build PL/Java (Windows MinGW-w64) + if: ${{ 'Windows' == runner.os && 'mingw' == matrix.oscc.cc }} + working-directory: pljava + # + # GitHub Actions will allow 'bash' as a shell choice, even on a Windows + # runner, in which case it's the bash from Git for Windows. That isn't the + # same as the msys64\usr\bin\bash that we want; what's more, while both + # rely on a cygwin DLL, they don't rely on the same one, and an attempt + # to exec one from the other leads to a "fatal error - cygheap base + # mismatch". So, the bash we want has to be started by something other + # than the bash we've got. In this case, set shell: to a command that + # will use cmd to start the right bash. + # + # Some of the MinGW magic is set up by the bash profile run at "login", so + # bash must be started with -l. That profile ends with a cd $HOME, so to + # avoid changing the current directory, set HOME=. first (credit for that: + # https://superuser.com/a/806371). As set above, . is really the pljava + # working-directory, so the bash script should start by resetting HOME to + # the path of its parent. + # + # The runner is provisioned with a very long PATH that includes separate + # bin directories for pre-provisioned packages. The MinGW profile replaces + # that with a much shorter path, so mvn and pg_config below must be given + # as absolute paths (using M2 and PGBIN supplied in the environment) or + # they won't be found. As long as mvn itself can be found, it is able + # to find java without difficulty, using the JAVA_HOME that is also in + # the environment. + # + # Those existing variables in the environment are all spelled in Windows + # style with drive letters, colons, and backslashes, rather than the MinGW + # unixy style, but the mingw bash doesn't seem to object. + # + # If you use the runner-supplied bash to examine the environment, you will + # see MSYSTEM=MINGW64 already in it, but that apparently is something the + # runner-supplied bash does. It must be set here before invoking the MinGW + # bash directly. + # + env: + HOME: . + MSYSTEM: MINGW64 + shell: 'cmd /C "c:\msys64\usr\bin\bash -l "{0}""' + run: | + HOME=$( (cd .. && pwd) ) + pgConfig="$PGBIN"'\pg_config' # might need attention to the path + "$M2"/mvn clean install --batch-mode \ + -Dpgsql.pgconfig="$pgConfig' \ + -Psaxon-examples -Ppgjdbc-ng \ + -Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=warn + + - name: Install and test PL/Java + if: ${{ '9' != matrix.java || 'Windows' != runner.os }} + working-directory: pljava + shell: bash + run: | + pgConfig=pg_config # might need attention to the path + + packageJar=$(find pljava-packaging -name pljava-pg*.jar -print) + + mavenRepo="$HOME/.m2/repository" + + saxonVer=$( + find "$mavenRepo/net/sf/saxon/Saxon-HE" \ + -name 'Saxon-HE-*.jar' -print | + sort | + tail -n 1 + ) + saxonVer=${saxonVer%/*} + saxonVer=${saxonVer##*/} + + jdbcJar=$( + find "$mavenRepo/com/impossibl/pgjdbc-ng/pgjdbc-ng-all" \ + -name 'pgjdbc-ng-all-*.jar' -print | + sort | + tail -n 1 + ) + + # + # The runner on a Unix-like OS is running as a non-privileged user, but + # has passwordless sudo available (needed to install the PL/Java files + # into the system directories where the supplied PostgreSQL lives). By + # contrast, on Windows the runner has admin privilege, and can install + # the files without any fuss (but later below, pg_ctl will have to be + # used when starting PostgreSQL; pg_ctl has a Windows-specific ability + # to drop admin privs so postgres will not refuse to start). + # + # The Windows runner seems to have an extra pg_config somewhere on the + # path, that reports it was built with MinGW and installed in paths + # containing Strawberry that don't really exist. $PGBIN\pg_config refers + # to a different build made with MSVC, and those directories really + # exist, so specify that one explicitly when running on Windows. + # + # The Git for Windows bash environment includes a find command, and the + # things found have unixy paths returned. Make them Windowsy here, with + # a hardcoded assumption they start with /c which should become c: (as + # appears to be the case in the Windows runner currently). + # + if [[ $RUNNER_OS == Windows ]] + then + pathSep=';' + pgConfig="$PGBIN"'\pg_config' + java -Dpgconfig="$pgConfig" -jar "$packageJar" + function toWindowsPath() { + local p + p="c:${1#/c}" + printf "%s" "${p//\//\\}" + } + jdbcJar="$(toWindowsPath "$jdbcJar")" + mavenRepo="$(toWindowsPath "$mavenRepo")" + else + pathSep=':' + sudo "$JAVA_HOME"/bin/java -Dpgconfig="$pgConfig" -jar "$packageJar" + fi + + jshell \ + -execution local \ + "-J--class-path=$packageJar$pathSep$jdbcJar" \ + "--class-path=$packageJar" \ + "-J--add-modules=java.sql,java.sql.rowset" \ + "-J-Dpgconfig=$pgConfig" \ + "-J-Dcom.impossibl.shadow.io.netty.noUnsafe=true" \ + "-J-DmavenRepo=$mavenRepo" \ + "-J-DsaxonVer=$saxonVer" - <<\ENDJSHELL + + boolean succeeding = false; // begin pessimistic + + import static java.nio.file.Paths.get + import java.sql.Connection + import org.postgresql.pljava.packaging.Node + import static org.postgresql.pljava.packaging.Node.q + import static org.postgresql.pljava.packaging.Node.stateMachine + import static org.postgresql.pljava.packaging.Node.isVoidResultSet + import static org.postgresql.pljava.packaging.Node.s_isWindows + + Path javaLibDir = + get(System.getProperty("java.home"), s_isWindows ? "bin" : "lib") + + Path libjvm = ( + "Mac OS X".equals(System.getProperty("os.name")) + ? Stream.of("libjli.dylib", "jli/libjli.dylib") + .map(s -> javaLibDir.resolve(s)) + .filter(Files::exists).findFirst().get() + : javaLibDir.resolve(s_isWindows ? "jvm.dll" : "server/libjvm.so") + ); + + String vmopts = "-enableassertions:org.postgresql.pljava... -Xcheck:jni" + + Node n1 = Node.get_new_node("TestNode1") + + if ( s_isWindows ) + n1.use_pg_ctl(true) + + /* + * Keep a tally of the three types of diagnostic notices that may be + * received, and, independently, how many represent no-good test results + * (error always, but also warning if seen from the tests in the + * examples.jar deployment descriptor). + */ + Map results = + Stream.of("info", "warning", "error", "ng").collect( + LinkedHashMap::new, + (m,k) -> m.put(k, 0), (r,s) -> {}) + + boolean isDiagnostic(Object o, Set whatIsNG) + { + if ( ! ( o instanceof Throwable ) ) + return false; + String[] parts = Node.classify((Throwable)o); + String type = parts[0]; + results.compute(type, (k,v) -> 1 + v); + if ( whatIsNG.contains(type) ) + results.compute("ng", (k,v) -> 1 + v); + return true; + } + + try ( + AutoCloseable t1 = n1.initialized_cluster(); + AutoCloseable t2 = n1.started_server(Map.of( + "client_min_messages", "info", + "pljava.vmoptions", vmopts, + "pljava.libjvm_location", libjvm.toString() + )); + ) + { + try ( Connection c = n1.connect() ) + { + succeeding = true; // become optimistic, will be using &= below + + succeeding &= stateMachine( + "create extension no result", + null, + + q(c, "create extension pljava") + .flatMap(Node::semiFlattenDiagnostics) + .peek(Node::peek), + + // state 1: consume any diagnostics, or to state 2 with same item + (o,p,q) -> isDiagnostic(o, Set.of("error")) ? 1 : -2, + + // state 2: must be end of input + (o,p,q) -> null == o + ); + } + + /* + * Get a new connection; 'create extension' always sets a near-silent + * logging level, and PL/Java only checks once at VM start time, so in + * the same session where 'create extension' was done, logging is + * somewhat suppressed. + */ + try ( Connection c = n1.connect() ) + { + succeeding &= stateMachine( + "saxon path examples path", + null, + + Node.installSaxonAndExamplesAndPath(c, + System.getProperty("mavenRepo"), + System.getProperty("saxonVer"), + true) + .flatMap(Node::semiFlattenDiagnostics) + .peek(Node::peek), + + // states 1,2: diagnostics* then a void result set (saxon install) + (o,p,q) -> isDiagnostic(o, Set.of("error")) ? 1 : -2, + (o,p,q) -> isVoidResultSet(o, 1, 1) ? 3 : false, + + // states 3,4: diagnostics* then a void result set (set classpath) + (o,p,q) -> isDiagnostic(o, Set.of("error")) ? 3 : -4, + (o,p,q) -> isVoidResultSet(o, 1, 1) ? 5 : false, + + // states 5,6: diagnostics* then void result set (example install) + (o,p,q) -> isDiagnostic(o, Set.of("error", "warning")) ? 5 : -6, + (o,p,q) -> isVoidResultSet(o, 1, 1) ? 7 : false, + + // states 7,8: diagnostics* then a void result set (set classpath) + (o,p,q) -> isDiagnostic(o, Set.of("error")) ? 7 : -8, + (o,p,q) -> isVoidResultSet(o, 1, 1) ? 9 : false, + + // state 9: must be end of input + (o,p,q) -> null == o + ); + } + } catch ( Throwable t ) + { + succeeding = false; + throw t; + } + + System.out.println(results); + succeeding &= (0 == results.get("ng")); + System.exit(succeeding ? 0 : 1) + ENDJSHELL diff --git a/pljava-so/pom.xml b/pljava-so/pom.xml index c8830e32b..71704ae2f 100644 --- a/pljava-so/pom.xml +++ b/pljava-so/pom.xml @@ -151,6 +151,51 @@ } }, + { + name : "Windows MinGW with MSVC PostgreSQL", + + object_extension : ".o", + + probe: function(os_name) { + return os_name.toLowerCase().contains("windows") + && java.lang.System.getenv().containsKey("MSYSTEM") + && java.lang.System.getenv().get("MSYSTEM").equalsIgnoreCase("MINGW64") + && getPgConfigProperty("--cc").equals("not recorded"); + }, + + compile : function(cc, files, output_dir, includes, defines, flags) { + includes.add(java_include.resolve("win32").toString()); + includes.add(Paths.get(includedir_server, "port", "win32").toString()); + includes.add(resolve(pljava_include, + Paths.get("fallback", "win32")).toString()); + defines.put("Windows", null); + + var compileProcess = utils.processBuilder(function(l) { + l.add(cc); + l.addAll(pgxs.formatDefines(defines)); + l.addAll(pgxs.formatIncludes(includes)); + l.add("-c"); + l.addAll(files); + }); + compileProcess.directory(output_dir.toFile()); + return runCommand(utils.forWindowsCRuntime(compileProcess)); + }, + + link : function(cc, flags, files, target_path) { + flags.addAll(of("-Wl,--export-all-symbols","-shared-libgcc")); + var linkingProcess = utils.processBuilder(function(l) { + l.add(cc); + l.addAll(of("-shared", "-o", library_name + ".dll")); + l.addAll(files); + + // From compiler-mingw64 profile + l.addAll(of("-L" + pkglibdir, "-Bdynamic", "-lpostgres")); + }); + linkingProcess.directory(target_path.toFile()); + return runCommand(utils.forWindowsCRuntime(linkingProcess)); + } + }, + { name : "Windows MinGW",