You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ant.apache.org by craigpell <gi...@git.apache.org> on 2018/12/07 04:46:59 UTC

[GitHub] ant pull request #80: Added tasks for JDK's jmod and jlink tools.

GitHub user craigpell opened a pull request:

    https://github.com/apache/ant/pull/80

    Added tasks for JDK's jmod and jlink tools.

    Support for the jmod and jlink tools present in the JDK since Java 9.  Now that Java 11 has no standalone JRE, officially, these tools are the only way to distribute client-side Java applications.

You can merge this pull request into a Git repository by running:

    $ git pull https://github.com/craigpell/ant jmod-and-jlink

Alternatively you can review and apply these changes as the patch at:

    https://github.com/apache/ant/pull/80.patch

To close this pull request, make a commit to your master/trunk branch
with (at least) the following in the commit message:

    This closes #80
    
----
commit 3bc80754d5063b88c3a8b044e9774c0a791ac2ff
Author: VGR <vg...@...>
Date:   2018-12-07T04:32:57Z

    Added tasks for JDK's jmod and jlink tools.

----


---

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
For additional commands, e-mail: dev-help@ant.apache.org


[GitHub] ant issue #80: Added tasks for JDK's jmod and jlink tools.

Posted by bodewig <gi...@git.apache.org>.
Github user bodewig commented on the issue:

    https://github.com/apache/ant/pull/80
  
    Many thanks again, @craigpell . I've merged this but would like to resolve the discussion about the log output.
    
    With b74d5b366 I've committed a branch (logoutput-jmod-link) where I use `LogOutputStream` in `link` - I'm not sure why one would suppress the output at all, but I've never worked with the tool. A similar approach (or one that does away with the `ByteArrayOutputStream`s completely and removes the output from the exception and simply channels the output to Ant's log unconditionally) would work for `jmod` as well.
    
    /cc @jaikiran 


---

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
For additional commands, e-mail: dev-help@ant.apache.org


[GitHub] ant pull request #80: Added tasks for JDK's jmod and jlink tools.

