/* * Copyright (c) 2017, 2024, 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. * * 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. * */ import java.io.File; import java.io.IOException; import java.nio.file.FileSystemLoopException; import java.nio.file.FileVisitResult; import java.nio.file.FileVisitor; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.PosixFilePermission; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeSet; public final class XcodeProjectMaker { private static final String JDK_SCRIPT_TOKEN_1 = "configure"; private static final String JDK_SCRIPT_TOKEN_2 = ".jcheck"; private static final String COMPILER_LINE_HEADER = "-I"; private static final String COMPILER_IFRAMEWORK = "-iframework"; private static final String COMPILER_FFRAMEWORK = "-F"; private static final String SRC_HOTSPOT_PATH = "/src/hotspot"; private static final String TEST_HOTSPOT_PATH = "/test/hotspot/gtest"; private static final String ALIAS_JAVA_OLD = "java_old.sh"; private static final String ALIAS_JAVA_NEW = "java_new.sh"; private static final String JDK_BIN_JAVA = "/jdk/bin/java"; private static final String FILE_TOKEN = "\"file\": "; private static final String COMMAND_TOKEN = "\"command\": "; private static final String QUOTE_START_TOKEN = "'\\\""; private static final String QUOTE_END_TOKEN = "\\\"'"; private static final String VERSION = "2.0.0"; private static final String EXCLUDE_PARSE_TOKEN_1 = "gtest"; private static final String TEMPLATE_FRAMEWORK_SEARCH_PATHS = "TEMPLATE_FRAMEWORK_SEARCH_PATHS"; private static final String TEMPLATE_OTHER_CFLAGS = "TEMPLATE_OTHER_CFLAGS"; private static final String TEMPLATE_OTHER_LDFLAGS = "TEMPLATE_OTHER_LDFLAGS"; private static final String TEMPLATE_USER_HEADER_SEARCH_PATHS = "TEMPLATE_USER_HEADER_SEARCH_PATHS"; private static final String TEMPLATE_GROUP_GENSRC = "TEMPLATE_GROUP_GENSRC"; private static final String TEMPLATE_GROUP_SRC = "TEMPLATE_GROUP_SRC"; private static final String TEMPLATE_GROUP_TEST = "TEMPLATE_GROUP_TEST"; private static final String TEMPLATE_GROUPS = "TEMPLATE_GROUPS"; private static final String TEMPLATE_PBXBUILDFILE = "TEMPLATE_PBXBUILDFILE"; private static final String TEMPLATE_PBXFILEREFERENCE = "TEMPLATE_PBXFILEREFERENCE"; private static final String TEMPLATE_PBXSOURCESSBUILDPHASE = "TEMPLATE_PBXSOURCESSBUILDPHASE"; private static final String TEMPLATE_JDK_PATH = "TEMPLATE_JDK_PATH"; private static final String HOTSPOT_PBXPROJ = "hotspot.xcodeproj"; private static final String PBXPROJ = "project.pbxproj"; private static final String XCSAHAREDDATA = "xcshareddata"; private static final String XCSCHEMES = "xcschemes"; private static final String JVM_XCSCHEME = "jvm.xcscheme"; private static final String J2D_XCSCHEME = "runJ2Demo.xcscheme"; private static final String XCDEBUGGER = "xcdebugger"; private static final String XCBKPTLIST = "Breakpoints_v2.xcbkptlist"; private static final String TEMPLATE_PBXPROJ = PBXPROJ + ".template"; private static final String TEMPLATE_JVM_XCSCHEME = JVM_XCSCHEME + ".template"; private static final String TEMPLATE_J2D_XCSCHEME = J2D_XCSCHEME + ".template"; private static final String TEMPLATE_XCBKPTLIST = XCBKPTLIST + ".template"; private static final String[] EXCLUDE_FILES_PREFIX = {"."}; private static final String[] EXCLUDE_FILES_POSTFIX = {".log", ".cmdline"}; private static final String[] COMPILER_FLAGS_INCLUDE = {"-m", "-f", "-D", "-W"}; private static final String[] COMPILER_FLAGS_IS = {"-g", "-Os", "-0"}; private static final String[] COMPILER_FLAGS_EXCLUDE = {"-DTHIS_FILE", "-DGTEST_OS_MAC", "-mmacosx-version-min", "-Werror"}; // "-Werror" causes Xcode to stop compiling private static final int EXIT4 = -4; private static final int EXIT5 = -5; private static final int EXIT6 = -6; private static final int EXIT7 = -7; private final HashMap> compiledFiles = new HashMap<>(); private final TreeSet compilerFlags = new TreeSet<>(); private List linkerFlags = List.of(); private final TreeSet headerPaths = new TreeSet<>(); private final boolean debugLog; private String projectMakerDataPath = null; private String generatedHotspotPath = null; private String iframework = null; private String fframework = null; private DiskFile rootGensrc = new DiskFile("/", true); private DiskFile rootSrc = new DiskFile("/", true); private DiskFile rootTest = new DiskFile("/", true); public XcodeProjectMaker(boolean debugLog) { this.debugLog = debugLog; } public static void main(String[] args) { String workspaceRoot = args[0]; String outputDir = args[1]; String pathToProjectMakerData = args[2]; String pathToCompileCommands = args[3]; String pathToLinkerOptionsFile = args[4]; String linkerOptionsString = readFile(pathToLinkerOptionsFile); boolean debugLog = args.length > 5 && args[5].equals("-d"); File xcodeFolder = new File(outputDir); xcodeFolder.mkdirs(); String workspaceRootPathFromOutputDir = findRelativePathToWorkspaceRoot(outputDir); if (debugLog) { System.out.println(); System.out.println("Version " + VERSION); System.out.println(); System.out.println(" Path to workspace root is \"" + workspaceRoot + "\""); System.out.println("Path to compile commands file is \"" + pathToCompileCommands + "\""); System.out.println(" Xcode project will be placed in \"" + outputDir + "\""); System.out.println(); } XcodeProjectMaker maker = new XcodeProjectMaker(debugLog); maker.parseHotspotCompileCommands(pathToCompileCommands); maker.linkerFlags = List.of(linkerOptionsString.split(" ")); maker.projectMakerDataPath = pathToProjectMakerData; maker.printLogDetails(); maker.prepareFiles(workspaceRoot); maker.makeXcodeProj(outputDir, workspaceRootPathFromOutputDir); String pathToBuild = getFileParent(outputDir); maker.makeAliases(outputDir, pathToBuild); System.out.println(); System.out.println("The Xcode project for hotspot was succesfully created"); System.out.println("It can be found in '" + outputDir + "/" + HOTSPOT_PBXPROJ + "'"); System.out.println(); } // find a path to what looks like jdk private static String findRelativePathToWorkspaceRoot(String root) { String pathToWorkspaceRoot = null; String path = root; boolean found1 = false; boolean found2 = false; while (!found1 && !found2) { File folder = new File(path); File[] files = folder.listFiles(); for (File file : files) { String fileName = file.toPath().getFileName().toString(); if (fileName.equals(JDK_SCRIPT_TOKEN_1)) { found1 = true; } if (fileName.equals(JDK_SCRIPT_TOKEN_2)) { found2 = true; } if (found1 && found2) { break; } } if (!found1 && !found2) { path = Paths.get(path).getParent().toString(); if (pathToWorkspaceRoot == null) { pathToWorkspaceRoot = ".."; } else { pathToWorkspaceRoot += "/.."; } } } return pathToWorkspaceRoot; } private static String readFile(File file) { return readFile(file.toPath()); } private static String readFile(String path) { return readFile(Paths.get(path)); } private static String readFile(Path path) { try { return Files.readString(path); } catch (IOException e) { e.printStackTrace(); return null; } } private static void writeFile(File file, String string) { writeFile(file.toPath(), string); } private static void writeFile(Path path, String string) { try { Files.writeString(path, string); } catch (IOException e) { e.printStackTrace(); System.exit(EXIT4); } } private static boolean excludeFile(Path path) { return excludeFile(path.toString()); } private static boolean excludeFile(String string) { return excludeFile(string, null); } private static boolean excludeFile(String string, String exclude) { if (exclude != null) { if (contains(string, exclude)) { return true; } } for (String excludeFilesPrefix : EXCLUDE_FILES_PREFIX) { if (string.startsWith(excludeFilesPrefix)) { return true; } } for (String excludeFilesPostfix : EXCLUDE_FILES_POSTFIX) { if (string.endsWith(excludeFilesPostfix)) { return true; } } return false; } private static boolean isExcludeCompilerFlag(String string) { boolean flag = false; for (String exclude : COMPILER_FLAGS_EXCLUDE) { if (string.contains(exclude)) { flag = true; break; } } return flag; } private static boolean isCompilerFlag(String string) { boolean flag = false; for (String include : COMPILER_FLAGS_INCLUDE) { if (string.startsWith(include)) { flag = true; break; } } for (String is : COMPILER_FLAGS_IS) { if (string.equals(is)) { flag = true; break; } } if (isExcludeCompilerFlag(string)) { flag = false; } return flag; } private static String strip(String string) { return string.substring(2, string.length() - 1); } private static String strip(String string, String token) { int start = string.indexOf(token); int end = start + token.length(); return strip(string.substring(end)); } private static boolean contains(String string, String token) { return ((string.length() >= token.length()) && (string.contains(token))); } private static String getFileParent(String path) { return Paths.get(path).getParent().toString(); } private static String extractPath(String string, String from, String to) { String result = null; String[] tokens = string.split("/"); int i = 0; for (; i < tokens.length; i++) { if (tokens[i].equals(from)) { result = ""; break; } } for (; i < tokens.length; i++) { result += "/" + tokens[i]; if (tokens[i].equals(to)) { break; } } return result; } private void extractCommonCompilerFlags() { // heuristic, find average count of number of flags used by each compiled file int countFiles = 0; int countFlags = 0; for (Map.Entry> entry : this.compiledFiles.entrySet()) { countFiles++; List flags = entry.getValue(); countFlags += flags.size(); } // when finding common flags, only consider files with this many flags int flagCutoff = (countFlags / countFiles) / 2; // collect all flags for (Map.Entry> entry : this.compiledFiles.entrySet()) { List flags = entry.getValue(); if (flags.size() > flagCutoff) { this.compilerFlags.addAll(flags); } } // find flags to remove Set removeFlags = new TreeSet<>(); for (Map.Entry> entry : this.compiledFiles.entrySet()) { List flags = entry.getValue(); if (flags.size() > flagCutoff) { for (String common : this.compilerFlags) { if (!flags.contains(common)) { removeFlags.add(common); } } } } // leave only common flags for (String flag : removeFlags) { this.compilerFlags.remove(flag); } // remove common flags from each compiler file, leaving only the unique ones for (Map.Entry> entry : this.compiledFiles.entrySet()) { List flags = entry.getValue(); if (flags.size() > flagCutoff) { for (String common : this.compilerFlags) { flags.remove(common); } } } } private void extractCompilerFlags(String line) { boolean verboseCompilerTokens = false; String file = null; ArrayList flags = null; String[] commands = line.split(","); for (String command : commands) { if (contains(command, FILE_TOKEN)) { file = strip(command, FILE_TOKEN); //verbose_compiler_tokens = Contains(file, "vm_version.cpp"); } else if (contains(command, COMMAND_TOKEN)) { String tokens = strip(command, COMMAND_TOKEN); String[] arguments = tokens.split(" "); if (arguments.length >= 3) { flags = new ArrayList<>(); for (int a = 2; a < arguments.length; a++) { String argument = arguments[a]; if (isCompilerFlag(argument)) { // catch argument like -DVMTYPE=\"Minimal\" if (contains(argument, "\\\\\\\"") && argument.endsWith("\\\\\\\"")) { // TODO: more robust fix needed here argument = argument.replace("\\", ""); argument = argument.replaceFirst("\"", "~.~"); // temp token ~.~ argument = argument.replace("\"", "\\\"'"); argument = argument.replace("~.~", "'\\\""); } // argument like -DHOTSPOT_VM_DISTRO='\"Java HotSpot(TM)\"' // gets split up, so reconstruct as single string if (contains(argument, QUOTE_START_TOKEN) && !argument.endsWith(QUOTE_END_TOKEN)) { String fullArgument = argument; do { ++a; argument = arguments[a]; fullArgument = fullArgument + " " + argument; } while (!argument.endsWith(QUOTE_END_TOKEN)); argument = fullArgument; } flags.add(argument); if (verboseCompilerTokens) { System.out.println(" FOUND COMPILER FLAG: " + argument); } } else if (argument.startsWith(COMPILER_LINE_HEADER)) { this.headerPaths.add(argument.substring(2)); } else if (argument.equals(COMPILER_IFRAMEWORK)) { if (iframework == null) { ++a; this.iframework = arguments[a]; // gets the value, so skip it for the next loop } } else if (argument.equals(COMPILER_FFRAMEWORK)) { if (fframework == null) { ++a; this.fframework = arguments[a]; // gets the value, so skip it for the next loop } } } } } } if ((file != null) && (flags != null)) { this.compiledFiles.put(file, flags); } else { System.err.println(" WARNING: extractCompilerFlags returns file:" + file + ", flags:" + flags); } if (verboseCompilerTokens) { System.exit(0); } } public void parseHotspotCompileCommands(String path) { String content = readFile(path); String[] parts = content.split("\\{"); // } int found = 0; for (String line : parts) { if (!contains(line, EXCLUDE_PARSE_TOKEN_1) && !line.startsWith("[")) { extractCompilerFlags(line); found++; } } if (debugLog) { System.out.println("Found total of " + found + " files that make up the libjvm.dylib"); } extractCommonCompilerFlags(); // figure out "gensrc" folder // from: "/Users/gerard/Desktop/jdk_test/jdk10/build/macosx-x86_64-normal-server-fastdebug/hotspot/variant-server/gensrc/adfiles/ad_x86_clone.cpp" // to: "/build/macosx-x86_64-normal-server-fastdebug/hotspot/variant-server/gensrc" for (Map.Entry> entry : this.compiledFiles.entrySet()) { String file = entry.getKey(); if (file.contains("gensrc")) { this.generatedHotspotPath = extractPath(file, "build", "gensrc"); //generatedHotspotPath = "/build/macosx-x64/hotspot/variant-server/gensrc"; //generatedHotspotPath = "/build/macosx-x86_64-normal-server-fastdebug/hotspot/variant-server/gensrc"; } } } // https://docs.oracle.com/javase/tutorial/displayCode.html?code=https://docs.oracle.com/javase/tutorial/essential/io/examples/Copy.java private DiskFile getHotspotFiles(DiskFile root, String workspaceRoot, String hotspotPath) { File file = new File(workspaceRoot + "/" + hotspotPath); if (!file.exists()) { return null; } try { final Path rootDir = Paths.get(workspaceRoot + hotspotPath); Files.walkFileTree(rootDir, new HotspotFileVisitor(root, hotspotPath)); } catch (IOException ex) { System.err.println("ex: " + ex); } return root; } public void prepareFiles(String workspaceRoot) { this.rootGensrc = getHotspotFiles(this.rootGensrc, workspaceRoot, this.generatedHotspotPath); this.rootSrc = getHotspotFiles(this.rootSrc, workspaceRoot, SRC_HOTSPOT_PATH); this.rootTest = getHotspotFiles(this.rootTest, workspaceRoot, TEST_HOTSPOT_PATH); // make a copy of files from the log Set logFiles = new TreeSet<>(this.compiledFiles.keySet()); int totalMarkedFiles = 0; DiskFile[] roots = { this.rootGensrc, this.rootSrc }; for (DiskFile root : roots) { List diskFiles = root.getFiles(); for (DiskFile diskFile : diskFiles) { if (!diskFile.isDirectory()) { String logFileProcessed = null; String diskFilePath = diskFile.getFilePath(); for (String logFilePath : logFiles) { if (contains(logFilePath, diskFilePath)) { totalMarkedFiles++; logFileProcessed = logFilePath; // mark the file as needing compilation diskFile.markAsCompiled(this.compiledFiles.get(logFilePath)); // break early if found break; } } if (logFileProcessed != null) { // remove the file, so we don't have to search through it again logFiles.remove(logFileProcessed); } } } } if (this.compiledFiles.size() != totalMarkedFiles) { System.err.println("\nError: was expecting to compile " + this.compiledFiles.size() + " files, but marked " + totalMarkedFiles); for (String file : logFiles) { System.err.println("file: " + file); } System.exit(EXIT5); } if (!logFiles.isEmpty()) { System.err.println("\nError: unprocessed files left over:"); for (String logFile : logFiles) { System.err.println(" " + logFile); } System.exit(EXIT6); } } public void printLogDetails() { if (!debugLog) return; System.out.println("\nFound " + this.compilerFlags.size() + " common compiler flags:"); for (String flag : this.compilerFlags) { System.out.println(" " + flag); } System.out.println("\nList of compiled files (each one uses common compiler flags plus extra ones as specified):"); int count = 1; for (Map.Entry> entry : this.compiledFiles.entrySet()) { String file = entry.getKey(); System.out.format("%4d: %s\n", (count), file); count++; List flags = entry.getValue(); for (String flag : flags) { System.out.println(" " + flag); } } System.out.println("\nFound " + this.linkerFlags.size() + " linker flags:"); for (String flag : this.linkerFlags) { System.out.println(" " + flag); } System.out.println("\nFound " + this.headerPaths.size() + " header paths:"); for (String header : this.headerPaths) { System.out.println(" " + header); } System.out.println("\nFrameworks:"); System.out.println(" -iframework " + iframework); System.out.println(" -f " + fframework); } private String makeProjectPbxproj(String workspaceRootPathFromOutputDir, String string) { String cFlags = ""; for (String flag : this.compilerFlags) { cFlags += " \"" + flag.replace("\"", "\\\\\"") + "\",\n"; } cFlags = cFlags.substring(0, cFlags.length() - 2); string = string.replaceFirst(TEMPLATE_OTHER_CFLAGS, cFlags); String ldFlags = ""; for (String flag : this.linkerFlags) { ldFlags += " \"" + flag + "\",\n"; } ldFlags = ldFlags.substring(0, ldFlags.length() - 2); string = string.replaceFirst(TEMPLATE_OTHER_LDFLAGS, ldFlags); String headerPaths = ""; for (String header : this.headerPaths) { headerPaths += " \"" + header + "\",\n"; } headerPaths = headerPaths.substring(0, headerPaths.length() - 2); string = string.replaceFirst(TEMPLATE_USER_HEADER_SEARCH_PATHS, headerPaths); String frameworkPaths = ""; if (fframework != null) { frameworkPaths += " \"" + fframework + "\"\n"; } string = string.replaceFirst(TEMPLATE_FRAMEWORK_SEARCH_PATHS, frameworkPaths); DiskFile gensrcFile = this.rootGensrc.getChild("gensrc"); string = string.replaceFirst(TEMPLATE_GROUP_GENSRC, " " + gensrcFile.getXcodeId()); DiskFile srcFile = this.rootSrc.getChild("src"); string = string.replaceFirst(TEMPLATE_GROUP_SRC, " " + srcFile.getXcodeId()); DiskFile testFile = this.rootTest.getChild("test"); string = string.replaceFirst(TEMPLATE_GROUP_TEST, " " + testFile.getXcodeId()); String gensrcGroups = gensrcFile.generatePbxGroup(); String srcGroups = srcFile.generatePbxGroup(); String testGroups = testFile.generatePbxGroup(); string = string.replaceFirst(TEMPLATE_GROUPS, gensrcGroups + srcGroups + testGroups); String gensrcFiles = gensrcFile.generatePbxFileReference(workspaceRootPathFromOutputDir); String srcFiles = srcFile.generatePbxFileReference(workspaceRootPathFromOutputDir); String testFiles = testFile.generatePbxFileReference(workspaceRootPathFromOutputDir); string = string.replaceFirst(TEMPLATE_PBXFILEREFERENCE, gensrcFiles + srcFiles + testFiles); String gensrcCompiled = gensrcFile.generatePbxBuildFile(); String compiled = srcFile.generatePbxBuildFile(); string = string.replaceFirst(TEMPLATE_PBXBUILDFILE, gensrcCompiled + compiled); String gensrcBuilt = gensrcFile.generatePbxSourcesBuildPhase(); String built = srcFile.generatePbxSourcesBuildPhase(); string = string.replaceFirst(TEMPLATE_PBXSOURCESSBUILDPHASE, gensrcBuilt + built); return string; } private String makeTemplateXcscheme(String outputDir, String string) { string = string.replaceAll(TEMPLATE_JDK_PATH, outputDir); return string; } public void makeXcodeProj(String outputDir, String workspaceRootPathFromOutputDir) { /* jvm.xcodeproj <-- folder project.pbxproj <-- file xcshareddata <-- folder xcschemes <-- folder jvm.xcscheme <-- file xcdebugger <-- folder Breakpoints_v2.xcbkptlist <-- file */ File xcodeDir = new File(outputDir); File jvmXcodeprojDir = new File(xcodeDir, HOTSPOT_PBXPROJ); File projectPbxprojFile = new File(jvmXcodeprojDir, PBXPROJ); File xcshareddataDir = new File(jvmXcodeprojDir, XCSAHAREDDATA); File xcschemesDir = new File(xcshareddataDir, XCSCHEMES); File jvmXcschemeFile = new File(xcschemesDir, JVM_XCSCHEME); File j2DemoXcschemeFile = new File(xcschemesDir, J2D_XCSCHEME); File xcdebuggerDir = new File(xcshareddataDir, XCDEBUGGER); File jBreakpointsV2XcbkptlistFile = new File(xcdebuggerDir, XCBKPTLIST); if (xcodeDir.exists()) { xcodeDir.delete(); } jvmXcodeprojDir.mkdirs(); xcshareddataDir.mkdirs(); xcschemesDir.mkdirs(); xcdebuggerDir.mkdirs(); File dataDir = new File(projectMakerDataPath); File templateProjectPbxprojFile = new File(dataDir, TEMPLATE_PBXPROJ); File templateJvmXcschemeFile = new File(dataDir, TEMPLATE_JVM_XCSCHEME); File templateJ2DemoXcschemeFile = new File(dataDir, TEMPLATE_J2D_XCSCHEME); File templateJBreakpointsV2XcbkptlistFile = new File(dataDir, TEMPLATE_XCBKPTLIST); String projectPbxprojString = readFile(templateProjectPbxprojFile); String jvmXcschemeString = readFile(templateJvmXcschemeFile); String j2DemoXcschemeString = readFile(templateJ2DemoXcschemeFile); String jBreakpointsV2XcbkptlistString = readFile(templateJBreakpointsV2XcbkptlistFile); writeFile(projectPbxprojFile, makeProjectPbxproj(workspaceRootPathFromOutputDir, projectPbxprojString)); writeFile(jvmXcschemeFile, makeTemplateXcscheme(outputDir, jvmXcschemeString)); writeFile(j2DemoXcschemeFile, makeTemplateXcscheme(outputDir, j2DemoXcschemeString)); writeFile(jBreakpointsV2XcbkptlistFile, jBreakpointsV2XcbkptlistString); } public void makeAliases(String outputDir, String pathToBuild) { File xcodeDir = new File(outputDir); File jdkOldSh = new File(xcodeDir, ALIAS_JAVA_OLD); File jdkNewSh = new File(xcodeDir, ALIAS_JAVA_NEW); writeFile(jdkOldSh, "#!/bin/bash\n" + pathToBuild + JDK_BIN_JAVA + " $@"); writeFile(jdkNewSh, "#!/bin/bash\n" + outputDir + "/build" + JDK_BIN_JAVA + " $@"); try { Set permissions = new HashSet<>(); permissions.add(PosixFilePermission.OWNER_READ); permissions.add(PosixFilePermission.OWNER_WRITE); permissions.add(PosixFilePermission.OWNER_EXECUTE); permissions.add(PosixFilePermission.GROUP_READ); permissions.add(PosixFilePermission.OTHERS_READ); Files.setPosixFilePermissions(jdkOldSh.toPath(), permissions); Files.setPosixFilePermissions(jdkNewSh.toPath(), permissions); } catch (IOException ex) { System.err.println("Warning: unable to change file permissions"); System.err.println(ex); } } private static class HotspotFileVisitor implements FileVisitor { private final DiskFile root; private final String hotspotPath; public HotspotFileVisitor(DiskFile root, String hotspotPath) { this.root = root; this.hotspotPath = hotspotPath; } @Override public FileVisitResult preVisitDirectory(Path path, BasicFileAttributes attrs) { if (excludeFile(path)) { return FileVisitResult.SKIP_SUBTREE; } else { // consider folders based on their names Path file = path.getFileName(); if (!excludeFile(file)) { root.addDirectory(path, hotspotPath); return FileVisitResult.CONTINUE; } else { // skip folders with names beginning with ".", etc return FileVisitResult.SKIP_SUBTREE; } } } @Override public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) { Path file = path.getFileName(); if (!excludeFile(file)) { //System.err.println(path.toString()); root.addFile(path, hotspotPath); } return FileVisitResult.CONTINUE; } @Override public FileVisitResult postVisitDirectory(Path path, IOException exc) { return FileVisitResult.CONTINUE; } @Override public FileVisitResult visitFileFailed(Path path, IOException exc) { if (exc instanceof FileSystemLoopException) { System.err.println("cycle detected: " + path); } else { System.err.format("Unable to process: %s: %s\n", path, exc); } return FileVisitResult.CONTINUE; } } }