1 /******************************************************************************* 2 * Copyright (c) 2009, 2019 Mountainminds GmbH & Co. KG and Contributors 3 * All rights reserved. This program and the accompanying materials 4 * are made available under the terms of the Eclipse Public License v1.0 5 * which accompanies this distribution, and is available at 6 * http://www.eclipse.org/legal/epl-v10.html 7 * 8 * Contributors: 9 * John Keeping - initial implementation 10 * Marc R. Hoffmann - rework 11 * 12 *******************************************************************************/ 13 package org.jacoco.cli.internal.commands; 14 15 import java.io.File; 16 import java.io.FileOutputStream; 17 import java.io.IOException; 18 import java.io.PrintWriter; 19 import java.util.ArrayList; 20 import java.util.Collection; 21 import java.util.List; 22 23 import org.jacoco.cli.internal.Command; 24 import org.jacoco.core.analysis.Analyzer; 25 import org.jacoco.core.analysis.CoverageBuilder; 26 import org.jacoco.core.analysis.IBundleCoverage; 27 import org.jacoco.core.analysis.IClassCoverage; 28 import org.jacoco.core.data.ExecutionDataStore; 29 import org.jacoco.core.tools.ExecFileLoader; 30 import org.jacoco.report.DirectorySourceFileLocator; 31 import org.jacoco.report.FileMultiReportOutput; 32 import org.jacoco.report.IReportVisitor; 33 import org.jacoco.report.ISourceFileLocator; 34 import org.jacoco.report.MultiReportVisitor; 35 import org.jacoco.report.MultiSourceFileLocator; 36 import org.jacoco.report.csv.CSVFormatter; 37 import org.jacoco.report.html.HTMLFormatter; 38 import org.jacoco.report.xml.XMLFormatter; 39 import org.kohsuke.args4j.Argument; 40 import org.kohsuke.args4j.Option; 41 42 /** 43 * The <code>report</code> command. 44 */ 45 public class Report extends Command { 46 47 @Argument(usage = "list of JaCoCo *.exec files to read", metaVar = "<execfiles>") 48 List<File> execfiles = new ArrayList<File>(); 49 50 @Option(name = "--classfiles", usage = "location of Java class files", metaVar = "<path>", required = true) 51 List<File> classfiles = new ArrayList<File>(); 52 53 @Option(name = "--sourcefiles", usage = "location of the source files", metaVar = "<path>") 54 List<File> sourcefiles = new ArrayList<File>(); 55 56 @Option(name = "--tabwith", usage = "tab stop width for the source pages (default 4)", metaVar = "<n>") 57 int tabwidth = 4; 58 59 @Option(name = "--name", usage = "name used for this report", metaVar = "<name>") 60 String name = "JaCoCo Coverage Report"; 61 62 @Option(name = "--encoding", usage = "source file encoding (by default platform encoding is used)", metaVar = "<charset>") 63 String encoding; 64 65 @Option(name = "--xml", usage = "output file for the XML report", metaVar = "<file>") 66 File xml; 67 68 @Option(name = "--csv", usage = "output file for the CSV report", metaVar = "<file>") 69 File csv; 70 71 @Option(name = "--html", usage = "output directory for the HTML report", metaVar = "<dir>") 72 File html; 73 74 @Override description()75 public String description() { 76 return "Generate reports in different formats by reading exec and Java class files."; 77 } 78 79 @Override execute(final PrintWriter out, final PrintWriter err)80 public int execute(final PrintWriter out, final PrintWriter err) 81 throws IOException { 82 final ExecFileLoader loader = loadExecutionData(out); 83 final IBundleCoverage bundle = analyze(loader.getExecutionDataStore(), 84 out); 85 writeReports(bundle, loader, out); 86 return 0; 87 } 88 loadExecutionData(final PrintWriter out)89 private ExecFileLoader loadExecutionData(final PrintWriter out) 90 throws IOException { 91 final ExecFileLoader loader = new ExecFileLoader(); 92 if (execfiles.isEmpty()) { 93 out.println("[WARN] No execution data files provided."); 94 } else { 95 for (final File file : execfiles) { 96 out.printf("[INFO] Loading execution data file %s.%n", 97 file.getAbsolutePath()); 98 loader.load(file); 99 } 100 } 101 return loader; 102 } 103 analyze(final ExecutionDataStore data, final PrintWriter out)104 private IBundleCoverage analyze(final ExecutionDataStore data, 105 final PrintWriter out) throws IOException { 106 final CoverageBuilder builder = new CoverageBuilder(); 107 final Analyzer analyzer = new Analyzer(data, builder); 108 for (final File f : classfiles) { 109 analyzer.analyzeAll(f); 110 } 111 printNoMatchWarning(builder.getNoMatchClasses(), out); 112 return builder.getBundle(name); 113 } 114 printNoMatchWarning(final Collection<IClassCoverage> nomatch, final PrintWriter out)115 private void printNoMatchWarning(final Collection<IClassCoverage> nomatch, 116 final PrintWriter out) { 117 if (!nomatch.isEmpty()) { 118 out.println( 119 "[WARN] Some classes do not match with execution data."); 120 out.println( 121 "[WARN] For report generation the same class files must be used as at runtime."); 122 for (final IClassCoverage c : nomatch) { 123 out.printf( 124 "[WARN] Execution data for class %s does not match.%n", 125 c.getName()); 126 } 127 } 128 } 129 writeReports(final IBundleCoverage bundle, final ExecFileLoader loader, final PrintWriter out)130 private void writeReports(final IBundleCoverage bundle, 131 final ExecFileLoader loader, final PrintWriter out) 132 throws IOException { 133 out.printf("[INFO] Analyzing %s classes.%n", 134 Integer.valueOf(bundle.getClassCounter().getTotalCount())); 135 final IReportVisitor visitor = createReportVisitor(); 136 visitor.visitInfo(loader.getSessionInfoStore().getInfos(), 137 loader.getExecutionDataStore().getContents()); 138 visitor.visitBundle(bundle, getSourceLocator()); 139 visitor.visitEnd(); 140 } 141 createReportVisitor()142 private IReportVisitor createReportVisitor() throws IOException { 143 final List<IReportVisitor> visitors = new ArrayList<IReportVisitor>(); 144 145 if (xml != null) { 146 final XMLFormatter formatter = new XMLFormatter(); 147 visitors.add(formatter.createVisitor(new FileOutputStream(xml))); 148 } 149 150 if (csv != null) { 151 final CSVFormatter formatter = new CSVFormatter(); 152 visitors.add(formatter.createVisitor(new FileOutputStream(csv))); 153 } 154 155 if (html != null) { 156 final HTMLFormatter formatter = new HTMLFormatter(); 157 visitors.add( 158 formatter.createVisitor(new FileMultiReportOutput(html))); 159 } 160 161 return new MultiReportVisitor(visitors); 162 } 163 getSourceLocator()164 private ISourceFileLocator getSourceLocator() { 165 final MultiSourceFileLocator multi = new MultiSourceFileLocator( 166 tabwidth); 167 for (final File f : sourcefiles) { 168 multi.add(new DirectorySourceFileLocator(f, encoding, tabwidth)); 169 } 170 return multi; 171 } 172 173 } 174