11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 /* 27 * Read an input file which is output from a java -verbose run, 28 * combine with an argument list of files and directories, and 29 * write a list of items to be included in a jar file. 30 */ 31 32 package build.tools.jarreorder; 33 34 import java.io.BufferedReader; 35 import java.io.File; 36 import java.io.FileNotFoundException; 37 import java.io.FileReader; 38 import java.io.IOException; 39 import java.util.Arrays; 40 import java.util.HashMap; 41 import java.util.HashSet; 42 import java.util.Vector; 43 import java.io.PrintStream; 44 import java.io.FileOutputStream; 45 46 public class JarReorder { 47 48 // To deal with output 49 private static PrintStream out; 50 51 private final static boolean useTopDir = false; 52 53 private static void usage() { 54 String help; 55 help = 56 "Usage: jar JarReorder [-o <outputfile>] <order_list> <exclude_list> <file> ...\n" 57 + " order_list is a file containing names of files to load\n" 58 + " in order at the end of a jar file.\n" 59 + " exclude_list is a file containing names of files/directories\n" 60 + " NOT to be included in a jar file.\n"; 61 if (useTopDir) 62 help += 63 " top_dir is the top of the directory structure to be searched;\n" 64 + " the contents of the lists and remaining arguments are\n" 65 + " relative to this.\n"; 66 help += 67 "\n" 68 + "The order_list or exclude_list may be replaced by a \"_\" if no\n" 69 + "data is to be provided.\n" 70 + "\n" 71 + " The remaining arguments are files or directories to be included\n" 72 + " in a jar file, from which will be excluded thse entries which\n" 73 + " appear in the exclude list.\n"; 74 System.err.println(help); 75 System.exit(1); 76 } 77 78 79 /* 80 * Create a list of files to be included in a jar file, such that the 81 * some the files will appear in a specific order, and allowing certain 82 * files and directories to be excluded. 83 * 84 * Command line arguments are 85 * - optional -o outputfile 86 * - name of a file containing a list of files to be included in a jar file. 87 * - name of a file containing a list of files (or directories) to be 88 * excluded from the jar file. 89 * - names of files or directories to be searched for files to include 90 * in the jar file. 91 */ 92 public static void main(String[] args) { 93 94 HashMap filesExcluded = new HashMap(); 95 Vector filesIncluded = new Vector(); 96 int fileArgs; 97 String topDirName = ""; 98 int arglen = args.length; 99 int argpos = 0; 100 101 // Look for "-o outputfilename" option 102 if ( arglen > 0 ) { 103 if ( arglen >= 2 && args[0].equals("-o") ) { 104 try { 105 out = new PrintStream(new FileOutputStream(args[1])); 106 } catch ( FileNotFoundException e ) { 107 System.err.println("Error: " + e.getMessage()); 108 e.printStackTrace(System.err); 109 System.exit(1); 110 } 111 argpos += 2; 112 arglen -= 2; 113 } else { 114 System.err.println("Error: Illegal arg count"); 115 System.exit(1); 116 } 117 } else { 118 out = System.out; 119 } 120 121 fileArgs = useTopDir ? 3 : 2; 122 123 if (arglen <= fileArgs) { 124 usage(); 125 } 126 127 // Read the ordered list of files to be included in rt.jar. 128 // Read the list of files/directories to be excluded from rt.jar. 129 130 Vector orderList = readListFromFile(args[argpos], true); 131 Vector excludeList = readListFromFile(args[argpos+1], false); 132 if (useTopDir) { 133 topDirName = args[argpos+2]; 134 if (!topDirName.endsWith(File.separator)) 135 topDirName = topDirName + File.separator; 136 } 137 138 // Copy these lists into filesExcluded so that these files will be excluded 139 // from the file list. (The orderList files will be appended later.) 140 141 for (int i = 0; i < orderList.size(); ++i) { 142 String s = (String) orderList.elementAt(i); 143 filesExcluded.put(s, s); 144 } 145 for (int i = 0; i < excludeList.size(); ++i) { 146 String s = (String) excludeList.elementAt(i); 147 filesExcluded.put(s, s); 148 } 149 150 // The remaining arguments are names of files/directories to be included 151 // in the jar file. 152 153 String[] files = new String[arglen - fileArgs]; 154 for (int i = fileArgs; i < arglen; ++i) { 155 files[i-fileArgs] = args[argpos+i]; 156 filesExcluded.put(args[argpos+i], args[argpos+i]); 157 } 158 159 // Expand file/directory list to file list excluding those 160 // read from the class list. 161 162 if (useTopDir) 163 expand(new File(topDirName), files, filesIncluded, filesExcluded, topDirName); 164 else 165 expand(null, files, filesIncluded, filesExcluded, null); 166 167 // Now add the ordered list to the end of the expanded list. 168 // Add in REVERSE ORDER, so that the first element is closest to 169 // the end (and the index). 170 171 HashSet excludeSet = new HashSet(excludeList); 172 for (int i = orderList.size() - 1; i >= 0; --i) { 173 String s = (String) orderList.elementAt(i); 174 if (excludeSet.contains(s)) { 175 System.err.println("Included file " + s + " is also excluded, skipping."); 176 continue; 177 } 178 if (new File(topDirName + s).exists()) 179 filesIncluded.addElement(s); 180 else 181 System.err.println("Included file "+s+" missing, skipping."); 182 } 183 184 // Print results. 185 186 for (int i = 0; i < filesIncluded.size(); ++i) { 187 if (useTopDir) { 188 out.print("-C "); 189 out.print(topDirName); 190 out.print(" "); 191 } 192 out.println((String)filesIncluded.elementAt(i)); 193 } 194 195 out.flush(); 196 out.close(); 197 } 198 199 200 /* 201 * Read a file containing a list of files into a Vector. 202 */ 203 private static Vector readListFromFile(String fileName, 204 boolean addClassSuffix) { 205 206 BufferedReader br = null; 207 Vector v = new Vector(2000); 208 209 if ("-".equals(fileName)) 210 return v; 211 212 try { 213 br = new BufferedReader(new FileReader(fileName)); 214 215 // Read the input file a line at a time. # in column 1 is a comment. 216 217 while (true) { 218 String line = null; 219 line = br.readLine(); 220 221 if (line == null) 222 break; 223 224 if (line.length() == 0 || 225 line.charAt(0) == '#') 226 continue; 227 228 // Convert forward or back slashes to the type expected for 229 // the current platform. 230 231 if (File.separatorChar == '/') 232 line = line.replace('\\', '/'); 233 else 234 line = line.replace('/', '\\'); 235 236 line = line.trim(); 237 if (addClassSuffix) { 238 if (!line.endsWith(".class")) { 239 line = line + ".class"; 240 } 241 } 242 v.addElement(line); 243 } 244 br.close(); 245 } catch (FileNotFoundException e) { 246 System.err.println("Can't find file \"" + fileName + "\"."); 247 System.exit(1); 248 } catch (IOException e) { 249 e.printStackTrace(); 250 System.exit(2); 251 } 252 return v; 253 } 254 255 256 /* 257 * Expands list of files to process into full list of all files that 258 * can be found by recursively descending directories. 259 */ 260 private static void expand(File dir, String[] files, 261 Vector includedFiles, HashMap excludedFiles, 262 String topDirName) { 263 if (files == null) { 264 return; 265 } 266 for (int i = 0; i < files.length; i++) { 267 File f = (dir == null) ? new File(files[i]) 268 : new File(dir, files[i]); 269 if (f.isFile()) { 270 String filePath = f.getPath(); 271 272 if (useTopDir) { 273 if (filePath.startsWith(topDirName)) 274 filePath = filePath.substring(topDirName.length()); 275 } 276 277 if (filePath.length() >= 2 && 278 filePath.charAt(0) == '.' && 279 filePath.charAt(1) == File.separatorChar) 280 filePath = filePath.substring(2); 281 282 if (!excludedFiles.containsKey(filePath)) { 283 excludedFiles.put(filePath, filePath); 284 includedFiles.addElement(filePath); 285 } 286 } else if (f.isDirectory()) { 287 String dirPath = f.getPath(); 288 dirPath = (dirPath.endsWith(File.separator)) ? dirPath : 289 (dirPath + File.separator); 290 291 if (useTopDir) { 292 if (dirPath.startsWith(topDirName)) 293 dirPath = dirPath.substring(topDirName.length()); 294 } 295 296 if (dirPath.length() >= 2 && 297 dirPath.charAt(0) == '.' && 298 dirPath.charAt(1) == File.separatorChar) 299 dirPath = dirPath.substring(2); 300 301 if (!excludedFiles.containsKey(dirPath)) { 302 303 // Sort the directory list so that entries in the jar file 304 // are in a repeatable order. The order itself is not particularly 305 // important. [File.list() is unpredictable.] 306 307 String[] dirList = f.list(); 308 Arrays.sort(dirList); 309 expand(f, dirList, includedFiles, excludedFiles, topDirName); 310 } 311 } else { 312 System.err.println("Error accessing: " + f.getPath()); 313 } 314 } 315 } 316 } | 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 /* 27 * Read an input file which is output from a java -verbose run, 28 * combine with an argument list of files and directories, and 29 * write a list of items to be included in a jar file. 30 */ 31 package build.tools.jarreorder; 32 33 import java.io.BufferedReader; 34 import java.io.File; 35 import java.io.FileNotFoundException; 36 import java.io.FileReader; 37 import java.io.IOException; 38 import java.util.Collections; 39 import java.util.HashSet; 40 import java.io.PrintStream; 41 import java.io.FileOutputStream; 42 import java.util.ArrayList; 43 import java.util.List; 44 import java.util.Set; 45 46 public class JarReorder { 47 48 // To deal with output 49 private PrintStream out; 50 51 private void usage() { 52 String help; 53 help = 54 "Usage: jar JarReorder [-o <outputfile>] <order_list> <exclude_list> <file> ...\n" 55 + " order_list is a file containing names of files to load\n" 56 + " in order at the end of a jar file unless\n" 57 + " excluded in the exclude list.\n" 58 + " exclude_list is a file containing names of files/directories\n" 59 + " NOT to be included in a jar file.\n" 60 + "\n" 61 + "The order_list or exclude_list may be replaced by a \"-\" if no\n" 62 + "data is to be provided.\n" 63 + "\n" 64 + " The remaining arguments are files or directories to be included\n" 65 + " in a jar file, from which will be excluded those entries which\n" 66 + " appear in the exclude list.\n"; 67 System.err.println(help); 68 } 69 70 71 /* 72 * Create the file list to be included in a jar file, such that the 73 * list will appear in a specific order, and allowing certain 74 * files and directories to be excluded. 75 * 76 * Command path arguments are 77 * - optional -o outputfile 78 * - name of a file containing a set of files to be included in a jar file. 79 * - name of a file containing a set of files (or directories) to be 80 * excluded from the jar file. 81 * - names of files or directories to be searched for files to include 82 * in the jar file. 83 */ 84 public static void main(String[] args) { 85 JarReorder jr = new JarReorder(); 86 jr.run(args); 87 } 88 89 private void run(String args[]) { 90 91 int arglen = args.length; 92 int argpos = 0; 93 94 // Look for "-o outputfilename" option 95 if (arglen > 0) { 96 if (arglen >= 2 && args[0].equals("-o")) { 97 try { 98 out = new PrintStream(new FileOutputStream(args[1])); 99 } catch (FileNotFoundException e) { 100 System.err.println("Error: " + e.getMessage()); 101 e.printStackTrace(System.err); 102 System.exit(1); 103 } 104 argpos += 2; 105 arglen -= 2; 106 } else { 107 System.err.println("Error: Illegal arg count"); 108 System.exit(1); 109 } 110 } else { 111 out = System.out; 112 } 113 114 // Should be 2 or more args left 115 if (arglen <= 2) { 116 usage(); 117 System.exit(1); 118 } 119 120 // Read the ordered set of files to be included in rt.jar. 121 // Read the set of files/directories to be excluded from rt.jar. 122 String classListFile = args[argpos]; 123 String excludeListFile = args[argpos + 1]; 124 argpos += 2; 125 arglen -= 2; 126 127 // Create 2 lists and a set of processed files 128 List<String> orderList = readListFromFile(classListFile, true); 129 List<String> excludeList = readListFromFile(excludeListFile, false); 130 Set<String> processed = new HashSet<String>(); 131 132 // Create set of all files and directories excluded, then expand 133 // that list completely 134 Set<String> excludeSet = new HashSet<String>(excludeList); 135 Set<String> allFilesExcluded = expand(null, excludeSet, processed); 136 137 // Indicate all these have been processed, orderList too, kept to end. 138 processed.addAll(orderList); 139 140 // The remaining arguments are names of files/directories to be included 141 // in the jar file. 142 Set<String> inputSet = new HashSet<String>(); 143 for (int i = 0; i < arglen; ++i) { 144 String name = args[argpos + i]; 145 name = cleanPath(new File(name)); 146 if ( name != null && name.length() > 0 && !inputSet.contains(name) ) { 147 inputSet.add(name); 148 } 149 } 150 151 // Expand file/directory input so we get a complete set (except ordered) 152 // Should be everything not excluded and not in order list. 153 Set<String> allFilesIncluded = expand(null, inputSet, processed); 154 155 // Create simple sorted list so we can add ordered items at end. 156 List<String> allFiles = new ArrayList<String>(allFilesIncluded); 157 Collections.sort(allFiles); 158 159 // Now add the ordered set to the end of the list. 160 // Add in REVERSE ORDER, so that the first element is closest to 161 // the end (and the index). 162 for (int i = orderList.size() - 1; i >= 0; --i) { 163 String s = orderList.get(i); 164 if (allFilesExcluded.contains(s)) { 165 System.err.println("Included order file " + s 166 + " is also excluded, skipping."); 167 } else if (new File(s).exists()) { 168 allFiles.add(s); 169 } else { 170 System.err.println("Included order file " + s 171 + " missing, skipping."); 172 } 173 } 174 175 // Print final results. 176 for (String str : allFiles) { 177 out.println(str); 178 } 179 out.flush(); 180 out.close(); 181 } 182 183 /* 184 * Read a file containing a list of files and directories into a List. 185 */ 186 private List<String> readListFromFile(String fileName, 187 boolean addClassSuffix) { 188 189 BufferedReader br = null; 190 List<String> list = new ArrayList<String>(); 191 // If you see "-" for the name, just assume nothing was provided. 192 if ("-".equals(fileName)) { 193 return list; 194 } 195 try { 196 br = new BufferedReader(new FileReader(fileName)); 197 // Read the input file a path at a time. # in column 1 is a comment. 198 while (true) { 199 String path = br.readLine(); 200 if (path == null) { 201 break; 202 } 203 // Look for comments 204 path = path.trim(); 205 if (path.length() == 0 206 || path.charAt(0) == '#') { 207 continue; 208 } 209 // Add trailing .class if necessary 210 if (addClassSuffix && !path.endsWith(".class")) { 211 path = path + ".class"; 212 } 213 // Normalize the path 214 path = cleanPath(new File(path)); 215 // Add to list 216 if (path != null && path.length() > 0 && !list.contains(path)) { 217 list.add(path); 218 } 219 } 220 br.close(); 221 } catch (FileNotFoundException e) { 222 System.err.println("Can't find file \"" + fileName + "\"."); 223 System.exit(1); 224 } catch (IOException e) { 225 e.printStackTrace(); 226 System.exit(2); 227 } 228 return list; 229 } 230 231 /* 232 * Expands inputSet (files or dirs) into full set of all files that 233 * can be found by recursively descending directories. 234 * @param dir root directory 235 * @param inputSet set of files or dirs to look into 236 * @param processed files or dirs already processed 237 * @return set of files 238 */ 239 private Set<String> expand(File dir, 240 Set<String> inputSet, 241 Set<String> processed) { 242 Set<String> includedFiles = new HashSet<String>(); 243 if (inputSet.isEmpty()) { 244 return includedFiles; 245 } 246 for (String name : inputSet) { 247 // Depending on start location 248 File f = (dir == null) ? new File(name) 249 : new File(dir, name); 250 // Normalized path to use 251 String path = cleanPath(f); 252 if (path != null && path.length() > 0 253 && !processed.contains(path)) { 254 if (f.isFile()) { 255 // Not in the excludeList, add it to both lists 256 includedFiles.add(path); 257 processed.add(path); 258 } else if (f.isDirectory()) { 259 // Add the directory entries 260 String[] dirList = f.list(); 261 Set<String> dirInputSet = new HashSet<String>(); 262 for (String x : dirList) { 263 dirInputSet.add(x); 264 } 265 // Process all entries in this directory 266 Set<String> subList = expand(f, dirInputSet, processed); 267 includedFiles.addAll(subList); 268 processed.add(path); 269 } 270 } 271 } 272 return includedFiles; 273 } 274 275 private String cleanPath(File f) { 276 String path = f.getPath(); 277 if (f.isFile()) { 278 path = cleanFilePath(path); 279 } else if (f.isDirectory()) { 280 path = cleanDirPath(path); 281 } else { 282 System.err.println("WARNING: Path does not exist as file or directory: " + path); 283 path = null; 284 } 285 return path; 286 } 287 288 private String cleanFilePath(String path) { 289 // Remove leading and trailing whitespace 290 path = path.trim(); 291 // Make all / and \ chars one 292 if (File.separatorChar == '/') { 293 path = path.replace('\\', '/'); 294 } else { 295 path = path.replace('/', '\\'); 296 } 297 // Remove leading ./ 298 if (path.startsWith("." + File.separator)) { 299 path = path.substring(2); 300 } 301 return path; 302 } 303 304 private String cleanDirPath(String path) { 305 path = cleanFilePath(path); 306 // Make sure it ends with a file separator 307 if (!path.endsWith(File.separator)) { 308 path = path + File.separator; 309 } 310 return path; 311 } 312 313 } |