All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.maven.plugins.jmod.JModCreateMojo Maven / Gradle / Ivy

package org.apache.maven.plugins.jmod;

/*
 * 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 java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Component;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.apache.maven.project.MavenProject;
import org.apache.maven.shared.utils.StringUtils;
import org.apache.maven.shared.utils.logging.MessageUtils;
import org.apache.maven.toolchain.Toolchain;
import org.apache.maven.toolchain.java.DefaultJavaToolChain;
import org.codehaus.plexus.languages.java.jpms.JavaModuleDescriptor;
import org.codehaus.plexus.languages.java.jpms.LocationManager;
import org.codehaus.plexus.languages.java.jpms.ModuleNameSource;
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 create goal is intended to create jmod files which can be used for later linking via
 * maven-jlink-plugin. The jmod files
 * can not be used as usual dependencies on the classpath only in relationship with maven-jlink-plugin.
 * 
 * @author Karl Heinz Marbaise [email protected]
 */
// CHECKSTYLE_OFF: LineLength
@Mojo( name = "create", requiresDependencyResolution = ResolutionScope.RUNTIME, defaultPhase = LifecyclePhase.PACKAGE, requiresProject = true )
// CHECKSTYLE_ON: LineLength
public class JModCreateMojo
    extends AbstractJModMojo
{
    private static final String JMODS = "jmods";

    private List classpathElements;

    private List modulepathElements;

    @Parameter( defaultValue = "${project.compileClasspathElements}", readonly = true, required = true )
    private List compilePath;

    @Component
    private LocationManager locationManager;

    /**
     * Specifies one or more directories containing native commands to be copied. The given directories are relative to
     * the current base directory. If no entry is defined the default is src/main/cmds used.
     * 
     * 
     * <cmds>
     *   <cmd>...</cmd>
     *   <cmd>...</cmd>
     *   .
     *   .
     * </cmds>
     * 
*

* All files from those directories will be copied into the resulting directory bin within the jmod * file. *

* JMod command line equivalent: --cmds <path>. */ @Parameter private List cmds; private static final String DEFAULT_CMD_DIRECTORY = "src/main/cmds"; /** * Specifies one or more directories containing configuration files to be copied. Location of user-editable config * files. If no configuration is given the src/main/configs location is used as default. If this * directory does not exist the whole will be ignored. * *
     * <configs>
     *   <config>...</config>
     *   <config>...</config>
     *   .
     *   .
     * </configs>
     * 
*

* All files from those directories will be copied into the resulting directory config within the jmod * file. *

* jmod command line equivalent: --config <path>. */ @Parameter private List configs; private static final String DEFAULT_CONFIG_DIRECTORY = "src/main/configs"; /** * Exclude files matching the pattern list. Each element using one the following forms: <glob-pattern>, * glob:<glob-pattern> or regex:<regex-pattern> * *
     * <excludes>
     *   <exclude>...</exclude>
     *   <exclude>...</exclude>
     *   .
     *   .
     * </excludes>
     * 
*/ @Parameter private List excludes; /** * Define the main class which is recorded in the module-info.class file. */ @Parameter private String mainClass; /** * Specifies one or more directories containing native libraries to be copied (The given directories are relative to * project base directory). If no configuration is given in <> file the location src/main/libs * will be used. If the default location does not exist the whole configuration will be ignored. * *
     * <libs>
     *   <lib>...</lib>
     *   <lib>...</lib>
     *   .
     *   .
     * </libs>
     * 
*

* All files from those directories will be copied into the resulting directory lib within the jmod * file. *

*/ @Parameter private List libs; private static final String DEFAULT_LIB_DIRECTORY = "src/main/libs"; /** * Define the module version of the jmod file. */ @Parameter( defaultValue = "${project.version}" ) private String moduleVersion; /** * --do-not-resolve-by-default Exclude from the default root set of modules */ @Parameter( defaultValue = "false" ) private boolean doNotResolveByDefault; /** * Define the locations of header files. The default location is src/main/headerfiles. If the the * default location does not exist in the current project it will be ignored. The given directories are relative to * the project base directory. If an entry is defined the definition of all locations is needed. * *
     * <headerFiles>
     *   <headerFile>...</headerFile>
     *   <headerFile>...</headerFile>
     *   .
     *   .
     * </headerFiles>
     * 
*

* All files from those directories will be copied into the resulting directory includes within the * jmod file. *

* jmod command line equivalent --header-files <path> */ @Parameter private List headerFiles; private static final String DEFAULT_HEADER_FILES_DIRECTORY = "src/main/headerfiles"; /** * Define the locations of man pages. The default location is src/main/manpages. The given man pages * locations are relative to the project base directory. * *
     * <manPages>
     *   <manPage>...</manPage>
     *   <manPage>...</manPage>
     *   .
     *   .
     * </manPages>
     * 
*

* All files from those directories will be copied into the resulting directory man within the jmod * file. *

* jmod command line equivalent --man-pages <path> */ @Parameter private List manPages; private static final String DEFAULT_MAN_PAGES_DIRECTORY = "src/main/manpages"; /** * This is only the name of the jmod file in the target directory. */ @Parameter( defaultValue = "${project.artifactId}", required = true, readonly = true ) private String outputFileName; /** * Define the location of legal notices. The default location is src/main/legalnotices. The given man * pages locations are relative to the project base directory. * *
     * <legalNotices>
     *   <legalNotice>...</legalNotice>
     *   <legalNotice>...</legalNotice>
     *   .
     *   .
     * </legalNotices>
     * 
*

* All files from those directories will be copied into the resulting directory legal within the jmod * file. *

* jmod command line equivalent --legal-notices <path> */ @Parameter private List legalNotices; private static final String DEFAULT_LEGAL_NOTICES_DIRECTORY = "src/main/legalnotices"; /** * --target-platform <target-platform> Target platform TODO: Which values are valid? */ @Parameter private String targetPlatform; /** * Hint for a tool to issue a warning if the module is resolved. The valid values are: *
    *
  • deprecated
  • *
  • deprecated-for-removal
  • *
  • incubating
  • *
*/ @Parameter private String warnIfResolved; @Parameter( defaultValue = "${project.build.outputDirectory}", required = true, readonly = true ) private File targetClassesDirectory; @Parameter( defaultValue = "${project.build.directory}", required = true, readonly = true ) private File outputDirectory; // calculated based on jmod(.exe)/../.. private File javaHome; public void execute() throws MojoExecutionException, MojoFailureException { String jModExecutable; try { jModExecutable = getJModExecutable(); } catch ( IOException e ) { throw new MojoFailureException( "Unable to find jmod command: " + e.getMessage(), e ); } File jModExecuteableFile = new File( jModExecutable ); javaHome = jModExecuteableFile.getParentFile().getParentFile(); File jmodsFolderJDK = new File( javaHome, JMODS ); getLog().debug( "Parent: " + javaHome.getAbsolutePath() ); getLog().debug( "jmodsFolder: " + jmodsFolderJDK.getAbsolutePath() ); preparePaths(); failIfParametersAreNotInTheirValidValueRanges(); getLog().info( "Toolchain in jmod-maven-plugin: jmod [ " + jModExecutable + " ]" ); // We need to put the resulting x.jmod files into jmods folder otherwise is // seemed to be not working. // Check why? File modsFolder = new File( outputDirectory, "jmods" ); File resultingJModFile = new File( modsFolder, outputFileName + ".jmod" ); deleteOutputIfAlreadyExists( resultingJModFile ); // create the jmods folder... modsFolder.mkdirs(); Commandline cmd; try { cmd = createJModCreateCommandLine( resultingJModFile ); } catch ( IOException e ) { throw new MojoExecutionException( e.getMessage() ); } cmd.setExecutable( jModExecutable ); executeCommand( cmd, outputDirectory ); if ( projectHasAlreadySetAnArtifact() ) { throw new MojoExecutionException( "You have to use a classifier " + "to attach supplemental artifacts to the project instead of replacing them." ); } getProject().getArtifact().setFile( resultingJModFile ); } private void deleteOutputIfAlreadyExists( File resultingJModFile ) throws MojoFailureException { if ( resultingJModFile.exists() && resultingJModFile.isFile() ) { try { getLog().debug( "Deleting the existing " + resultingJModFile.getAbsolutePath() + " file." ); FileUtils.forceDelete( resultingJModFile ); } catch ( IOException e ) { String message = "Failure during deleting of file " + resultingJModFile.getAbsolutePath(); getLog().error( message ); throw new MojoFailureException( message ); } } } private void failIfParametersAreNotInTheirValidValueRanges() throws MojoFailureException { if ( warnIfResolved != null ) { String x = warnIfResolved.toLowerCase().trim(); if ( !"deprecated".equals( x ) && "deprecated-for-removal".equals( x ) && "incubating".equals( x ) ) { String message = "The parameter warnIfResolved does not contain a valid value. " + "Valid values are 'deprecated', 'deprecated-for-removal' or 'incubating'."; getLog().error( message ); throw new MojoFailureException( message ); } } throwExceptionIfNotExistOrNotADirectory( handleConfigurationListWithDefault( cmds, DEFAULT_CMD_DIRECTORY ), "cmd" ); throwExceptionIfNotExistOrNotADirectory( handleConfigurationListWithDefault( configs, DEFAULT_CONFIG_DIRECTORY ), "config" ); throwExceptionIfNotExistOrNotADirectory( handleConfigurationListWithDefault( libs, DEFAULT_LIB_DIRECTORY ), "lib" ); throwExceptionIfNotExistOrNotADirectory( handleConfigurationListWithDefault( headerFiles, DEFAULT_HEADER_FILES_DIRECTORY ), "headerFile" ); throwExceptionIfNotExistOrNotADirectory( handleConfigurationListWithDefault( legalNotices, DEFAULT_LEGAL_NOTICES_DIRECTORY ), "legalNotice" ); throwExceptionIfNotExistOrNotADirectory( handleConfigurationListWithDefault( manPages, DEFAULT_MAN_PAGES_DIRECTORY ), "manPage" ); } private void throwExceptionIfNotExistOrNotADirectory( List configurations, String partialMessage ) throws MojoFailureException { for ( String configLocation : configurations ) { File dir = new File( configLocation ); if ( !dir.exists() || !dir.isDirectory() ) { String message = "The directory " + configLocation + " for " + partialMessage + " parameter does not exist " + "or is not a directory. "; getLog().error( message ); throw new MojoFailureException( message ); } } } private List getCompileClasspathElements( MavenProject project ) { List list = new ArrayList( project.getArtifacts().size() + 1 ); if ( targetClassesDirectory.exists() ) { list.add( new File( project.getBuild().getOutputDirectory() ) ); } for ( Artifact a : project.getArtifacts() ) { list.add( a.getFile() ); } return list; } private void preparePaths() { boolean hasModuleDescriptor = false; // Assuming that the module-info.java is already compiled by compiler plugin so only // check if the module-info.class file exists. File moduleInfo = new File( targetClassesDirectory, "module-info.class" ); if ( moduleInfo.exists() && moduleInfo.isFile() ) { getLog().debug( "We have found a module-info.class file." ); hasModuleDescriptor = true; } Collection dependencyArtifacts = getCompileClasspathElements( getProject() ); if ( hasModuleDescriptor ) { // For now only allow named modules. Once we can create a graph with ASM we can specify exactly the modules // and we can detect if auto modules are used. In that case, MavenProject.setFile() should not be used, so // you cannot depend on this project and so it won't be distributed. modulepathElements = new ArrayList(); classpathElements = new ArrayList(); ResolvePathsResult resolvePathsResult; try { ResolvePathsRequest request = ResolvePathsRequest.ofFiles( dependencyArtifacts ).setMainModuleDescriptor( moduleInfo ); Toolchain toolchain = getToolchain(); if ( toolchain != null && toolchain instanceof DefaultJavaToolChain ) { request.setJdkHome( new File( ( (DefaultJavaToolChain) toolchain ).getJavaHome() ) ); } resolvePathsResult = locationManager.resolvePaths( request ); JavaModuleDescriptor moduleDescriptor = resolvePathsResult.getMainModuleDescriptor(); for ( Map.Entry entry : resolvePathsResult.getModulepathElements().entrySet() ) { getLog().debug( "File: " + entry.getKey().getAbsolutePath() + " " + entry.getValue().name() ); if ( ModuleNameSource.FILENAME.equals( entry.getValue() ) ) { final String message = "Required filename-based automodules detected. " + "Please don't publish this project to a public artifact repository!"; if ( moduleDescriptor.exports().isEmpty() ) { // application getLog().info( message ); } else { // library writeBoxedWarning( message ); } break; } } for ( File file : resolvePathsResult.getClasspathElements() ) { getLog().debug( "classpathElements: File: " + file.getPath() ); classpathElements.add( file.getPath() ); } for ( File file : resolvePathsResult.getModulepathElements().keySet() ) { getLog().debug( "modulepathElements: File: " + file.getPath() ); if ( file.isDirectory() ) { modulepathElements.add( file.getPath() ); } else { modulepathElements.add( file.getParent() ); } } } catch ( IOException e ) { getLog().warn( e.getMessage() ); } } else { modulepathElements = Collections.emptyList(); classpathElements = new ArrayList(); for ( File file : dependencyArtifacts ) { classpathElements.add( file.getPath() ); } } } private Commandline createJModCreateCommandLine( File resultingJModFile ) throws IOException { File file = new File( outputDirectory, "jmodCreateArgs" ); if ( !getLog().isDebugEnabled() ) { file.deleteOnExit(); } file.getParentFile().mkdirs(); file.createNewFile(); PrintStream argsFile = new PrintStream( file ); argsFile.println( "create" ); if ( moduleVersion != null ) { argsFile.println( "--module-version" ); argsFile.println( moduleVersion ); } List classPaths; if ( classpathElements != null ) { classPaths = new ArrayList<>( classpathElements ); } else { classPaths = new ArrayList<>( 1 ); } if ( targetClassesDirectory.exists() ) { classPaths.add( targetClassesDirectory.getAbsolutePath() ); } argsFile.println( "--class-path" ); argsFile .append( '"' ) .append( getPlatformSeparatedList( classPaths ).replace( "\\", "\\\\" ) ) .println( '"' ); if ( excludes != null && !excludes.isEmpty() ) { argsFile.println( "--exclude" ); String commaSeparatedList = getCommaSeparatedList( excludes ); argsFile.append( '"' ).append( commaSeparatedList.replace( "\\", "\\\\" ) ).println( '"' ); } List configList = handleConfigurationListWithDefault( configs, DEFAULT_CONFIG_DIRECTORY ); if ( !configList.isEmpty() ) { argsFile.println( "--config" ); // Should we quote the paths? argsFile.println( getPlatformSeparatedList( configList ) ); } if ( StringUtils.isNotBlank( mainClass ) ) { argsFile.println( "--main-class" ); argsFile.println( mainClass ); } List cmdsList = handleConfigurationListWithDefault( cmds, DEFAULT_CMD_DIRECTORY ); if ( !cmdsList.isEmpty() ) { argsFile.println( "--cmds" ); argsFile.println( getPlatformSeparatedList( cmdsList ) ); } List libsList = handleConfigurationListWithDefault( libs, DEFAULT_LIB_DIRECTORY ); if ( !libsList.isEmpty() ) { argsFile.println( "--libs" ); argsFile.println( getPlatformSeparatedList( libsList ) ); } List headerFilesList = handleConfigurationListWithDefault( headerFiles, DEFAULT_HEADER_FILES_DIRECTORY ); if ( !headerFilesList.isEmpty() ) { argsFile.println( "--header-files" ); argsFile.println( getPlatformSeparatedList( headerFilesList ) ); } List legalNoticesList = handleConfigurationListWithDefault( legalNotices, DEFAULT_LEGAL_NOTICES_DIRECTORY ); if ( !legalNoticesList.isEmpty() ) { argsFile.println( "--legal-notices" ); argsFile.println( getPlatformSeparatedList( legalNoticesList ) ); } List manPagesList = handleConfigurationListWithDefault( manPages, DEFAULT_MAN_PAGES_DIRECTORY ); if ( !manPagesList.isEmpty() ) { argsFile.println( "--man-pages" ); argsFile.println( getPlatformSeparatedList( manPagesList ) ); } List modulePaths = new ArrayList<>( modulepathElements ); modulePaths.add( new File( javaHome, JMODS ).getAbsolutePath() ); if ( modulePaths != null ) { //@formatter:off argsFile.println( "--module-path" ); argsFile .append( '"' ) .append( getPlatformSeparatedList( modulePaths ).replace( "\\", "\\\\" ) ) .println( '"' ); //@formatter:off } if ( targetPlatform != null ) { argsFile.println( "--target-platform" ); argsFile.println( targetPlatform ); } if ( warnIfResolved != null ) { argsFile.println( "--warn-if-resolved" ); argsFile.println( warnIfResolved ); } if ( doNotResolveByDefault ) { argsFile.println( "--do-not-resolve-by-default" ); } argsFile.println( resultingJModFile.getAbsolutePath() ); argsFile.close(); Commandline cmd = new Commandline(); cmd.createArg().setValue( '@' + file.getAbsolutePath() ); return cmd; } private boolean isConfigurationDefinedInPOM( List configuration ) { return configuration != null && !configuration.isEmpty(); } private List handleConfigurationListWithDefault( List configuration, String defaultLocation ) { List commands = new ArrayList(); if ( isConfigurationDefinedInPOM( configuration ) ) { commands.addAll( configuration ); } else { if ( doDefaultsExist( defaultLocation ) ) { commands.add( defaultLocation ); } } commands = resolveAgainstProjectBaseDir( commands ); return commands; } private List resolveAgainstProjectBaseDir( List relativeDirectories ) { List result = new ArrayList<>(); for ( String configLocation : relativeDirectories ) { File dir = new File( getProject().getBasedir(), configLocation ); result.add( dir.getAbsolutePath() ); } return result; } private boolean doDefaultsExist( String defaultLocation ) { boolean result = false; File dir = new File( getProject().getBasedir(), defaultLocation ); if ( dir.exists() && dir.isDirectory() ) { result = true; } return result; } private String getPlatformSeparatedList( Collection paths ) { StringBuilder sb = new StringBuilder(); for ( String module : paths ) { if ( sb.length() > 0 ) { sb.append( File.pathSeparatorChar ); } sb.append( module ); } return sb.toString(); } private void writeBoxedWarning( String message ) { String line = StringUtils.repeat( "*", message.length() + 4 ); getLog().warn( line ); getLog().warn( "* " + MessageUtils.buffer().strong( message ) + " *" ); getLog().warn( line ); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy