Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

process exit? #7

Open
batsatt opened this issue Apr 9, 2013 · 5 comments
Open

process exit? #7

batsatt opened this issue Apr 9, 2013 · 5 comments

Comments

@batsatt
Copy link

batsatt commented Apr 9, 2013

Looks like this task causes the process to exit. The task seems to complete correctly (at least the output looks right), but an (or anything else :) after the never executes.

I know that the https://github.com/phasebash/jsdoc3-maven-plugin JSDocTask.java forks a process, which I assume is to fix this problem?

@jannon
Copy link
Owner

jannon commented Apr 10, 2013

Can you post a simple build script that replicates the problem?

@batsatt
Copy link
Author

batsatt commented Apr 10, 2013

I re-wrote this to fork a process (and switched to List from Vector), and it solved my issue:

/**
 * JSDoc3 is ant Ant task for JSDoc3, a JavaScript documentation tool.
 *
 * Usage:
 *      <taskdef name="jsdoc3"
 *          classname="net.jannon.ant.tasks.JsDoc3"
 *          classpath="/path/to/jsdoc3-ant-task.jar;/path/to/js.jar"/>
 *
 *      <jsdoc3 jsdochome="/path/to/jsdoc3/" template="default" outputdir="/output/dir/">
 *          <fileset dir="src" includes="*.js"/>
 *      </jsdoc3>
 *
 * @author Jannon Frank
 * @author Bryan Atsatt (modified to fork process)
 */

package net.jannon.ant.tasks;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.Commandline.Argument;
import org.apache.tools.ant.types.FileList;
import org.apache.tools.ant.types.FileSet;

public class JsDoc3 extends Task {

    private String jsDocHome, template;
    private List<Argument> args = new ArrayList<Argument>();
    private List<FileSet> fileSets = new ArrayList<FileSet>();
    private List<FileList> fileLists = new ArrayList<FileList>();
    private String encoding = null,
            config = null,
            inputDir = null,
            inputFile = null,
            toDir = null,
            tutorials = null;
    private boolean isIncludingPrivate = false, isRecursive = false;

    /**
     * Method invoked by Ant to actually run the task
     */
    public void execute() throws BuildException {
        final File jsDocHome = getJSDocHome();
        final File rhinoJar = new File(jsDocHome, "rhino/js.jar");
        final String javaHome = System.getProperty("java.home");
        final File java = new File(javaHome, "bin" + File.separator + "java");
        final List<String> processArgs = new ArrayList<>();

        processArgs.add(java.getAbsolutePath());
        processArgs.add("-classpath");
        processArgs.add(rhinoJar.getAbsolutePath());
        processArgs.add("org.mozilla.javascript.tools.shell.Main");
        processArgs.addAll(createArguments());

        Process process;
        final ProcessBuilder processBuilder = new ProcessBuilder(processArgs);
        try {
            process = processBuilder.start();
        } catch (Exception e) {
            throw new BuildException(e);
        }
        try {
            final int exitCode = process.waitFor();
            if (exitCode != 0) {
                throw new BuildException("Process died with exit code " + exitCode);
            }
        } catch (InterruptedException e) {
            throw new BuildException("Process interrupted", e);
        }
    }

    /**
     * Receive a nested argument
     * @param arg An argument to pass to JSDoc3.  This can be used pass arguments
     * not exposed directly by the ant task (e.g --test, --explain, etc.)
     */
    public void addArg(Argument arg) {
        if (!args.contains(arg)) {
            args.add(arg);
        }
    }

    /**
     * Receive a nested fileset
     * @param fileSet a fileset of source files
     */
    public void addFileSet(FileSet fileSet) {
        if (!fileSets.contains(fileSet)) {
            fileSets.add(fileSet);
        }
    }

    /**
     * Receive a nested FileList
     * @param fileList a list of sources
     */
    public void addFileList(FileList fileList) {
        if (!fileLists.contains(fileList)) {
            fileLists.add(fileList);
        }
    }

    /**
     * Sets the jsdochome attribute, the home directory of JSDoc3.
     * @param jsDocHome a string representing the the home directory of JSDoc3
     */
    public void setJsdochome(String jsDocHome) {
        this.jsDocHome = jsDocHome;
    }

