Path: blob/master/test/jdk/tools/lib/tests/JImageGenerator.java
41149 views
/*1* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/22package tests;2324import java.io.ByteArrayInputStream;25import java.io.ByteArrayOutputStream;26import java.io.File;27import java.io.FileOutputStream;28import java.io.IOException;29import java.io.InputStream;30import java.io.OutputStream;31import java.io.PrintStream;32import java.io.PrintWriter;33import java.io.StringWriter;34import java.nio.file.Files;35import java.nio.file.Path;36import java.nio.file.Paths;37import java.nio.file.StandardCopyOption;38import java.util.ArrayList;39import java.util.Arrays;40import java.util.Collections;41import java.util.HashSet;42import java.util.List;43import java.util.Objects;44import java.util.Set;45import java.util.jar.JarEntry;46import java.util.jar.JarInputStream;47import java.util.jar.JarOutputStream;48import java.util.stream.Collectors;49import java.util.stream.Stream;50import java.util.zip.ZipEntry;5152import javax.tools.JavaCompiler;53import javax.tools.StandardJavaFileManager;54import javax.tools.StandardLocation;55import javax.tools.ToolProvider;5657/**58*59* A generator for jmods, jars and images.60*/61public class JImageGenerator {6263public static final String LOAD_ALL_CLASSES_TEMPLATE = "package PACKAGE;\n"64+ "\n"65+ "import java.net.URI;\n"66+ "import java.nio.file.FileSystems;\n"67+ "import java.nio.file.Files;\n"68+ "import java.nio.file.Path;\n"69+ "import java.util.function.Function;\n"70+ "\n"71+ "public class CLASS {\n"72+ " private static long total_time;\n"73+ " private static long num_classes;\n"74+ " public static void main(String[] args) throws Exception {\n"75+ " Function<Path, String> formatter = (path) -> {\n"76+ " String clazz = path.toString().substring(\"modules/\".length()+1, path.toString().lastIndexOf(\".\"));\n"77+ " clazz = clazz.substring(clazz.indexOf(\"/\") + 1);\n"78+ " return clazz.replaceAll(\"/\", \"\\\\.\");\n"79+ " };\n"80+ " Files.walk(FileSystems.getFileSystem(URI.create(\"jrt:/\")).getPath(\"/modules/\")).\n"81+ " filter((p) -> {\n"82+ " return Files.isRegularFile(p) && p.toString().endsWith(\".class\")\n"83+ " && !p.toString().endsWith(\"module-info.class\");\n"84+ " }).\n"85+ " map(formatter).forEach((clazz) -> {\n"86+ " try {\n"87+ " long t = System.currentTimeMillis();\n"88+ " Class.forName(clazz, false, Thread.currentThread().getContextClassLoader());\n"89+ " total_time+= System.currentTimeMillis()-t;\n"90+ " num_classes+=1;\n"91+ " } catch (IllegalAccessError ex) {\n"92+ " // Security exceptions can occur, this is not what we are testing\n"93+ " System.err.println(\"Access error, OK \" + clazz);\n"94+ " } catch (Exception ex) {\n"95+ " System.err.println(\"ERROR \" + clazz);\n"96+ " throw new RuntimeException(ex);\n"97+ " }\n"98+ " });\n"99+ " double res = (double) total_time / num_classes;\n"100+ " // System.out.println(\"Total time \" + total_time + \" num classes \" + num_classes + \" average \" + res);\n"101+ " }\n"102+ "}\n";103104private static final String OUTPUT_OPTION = "--output";105private static final String POST_PROCESS_OPTION = "--post-process-path";106private static final String MAIN_CLASS_OPTION = "--main-class";107private static final String CLASS_PATH_OPTION = "--class-path";108private static final String MODULE_PATH_OPTION = "--module-path";109private static final String ADD_MODULES_OPTION = "--add-modules";110private static final String LIMIT_MODULES_OPTION = "--limit-modules";111private static final String PLUGIN_MODULE_PATH = "--plugin-module-path";112private static final String LAUNCHER = "--launcher";113114private static final String CMDS_OPTION = "--cmds";115private static final String CONFIG_OPTION = "--config";116private static final String HASH_MODULES_OPTION = "--hash-modules";117private static final String LIBS_OPTION = "--libs";118private static final String MODULE_VERSION_OPTION = "--module-version";119120private JImageGenerator() {}121122private static String optionsPrettyPrint(String... args) {123return Stream.of(args).collect(Collectors.joining(" "));124}125126public static File getJModsDir(File jdkHome) {127File jdkjmods = new File(jdkHome, "jmods");128if (!jdkjmods.exists()) {129return null;130}131return jdkjmods;132}133134public static Path addFiles(Path module, InMemoryFile... resources) throws IOException {135Path tempFile = Files.createTempFile("jlink-test", "");136try (JarInputStream in = new JarInputStream(Files.newInputStream(module));137JarOutputStream out = new JarOutputStream(new FileOutputStream(tempFile.toFile()))) {138ZipEntry entry;139while ((entry = in.getNextEntry()) != null) {140String name = entry.getName();141out.putNextEntry(new ZipEntry(name));142copy(in, out);143out.closeEntry();144}145for (InMemoryFile r : resources) {146addFile(r, out);147}148}149Files.move(tempFile, module, StandardCopyOption.REPLACE_EXISTING);150return module;151}152153private static void copy(InputStream in, OutputStream out) throws IOException {154int len;155byte[] buf = new byte[4096];156while ((len = in.read(buf)) > 0) {157out.write(buf, 0, len);158}159}160161public static JModTask getJModTask() {162return new JModTask();163}164165public static JLinkTask getJLinkTask() {166return new JLinkTask();167}168169public static JImageTask getJImageTask() {170return new JImageTask();171}172173private static void addFile(InMemoryFile resource, JarOutputStream target) throws IOException {174String fileName = resource.getPath();175fileName = fileName.replace("\\", "/");176String[] ss = fileName.split("/");177Path p = Paths.get("");178for (int i = 0; i < ss.length; ++i) {179if (i < ss.length - 1) {180if (!ss[i].isEmpty()) {181p = p.resolve(ss[i]);182JarEntry entry = new JarEntry(p.toString() + "/");183target.putNextEntry(entry);184target.closeEntry();185}186} else {187p = p.resolve(ss[i]);188JarEntry entry = new JarEntry(p.toString());189target.putNextEntry(entry);190copy(resource.getBytes(), target);191target.closeEntry();192}193}194}195196public static Path createNewFile(Path root, String pathName, String extension) {197Path out = root.resolve(pathName + extension);198int i = 1;199while (Files.exists(out)) {200out = root.resolve(pathName + "-" + (++i) + extension);201}202return out;203}204205public static Path generateSources(Path output, String moduleName, List<InMemorySourceFile> sources) throws IOException {206Path moduleDir = output.resolve(moduleName);207Files.createDirectory(moduleDir);208for (InMemorySourceFile source : sources) {209Path fileDir = moduleDir;210if (!source.packageName.isEmpty()) {211String dir = source.packageName.replace('.', File.separatorChar);212fileDir = moduleDir.resolve(dir);213Files.createDirectories(fileDir);214}215Files.write(fileDir.resolve(source.className + ".java"), source.source.getBytes());216}217return moduleDir;218}219220public static Path generateSourcesFromTemplate(Path output, String moduleName, String... classNames) throws IOException {221List<InMemorySourceFile> sources = new ArrayList<>();222for (String className : classNames) {223String packageName = getPackageName(className);224String simpleName = getSimpleName(className);225String content = LOAD_ALL_CLASSES_TEMPLATE226.replace("CLASS", simpleName);227if (packageName.isEmpty()) {228content = content.replace("package PACKAGE;", packageName);229} else {230content = content.replace("PACKAGE", packageName);231}232sources.add(new InMemorySourceFile(packageName, simpleName, content));233}234return generateSources(output, moduleName, sources);235}236237public static void generateModuleInfo(Path moduleDir, List<String> packages, String... dependencies) throws IOException {238StringBuilder moduleInfoBuilder = new StringBuilder();239Path file = moduleDir.resolve("module-info.java");240String moduleName = moduleDir.getFileName().toString();241moduleInfoBuilder.append("module ").append(moduleName).append("{\n");242for (String dep : dependencies) {243moduleInfoBuilder.append("requires ").append(dep).append(";\n");244}245for (String pkg : packages) {246if (!pkg.trim().isEmpty()) {247moduleInfoBuilder.append("exports ").append(pkg).append(";\n");248}249}250moduleInfoBuilder.append("}");251Files.write(file, moduleInfoBuilder.toString().getBytes());252}253254public static void compileSuccess(Path source, Path destination, String... options) throws IOException {255if (!compile(source, destination, options)) {256throw new AssertionError("Compilation failed.");257}258}259260public static boolean compile(Path source, Path destination, String... options) throws IOException {261JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();262try (StandardJavaFileManager jfm = compiler.getStandardFileManager(null, null, null)) {263List<Path> sources264= Files.find(source, Integer.MAX_VALUE,265(file, attrs) -> file.toString().endsWith(".java"))266.collect(Collectors.toList());267268Files.createDirectories(destination);269jfm.setLocationFromPaths(StandardLocation.CLASS_OUTPUT, Collections.singleton(destination));270271List<String> opts = Arrays.asList(options);272JavaCompiler.CompilationTask task273= compiler.getTask(null, jfm, null, opts, null,274jfm.getJavaFileObjectsFromPaths(sources));275List<String> list = new ArrayList<>(opts);276list.addAll(sources.stream()277.map(Path::toString)278.collect(Collectors.toList()));279System.err.println("javac options: " + optionsPrettyPrint(list.toArray(new String[list.size()])));280return task.call();281}282}283284public static Path createJarFile(Path jarfile, Path dir) throws IOException {285return createJarFile(jarfile, dir, Paths.get("."));286}287288public static Path createJarFile(Path jarfile, Path dir, Path file) throws IOException {289// create the target directory290Path parent = jarfile.getParent();291if (parent != null)292Files.createDirectories(parent);293294List<Path> entries = Files.find(dir.resolve(file), Integer.MAX_VALUE,295(p, attrs) -> attrs.isRegularFile())296.map(dir::relativize)297.collect(Collectors.toList());298299try (OutputStream out = Files.newOutputStream(jarfile);300JarOutputStream jos = new JarOutputStream(out)) {301for (Path entry : entries) {302// map the file path to a name in the JAR file303Path normalized = entry.normalize();304String name = normalized305.subpath(0, normalized.getNameCount()) // drop root306.toString()307.replace(File.separatorChar, '/');308309jos.putNextEntry(new JarEntry(name));310Files.copy(dir.resolve(entry), jos);311}312}313return jarfile;314}315316public static Set<String> getModuleContent(Path module) {317Result result = JImageGenerator.getJModTask()318.jmod(module)319.list();320result.assertSuccess();321return Stream.of(result.getMessage().split("\r?\n"))322.collect(Collectors.toSet());323}324325public static void checkModule(Path module, Set<String> expected) throws IOException {326Set<String> actual = getModuleContent(module);327if (!Objects.equals(actual, expected)) {328Set<String> unexpected = new HashSet<>(actual);329unexpected.removeAll(expected);330Set<String> notFound = new HashSet<>(expected);331notFound.removeAll(actual);332System.err.println("Unexpected files:");333unexpected.forEach(s -> System.err.println("\t" + s));334System.err.println("Not found files:");335notFound.forEach(s -> System.err.println("\t" + s));336throw new AssertionError("Module check failed.");337}338}339340public static class JModTask {341static final java.util.spi.ToolProvider JMOD_TOOL =342java.util.spi.ToolProvider.findFirst("jmod").orElseThrow(() ->343new RuntimeException("jmod tool not found")344);345346private final List<Path> classpath = new ArrayList<>();347private final List<Path> libs = new ArrayList<>();348private final List<Path> cmds = new ArrayList<>();349private final List<Path> config = new ArrayList<>();350private final List<Path> jars = new ArrayList<>();351private final List<Path> jmods = new ArrayList<>();352private final List<String> options = new ArrayList<>();353private Path output;354private String hashModules;355private String mainClass;356private String moduleVersion;357358public JModTask addNativeLibraries(Path cp) {359this.libs.add(cp);360return this;361}362363public JModTask hashModules(String hash) {364this.hashModules = hash;365return this;366}367368public JModTask addCmds(Path cp) {369this.cmds.add(cp);370return this;371}372373public JModTask addClassPath(Path cp) {374this.classpath.add(cp);375return this;376}377378public JModTask addConfig(Path cp) {379this.config.add(cp);380return this;381}382383public JModTask addJars(Path jars) {384this.jars.add(jars);385return this;386}387388public JModTask addJmods(Path jmods) {389this.jmods.add(jmods);390return this;391}392393public JModTask jmod(Path output) {394this.output = output;395return this;396}397398public JModTask moduleVersion(String moduleVersion) {399this.moduleVersion = moduleVersion;400return this;401}402403public JModTask mainClass(String mainClass) {404this.mainClass = mainClass;405return this;406}407408public JModTask option(String o) {409this.options.add(o);410return this;411}412413private String modulePath() {414// This is expect FIRST jmods THEN jars, if you change this, some tests could fail415String jmods = toPath(this.jmods);416String jars = toPath(this.jars);417return jmods + File.pathSeparator + jars;418}419420private String toPath(List<Path> paths) {421return paths.stream()422.map(Path::toString)423.collect(Collectors.joining(File.pathSeparator));424}425426private String[] optionsJMod(String cmd) {427List<String> options = new ArrayList<>();428options.add(cmd);429if (!cmds.isEmpty()) {430options.add(CMDS_OPTION);431options.add(toPath(cmds));432}433if (!config.isEmpty()) {434options.add(CONFIG_OPTION);435options.add(toPath(config));436}437if (hashModules != null) {438options.add(HASH_MODULES_OPTION);439options.add(hashModules);440}441if (mainClass != null) {442options.add(MAIN_CLASS_OPTION);443options.add(mainClass);444}445if (!libs.isEmpty()) {446options.add(LIBS_OPTION);447options.add(toPath(libs));448}449if (!classpath.isEmpty()) {450options.add(CLASS_PATH_OPTION);451options.add(toPath(classpath));452}453if (!jars.isEmpty() || !jmods.isEmpty()) {454options.add(MODULE_PATH_OPTION);455options.add(modulePath());456}457if (moduleVersion != null) {458options.add(MODULE_VERSION_OPTION);459options.add(moduleVersion);460}461options.addAll(this.options);462if (output != null) {463options.add(output.toString());464}465return options.toArray(new String[options.size()]);466}467468public Result create() {469return cmd("create");470}471472public Result list() {473return cmd("list");474}475476public Result call() {477return cmd("");478}479480private Result cmd(String cmd) {481String[] args = optionsJMod(cmd);482System.err.println("jmod options: " + optionsPrettyPrint(args));483ByteArrayOutputStream baos = new ByteArrayOutputStream();484PrintStream ps = new PrintStream(baos);485int exitCode = JMOD_TOOL.run(ps, ps, args);486String msg = new String(baos.toByteArray());487return new Result(exitCode, msg, output);488}489}490491public static String getPackageName(String canonicalName) {492int index = canonicalName.lastIndexOf('.');493return index > 0 ? canonicalName.substring(0, index) : "";494}495496public static String getSimpleName(String canonicalName) {497int index = canonicalName.lastIndexOf('.');498return canonicalName.substring(index + 1);499}500501public static class JImageTask {502503private final List<Path> pluginModulePath = new ArrayList<>();504private final List<String> options = new ArrayList<>();505private Path dir;506private Path image;507508public JImageTask pluginModulePath(Path p) {509this.pluginModulePath.add(p);510return this;511}512513public JImageTask image(Path image) {514this.image = image;515return this;516}517518public JImageTask dir(Path dir) {519this.dir = dir;520return this;521}522523public JImageTask option(String o) {524this.options.add(o);525return this;526}527528private String toPath(List<Path> paths) {529return paths.stream()530.map(Path::toString)531.collect(Collectors.joining(File.pathSeparator));532}533534private String[] optionsJImage(String cmd) {535List<String> options = new ArrayList<>();536options.add(cmd);537if (dir != null) {538options.add("--dir");539options.add(dir.toString());540}541if (!pluginModulePath.isEmpty()) {542options.add(PLUGIN_MODULE_PATH);543options.add(toPath(pluginModulePath));544}545options.addAll(this.options);546options.add(image.toString());547return options.toArray(new String[options.size()]);548}549550private Result cmd(String cmd, Path returnPath) {551String[] args = optionsJImage(cmd);552System.err.println("jimage options: " + optionsPrettyPrint(args));553StringWriter writer = new StringWriter();554int exitCode = jdk.tools.jimage.Main.run(args, new PrintWriter(writer));555return new Result(exitCode, writer.toString(), returnPath);556}557558public Result extract() {559return cmd("extract", dir);560}561}562563public static class JLinkTask {564static final java.util.spi.ToolProvider JLINK_TOOL =565java.util.spi.ToolProvider.findFirst("jlink").orElseThrow(() ->566new RuntimeException("jlink tool not found")567);568569private final List<Path> jars = new ArrayList<>();570private final List<Path> jmods = new ArrayList<>();571private final List<Path> pluginModulePath = new ArrayList<>();572private final List<String> addMods = new ArrayList<>();573private final List<String> limitMods = new ArrayList<>();574private final List<String> options = new ArrayList<>();575private String modulePath;576// if you want to specifiy repeated --module-path option577private String repeatedModulePath;578// if you want to specifiy repeated --limit-modules option579private String repeatedLimitMods;580private Path output;581private Path existing;582private String launcher; // optional583584public JLinkTask modulePath(String modulePath) {585this.modulePath = modulePath;586return this;587}588589public JLinkTask launcher(String cmd) {590launcher = Objects.requireNonNull(cmd);591return this;592}593594public JLinkTask repeatedModulePath(String modulePath) {595this.repeatedModulePath = modulePath;596return this;597}598599public JLinkTask addJars(Path jars) {600this.jars.add(jars);601return this;602}603604public JLinkTask addJmods(Path jmods) {605this.jmods.add(jmods);606return this;607}608609public JLinkTask pluginModulePath(Path p) {610this.pluginModulePath.add(p);611return this;612}613614public JLinkTask addMods(String moduleName) {615this.addMods.add(moduleName);616return this;617}618619public JLinkTask limitMods(String moduleName) {620this.limitMods.add(moduleName);621return this;622}623624public JLinkTask repeatedLimitMods(String modules) {625this.repeatedLimitMods = modules;626return this;627}628629public JLinkTask output(Path output) {630this.output = output;631return this;632}633634public JLinkTask existing(Path existing) {635this.existing = existing;636return this;637}638639public JLinkTask option(String o) {640this.options.add(o);641return this;642}643644private String modulePath() {645// This is expect FIRST jmods THEN jars, if you change this, some tests could fail646String jmods = toPath(this.jmods);647String jars = toPath(this.jars);648return jmods + File.pathSeparator + jars;649}650651private String toPath(List<Path> paths) {652return paths.stream()653.map(Path::toString)654.collect(Collectors.joining(File.pathSeparator));655}656657private String[] optionsJLink() {658List<String> options = new ArrayList<>();659if (output != null) {660options.add(OUTPUT_OPTION);661options.add(output.toString());662}663if (!addMods.isEmpty()) {664options.add(ADD_MODULES_OPTION);665options.add(addMods.stream().collect(Collectors.joining(",")));666}667if (!limitMods.isEmpty()) {668options.add(LIMIT_MODULES_OPTION);669options.add(limitMods.stream().collect(Collectors.joining(",")));670}671if (repeatedLimitMods != null) {672options.add(LIMIT_MODULES_OPTION);673options.add(repeatedLimitMods);674}675if (!jars.isEmpty() || !jmods.isEmpty()) {676options.add(MODULE_PATH_OPTION);677options.add(modulePath());678}679if (modulePath != null) {680options.add(MODULE_PATH_OPTION);681options.add(modulePath);682}683if (repeatedModulePath != null) {684options.add(MODULE_PATH_OPTION);685options.add(repeatedModulePath);686}687if (!pluginModulePath.isEmpty()) {688options.add(PLUGIN_MODULE_PATH);689options.add(toPath(pluginModulePath));690}691if (launcher != null && !launcher.isEmpty()) {692options.add(LAUNCHER);693options.add(launcher);694}695options.addAll(this.options);696return options.toArray(new String[options.size()]);697}698699private String[] optionsPostProcessJLink() {700List<String> options = new ArrayList<>();701if (existing != null) {702options.add(POST_PROCESS_OPTION);703options.add(existing.toString());704}705options.addAll(this.options);706return options.toArray(new String[options.size()]);707}708709public Result call() {710String[] args = optionsJLink();711System.err.println("jlink options: " + optionsPrettyPrint(args));712StringWriter writer = new StringWriter();713PrintWriter pw = new PrintWriter(writer);714int exitCode = JLINK_TOOL.run(pw, pw, args);715return new Result(exitCode, writer.toString(), output);716}717718public Result callPostProcess() {719String[] args = optionsPostProcessJLink();720System.err.println("jlink options: " + optionsPrettyPrint(args));721StringWriter writer = new StringWriter();722PrintWriter pw = new PrintWriter(writer);723int exitCode = JLINK_TOOL.run(pw, pw, args);724return new Result(exitCode, writer.toString(), output);725}726}727728public static class InMemorySourceFile {729public final String packageName;730public final String className;731public final String source;732733public InMemorySourceFile(String packageName, String simpleName, String source) {734this.packageName = packageName;735this.className = simpleName;736this.source = source;737}738}739740public static class InMemoryFile {741private final String path;742private final byte[] bytes;743744public String getPath() {745return path;746}747748public InputStream getBytes() {749return new ByteArrayInputStream(bytes);750}751752public InMemoryFile(String path, byte[] bytes) {753this.path = path;754this.bytes = bytes;755}756757public InMemoryFile(String path, InputStream is) throws IOException {758this(path, readAllBytes(is));759}760}761762public static byte[] readAllBytes(InputStream is) throws IOException {763ByteArrayOutputStream baos = new ByteArrayOutputStream();764byte[] buf = new byte[1024];765while (true) {766int n = is.read(buf);767if (n < 0) {768break;769}770baos.write(buf, 0, n);771}772return baos.toByteArray();773}774}775776777