1 /* 2 * Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 import java.nio.file.Path; 25 import java.io.BufferedReader; 26 import java.io.ByteArrayOutputStream; 27 import java.io.Closeable; 28 import java.io.File; 29 import java.io.FileFilter; 30 import java.io.FileOutputStream; 31 import java.io.IOException; 32 import java.io.InputStream; 33 import java.io.InputStreamReader; 34 import java.io.PrintStream; 35 import java.net.URI; 36 import java.nio.charset.Charset; 37 import java.nio.file.attribute.BasicFileAttributes; 38 import java.nio.file.FileSystem; 39 import java.nio.file.FileSystems; 40 import java.nio.file.FileVisitResult; 41 import java.nio.file.FileVisitor; 42 import java.nio.file.Files; 43 import java.util.ArrayList; 44 import java.util.Arrays; 45 import java.util.Collections; 46 import java.util.List; 47 import java.util.Map; 48 import java.util.HashMap; 49 import java.util.jar.JarFile; 50 import java.util.jar.JarOutputStream; 51 import java.util.jar.Pack200; 52 import java.util.regex.Matcher; 53 import java.util.regex.Pattern; 54 import java.util.zip.ZipEntry; 55 import java.util.zip.ZipFile; 56 57 import static java.nio.file.StandardCopyOption.*; 58 import static java.nio.file.StandardOpenOption.*; 59 60 61 /** 62 * 63 * @author ksrini 64 */ 65 66 /* 67 * This class contains the commonly used utilities. 68 */ 69 class Utils { 70 static final String JavaHome = System.getProperty("test.java", 71 System.getProperty("java.home")); 72 static final boolean IsWindows = 73 System.getProperty("os.name").startsWith("Windows"); 74 static final boolean Is64Bit = 75 System.getProperty("sun.arch.data.model", "32").equals("64"); 76 static final File JavaSDK = new File(JavaHome); 77 78 static final String PACK_FILE_EXT = ".pack"; 79 static final String JAVA_FILE_EXT = ".java"; 80 static final String CLASS_FILE_EXT = ".class"; 81 static final String JAR_FILE_EXT = ".jar"; 82 83 static final File TEST_SRC_DIR = new File(System.getProperty("test.src")); 84 static final File TEST_CLS_DIR = new File(System.getProperty("test.classes")); 85 static final String VERIFIER_DIR_NAME = "pack200-verifier"; 86 static final File VerifierJar = new File(VERIFIER_DIR_NAME + JAR_FILE_EXT); 87 static final File XCLASSES = new File("xclasses"); 88 89 private Utils() {} // all static 90 91 private static void init() throws IOException { 92 if (VerifierJar.exists()) { 93 return; 94 } 95 File srcDir = new File(getVerifierDir(), "src"); 96 List<File> javaFileList = findFiles(srcDir, createFilter(JAVA_FILE_EXT)); 97 File tmpFile = File.createTempFile("javac", ".tmp", new File(".")); 98 XCLASSES.mkdirs(); 99 FileOutputStream fos = null; 100 PrintStream ps = null; 101 try { 102 fos = new FileOutputStream(tmpFile); 103 ps = new PrintStream(fos); 104 for (File f : javaFileList) { 105 ps.println(f.getAbsolutePath()); 106 } 107 } finally { 108 close(ps); 109 close(fos); 110 } 111 112 compiler("-d", 113 XCLASSES.getName(), 114 "--add-modules=jdk.jdeps", 115 "--add-exports=jdk.jdeps/com.sun.tools.classfile=ALL-UNNAMED", 116 "@" + tmpFile.getAbsolutePath()); 117 118 jar("cvfe", 119 VerifierJar.getName(), 120 "sun.tools.pack.verify.Main", 121 "-C", 122 XCLASSES.getName(), 123 "."); 124 } 125 126 private static File getVerifierDir() { 127 File srcDir = new File(TEST_SRC_DIR, VERIFIER_DIR_NAME); 128 if (!srcDir.exists()) { 129 // if not available try one level above 130 srcDir = new File(TEST_SRC_DIR.getParentFile(), VERIFIER_DIR_NAME); 131 } 132 return srcDir; 133 } 134 135 static File getGoldenJar() { 136 return new File(new File(getVerifierDir(), "data"), "golden.jar"); 137 } 138 static void dirlist(File dir) { 139 File[] files = dir.listFiles(); 140 System.out.println("--listing " + dir.getAbsolutePath() + "---"); 141 for (File f : files) { 142 StringBuffer sb = new StringBuffer(); 143 sb.append(f.isDirectory() ? "d " : "- "); 144 sb.append(f.getName()); 145 System.out.println(sb); 146 } 147 } 148 static void doCompareVerify(File reference, File specimen) throws IOException { 149 init(); 150 List<String> cmds = new ArrayList<String>(); 151 cmds.add(getJavaCmd()); 152 cmds.add("--add-exports=jdk.jdeps/com.sun.tools.classfile=ALL-UNNAMED"); 153 cmds.add("-cp"); 154 cmds.add(VerifierJar.getName()); 155 cmds.add("sun.tools.pack.verify.Main"); 156 cmds.add(reference.getAbsolutePath()); 157 cmds.add(specimen.getAbsolutePath()); 158 cmds.add("-O"); 159 runExec(cmds); 160 } 161 162 static void doCompareBitWise(File reference, File specimen) 163 throws IOException { 164 init(); 165 List<String> cmds = new ArrayList<String>(); 166 cmds.add(getJavaCmd()); 167 cmds.add("-cp"); 168 cmds.add(VerifierJar.getName()); 169 cmds.add("sun.tools.pack.verify.Main"); 170 cmds.add(reference.getName()); 171 cmds.add(specimen.getName()); 172 cmds.add("-O"); 173 cmds.add("-b"); 174 runExec(cmds); 175 } 176 177 static FileFilter createFilter(final String extension) { 178 return new FileFilter() { 179 @Override 180 public boolean accept(File pathname) { 181 String name = pathname.getName(); 182 if (name.endsWith(extension)) { 183 return true; 184 } 185 return false; 186 } 187 }; 188 } 189 190 /* 191 * clean up all the usual suspects 192 */ 193 static void cleanup() throws IOException { 194 recursiveDelete(XCLASSES); 195 List<File> toDelete = new ArrayList<>(); 196 toDelete.addAll(Utils.findFiles(new File("."), 197 Utils.createFilter(".out"))); 198 toDelete.addAll(Utils.findFiles(new File("."), 199 Utils.createFilter(".bak"))); 200 toDelete.addAll(Utils.findFiles(new File("."), 201 Utils.createFilter(".jar"))); 202 toDelete.addAll(Utils.findFiles(new File("."), 203 Utils.createFilter(".pack"))); 204 toDelete.addAll(Utils.findFiles(new File("."), 205 Utils.createFilter(".bnd"))); 206 toDelete.addAll(Utils.findFiles(new File("."), 207 Utils.createFilter(".txt"))); 208 toDelete.addAll(Utils.findFiles(new File("."), 209 Utils.createFilter(".idx"))); 210 toDelete.addAll(Utils.findFiles(new File("."), 211 Utils.createFilter(".gidx"))); 212 toDelete.addAll(Utils.findFiles(new File("."), 213 Utils.createFilter(".tmp"))); 214 toDelete.addAll(Utils.findFiles(new File("."), 215 Utils.createFilter(".class"))); 216 for (File f : toDelete) { 217 f.delete(); 218 } 219 } 220 221 static final FileFilter DIR_FILTER = new FileFilter() { 222 public boolean accept(File pathname) { 223 if (pathname.isDirectory()) { 224 return true; 225 } 226 return false; 227 } 228 }; 229 230 static final FileFilter FILE_FILTER = new FileFilter() { 231 public boolean accept(File pathname) { 232 if (pathname.isFile()) { 233 return true; 234 } 235 return false; 236 } 237 }; 238 239 static void copyFile(File src, File dst) throws IOException { 240 Path parent = dst.toPath().getParent(); 241 if (parent != null) { 242 Files.createDirectories(parent); 243 } 244 Files.copy(src.toPath(), dst.toPath(), COPY_ATTRIBUTES, REPLACE_EXISTING); 245 if (dst.isDirectory() && !dst.canWrite()) { 246 dst.setWritable(true); 247 } 248 } 249 250 static String baseName(File file, String extension) { 251 return baseName(file.getAbsolutePath(), extension); 252 } 253 254 static String baseName(String name, String extension) { 255 int cut = name.length() - extension.length(); 256 return name.lastIndexOf(extension) == cut 257 ? name.substring(0, cut) 258 : name; 259 260 } 261 static void createFile(File outFile, List<String> content) throws IOException { 262 Files.write(outFile.getAbsoluteFile().toPath(), content, 263 Charset.defaultCharset(), CREATE_NEW, TRUNCATE_EXISTING); 264 } 265 266 /* 267 * Suppose a path is provided which consists of a full path 268 * this method returns the sub path for a full path ex: /foo/bar/baz/foobar.z 269 * and the base path is /foo/bar it will will return baz/foobar.z. 270 */ 271 private static String getEntryPath(String basePath, String fullPath) { 272 if (!fullPath.startsWith(basePath)) { 273 return null; 274 } 275 return fullPath.substring(basePath.length()); 276 } 277 278 static String getEntryPath(File basePathFile, File fullPathFile) { 279 return getEntryPath(basePathFile.toString(), fullPathFile.toString()); 280 } 281 282 public static void recursiveCopy(File src, File dest) throws IOException { 283 if (!src.exists() || !src.canRead()) { 284 throw new IOException("file not found or readable: " + src); 285 } 286 if (dest.exists() && !dest.isDirectory() && !dest.canWrite()) { 287 throw new IOException("file not found or writeable: " + dest); 288 } 289 if (!dest.exists()) { 290 dest.mkdirs(); 291 } 292 List<File> a = directoryList(src); 293 for (File f : a) { 294 copyFile(f, new File(dest, getEntryPath(src, f))); 295 } 296 } 297 298 static List<File> directoryList(File dirname) { 299 List<File> dirList = new ArrayList<File>(); 300 return directoryList(dirname, dirList, null); 301 } 302 303 private static List<File> directoryList(File dirname, List<File> dirList, 304 File[] dirs) { 305 dirList.addAll(Arrays.asList(dirname.listFiles(FILE_FILTER))); 306 dirs = dirname.listFiles(DIR_FILTER); 307 for (File f : dirs) { 308 if (f.isDirectory() && !f.equals(dirname)) { 309 dirList.add(f); 310 directoryList(f, dirList, dirs); 311 } 312 } 313 return dirList; 314 } 315 316 static void recursiveDelete(File dir) throws IOException { 317 if (dir.isFile()) { 318 dir.delete(); 319 } else if (dir.isDirectory()) { 320 File[] entries = dir.listFiles(); 321 for (int i = 0; i < entries.length; i++) { 322 if (entries[i].isDirectory()) { 323 recursiveDelete(entries[i]); 324 } 325 entries[i].delete(); 326 } 327 dir.delete(); 328 } 329 } 330 331 static List<File> findFiles(File startDir, FileFilter filter) 332 throws IOException { 333 List<File> list = new ArrayList<File>(); 334 findFiles0(startDir, list, filter); 335 return list; 336 } 337 /* 338 * finds files in the start directory using the the filter, appends 339 * the files to the dirList. 340 */ 341 private static void findFiles0(File startDir, List<File> list, 342 FileFilter filter) throws IOException { 343 File[] foundFiles = startDir.listFiles(filter); 344 if (foundFiles == null) { 345 return; 346 } 347 list.addAll(Arrays.asList(foundFiles)); 348 File[] dirs = startDir.listFiles(DIR_FILTER); 349 for (File dir : dirs) { 350 findFiles0(dir, list, filter); 351 } 352 } 353 354 static void close(Closeable c) { 355 if (c == null) { 356 return; 357 } 358 try { 359 c.close(); 360 } catch (IOException ignore) { 361 } 362 } 363 364 static void compiler(String... javacCmds) { 365 List<String> cmdList = new ArrayList<>(); 366 cmdList.add(getJavacCmd()); 367 for (String x : javacCmds) { 368 cmdList.add(x); 369 } 370 runExec(cmdList); 371 } 372 373 static void jar(String... jargs) { 374 List<String> cmdList = new ArrayList<>(); 375 cmdList.add(getJarCmd()); 376 for (String x : jargs) { 377 cmdList.add(x); 378 } 379 runExec(cmdList); 380 } 381 382 static void testWithRepack(File inFile, String... repackOpts) throws IOException { 383 File cwd = new File("."); 384 // pack using --repack in native mode 385 File nativejarFile = new File(cwd, "out-n" + Utils.JAR_FILE_EXT); 386 repack(inFile, nativejarFile, false, repackOpts); 387 doCompareVerify(inFile, nativejarFile); 388 389 // ensure bit compatibility between the unpacker variants 390 File javajarFile = new File(cwd, "out-j" + Utils.JAR_FILE_EXT); 391 repack(inFile, javajarFile, true, repackOpts); 392 doCompareBitWise(javajarFile, nativejarFile); 393 } 394 395 static List<String> repack(File inFile, File outFile, 396 boolean disableNative, String... extraOpts) { 397 List<String> cmdList = new ArrayList<>(); 398 cmdList.clear(); 399 cmdList.add(Utils.getJavaCmd()); 400 cmdList.add("-ea"); 401 cmdList.add("-esa"); 402 if (disableNative) { 403 cmdList.add("-Dcom.sun.java.util.jar.pack.disable.native=true"); 404 } 405 cmdList.add("com.sun.java.util.jar.pack.Driver"); 406 cmdList.add("--repack"); 407 if (extraOpts != null) { 408 for (String opt: extraOpts) { 409 cmdList.add(opt); 410 } 411 } 412 cmdList.add(outFile.getName()); 413 cmdList.add(inFile.getName()); 414 return Utils.runExec(cmdList); 415 } 416 417 // given a jar file foo.jar will write to foo.pack 418 static void pack(JarFile jarFile, File packFile) throws IOException { 419 Pack200.Packer packer = Pack200.newPacker(); 420 Map<String, String> p = packer.properties(); 421 // Take the time optimization vs. space 422 p.put(packer.EFFORT, "1"); // CAUTION: do not use 0. 423 // Make the memory consumption as effective as possible 424 p.put(packer.SEGMENT_LIMIT, "10000"); 425 // ignore all JAR deflation requests to save time 426 p.put(packer.DEFLATE_HINT, packer.FALSE); 427 // save the file ordering of the original JAR 428 p.put(packer.KEEP_FILE_ORDER, packer.TRUE); 429 FileOutputStream fos = null; 430 try { 431 // Write out to a jtreg scratch area 432 fos = new FileOutputStream(packFile); 433 // Call the packer 434 packer.pack(jarFile, fos); 435 } finally { 436 close(fos); 437 } 438 } 439 440 // uses java unpacker, slow but useful to discover issues with the packer 441 static void unpackj(File inFile, JarOutputStream jarStream) 442 throws IOException { 443 unpack0(inFile, jarStream, true); 444 445 } 446 447 // uses native unpacker using the java APIs 448 static void unpackn(File inFile, JarOutputStream jarStream) 449 throws IOException { 450 unpack0(inFile, jarStream, false); 451 } 452 453 // given a packed file, create the jar file in the current directory. 454 private static void unpack0(File inFile, JarOutputStream jarStream, 455 boolean useJavaUnpack) throws IOException { 456 // Unpack the files 457 Pack200.Unpacker unpacker = Pack200.newUnpacker(); 458 Map<String, String> props = unpacker.properties(); 459 if (useJavaUnpack) { 460 props.put("com.sun.java.util.jar.pack.disable.native", "true"); 461 } 462 // Call the unpacker 463 unpacker.unpack(inFile, jarStream); 464 } 465 466 static byte[] getBuffer(ZipFile zf, ZipEntry ze) throws IOException { 467 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 468 byte buf[] = new byte[8192]; 469 InputStream is = null; 470 try { 471 is = zf.getInputStream(ze); 472 int n = is.read(buf); 473 while (n > 0) { 474 baos.write(buf, 0, n); 475 n = is.read(buf); 476 } 477 return baos.toByteArray(); 478 } finally { 479 close(is); 480 } 481 } 482 483 static ArrayList<String> getZipFileEntryNames(ZipFile z) { 484 ArrayList<String> out = new ArrayList<String>(); 485 for (ZipEntry ze : Collections.list(z.entries())) { 486 out.add(ze.getName()); 487 } 488 return out; 489 } 490 491 static List<String> runExec(String... cmds) { 492 return runExec(Arrays.asList(cmds)); 493 } 494 495 static List<String> runExec(List<String> cmdsList) { 496 return runExec(cmdsList, null); 497 } 498 499 static List<String> runExec(List<String> cmdsList, Map<String, String> penv) { 500 return runExec(cmdsList, penv, false); 501 } 502 503 static List<String> runExec(List<String> cmdsList, Map<String, String> penv, boolean ignoreReturnValue) { 504 ArrayList<String> alist = new ArrayList<String>(); 505 ProcessBuilder pb = 506 new ProcessBuilder(cmdsList); 507 Map<String, String> env = pb.environment(); 508 if (penv != null && !penv.isEmpty()) { 509 env.putAll(penv); 510 } 511 pb.directory(new File(".")); 512 dirlist(new File(".")); 513 for (String x : cmdsList) { 514 System.out.print(x + " "); 515 } 516 System.out.println(""); 517 int retval = 0; 518 Process p = null; 519 InputStreamReader ir = null; 520 BufferedReader rd = null; 521 InputStream is = null; 522 try { 523 pb.redirectErrorStream(true); 524 p = pb.start(); 525 is = p.getInputStream(); 526 ir = new InputStreamReader(is); 527 rd = new BufferedReader(ir, 8192); 528 529 String in = rd.readLine(); 530 while (in != null) { 531 alist.add(in); 532 System.out.println(in); 533 in = rd.readLine(); 534 } 535 retval = p.waitFor(); 536 if (!ignoreReturnValue && retval != 0) { 537 throw new RuntimeException("process failed with non-zero exit"); 538 } 539 } catch (Exception ex) { 540 throw new RuntimeException(ex.getMessage()); 541 } finally { 542 close(rd); 543 close(ir); 544 close(is); 545 if (p != null) { 546 p.destroy(); 547 } 548 } 549 return alist; 550 } 551 552 static String getUnpack200Cmd() { 553 return getAjavaCmd("unpack200"); 554 } 555 556 static String getPack200Cmd() { 557 return getAjavaCmd("pack200"); 558 } 559 560 static String getJavaCmd() { 561 return getAjavaCmd("java"); 562 } 563 564 static String getJavacCmd() { 565 return getAjavaCmd("javac"); 566 } 567 568 static String getJarCmd() { 569 return getAjavaCmd("jar"); 570 } 571 572 static String getAjavaCmd(String cmdStr) { 573 File binDir = new File(JavaHome, "bin"); 574 File unpack200File = IsWindows 575 ? new File(binDir, cmdStr + ".exe") 576 : new File(binDir, cmdStr); 577 578 String cmd = unpack200File.getAbsolutePath(); 579 if (!unpack200File.canExecute()) { 580 throw new RuntimeException("please check" + 581 cmd + " exists and is executable"); 582 } 583 return cmd; 584 } 585 586 // used to get all classes 587 static File createRtJar() throws Exception { 588 File rtJar = new File("rt.jar"); 589 new JrtToZip(".*\\.class", rtJar).run(); 590 return rtJar; 591 } 592 593 // used to select the contents 594 static File createRtJar(String pattern) throws Exception { 595 File rtJar = new File("rt.jar"); 596 new JrtToZip(pattern, rtJar).run(); 597 return rtJar; 598 } 599 600 /* 601 * A helper class to create a pseudo rt.jar. 602 */ 603 static class JrtToZip { 604 605 final File outFile; 606 final Pattern pattern; 607 608 public static void main(String[] args) throws Exception { 609 new JrtToZip(args[0], new File(args[1])).run(); 610 } 611 612 JrtToZip(String pattern, File outFile) throws Exception { 613 this.pattern = Pattern.compile(pattern); 614 this.outFile = outFile; 615 } 616 617 void run() throws Exception { 618 URI uri = URI.create("jar:" + outFile.toURI()); 619 Map<String, String> env = new HashMap<>(); 620 env.put("create", "true"); 621 try (FileSystem zipfs = FileSystems.newFileSystem(uri, env)) { 622 toZipfs(zipfs); 623 } 624 } 625 626 void toZipfs(FileSystem zipfs) throws Exception { 627 FileSystem jrtfs = FileSystems.getFileSystem(URI.create("jrt:/")); 628 for (Path root : jrtfs.getRootDirectories()) { 629 Files.walkFileTree(root, new FileVisitor<Path>() { 630 @Override 631 public FileVisitResult preVisitDirectory(Path dir, 632 BasicFileAttributes attrs) throws IOException { 633 // ignore unneeded directory 634 if (dir.startsWith("/packages")) 635 return FileVisitResult.SKIP_SUBTREE; 636 637 // pre-create required directories 638 Path zpath = zipfs.getPath(dir.toString()); 639 Files.createDirectories(zpath); 640 return FileVisitResult.CONTINUE; 641 } 642 643 @Override 644 public FileVisitResult visitFile(Path file, 645 BasicFileAttributes attrs) throws IOException { 646 Matcher matcher = pattern.matcher(file.toString()); 647 if (matcher.matches()) { 648 // System.out.println("x: " + file); 649 Path zpath = zipfs.getPath(file.toString()); 650 Files.copy(file, zpath, REPLACE_EXISTING); 651 } 652 return FileVisitResult.CONTINUE; 653 } 654 655 @Override 656 public FileVisitResult visitFileFailed(Path file, 657 IOException exc) throws IOException { 658 return FileVisitResult.CONTINUE; 659 } 660 661 @Override 662 public FileVisitResult postVisitDirectory(Path dir, 663 IOException exc) throws IOException { 664 return FileVisitResult.CONTINUE; 665 } 666 }); 667 } 668 } 669 } 670 }