    /**
     * Sets the input source directory.  One of the following must be specified:
     * 'dir', 'file', or nested filesets
     * @param dir a string representing the path to the source file directory.
     */
    public void setDir(String dir) {
        this.inputDir = dir;
    }

    /**
     * Sets the input source file.  One of the following must be specified:
     * 'dir', 'file', or nested filesets
     * @param file a string representing the path to the source file
     */
    public void setFile(String file) {
        this.inputFile = file;
    }

    /**
     * Sets the optional template attribute, which is the name of the template
     * used to generate the documentation
     * @param template the name of the template to use (default: "default")
     */
    public void setTemplate(String template) {
        this.template = "templates/" + template;
    }

    /**
     * Sets the optional to attribute which determines where the generated
     * documentation is placed.
     * @param toDir a string representing the path to the directory in which to
     * place the generated documentation
     */
    public void setTo(String toDir) {
        this.toDir = toDir;
    }

    /**
     * Sets the optional encoding attribute which sets the encoding of the input and output
     * files.
     * @param encoding the encoding to use (default: "utf-8")
     */
    public void setEncoding(String encoding) {
        this.encoding = encoding;
    }

    /**
     * Sets the optional private attribute which determines whether or not JSDoc3
     * should generate documentation for private members/methods
     * @param isPrivate whether or not to show private members/methods (default: false)
     */
    public void setPrivate(Boolean isPrivate) {
        this.isIncludingPrivate = isPrivate;
    }

    /**
     * Sets the optional recurse attribute which determines whether or not JSDoc3
     * should recurse into source directories.
     * @param recurse whether or not to recurse into source subdirectories (default: false)
     */
    public void setRecurse(Boolean recurse) {
        this.isRecursive = recurse;
    }

    /**
     * Sets the optional tutorials attribute. The tutorials attribute points
     * to where JSDoc3 should look for tutorials
     * @param tutorials a string representing the path to the tutorials directory
     */
    public void setTutorials(String tutorials) {
        this.tutorials = tutorials;
    }

    /**
     * Returns the JSDoc home directory.
     * @return The directory.
     * @throws BuildException If jsDocHome is not specified, or is not an existing directory.
     */
    private File getJSDocHome() throws BuildException {
        if (jsDocHome == null) {
            throw new BuildException("jsdochome must be specified");
        }
        File result = new File(jsDocHome);
        if (!result.exists()) {
            throw new BuildException("jsdochome '" + jsDocHome + " does not exist.");
        }
        if (!result.isDirectory()) {
            throw new BuildException("jsdochome '" + jsDocHome + " is not a directory");
        }
        return result;
    }

