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