aboutsummaryrefslogtreecommitdiff
path: root/org.jacoco.cli/src
diff options
context:
space:
mode:
authorMarc R. Hoffmann <hoffmann@mountainminds.com>2017-05-25 00:53:18 +0200
committerEvgeny Mandrikov <Godin@users.noreply.github.com>2017-05-25 00:53:18 +0200
commit13f12d39db0b3c0ddc60124572e5d4f984be0255 (patch)
tree2e1ea16bf0675dfd83730f48ad48edf2e880df89 /org.jacoco.cli/src
parent10f3ff0dd010d647625ef5937f301126250267d0 (diff)
downloadjacoco-13f12d39db0b3c0ddc60124572e5d4f984be0255.tar.gz
Add Command Line Interface (#525)
Diffstat (limited to 'org.jacoco.cli/src')
-rw-r--r--org.jacoco.cli/src/org/jacoco/cli/internal/Command.java92
-rw-r--r--org.jacoco.cli/src/org/jacoco/cli/internal/CommandHandler.java89
-rw-r--r--org.jacoco.cli/src/org/jacoco/cli/internal/CommandParser.java33
-rw-r--r--org.jacoco.cli/src/org/jacoco/cli/internal/Main.java108
-rw-r--r--org.jacoco.cli/src/org/jacoco/cli/internal/XmlDocumentation.java78
-rw-r--r--org.jacoco.cli/src/org/jacoco/cli/internal/commands/AllCommands.java49
-rw-r--r--org.jacoco.cli/src/org/jacoco/cli/internal/commands/ClassInfo.java75
-rw-r--r--org.jacoco.cli/src/org/jacoco/cli/internal/commands/Dump.java77
-rw-r--r--org.jacoco.cli/src/org/jacoco/cli/internal/commands/ExecInfo.java94
-rw-r--r--org.jacoco.cli/src/org/jacoco/cli/internal/commands/Instrument.java96
-rw-r--r--org.jacoco.cli/src/org/jacoco/cli/internal/commands/Merge.java66
-rw-r--r--org.jacoco.cli/src/org/jacoco/cli/internal/commands/Report.java174
-rw-r--r--org.jacoco.cli/src/org/jacoco/cli/internal/commands/Version.java37
13 files changed, 1068 insertions, 0 deletions
diff --git a/org.jacoco.cli/src/org/jacoco/cli/internal/Command.java b/org.jacoco.cli/src/org/jacoco/cli/internal/Command.java
new file mode 100644
index 00000000..7c4f5c15
--- /dev/null
+++ b/org.jacoco.cli/src/org/jacoco/cli/internal/Command.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+import org.kohsuke.args4j.Option;
+
+/**
+ * Common interface for all commands.
+ */
+public abstract class Command {
+
+ /**
+ * Common command line prefix.
+ */
+ public static final String JAVACMD = "java -jar jacococli.jar ";
+
+ /**
+ * Flag whether help should be printed for this command.
+ */
+ @Option(name = "--help", usage = "show help", help = true)
+ public boolean help = false;
+
+ /**
+ * Flag whether output to stdout should be suppressed.
+ */
+ @Option(name = "--quiet", usage = "suppress all output on stdout")
+ public boolean quiet = false;
+
+ /**
+ * @return Short description of the command.
+ */
+ public abstract String description();
+
+ /**
+ * @return name of the command
+ */
+ public String name() {
+ return getClass().getSimpleName().toLowerCase();
+ }
+
+ /**
+ * @param parser
+ * parser for this command
+ * @return usage string displayed for help
+ */
+ public String usage(final CommandParser parser) {
+ final StringWriter writer = new StringWriter();
+ parser.printSingleLineUsage(writer, null);
+ return JAVACMD + name() + writer;
+ }
+
+ /**
+ * Executes the given command.
+ *
+ * @param out
+ * std out
+ * @param err
+ * std err
+ * @return exit code, should be 0 for normal operation
+ * @throws Exception
+ * any exception that my occur during execution
+ */
+ public abstract int execute(PrintWriter out, PrintWriter err)
+ throws Exception;
+
+ /**
+ * Prints textual help for this command.
+ *
+ * @param writer
+ * output destination
+ */
+ protected void printHelp(final PrintWriter writer) {
+ final CommandParser parser = new CommandParser(this);
+ writer.println(description());
+ writer.println();
+ writer.println("Usage: " + parser.getCommand().usage(parser));
+ parser.printUsage(writer, null);
+ }
+
+}
diff --git a/org.jacoco.cli/src/org/jacoco/cli/internal/CommandHandler.java b/org.jacoco.cli/src/org/jacoco/cli/internal/CommandHandler.java
new file mode 100644
index 00000000..3c6a2a54
--- /dev/null
+++ b/org.jacoco.cli/src/org/jacoco/cli/internal/CommandHandler.java
@@ -0,0 +1,89 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal;
+
+import java.util.AbstractList;
+
+import org.jacoco.cli.internal.commands.AllCommands;
+import org.kohsuke.args4j.CmdLineException;
+import org.kohsuke.args4j.CmdLineParser;
+import org.kohsuke.args4j.OptionDef;
+import org.kohsuke.args4j.spi.Messages;
+import org.kohsuke.args4j.spi.OptionHandler;
+import org.kohsuke.args4j.spi.Parameters;
+import org.kohsuke.args4j.spi.Setter;
+
+/**
+ * {@link OptionHandler} which uses {@link CommandParser} internally to provide
+ * help context also for sub-commands.
+ */
+public class CommandHandler extends OptionHandler<Command> {
+
+ /**
+ * This constructor is required by the args4j framework.
+ *
+ * @param parser
+ * @param option
+ * @param setter
+ */
+ public CommandHandler(final CmdLineParser parser, final OptionDef option,
+ final Setter<Object> setter) {
+ super(parser,
+ new OptionDef(AllCommands.names(), "<command>",
+ option.required(), option.help(), option.hidden(),
+ CommandHandler.class, option.isMultiValued()) {
+ }, setter);
+ }
+
+ @Override
+ public int parseArguments(final Parameters params) throws CmdLineException {
+ final String subCmd = params.getParameter(0);
+
+ for (final Command c : AllCommands.get()) {
+ if (c.name().equals(subCmd)) {
+ parseSubArguments(c, params);
+ setter.addValue(c);
+ return params.size(); // consume all the remaining tokens
+ }
+ }
+
+ throw new CmdLineException(owner,
+ Messages.ILLEGAL_OPERAND.format(option.toString(), subCmd));
+ }
+
+ private void parseSubArguments(final Command c, final Parameters params)
+ throws CmdLineException {
+ final CmdLineParser p = new CommandParser(c);
+ p.parseArgument(new AbstractList<String>() {
+ @Override
+ public String get(final int index) {
+ try {
+ return params.getParameter(index + 1);
+ } catch (final CmdLineException e) {
+ // invalid index was accessed.
+ throw new IndexOutOfBoundsException();
+ }
+ }
+
+ @Override
+ public int size() {
+ return params.size() - 1;
+ }
+ });
+ }
+
+ @Override
+ public String getDefaultMetaVariable() {
+ return "<command>";
+ }
+
+}
diff --git a/org.jacoco.cli/src/org/jacoco/cli/internal/CommandParser.java b/org.jacoco.cli/src/org/jacoco/cli/internal/CommandParser.java
new file mode 100644
index 00000000..0e31093b
--- /dev/null
+++ b/org.jacoco.cli/src/org/jacoco/cli/internal/CommandParser.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal;
+
+import org.kohsuke.args4j.CmdLineParser;
+
+/**
+ * Parser which remembers the parsed command to have additional context
+ * information to produce help output.
+ */
+public class CommandParser extends CmdLineParser {
+
+ private final Command command;
+
+ CommandParser(final Command command) {
+ super(command);
+ this.command = command;
+ }
+
+ Command getCommand() {
+ return command;
+ }
+
+}
diff --git a/org.jacoco.cli/src/org/jacoco/cli/internal/Main.java b/org.jacoco.cli/src/org/jacoco/cli/internal/Main.java
new file mode 100644
index 00000000..1c02127f
--- /dev/null
+++ b/org.jacoco.cli/src/org/jacoco/cli/internal/Main.java
@@ -0,0 +1,108 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Writer;
+
+import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.CmdLineException;
+
+/**
+ * Entry point for all command line operations.
+ */
+public class Main extends Command {
+
+ private static final PrintWriter NUL = new PrintWriter(new Writer() {
+
+ @Override
+ public void write(final char[] arg0, final int arg1, final int arg2)
+ throws IOException {
+ }
+
+ @Override
+ public void flush() throws IOException {
+ }
+
+ @Override
+ public void close() throws IOException {
+ }
+ });
+
+ private final String[] args;
+
+ Main(final String... args) {
+ this.args = args;
+ }
+
+ @Argument(handler = CommandHandler.class, required = true)
+ Command command;
+
+ @Override
+ public String description() {
+ return "Command line interface for JaCoCo.";
+ }
+
+ @Override
+ public String usage(final CommandParser parser) {
+ return JAVACMD + "--help | <command>";
+ }
+
+ @Override
+ public int execute(PrintWriter out, final PrintWriter err)
+ throws Exception {
+
+ final CommandParser mainParser = new CommandParser(this);
+ try {
+ mainParser.parseArgument(args);
+ } catch (final CmdLineException e) {
+ ((CommandParser) e.getParser()).getCommand().printHelp(err);
+ err.println();
+ err.println(e.getMessage());
+ return -1;
+ }
+
+ if (help) {
+ printHelp(out);
+ return 0;
+ }
+
+ if (command.help) {
+ command.printHelp(out);
+ return 0;
+ }
+
+ if (command.quiet) {
+ out = NUL;
+ }
+
+ return command.execute(out, err);
+ }
+
+ /**
+ * Main entry point for program invocations.
+ *
+ * @param args
+ * program arguments
+ * @throws Exception
+ * All internal exceptions are directly passed on to get printed
+ * on the console
+ */
+ public static void main(final String... args) throws Exception {
+ final PrintWriter out = new PrintWriter(System.out, true);
+ final PrintWriter err = new PrintWriter(System.err, true);
+ final int returncode = new Main(args).execute(out, err);
+ System.exit(returncode);
+ }
+
+}
diff --git a/org.jacoco.cli/src/org/jacoco/cli/internal/XmlDocumentation.java b/org.jacoco.cli/src/org/jacoco/cli/internal/XmlDocumentation.java
new file mode 100644
index 00000000..83241c9a
--- /dev/null
+++ b/org.jacoco.cli/src/org/jacoco/cli/internal/XmlDocumentation.java
@@ -0,0 +1,78 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.List;
+
+import org.jacoco.cli.internal.commands.AllCommands;
+import org.jacoco.report.internal.xml.XMLDocument;
+import org.jacoco.report.internal.xml.XMLElement;
+import org.kohsuke.args4j.spi.OptionHandler;
+
+/**
+ * Internal utility to dump all command descriptions as XML.
+ */
+public final class XmlDocumentation {
+
+ private XmlDocumentation() {
+ }
+
+ private static void writeCommand(final Command command,
+ final XMLElement parent) throws IOException {
+ final CommandParser parser = new CommandParser(command);
+ final XMLElement element = parent.element("command");
+ element.attr("name", command.name());
+ element.element("usage").text(command.usage(parser));
+ element.element("description").text(command.description());
+ writeOptions(element, parser.getArguments());
+ writeOptions(element, parser.getOptions());
+ }
+
+ private static void writeOptions(final XMLElement parent,
+ @SuppressWarnings("rawtypes") final List<OptionHandler> list)
+ throws IOException {
+ for (final OptionHandler<?> o : list) {
+ final XMLElement optionNode = parent.element("option");
+ optionNode.attr("required", String.valueOf(o.option.required()));
+ optionNode.attr("multiple",
+ String.valueOf(o.setter.isMultiValued()));
+ optionNode.element("usage").text(o.getNameAndMeta(null));
+ optionNode.element("description").text(o.option.usage());
+ }
+ }
+
+ /**
+ * Called during the build process.
+ *
+ * @param args
+ * exactly one argument expected with the target location
+ * @throws IOException
+ * if XML document cannot be written
+ */
+ public static void main(final String... args) throws IOException {
+ final File file = new File(args[0]);
+ file.getParentFile().mkdirs();
+
+ final XMLElement root = new XMLDocument("documentation", null, null,
+ "UTF-8", true, new FileOutputStream(file));
+
+ for (final Command c : AllCommands.get()) {
+ writeCommand(c, root);
+ }
+
+ root.close();
+ }
+
+}
diff --git a/org.jacoco.cli/src/org/jacoco/cli/internal/commands/AllCommands.java b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/AllCommands.java
new file mode 100644
index 00000000..e0df1d91
--- /dev/null
+++ b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/AllCommands.java
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal.commands;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.jacoco.cli.internal.Command;
+
+/**
+ * List of all available commands.
+ */
+public final class AllCommands {
+
+ private AllCommands() {
+ }
+
+ /**
+ * @return list of new instances of all available commands
+ */
+ public static List<Command> get() {
+ return Arrays.asList(new Dump(), new Instrument(), new Merge(),
+ new Report(), new ClassInfo(), new ExecInfo(), new Version());
+ }
+
+ /**
+ * @return String containing all available command names
+ */
+ public static String names() {
+ final StringBuilder sb = new StringBuilder();
+ for (final Command c : get()) {
+ if (sb.length() > 0) {
+ sb.append('|');
+ }
+ sb.append(c.name());
+ }
+ return sb.toString();
+ }
+
+}
diff --git a/org.jacoco.cli/src/org/jacoco/cli/internal/commands/ClassInfo.java b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/ClassInfo.java
new file mode 100644
index 00000000..dd6a3718
--- /dev/null
+++ b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/ClassInfo.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal.commands;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jacoco.cli.internal.Command;
+import org.jacoco.core.analysis.Analyzer;
+import org.jacoco.core.analysis.IClassCoverage;
+import org.jacoco.core.analysis.ICoverageVisitor;
+import org.jacoco.core.data.ExecutionDataStore;
+import org.kohsuke.args4j.Argument;
+
+/**
+ * The <code>classinfo</code> command.
+ */
+public class ClassInfo extends Command {
+
+ @Argument(usage = "location of Java class files", metaVar = "<classlocations>")
+ List<File> classfiles = new ArrayList<File>();
+
+ @Override
+ public String description() {
+ return "Print information about Java class files at the provided location.";
+ }
+
+ @Override
+ public int execute(final PrintWriter out, final PrintWriter err)
+ throws IOException {
+ if (classfiles.isEmpty()) {
+ out.println("[WARN] No class files provided.");
+ } else {
+ final Analyzer analyzer = new Analyzer(new ExecutionDataStore(),
+ new ICoverageVisitor() {
+ public void visitCoverage(
+ final IClassCoverage coverage) {
+ print(coverage, out);
+ }
+ });
+ for (final File file : classfiles) {
+ analyzer.analyzeAll(file);
+ }
+ }
+ return 0;
+ }
+
+ private void print(final IClassCoverage coverage, final PrintWriter out) {
+ out.printf("class name: %s%n", coverage.getName());
+ out.printf("class id: %016x%n", Long.valueOf(coverage.getId()));
+ out.printf("instructions: %s%n", Integer
+ .valueOf(coverage.getInstructionCounter().getTotalCount()));
+ out.printf("branches: %s%n",
+ Integer.valueOf(coverage.getBranchCounter().getTotalCount()));
+ out.printf("lines: %s%n",
+ Integer.valueOf(coverage.getLineCounter().getTotalCount()));
+ out.printf("methods: %s%n",
+ Integer.valueOf(coverage.getMethodCounter().getTotalCount()));
+ out.printf("complexity: %s%n%n", Integer
+ .valueOf(coverage.getComplexityCounter().getTotalCount()));
+ }
+
+}
diff --git a/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Dump.java b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Dump.java
new file mode 100644
index 00000000..3c1575d9
--- /dev/null
+++ b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Dump.java
@@ -0,0 +1,77 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal.commands;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.InetAddress;
+
+import org.jacoco.cli.internal.Command;
+import org.jacoco.core.runtime.AgentOptions;
+import org.jacoco.core.tools.ExecDumpClient;
+import org.jacoco.core.tools.ExecFileLoader;
+import org.kohsuke.args4j.Option;
+
+/**
+ * The <code>dump</code> command.
+ */
+public class Dump extends Command {
+
+ @Option(name = "--address", usage = "host name or ip address to connect to (default localhost)", metaVar = "<address>")
+ String address = AgentOptions.DEFAULT_ADDRESS;
+
+ @Option(name = "--port", usage = "the port to connect to (default 6300)", metaVar = "<port>")
+ int port = AgentOptions.DEFAULT_PORT;
+
+ @Option(name = "--destfile", usage = "file to write execution data to", metaVar = "<path>", required = true)
+ File destfile;
+
+ @Option(name = "--reset", usage = "reset execution data on test target after dump")
+ boolean reset = false;
+
+ @Option(name = "--retry", usage = "number of retries (default 10)", metaVar = "<count>")
+ int retrycount = 10;
+
+ @Override
+ public String description() {
+ return "Request execution data from a JaCoCo agent running in 'tcpserver' output mode.";
+ }
+
+ @Override
+ public int execute(final PrintWriter out, final PrintWriter err)
+ throws Exception {
+ final ExecDumpClient client = new ExecDumpClient() {
+ @Override
+ protected void onConnecting(final InetAddress address,
+ final int port) {
+ out.printf("[INFO] Connecting to %s:%s.%n", address,
+ Integer.valueOf(port));
+ }
+
+ @Override
+ protected void onConnectionFailure(final IOException exception) {
+ err.printf("[WARN] %s.%n", exception.getMessage());
+ }
+ };
+ client.setReset(reset);
+ client.setRetryCount(retrycount);
+
+ final ExecFileLoader loader = client.dump(address, port);
+ out.printf("[INFO] Writing execution data to %s.%n",
+ destfile.getAbsolutePath());
+ loader.save(destfile, true);
+
+ return 0;
+ }
+
+}
diff --git a/org.jacoco.cli/src/org/jacoco/cli/internal/commands/ExecInfo.java b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/ExecInfo.java
new file mode 100644
index 00000000..211be983
--- /dev/null
+++ b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/ExecInfo.java
@@ -0,0 +1,94 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal.commands;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+import org.jacoco.cli.internal.Command;
+import org.jacoco.core.data.ExecutionData;
+import org.jacoco.core.data.ExecutionDataReader;
+import org.jacoco.core.data.IExecutionDataVisitor;
+import org.jacoco.core.data.ISessionInfoVisitor;
+import org.jacoco.core.data.SessionInfo;
+import org.kohsuke.args4j.Argument;
+
+/**
+ * The <code>execinfo</code> command.
+ */
+public class ExecInfo extends Command {
+
+ @Argument(usage = "list of JaCoCo *.exec files to read", metaVar = "<execfiles>")
+ List<File> execfiles = new ArrayList<File>();
+
+ @Override
+ public String description() {
+ return "Print exec file content in human readable format.";
+ }
+
+ @Override
+ public int execute(final PrintWriter out, final PrintWriter err)
+ throws IOException {
+ if (execfiles.isEmpty()) {
+ out.println("[WARN] No execution data files provided.");
+ } else {
+ for (final File file : execfiles) {
+ dump(file, out);
+ }
+ }
+ return 0;
+ }
+
+ private void dump(final File file, final PrintWriter out)
+ throws IOException {
+ out.printf("[INFO] Loading exec file %s.%n", file);
+ out.println("CLASS ID HITS/PROBES CLASS NAME");
+
+ final FileInputStream in = new FileInputStream(file);
+ final ExecutionDataReader reader = new ExecutionDataReader(in);
+ reader.setSessionInfoVisitor(new ISessionInfoVisitor() {
+ public void visitSessionInfo(final SessionInfo info) {
+ out.printf("Session \"%s\": %s - %s%n", info.getId(),
+ new Date(info.getStartTimeStamp()),
+ new Date(info.getDumpTimeStamp()));
+ }
+ });
+ reader.setExecutionDataVisitor(new IExecutionDataVisitor() {
+ public void visitClassExecution(final ExecutionData data) {
+ out.printf("%016x %3d of %3d %s%n",
+ Long.valueOf(data.getId()),
+ Integer.valueOf(getHitCount(data.getProbes())),
+ Integer.valueOf(data.getProbes().length),
+ data.getName());
+ }
+ });
+ reader.read();
+ in.close();
+ out.println();
+ }
+
+ private int getHitCount(final boolean[] data) {
+ int count = 0;
+ for (final boolean hit : data) {
+ if (hit) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+}
diff --git a/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Instrument.java b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Instrument.java
new file mode 100644
index 00000000..c52c0722
--- /dev/null
+++ b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Instrument.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * John Keeping - initial implementation
+ * Marc R. Hoffmann - rework
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal.commands;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jacoco.cli.internal.Command;
+import org.jacoco.core.instr.Instrumenter;
+import org.jacoco.core.runtime.OfflineInstrumentationAccessGenerator;
+import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.Option;
+
+/**
+ * The <code>instrument</code> command.
+ */
+public class Instrument extends Command {
+
+ @Option(name = "--dest", usage = "path to write instrumented Java classes to", metaVar = "<dir>", required = true)
+ File dest;
+
+ @Argument(usage = "list of folder or files to instrument recusively", metaVar = "<sourcefiles>")
+ List<File> source = new ArrayList<File>();
+
+ private Instrumenter instrumenter;
+
+ @Override
+ public String description() {
+ return "Off-line instrumentation of Java class files and JAR files.";
+ }
+
+ @Override
+ public int execute(final PrintWriter out, final PrintWriter err)
+ throws IOException {
+ instrumenter = new Instrumenter(
+ new OfflineInstrumentationAccessGenerator());
+ int total = 0;
+ for (final File s : source) {
+ total += instrumentRecursive(s, dest);
+ }
+ out.printf("[INFO] %s classes instrumented to %s.%n",
+ Integer.valueOf(total), dest.getAbsolutePath());
+ return 0;
+ }
+
+ private int instrumentRecursive(final File src, final File dest)
+ throws IOException {
+ int total = 0;
+ if (src.isDirectory()) {
+ for (final File child : src.listFiles()) {
+ total += instrumentRecursive(child,
+ new File(dest, child.getName()));
+ }
+ } else {
+ total += instrument(src, dest);
+ }
+ return total;
+ }
+
+ private int instrument(final File src, final File dest) throws IOException {
+ dest.getParentFile().mkdirs();
+ final InputStream input = new FileInputStream(src);
+ try {
+ final OutputStream output = new FileOutputStream(dest);
+ try {
+ return instrumenter.instrumentAll(input, output,
+ src.getAbsolutePath());
+ } finally {
+ output.close();
+ }
+ } catch (final IOException e) {
+ dest.delete();
+ throw e;
+ } finally {
+ input.close();
+ }
+ }
+
+}
diff --git a/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Merge.java b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Merge.java
new file mode 100644
index 00000000..d4262abc
--- /dev/null
+++ b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Merge.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal.commands;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jacoco.cli.internal.Command;
+import org.jacoco.core.tools.ExecFileLoader;
+import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.Option;
+
+/**
+ * The <code>merge</code> command.
+ */
+public class Merge extends Command {
+
+ @Argument(usage = "list of JaCoCo *.exec files to read", metaVar = "<execfiles>")
+ List<File> execfiles = new ArrayList<File>();
+
+ @Option(name = "--destfile", usage = "file to write merged execution data to", metaVar = "<path>", required = true)
+ File destfile;
+
+ @Override
+ public String description() {
+ return "Merges multiple exec files into a new one.";
+ }
+
+ @Override
+ public int execute(final PrintWriter out, final PrintWriter err)
+ throws IOException {
+ final ExecFileLoader loader = loadExecutionData(out);
+ out.printf("[INFO] Writing execution data to %s.%n",
+ destfile.getAbsolutePath());
+ loader.save(destfile, true);
+ return 0;
+ }
+
+ private ExecFileLoader loadExecutionData(final PrintWriter out)
+ throws IOException {
+ final ExecFileLoader loader = new ExecFileLoader();
+ if (execfiles.isEmpty()) {
+ out.println("[WARN] No execution data files provided.");
+ } else {
+ for (final File file : execfiles) {
+ out.printf("[INFO] Loading execution data file %s.%n",
+ file.getAbsolutePath());
+ loader.load(file);
+ }
+ }
+ return loader;
+ }
+
+}
diff --git a/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Report.java b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Report.java
new file mode 100644
index 00000000..eaa78842
--- /dev/null
+++ b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Report.java
@@ -0,0 +1,174 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * John Keeping - initial implementation
+ * Marc R. Hoffmann - rework
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal.commands;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.jacoco.cli.internal.Command;
+import org.jacoco.core.analysis.Analyzer;
+import org.jacoco.core.analysis.CoverageBuilder;
+import org.jacoco.core.analysis.IBundleCoverage;
+import org.jacoco.core.analysis.IClassCoverage;
+import org.jacoco.core.data.ExecutionDataStore;
+import org.jacoco.core.tools.ExecFileLoader;
+import org.jacoco.report.DirectorySourceFileLocator;
+import org.jacoco.report.FileMultiReportOutput;
+import org.jacoco.report.IReportVisitor;
+import org.jacoco.report.ISourceFileLocator;
+import org.jacoco.report.MultiReportVisitor;
+import org.jacoco.report.MultiSourceFileLocator;
+import org.jacoco.report.csv.CSVFormatter;
+import org.jacoco.report.html.HTMLFormatter;
+import org.jacoco.report.xml.XMLFormatter;
+import org.kohsuke.args4j.Argument;
+import org.kohsuke.args4j.Option;
+
+/**
+ * The <code>report</code> command.
+ */
+public class Report extends Command {
+
+ @Argument(usage = "list of JaCoCo *.exec files to read", metaVar = "<execfiles>")
+ List<File> execfiles = new ArrayList<File>();
+
+ @Option(name = "--classfiles", usage = "location of Java class files", metaVar = "<path>", required = true)
+ List<File> classfiles = new ArrayList<File>();
+
+ @Option(name = "--sourcefiles", usage = "location of the source files", metaVar = "<path>")
+ List<File> sourcefiles = new ArrayList<File>();
+
+ @Option(name = "--tabwith", usage = "tab stop width for the source pages (default 4)", metaVar = "<n>")
+ int tabwidth = 4;
+
+ @Option(name = "--name", usage = "name used for this report", metaVar = "<name>")
+ String name = "JaCoCo Coverage Report";
+
+ @Option(name = "--encoding", usage = "source file encoding (by default platform encoding is used)", metaVar = "<charset>")
+ String encoding;
+
+ @Option(name = "--xml", usage = "output file for the XML report", metaVar = "<file>")
+ File xml;
+
+ @Option(name = "--csv", usage = "output file for the CSV report", metaVar = "<file>")
+ File csv;
+
+ @Option(name = "--html", usage = "output directory for the HTML report", metaVar = "<dir>")
+ File html;
+
+ @Override
+ public String description() {
+ return "Generate reports in different formats by reading exec and Java class files.";
+ }
+
+ @Override
+ public int execute(final PrintWriter out, final PrintWriter err)
+ throws IOException {
+ final ExecFileLoader loader = loadExecutionData(out);
+ final IBundleCoverage bundle = analyze(loader.getExecutionDataStore(),
+ out);
+ writeReports(bundle, loader, out);
+ return 0;
+ }
+
+ private ExecFileLoader loadExecutionData(final PrintWriter out)
+ throws IOException {
+ final ExecFileLoader loader = new ExecFileLoader();
+ if (execfiles.isEmpty()) {
+ out.println("[WARN] No execution data files provided.");
+ } else {
+ for (final File file : execfiles) {
+ out.printf("[INFO] Loading execution data file %s.%n",
+ file.getAbsolutePath());
+ loader.load(file);
+ }
+ }
+ return loader;
+ }
+
+ private IBundleCoverage analyze(final ExecutionDataStore data,
+ final PrintWriter out) throws IOException {
+ final CoverageBuilder builder = new CoverageBuilder();
+ final Analyzer analyzer = new Analyzer(data, builder);
+ for (final File f : classfiles) {
+ analyzer.analyzeAll(f);
+ }
+ printNoMatchWarning(builder.getNoMatchClasses(), out);
+ return builder.getBundle(name);
+ }
+
+ private void printNoMatchWarning(final Collection<IClassCoverage> nomatch,
+ final PrintWriter out) {
+ if (!nomatch.isEmpty()) {
+ out.println(
+ "[WARN] Some classes do not match with execution data.");
+ out.println(
+ "[WARN] For report generation the same class files must be used as at runtime.");
+ for (final IClassCoverage c : nomatch) {
+ out.printf(
+ "[WARN] Execution data for class %s does not match.%n",
+ c.getName());
+ }
+ }
+ }
+
+ private void writeReports(final IBundleCoverage bundle,
+ final ExecFileLoader loader, final PrintWriter out)
+ throws IOException {
+ out.printf("[INFO] Analyzing %s classes.%n",
+ Integer.valueOf(bundle.getClassCounter().getTotalCount()));
+ final IReportVisitor visitor = createReportVisitor();
+ visitor.visitInfo(loader.getSessionInfoStore().getInfos(),
+ loader.getExecutionDataStore().getContents());
+ visitor.visitBundle(bundle, getSourceLocator());
+ visitor.visitEnd();
+ }
+
+ private IReportVisitor createReportVisitor()
+ throws IOException, IOException {
+ final List<IReportVisitor> visitors = new ArrayList<IReportVisitor>();
+
+ if (xml != null) {
+ final XMLFormatter formatter = new XMLFormatter();
+ visitors.add(formatter.createVisitor(new FileOutputStream(xml)));
+ }
+
+ if (csv != null) {
+ final CSVFormatter formatter = new CSVFormatter();
+ visitors.add(formatter.createVisitor(new FileOutputStream(csv)));
+ }
+
+ if (html != null) {
+ final HTMLFormatter formatter = new HTMLFormatter();
+ visitors.add(
+ formatter.createVisitor(new FileMultiReportOutput(html)));
+ }
+
+ return new MultiReportVisitor(visitors);
+ }
+
+ private ISourceFileLocator getSourceLocator() {
+ final MultiSourceFileLocator multi = new MultiSourceFileLocator(
+ tabwidth);
+ for (final File f : sourcefiles) {
+ multi.add(new DirectorySourceFileLocator(f, encoding, tabwidth));
+ }
+ return multi;
+ }
+
+}
diff --git a/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Version.java b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Version.java
new file mode 100644
index 00000000..ef1e33a1
--- /dev/null
+++ b/org.jacoco.cli/src/org/jacoco/cli/internal/commands/Version.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 Mountainminds GmbH & Co. KG and Contributors
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Marc R. Hoffmann - initial API and implementation
+ *
+ *******************************************************************************/
+package org.jacoco.cli.internal.commands;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import org.jacoco.cli.internal.Command;
+import org.jacoco.core.JaCoCo;
+
+/**
+ * The <code>version</code> command.
+ */
+public class Version extends Command {
+
+ @Override
+ public String description() {
+ return "Print JaCoCo version information.";
+ }
+
+ @Override
+ public int execute(final PrintWriter out, final PrintWriter err)
+ throws IOException {
+ out.println(JaCoCo.VERSION);
+ return 0;
+ }
+
+}