Posted by jaikiran <gi...@git.apache.org>.
Github user jaikiran commented on a diff in the pull request:

    https://github.com/apache/ant/pull/80#discussion_r240889855
  
    --- Diff: src/main/org/apache/tools/ant/taskdefs/modules/Jmod.java ---
    @@ -0,0 +1,1282 @@
    +/*
    + *  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.tools.ant.taskdefs.modules;
    +
    +import java.io.File;
    +import java.io.ByteArrayOutputStream;
    +import java.io.PrintStream;
    +import java.io.IOException;
    +
    +import java.nio.file.Files;
    +
    +import java.util.Collection;
    +import java.util.List;
    +import java.util.ArrayList;
    +
    +import java.util.Map;
    +import java.util.LinkedHashMap;
    +
    +import java.util.Collections;
    +
    +import java.util.spi.ToolProvider;
    +
    +import org.apache.tools.ant.BuildException;
    +import org.apache.tools.ant.Project;
    +import org.apache.tools.ant.Task;
    +
    +import org.apache.tools.ant.util.MergingMapper;
    +import org.apache.tools.ant.util.FileUtils;
    +import org.apache.tools.ant.util.ResourceUtils;
    +
    +import org.apache.tools.ant.types.EnumeratedAttribute;
    +import org.apache.tools.ant.types.FileSet;
    +import org.apache.tools.ant.types.ModuleVersion;
    +import org.apache.tools.ant.types.Path;
    +import org.apache.tools.ant.types.Reference;
    +import org.apache.tools.ant.types.Resource;
    +import org.apache.tools.ant.types.ResourceCollection;
    +
    +import org.apache.tools.ant.types.resources.FileResource;
    +import org.apache.tools.ant.types.resources.Union;
    +
    +/**
    + * Creates a linkable .jmod file from a modular jar file, and optionally from
    + * other resource files such as native libraries and documents.  Equivalent
    + * to the JDK's
    + * <a href="https://docs.oracle.com/en/java/javase/11/tools/jmod.html">jmod</a>
    + * tool.
    + * <p>
    + * Supported attributes:
    + * <dl>
    + * <dt>{@code destFile}
    + * <dd>Required, jmod file to create.
    + * <dt>{@code classpath}
    + * <dt>{@code classpathref}
    + * <dd>Where to locate files to be placed in the jmod file.
    + * <dt>{@code modulepath}
    + * <dt>{@code modulepathref}
    + * <dd>Where to locate dependencies.
    + * <dt>{@code commandpath}
    + * <dt>{@code commandpathref}
    + * <dd>Directories containing native commands to include in jmod.
    + * <dt>{@code headerpath}
    + * <dt>{@code headerpathref}
    + * <dd>Directories containing header files to include in jmod.
    + * <dt>{@code configpath}
    + * <dt>{@code configpathref}
    + * <dd>Directories containing user-editable configuration files
    + *     to include in jmod.
    + * <dt>{@code legalpath}
    + * <dt>{@code legalpathref}
    + * <dd>Directories containing legal licenses and notices to include in jmod.
    + * <dt>{@code nativelibpath}
    + * <dt>{@code nativelibpathref}
    + * <dd>Directories containing native libraries to include in jmod.
    + * <dt>{@code manpath}
    + * <dt>{@code manpathref}
    + * <dd>Directories containing man pages to include in jmod.
    + * <dt>{@code version}
    + * <dd>Module <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/module/ModuleDescriptor.Version.html">version</a>.
    + * <dt>{@code mainclass}
    + * <dd>Main class of module.
    + * <dt>{@code platform}
    + * <dd>The target platform for the jmod.  A particular JDK's platform
    + * can be seen by running
    + * <code>jmod describe $JDK_HOME/jmods/java.base.jmod | grep -i platform</code>.
    + * <dt>{@code hashModulesPattern}
    + * <dd>Regular expression for names of modules in the module path
    + *     which depend on the jmod being created, and which should have
    + *     hashes generated for them and included in the new jmod.
    + * <dt>{@code resolveByDefault}
    + * <dd>Boolean indicating whether the jmod should be one of
    + *     the default resolved modules in an application.  Default is true.
    + * <dt>{@code moduleWarnings}
    + * <dd>Whether to emit warnings when resolving modules which are
    + *     not recommended for use.  Comma-separated list of one of more of
    + *     the following:
    + *     <dl>
    + *     <dt>{@code deprecated}
    + *     <dd>Warn if module is deprecated
    + *     <dt>{@code leaving}
    + *     <dd>Warn if module is deprecated for removal
    + *     <dt>{@code incubating}
    + *     <dd>Warn if module is an incubating (not yet official) module
    + *     </dl>
    + * </dl>
    + *
    + * <p>
    + * Supported nested elements:
    + * <dl>
    + * <dt>{@code <classpath>}
    + * <dd>Path indicating where to locate files to be placed in the jmod file.
    + * <dt>{@code <modulepath>}
    + * <dd>Path indicating where to locate dependencies.
    + * <dt>{@code <commandpath>}
    + * <dd>Path of directories containing native commands to include in jmod.
    + * <dt>{@code <headerpath>}
    + * <dd>Path of directories containing header files to include in jmod.
    + * <dt>{@code <configpath>}
    + * <dd>Path of directories containing user-editable configuration files
    + *     to include in jmod.
    + * <dt>{@code <legalpath>}
    + * <dd>Path of directories containing legal notices to include in jmod.
    + * <dt>{@code <nativelibpath>}
    + * <dd>Path of directories containing native libraries to include in jmod.
    + * <dt>{@code <manpath>}
    + * <dd>Path of directories containing man pages to include in jmod.
    + * <dt>{@code <version>}
    + * <dd><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/module/ModuleDescriptor.Version.html">Module version</a> of jmod.
    + *     Must have a required {@code number} attribute.  May also have optional
    + *     {@code preRelease} and {@code build} attributes.
    + * <dt>{@code <moduleWarning>}
    + * <dd>Has one required attribute, {@code reason}.  See {@code moduleWarnings}
    + *     attribute above.  This element may be specified multiple times.
    + * </dl>
    + * <p>
    + * destFile and classpath are required data.
    + */
    +public class Jmod
    +extends Task {
    +    /** Location of jmod file to be created. */
    +    private File jmodFile;
    +
    +    /**
    +     * Path of files (usually jar files or directories containing
    +     * compiled classes) from which to create jmod.
    +     */
    +    private Path classpath;
    +
    +    /**
    +     * Path of directories containing modules on which the modules
    +     * in the classpath depend.
    +     */
    +    private Path modulePath;
    +
    +    /**
    +     * Path of directories containing executable files to bundle in the
    +     * created jmod.
    +     */
    +    private Path commandPath;
    +
    +    /**
    +     * Path of directories containing configuration files to bundle in the
    +     * created jmod.
    +     */
    +    private Path configPath;
    +
    +    /**
    +     * Path of directories containing includable header files (such as for
    +     * other languages) to bundle in the created jmod.
    +     */
    +    private Path headerPath;
    +
    +    /**
    +     * Path of directories containing legal license files to bundle
    +     * in the created jmod.
    +     */
    +    private Path legalPath;
    +
    +    /**
    +     * Path of directories containing native libraries needed by classes
    +     * in the modules comprising the created jmod.
    +     */
    +    private Path nativeLibPath;
    +
    +    /**
    +     * Path of directories containing manual pages to bundle
    +     * in the created jmod.
    +     */
    +    private Path manPath;
    +
    +    /**
    +     * Module version of jmod.  Either this or {@link #moduleVersion}
    +     * may be set.
    +     */
    +    private String version;
    +
    +    /** Module version of jmod.  Either this or {@link #version} may be set. */
    +    private ModuleVersion moduleVersion;
    +
    +    /**
    +     * Main class to execute, if Java attempts to execute jmod's module
    +     * without specifying a main class explicitly.
    +     */
    +    private String mainClass;
    +
    +    /**
    +     * Target platform of created jmod.  Examples are {@code windows-amd64}
    +     * and {@code linux-amd64}.  Target platform is an attribute
    +     * of each JDK, which can be seen by executing
    +     * <code>jmod describe $JDK_HOME/jmods/java.base.jmod</code> and
    +     * searching the output for a line starting with {@code platform}.
    +     */
    +    private String platform;
    +
    +    /**
    +     * Regular expression matching names of modules which depend on the
    +     * the created jmod's module, for which hashes should be added to the
    +     * created jmod.
    +     */
    +    private String hashModulesPattern;
    +
    +    /**
    +     * Whether the created jmod should be seen by Java when present in a
    +     * module path, even if not explicitly named.  Normally true.
    +     */
    +    private boolean resolveByDefault = true;
    +
    +    /**
    +     * Reasons why module resolution during jmod creation may emit warnings.
    +     */
    +    private final List<ResolutionWarningSpec> moduleWarnings =
    +        new ArrayList<>();
    +
    +    /**
    +     * Attribute containing the location of the jmod file to create.
    +     *
    +     * @return location of jmod file
    +     *
    +     * @see #setDestFile(File)
    +     */
    +    public File getDestFile() {
    +        return jmodFile;
    +    }
    +
    +    /**
    +     * Sets attribute containing the location of the jmod file to create.
    +     * This value is required.
    +     *
    +     * @param file location where jmod file will be created.
    +     */
    +    public void setDestFile(final File file) {
    +        this.jmodFile = file;
    +    }
    +
    +    /**
    +     * Adds an unconfigured {@code <classpath>} child element which can
    +     * specify the files which will comprise the created jmod.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setClasspath(Path)
    +     */
    +    public Path createClasspath() {
    +        if (classpath == null) {
    +            classpath = new Path(getProject());
    +        }
    +        return classpath.createPath();
    +    }
    +
    +    /**
    +     * Attribute which specifies the files (usually modular .jar files)
    +     * which will comprise the created jmod file.
    +     *
    +     * @return path of constituent files
    +     *
    +     * @see #setClasspath(Path)
    +     */
    +    public Path getClasspath() {
    +        return classpath;
    +    }
    +
    +    /**
    +     * Sets attribute specifying the files that will comprise the created jmod
    +     * file.  Usually this contains a single modular .jar file.
    +     * <p>
    +     * The classpath is required and must not be empty.
    +     *
    +     * @param path path of files that will comprise jmod
    +     *
    +     * @see #createClasspath()
    +     */
    +    public void setClasspath(final Path path) {
    +        if (classpath == null) {
    +            this.classpath = path;
    +        } else {
    +            classpath.append(path);
    +        }
    +    }
    +
    +    /**
    +     * Sets {@linkplain #setClasspath(Path) classpath attribute} from a
    +     * path reference.
    +     *
    +     * @param ref reference to path which will act as classpath
    +     */
    +    public void setClasspathRef(final Reference ref) {
    +        createClasspath().setRefid(ref);
    +    }
    +
    +    /**
    +     * Creates a child {@code <modulePath>} element which can contain a
    +     * path of directories containing modules upon which modules in the
    +     * {@linkplain #setClasspath(Path) classpath} depend.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setModulePath(Path)
    +     */
    +    public Path createModulePath() {
    +        if (modulePath == null) {
    +            modulePath = new Path(getProject());
    +        }
    +        return modulePath.createPath();
    +    }
    +
    +    /**
    +     * Attribute containing path of directories which contain modules on which
    +     * the created jmod's {@linkplain #setClasspath(Path) constituent modules}
    +     * depend.
    +     *
    +     * @return path of directories containing modules needed by
    +     *         classpath modules
    +     *
    +     * @see #setModulePath(Path)
    +     */
    +    public Path getModulePath() {
    +        return modulePath;
    +    }
    +
    +    /**
    +     * Sets attribute containing path of directories which contain modules
    +     * on which the created jmod's
    +     * {@linkplain #setClasspath(Path) constituent modules} depend.
    +     *
    +     * @param path path of directories containing modules needed by
    +     *             classpath modules
    +     *
    +     * @see #createModulePath()
    +     */
    +    public void setModulePath(final Path path) {
    +        if (modulePath == null) {
    +            this.modulePath = path;
    +        } else {
    +            modulePath.append(path);
    +        }
    +    }
    +
    +    /**
    +     * Sets {@linkplain #setModulePath(Path) module path}
    +     * from a path reference.
    +     *
    +     * @param ref reference to path which will act as module path
    +     */
    +    public void setModulePathRef(final Reference ref) {
    +        createModulePath().setRefid(ref);
    +    }
    +
    +    /**
    +     * Creates a child element which can contain a list of directories
    +     * containing native executable files to include in the created jmod.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setCommandPath(Path)
    +     */
    +    public Path createCommandPath() {
    +        if (commandPath == null) {
    +            commandPath = new Path(getProject());
    +        }
    +        return commandPath.createPath();
    +    }
    +
    +    /**
    +     * Attribute containing path of directories which contain native
    +     * executable files to include in the created jmod.
    +     *
    +     * @return list of directories containing native executables
    +     *
    +     * @see #setCommandPath(Path)
    +     */
    +    public Path getCommandPath() {
    +        return commandPath;
    +    }
    +
    +    /**
    +     * Sets attribute containing path of directories which contain native
    +     * executable files to include in the created jmod.
    +     *
    +     * @param path list of directories containing native executables
    +     *
    +     * @see #createCommandPath()
    +     */
    +    public void setCommandPath(final Path path) {
    +        if (commandPath == null) {
    +            this.commandPath = path;
    +        } else {
    +            commandPath.append(path);
    +        }
    +    }
    +
    +    /**
    +     * Sets {@linkplain #setCommandPath(Path) command path}
    +     * from a path reference.
    +     *
    +     * @param ref reference to path which will act as command path
    +     */
    +    public void setCommandPathRef(final Reference ref) {
    +        createCommandPath().setRefid(ref);
    +    }
    +
    +    /**
    +     * Creates a child element which can contain a list of directories
    +     * containing user configuration files to include in the created jmod.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setConfigPath(Path)
    +     */
    +    public Path createConfigPath() {
    +        if (configPath == null) {
    +            configPath = new Path(getProject());
    +        }
    +        return configPath.createPath();
    +    }
    +
    +    /**
    +     * Attribute containing list of directories which contain
    +     * user configuration files.
    +     *
    +     * @return list of directories containing user configuration files
    +     *
    +     * @see #setConfigPath(Path)
    +     */
    +    public Path getConfigPath() {
    +        return configPath;
    +    }
    +
    +    /**
    +     * Sets attribute containing list of directories which contain
    +     * user configuration files.
    +     *
    +     * @param path list of directories containing user configuration files
    +     *
    +     * @see #createConfigPath()
    +     */
    +    public void setConfigPath(final Path path) {
    +        if (configPath == null) {
    +            this.configPath = path;
    +        } else {
    +            configPath.append(path);
    +        }
    +    }
    +
    +    /**
    +     * Sets {@linkplain #setConfigPath(Path) configuration file path}
    +     * from a path reference.
    +     *
    +     * @param ref reference to path which will act as configuration file path
    +     */
    +    public void setConfigPathRef(final Reference ref) {
    +        createConfigPath().setRefid(ref);
    +    }
    +
    +    /**
    +     * Creates a child element which can contain a list of directories
    +     * containing compile-time header files for third party use, to include
    +     * in the created jmod.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setHeaderPath(Path)
    +     */
    +    public Path createHeaderPath() {
    +        if (headerPath == null) {
    +            headerPath = new Path(getProject());
    +        }
    +        return headerPath.createPath();
    +    }
    +
    +    /**
    +     * Attribute containing a path of directories which hold compile-time
    +     * header files for third party use, all of which will be included in the
    +     * created jmod.
    +     *
    +     * @return path of directories containing header files
    +     */
    +    public Path getHeaderPath() {
    +        return headerPath;
    +    }
    +
    +    /**
    +     * Sets attribute containing a path of directories which hold compile-time
    +     * header files for third party use, all of which will be included in the
    +     * created jmod.
    +     *
    +     * @param path path of directories containing header files
    +     *
    +     * @see #createHeaderPath()
    +     */
    +    public void setHeaderPath(final Path path) {
    +        if (headerPath == null) {
    +            this.headerPath = path;
    +        } else {
    +            headerPath.append(path);
    +        }
    +    }
    +
    +    /**
    +     * Sets {@linkplain #setHeaderPath(Path) header path}
    +     * from a path reference.
    +     *
    +     * @param ref reference to path which will act as header path
    +     */
    +    public void setHeaderPathRef(final Reference ref) {
    +        createHeaderPath().setRefid(ref);
    +    }
    +
    +    /**
    +     * Creates a child element which can contain a list of directories
    +     * containing license files to include in the created jmod.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setLegalPath(Path)
    +     */
    +    public Path createLegalPath() {
    +        if (legalPath == null) {
    +            legalPath = new Path(getProject());
    +        }
    +        return legalPath.createPath();
    +    }
    +
    +    /**
    +     * Attribute containing list of directories which hold license files
    +     * to include in the created jmod.
    +     *
    +     * @return path containing directories which hold license files
    +     */
    +    public Path getLegalPath() {
    +        return legalPath;
    +    }
    +
    +    /**
    +     * Sets attribute containing list of directories which hold license files
    +     * to include in the created jmod.
    +     *
    +     * @param path path containing directories which hold license files
    +     *
    +     * @see #createLegalPath()
    +     */
    +    public void setLegalPath(final Path path) {
    +        if (legalPath == null) {
    +            this.legalPath = path;
    +        } else {
    +            legalPath.append(path);
    +        }
    +    }
    +
    +    /**
    +     * Sets {@linkplain #setLegalPath(Path) legal licenses path}
    +     * from a path reference.
    +     *
    +     * @param ref reference to path which will act as legal path
    +     */
    +    public void setLegalPathRef(final Reference ref) {
    +        createLegalPath().setRefid(ref);
    +    }
    +
    +    /**
    +     * Creates a child element which can contain a list of directories
    +     * containing native libraries to include in the created jmod.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setNativeLibPath(Path)
    +     */
    +    public Path createNativeLibPath() {
    +        if (nativeLibPath == null) {
    +            nativeLibPath = new Path(getProject());
    +        }
    +        return nativeLibPath.createPath();
    +    }
    +
    +    /**
    +     * Attribute containing list of directories which hold native libraries
    +     * to include in the created jmod.
    +     *
    +     * @return path of directories containing native libraries
    +     */
    +    public Path getNativeLibPath() {
    +        return nativeLibPath;
    +    }
    +
    +    /**
    +     * Sets attribute containing list of directories which hold native libraries
    +     * to include in the created jmod.
    +     *
    +     * @param path path of directories containing native libraries
    +     *
    +     * @see #createNativeLibPath()
    +     */
    +    public void setNativeLibPath(final Path path) {
    +        if (nativeLibPath == null) {
    +            this.nativeLibPath = path;
    +        } else {
    +            nativeLibPath.append(path);
    +        }
    +    }
    +
    +    /**
    +     * Sets {@linkplain #setNativeLibPath(Path) native library path}
    +     * from a path reference.
    +     *
    +     * @param ref reference to path which will act as native library path
    +     */
    +    public void setNativeLibPathRef(final Reference ref) {
    +        createNativeLibPath().setRefid(ref);
    +    }
    +
    +    /**
    +     * Creates a child element which can contain a list of directories
    +     * containing man pages (program manuals, typically in troff format)
    +     * to include in the created jmod.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setManPath(Path)
    +     */
    +    public Path createManPath() {
    +        if (manPath == null) {
    +            manPath = new Path(getProject());
    +        }
    +        return manPath.createPath();
    +    }
    +
    +    /**
    +     * Attribute containing list of directories containing man pages
    +     * to include in created jmod.  Man pages are textual program manuals,
    +     * typically in troff format.
    +     *
    +     * @return path containing directories which hold man pages to include
    +     *         in jmod
    +     */
    +    public Path getManPath() {
    +        return manPath;
    +    }
    +
    +    /**
    +     * Sets attribute containing list of directories containing man pages
    +     * to include in created jmod.  Man pages are textual program manuals,
    +     * typically in troff format.
    +     *
    +     * @param path path containing directories which hold man pages to include
    +     *             in jmod
    +     *
    +     * @see #createManPath()
    +     */
    +    public void setManPath(final Path path) {
    +        if (manPath == null) {
    +            this.manPath = path;
    +        } else {
    +            manPath.append(path);
    +        }
    +    }
    +
    +    /**
    +     * Sets {@linkplain #setManPath(Path) man pages path}
    +     * from a path reference.
    +     *
    +     * @param ref reference to path which will act as module path
    +     */
    +    public void setManPathRef(final Reference ref) {
    +        createManPath().setRefid(ref);
    +    }
    +
    +    /**
    +     * Creates an uninitialized child element representing the version of
    +     * the module represented by the created jmod.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setVersion(String)
    +     */
    +    public ModuleVersion createVersion() {
    +        if (moduleVersion != null) {
    +            throw new BuildException(
    +                "No more than one <moduleVersion> element is allowed.",
    +                getLocation());
    +        }
    +        moduleVersion = new ModuleVersion();
    +        return moduleVersion;
    +    }
    +
    +    /**
    +     * Attribute which specifies
    +     * a <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/module/ModuleDescriptor.Version.html">module version</a>
    +     * for created jmod.
    +     *
    +     * @return module version for created jmod
    +     */
    +    public String getVersion() {
    +        return version;
    +    }
    +
    +    /**
    +     * Sets the <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/module/ModuleDescriptor.Version.html">module version</a>
    +     * for the created jmod.
    +     *
    +     * @param version module version of created jmod
    +     *
    +     * @see #createVersion()
    +     */
    +    public void setVersion(final String version) {
    +        this.version = version;
    +    }
    +
    +    /**
    +     * Attribute containing the class that acts as the executable entry point
    +     * of the created jmod.
    +     *
    +     * @return fully-qualified name of jmod's main class
    +     */
    +    public String getMainClass() {
    +        return mainClass;
    +    }
    +
    +    /**
    +     * Sets attribute containing the class that acts as the
    +     * executable entry point of the created jmod.
    +     *
    +     * @param className fully-qualified name of jmod's main class
    +     */
    +    public void setMainClass(final String className) {
    +        this.mainClass = className;
    +    }
    +
    +    /**
    +     * Attribute containing the platform for which the jmod
    +     * will be built.  Platform values are defined in the
    +     * {@code java.base.jmod} of JDKs, and usually take the form
    +     * <var>OS</var>{@code -}<var>architecture</var>.  If unset,
    +     * current platform is used.
    +     *
    +     * @return OS and architecture for which jmod will be built, or {@code null}
    +     */
    +    public String getPlatform() {
    +        return platform;
    +    }
    +
    +    /**
    +     * Sets attribute containing the platform for which the jmod
    +     * will be built.  Platform values are defined in the
    +     * {@code java.base.jmod} of JDKs, and usually take the form
    +     * <var>OS</var>{@code -}<var>architecture</var>.  If unset,
    +     * current platform is used.
    +     * <p>
    +     * A JDK's platform can be viewed with a command like:
    +     * <code>jmod describe $JDK_HOME/jmods/java.base.jmod | grep -i platform</code>.
    +o    *
    +     * @param platform platform for which jmod will be created, or {@code null}
    +     */
    +    public void setPlatform(final String platform) {
    +        this.platform = platform;
    +    }
    +
    +    /**
    +     * Attribute containing a regular expression which specifies which
    +     * of the modules that depend on the jmod being created should have
    +     * hashes generated and added to the jmod.
    +     *
    +     * @return regex specifying which dependent modules should have
    +     *         their generated hashes included
    +     */
    +    public String getHashModulesPattern() {
    +        return hashModulesPattern;
    +    }
    +
    +    /**
    +     * Sets attribute containing a regular expression which specifies which
    +     * of the modules that depend on the jmod being created should have
    +     * hashes generated and added to the jmod.
    +     *
    +     * @param pattern regex specifying which dependent modules should have
    +     *         their generated hashes included
    +     */
    +    public void setHashModulesPattern(final String pattern) {
    +        this.hashModulesPattern = pattern;
    +    }
    +
    +    /**
    +     * Attribute indicating whether the created jmod should be visible
    +     * in a module path, even when not specified explicitly.  True by default.
    +     *
    +     * @return whether jmod should be visible in module paths
    +     */
    +    public boolean getResolveByDefault() {
    +        return resolveByDefault;
    +    }
    +
    +    /**
    +     * Sets attribute indicating whether the created jmod should be visible
    +     * in a module path, even when not specified explicitly.  True by default.
    +     *
    +     * @param resolve whether jmod should be visible in module paths
    +     */
    +    public void setResolveByDefault(final boolean resolve) {
    +        this.resolveByDefault = resolve;
    +    }
    +
    +    /**
    +     * Creates a child element which can specify the circumstances
    +     * under which jmod creation emits warnings.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setModuleWarnings(String)
    +     */
    +    public ResolutionWarningSpec createModuleWarning() {
    +        ResolutionWarningSpec warningSpec = new ResolutionWarningSpec();
    +        moduleWarnings.add(warningSpec);
    +        return warningSpec;
    +    }
    +
    +    /**
    +     * Sets attribute containing a comma-separated list of reasons for
    +     * jmod creation to emit warnings.  Valid values in list are:
    +     * {@code deprecated}, {@code leaving}, {@code incubating}.
    +     *
    +     * @param warningList list containing one or more of the above values,
    +     *                    separated by commas
    +     *
    +     * @see #createModuleWarning()
    +     * @see Jmod.ResolutionWarningReason
    +     */
    +    public void setModuleWarnings(final String warningList) {
    +        for (String warning : warningList.split(",")) {
    +            moduleWarnings.add(new ResolutionWarningSpec(warning));
    +        }
    +    }
    +
    +    /**
    +     * Permissible reasons for jmod creation to emit warnings.
    +     */
    +    public static class ResolutionWarningReason
    +    extends EnumeratedAttribute {
    +        /**
    +         * String value indicating warnings are emitted for modules
    +         * marked as deprecated (but not deprecated for removal).
    +         */
    +        public static final String DEPRECATED = "deprecated";
    +
    +        /**
    +         * String value indicating warnings are emitted for modules
    +         * marked as deprecated for removal.
    +         */
    +        public static final String LEAVING = "leaving";
    +
    +        /**
    +         * String value indicating warnings are emitted for modules
    +         * designated as "incubating" in the JDK.
    +         */
    +        public static final String INCUBATING = "incubating";
    +
    +        /** Maps Ant task values to jmod option values. */
    +        private static final Map<String, String> VALUES_TO_OPTIONS;
    +
    +        static {
    +            Map<String, String> map = new LinkedHashMap<>();
    +            map.put(DEPRECATED, "deprecated");
    +            map.put(LEAVING,    "deprecated-for-removal");
    +            map.put(INCUBATING, "incubating");
    +
    +            VALUES_TO_OPTIONS = Collections.unmodifiableMap(map);
    +        }
    +
    +        @Override
    +        public String[] getValues() {
    +            return VALUES_TO_OPTIONS.keySet().toArray(new String[0]);
    +        }
    +
    +        /**
    +         * Converts this object's current value to a jmod tool
    +         * option value.
    +         *
    +         * @return jmod option value
    +         */
    +        String toCommandLineOption() {
    +            return VALUES_TO_OPTIONS.get(getValue());
    +        }
    +
    +        /**
    +         * Converts a string to a {@code ResolutionWarningReason} instance.
    +         *
    +         * @param s string to convert
    +         *
    +         * @return {@code ResolutionWarningReason} instance corresponding to
    +         *         string argument
    +         *
    +         * @throws BuildException if argument is not a valid
    +         *                        {@code ResolutionWarningReason} value
    +         */
    +        public static ResolutionWarningReason valueOf(String s) {
    +            return (ResolutionWarningReason)
    +                getInstance(ResolutionWarningReason.class, s);
    +        }
    +    }
    +
    +    /**
    +     * Child element which enables jmod tool warnings.  'reason' attribute
    +     * is required.
    +     */
    +    public class ResolutionWarningSpec {
    +        /** Condition which should trigger jmod warning output. */
    +        private ResolutionWarningReason reason;
    +
    +        /**
    +         * Creates an uninitialized element.
    +         */
    +        public ResolutionWarningSpec() {
    +            // Deliberately empty.
    +        }
    +
    +        /**
    +         * Creates an element with the given reason attribute.
    +         *
    +         * @param reason non{@code null} {@link Jmod.ResolutionWarningReason}
    +         *               value
    +         *
    +         * @throws BuildException if argument is not a valid
    +         *                        {@code ResolutionWarningReason}
    +         */
    +        public ResolutionWarningSpec(String reason) {
    +            setReason(ResolutionWarningReason.valueOf(reason));
    +        }
    +
    +        /**
    +         * Required attribute containing reason for emitting jmod warnings.
    +         *
    +         * @return condition which triggers jmod warnings
    +         */
    +        public ResolutionWarningReason getReason() {
    +            return reason;
    +        }
    +
    +        /**
    +         * Sets attribute containing reason for emitting jmod warnings.
    +         *
    +         * @param reason condition which triggers jmod warnings
    +         */
    +        public void setReason(ResolutionWarningReason reason) {
    +            this.reason = reason;
    +        }
    +
    +        /**
    +         * Verifies this object's state.
    +         *
    +         * @throws BuildException if this object's reason is {@code null}
    +         */
    +        public void validate() {
    +            if (reason == null) {
    +                throw new BuildException("reason attribute is required",
    +                    getLocation());
    +            }
    +        }
    +    }
    +
    +    /**
    +     * Checks whether a resource is a directory.  Used for checking validity
    +     * of jmod path arguments which have to be directories.
    +     *
    +     * @param resource resource to check
    +     *
    +     * @return true if resource exists and is not a directory,
    +     *         false if it is a directory or does not exist
    +     */
    +    private static boolean isRegularFile(Resource resource) {
    +        return resource.isExists() && !resource.isDirectory();
    +    }
    +
    +    /**
    +     * Checks that all paths which are required to be directories only,
    +     * refer only to directories.
    +     *
    +     * @throws BuildException if any path has an existing file
    +     *                        which is a non-directory
    +     */
    +    private void checkDirPaths() {
    +        if (modulePath != null
    +            && modulePath.stream().anyMatch(Jmod::isRegularFile)) {
    +
    +            throw new BuildException(
    +                "ModulePath must contain only directories.", getLocation());
    +        }
    +        if (commandPath != null
    +            && commandPath.stream().anyMatch(Jmod::isRegularFile)) {
    +
    +            throw new BuildException(
    +                "CommandPath must contain only directories.", getLocation());
    +        }
    +        if (configPath != null
    +            && configPath.stream().anyMatch(Jmod::isRegularFile)) {
    +
    +            throw new BuildException(
    +                "ConfigPath must contain only directories.", getLocation());
    +        }
    +        if (headerPath != null
    +            && headerPath.stream().anyMatch(Jmod::isRegularFile)) {
    +
    +            throw new BuildException(
    +                "HeaderPath must contain only directories.", getLocation());
    +        }
    +        if (legalPath != null
    +            && legalPath.stream().anyMatch(Jmod::isRegularFile)) {
    +
    +            throw new BuildException(
    +                "LegalPath must contain only directories.", getLocation());
    +        }
    +        if (nativeLibPath != null
    +            && nativeLibPath.stream().anyMatch(Jmod::isRegularFile)) {
    +
    +            throw new BuildException(
    +                "NativeLibPath must contain only directories.", getLocation());
    +        }
    +        if (manPath != null
    +            && manPath.stream().anyMatch(Jmod::isRegularFile)) {
    +
    +            throw new BuildException(
    +                "ManPath must contain only directories.", getLocation());
    +        }
    +    }
    +
    +    /**
    +     * Creates a jmod file according to this task's properties
    +     * and child elements.
    +     *
    +     * @throws BuildException if destFile is not set
    +     * @throws BuildException if classpath is not set or is empty
    +     * @throws BuildException if any path other than classpath refers to an
    +     *                        existing file which is not a directory
    +     * @throws BuildException if both {@code version} attribute and
    +     *                        {@code <version>} child element are present
    +     * @throws BuildException if {@code hashModulesPattern} is set, but
    +     *                        module path is not defined
    +     */
    +    @Override
    +    public void execute()
    +    throws BuildException {
    +
    +        if (jmodFile == null) {
    +            throw new BuildException("Destination file is required.",
    +                getLocation());
    +        }
    +
    +        if (classpath == null) {
    +            throw new BuildException("Classpath is required.",
    +                getLocation());
    +        }
    +
    +        if (classpath.stream().noneMatch(Resource::isExists)) {
    +            throw new BuildException(
    +                "Classpath must contain at least one entry which exists.",
    +                getLocation());
    +        }
    +
    +        if (version != null && moduleVersion != null) {
    +            throw new BuildException(
    +                "version attribute and nested <version> element "
    +                + "cannot both be present.",
    +                getLocation());
    +        }
    +
    +        if (hashModulesPattern != null && !hashModulesPattern.isEmpty()
    +            && modulePath == null) {
    +
    +            throw new BuildException(
    +                "hashModulesPattern requires a module path, since "
    +                + "it will generate hashes of the other modules which depend "
    +                + "on the module being created.",
    +                getLocation());
    +        }
    +
    +        checkDirPaths();
    +
    +        Path[] dependentPaths = {
    +            classpath,
    +            modulePath,
    +            commandPath,
    +            configPath,
    +            headerPath,
    +            legalPath,
    +            nativeLibPath,
    +            manPath,
    +        };
    +        Union allResources = new Union(getProject());
    +        for (Path path : dependentPaths) {
    +            if (path != null) {
    +                for (String entry : path.list()) {
    +                    File entryFile = new File(entry);
    +                    if (entryFile.isDirectory()) {
    +                        log("Will compare timestamp of all files in "
    +                            + "\"" + entryFile + "\" with timestamp of "
    +                            + jmodFile, Project.MSG_VERBOSE);
    +                        FileSet fileSet = new FileSet();
    +                        fileSet.setDir(entryFile);
    +                        allResources.add(fileSet);
    +                    } else {
    +                        log("Will compare timestamp of \"" + entryFile + "\" "
    +                            + "with timestamp of " + jmodFile,
    +                            Project.MSG_VERBOSE);
    +                        allResources.add(new FileResource(entryFile));
    +                    }
    +                }
    +            }
    +        }
    +
    +        ResourceCollection outOfDate =
    +            ResourceUtils.selectOutOfDateSources(this, allResources,
    +                new MergingMapper(jmodFile.toString()),
    +                getProject(),
    +                FileUtils.getFileUtils().getFileTimestampGranularity());
    +
    +        if (outOfDate.isEmpty()) {
    +            log("Skipping jmod creation, since \"" + jmodFile + "\" "
    +                + "is already newer than all files in paths.",
    +                Project.MSG_VERBOSE);
    +            return;
    +        }
    +
    +        Collection<String> args = buildJmodArgs();
    +
    +        try {
    +            log("Deleting " + jmodFile + " if it exists.", Project.MSG_VERBOSE);
    +            Files.deleteIfExists(jmodFile.toPath());
    +        } catch (IOException e) {
    +            throw new BuildException(
    +                "Could not remove old file \"" + jmodFile + "\": " + e, e,
    +                getLocation());
    +        }
    +
    +        ToolProvider jmod = ToolProvider.findFirst("jmod").orElseThrow(
    +            () -> new BuildException("jmod tool not found in JDK.",
    +                getLocation()));
    +
    +        log("Executing: jmod " + String.join(" ", args), Project.MSG_VERBOSE);
    +
    +        ByteArrayOutputStream stdout = new ByteArrayOutputStream();
    +        ByteArrayOutputStream stderr = new ByteArrayOutputStream();
    --- End diff --
    
    Hi Craig, like Stefan already said, this patch is a really good addition to Ant. Thank you for that.
    
    The only question/suggestion I have is, should we instead just pass `System.out` and `System.err` instead of creating a `ByteArrayOutputStream`? That way the actual output and errors are logged instead of we deciding to print it only when the tool execution fails.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
For additional commands, e-mail: dev-help@ant.apache.org


[GitHub] ant issue #80: Added tasks for JDK's jmod and jlink tools.

Posted by asfgit <gi...@git.apache.org>.
Github user asfgit commented on the issue:

    https://github.com/apache/ant/pull/80
  
    
    Refer to this link for build results (access rights to CI server needed): 
    https://builds.apache.org/job/Ant%20Github-PR-Windows/101/



---

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
For additional commands, e-mail: dev-help@ant.apache.org


[GitHub] ant issue #80: Added tasks for JDK's jmod and jlink tools.

Posted by asfgit <gi...@git.apache.org>.
Github user asfgit commented on the issue:

    https://github.com/apache/ant/pull/80
  
    
    Refer to this link for build results (access rights to CI server needed): 
    https://builds.apache.org/job/Ant%20Github-PR-Windows/103/



---

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
For additional commands, e-mail: dev-help@ant.apache.org


[GitHub] ant issue #80: Added tasks for JDK's jmod and jlink tools.

Posted by asfgit <gi...@git.apache.org>.
Github user asfgit commented on the issue:

    https://github.com/apache/ant/pull/80
  
    
    Refer to this link for build results (access rights to CI server needed): 
    https://builds.apache.org/job/Ant%20Github-PR-Linux/95/



---

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
For additional commands, e-mail: dev-help@ant.apache.org


[GitHub] ant pull request #80: Added tasks for JDK's jmod and jlink tools.

Posted by asfgit <gi...@git.apache.org>.
Github user asfgit closed the pull request at:

    https://github.com/apache/ant/pull/80


---

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
For additional commands, e-mail: dev-help@ant.apache.org


[GitHub] ant issue #80: Added tasks for JDK's jmod and jlink tools.

Posted by craigpell <gi...@git.apache.org>.
Github user craigpell commented on the issue:

    https://github.com/apache/ant/pull/80
  
    All done, except that I’m finding there aren’t many man pages on my Linux system which aren’t GPL.  I’ve found a few which are under a BSD license;  is that acceptable?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
For additional commands, e-mail: dev-help@ant.apache.org


[GitHub] ant issue #80: Added tasks for JDK's jmod and jlink tools.

Posted by bodewig <gi...@git.apache.org>.
Github user bodewig commented on the issue:

    https://github.com/apache/ant/pull/80
  
    BSD is perfectly fine, thanks.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
For additional commands, e-mail: dev-help@ant.apache.org


[GitHub] ant issue #80: Added tasks for JDK's jmod and jlink tools.

Posted by bodewig <gi...@git.apache.org>.
Github user bodewig commented on the issue:

    https://github.com/apache/ant/pull/80
  
    This looks great Craig, many thanks!
    
    I've got a short list of wishes, though:
    
    * please use a manual page that is not covered by the GPL inside of the tests - this is me probably being overly cautious but we really don't want to ship anything under the GPL as part of our releases
    * add yourself to `CONTRIBUTORS` and `contributors.xml`
    * add a blurb to `WHATSNEW`
    * add `@since` markers (with 1.10.6) to the new tasks and the new type - and do so to the manual pages as well.
    
    I can do all or some of them myself if you prefer me to do it. In that case I'd ask you to confirm you want to be addressed as "Craig Pell" in the contributors files.
    
    Many thanks again.


---

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
For additional commands, e-mail: dev-help@ant.apache.org


[GitHub] ant pull request #80: Added tasks for JDK's jmod and jlink tools.

Posted by craigpell <gi...@git.apache.org>.
Github user craigpell commented on a diff in the pull request:

    https://github.com/apache/ant/pull/80#discussion_r241010770
  
    --- Diff: src/main/org/apache/tools/ant/taskdefs/modules/Jmod.java ---
    @@ -0,0 +1,1282 @@
    +/*
    + *  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.tools.ant.taskdefs.modules;
    +
    +import java.io.File;
    +import java.io.ByteArrayOutputStream;
    +import java.io.PrintStream;
    +import java.io.IOException;
    +
    +import java.nio.file.Files;
    +
    +import java.util.Collection;
    +import java.util.List;
    +import java.util.ArrayList;
    +
    +import java.util.Map;
    +import java.util.LinkedHashMap;
    +
    +import java.util.Collections;
    +
    +import java.util.spi.ToolProvider;
    +
    +import org.apache.tools.ant.BuildException;
    +import org.apache.tools.ant.Project;
    +import org.apache.tools.ant.Task;
    +
    +import org.apache.tools.ant.util.MergingMapper;
    +import org.apache.tools.ant.util.FileUtils;
    +import org.apache.tools.ant.util.ResourceUtils;
    +
    +import org.apache.tools.ant.types.EnumeratedAttribute;
    +import org.apache.tools.ant.types.FileSet;
    +import org.apache.tools.ant.types.ModuleVersion;
    +import org.apache.tools.ant.types.Path;
    +import org.apache.tools.ant.types.Reference;
    +import org.apache.tools.ant.types.Resource;
    +import org.apache.tools.ant.types.ResourceCollection;
    +
    +import org.apache.tools.ant.types.resources.FileResource;
    +import org.apache.tools.ant.types.resources.Union;
    +
    +/**
    + * Creates a linkable .jmod file from a modular jar file, and optionally from
    + * other resource files such as native libraries and documents.  Equivalent
    + * to the JDK's
    + * <a href="https://docs.oracle.com/en/java/javase/11/tools/jmod.html">jmod</a>
    + * tool.
    + * <p>
    + * Supported attributes:
    + * <dl>
    + * <dt>{@code destFile}
    + * <dd>Required, jmod file to create.
    + * <dt>{@code classpath}
    + * <dt>{@code classpathref}
    + * <dd>Where to locate files to be placed in the jmod file.
    + * <dt>{@code modulepath}
    + * <dt>{@code modulepathref}
    + * <dd>Where to locate dependencies.
    + * <dt>{@code commandpath}
    + * <dt>{@code commandpathref}
    + * <dd>Directories containing native commands to include in jmod.
    + * <dt>{@code headerpath}
    + * <dt>{@code headerpathref}
    + * <dd>Directories containing header files to include in jmod.
    + * <dt>{@code configpath}
    + * <dt>{@code configpathref}
    + * <dd>Directories containing user-editable configuration files
    + *     to include in jmod.
    + * <dt>{@code legalpath}
    + * <dt>{@code legalpathref}
    + * <dd>Directories containing legal licenses and notices to include in jmod.
    + * <dt>{@code nativelibpath}
    + * <dt>{@code nativelibpathref}
    + * <dd>Directories containing native libraries to include in jmod.
    + * <dt>{@code manpath}
    + * <dt>{@code manpathref}
    + * <dd>Directories containing man pages to include in jmod.
    + * <dt>{@code version}
    + * <dd>Module <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/module/ModuleDescriptor.Version.html">version</a>.
    + * <dt>{@code mainclass}
    + * <dd>Main class of module.
    + * <dt>{@code platform}
    + * <dd>The target platform for the jmod.  A particular JDK's platform
    + * can be seen by running
    + * <code>jmod describe $JDK_HOME/jmods/java.base.jmod | grep -i platform</code>.
    + * <dt>{@code hashModulesPattern}
    + * <dd>Regular expression for names of modules in the module path
    + *     which depend on the jmod being created, and which should have
    + *     hashes generated for them and included in the new jmod.
    + * <dt>{@code resolveByDefault}
    + * <dd>Boolean indicating whether the jmod should be one of
    + *     the default resolved modules in an application.  Default is true.
    + * <dt>{@code moduleWarnings}
    + * <dd>Whether to emit warnings when resolving modules which are
    + *     not recommended for use.  Comma-separated list of one of more of
    + *     the following:
    + *     <dl>
    + *     <dt>{@code deprecated}
    + *     <dd>Warn if module is deprecated
    + *     <dt>{@code leaving}
    + *     <dd>Warn if module is deprecated for removal
    + *     <dt>{@code incubating}
    + *     <dd>Warn if module is an incubating (not yet official) module
    + *     </dl>
    + * </dl>
    + *
    + * <p>
    + * Supported nested elements:
    + * <dl>
    + * <dt>{@code <classpath>}
    + * <dd>Path indicating where to locate files to be placed in the jmod file.
    + * <dt>{@code <modulepath>}
    + * <dd>Path indicating where to locate dependencies.
    + * <dt>{@code <commandpath>}
    + * <dd>Path of directories containing native commands to include in jmod.
    + * <dt>{@code <headerpath>}
    + * <dd>Path of directories containing header files to include in jmod.
    + * <dt>{@code <configpath>}
    + * <dd>Path of directories containing user-editable configuration files
    + *     to include in jmod.
    + * <dt>{@code <legalpath>}
    + * <dd>Path of directories containing legal notices to include in jmod.
    + * <dt>{@code <nativelibpath>}
    + * <dd>Path of directories containing native libraries to include in jmod.
    + * <dt>{@code <manpath>}
    + * <dd>Path of directories containing man pages to include in jmod.
    + * <dt>{@code <version>}
    + * <dd><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/module/ModuleDescriptor.Version.html">Module version</a> of jmod.
    + *     Must have a required {@code number} attribute.  May also have optional
    + *     {@code preRelease} and {@code build} attributes.
    + * <dt>{@code <moduleWarning>}
    + * <dd>Has one required attribute, {@code reason}.  See {@code moduleWarnings}
    + *     attribute above.  This element may be specified multiple times.
    + * </dl>
    + * <p>
    + * destFile and classpath are required data.
    + */
    +public class Jmod
    +extends Task {
    +    /** Location of jmod file to be created. */
    +    private File jmodFile;
    +
    +    /**
    +     * Path of files (usually jar files or directories containing
    +     * compiled classes) from which to create jmod.
    +     */
    +    private Path classpath;
    +
    +    /**
    +     * Path of directories containing modules on which the modules
    +     * in the classpath depend.
    +     */
    +    private Path modulePath;
    +
    +    /**
    +     * Path of directories containing executable files to bundle in the
    +     * created jmod.
    +     */
    +    private Path commandPath;
    +
    +    /**
    +     * Path of directories containing configuration files to bundle in the
    +     * created jmod.
    +     */
    +    private Path configPath;
    +
    +    /**
    +     * Path of directories containing includable header files (such as for
    +     * other languages) to bundle in the created jmod.
    +     */
    +    private Path headerPath;
    +
    +    /**
    +     * Path of directories containing legal license files to bundle
    +     * in the created jmod.
    +     */
    +    private Path legalPath;
    +
    +    /**
    +     * Path of directories containing native libraries needed by classes
    +     * in the modules comprising the created jmod.
    +     */
    +    private Path nativeLibPath;
    +
    +    /**
    +     * Path of directories containing manual pages to bundle
    +     * in the created jmod.
    +     */
    +    private Path manPath;
    +
    +    /**
    +     * Module version of jmod.  Either this or {@link #moduleVersion}
    +     * may be set.
    +     */
    +    private String version;
    +
    +    /** Module version of jmod.  Either this or {@link #version} may be set. */
    +    private ModuleVersion moduleVersion;
    +
    +    /**
    +     * Main class to execute, if Java attempts to execute jmod's module
    +     * without specifying a main class explicitly.
    +     */
    +    private String mainClass;
    +
    +    /**
    +     * Target platform of created jmod.  Examples are {@code windows-amd64}
    +     * and {@code linux-amd64}.  Target platform is an attribute
    +     * of each JDK, which can be seen by executing
    +     * <code>jmod describe $JDK_HOME/jmods/java.base.jmod</code> and
    +     * searching the output for a line starting with {@code platform}.
    +     */
    +    private String platform;
    +
    +    /**
    +     * Regular expression matching names of modules which depend on the
    +     * the created jmod's module, for which hashes should be added to the
    +     * created jmod.
    +     */
    +    private String hashModulesPattern;
    +
    +    /**
    +     * Whether the created jmod should be seen by Java when present in a
    +     * module path, even if not explicitly named.  Normally true.
    +     */
    +    private boolean resolveByDefault = true;
    +
    +    /**
    +     * Reasons why module resolution during jmod creation may emit warnings.
    +     */
    +    private final List<ResolutionWarningSpec> moduleWarnings =
    +        new ArrayList<>();
    +
    +    /**
    +     * Attribute containing the location of the jmod file to create.
    +     *
    +     * @return location of jmod file
    +     *
    +     * @see #setDestFile(File)
    +     */
    +    public File getDestFile() {
    +        return jmodFile;
    +    }
    +
    +    /**
    +     * Sets attribute containing the location of the jmod file to create.
    +     * This value is required.
    +     *
    +     * @param file location where jmod file will be created.
    +     */
    +    public void setDestFile(final File file) {
    +        this.jmodFile = file;
    +    }
    +
    +    /**
    +     * Adds an unconfigured {@code <classpath>} child element which can
    +     * specify the files which will comprise the created jmod.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setClasspath(Path)
    +     */
    +    public Path createClasspath() {
    +        if (classpath == null) {
    +            classpath = new Path(getProject());
    +        }
    +        return classpath.createPath();
    +    }
    +
    +    /**
    +     * Attribute which specifies the files (usually modular .jar files)
    +     * which will comprise the created jmod file.
    +     *
    +     * @return path of constituent files
    +     *
    +     * @see #setClasspath(Path)
    +     */
    +    public Path getClasspath() {
    +        return classpath;
    +    }
    +
    +    /**
    +     * Sets attribute specifying the files that will comprise the created jmod
    +     * file.  Usually this contains a single modular .jar file.
    +     * <p>
    +     * The classpath is required and must not be empty.
    +     *
    +     * @param path path of files that will comprise jmod
    +     *
    +     * @see #createClasspath()
    +     */
    +    public void setClasspath(final Path path) {
    +        if (classpath == null) {
    +            this.classpath = path;
    +        } else {
    +            classpath.append(path);
    +        }
    +    }
    +
    +    /**
    +     * Sets {@linkplain #setClasspath(Path) classpath attribute} from a
    +     * path reference.
    +     *
    +     * @param ref reference to path which will act as classpath
    +     */
    +    public void setClasspathRef(final Reference ref) {
    +        createClasspath().setRefid(ref);
    +    }
    +
    +    /**
    +     * Creates a child {@code <modulePath>} element which can contain a
    +     * path of directories containing modules upon which modules in the
    +     * {@linkplain #setClasspath(Path) classpath} depend.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setModulePath(Path)
    +     */
    +    public Path createModulePath() {
    +        if (modulePath == null) {
    +            modulePath = new Path(getProject());
    +        }
    +        return modulePath.createPath();
    +    }
    +
    +    /**
    +     * Attribute containing path of directories which contain modules on which
    +     * the created jmod's {@linkplain #setClasspath(Path) constituent modules}
    +     * depend.
    +     *
    +     * @return path of directories containing modules needed by
    +     *         classpath modules
    +     *
    +     * @see #setModulePath(Path)
    +     */
    +    public Path getModulePath() {
    +        return modulePath;
    +    }
    +
    +    /**
    +     * Sets attribute containing path of directories which contain modules
    +     * on which the created jmod's
    +     * {@linkplain #setClasspath(Path) constituent modules} depend.
    +     *
    +     * @param path path of directories containing modules needed by
    +     *             classpath modules
    +     *
    +     * @see #createModulePath()
    +     */
    +    public void setModulePath(final Path path) {
    +        if (modulePath == null) {
    +            this.modulePath = path;
    +        } else {
    +            modulePath.append(path);
    +        }
    +    }
    +
    +    /**
    +     * Sets {@linkplain #setModulePath(Path) module path}
    +     * from a path reference.
    +     *
    +     * @param ref reference to path which will act as module path
    +     */
    +    public void setModulePathRef(final Reference ref) {
    +        createModulePath().setRefid(ref);
    +    }
    +
    +    /**
    +     * Creates a child element which can contain a list of directories
    +     * containing native executable files to include in the created jmod.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setCommandPath(Path)
    +     */
    +    public Path createCommandPath() {
    +        if (commandPath == null) {
    +            commandPath = new Path(getProject());
    +        }
    +        return commandPath.createPath();
    +    }
    +
    +    /**
    +     * Attribute containing path of directories which contain native
    +     * executable files to include in the created jmod.
    +     *
    +     * @return list of directories containing native executables
    +     *
    +     * @see #setCommandPath(Path)
    +     */
    +    public Path getCommandPath() {
    +        return commandPath;
    +    }
    +
    +    /**
    +     * Sets attribute containing path of directories which contain native
    +     * executable files to include in the created jmod.
    +     *
    +     * @param path list of directories containing native executables
    +     *
    +     * @see #createCommandPath()
    +     */
    +    public void setCommandPath(final Path path) {
    +        if (commandPath == null) {
    +            this.commandPath = path;
    +        } else {
    +            commandPath.append(path);
    +        }
    +    }
    +
    +    /**
    +     * Sets {@linkplain #setCommandPath(Path) command path}
    +     * from a path reference.
    +     *
    +     * @param ref reference to path which will act as command path
    +     */
    +    public void setCommandPathRef(final Reference ref) {
    +        createCommandPath().setRefid(ref);
    +    }
    +
    +    /**
    +     * Creates a child element which can contain a list of directories
    +     * containing user configuration files to include in the created jmod.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setConfigPath(Path)
    +     */
    +    public Path createConfigPath() {
    +        if (configPath == null) {
    +            configPath = new Path(getProject());
    +        }
    +        return configPath.createPath();
    +    }
    +
    +    /**
    +     * Attribute containing list of directories which contain
    +     * user configuration files.
    +     *
    +     * @return list of directories containing user configuration files
    +     *
    +     * @see #setConfigPath(Path)
    +     */
    +    public Path getConfigPath() {
    +        return configPath;
    +    }
    +
    +    /**
    +     * Sets attribute containing list of directories which contain
    +     * user configuration files.
    +     *
    +     * @param path list of directories containing user configuration files
    +     *
    +     * @see #createConfigPath()
    +     */
    +    public void setConfigPath(final Path path) {
    +        if (configPath == null) {
    +            this.configPath = path;
    +        } else {
    +            configPath.append(path);
    +        }
    +    }
    +
    +    /**
    +     * Sets {@linkplain #setConfigPath(Path) configuration file path}
    +     * from a path reference.
    +     *
    +     * @param ref reference to path which will act as configuration file path
    +     */
    +    public void setConfigPathRef(final Reference ref) {
    +        createConfigPath().setRefid(ref);
    +    }
    +
    +    /**
    +     * Creates a child element which can contain a list of directories
    +     * containing compile-time header files for third party use, to include
    +     * in the created jmod.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setHeaderPath(Path)
    +     */
    +    public Path createHeaderPath() {
    +        if (headerPath == null) {
    +            headerPath = new Path(getProject());
    +        }
    +        return headerPath.createPath();
    +    }
    +
    +    /**
    +     * Attribute containing a path of directories which hold compile-time
    +     * header files for third party use, all of which will be included in the
    +     * created jmod.
    +     *
    +     * @return path of directories containing header files
    +     */
    +    public Path getHeaderPath() {
    +        return headerPath;
    +    }
    +
    +    /**
    +     * Sets attribute containing a path of directories which hold compile-time
    +     * header files for third party use, all of which will be included in the
    +     * created jmod.
    +     *
    +     * @param path path of directories containing header files
    +     *
    +     * @see #createHeaderPath()
    +     */
    +    public void setHeaderPath(final Path path) {
    +        if (headerPath == null) {
    +            this.headerPath = path;
    +        } else {
    +            headerPath.append(path);
    +        }
    +    }
    +
    +    /**
    +     * Sets {@linkplain #setHeaderPath(Path) header path}
    +     * from a path reference.
    +     *
    +     * @param ref reference to path which will act as header path
    +     */
    +    public void setHeaderPathRef(final Reference ref) {
    +        createHeaderPath().setRefid(ref);
    +    }
    +
    +    /**
    +     * Creates a child element which can contain a list of directories
    +     * containing license files to include in the created jmod.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setLegalPath(Path)
    +     */
    +    public Path createLegalPath() {
    +        if (legalPath == null) {
    +            legalPath = new Path(getProject());
    +        }
    +        return legalPath.createPath();
    +    }
    +
    +    /**
    +     * Attribute containing list of directories which hold license files
    +     * to include in the created jmod.
    +     *
    +     * @return path containing directories which hold license files
    +     */
    +    public Path getLegalPath() {
    +        return legalPath;
    +    }
    +
    +    /**
    +     * Sets attribute containing list of directories which hold license files
    +     * to include in the created jmod.
    +     *
    +     * @param path path containing directories which hold license files
    +     *
    +     * @see #createLegalPath()
    +     */
    +    public void setLegalPath(final Path path) {
    +        if (legalPath == null) {
    +            this.legalPath = path;
    +        } else {
    +            legalPath.append(path);
    +        }
    +    }
    +
    +    /**
    +     * Sets {@linkplain #setLegalPath(Path) legal licenses path}
    +     * from a path reference.
    +     *
    +     * @param ref reference to path which will act as legal path
    +     */
    +    public void setLegalPathRef(final Reference ref) {
    +        createLegalPath().setRefid(ref);
    +    }
    +
    +    /**
    +     * Creates a child element which can contain a list of directories
    +     * containing native libraries to include in the created jmod.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setNativeLibPath(Path)
    +     */
    +    public Path createNativeLibPath() {
    +        if (nativeLibPath == null) {
    +            nativeLibPath = new Path(getProject());
    +        }
    +        return nativeLibPath.createPath();
    +    }
    +
    +    /**
    +     * Attribute containing list of directories which hold native libraries
    +     * to include in the created jmod.
    +     *
    +     * @return path of directories containing native libraries
    +     */
    +    public Path getNativeLibPath() {
    +        return nativeLibPath;
    +    }
    +
    +    /**
    +     * Sets attribute containing list of directories which hold native libraries
    +     * to include in the created jmod.
    +     *
    +     * @param path path of directories containing native libraries
    +     *
    +     * @see #createNativeLibPath()
    +     */
    +    public void setNativeLibPath(final Path path) {
    +        if (nativeLibPath == null) {
    +            this.nativeLibPath = path;
    +        } else {
    +            nativeLibPath.append(path);
    +        }
    +    }
    +
    +    /**
    +     * Sets {@linkplain #setNativeLibPath(Path) native library path}
    +     * from a path reference.
    +     *
    +     * @param ref reference to path which will act as native library path
    +     */
    +    public void setNativeLibPathRef(final Reference ref) {
    +        createNativeLibPath().setRefid(ref);
    +    }
    +
    +    /**
    +     * Creates a child element which can contain a list of directories
    +     * containing man pages (program manuals, typically in troff format)
    +     * to include in the created jmod.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setManPath(Path)
    +     */
    +    public Path createManPath() {
    +        if (manPath == null) {
    +            manPath = new Path(getProject());
    +        }
    +        return manPath.createPath();
    +    }
    +
    +    /**
    +     * Attribute containing list of directories containing man pages
    +     * to include in created jmod.  Man pages are textual program manuals,
    +     * typically in troff format.
    +     *
    +     * @return path containing directories which hold man pages to include
    +     *         in jmod
    +     */
    +    public Path getManPath() {
    +        return manPath;
    +    }
    +
    +    /**
    +     * Sets attribute containing list of directories containing man pages
    +     * to include in created jmod.  Man pages are textual program manuals,
    +     * typically in troff format.
    +     *
    +     * @param path path containing directories which hold man pages to include
    +     *             in jmod
    +     *
    +     * @see #createManPath()
    +     */
    +    public void setManPath(final Path path) {
    +        if (manPath == null) {
    +            this.manPath = path;
    +        } else {
    +            manPath.append(path);
    +        }
    +    }
    +
    +    /**
    +     * Sets {@linkplain #setManPath(Path) man pages path}
    +     * from a path reference.
    +     *
    +     * @param ref reference to path which will act as module path
    +     */
    +    public void setManPathRef(final Reference ref) {
    +        createManPath().setRefid(ref);
    +    }
    +
    +    /**
    +     * Creates an uninitialized child element representing the version of
    +     * the module represented by the created jmod.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setVersion(String)
    +     */
    +    public ModuleVersion createVersion() {
    +        if (moduleVersion != null) {
    +            throw new BuildException(
    +                "No more than one <moduleVersion> element is allowed.",
    +                getLocation());
    +        }
    +        moduleVersion = new ModuleVersion();
    +        return moduleVersion;
    +    }
    +
    +    /**
    +     * Attribute which specifies
    +     * a <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/module/ModuleDescriptor.Version.html">module version</a>
    +     * for created jmod.
    +     *
    +     * @return module version for created jmod
    +     */
    +    public String getVersion() {
    +        return version;
    +    }
    +
    +    /**
    +     * Sets the <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/module/ModuleDescriptor.Version.html">module version</a>
    +     * for the created jmod.
    +     *
    +     * @param version module version of created jmod
    +     *
    +     * @see #createVersion()
    +     */
    +    public void setVersion(final String version) {
    +        this.version = version;
    +    }
    +
    +    /**
    +     * Attribute containing the class that acts as the executable entry point
    +     * of the created jmod.
    +     *
    +     * @return fully-qualified name of jmod's main class
    +     */
    +    public String getMainClass() {
    +        return mainClass;
    +    }
    +
    +    /**
    +     * Sets attribute containing the class that acts as the
    +     * executable entry point of the created jmod.
    +     *
    +     * @param className fully-qualified name of jmod's main class
    +     */
    +    public void setMainClass(final String className) {
    +        this.mainClass = className;
    +    }
    +
    +    /**
    +     * Attribute containing the platform for which the jmod
    +     * will be built.  Platform values are defined in the
    +     * {@code java.base.jmod} of JDKs, and usually take the form
    +     * <var>OS</var>{@code -}<var>architecture</var>.  If unset,
    +     * current platform is used.
    +     *
    +     * @return OS and architecture for which jmod will be built, or {@code null}
    +     */
    +    public String getPlatform() {
    +        return platform;
    +    }
    +
    +    /**
    +     * Sets attribute containing the platform for which the jmod
    +     * will be built.  Platform values are defined in the
    +     * {@code java.base.jmod} of JDKs, and usually take the form
    +     * <var>OS</var>{@code -}<var>architecture</var>.  If unset,
    +     * current platform is used.
    +     * <p>
    +     * A JDK's platform can be viewed with a command like:
    +     * <code>jmod describe $JDK_HOME/jmods/java.base.jmod | grep -i platform</code>.
    +o    *
    +     * @param platform platform for which jmod will be created, or {@code null}
    +     */
    +    public void setPlatform(final String platform) {
    +        this.platform = platform;
    +    }
    +
    +    /**
    +     * Attribute containing a regular expression which specifies which
    +     * of the modules that depend on the jmod being created should have
    +     * hashes generated and added to the jmod.
    +     *
    +     * @return regex specifying which dependent modules should have
    +     *         their generated hashes included
    +     */
    +    public String getHashModulesPattern() {
    +        return hashModulesPattern;
    +    }
    +
    +    /**
    +     * Sets attribute containing a regular expression which specifies which
    +     * of the modules that depend on the jmod being created should have
    +     * hashes generated and added to the jmod.
    +     *
    +     * @param pattern regex specifying which dependent modules should have
    +     *         their generated hashes included
    +     */
    +    public void setHashModulesPattern(final String pattern) {
    +        this.hashModulesPattern = pattern;
    +    }
    +
    +    /**
    +     * Attribute indicating whether the created jmod should be visible
    +     * in a module path, even when not specified explicitly.  True by default.
    +     *
    +     * @return whether jmod should be visible in module paths
    +     */
    +    public boolean getResolveByDefault() {
    +        return resolveByDefault;
    +    }
    +
    +    /**
    +     * Sets attribute indicating whether the created jmod should be visible
    +     * in a module path, even when not specified explicitly.  True by default.
    +     *
    +     * @param resolve whether jmod should be visible in module paths
    +     */
    +    public void setResolveByDefault(final boolean resolve) {
    +        this.resolveByDefault = resolve;
    +    }
    +
    +    /**
    +     * Creates a child element which can specify the circumstances
    +     * under which jmod creation emits warnings.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setModuleWarnings(String)
    +     */
    +    public ResolutionWarningSpec createModuleWarning() {
    +        ResolutionWarningSpec warningSpec = new ResolutionWarningSpec();
    +        moduleWarnings.add(warningSpec);
    +        return warningSpec;
    +    }
    +
    +    /**
    +     * Sets attribute containing a comma-separated list of reasons for
    +     * jmod creation to emit warnings.  Valid values in list are:
    +     * {@code deprecated}, {@code leaving}, {@code incubating}.
    +     *
    +     * @param warningList list containing one or more of the above values,
    +     *                    separated by commas
    +     *
    +     * @see #createModuleWarning()
    +     * @see Jmod.ResolutionWarningReason
    +     */
    +    public void setModuleWarnings(final String warningList) {
    +        for (String warning : warningList.split(",")) {
    +            moduleWarnings.add(new ResolutionWarningSpec(warning));
    +        }
    +    }
    +
    +    /**
    +     * Permissible reasons for jmod creation to emit warnings.
    +     */
    +    public static class ResolutionWarningReason
    +    extends EnumeratedAttribute {
    +        /**
    +         * String value indicating warnings are emitted for modules
    +         * marked as deprecated (but not deprecated for removal).
    +         */
    +        public static final String DEPRECATED = "deprecated";
    +
    +        /**
    +         * String value indicating warnings are emitted for modules
    +         * marked as deprecated for removal.
    +         */
    +        public static final String LEAVING = "leaving";
    +
    +        /**
    +         * String value indicating warnings are emitted for modules
    +         * designated as "incubating" in the JDK.
    +         */
    +        public static final String INCUBATING = "incubating";
    +
    +        /** Maps Ant task values to jmod option values. */
    +        private static final Map<String, String> VALUES_TO_OPTIONS;
    +
    +        static {
    +            Map<String, String> map = new LinkedHashMap<>();
    +            map.put(DEPRECATED, "deprecated");
    +            map.put(LEAVING,    "deprecated-for-removal");
    +            map.put(INCUBATING, "incubating");
    +
    +            VALUES_TO_OPTIONS = Collections.unmodifiableMap(map);
    +        }
    +
    +        @Override
    +        public String[] getValues() {
    +            return VALUES_TO_OPTIONS.keySet().toArray(new String[0]);
    +        }
    +
    +        /**
    +         * Converts this object's current value to a jmod tool
    +         * option value.
    +         *
    +         * @return jmod option value
    +         */
    +        String toCommandLineOption() {
    +            return VALUES_TO_OPTIONS.get(getValue());
    +        }
    +
    +        /**
    +         * Converts a string to a {@code ResolutionWarningReason} instance.
    +         *
    +         * @param s string to convert
    +         *
    +         * @return {@code ResolutionWarningReason} instance corresponding to
    +         *         string argument
    +         *
    +         * @throws BuildException if argument is not a valid
    +         *                        {@code ResolutionWarningReason} value
    +         */
    +        public static ResolutionWarningReason valueOf(String s) {
    +            return (ResolutionWarningReason)
    +                getInstance(ResolutionWarningReason.class, s);
    +        }
    +    }
    +
    +    /**
    +     * Child element which enables jmod tool warnings.  'reason' attribute
    +     * is required.
    +     */
    +    public class ResolutionWarningSpec {
    +        /** Condition which should trigger jmod warning output. */
    +        private ResolutionWarningReason reason;
    +
    +        /**
    +         * Creates an uninitialized element.
    +         */
    +        public ResolutionWarningSpec() {
    +            // Deliberately empty.
    +        }
    +
    +        /**
    +         * Creates an element with the given reason attribute.
    +         *
    +         * @param reason non{@code null} {@link Jmod.ResolutionWarningReason}
    +         *               value
    +         *
    +         * @throws BuildException if argument is not a valid
    +         *                        {@code ResolutionWarningReason}
    +         */
    +        public ResolutionWarningSpec(String reason) {
    +            setReason(ResolutionWarningReason.valueOf(reason));
    +        }
    +
    +        /**
    +         * Required attribute containing reason for emitting jmod warnings.
    +         *
    +         * @return condition which triggers jmod warnings
    +         */
    +        public ResolutionWarningReason getReason() {
    +            return reason;
    +        }
    +
    +        /**
    +         * Sets attribute containing reason for emitting jmod warnings.
    +         *
    +         * @param reason condition which triggers jmod warnings
    +         */
    +        public void setReason(ResolutionWarningReason reason) {
    +            this.reason = reason;
    +        }
    +
    +        /**
    +         * Verifies this object's state.
    +         *
    +         * @throws BuildException if this object's reason is {@code null}
    +         */
    +        public void validate() {
    +            if (reason == null) {
    +                throw new BuildException("reason attribute is required",
    +                    getLocation());
    +            }
    +        }
    +    }
    +
    +    /**
    +     * Checks whether a resource is a directory.  Used for checking validity
    +     * of jmod path arguments which have to be directories.
    +     *
    +     * @param resource resource to check
    +     *
    +     * @return true if resource exists and is not a directory,
    +     *         false if it is a directory or does not exist
    +     */
    +    private static boolean isRegularFile(Resource resource) {
    +        return resource.isExists() && !resource.isDirectory();
    +    }
    +
    +    /**
    +     * Checks that all paths which are required to be directories only,
    +     * refer only to directories.
    +     *
    +     * @throws BuildException if any path has an existing file
    +     *                        which is a non-directory
    +     */
    +    private void checkDirPaths() {
    +        if (modulePath != null
    +            && modulePath.stream().anyMatch(Jmod::isRegularFile)) {
    +
    +            throw new BuildException(
    +                "ModulePath must contain only directories.", getLocation());
    +        }
    +        if (commandPath != null
    +            && commandPath.stream().anyMatch(Jmod::isRegularFile)) {
    +
    +            throw new BuildException(
    +                "CommandPath must contain only directories.", getLocation());
    +        }
    +        if (configPath != null
    +            && configPath.stream().anyMatch(Jmod::isRegularFile)) {
    +
    +            throw new BuildException(
    +                "ConfigPath must contain only directories.", getLocation());
    +        }
    +        if (headerPath != null
    +            && headerPath.stream().anyMatch(Jmod::isRegularFile)) {
    +
    +            throw new BuildException(
    +                "HeaderPath must contain only directories.", getLocation());
    +        }
    +        if (legalPath != null
    +            && legalPath.stream().anyMatch(Jmod::isRegularFile)) {
    +
    +            throw new BuildException(
    +                "LegalPath must contain only directories.", getLocation());
    +        }
    +        if (nativeLibPath != null
    +            && nativeLibPath.stream().anyMatch(Jmod::isRegularFile)) {
    +
    +            throw new BuildException(
    +                "NativeLibPath must contain only directories.", getLocation());
    +        }
    +        if (manPath != null
    +            && manPath.stream().anyMatch(Jmod::isRegularFile)) {
    +
    +            throw new BuildException(
    +                "ManPath must contain only directories.", getLocation());
    +        }
    +    }
    +
    +    /**
    +     * Creates a jmod file according to this task's properties
    +     * and child elements.
    +     *
    +     * @throws BuildException if destFile is not set
    +     * @throws BuildException if classpath is not set or is empty
    +     * @throws BuildException if any path other than classpath refers to an
    +     *                        existing file which is not a directory
    +     * @throws BuildException if both {@code version} attribute and
    +     *                        {@code <version>} child element are present
    +     * @throws BuildException if {@code hashModulesPattern} is set, but
    +     *                        module path is not defined
    +     */
    +    @Override
    +    public void execute()
    +    throws BuildException {
    +
    +        if (jmodFile == null) {
    +            throw new BuildException("Destination file is required.",
    +                getLocation());
    +        }
    +
    +        if (classpath == null) {
    +            throw new BuildException("Classpath is required.",
    +                getLocation());
    +        }
    +
    +        if (classpath.stream().noneMatch(Resource::isExists)) {
    +            throw new BuildException(
    +                "Classpath must contain at least one entry which exists.",
    +                getLocation());
    +        }
    +
    +        if (version != null && moduleVersion != null) {
    +            throw new BuildException(
    +                "version attribute and nested <version> element "
    +                + "cannot both be present.",
    +                getLocation());
    +        }
    +
    +        if (hashModulesPattern != null && !hashModulesPattern.isEmpty()
    +            && modulePath == null) {
    +
    +            throw new BuildException(
    +                "hashModulesPattern requires a module path, since "
    +                + "it will generate hashes of the other modules which depend "
    +                + "on the module being created.",
    +                getLocation());
    +        }
    +
    +        checkDirPaths();
    +
    +        Path[] dependentPaths = {
    +            classpath,
    +            modulePath,
    +            commandPath,
    +            configPath,
    +            headerPath,
    +            legalPath,
    +            nativeLibPath,
    +            manPath,
    +        };
    +        Union allResources = new Union(getProject());
    +        for (Path path : dependentPaths) {
    +            if (path != null) {
    +                for (String entry : path.list()) {
    +                    File entryFile = new File(entry);
    +                    if (entryFile.isDirectory()) {
    +                        log("Will compare timestamp of all files in "
    +                            + "\"" + entryFile + "\" with timestamp of "
    +                            + jmodFile, Project.MSG_VERBOSE);
    +                        FileSet fileSet = new FileSet();
    +                        fileSet.setDir(entryFile);
    +                        allResources.add(fileSet);
    +                    } else {
    +                        log("Will compare timestamp of \"" + entryFile + "\" "
    +                            + "with timestamp of " + jmodFile,
    +                            Project.MSG_VERBOSE);
    +                        allResources.add(new FileResource(entryFile));
    +                    }
    +                }
    +            }
    +        }
    +
    +        ResourceCollection outOfDate =
    +            ResourceUtils.selectOutOfDateSources(this, allResources,
    +                new MergingMapper(jmodFile.toString()),
    +                getProject(),
    +                FileUtils.getFileUtils().getFileTimestampGranularity());
    +
    +        if (outOfDate.isEmpty()) {
    +            log("Skipping jmod creation, since \"" + jmodFile + "\" "
    +                + "is already newer than all files in paths.",
    +                Project.MSG_VERBOSE);
    +            return;
    +        }
    +
    +        Collection<String> args = buildJmodArgs();
    +
    +        try {
    +            log("Deleting " + jmodFile + " if it exists.", Project.MSG_VERBOSE);
    +            Files.deleteIfExists(jmodFile.toPath());
    +        } catch (IOException e) {
    +            throw new BuildException(
    +                "Could not remove old file \"" + jmodFile + "\": " + e, e,
    +                getLocation());
    +        }
    +
    +        ToolProvider jmod = ToolProvider.findFirst("jmod").orElseThrow(
    +            () -> new BuildException("jmod tool not found in JDK.",
    +                getLocation()));
    +
    +        log("Executing: jmod " + String.join(" ", args), Project.MSG_VERBOSE);
    +
    +        ByteArrayOutputStream stdout = new ByteArrayOutputStream();
    +        ByteArrayOutputStream stderr = new ByteArrayOutputStream();
    --- End diff --
    
    That was what I tried first, but the output and error were never showing up at all in unit tests.  (Also, my observation has been that the tool produces no output when it runs successfully.)


---

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
For additional commands, e-mail: dev-help@ant.apache.org


[GitHub] ant issue #80: Added tasks for JDK's jmod and jlink tools.

Posted by asfgit <gi...@git.apache.org>.
Github user asfgit commented on the issue:

    https://github.com/apache/ant/pull/80
  
    Can one of the admins verify this patch?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
For additional commands, e-mail: dev-help@ant.apache.org


[GitHub] ant pull request #80: Added tasks for JDK's jmod and jlink tools.

Posted by bodewig <gi...@git.apache.org>.
Github user bodewig commented on a diff in the pull request:

    https://github.com/apache/ant/pull/80#discussion_r241292280
  
    --- Diff: src/main/org/apache/tools/ant/taskdefs/modules/Jmod.java ---
    @@ -0,0 +1,1282 @@
    +/*
    + *  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.tools.ant.taskdefs.modules;
    +
    +import java.io.File;
    +import java.io.ByteArrayOutputStream;
    +import java.io.PrintStream;
    +import java.io.IOException;
    +
    +import java.nio.file.Files;
    +
    +import java.util.Collection;
    +import java.util.List;
    +import java.util.ArrayList;
    +
    +import java.util.Map;
    +import java.util.LinkedHashMap;
    +
    +import java.util.Collections;
    +
    +import java.util.spi.ToolProvider;
    +
    +import org.apache.tools.ant.BuildException;
    +import org.apache.tools.ant.Project;
    +import org.apache.tools.ant.Task;
    +
    +import org.apache.tools.ant.util.MergingMapper;
    +import org.apache.tools.ant.util.FileUtils;
    +import org.apache.tools.ant.util.ResourceUtils;
    +
    +import org.apache.tools.ant.types.EnumeratedAttribute;
    +import org.apache.tools.ant.types.FileSet;
    +import org.apache.tools.ant.types.ModuleVersion;
    +import org.apache.tools.ant.types.Path;
    +import org.apache.tools.ant.types.Reference;
    +import org.apache.tools.ant.types.Resource;
    +import org.apache.tools.ant.types.ResourceCollection;
    +
    +import org.apache.tools.ant.types.resources.FileResource;
    +import org.apache.tools.ant.types.resources.Union;
    +
    +/**
    + * Creates a linkable .jmod file from a modular jar file, and optionally from
    + * other resource files such as native libraries and documents.  Equivalent
    + * to the JDK's
    + * <a href="https://docs.oracle.com/en/java/javase/11/tools/jmod.html">jmod</a>
    + * tool.
    + * <p>
    + * Supported attributes:
    + * <dl>
    + * <dt>{@code destFile}
    + * <dd>Required, jmod file to create.
    + * <dt>{@code classpath}
    + * <dt>{@code classpathref}
    + * <dd>Where to locate files to be placed in the jmod file.
    + * <dt>{@code modulepath}
    + * <dt>{@code modulepathref}
    + * <dd>Where to locate dependencies.
    + * <dt>{@code commandpath}
    + * <dt>{@code commandpathref}
    + * <dd>Directories containing native commands to include in jmod.
    + * <dt>{@code headerpath}
    + * <dt>{@code headerpathref}
    + * <dd>Directories containing header files to include in jmod.
    + * <dt>{@code configpath}
    + * <dt>{@code configpathref}
    + * <dd>Directories containing user-editable configuration files
    + *     to include in jmod.
    + * <dt>{@code legalpath}
    + * <dt>{@code legalpathref}
    + * <dd>Directories containing legal licenses and notices to include in jmod.
    + * <dt>{@code nativelibpath}
    + * <dt>{@code nativelibpathref}
    + * <dd>Directories containing native libraries to include in jmod.
    + * <dt>{@code manpath}
    + * <dt>{@code manpathref}
    + * <dd>Directories containing man pages to include in jmod.
    + * <dt>{@code version}
    + * <dd>Module <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/module/ModuleDescriptor.Version.html">version</a>.
    + * <dt>{@code mainclass}
    + * <dd>Main class of module.
    + * <dt>{@code platform}
    + * <dd>The target platform for the jmod.  A particular JDK's platform
    + * can be seen by running
    + * <code>jmod describe $JDK_HOME/jmods/java.base.jmod | grep -i platform</code>.
    + * <dt>{@code hashModulesPattern}
    + * <dd>Regular expression for names of modules in the module path
    + *     which depend on the jmod being created, and which should have
    + *     hashes generated for them and included in the new jmod.
    + * <dt>{@code resolveByDefault}
    + * <dd>Boolean indicating whether the jmod should be one of
    + *     the default resolved modules in an application.  Default is true.
    + * <dt>{@code moduleWarnings}
    + * <dd>Whether to emit warnings when resolving modules which are
    + *     not recommended for use.  Comma-separated list of one of more of
    + *     the following:
    + *     <dl>
    + *     <dt>{@code deprecated}
    + *     <dd>Warn if module is deprecated
    + *     <dt>{@code leaving}
    + *     <dd>Warn if module is deprecated for removal
    + *     <dt>{@code incubating}
    + *     <dd>Warn if module is an incubating (not yet official) module
    + *     </dl>
    + * </dl>
    + *
    + * <p>
    + * Supported nested elements:
    + * <dl>
    + * <dt>{@code <classpath>}
    + * <dd>Path indicating where to locate files to be placed in the jmod file.
    + * <dt>{@code <modulepath>}
    + * <dd>Path indicating where to locate dependencies.
    + * <dt>{@code <commandpath>}
    + * <dd>Path of directories containing native commands to include in jmod.
    + * <dt>{@code <headerpath>}
    + * <dd>Path of directories containing header files to include in jmod.
    + * <dt>{@code <configpath>}
    + * <dd>Path of directories containing user-editable configuration files
    + *     to include in jmod.
    + * <dt>{@code <legalpath>}
    + * <dd>Path of directories containing legal notices to include in jmod.
    + * <dt>{@code <nativelibpath>}
    + * <dd>Path of directories containing native libraries to include in jmod.
    + * <dt>{@code <manpath>}
    + * <dd>Path of directories containing man pages to include in jmod.
    + * <dt>{@code <version>}
    + * <dd><a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/module/ModuleDescriptor.Version.html">Module version</a> of jmod.
    + *     Must have a required {@code number} attribute.  May also have optional
    + *     {@code preRelease} and {@code build} attributes.
    + * <dt>{@code <moduleWarning>}
    + * <dd>Has one required attribute, {@code reason}.  See {@code moduleWarnings}
    + *     attribute above.  This element may be specified multiple times.
    + * </dl>
    + * <p>
    + * destFile and classpath are required data.
    + */
    +public class Jmod
    +extends Task {
    +    /** Location of jmod file to be created. */
    +    private File jmodFile;
    +
    +    /**
    +     * Path of files (usually jar files or directories containing
    +     * compiled classes) from which to create jmod.
    +     */
    +    private Path classpath;
    +
    +    /**
    +     * Path of directories containing modules on which the modules
    +     * in the classpath depend.
    +     */
    +    private Path modulePath;
    +
    +    /**
    +     * Path of directories containing executable files to bundle in the
    +     * created jmod.
    +     */
    +    private Path commandPath;
    +
    +    /**
    +     * Path of directories containing configuration files to bundle in the
    +     * created jmod.
    +     */
    +    private Path configPath;
    +
    +    /**
    +     * Path of directories containing includable header files (such as for
    +     * other languages) to bundle in the created jmod.
    +     */
    +    private Path headerPath;
    +
    +    /**
    +     * Path of directories containing legal license files to bundle
    +     * in the created jmod.
    +     */
    +    private Path legalPath;
    +
    +    /**
    +     * Path of directories containing native libraries needed by classes
    +     * in the modules comprising the created jmod.
    +     */
    +    private Path nativeLibPath;
    +
    +    /**
    +     * Path of directories containing manual pages to bundle
    +     * in the created jmod.
    +     */
    +    private Path manPath;
    +
    +    /**
    +     * Module version of jmod.  Either this or {@link #moduleVersion}
    +     * may be set.
    +     */
    +    private String version;
    +
    +    /** Module version of jmod.  Either this or {@link #version} may be set. */
    +    private ModuleVersion moduleVersion;
    +
    +    /**
    +     * Main class to execute, if Java attempts to execute jmod's module
    +     * without specifying a main class explicitly.
    +     */
    +    private String mainClass;
    +
    +    /**
    +     * Target platform of created jmod.  Examples are {@code windows-amd64}
    +     * and {@code linux-amd64}.  Target platform is an attribute
    +     * of each JDK, which can be seen by executing
    +     * <code>jmod describe $JDK_HOME/jmods/java.base.jmod</code> and
    +     * searching the output for a line starting with {@code platform}.
    +     */
    +    private String platform;
    +
    +    /**
    +     * Regular expression matching names of modules which depend on the
    +     * the created jmod's module, for which hashes should be added to the
    +     * created jmod.
    +     */
    +    private String hashModulesPattern;
    +
    +    /**
    +     * Whether the created jmod should be seen by Java when present in a
    +     * module path, even if not explicitly named.  Normally true.
    +     */
    +    private boolean resolveByDefault = true;
    +
    +    /**
    +     * Reasons why module resolution during jmod creation may emit warnings.
    +     */
    +    private final List<ResolutionWarningSpec> moduleWarnings =
    +        new ArrayList<>();
    +
    +    /**
    +     * Attribute containing the location of the jmod file to create.
    +     *
    +     * @return location of jmod file
    +     *
    +     * @see #setDestFile(File)
    +     */
    +    public File getDestFile() {
    +        return jmodFile;
    +    }
    +
    +    /**
    +     * Sets attribute containing the location of the jmod file to create.
    +     * This value is required.
    +     *
    +     * @param file location where jmod file will be created.
    +     */
    +    public void setDestFile(final File file) {
    +        this.jmodFile = file;
    +    }
    +
    +    /**
    +     * Adds an unconfigured {@code <classpath>} child element which can
    +     * specify the files which will comprise the created jmod.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setClasspath(Path)
    +     */
    +    public Path createClasspath() {
    +        if (classpath == null) {
    +            classpath = new Path(getProject());
    +        }
    +        return classpath.createPath();
    +    }
    +
    +    /**
    +     * Attribute which specifies the files (usually modular .jar files)
    +     * which will comprise the created jmod file.
    +     *
    +     * @return path of constituent files
    +     *
    +     * @see #setClasspath(Path)
    +     */
    +    public Path getClasspath() {
    +        return classpath;
    +    }
    +
    +    /**
    +     * Sets attribute specifying the files that will comprise the created jmod
    +     * file.  Usually this contains a single modular .jar file.
    +     * <p>
    +     * The classpath is required and must not be empty.
    +     *
    +     * @param path path of files that will comprise jmod
    +     *
    +     * @see #createClasspath()
    +     */
    +    public void setClasspath(final Path path) {
    +        if (classpath == null) {
    +            this.classpath = path;
    +        } else {
    +            classpath.append(path);
    +        }
    +    }
    +
    +    /**
    +     * Sets {@linkplain #setClasspath(Path) classpath attribute} from a
    +     * path reference.
    +     *
    +     * @param ref reference to path which will act as classpath
    +     */
    +    public void setClasspathRef(final Reference ref) {
    +        createClasspath().setRefid(ref);
    +    }
    +
    +    /**
    +     * Creates a child {@code <modulePath>} element which can contain a
    +     * path of directories containing modules upon which modules in the
    +     * {@linkplain #setClasspath(Path) classpath} depend.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setModulePath(Path)
    +     */
    +    public Path createModulePath() {
    +        if (modulePath == null) {
    +            modulePath = new Path(getProject());
    +        }
    +        return modulePath.createPath();
    +    }
    +
    +    /**
    +     * Attribute containing path of directories which contain modules on which
    +     * the created jmod's {@linkplain #setClasspath(Path) constituent modules}
    +     * depend.
    +     *
    +     * @return path of directories containing modules needed by
    +     *         classpath modules
    +     *
    +     * @see #setModulePath(Path)
    +     */
    +    public Path getModulePath() {
    +        return modulePath;
    +    }
    +
    +    /**
    +     * Sets attribute containing path of directories which contain modules
    +     * on which the created jmod's
    +     * {@linkplain #setClasspath(Path) constituent modules} depend.
    +     *
    +     * @param path path of directories containing modules needed by
    +     *             classpath modules
    +     *
    +     * @see #createModulePath()
    +     */
    +    public void setModulePath(final Path path) {
    +        if (modulePath == null) {
    +            this.modulePath = path;
    +        } else {
    +            modulePath.append(path);
    +        }
    +    }
    +
    +    /**
    +     * Sets {@linkplain #setModulePath(Path) module path}
    +     * from a path reference.
    +     *
    +     * @param ref reference to path which will act as module path
    +     */
    +    public void setModulePathRef(final Reference ref) {
    +        createModulePath().setRefid(ref);
    +    }
    +
    +    /**
    +     * Creates a child element which can contain a list of directories
    +     * containing native executable files to include in the created jmod.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setCommandPath(Path)
    +     */
    +    public Path createCommandPath() {
    +        if (commandPath == null) {
    +            commandPath = new Path(getProject());
    +        }
    +        return commandPath.createPath();
    +    }
    +
    +    /**
    +     * Attribute containing path of directories which contain native
    +     * executable files to include in the created jmod.
    +     *
    +     * @return list of directories containing native executables
    +     *
    +     * @see #setCommandPath(Path)
    +     */
    +    public Path getCommandPath() {
    +        return commandPath;
    +    }
    +
    +    /**
    +     * Sets attribute containing path of directories which contain native
    +     * executable files to include in the created jmod.
    +     *
    +     * @param path list of directories containing native executables
    +     *
    +     * @see #createCommandPath()
    +     */
    +    public void setCommandPath(final Path path) {
    +        if (commandPath == null) {
    +            this.commandPath = path;
    +        } else {
    +            commandPath.append(path);
    +        }
    +    }
    +
    +    /**
    +     * Sets {@linkplain #setCommandPath(Path) command path}
    +     * from a path reference.
    +     *
    +     * @param ref reference to path which will act as command path
    +     */
    +    public void setCommandPathRef(final Reference ref) {
    +        createCommandPath().setRefid(ref);
    +    }
    +
    +    /**
    +     * Creates a child element which can contain a list of directories
    +     * containing user configuration files to include in the created jmod.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setConfigPath(Path)
    +     */
    +    public Path createConfigPath() {
    +        if (configPath == null) {
    +            configPath = new Path(getProject());
    +        }
    +        return configPath.createPath();
    +    }
    +
    +    /**
    +     * Attribute containing list of directories which contain
    +     * user configuration files.
    +     *
    +     * @return list of directories containing user configuration files
    +     *
    +     * @see #setConfigPath(Path)
    +     */
    +    public Path getConfigPath() {
    +        return configPath;
    +    }
    +
    +    /**
    +     * Sets attribute containing list of directories which contain
    +     * user configuration files.
    +     *
    +     * @param path list of directories containing user configuration files
    +     *
    +     * @see #createConfigPath()
    +     */
    +    public void setConfigPath(final Path path) {
    +        if (configPath == null) {
    +            this.configPath = path;
    +        } else {
    +            configPath.append(path);
    +        }
    +    }
    +
    +    /**
    +     * Sets {@linkplain #setConfigPath(Path) configuration file path}
    +     * from a path reference.
    +     *
    +     * @param ref reference to path which will act as configuration file path
    +     */
    +    public void setConfigPathRef(final Reference ref) {
    +        createConfigPath().setRefid(ref);
    +    }
    +
    +    /**
    +     * Creates a child element which can contain a list of directories
    +     * containing compile-time header files for third party use, to include
    +     * in the created jmod.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setHeaderPath(Path)
    +     */
    +    public Path createHeaderPath() {
    +        if (headerPath == null) {
    +            headerPath = new Path(getProject());
    +        }
    +        return headerPath.createPath();
    +    }
    +
    +    /**
    +     * Attribute containing a path of directories which hold compile-time
    +     * header files for third party use, all of which will be included in the
    +     * created jmod.
    +     *
    +     * @return path of directories containing header files
    +     */
    +    public Path getHeaderPath() {
    +        return headerPath;
    +    }
    +
    +    /**
    +     * Sets attribute containing a path of directories which hold compile-time
    +     * header files for third party use, all of which will be included in the
    +     * created jmod.
    +     *
    +     * @param path path of directories containing header files
    +     *
    +     * @see #createHeaderPath()
    +     */
    +    public void setHeaderPath(final Path path) {
    +        if (headerPath == null) {
    +            this.headerPath = path;
    +        } else {
    +            headerPath.append(path);
    +        }
    +    }
    +
    +    /**
    +     * Sets {@linkplain #setHeaderPath(Path) header path}
    +     * from a path reference.
    +     *
    +     * @param ref reference to path which will act as header path
    +     */
    +    public void setHeaderPathRef(final Reference ref) {
    +        createHeaderPath().setRefid(ref);
    +    }
    +
    +    /**
    +     * Creates a child element which can contain a list of directories
    +     * containing license files to include in the created jmod.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setLegalPath(Path)
    +     */
    +    public Path createLegalPath() {
    +        if (legalPath == null) {
    +            legalPath = new Path(getProject());
    +        }
    +        return legalPath.createPath();
    +    }
    +
    +    /**
    +     * Attribute containing list of directories which hold license files
    +     * to include in the created jmod.
    +     *
    +     * @return path containing directories which hold license files
    +     */
    +    public Path getLegalPath() {
    +        return legalPath;
    +    }
    +
    +    /**
    +     * Sets attribute containing list of directories which hold license files
    +     * to include in the created jmod.
    +     *
    +     * @param path path containing directories which hold license files
    +     *
    +     * @see #createLegalPath()
    +     */
    +    public void setLegalPath(final Path path) {
    +        if (legalPath == null) {
    +            this.legalPath = path;
    +        } else {
    +            legalPath.append(path);
    +        }
    +    }
    +
    +    /**
    +     * Sets {@linkplain #setLegalPath(Path) legal licenses path}
    +     * from a path reference.
    +     *
    +     * @param ref reference to path which will act as legal path
    +     */
    +    public void setLegalPathRef(final Reference ref) {
    +        createLegalPath().setRefid(ref);
    +    }
    +
    +    /**
    +     * Creates a child element which can contain a list of directories
    +     * containing native libraries to include in the created jmod.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setNativeLibPath(Path)
    +     */
    +    public Path createNativeLibPath() {
    +        if (nativeLibPath == null) {
    +            nativeLibPath = new Path(getProject());
    +        }
    +        return nativeLibPath.createPath();
    +    }
    +
    +    /**
    +     * Attribute containing list of directories which hold native libraries
    +     * to include in the created jmod.
    +     *
    +     * @return path of directories containing native libraries
    +     */
    +    public Path getNativeLibPath() {
    +        return nativeLibPath;
    +    }
    +
    +    /**
    +     * Sets attribute containing list of directories which hold native libraries
    +     * to include in the created jmod.
    +     *
    +     * @param path path of directories containing native libraries
    +     *
    +     * @see #createNativeLibPath()
    +     */
    +    public void setNativeLibPath(final Path path) {
    +        if (nativeLibPath == null) {
    +            this.nativeLibPath = path;
    +        } else {
    +            nativeLibPath.append(path);
    +        }
    +    }
    +
    +    /**
    +     * Sets {@linkplain #setNativeLibPath(Path) native library path}
    +     * from a path reference.
    +     *
    +     * @param ref reference to path which will act as native library path
    +     */
    +    public void setNativeLibPathRef(final Reference ref) {
    +        createNativeLibPath().setRefid(ref);
    +    }
    +
    +    /**
    +     * Creates a child element which can contain a list of directories
    +     * containing man pages (program manuals, typically in troff format)
    +     * to include in the created jmod.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setManPath(Path)
    +     */
    +    public Path createManPath() {
    +        if (manPath == null) {
    +            manPath = new Path(getProject());
    +        }
    +        return manPath.createPath();
    +    }
    +
    +    /**
    +     * Attribute containing list of directories containing man pages
    +     * to include in created jmod.  Man pages are textual program manuals,
    +     * typically in troff format.
    +     *
    +     * @return path containing directories which hold man pages to include
    +     *         in jmod
    +     */
    +    public Path getManPath() {
    +        return manPath;
    +    }
    +
    +    /**
    +     * Sets attribute containing list of directories containing man pages
    +     * to include in created jmod.  Man pages are textual program manuals,
    +     * typically in troff format.
    +     *
    +     * @param path path containing directories which hold man pages to include
    +     *             in jmod
    +     *
    +     * @see #createManPath()
    +     */
    +    public void setManPath(final Path path) {
    +        if (manPath == null) {
    +            this.manPath = path;
    +        } else {
    +            manPath.append(path);
    +        }
    +    }
    +
    +    /**
    +     * Sets {@linkplain #setManPath(Path) man pages path}
    +     * from a path reference.
    +     *
    +     * @param ref reference to path which will act as module path
    +     */
    +    public void setManPathRef(final Reference ref) {
    +        createManPath().setRefid(ref);
    +    }
    +
    +    /**
    +     * Creates an uninitialized child element representing the version of
    +     * the module represented by the created jmod.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setVersion(String)
    +     */
    +    public ModuleVersion createVersion() {
    +        if (moduleVersion != null) {
    +            throw new BuildException(
    +                "No more than one <moduleVersion> element is allowed.",
    +                getLocation());
    +        }
    +        moduleVersion = new ModuleVersion();
    +        return moduleVersion;
    +    }
    +
    +    /**
    +     * Attribute which specifies
    +     * a <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/module/ModuleDescriptor.Version.html">module version</a>
    +     * for created jmod.
    +     *
    +     * @return module version for created jmod
    +     */
    +    public String getVersion() {
    +        return version;
    +    }
    +
    +    /**
    +     * Sets the <a href="https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/module/ModuleDescriptor.Version.html">module version</a>
    +     * for the created jmod.
    +     *
    +     * @param version module version of created jmod
    +     *
    +     * @see #createVersion()
    +     */
    +    public void setVersion(final String version) {
    +        this.version = version;
    +    }
    +
    +    /**
    +     * Attribute containing the class that acts as the executable entry point
    +     * of the created jmod.
    +     *
    +     * @return fully-qualified name of jmod's main class
    +     */
    +    public String getMainClass() {
    +        return mainClass;
    +    }
    +
    +    /**
    +     * Sets attribute containing the class that acts as the
    +     * executable entry point of the created jmod.
    +     *
    +     * @param className fully-qualified name of jmod's main class
    +     */
    +    public void setMainClass(final String className) {
    +        this.mainClass = className;
    +    }
    +
    +    /**
    +     * Attribute containing the platform for which the jmod
    +     * will be built.  Platform values are defined in the
    +     * {@code java.base.jmod} of JDKs, and usually take the form
    +     * <var>OS</var>{@code -}<var>architecture</var>.  If unset,
    +     * current platform is used.
    +     *
    +     * @return OS and architecture for which jmod will be built, or {@code null}
    +     */
    +    public String getPlatform() {
    +        return platform;
    +    }
    +
    +    /**
    +     * Sets attribute containing the platform for which the jmod
    +     * will be built.  Platform values are defined in the
    +     * {@code java.base.jmod} of JDKs, and usually take the form
    +     * <var>OS</var>{@code -}<var>architecture</var>.  If unset,
    +     * current platform is used.
    +     * <p>
    +     * A JDK's platform can be viewed with a command like:
    +     * <code>jmod describe $JDK_HOME/jmods/java.base.jmod | grep -i platform</code>.
    +o    *
    +     * @param platform platform for which jmod will be created, or {@code null}
    +     */
    +    public void setPlatform(final String platform) {
    +        this.platform = platform;
    +    }
    +
    +    /**
    +     * Attribute containing a regular expression which specifies which
    +     * of the modules that depend on the jmod being created should have
    +     * hashes generated and added to the jmod.
    +     *
    +     * @return regex specifying which dependent modules should have
    +     *         their generated hashes included
    +     */
    +    public String getHashModulesPattern() {
    +        return hashModulesPattern;
    +    }
    +
    +    /**
    +     * Sets attribute containing a regular expression which specifies which
    +     * of the modules that depend on the jmod being created should have
    +     * hashes generated and added to the jmod.
    +     *
    +     * @param pattern regex specifying which dependent modules should have
    +     *         their generated hashes included
    +     */
    +    public void setHashModulesPattern(final String pattern) {
    +        this.hashModulesPattern = pattern;
    +    }
    +
    +    /**
    +     * Attribute indicating whether the created jmod should be visible
    +     * in a module path, even when not specified explicitly.  True by default.
    +     *
    +     * @return whether jmod should be visible in module paths
    +     */
    +    public boolean getResolveByDefault() {
    +        return resolveByDefault;
    +    }
    +
    +    /**
    +     * Sets attribute indicating whether the created jmod should be visible
    +     * in a module path, even when not specified explicitly.  True by default.
    +     *
    +     * @param resolve whether jmod should be visible in module paths
    +     */
    +    public void setResolveByDefault(final boolean resolve) {
    +        this.resolveByDefault = resolve;
    +    }
    +
    +    /**
    +     * Creates a child element which can specify the circumstances
    +     * under which jmod creation emits warnings.
    +     *
    +     * @return new, unconfigured child element
    +     *
    +     * @see #setModuleWarnings(String)
    +     */
    +    public ResolutionWarningSpec createModuleWarning() {
    +        ResolutionWarningSpec warningSpec = new ResolutionWarningSpec();
    +        moduleWarnings.add(warningSpec);
    +        return warningSpec;
    +    }
    +
    +    /**
    +     * Sets attribute containing a comma-separated list of reasons for
    +     * jmod creation to emit warnings.  Valid values in list are:
    +     * {@code deprecated}, {@code leaving}, {@code incubating}.
    +     *
    +     * @param warningList list containing one or more of the above values,
    +     *                    separated by commas
    +     *
    +     * @see #createModuleWarning()
    +     * @see Jmod.ResolutionWarningReason
    +     */
    +    public void setModuleWarnings(final String warningList) {
    +        for (String warning : warningList.split(",")) {
    +            moduleWarnings.add(new ResolutionWarningSpec(warning));
    +        }
    +    }
    +
    +    /**
    +     * Permissible reasons for jmod creation to emit warnings.
    +     */
    +    public static class ResolutionWarningReason
    +    extends EnumeratedAttribute {
    +        /**
    +         * String value indicating warnings are emitted for modules
    +         * marked as deprecated (but not deprecated for removal).
    +         */
    +        public static final String DEPRECATED = "deprecated";
    +
    +        /**
    +         * String value indicating warnings are emitted for modules
    +         * marked as deprecated for removal.
    +         */
    +        public static final String LEAVING = "leaving";
    +
    +        /**
    +         * String value indicating warnings are emitted for modules
    +         * designated as "incubating" in the JDK.
    +         */
    +        public static final String INCUBATING = "incubating";
    +
    +        /** Maps Ant task values to jmod option values. */
    +        private static final Map<String, String> VALUES_TO_OPTIONS;
    +
    +        static {
    +            Map<String, String> map = new LinkedHashMap<>();
    +            map.put(DEPRECATED, "deprecated");
    +            map.put(LEAVING,    "deprecated-for-removal");
    +            map.put(INCUBATING, "incubating");
    +
    +            VALUES_TO_OPTIONS = Collections.unmodifiableMap(map);
    +        }
    +
    +        @Override
    +        public String[] getValues() {
    +            return VALUES_TO_OPTIONS.keySet().toArray(new String[0]);
    +        }
    +
    +        /**
    +         * Converts this object's current value to a jmod tool
    +         * option value.
    +         *
    +         * @return jmod option value
    +         */
    +        String toCommandLineOption() {
    +            return VALUES_TO_OPTIONS.get(getValue());
    +        }
    +
    +        /**
    +         * Converts a string to a {@code ResolutionWarningReason} instance.
    +         *
    +         * @param s string to convert
    +         *
    +         * @return {@code ResolutionWarningReason} instance corresponding to
    +         *         string argument
    +         *
    +         * @throws BuildException if argument is not a valid
    +         *                        {@code ResolutionWarningReason} value
    +         */
    +        public static ResolutionWarningReason valueOf(String s) {
    +            return (ResolutionWarningReason)
    +                getInstance(ResolutionWarningReason.class, s);
    +        }
    +    }
    +
    +    /**
    +     * Child element which enables jmod tool warnings.  'reason' attribute
    +     * is required.
    +     */
    +    public class ResolutionWarningSpec {
    +        /** Condition which should trigger jmod warning output. */
    +        private ResolutionWarningReason reason;
    +
    +        /**
    +         * Creates an uninitialized element.
    +         */
    +        public ResolutionWarningSpec() {
    +            // Deliberately empty.
    +        }
    +
    +        /**
    +         * Creates an element with the given reason attribute.
    +         *
    +         * @param reason non{@code null} {@link Jmod.ResolutionWarningReason}
    +         *               value
    +         *
    +         * @throws BuildException if argument is not a valid
    +         *                        {@code ResolutionWarningReason}
    +         */
    +        public ResolutionWarningSpec(String reason) {
    +            setReason(ResolutionWarningReason.valueOf(reason));
    +        }
    +
    +        /**
    +         * Required attribute containing reason for emitting jmod warnings.
    +         *
    +         * @return condition which triggers jmod warnings
    +         */
    +        public ResolutionWarningReason getReason() {
    +            return reason;
    +        }
    +
    +        /**
    +         * Sets attribute containing reason for emitting jmod warnings.
    +         *
    +         * @param reason condition which triggers jmod warnings
    +         */
    +        public void setReason(ResolutionWarningReason reason) {
    +            this.reason = reason;
    +        }
    +
    +        /**
    +         * Verifies this object's state.
    +         *
    +         * @throws BuildException if this object's reason is {@code null}
    +         */
    +        public void validate() {
    +            if (reason == null) {
    +                throw new BuildException("reason attribute is required",
    +                    getLocation());
    +            }
    +        }
    +    }
    +
    +    /**
    +     * Checks whether a resource is a directory.  Used for checking validity
    +     * of jmod path arguments which have to be directories.
    +     *
    +     * @param resource resource to check
    +     *
    +     * @return true if resource exists and is not a directory,
    +     *         false if it is a directory or does not exist
    +     */
    +    private static boolean isRegularFile(Resource resource) {
    +        return resource.isExists() && !resource.isDirectory();
    +    }
    +
    +    /**
    +     * Checks that all paths which are required to be directories only,
    +     * refer only to directories.
    +     *
    +     * @throws BuildException if any path has an existing file
    +     *                        which is a non-directory
    +     */
    +    private void checkDirPaths() {
    +        if (modulePath != null
    +            && modulePath.stream().anyMatch(Jmod::isRegularFile)) {
    +
    +            throw new BuildException(
    +                "ModulePath must contain only directories.", getLocation());
    +        }
    +        if (commandPath != null
    +            && commandPath.stream().anyMatch(Jmod::isRegularFile)) {
    +
    +            throw new BuildException(
    +                "CommandPath must contain only directories.", getLocation());
    +        }
    +        if (configPath != null
    +            && configPath.stream().anyMatch(Jmod::isRegularFile)) {
    +
    +            throw new BuildException(
    +                "ConfigPath must contain only directories.", getLocation());
    +        }
    +        if (headerPath != null
    +            && headerPath.stream().anyMatch(Jmod::isRegularFile)) {
    +
    +            throw new BuildException(
    +                "HeaderPath must contain only directories.", getLocation());
    +        }
    +        if (legalPath != null
    +            && legalPath.stream().anyMatch(Jmod::isRegularFile)) {
    +
    +            throw new BuildException(
    +                "LegalPath must contain only directories.", getLocation());
    +        }
    +        if (nativeLibPath != null
    +            && nativeLibPath.stream().anyMatch(Jmod::isRegularFile)) {
    +
    +            throw new BuildException(
    +                "NativeLibPath must contain only directories.", getLocation());
    +        }
    +        if (manPath != null
    +            && manPath.stream().anyMatch(Jmod::isRegularFile)) {
    +
    +            throw new BuildException(
    +                "ManPath must contain only directories.", getLocation());
    +        }
    +    }
    +
    +    /**
    +     * Creates a jmod file according to this task's properties
    +     * and child elements.
    +     *
    +     * @throws BuildException if destFile is not set
    +     * @throws BuildException if classpath is not set or is empty
    +     * @throws BuildException if any path other than classpath refers to an
    +     *                        existing file which is not a directory
    +     * @throws BuildException if both {@code version} attribute and
    +     *                        {@code <version>} child element are present
    +     * @throws BuildException if {@code hashModulesPattern} is set, but
    +     *                        module path is not defined
    +     */
    +    @Override
    +    public void execute()
    +    throws BuildException {
    +
    +        if (jmodFile == null) {
    +            throw new BuildException("Destination file is required.",
    +                getLocation());
    +        }
    +
    +        if (classpath == null) {
    +            throw new BuildException("Classpath is required.",
    +                getLocation());
    +        }
    +
    +        if (classpath.stream().noneMatch(Resource::isExists)) {
    +            throw new BuildException(
    +                "Classpath must contain at least one entry which exists.",
    +                getLocation());
    +        }
    +
    +        if (version != null && moduleVersion != null) {
    +            throw new BuildException(
    +                "version attribute and nested <version> element "
    +                + "cannot both be present.",
    +                getLocation());
    +        }
    +
    +        if (hashModulesPattern != null && !hashModulesPattern.isEmpty()
    +            && modulePath == null) {
    +
    +            throw new BuildException(
    +                "hashModulesPattern requires a module path, since "
    +                + "it will generate hashes of the other modules which depend "
    +                + "on the module being created.",
    +                getLocation());
    +        }
    +
    +        checkDirPaths();
    +
    +        Path[] dependentPaths = {
    +            classpath,
    +            modulePath,
    +            commandPath,
    +            configPath,
    +            headerPath,
    +            legalPath,
    +            nativeLibPath,
    +            manPath,
    +        };
    +        Union allResources = new Union(getProject());
    +        for (Path path : dependentPaths) {
    +            if (path != null) {
    +                for (String entry : path.list()) {
    +                    File entryFile = new File(entry);
    +                    if (entryFile.isDirectory()) {
    +                        log("Will compare timestamp of all files in "
    +                            + "\"" + entryFile + "\" with timestamp of "
    +                            + jmodFile, Project.MSG_VERBOSE);
    +                        FileSet fileSet = new FileSet();
    +                        fileSet.setDir(entryFile);
    +                        allResources.add(fileSet);
    +                    } else {
    +                        log("Will compare timestamp of \"" + entryFile + "\" "
    +                            + "with timestamp of " + jmodFile,
    +                            Project.MSG_VERBOSE);
    +                        allResources.add(new FileResource(entryFile));
    +                    }
    +                }
    +            }
    +        }
    +
    +        ResourceCollection outOfDate =
    +            ResourceUtils.selectOutOfDateSources(this, allResources,
    +                new MergingMapper(jmodFile.toString()),
    +                getProject(),
    +                FileUtils.getFileUtils().getFileTimestampGranularity());
    +
    +        if (outOfDate.isEmpty()) {
    +            log("Skipping jmod creation, since \"" + jmodFile + "\" "
    +                + "is already newer than all files in paths.",
    +                Project.MSG_VERBOSE);
    +            return;
    +        }
    +
    +        Collection<String> args = buildJmodArgs();
    +
    +        try {
    +            log("Deleting " + jmodFile + " if it exists.", Project.MSG_VERBOSE);
    +            Files.deleteIfExists(jmodFile.toPath());
    +        } catch (IOException e) {
    +            throw new BuildException(
    +                "Could not remove old file \"" + jmodFile + "\": " + e, e,
    +                getLocation());
    +        }
    +
    +        ToolProvider jmod = ToolProvider.findFirst("jmod").orElseThrow(
    +            () -> new BuildException("jmod tool not found in JDK.",
    +                getLocation()));
    +
    +        log("Executing: jmod " + String.join(" ", args), Project.MSG_VERBOSE);
    +
    +        ByteArrayOutputStream stdout = new ByteArrayOutputStream();
    +        ByteArrayOutputStream stderr = new ByteArrayOutputStream();
    --- End diff --
    
    Test setup captures streams so it may become difficult. Would using `LogOutputStreams` do which would send the output directly to An't logging system?


---

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
For additional commands, e-mail: dev-help@ant.apache.org


[GitHub] ant issue #80: Added tasks for JDK's jmod and jlink tools.

Posted by jaikiran <gi...@git.apache.org>.
Github user jaikiran commented on the issue:

    https://github.com/apache/ant/pull/80
  
    this is ok to test


---

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
For additional commands, e-mail: dev-help@ant.apache.org


[GitHub] ant issue #80: Added tasks for JDK's jmod and jlink tools.

Posted by asfgit <gi...@git.apache.org>.
Github user asfgit commented on the issue:

    https://github.com/apache/ant/pull/80
  
    
    Refer to this link for build results (access rights to CI server needed): 
    https://builds.apache.org/job/Ant%20Github-PR-Linux/97/



---

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@ant.apache.org
For additional commands, e-mail: dev-help@ant.apache.org