From b25fc7cd9f7edc0f6bf1b5581266049a5296cd8b Mon Sep 17 00:00:00 2001 From: Chapman Flack Date: Wed, 12 Jun 2024 15:39:07 -0400 Subject: [PATCH 1/3] Report PostgreSQL version found to build against --- .../org/postgresql/pljava/pgxs/PGXSUtils.java | 40 ++++++++++++++++++- pljava-so/pom.xml | 2 + 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/PGXSUtils.java b/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/PGXSUtils.java index eac54fcaa..585f2eab6 100644 --- a/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/PGXSUtils.java +++ b/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/PGXSUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Tada AB and other contributors, as listed below. + * Copyright (c) 2020-2024 Tada AB and other contributors, as listed below. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the The BSD 3-Clause License @@ -32,6 +32,7 @@ import java.util.List; import java.util.ListIterator; import java.util.Map; +import java.util.Objects; import java.util.function.BiConsumer; import java.util.function.BinaryOperator; import java.util.function.Consumer; @@ -43,6 +44,8 @@ import java.util.regex.Pattern; import static java.lang.System.getProperty; +import static java.util.stream.Collectors.joining; +import static java.util.stream.Stream.iterate; import static javax.script.ScriptContext.GLOBAL_SCOPE; /** @@ -314,6 +317,41 @@ public String getPgConfigProperty (String pgConfigCommand, pgConfigOutput.length() - System.lineSeparator().length()); } + /** + * Reports the detailed {@code PG_VERSION_STR} for the PostgreSQL version + * found to build against. + *

+ * This should be found as a C string literal after + * {@code #define PG_VERSION_STR} in + * includedir_server/{@code pg_config.h}. + *

+ * If the value can be found, it is logged at {@code info} level. Otherwise, + * the exception(s) responsible will be logged at {@code debug} level. + * @param includedir_server pass the result of a previous + * {@code getPgConfigProperty(..., "--includedir_server")} + */ + public void reportPostgreSQLVersion(String includedir_server) + { + Path pg_config_h = Paths.get(includedir_server, "pg_config.h"); + try + { + log.info( + defaultCharsetDecodeStrict(Files.readAllBytes(pg_config_h)) + .replaceFirst( + "(?ms).*^#define\\s++PG_VERSION_STR\\s++(?-s:(.++))$.*+", + "Found $1") + ); + } + catch ( IOException | IndexOutOfBoundsException e ) + { + log.debug( + "in reportPostgreSQLVersion: " + + iterate(e, Objects::nonNull, Throwable::getCause) + .map(Object::toString).collect(joining("\nCaused by: ")) + ); + } + } + /** * Returns a ProcessBuilder with suitable defaults and arguments added from * input function. diff --git a/pljava-so/pom.xml b/pljava-so/pom.xml index 59ec48826..368b30afc 100644 --- a/pljava-so/pom.xml +++ b/pljava-so/pom.xml @@ -66,6 +66,8 @@ Paths.get(base_dir_path, "..", "pljava", "target", "javah-include").toString() )); + utils.reportPostgreSQLVersion(includedir_server); + var base_defines = new HashMap(); base_defines.put("PLJAVA_SO_VERSION", project.parent.version); if ( cc.equalsIgnoreCase("gcc") ) From 00639c656d8489c4d9f0a5e1d0b38ce5dbce2bd3 Mon Sep 17 00:00:00 2001 From: Chapman Flack Date: Wed, 12 Jun 2024 15:42:17 -0400 Subject: [PATCH 2/3] Include command/arguments in Maven debug output For the Windows cases, log the arguments before applying the forWindowsCRuntime() transformation (but indicating, in the message, that it will be applied). Eliminate the debug logging of the script text in ScriptingMojo and ReportScriptingMojo; Maven itself, when producing debug output, will have dumped that to the log perhaps twice already (when reading the configuration, and when configuring the mojo). --- .../org/postgresql/pljava/pgxs/PGXSUtils.java | 16 ++++++++++++++-- .../pljava/pgxs/ReportScriptingMojo.java | 3 +-- .../postgresql/pljava/pgxs/ScriptingMojo.java | 3 +-- pljava-so/pom.xml | 4 ++-- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/PGXSUtils.java b/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/PGXSUtils.java index 585f2eab6..1c33c345b 100644 --- a/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/PGXSUtils.java +++ b/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/PGXSUtils.java @@ -159,9 +159,21 @@ ScriptEngine getScriptEngine(PlexusConfiguration script) context.setAttribute("buildPaths", (Function, Map>) this::buildPaths, GLOBAL_SCOPE); + context.setAttribute("runCommand", - (ToIntFunction) this::runCommand, - GLOBAL_SCOPE); + (ToIntFunction) b -> + { + log.debug("To run: " + b.command()); + return runCommand(b); + }, GLOBAL_SCOPE); + + context.setAttribute("runWindowsCRuntimeCommand", + (ToIntFunction) b -> + { + log.debug("To run (needs WindowsCRuntime transformation): " + + b.command()); + return runCommand(forWindowsCRuntime(b)); + }, GLOBAL_SCOPE); /* * Also provide a specialized method useful for a script that may diff --git a/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/ReportScriptingMojo.java b/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/ReportScriptingMojo.java index f37577fd4..868bcaaaf 100644 --- a/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/ReportScriptingMojo.java +++ b/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/ReportScriptingMojo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Tada AB and other contributors, as listed below. + * Copyright (c) 2020-2024 Tada AB and other contributors, as listed below. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the The BSD 3-Clause License @@ -70,7 +70,6 @@ private void setReportScript() utils = new PGXSUtils(project, getLog()); ScriptEngine engine = utils.getScriptEngine(script); String scriptText = script.getValue(); - getLog().debug(scriptText); engine.eval(scriptText); reportScript = ((Invocable)engine).getInterface(ReportScript.class); } diff --git a/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/ScriptingMojo.java b/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/ScriptingMojo.java index be5e7c0bc..289d707bf 100644 --- a/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/ScriptingMojo.java +++ b/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/ScriptingMojo.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 Tada AB and other contributors, as listed below. + * Copyright (c) 2020-2024 Tada AB and other contributors, as listed below. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the The BSD 3-Clause License @@ -66,7 +66,6 @@ public void execute () throws MojoExecutionException, MojoFailureException utils = new PGXSUtils(project, getLog()); String scriptText = script.getValue(); ScriptEngine engine = utils.getScriptEngine(script); - getLog().debug(scriptText); engine.getContext().setAttribute("session", session, ScriptContext.GLOBAL_SCOPE); diff --git a/pljava-so/pom.xml b/pljava-so/pom.xml index 368b30afc..ab7c30bf6 100644 --- a/pljava-so/pom.xml +++ b/pljava-so/pom.xml @@ -241,7 +241,7 @@ l.addAll(files); }); compileProcess.directory(output_dir.toFile()); - return runCommand(utils.forWindowsCRuntime(compileProcess)); + return runWindowsCRuntimeCommand(compileProcess); }, link : function(cc, flags, files, target_path) { @@ -258,7 +258,7 @@ l.addAll(of("-L" + pkglibdir, "-Bdynamic", "-lpostgres")); }); linkingProcess.directory(target_path.toFile()); - return runCommand(utils.forWindowsCRuntime(linkingProcess)); + return runWindowsCRuntimeCommand(linkingProcess); } }, From 96a1690162f23a1543b6225a0a8c7cb90d2ec65c Mon Sep 17 00:00:00 2001 From: Chapman Flack Date: Wed, 12 Jun 2024 15:42:26 -0400 Subject: [PATCH 3/3] Javadoc/whitespace cleanup and slight refactoring As dubious as combining any refactoring with a Javadoc/whitespace cleanup may be, it was preferable to writing new Javadoc to try to explain why it was factored as it was (with some script engine bindings being added in PGXSUtils.getScriptEngine and others in ScriptingMojo.execute, especially as that didn't match where the underlying methods lived). Adjusts javadoc options to include package-access types and members in pljava-pgxs, as some of those are exported to the script engine and ought to be documented. --- pljava-pgxs/pom.xml | 2 + .../postgresql/pljava/pgxs/AbstractPGXS.java | 73 ++++-- .../org/postgresql/pljava/pgxs/PGXSUtils.java | 232 ++++++++++++------ .../pljava/pgxs/ReportScriptingMojo.java | 104 ++++---- .../postgresql/pljava/pgxs/ScriptingMojo.java | 116 ++++----- 5 files changed, 314 insertions(+), 213 deletions(-) diff --git a/pljava-pgxs/pom.xml b/pljava-pgxs/pom.xml index b3d3de753..c53ff1e22 100644 --- a/pljava-pgxs/pom.xml +++ b/pljava-pgxs/pom.xml @@ -170,6 +170,8 @@ function executeReport(report, locale) */ "-locale", locale.toString(), "-quiet", + "--show-members", "package", + "--show-types", "package", /* * Options that are passed to the doclet. */ diff --git a/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/AbstractPGXS.java b/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/AbstractPGXS.java index 642e06753..09d2a6511 100644 --- a/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/AbstractPGXS.java +++ b/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/AbstractPGXS.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020-2021 Tada AB and other contributors, as listed below. + * Copyright (c) 2020-2024 Tada AB and other contributors, as listed below. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the The BSD 3-Clause License @@ -21,33 +21,59 @@ import java.util.stream.Collectors; /** - * Class to act as a blueprint for platform specific build configurations in - * pljava-so/pom.xml + * Class to act as a blueprint for platform-specific build configurations in a + * {@code pom.xml}. + *

+ * A {@code scripted-goal} configuration in the POM should contain a script + * that somehow selects and supplies a concrete implementation of this abstract + * class. + *

+ * In {@code pljava-so/pom.xml}, a block of {@code application/javascript} is + * supplied that contains a {@code configuration} array of JS objects, each of + * which has a {@code name} entry, a {@code probe} function returning true on + * some supported platform, and the necessary functions to serve as an + * implementation of this class. The script selects one whose probe succeeds + * and, using JSR 223 magic, makes an instance of this class from it. + *

+ * The script can make use of convenience methods implemented here, and also + * a number of items (such as a {@code runCommand} function) presupplied in the + * script engine's binding scope by + * {@link PGXSUtils#getScriptEngine PGXSUtils.getScriptEngine} and by + * {@link ScriptingMojo#execute ScriptingMojo.execute}). */ public abstract class AbstractPGXS { - /** - * Add instructions for compiling the pljava-so C files on your platform - * by implementing this method in your configuration block. + * Performs platform-specific compilation of a set of {@code .c} files with + * the specified compiler, target path, includes, defines, and flags. + *

+ * An implementation should make any needed adjustments to the includes, + * defines, and flags, format everything appropriately for the compiler + * in question, execute it, and return an exit status (zero on success). */ - public abstract int compile(String compiler, List files, Path targetPath, - List includes, Map defines, - List flags); + public abstract int compile( + String compiler, List files, Path targetPath, + List includes, Map defines, List flags); /** - * Add instructions for linking and producing the pljava-so shared library - * artifact on your platform by implementing this method in your - * configuration block. + * Performs platform-specific linking of a set of object files with + * the specified linker and flags, to produce the shared object at the + * specified target path. + *

+ * An implementation should make any needed adjustments to the flags, format + * everything appropriately for the linker in question, execute it, and + * return an exit status (zero on success). */ - public abstract int link(String linker, List flags, List files, Path targetPath); + public abstract int link( + String linker, List flags, List files, Path targetPath); /** * Returns a list with all items prefixed with correct include flag symbol. * * This is the default implementation for formatting the list of includes, - * and prefixes the includes with -I. For compilers like MSVC that require - * different symbols, override this method in your configuration block. + * and prefixes the includes with {@code -I}. For compilers like MSVC that + * require different formatting, the script should supply an overriding + * implementation of this method. */ public List formatIncludes(List includesList) { @@ -56,12 +82,13 @@ public List formatIncludes(List includesList) } /** - * Returns a list with all defines prefixed correctly. + * Returns a list with all defines represented correctly. * - * This is the default implementation for formatting the list of defines. - * Each item is prefixed with -D. If the define is associated with a value, - * adds equal symbol also followed by the value. If your linker expects a - * different format, override the method in your configuration block. + * This is the default implementation for formatting the map of defines. + * Each item is prefixed with {@code -D}. If the name is mapped to a + * non-null value, an {@code =} is appended, followed by the value. For + * compilers like MSVC that require different formatting, the script should + * supply an overriding implementation of this method. */ public List formatDefines(Map definesMap) { @@ -76,11 +103,11 @@ public List formatDefines(Map definesMap) } /** - * Returns the input pg_config property as a list of individual flags split - * at whitespace, except when quoted, and the quotes removed. + * Returns the requested {@code pg_config} property as a list of individual + * flags split at whitespace, except when quoted, and the quotes removed. *

* The assumed quoting convention is single straight quotes around regions - * to be protected, which do not have to be the entire argument. This method + * to be protected, which do not have to be an entire argument. This method * doesn't handle a value that contains a single quote as content; * the intended convention for that case doesn't seem to be documented, and * PostgreSQL's own build breaks in such a case, so there is little need, diff --git a/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/PGXSUtils.java b/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/PGXSUtils.java index 1c33c345b..bb216106b 100644 --- a/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/PGXSUtils.java +++ b/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/PGXSUtils.java @@ -46,7 +46,7 @@ import static java.lang.System.getProperty; import static java.util.stream.Collectors.joining; import static java.util.stream.Stream.iterate; -import static javax.script.ScriptContext.GLOBAL_SCOPE; +import static javax.script.ScriptContext.ENGINE_SCOPE; /** * Utility methods to simplify and hide the bland implementation details @@ -77,11 +77,47 @@ public PGXSUtils (MavenProject project, Log log) } /** - * Returns a ScriptEngine with some basic utilities for scripting. + * Returns a ScriptEngine with some useful engine-scoped bindings + * supplied for the convenience of the script. + *

+ * These bindings are placed in the engine's scope: + *

+ *
project
The Maven project instance + *
utils
This object + *
error, warn, info, debug
Consumers of {@link CharSequence} that + * will log a message through Maven with the corresponding severity + *
diag
A BiConsumer of {@link Diagnostic.Kind} and a + * {@link CharSequence}, to log a message through Maven with its severity + * determined by the {@code Diagnostic.Kind}. + *
runCommand
A function from {@link ProcessBuilder} to {@code int} + * that will run the specified command and return its exit status. The + * command and arguments are first logged through Maven at {@code debug} + * level. + *
runWindowsCRuntimeCommand
A function from {@link ProcessBuilder} + * to {@code int} that will apply the + * {@link #forWindowsCRuntime forWindowsCRuntime} transformation to the + * arguments and then run the specified command and return its exit + * status. The command and arguments are first logged through Maven at + * {@code debug} level, and before the transformation is applied. + *
buildPaths
Separates a list of pathnames into those that belong + * on a class path and those that belong on a module path. + *
getPgConfigProperty
Returns the output of {@code pg_config} when + * run with the given single argument. + *
isProfileActive
Predicate indicating whether a named Maven profile + * is active. + *
quoteStringForC
Transforms a {@code String} into a C string + * literal representing it. + *
resolve
A direct reference to the {code Path.resolve} overload + * with {@code Path} parameter types, to work around some versions of + * graaljs being unable to determine which overload a script intends. + *
setProjectProperty
Sets a property of the Maven project to a + * supplied value. + *
* * @param script the script block element in the configuration block of the - * plugin in the project - * @return ScriptEngine based on the engine and mime type provided in the + * plugin in the project object model. Its {@code mimetype} or + * {@code engine} attribute will be used to find a suitable engine + * @return ScriptEngine based on the engine and/or MIME type provided in the * script block */ ScriptEngine getScriptEngine(PlexusConfiguration script) @@ -111,8 +147,9 @@ ScriptEngine getScriptEngine(PlexusConfiguration script) " mimetype defined."); else { - ScriptEngineManager manager = new ScriptEngineManager( - new ScriptEngineLoader(ScriptingMojo.class.getClassLoader())); + ScriptEngineManager manager = + new ScriptEngineManager(new ScriptEngineLoader( + ScriptingMojo.class.getClassLoader())); if (engineName != null) engine = manager.getEngineByName(engineName); @@ -137,6 +174,15 @@ ScriptEngine getScriptEngine(PlexusConfiguration script) log.error(e); } + ScriptContext context = engine.getContext(); + + /* + * Give the script convenient access to the Maven project and this + * object. + */ + context.setAttribute("project", project, ENGINE_SCOPE); + context.setAttribute("utils", this, ENGINE_SCOPE); + /* * Give the script some convenient methods for logging to the Maven log. * Only supply the versions with one CharSequence parameter, in case of @@ -144,36 +190,14 @@ ScriptEngine getScriptEngine(PlexusConfiguration script) * have another way to get access to the Log instance and use its other * methods; these are just for convenience. */ - ScriptContext context = engine.getContext(); - context.setAttribute("debug", - (Consumer) log::debug, GLOBAL_SCOPE); context.setAttribute("error", - (Consumer) log::error, GLOBAL_SCOPE); + (Consumer) log::error, ENGINE_SCOPE); context.setAttribute("warn", - (Consumer) log::warn, GLOBAL_SCOPE); + (Consumer) log::warn, ENGINE_SCOPE); context.setAttribute("info", - (Consumer) log::info, GLOBAL_SCOPE); - context.setAttribute("isProfileActive", - (Function) this::isProfileActive, - GLOBAL_SCOPE); - context.setAttribute("buildPaths", - (Function, Map>) this::buildPaths, - GLOBAL_SCOPE); - - context.setAttribute("runCommand", - (ToIntFunction) b -> - { - log.debug("To run: " + b.command()); - return runCommand(b); - }, GLOBAL_SCOPE); - - context.setAttribute("runWindowsCRuntimeCommand", - (ToIntFunction) b -> - { - log.debug("To run (needs WindowsCRuntime transformation): " + - b.command()); - return runCommand(forWindowsCRuntime(b)); - }, GLOBAL_SCOPE); + (Consumer) log::info, ENGINE_SCOPE); + context.setAttribute("debug", + (Consumer) log::debug, ENGINE_SCOPE); /* * Also provide a specialized method useful for a script that may @@ -199,14 +223,62 @@ ScriptEngine getScriptEngine(PlexusConfiguration script) break; } } - ), GLOBAL_SCOPE); + ), ENGINE_SCOPE); /* - * Give the script convenient access to the Maven project and this - * object. + * Supply a runCommand function to which the script can supply + * a ProcessBuilder after configuring it as needed, and an alias + * runWindowsCRuntimeCommand that does the same, but applies the + * forWindowsCRuntime transformation to the ProcessBuilder's arguments + * first. Two aliases are used so that the command arguments can be + * logged (at debug level) in either case, and before the transformation + * is applied, in the Windows case. */ - context.setAttribute("project", project, GLOBAL_SCOPE); - context.setAttribute("utils", this, GLOBAL_SCOPE); + context.setAttribute("runCommand", + (ToIntFunction) b -> + { + log.debug("To run: " + b.command()); + return runCommand(b); + }, ENGINE_SCOPE); + + context.setAttribute("runWindowsCRuntimeCommand", + (ToIntFunction) b -> + { + log.debug("To run (needs WindowsCRuntime transformation): " + + b.command()); + return runCommand(forWindowsCRuntime(b)); + }, ENGINE_SCOPE); + + /* + * Convenient access to some other methods provided here. + */ + context.setAttribute("buildPaths", + (Function, Map>) this::buildPaths, + ENGINE_SCOPE); + + context.setAttribute("getPgConfigProperty", + (Function) p -> + { + try + { + return getPgConfigProperty(p); + } + catch ( Exception e ) + { + log.error(e); + return null; + } + }, ENGINE_SCOPE); + + context.setAttribute("isProfileActive", + (Function) this::isProfileActive, + ENGINE_SCOPE); + + context.setAttribute("quoteStringForC", + (Function) this::quoteStringForC, ENGINE_SCOPE); + + context.setAttribute("setProjectProperty", + (BiConsumer)this::setProjectProperty, ENGINE_SCOPE); /* * A graaljs bug (graalvm/graaljs#254) means that when you are passing @@ -215,7 +287,7 @@ ScriptEngine getScriptEngine(PlexusConfiguration script) * (Path,Path) function to make it a little more blindingly obvious. */ context.setAttribute("resolve", (BinaryOperator)Path::resolve, - GLOBAL_SCOPE); + ENGINE_SCOPE); return engine; } @@ -225,7 +297,7 @@ ScriptEngine getScriptEngine(PlexusConfiguration script) * escaped where appropriate using the C conventions. * * @param s string to be escaped - * @return a C compatible String enclosed in double quotes + * @return a C string literal representing s */ public String quoteStringForC (String s) { @@ -276,7 +348,8 @@ else if (-1 != m.start(2)) // things with specific escapes } /** - * Returns the string decoded from input bytes using default platform charset. + * Returns the string decoded from input bytes using default platform + * charset. * * @param bytes byte array to be decoded * @return string decoded from input bytes @@ -291,30 +364,27 @@ public String defaultCharsetDecodeStrict (byte[] bytes) } /** - * Returns the output, decoded using default platform charset, of the input - * command executed with the input argument. + * Returns the output, decoded using default platform charset, of the + * {@code pg_config} command executed with the single supplied argument. *

- * If the input parameter {@code pgConfigCommand} is empty or null, - * {@code pg_config} is used as the default value. If multiple version of - * {@code pg_config} are available or {@code pg_config} is not present on - * the path, consider passing an absolute path to {@code pg_config}. It is - * also recommended that only a single property be passed at a time. + * If multiple versions of {@code pg_config} are available or + * {@code pg_config} is not present on the path, the system property + * {@code pgsql.pgconfig} should be set as an absolute path to the desired + * executable. * - * @param pgConfigCommand pg_config command to execute * @param pgConfigArgument argument to be passed to the command * @return output of the input command executed with the input argument * @throws IOException if unable to read output of the command * @throws InterruptedException if command does not complete successfully */ - public String getPgConfigProperty (String pgConfigCommand, - String pgConfigArgument) + public String getPgConfigProperty (String pgConfigArgument) throws IOException, InterruptedException { - if (pgConfigCommand == null || pgConfigCommand.isEmpty()) - pgConfigCommand = "pg_config"; + String pgConfigCommand = + System.getProperty("pgsql.pgconfig", "pg_config"); - ProcessBuilder processBuilder = new ProcessBuilder(pgConfigCommand, - pgConfigArgument); + ProcessBuilder processBuilder = + new ProcessBuilder(pgConfigCommand, pgConfigArgument); processBuilder.redirectError(ProcessBuilder.Redirect.INHERIT); Process process = processBuilder.start(); process.getOutputStream().close(); @@ -365,8 +435,19 @@ public void reportPostgreSQLVersion(String includedir_server) } /** - * Returns a ProcessBuilder with suitable defaults and arguments added from - * input function. + * Sets the value of a property for the current project. + * + * @param property key to use for property + * @param value the value of property to set + */ + public void setProjectProperty (String property, String value) + { + project.getProperties().setProperty(property, value); + } + + /** + * Returns a ProcessBuilder with suitable defaults and arguments added + * by the supplied consumer. * * @param consumer function which adds arguments to the ProcessBuilder * @return ProcessBuilder with input arguments and suitable defaults @@ -408,7 +489,7 @@ public int runCommand(ProcessBuilder processBuilder) * Returns true if the profile with given name exists and is active, false * otherwise. *

- * A warning is logged if the no profile with the input name exists in the + * A warning is logged if no profile with the input name exists in the * current project. * * @param profileName name of profile to check @@ -431,8 +512,13 @@ public boolean isProfileActive(String profileName) } /** - * Returns a map with two elements with {@code classpath} and {@code modulepath} - * as keys and their joined string paths as the respective values. + * Returns a two-element map with with {@code classpath} and + * {@code modulepath} as keys and their joined string paths as the + * respective values. + *

+ * For each supplied element, + * {@link #shouldPlaceOnModulepath shouldPlaceOnModulepath} is used to + * determine which path the element is added to. * * @param elements list of elements to build classpath and modulepath from * @return a map containing the {@code classpath} and {@code modulepath} @@ -449,7 +535,8 @@ public Map buildPaths(List elements) { if (element.contains(pathSeparator)) log.warn(String.format("cannot add %s to path because " + - "it contains path separator %s", element, pathSeparator)); + "it contains path separator %s", + element, pathSeparator)); else if (shouldPlaceOnModulepath(element)) modulepathElements.add(element); else @@ -469,11 +556,13 @@ else if (shouldPlaceOnModulepath(element)) * Returns true if the element should be placed on the module path. *

* An file path element should be placed on the module path if it points to - * 1) a directory with a top level {@code module-info.class} file - * 2) a {@code JAR} file having a {@code module-info.class} entry or the + *

    + *
  1. a directory with a top level {@code module-info.class} file + *
  2. a {@code JAR} file having a {@code module-info.class} entry or the * {@code Automatic-Module-Name} as a manifest attribute + *
* - * @param filePath the filepath to check whether is a module + * @param filePath the filepath to check * @return true if input path should go on modulepath, false otherwise * @throws IOException any thrown by the underlying file operations */ @@ -504,9 +593,10 @@ public boolean shouldPlaceOnModulepath(String filePath) } /** - * Returns the list of files with given extension in the input directory. + * Returns a list of files with given extension in and below + * the input directory. * - * @param sourceDirectory to list files inside + * @param sourceDirectory root of the tree of files to list * @param extension to filter files to be selected * @return list of strings of absolute paths of files */ @@ -529,9 +619,9 @@ public List getFilesWithExtension(Path sourceDirectory, } /* - * The same method is duplicated in pljava-packaging/Node.java . While making - * changes to this method, review the other occurrence also and replicate the - * changes there if desirable. + * This method is duplicated in pljava-packaging/Node.java. If making + * changes to this method, review the other occurrence also and replicate + * the changes there if desirable. */ /** * Adjust the command arguments of a {@code ProcessBuilder} so that they @@ -539,7 +629,7 @@ public List getFilesWithExtension(Path sourceDirectory, * the argument parsing algorithm of the usual C run-time code, when it is * known that the command will not be handled first by {@code cmd}. *

- * This transformation must account for the way the C runtime will + * This transformation must account for the way the Windows C runtime will * ultimately parse the parameters apart, and also for the behavior of * Java's runtime in assembling the command line that the invoked process * will receive. @@ -548,7 +638,7 @@ public List getFilesWithExtension(Path sourceDirectory, * should result from parsing. * @return The same ProcessBuilder, with the argument list rewritten as * necessary to produce the original list as a result of Windows C runtime - * parsing, + * parsing. * @throws IllegalArgumentException if the ProcessBuilder does not have at * least the first command element (the executable to run) * @throws UnsupportedOperationException if the arguments passed, or system diff --git a/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/ReportScriptingMojo.java b/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/ReportScriptingMojo.java index 868bcaaaf..4990daa1a 100644 --- a/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/ReportScriptingMojo.java +++ b/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/ReportScriptingMojo.java @@ -27,13 +27,14 @@ import java.util.Locale; /** - * Maven plugin goal to use JavaScript for configuring + * Maven plugin goal to use JavaScript (or another JSR 223 script engine) + * for configuring * {@link org.apache.maven.reporting.MavenReport} during the * {@link LifecyclePhase#SITE}. *

- * This plugin goal intends to allow the use of JavaScript during {@code SITE} + * This plugin goal intends to allow the use of scripting in the {@code SITE} * lifecycle phase with the help of {@link ReportScript}. The motivation behind - * this is the inability to use Maven AntRun during {@code SITE} phase. + * this is the inability to use Maven AntRun in the {@code SITE} phase. */ @Mojo(name = "scripted-report") @Execute(phase = LifecyclePhase.NONE) @@ -80,15 +81,17 @@ private void setReportScript() } /** - * Returns the path relative to the target site directory of the this report. + * Queries the script for the report output path relative to the target site + * directory. + *

* This value will be used by {@code Maven} to provide a link to the report * from {@code index.html}. *

* Calls {@code setReportScript} to ensure that the instance of * {@link ReportScript} is available. Invokes - * {@code fun getOutputName(report)} defined in the JavaScript snippet - * associated with the report. No default implementation is provided. User - * must implement the method in JavaScript. + * {@code getOutputName(report)} defined by the script snippet + * associated with the report. No default implementation is provided; the + * script must implement this method. */ @Override public String getOutputName () @@ -98,15 +101,15 @@ public String getOutputName () } /** - * Returns false if this report will produce output through a - * supplied {@link Sink}, true if it is 'external', producing its output - * some other way. + * Queries the script to return false if this report will produce output + * through a supplied {@link Sink}, or true if it is 'external', producing + * its output some other way. *

* Calls {@code setReportScript} to ensure that the instance of * {@link ReportScript} is available. Invokes - * {@code fun isExternalReport(report)} if defined in the javascript - * snippet associated with the report. Otherwise, the {@code super} - * implementation is invoked effectively. + * {@code isExternalReport(report)} if defined in the script + * snippet associated with the report. Otherwise, the implementation + * inherited by this class is effectively invoked. */ @Override public boolean isExternalReport () @@ -116,14 +119,14 @@ public boolean isExternalReport () } /** - * Returns the name of this report used by {@code Maven} for displaying in - * {@code index.html}. + * Queries the script for the name of this report to be used + * by {@code Maven} for display in {@code index.html}. *

* Calls {@code setReportScript} to ensure that the instance of * {@link ReportScript} is available. Invokes - * {@code fun getName(report, locale)} defined in the javascript - * snippet associated with the report. No default implementation is provided - * . User must implement the method in javascript. + * {@code getName(report, locale)} defined by the script + * snippet associated with the report. No default implementation is + * provided; the script must implement this method. */ @Override public String getName (Locale locale) @@ -133,14 +136,14 @@ public String getName (Locale locale) } /** - * Returns the description of this report, used by {@code Maven} to display - * report description in {@code index.html}. + * Queries the script for the description of this report, to be used + * by {@code Maven} for display in {@code index.html}. *

* Calls {@code setReportScript} to ensure that the instance of * {@link ReportScript} is available. Invokes - * {@code fun getDescription(report, locale)} defined in the javascript - * snippet associated with the report. No default implementation is provided - * . User must implement the method in javascript. + * {@code getDescription(report, locale)} defined in the script + * snippet associated with the report. No default implementation is + * provided; the script must implement this method. */ @Override public String getDescription (Locale locale) @@ -150,14 +153,15 @@ public String getDescription (Locale locale) } /** - * Returns the category name of this report, used by {@code Maven} to display - * the report under the correct in {@code index.html}. + * Queries the script for the category name of this report, used + * by {@code Maven} to place the report under the correct heading + * in {@code index.html}. *

* Calls {@code setReportScript} to ensure that the instance of * {@link ReportScript} is available. Invokes - * {@code fun getCategoryName(report)} if defined in the javascript - * snippet associated with the report. Otherwise, the {@code super} - * implementation is invoked effectively. + * {@code getCategoryName(report)} if defined by the script + * snippet associated with the report. Otherwise, the implementation + * inherited by this class is effectively invoked. */ @Override public String getCategoryName () @@ -167,13 +171,13 @@ public String getCategoryName () } /** - * Returns true if a report can be generated, false otherwise. + * Queries the script as to whether this report can be generated. *

* Calls {@code setReportScript} to ensure that the instance of * {@link ReportScript} is available. Invokes - * {@code fun canGenerateReport(report)} if defined in the javascript - * snippet. Otherwise, the {@code super} implementation is invoked - * effectively. + * {@code canGenerateReport(report)} if defined by the script + * snippet. Otherwise, the implementation inherited by this class is + * effectively invoked. */ @Override public boolean canGenerateReport () @@ -186,9 +190,9 @@ public boolean canGenerateReport () * {@inheritDoc} *

* Calls {@code setReportScript} to ensure that the instance of - * {@link ReportScript} is available. Invokes the - * {@code fun executeReport(report, locale)} with the instance of the - * current report. + * {@link ReportScript} is available. Invokes its + * {@code executeReport(report, locale)}, passing this instance and + * the supplied locale. */ @Override protected void executeReport (Locale locale) throws MavenReportException @@ -229,7 +233,7 @@ public String getOutputEncoding () /** * Default implementation for * {@link ReportScript#isExternalReport(ReportScriptingMojo)}. Invoked if - * {@code fun isExternalReport(report)} is not defined in the javascript + * {@code isExternalReport(report)} is not defined in the script * snippet associated with the report. */ boolean isExternalReportDefault () @@ -240,7 +244,7 @@ boolean isExternalReportDefault () /** * Default implementation of * {@link ReportScript#getCategoryName(ReportScriptingMojo)}. Invoked if - * {@code fun getCategoryName(report)} is not defined in the javascript + * {@code getCategoryName(report)} is not defined in the script * snippet associated with the report. */ String getCategoryNameDefault () @@ -251,7 +255,7 @@ String getCategoryNameDefault () /** * Default implementation of * {@link ReportScript#canGenerateReport(ReportScriptingMojo)}. Invoked if - * {@code fun canGenerateReport(report)} is not defined in the javascript + * {@code canGenerateReport(report)} is not defined in the script * snippet associated with the report. */ boolean canGenerateReportDefault () @@ -263,16 +267,17 @@ boolean canGenerateReportDefault () * Wraps the input object in a {@link MavenReportException}. * * The exception returned is constructed as follows: - * 1) If {@code object} is null, the exception message indicates the same. - * 2) If {@code object} is already a {@link MavenReportException}, return it - * as is. - * 3) If {@code object} is any other {@link Throwable}, set it as the cause - * for the exception. - * {@link MavenReportException} with {@code object} as its cause. - * 4) If {@code object} is a {@link String}, set it as the message of the - * exception. - * 5) For all other case, the message of the exception is set in this format - * , Class Name of object: String representation of object. + *

    + *
  • If {@code object} is null, the exception message indicates the same. + *
  • If {@code object} is already a {@link MavenReportException}, it is + * returned as is. + *
  • If {@code object} is any other {@link Throwable}, it is used as + * the wrapping exception's cause. + *
  • If {@code object} is a {@link String}, it is used as + * the wrapping exception's message. + *
  • If it is any other object, the wrapping exception's message is set in + * this format: Class mame of object: String representation of object. + *
* * @param object to wrap in MavenReportException * @return object wrapped inside a {@link MavenReportException} @@ -286,7 +291,8 @@ else if (object instanceof MavenReportException) else if (object instanceof Throwable) { Throwable t = (Throwable) object; - MavenReportException exception = new MavenReportException(t.getMessage()); + MavenReportException exception = + new MavenReportException(t.getMessage()); exception.initCause(t); return exception; } diff --git a/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/ScriptingMojo.java b/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/ScriptingMojo.java index 289d707bf..489f679bd 100644 --- a/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/ScriptingMojo.java +++ b/pljava-pgxs/src/main/java/org/postgresql/pljava/pgxs/ScriptingMojo.java @@ -25,18 +25,20 @@ import org.codehaus.plexus.configuration.PlexusConfiguration; import javax.script.Invocable; -import javax.script.ScriptContext; import javax.script.ScriptEngine; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Function; +import static javax.script.ScriptContext.ENGINE_SCOPE; + /** - * Maven plugin goal to use JavaScript during any of build lifecycle phases. + * Maven plugin goal to use JavaScript (or another JSR 223 script engine) + * during any of build lifecycle phases. *

- * The Mojo provides a limited subset of the functionality provided Maven AntRun - * Plugin. This is intentional to simplify usage as this maven plugin is - * specifically targeted at building Pl/Java native code. + * The Mojo provides a limited subset of the functionality of the Maven AntRun + * Plugin. This is intentional to simplify usage, as this Maven plugin is + * specifically targeted at building PL/Java native code. */ @Mojo(name = "scripted-goal", defaultPhase = LifecyclePhase.COMPILE, requiresDependencyResolution = ResolutionScope.TEST) @@ -55,8 +57,17 @@ public class ScriptingMojo extends AbstractMojo private PGXSUtils utils; /** - * Executes the javascript code inside {@code script} tag inside plugin + * Executes the script code inside the {@code script} tag in the plugin * configuration. + *

+ * Uses {@link PGXSUtils#getScriptEngine PGXSUtils.getScriptEngine} + * to instantiate the engine, and then makes these items available in + * the engine's scope (in addition to those placed there by + * {@link PGXSUtils#getScriptEngine getScriptEngine} itself): + *

+ *
session
The Maven session object + *
plugin
This object + *
*/ @Override public void execute () throws MojoExecutionException, MojoFailureException @@ -67,19 +78,14 @@ public void execute () throws MojoExecutionException, MojoFailureException String scriptText = script.getValue(); ScriptEngine engine = utils.getScriptEngine(script); - engine.getContext().setAttribute("session", session, - ScriptContext.GLOBAL_SCOPE); - engine.getContext().setAttribute("plugin", this, - ScriptContext.GLOBAL_SCOPE); - engine.put("quoteStringForC", - (Function) utils::quoteStringForC); - engine.put("setProjectProperty", - (BiConsumer) this::setProjectProperty); - engine.put("getPgConfigProperty", - (Function) this::getPgConfigProperty); + engine.getContext().setAttribute("session", session, ENGINE_SCOPE); + engine.getContext().setAttribute("plugin", this, ENGINE_SCOPE); + engine.eval(scriptText); - GoalScript goal = ((Invocable) engine).getInterface(GoalScript.class); + GoalScript goal = + ((Invocable) engine).getInterface(GoalScript.class); + AbstractMojoExecutionException exception = goal.execute(); if (exception != null) throw exception; @@ -95,63 +101,33 @@ public void execute () throws MojoExecutionException, MojoFailureException } /** - * Sets the value of a property for the current project. - * - * @param property key to use for property - * @param value the value of property to set - */ - public void setProjectProperty (String property, String value) - { - project.getProperties().setProperty(property, value); - } - - /** - * Returns the value of a pg_config property. - * - * @param property property whose value is to be retrieved from pg_config - * @return output of pg_config executed with the input property as argument - */ - public String getPgConfigProperty (String property) - { - try - { - String pgConfigCommand = System.getProperty("pgsql.pgconfig"); - return utils.getPgConfigProperty(pgConfigCommand, property); - } - catch (Exception e) - { - getLog().error(e); - return null; - } - } - - /** - * Wraps the input object in a {@link AbstractMojoExecutionException}. + * Wraps the input object in an {@link AbstractMojoExecutionException}. * * The returned exception is constructed as follows: - * 1) If {@code object} is null, then {@link MojoExecutionException} is used - * to wrap and the message indicates that null value was thrown by the script. - * 2) If {@code object} is already a {@link MojoExecutionException}, return - * it as is. - * 3) If {@code object} is already a {@link MojoFailureException}, return it - * as is. - * - * For the steps, below the wrapping exception is chosen according to the - * the value of {@code scriptFailure} parameter. - * - * 4) If {@code object} is any other {@link Throwable}, set it as the cause - * for the exception. - * 5) If {@code object} is a {@link String}, set it as the message of the - * exception. - * 6) For all other case, the message of the exception is set in this format - * , Class Name of object: String representation of object. + *
    + *
  • If {@code object} is null, then {@link MojoExecutionException} is + * used to wrap and the message indicates that a null value was thrown + * by the script. + *
  • If {@code object} is already a {@link MojoExecutionException}, it is + * returned as is. + *
  • If {@code object} is already a {@link MojoFailureException}, it is + * returned as is. + *
  • For the steps below, the wrapping exception is chosen according to + * the value of the {@code scriptFailure} parameter. + *
  • If {@code object} is any other {@link Throwable}, set it as + * the wrapping exception's cause. + *
  • If {@code object} is a {@link String}, set it as the wrapping + * exception's message. + *
  • For any other object, the message of the exception is set in + * this format: Class name of object: String representation of object. + *
* - * @param object to wrap in AbstractMojoExecutionException - * @param scriptFailure if true, use a MojoExecutionException for wrapping - * otherwise use MojoFailureException. this parameter - * is ignored, if the object is null or instance of + * @param object an object to wrap in an AbstractMojoExecutionException + * @param scriptFailure if true, use a MojoExecutionException for wrapping, + * otherwise use MojoFailureException. This parameter + * is ignored if the object is null or an instance of * MojoExecutionException or MojoFailureException - * @return object wrapped inside a {@link AbstractMojoExecutionException} + * @return object wrapped inside an {@link AbstractMojoExecutionException} */ public AbstractMojoExecutionException exceptionWrap(Object object, boolean scriptFailure)