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-unsubscr...@ant.apache.org
For additional commands, e-mail: dev-h...@ant.apache.org

Reply via email to