jsdoc3-ant-task
jsdoc3-ant-task copied to clipboard
process exit?
Looks like this task causes the process to exit. The task seems to complete correctly (at least the output looks right), but an
I know that the https://github.com/phasebash/jsdoc3-maven-plugin JSDocTask.java forks a process, which I assume is to fix this problem?
Can you post a simple build script that replicates the problem?
I re-wrote this to fork a process (and switched to List<String> 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);
}
}
}
Oops, hit the wrong button, didn't mean to close this.
I found the same problem and the code from batsatt solved it for me.
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).