    /**
     * Create the array of arguments to pass to rhino engine.  It looks something
     * like this:
     * -modules <jsdoc.home>/node_modules -modules <jsdoc.home>/rhino_modules \
     * -modules <jsdoc.home> <jsdoc.home>/jsdoc.js --dirname=<jsdoc.home> \
     * <options> <sourcefiles|sourcedirs>
     * @return a list of commands to pass to the rhino engine
     */
    private List<String> createArguments() throws BuildException {
        List<String> arguments = new ArrayList<String>();

        // add the modules folders
        arguments.add("-modules");
        arguments.add(jsDocHome + "/node_modules");
        arguments.add("-modules");

        if (new File(jsDocHome + "/rhino").exists()) {
            arguments.add(jsDocHome + "/rhino");
        } else {
            arguments.add(jsDocHome + "/rhino_modules");
        }

        arguments.add("-modules");
        arguments.add(jsDocHome + "/lib");
        arguments.add("-modules");
        arguments.add(jsDocHome);

        // add the main jsodc js
        arguments.add(jsDocHome + "/jsdoc.js");

        // add the dirname
        arguments.add("--dirname=" + jsDocHome);

        addOptionalArgument(arguments, template, "-t"); // add the template
        addOptionalArgument(arguments, toDir, "-d"); // add the output dir
        addOptionalArgument(arguments, encoding, "-e"); // the encoding to use
        addOptionalArgument(arguments, config, "-c"); // the config file to use
        addOptionalArgument(arguments, tutorials, "-u"); // the tutorials dir
        addOptionalBooleanArgument(arguments, isIncludingPrivate, "-p");
        addOptionalBooleanArgument(arguments, isRecursive, "-r");

        if (inputFile != null) {
            arguments.add(inputFile);
        } else if (inputDir != null) {
            arguments.add(inputDir);
        } else if (fileSets.size() != 0 || fileLists.size() != 0) {
            // Loop through fileSets
            for (int i = 0, l = fileSets.size(); i < l; i++) {
                FileSet fs = fileSets.get(i);
                // Ummm....?
                DirectoryScanner ds = fs.getDirectoryScanner(getProject());
                // Get base directory from fileset
                File dir = ds.getBasedir();
                // Get included files from fileset
                String[] srcs = ds.getIncludedFiles();
                // Loop through files
                for (int j = 0; j < srcs.length; j++) {
                    // Make file object from base directory and filename
                    File temp = new File(dir, srcs[j]);
                    // Call the JSMin class with this file
                    arguments.add(temp.getAbsolutePath());
                }
            }
            // Loop through fileLists
            for (int i = 0; i < fileLists.size(); i++) {
                FileList fs = fileLists.get(i);
                // Get included files from filelist
                String[] srcs = fs.getFiles(getProject());
                // Get base directory from filelist
                File dir = fs.getDir(getProject());
                // Loop through files
                for (int j = 0; j < srcs.length; j++) {
                    // Make file object from base directory and filename
                    File temp = new File(dir, srcs[j]);
                    // Call the JSMin class with this file
                    arguments.add(temp.getAbsolutePath());
                }
            }
        } else {
            throw new BuildException("No inputs specified.  Task requires 'file' attribute OR 'dir' attribute OR nested filesets");
        }
        if (args.size() != 0) {
            for (int i = 0, l = args.size(); i < l; i++) {
                arguments.addAll(Arrays.asList(args.get(i).getParts()));
            }
        }
        return arguments;

    }

    /**
     * Sets the optional config attribute. The config attribute points to the
     * JSON config file used by JSDoc3
     * @param config a string representing the path to the config file (default: "${jsdoc.home}/conf.json")
     */
    public void setConfig(String config) {
        this.config = config;
    }

    /**
     * Helper method to add optional arguments.  Checks for null first, then adds
     */
    private void addOptionalArgument(List<String> arguments, String value, String option) {
        if (value != null) {
            arguments.add(option);
            arguments.add(value);
        }
    }

    /**
     * Helper method to add optional boolean arguments.  Checks for null first, then adds
     */
    private void addOptionalBooleanArgument(List<String> arguments, Boolean value, String option) {
        if (value) {
            arguments.add(option);
        }
    }
}

@batsatt batsatt closed this as completed Apr 10, 2013
@batsatt
Copy link
Author

batsatt commented Apr 10, 2013

Oops, hit the wrong button, didn't mean to close this.

@batsatt batsatt reopened this Apr 10, 2013
@shybyte
Copy link

shybyte commented Jul 30, 2013

I found the same problem and the code from batsatt solved it for me.

@mandrachek
Copy link

Here's an example. The doEcho task depends on the generate-jsdoc task. So calling the doEcho task the expected output would contain the echo message from the doEcho task. This doesn't occur, it stops immediately after the generate-jsdoc.

<taskdef name="jsdoc3" classname="net.jannon.ant.tasks.JsDoc3" classpath="${jsdoc3-task-jar}:${rhino-jar}"/>
<target name="generate-jsdoc">
    <delete dir="${jsdoc.outdir}"/>
    <mkdir dir="${jsdoc.outdir}"/>
    <jsdoc3 jsdochome="${jsdoc.home}" to="${jsdoc.outdir}" dir="${jsdoc.contentdir}  private="true" recurse="true"/>
</target>
<target name="doEcho" depends="generate-jsdoc">
    <echo message="This should happen after the generate-jsdoc target, but never does."/>
</target>

I've worked around this temporarily locally by commenting the java.lang.System.exit(n); call in the global.process.exit function in the rhino-shim.js, but this might have adverse effects. It looks like @batsatt has the right approach, and it looks like it also resolves issue #9 (invalid command line option).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants