This is an automated email from the ASF dual-hosted git repository. rfscholte pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/maven-jlink-plugin.git
The following commit(s) were added to refs/heads/master by this push: new f8bdf50 Java 9+ ToolProvider via multi-release jar. f8bdf50 is described below commit f8bdf5050c266854524aaa51eb36109c00ca692a Author: Benjamin Marwell <bmarw...@apache.org> AuthorDate: Tue Nov 24 18:53:35 2020 +0100 Java 9+ ToolProvider via multi-release jar. Signed-off-by: rfscholte <rfscho...@apache.org> --- pom.xml | 47 +++++ .../MJLINK-36_toolchainjdk8/invoker.properties | 20 ++ src/it/projects/MJLINK-36_toolchainjdk8/pom.xml | 95 ++++++++++ .../MJLINK-36_toolchainjdk9/invoker.properties | 20 ++ src/it/projects/MJLINK-36_toolchainjdk9/pom.xml | 95 ++++++++++ .../MJLINK-40_includeLocales/verify.groovy | 16 +- .../maven/plugins/jlink/AbstractJLinkExecutor.java | 63 +++++++ .../maven/plugins/jlink/AbstractJLinkMojo.java | 154 +--------------- .../jlink/AbstractJLinkToolchainExecutor.java | 204 +++++++++++++++++++++ .../apache/maven/plugins/jlink/JLinkExecutor.java} | 41 ++--- .../org/apache/maven/plugins/jlink/JLinkMojo.java | 160 ++++++---------- .../apache/maven/plugins/jlink/JLinkExecutor.java | 143 +++++++++++++++ 12 files changed, 772 insertions(+), 286 deletions(-) diff --git a/pom.xml b/pom.xml index da94e29..45e7acc 100644 --- a/pom.xml +++ b/pom.xml @@ -199,6 +199,53 @@ </reporting> <profiles> <profile> + <id>jdk9</id> + <activation> + <jdk>[9,)</jdk> + </activation> + <build> + <pluginManagement> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <executions> + <execution> + <id>jdk9</id> + <goals> + <goal>compile</goal> + </goals> + <configuration> + <release>9</release> + <multiReleaseOutput>true</multiReleaseOutput> + <compileSourceRoots> + <compileSourceRoot>${project.basedir}/src/main/java9</compileSourceRoot> + </compileSourceRoots> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <executions> + <execution> + <id>default-jar</id> + <configuration> + <archive> + <manifestEntries> + <Multi-Release>true</Multi-Release> + </manifestEntries> + </archive> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </pluginManagement> + </build> + </profile> + <profile> <id>run-its</id> <build> <plugins> diff --git a/src/it/projects/MJLINK-36_toolchainjdk8/invoker.properties b/src/it/projects/MJLINK-36_toolchainjdk8/invoker.properties new file mode 100644 index 0000000..d3282c7 --- /dev/null +++ b/src/it/projects/MJLINK-36_toolchainjdk8/invoker.properties @@ -0,0 +1,20 @@ +# 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. + +invoker.java.version = 1.8 +invoker.goals = clean install +invoker.toolchain.jdk.version = 11 diff --git a/src/it/projects/MJLINK-36_toolchainjdk8/pom.xml b/src/it/projects/MJLINK-36_toolchainjdk8/pom.xml new file mode 100644 index 0000000..f6a37cd --- /dev/null +++ b/src/it/projects/MJLINK-36_toolchainjdk8/pom.xml @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- + 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. +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>org.apache.maven.plugins.jlink.its</groupId> + <artifactId>mjlink36-toolchain-jdk8</artifactId> + <version>1.0.0-SNAPSHOT</version> + <packaging>jlink</packaging> + + <properties> + <maven.compiler.source>1.9</maven.compiler.source> + <maven.compiler.target>1.9</maven.compiler.target> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + + <dependencies> + <dependency> + <!-- use a dependency with a module-info.class --> + <artifactId>asm</artifactId> + <groupId>org.ow2.asm</groupId> + <version>6.0</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-toolchains-plugin</artifactId> + <version>1.1</version> + <executions> + <execution> + <goals> + <goal>toolchain</goal> + </goals> + </execution> + </executions> + <configuration> + <toolchains> + <jdk> + <version>11</version> + </jdk> + </toolchains> + </configuration> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jlink-plugin</artifactId> + <version>@project.version@</version> + <extensions>true</extensions> + <configuration> + <noHeaderFiles>true</noHeaderFiles> + <noManPages>true</noManPages> + <verbose>true</verbose> + </configuration> + </plugin> + </plugins> + <pluginManagement> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.8.0</version> + <configuration> + <source>${maven.compiler.source}</source> + <target>${maven.compiler.target}</target> + </configuration> + </plugin> + </plugins> + </pluginManagement> + </build> + +</project> diff --git a/src/it/projects/MJLINK-36_toolchainjdk9/invoker.properties b/src/it/projects/MJLINK-36_toolchainjdk9/invoker.properties new file mode 100644 index 0000000..271bf45 --- /dev/null +++ b/src/it/projects/MJLINK-36_toolchainjdk9/invoker.properties @@ -0,0 +1,20 @@ +# 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. + +invoker.java.version = 9+ +invoker.goals = clean install +invoker.toolchain.jdk.version = 11 diff --git a/src/it/projects/MJLINK-36_toolchainjdk9/pom.xml b/src/it/projects/MJLINK-36_toolchainjdk9/pom.xml new file mode 100644 index 0000000..ab371a9 --- /dev/null +++ b/src/it/projects/MJLINK-36_toolchainjdk9/pom.xml @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<!-- + 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. +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + <groupId>org.apache.maven.plugins.jlink.its</groupId> + <artifactId>mjlink36-toolchain-jdk9</artifactId> + <version>1.0.0-SNAPSHOT</version> + <packaging>jlink</packaging> + + <properties> + <maven.compiler.source>1.9</maven.compiler.source> + <maven.compiler.target>1.9</maven.compiler.target> + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> + </properties> + + <dependencies> + <dependency> + <!-- use a dependency with a module-info.class --> + <artifactId>asm</artifactId> + <groupId>org.ow2.asm</groupId> + <version>6.0</version> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-toolchains-plugin</artifactId> + <version>1.1</version> + <executions> + <execution> + <goals> + <goal>toolchain</goal> + </goals> + </execution> + </executions> + <configuration> + <toolchains> + <jdk> + <version>11</version> + </jdk> + </toolchains> + </configuration> + </plugin> + + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jlink-plugin</artifactId> + <version>@project.version@</version> + <extensions>true</extensions> + <configuration> + <noHeaderFiles>true</noHeaderFiles> + <noManPages>true</noManPages> + <verbose>true</verbose> + </configuration> + </plugin> + </plugins> + <pluginManagement> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.8.0</version> + <configuration> + <source>${maven.compiler.source}</source> + <target>${maven.compiler.target}</target> + </configuration> + </plugin> + </plugins> + </pluginManagement> + </build> + +</project> diff --git a/src/it/projects/MJLINK-40_includeLocales/verify.groovy b/src/it/projects/MJLINK-40_includeLocales/verify.groovy index 1887547..c32508b 100644 --- a/src/it/projects/MJLINK-40_includeLocales/verify.groovy +++ b/src/it/projects/MJLINK-40_includeLocales/verify.groovy @@ -26,21 +26,15 @@ boolean result = false; try { - File target = new File( basedir, "target" ); - if ( !target.exists() || !target.isDirectory() ) + File buildLog = new File( basedir, "build.log" ); + if ( !buildLog.exists() || buildLog.isDirectory() ) { - System.err.println( "target file is missing or not a directory." ); - return false; - } - File jlinkArgs = new File( target, "jlinkArgs" ); - if ( !jlinkArgs.exists() || jlinkArgs.isDirectory() ) - { - System.err.println( "jlinkArgs file is missing or is a directory." ); + System.err.println( "build.log file is missing or is a directory." ); return false; } - def line = jlinkArgs.eachLine { line -> - if (line.equals('en,ja,de,*-IN')) + def line = buildLog.eachLine { line -> + if (line.contains('--include-locales, en,ja,de,*-IN')) { result = true; } diff --git a/src/main/java/org/apache/maven/plugins/jlink/AbstractJLinkExecutor.java b/src/main/java/org/apache/maven/plugins/jlink/AbstractJLinkExecutor.java new file mode 100644 index 0000000..3213736 --- /dev/null +++ b/src/main/java/org/apache/maven/plugins/jlink/AbstractJLinkExecutor.java @@ -0,0 +1,63 @@ +package org.apache.maven.plugins.jlink; + +/* + * 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. + */ + +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.logging.Log; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +abstract class AbstractJLinkExecutor +{ + protected static final String JMODS = "jmods"; + + private final Log log; + + private final List<String> modulesToAdd = new ArrayList<>(); + private final List<String> modulePaths = new ArrayList<>(); + + AbstractJLinkExecutor( Log log ) + { + this.log = log; + } + + protected Log getLog() + { + return this.log; + } + + public abstract Optional<File> getJmodsFolder( /* nullable */ File sourceJdkModules ); + + public abstract int executeJlink( List<String> jlinkArgs ) throws MojoExecutionException; + + public void addAllModules( Collection<String> modulesToAdd ) + { + this.modulesToAdd.addAll( modulesToAdd ); + } + + public void addAllModulePaths( Collection<String> pathsOfModules ) + { + this.modulePaths.addAll( pathsOfModules ); + } +} diff --git a/src/main/java/org/apache/maven/plugins/jlink/AbstractJLinkMojo.java b/src/main/java/org/apache/maven/plugins/jlink/AbstractJLinkMojo.java index 39e32ac..74bef13 100644 --- a/src/main/java/org/apache/maven/plugins/jlink/AbstractJLinkMojo.java +++ b/src/main/java/org/apache/maven/plugins/jlink/AbstractJLinkMojo.java @@ -20,26 +20,19 @@ package org.apache.maven.plugins.jlink; */ import java.io.File; -import java.io.IOException; import java.lang.reflect.Method; import java.util.Collection; import java.util.List; import java.util.Map; -import java.util.Properties; +import java.util.Optional; -import org.apache.commons.lang3.SystemUtils; import org.apache.maven.execution.MavenSession; import org.apache.maven.plugin.AbstractMojo; -import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugins.annotations.Component; import org.apache.maven.plugins.annotations.Parameter; import org.apache.maven.project.MavenProject; import org.apache.maven.toolchain.Toolchain; import org.apache.maven.toolchain.ToolchainManager; -import org.codehaus.plexus.util.StringUtils; -import org.codehaus.plexus.util.cli.CommandLineException; -import org.codehaus.plexus.util.cli.CommandLineUtils; -import org.codehaus.plexus.util.cli.Commandline; /** * @author Karl Heinz Marbaise <a href="mailto:khmarba...@apache.org">khmarba...@apache.org</a> @@ -66,139 +59,12 @@ public abstract class AbstractJLinkMojo @Component private ToolchainManager toolchainManager; - protected String getJLinkExecutable() - throws IOException + protected JLinkExecutor getJlinkExecutor() { - Toolchain tc = getToolchain(); - - String jLinkExecutable = null; - if ( tc != null ) - { - jLinkExecutable = tc.findTool( "jlink" ); - } - - // TODO: Check if there exist a more elegant way? - String jLinkCommand = "jlink" + ( SystemUtils.IS_OS_WINDOWS ? ".exe" : "" ); - - File jLinkExe; - - if ( StringUtils.isNotEmpty( jLinkExecutable ) ) - { - jLinkExe = new File( jLinkExecutable ); - - if ( jLinkExe.isDirectory() ) - { - jLinkExe = new File( jLinkExe, jLinkCommand ); - } - - if ( SystemUtils.IS_OS_WINDOWS && jLinkExe.getName().indexOf( '.' ) < 0 ) - { - jLinkExe = new File( jLinkExe.getPath() + ".exe" ); - } - - if ( !jLinkExe.isFile() ) - { - throw new IOException( "The jlink executable '" + jLinkExe + "' doesn't exist or is not a file." ); - } - return jLinkExe.getAbsolutePath(); - } - - // ---------------------------------------------------------------------- - // Try to find jlink from System.getProperty( "java.home" ) - // By default, System.getProperty( "java.home" ) = JRE_HOME and JRE_HOME - // should be in the JDK_HOME - // ---------------------------------------------------------------------- - jLinkExe = new File( SystemUtils.getJavaHome() + File.separator + ".." + File.separator + "bin", jLinkCommand ); - - // ---------------------------------------------------------------------- - // Try to find javadocExe from JAVA_HOME environment variable - // ---------------------------------------------------------------------- - if ( !jLinkExe.exists() || !jLinkExe.isFile() ) - { - Properties env = CommandLineUtils.getSystemEnvVars(); - String javaHome = env.getProperty( "JAVA_HOME" ); - if ( StringUtils.isEmpty( javaHome ) ) - { - throw new IOException( "The environment variable JAVA_HOME is not correctly set." ); - } - if ( !new File( javaHome ).getCanonicalFile().exists() - || new File( javaHome ).getCanonicalFile().isFile() ) - { - throw new IOException( "The environment variable JAVA_HOME=" + javaHome - + " doesn't exist or is not a valid directory." ); - } - - jLinkExe = new File( javaHome + File.separator + "bin", jLinkCommand ); - } - - if ( !jLinkExe.getCanonicalFile().exists() || !jLinkExe.getCanonicalFile().isFile() ) - { - throw new IOException( "The jlink executable '" + jLinkExe - + "' doesn't exist or is not a file. Verify the JAVA_HOME environment variable." ); - } - - return jLinkExe.getAbsolutePath(); - } - - protected void executeCommand( Commandline cmd, File outputDirectory ) - throws MojoExecutionException - { - if ( getLog().isDebugEnabled() ) - { - // no quoted arguments ??? - getLog().debug( CommandLineUtils.toString( cmd.getCommandline() ).replaceAll( "'", "" ) ); - } - - CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer(); - CommandLineUtils.StringStreamConsumer out = new CommandLineUtils.StringStreamConsumer(); - try - { - int exitCode = CommandLineUtils.executeCommandLine( cmd, out, err ); - - String output = ( StringUtils.isEmpty( out.getOutput() ) ? null : '\n' + out.getOutput().trim() ); - - if ( exitCode != 0 ) - { - - if ( StringUtils.isNotEmpty( output ) ) - { - // Reconsider to use WARN / ERROR ? - // getLog().error( output ); - for ( String outputLine : output.split( "\n" ) ) - { - getLog().error( outputLine ); - } - } - - StringBuilder msg = new StringBuilder( "\nExit code: " ); - msg.append( exitCode ); - if ( StringUtils.isNotEmpty( err.getOutput() ) ) - { - msg.append( " - " ).append( err.getOutput() ); - } - msg.append( '\n' ); - msg.append( "Command line was: " ).append( cmd ).append( '\n' ).append( '\n' ); - - throw new MojoExecutionException( msg.toString() ); - } - - if ( StringUtils.isNotEmpty( output ) ) - { - //getLog().info( output ); - for ( String outputLine : output.split( "\n" ) ) - { - getLog().info( outputLine ); - } - } - } - catch ( CommandLineException e ) - { - throw new MojoExecutionException( "Unable to execute jlink command: " + e.getMessage(), e ); - } - + return new JLinkExecutor( getToolchain().orElse( null ), getLog() ); } - protected Toolchain getToolchain() + protected Optional<Toolchain> getToolchain() { Toolchain tc = null; @@ -207,12 +73,12 @@ public abstract class AbstractJLinkMojo // Maven 3.3.1 has plugin execution scoped Toolchain Support try { - Method getToolchainsMethod = toolchainManager.getClass().getMethod( "getToolchains", MavenSession.class, - String.class, Map.class ); + Method getToolchainsMethod = toolchainManager.getClass().getMethod( "getToolchains", + MavenSession.class, String.class, Map.class ); @SuppressWarnings( "unchecked" ) - List<Toolchain> tcs = - (List<Toolchain>) getToolchainsMethod.invoke( toolchainManager, session, "jdk", jdkToolchain ); + List<Toolchain> tcs = (List<Toolchain>) getToolchainsMethod.invoke( toolchainManager, getSession(), + "jdk", jdkToolchain ); if ( tcs != null && tcs.size() > 0 ) { @@ -236,10 +102,10 @@ public abstract class AbstractJLinkMojo if ( tc == null ) { // TODO: Check if we should make the type configurable? - tc = toolchainManager.getToolchainFromBuildContext( "jdk", session ); + tc = toolchainManager.getToolchainFromBuildContext( "jdk", getSession() ); } - return tc; + return Optional.ofNullable( tc ); } protected MavenProject getProject() diff --git a/src/main/java/org/apache/maven/plugins/jlink/AbstractJLinkToolchainExecutor.java b/src/main/java/org/apache/maven/plugins/jlink/AbstractJLinkToolchainExecutor.java new file mode 100644 index 0000000..a38a9c7 --- /dev/null +++ b/src/main/java/org/apache/maven/plugins/jlink/AbstractJLinkToolchainExecutor.java @@ -0,0 +1,204 @@ +package org.apache.maven.plugins.jlink; + +/* + * 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. + */ + +import org.apache.commons.lang3.SystemUtils; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.shared.utils.cli.CommandLineException; +import org.apache.maven.shared.utils.cli.CommandLineUtils; +import org.apache.maven.shared.utils.cli.Commandline; +import org.apache.maven.toolchain.Toolchain; +import org.codehaus.plexus.util.StringUtils; + +import java.io.File; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.Optional; + +abstract class AbstractJLinkToolchainExecutor extends AbstractJLinkExecutor +{ + private final Toolchain toolchain; + + AbstractJLinkToolchainExecutor( Toolchain toolchain, Log log ) + { + super( log ); + this.toolchain = toolchain; + } + + protected Optional<Toolchain> getToolchain() + { + return Optional.ofNullable( this.toolchain ); + } + + /** + * Execute JLink via toolchain. + * + * @return the exit code ({@code 0} on success). + */ + @Override + public int executeJlink( List<String> jlinkArgs ) throws MojoExecutionException + { + File jlinkExecutable = getJlinkExecutable(); + getLog().info( "Toolchain in maven-jlink-plugin: jlink [ " + jlinkExecutable + " ]" ); + + Commandline cmd = createJLinkCommandLine( jlinkArgs ); + cmd.setExecutable( jlinkExecutable.getAbsolutePath() ); + + return executeCommand( cmd ); + } + + private File getJlinkExecutable() + { + return new File( getJLinkExecutable() ); + } + + @Override + public Optional<File> getJmodsFolder( /* nullable */ File sourceJdkModules ) + { + // Really Hacky...do we have a better solution to find the jmods directory of the JDK? + File jLinkParent = getJlinkExecutable().getParentFile().getParentFile(); + File jmodsFolder; + if ( sourceJdkModules != null && sourceJdkModules.isDirectory() ) + { + jmodsFolder = new File( sourceJdkModules, JMODS ); + } + else + { + jmodsFolder = new File( jLinkParent, JMODS ); + } + + getLog().debug( " Parent: " + jLinkParent.getAbsolutePath() ); + getLog().debug( " jmodsFolder: " + jmodsFolder.getAbsolutePath() ); + + return Optional.of( jmodsFolder ); + } + + private Commandline createJLinkCommandLine( List<String> jlinkArgs ) + { + Commandline cmd = new Commandline(); + jlinkArgs.forEach( arg -> cmd.createArg().setValue( arg ) ); + + return cmd; + } + + private String getJLinkExecutable() + { + Optional<Toolchain> toolchain = getToolchain(); + + if ( !toolchain.isPresent() ) + { + getLog().error( "Either JDK9+ or a toolchain " + + "pointing to a JDK9+ containing a jlink binary is required." ); + getLog().info( "See https://maven.apache.org/guides/mini/guide-using-toolchains.html " + + "for mor information." ); + throw new IllegalStateException( "Running on JDK8 and no toolchain found." ); + } + + String jLinkExecutable = toolchain.orElseThrow( NoSuchElementException::new ).findTool( "jlink" ); + + if ( StringUtils.isEmpty( jLinkExecutable ) ) + { + throw new IllegalStateException( "The jlink executable '" + + jLinkExecutable + "' doesn't exist or is not a file." ); + } + + // TODO: Check if there exist a more elegant way? + String jLinkCommand = "jlink" + ( SystemUtils.IS_OS_WINDOWS ? ".exe" : "" ); + + File jLinkExe = new File( jLinkExecutable ); + + if ( jLinkExe.isDirectory() ) + { + jLinkExe = new File( jLinkExe, jLinkCommand ); + } + + if ( SystemUtils.IS_OS_WINDOWS && jLinkExe.getName().indexOf( '.' ) < 0 ) + { + jLinkExe = new File( jLinkExe.getPath() + ".exe" ); + } + + if ( !jLinkExe.isFile() ) + { + throw new IllegalStateException( "The jlink executable '" + + jLinkExe + "' doesn't exist or is not a file." ); + } + return jLinkExe.getAbsolutePath(); + } + + private int executeCommand( Commandline cmd ) + throws MojoExecutionException + { + if ( getLog().isDebugEnabled() ) + { + // no quoted arguments ??? + getLog().debug( CommandLineUtils.toString( cmd.getCommandline() ).replaceAll( "'", "" ) ); + } + + CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer(); + CommandLineUtils.StringStreamConsumer out = new CommandLineUtils.StringStreamConsumer(); + try + { + int exitCode = CommandLineUtils.executeCommandLine( cmd, out, err ); + + String output = ( StringUtils.isEmpty( out.getOutput() ) ? null : '\n' + out.getOutput().trim() ); + + if ( exitCode != 0 ) + { + + if ( StringUtils.isNotEmpty( output ) ) + { + // Reconsider to use WARN / ERROR ? + // getLog().error( output ); + for ( String outputLine : output.split( "\n" ) ) + { + getLog().error( outputLine ); + } + } + + StringBuilder msg = new StringBuilder( "\nExit code: " ); + msg.append( exitCode ); + if ( StringUtils.isNotEmpty( err.getOutput() ) ) + { + msg.append( " - " ).append( err.getOutput() ); + } + msg.append( '\n' ); + msg.append( "Command line was: " ).append( cmd ).append( '\n' ).append( '\n' ); + + throw new MojoExecutionException( msg.toString() ); + } + + if ( StringUtils.isNotEmpty( output ) ) + { + //getLog().info( output ); + for ( String outputLine : output.split( "\n" ) ) + { + getLog().info( outputLine ); + } + } + + return exitCode; + } + catch ( CommandLineException e ) + { + throw new MojoExecutionException( "Unable to execute jlink command: " + e.getMessage(), e ); + } + } +} diff --git a/src/it/projects/MJLINK-40_includeLocales/verify.groovy b/src/main/java/org/apache/maven/plugins/jlink/JLinkExecutor.java similarity index 52% copy from src/it/projects/MJLINK-40_includeLocales/verify.groovy copy to src/main/java/org/apache/maven/plugins/jlink/JLinkExecutor.java index 1887547..7482adb 100644 --- a/src/it/projects/MJLINK-40_includeLocales/verify.groovy +++ b/src/main/java/org/apache/maven/plugins/jlink/JLinkExecutor.java @@ -1,3 +1,5 @@ +package org.apache.maven.plugins.jlink; + /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -17,38 +19,19 @@ * under the License. */ -import java.io.*; -import java.util.*; -import java.util.jar.*; -import org.codehaus.plexus.util.*; - -boolean result = false; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.toolchain.Toolchain; -try +/** + * JDK 8-only Jlink executor. + * + * <p>As JDK8 does not ship jlink, a toolchain is required.</p> + */ +class JLinkExecutor extends AbstractJLinkToolchainExecutor { - File target = new File( basedir, "target" ); - if ( !target.exists() || !target.isDirectory() ) - { - System.err.println( "target file is missing or not a directory." ); - return false; - } - File jlinkArgs = new File( target, "jlinkArgs" ); - if ( !jlinkArgs.exists() || jlinkArgs.isDirectory() ) + JLinkExecutor( Toolchain toolchain, Log log ) { - System.err.println( "jlinkArgs file is missing or is a directory." ); - return false; + super( toolchain, log ); } - def line = jlinkArgs.eachLine { line -> - if (line.equals('en,ja,de,*-IN')) - { - result = true; - } - } } -catch( Throwable e ) -{ - e.printStackTrace(); -} - -return result; diff --git a/src/main/java/org/apache/maven/plugins/jlink/JLinkMojo.java b/src/main/java/org/apache/maven/plugins/jlink/JLinkMojo.java index 14bebdc..3256565 100644 --- a/src/main/java/org/apache/maven/plugins/jlink/JLinkMojo.java +++ b/src/main/java/org/apache/maven/plugins/jlink/JLinkMojo.java @@ -21,7 +21,6 @@ package org.apache.maven.plugins.jlink; import java.io.File; import java.io.IOException; -import java.io.PrintStream; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -29,6 +28,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.NoSuchElementException; +import java.util.Optional; import org.apache.maven.artifact.Artifact; import org.apache.maven.plugin.MojoExecutionException; @@ -49,7 +50,6 @@ import org.codehaus.plexus.languages.java.jpms.LocationManager; import org.codehaus.plexus.languages.java.jpms.ResolvePathsRequest; import org.codehaus.plexus.languages.java.jpms.ResolvePathsResult; import org.codehaus.plexus.util.FileUtils; -import org.codehaus.plexus.util.cli.Commandline; /** * The JLink goal is intended to create a Java Run Time Image file based on @@ -64,8 +64,6 @@ import org.codehaus.plexus.util.cli.Commandline; public class JLinkMojo extends AbstractJLinkMojo { - private static final String JMODS = "jmods"; - @Component private LocationManager locationManager; @@ -274,41 +272,20 @@ public class JLinkMojo @Parameter( defaultValue = "${project.build.finalName}", readonly = true ) private String finalName; - public void execute() - throws MojoExecutionException, MojoFailureException + @Override + public void execute() throws MojoExecutionException, MojoFailureException { - - String jLinkExec = getExecutable(); - - getLog().info( "Toolchain in maven-jlink-plugin: jlink [ " + jLinkExec + " ]" ); - - // TODO: Find a more better and cleaner way? - File jLinkExecuteable = new File( jLinkExec ); - - // Really Hacky...do we have a better solution to find the jmods directory of the JDK? - File jLinkParent = jLinkExecuteable.getParentFile().getParentFile(); - File jmodsFolder; - if ( sourceJdkModules != null && sourceJdkModules.isDirectory() ) - { - jmodsFolder = new File ( sourceJdkModules, JMODS ); - } - else - { - jmodsFolder = new File( jLinkParent, JMODS ); - } - - getLog().debug( " Parent: " + jLinkParent.getAbsolutePath() ); - getLog().debug( " jmodsFolder: " + jmodsFolder.getAbsolutePath() ); - failIfParametersAreNotInTheirValidValueRanges(); ifOutputDirectoryExistsDelteIt(); + JLinkExecutor jLinkExec = getExecutor(); Collection<String> modulesToAdd = new ArrayList<>(); if ( addModules != null ) { modulesToAdd.addAll( addModules ); } + jLinkExec.addAllModules( modulesToAdd ); Collection<String> pathsOfModules = new ArrayList<>(); if ( modulePaths != null ) @@ -326,27 +303,28 @@ public class JLinkMojo } // The jmods directory of the JDK - pathsOfModules.add( jmodsFolder.getAbsolutePath() ); + jLinkExec.getJmodsFolder( this.sourceJdkModules ).ifPresent( + jmodsFolder -> pathsOfModules.add( jmodsFolder.getAbsolutePath() ) + ); + jLinkExec.addAllModulePaths( pathsOfModules ); + + List<String> jlinkArgs = createJlinkArgs( pathsOfModules, modulesToAdd ); - Commandline cmd; try { - cmd = createJLinkCommandLine( pathsOfModules, modulesToAdd ); + jLinkExec.executeJlink( jlinkArgs ); } - catch ( IOException e ) + catch ( IllegalStateException e ) { - throw new MojoExecutionException( e.getMessage() ); + throw new MojoFailureException( "Unable to find jlink command: " + e.getMessage(), e ); } - cmd.setExecutable( jLinkExec ); - - executeCommand( cmd, outputDirectoryImage ); File createZipArchiveFromImage = createZipArchiveFromImage( buildDirectory, outputDirectoryImage ); if ( projectHasAlreadySetAnArtifact() ) { throw new MojoExecutionException( "You have to use a classifier " - + "to attach supplemental artifacts to the project instead of replacing them." ); + + "to attach supplemental artifacts to the project instead of replacing them." ); } getProject().getArtifact().setFile( createZipArchiveFromImage ); @@ -379,10 +357,12 @@ public class JLinkMojo ResolvePathsRequest<File> request = ResolvePathsRequest.ofFiles( dependencyArtifacts ); - Toolchain toolchain = getToolchain(); - if ( toolchain != null && toolchain instanceof DefaultJavaToolChain ) + Optional<Toolchain> toolchain = getToolchain(); + if ( toolchain.isPresent() + && toolchain.orElseThrow( NoSuchElementException::new ) instanceof DefaultJavaToolChain ) { - request.setJdkHome( new File( ( (DefaultJavaToolChain) toolchain ).getJavaHome() ) ); + Toolchain toolcahin1 = toolchain.orElseThrow( NoSuchElementException::new ); + request.setJdkHome( new File( ( (DefaultJavaToolChain) toolcahin1 ).getJavaHome() ) ); } ResolvePathsResult<File> resolvePathsResult = locationManager.resolvePaths( request ); @@ -443,19 +423,9 @@ public class JLinkMojo return modulepathElements; } - private String getExecutable() - throws MojoFailureException + private JLinkExecutor getExecutor() { - String jLinkExec; - try - { - jLinkExec = getJLinkExecutable(); - } - catch ( IOException e ) - { - throw new MojoFailureException( "Unable to find jlink command: " + e.getMessage(), e ); - } - return jLinkExec; + return getJlinkExecutor(); } private boolean projectHasAlreadySetAnArtifact() @@ -510,7 +480,7 @@ public class JLinkMojo if ( endian != null && ( !"big".equals( endian ) && !"little".equals( endian ) ) ) { String message = "The given endian parameter " + endian - + " does not contain one of the following values: 'little' or 'big'."; + + " does not contain one of the following values: 'little' or 'big'."; getLog().error( message ); throw new MojoFailureException( message ); } @@ -537,130 +507,116 @@ public class JLinkMojo } } - private Commandline createJLinkCommandLine( Collection<String> pathsOfModules, Collection<String> modulesToAdd ) - throws IOException + private List<String> createJlinkArgs( Collection<String> pathsOfModules, + Collection<String> modulesToAdd ) { - File file = new File( outputDirectoryImage.getParentFile(), "jlinkArgs" ); - if ( !getLog().isDebugEnabled() ) - { - file.deleteOnExit(); - } - file.getParentFile().mkdirs(); - file.createNewFile(); - - PrintStream argsFile = new PrintStream( file ); + List<String> jlinkArgs = new ArrayList<>(); if ( stripDebug ) { - argsFile.println( "--strip-debug" ); + jlinkArgs.add( "--strip-debug" ); } if ( bindServices ) { - argsFile.println( "--bind-services" ); + jlinkArgs.add( "--bind-services" ); } if ( endian != null ) { - argsFile.println( "--endian" ); - argsFile.println( endian ); + jlinkArgs.add( "--endian" ); + jlinkArgs.add( endian ); } if ( ignoreSigningInformation ) { - argsFile.println( "--ignore-signing-information" ); + jlinkArgs.add( "--ignore-signing-information" ); } if ( compress != null ) { - argsFile.println( "--compress" ); - argsFile.println( compress ); + jlinkArgs.add( "--compress" ); + jlinkArgs.add( compress + "" ); } if ( launcher != null ) { - argsFile.println( "--launcher" ); - argsFile.println( launcher ); + jlinkArgs.add( "--launcher" ); + jlinkArgs.add( launcher ); } if ( disablePlugin != null ) { - argsFile.println( "--disable-plugin" ); - argsFile.append( '"' ).append( disablePlugin ).println( '"' ); + jlinkArgs.add( "--disable-plugin" ); + jlinkArgs.add( disablePlugin ); } if ( pathsOfModules != null ) { // @formatter:off - argsFile.println( "--module-path" ); - argsFile.append( '"' ) - .append( getPlatformDependSeparateList( pathsOfModules ) - .replace( "\\", "\\\\" ) ).println( '"' ); + jlinkArgs.add( "--module-path" ); + jlinkArgs.add( getPlatformDependSeparateList( pathsOfModules ).replace( "\\", "\\\\" ) ); // @formatter:off } if ( noHeaderFiles ) { - argsFile.println( "--no-header-files" ); + jlinkArgs.add( "--no-header-files" ); } if ( noManPages ) { - argsFile.println( "--no-man-pages" ); + jlinkArgs.add( "--no-man-pages" ); } if ( hasSuggestProviders() ) { - argsFile.println( "--suggest-providers" ); + jlinkArgs.add( "--suggest-providers" ); String sb = getCommaSeparatedList( suggestProviders ); - argsFile.println( sb ); + jlinkArgs.add( sb ); } if ( hasLimitModules() ) { - argsFile.println( "--limit-modules" ); + jlinkArgs.add( "--limit-modules" ); String sb = getCommaSeparatedList( limitModules ); - argsFile.println( sb ); + jlinkArgs.add( sb ); } if ( !modulesToAdd.isEmpty() ) { - argsFile.println( "--add-modules" ); + jlinkArgs.add( "--add-modules" ); // This must be name of the module and *NOT* the name of the // file! Can we somehow pre check this information to fail early? String sb = getCommaSeparatedList( modulesToAdd ); - argsFile.append( '"' ).append( sb.replace( "\\", "\\\\" ) ).println( '"' ); + jlinkArgs.add( sb.replace( "\\", "\\\\" ) ); } if ( hasIncludeLocales() ) { - argsFile.println( "--add-modules" ); - argsFile.println( "jdk.localedata" ); - argsFile.println( "--include-locales" ); + jlinkArgs.add( "--add-modules" ); + jlinkArgs.add( "jdk.localedata" ); + jlinkArgs.add( "--include-locales" ); String sb = getCommaSeparatedList( includeLocales ); - argsFile.println( sb ); + jlinkArgs.add( sb ); } if ( pluginModulePath != null ) { - argsFile.println( "--plugin-module-path" ); + jlinkArgs.add( "--plugin-module-path" ); StringBuilder sb = convertSeparatedModulePathToPlatformSeparatedModulePath( pluginModulePath ); - argsFile.append( '"' ).append( sb.toString().replace( "\\", "\\\\" ) ).println( '"' ); + jlinkArgs.add( sb.toString().replace( "\\", "\\\\" ) ); } if ( buildDirectory != null ) { - argsFile.println( "--output" ); - argsFile.println( outputDirectoryImage ); + jlinkArgs.add( "--output" ); + jlinkArgs.add( outputDirectoryImage.getAbsolutePath() ); } if ( verbose ) { - argsFile.println( "--verbose" ); + jlinkArgs.add( "--verbose" ); } - argsFile.close(); - - Commandline cmd = new Commandline(); - cmd.createArg().setValue( '@' + file.getAbsolutePath() ); - return cmd; + return Collections.unmodifiableList( jlinkArgs ); } private boolean hasIncludeLocales() diff --git a/src/main/java9/org/apache/maven/plugins/jlink/JLinkExecutor.java b/src/main/java9/org/apache/maven/plugins/jlink/JLinkExecutor.java new file mode 100644 index 0000000..d562816 --- /dev/null +++ b/src/main/java9/org/apache/maven/plugins/jlink/JLinkExecutor.java @@ -0,0 +1,143 @@ +/* + * 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. + */ + +package org.apache.maven.plugins.jlink; + +import org.apache.commons.lang3.StringUtils; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.toolchain.Toolchain; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.List; +import java.util.Optional; +import java.util.spi.ToolProvider; + +/** + * JDK9+ executor for jlink. + * + * <p>This implementation uses the JDK9+ Toolprovider SPI to find and execute jlink. + * This way, no fork needs to be created.</p> + */ +class JLinkExecutor extends AbstractJLinkToolchainExecutor +{ + private final ToolProvider toolProvider; + + JLinkExecutor( Toolchain toolchain, Log log ) throws IOException + { + super( toolchain, log ); + this.toolProvider = getJLinkExecutable(); + } + + protected final ToolProvider getJLinkExecutable() + { + return ToolProvider + .findFirst( "jlink" ) + .orElseThrow( () -> new IllegalStateException( "No jlink tool found." ) ); + } + + @Override + public int executeJlink( List<String> jlinkArgs ) throws MojoExecutionException + { + if (getToolchain().isPresent()) + { + return super.executeJlink( jlinkArgs ); + } + + if ( getLog().isDebugEnabled() ) + { + // no quoted arguments ??? + getLog().debug( this.toolProvider.name() + " " + jlinkArgs ); + } + + try ( ByteArrayOutputStream baosErr = new ByteArrayOutputStream(); + PrintWriter err = new PrintWriter( baosErr ); + ByteArrayOutputStream baosOut = new ByteArrayOutputStream(); + PrintWriter out = new PrintWriter( baosOut ) ) + { + int exitCode = this.toolProvider.run( out, err, jlinkArgs.toArray( new String[0] ) ); + out.flush(); + err.flush(); + + String outAsString = baosOut.toString( "UTF-8" ); + String output = ( StringUtils.isEmpty( outAsString ) ? null : '\n' + outAsString.trim() ); + + if ( exitCode != 0 ) + { + if ( StringUtils.isNotEmpty( output ) ) + { + // Reconsider to use WARN / ERROR ? + // getLog().error( output ); + for ( String outputLine : output.split( "\n" ) ) + { + getLog().error( outputLine ); + } + } + + StringBuilder msg = new StringBuilder( "\nExit code: " ); + msg.append( exitCode ); + String errAsString = baosErr.toString(); + if ( StringUtils.isNotEmpty( errAsString ) ) + { + msg.append( " - " ).append( errAsString ); + } + msg.append( '\n' ); + msg.append( "Command line was: " ).append( this.toolProvider.name() ).append( ' ' ).append( + jlinkArgs ).append( '\n' ).append( '\n' ); + + throw new MojoExecutionException( msg.toString() ); + } + + if ( StringUtils.isNotEmpty( output ) ) + { + //getLog().info( output ); + for ( String outputLine : output.split( "\n" ) ) + { + getLog().info( outputLine ); + } + } + + return exitCode; + } + catch ( IOException e ) + { + throw new MojoExecutionException( "Unable to execute jlink command: " + e.getMessage(), e ); + } + } + + @Override + public Optional<File> getJmodsFolder( /* nullable */ File sourceJdkModules ) + { + if ( getToolchain().isPresent()) + { + return super.getJmodsFolder( sourceJdkModules ); + } + + if ( sourceJdkModules != null && sourceJdkModules.isDirectory() ) + { + return Optional.of( new File( sourceJdkModules, JMODS ) ); + } + + // ToolProvider does not need jmods folder to be set. + return Optional.empty(); + } +}