/*
* Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package build.tools.jigsaw;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.module.Configuration;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleFinder;
import java.lang.module.ModuleReference;
import java.lang.module.ResolvedModule;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import static java.lang.module.ModuleDescriptor.*;
import static build.tools.jigsaw.ModuleSummary.HtmlDocument.Selector.*;
import static build.tools.jigsaw.ModuleSummary.HtmlDocument.Division.*;
public class ModuleSummary {
private static final String USAGE = "Usage: ModuleSummary --module-path
-o [--root mn]*";
public static void main(String[] args) throws Exception {
int i=0;
Path modpath = null;
Path outfile = null;
Set roots = new HashSet<>();
while (i < args.length && args[i].startsWith("-")) {
String arg = args[i++];
switch (arg) {
case "--module-path":
modpath = Paths.get(args[i++]);
break;
case "-o":
outfile = Paths.get(args[i++]);
break;
case "--root":
roots.add(args[i++]);
default:
System.err.println(USAGE);
System.exit(-1);
}
}
if (outfile == null || modpath == null) {
System.err.println(USAGE);
System.exit(1);
}
Path dir = outfile.getParent() != null ? outfile.getParent() : Paths.get(".");
Files.createDirectories(dir);
Map modules = new HashMap<>();
Set mrefs = ModuleFinder.ofSystem().findAll();
for (ModuleReference mref : mrefs) {
String mn = mref.descriptor().name();
Path jmod = modpath.resolve(mn + ".jmod");
modules.put(mn, new ModuleSummary(mref, jmod));
}
if (roots.isEmpty()) {
roots.addAll(modules.keySet());
}
genReport(outfile, modules, roots, "JDK Module Summary");
}
static void genReport(Path outfile, Map modules, Set roots, String title)
throws IOException
{
Configuration cf = resolve(roots);
try (PrintStream out = new PrintStream(Files.newOutputStream(outfile))) {
HtmlDocument doc = new HtmlDocument(title, modules);
Set descriptors = cf.modules().stream()
.map(ResolvedModule::reference)
.map(ModuleReference::descriptor)
.collect(Collectors.toSet());
doc.writeTo(out, descriptors);
}
}
private final String name;
private final ModuleDescriptor descriptor;
private final JmodInfo jmodInfo;
ModuleSummary(ModuleReference mref, Path jmod) throws IOException {
this.name = mref.descriptor().name();
this.descriptor = mref.descriptor();
this.jmodInfo = new JmodInfo(jmod);
}
String name() {
return name;
}
long uncompressedSize() {
return jmodInfo.size;
}
long jmodFileSize() {
return jmodInfo.filesize; // estimated compressed size
}
ModuleDescriptor descriptor() {
return descriptor;
}
int numClasses() {
return jmodInfo.classCount;
}
long classBytes() {
return jmodInfo.classBytes;
}
int numResources() {
return jmodInfo.resourceCount;
}
long resourceBytes() {
return jmodInfo.resourceBytes;
}
int numConfigs() {
return jmodInfo.configCount;
}
long configBytes() {
return jmodInfo.configBytes;
}
int numCommands() {
return jmodInfo.nativeCmds.size();
}
long commandBytes() {
return jmodInfo.nativeCmds.values().stream()
.mapToLong(l -> l.longValue()).sum() - jmodInfo.debugInfoCmdBytes;
}
int numCommandsDebug() {
return jmodInfo.debugInfoCmdCount;
}
long commandDebugBytes() {
return jmodInfo.debugInfoCmdBytes;
}
int numNativeLibraries() {
return jmodInfo.nativeLibs.size();
}
long nativeLibrariesBytes() {
return jmodInfo.nativeLibs.values().stream()
.mapToLong(l -> l.longValue()).sum() - jmodInfo.debugInfoLibBytes;
}
int numNativeLibrariesDebug() {
return jmodInfo.debugInfoLibCount;
}
long nativeLibrariesDebugBytes() {
return jmodInfo.debugInfoLibBytes;
}
Map commands() {
return jmodInfo.nativeCmds;
}
Map nativeLibs() {
return jmodInfo.nativeLibs;
}
Map configFiles() {
return jmodInfo.configFiles;
}
static class JmodInfo {
final long size;
final long filesize;
final int classCount;
final long classBytes;
final int resourceCount;
final long resourceBytes;
final int configCount;
final long configBytes;
final int debugInfoLibCount;
final long debugInfoLibBytes;
final int debugInfoCmdCount;
final long debugInfoCmdBytes;
final Map configFiles = new HashMap<>();
final Map nativeCmds = new HashMap<>();
final Map nativeLibs = new HashMap<>();
JmodInfo(Path jmod) throws IOException {
long total = 0;
long cBytes = 0, rBytes = 0, cfBytes = 0, dizLibBytes = 0, dizCmdBytes = 0;
int cCount = 0, rCount = 0, cfCount = 0, dizLibCount = 0, dizCmdCount = 0;
try (ZipFile zf = new ZipFile(jmod.toFile())) {
for (Enumeration extends ZipEntry> e = zf.entries(); e.hasMoreElements(); ) {
ZipEntry ze = e.nextElement();
String fn = ze.getName();
int pos = fn.indexOf('/');
String dir = fn.substring(0, pos);
String filename = fn.substring(fn.lastIndexOf('/') + 1);
// name shown in the column
String name = filename;
long len = ze.getSize();
total += len;
switch (dir) {
case NATIVE_LIBS:
nativeLibs.put(name, len);
if (filename.endsWith(".diz")) {
dizLibCount++;
dizLibBytes += len;
}
break;
case NATIVE_CMDS:
nativeCmds.put(name, len);
if (filename.endsWith(".diz")) {
dizCmdCount++;
dizCmdBytes += len;
}
break;
case CLASSES:
if (filename.endsWith(".class")) {
cCount++;
cBytes += len;
} else {
rCount++;
rBytes += len;
}
break;
case CONFIG:
configFiles.put(name, len);
cfCount++;
cfBytes += len;
break;
default:
break;
}
}
this.filesize = jmod.toFile().length();
this.classCount = cCount;
this.classBytes = cBytes;
this.resourceCount = rCount;
this.resourceBytes = rBytes;
this.configCount = cfCount;
this.configBytes = cfBytes;
this.size = total;
this.debugInfoLibCount = dizLibCount;
this.debugInfoLibBytes = dizLibBytes;
this.debugInfoCmdCount = dizCmdCount;
this.debugInfoCmdBytes = dizCmdBytes;
}
}
static final String NATIVE_LIBS = "native";
static final String NATIVE_CMDS = "bin";
static final String CLASSES = "classes";
static final String CONFIG = "conf";
static final String MODULE_ID = "module/id";
static final String MODULE_MAIN_CLASS = "module/main-class";
}
static Configuration resolve(Set roots) {
return Configuration.empty()
.resolveRequires(ModuleFinder.ofSystem(),
ModuleFinder.of(),
roots);
}
static class HtmlDocument {
final String title;
final Map modules;
boolean requiresTransitiveNote = false;
boolean aggregatorNote = false;
boolean totalBytesNote = false;
HtmlDocument(String title, Map modules) {
this.title = title;
this.modules = modules;
}
void writeTo(PrintStream out, Set selectedModules) {
out.format("%n");
out.format("%s%n", title);
// stylesheet
Arrays.stream(HtmlDocument.STYLES).forEach(out::println);
out.format("%n");
// body begins
out.format("%n");
// title and date
out.println(DOCTITLE.toString(title));
out.println(VERSION.toString(String.format("%tc", new Date())));
// total modules and sizes
long totalBytes = selectedModules.stream()
.map(ModuleDescriptor::name)
.map(modules::get)
.mapToLong(ModuleSummary::uncompressedSize)
.sum();
String[] sections = new String[] {
String.format("%s: %d", "Total modules", selectedModules.size()),
String.format("%s: %,d bytes (%s %s)", "Total size",
totalBytes,
System.getProperty("os.name"),
System.getProperty("os.arch"))
};
out.println(SECTION.toString(sections));
// write table and header
out.println(String.format("