Main.java revision adf4543e57086070c95b3ef439dbb6679b0bd562
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.mkstubs; 18 19import org.objectweb.asm.ClassReader; 20 21import java.io.BufferedReader; 22import java.io.File; 23import java.io.FileReader; 24import java.io.IOException; 25import java.util.Map; 26 27 28/** 29 * Main entry point of the MkStubs app. 30 * <p/> 31 * For workflow details, see {@link #process(Params)}. 32 */ 33public class Main { 34 35 /** 36 * A struct-like class to hold the various input values (e.g. command-line args) 37 */ 38 static class Params { 39 private String mInputJarPath; 40 private String mOutputJarPath; 41 private Filter mFilter; 42 43 public Params(String inputJarPath, String outputJarPath) { 44 mInputJarPath = inputJarPath; 45 mOutputJarPath = outputJarPath; 46 mFilter = new Filter(); 47 } 48 49 /** Returns the name of the input jar, where to read classes from. */ 50 public String getInputJarPath() { 51 return mInputJarPath; 52 } 53 54 /** Returns the name of the output jar, where to write classes to. */ 55 public String getOutputJarPath() { 56 return mOutputJarPath; 57 } 58 59 /** Returns the current instance of the filter, the include/exclude patterns. */ 60 public Filter getFilter() { 61 return mFilter; 62 } 63 } 64 65 /** 66 * Main entry point. Processes arguments then performs the "real" work. 67 */ 68 public static void main(String[] args) { 69 70 Main m = new Main(); 71 try { 72 Params p = m.processArgs(args); 73 m.process(p); 74 } catch (IOException e) { 75 e.printStackTrace(); 76 } 77 } 78 79 /** 80 * Grabs command-line arguments. 81 * The expected arguments are: 82 * <ul> 83 * <li> The filename of the input Jar. 84 * <li> The filename of the output Jar. 85 * <li> One or more include/exclude patterns or files containing these patterns. 86 * See {@link #addString(Params, String)} for syntax. 87 * </ul> 88 * @throws IOException on failure to read a pattern file. 89 */ 90 private Params processArgs(String[] args) throws IOException { 91 92 if (args.length < 2) { 93 usage(); 94 } 95 96 Params p = new Params(args[0], args[1]); 97 98 for (int i = 2; i < args.length; i++) { 99 addString(p, args[i]); 100 } 101 102 return p; 103 } 104 105 /** 106 * Adds one pattern string to the current filter. 107 * The syntax must be: 108 * <ul> 109 * <li> +full_include or +prefix_include* 110 * <li> -full_exclude or -prefix_exclude* 111 * <li> @filename 112 * </ul> 113 * The input string is trimmed so any space around the first letter (-/+/@) or 114 * at the end is removed. Empty strings are ignored. 115 * 116 * @param p The params which filters to edit. 117 * @param s The string to examine. 118 * @throws IOException 119 */ 120 private void addString(Params p, String s) throws IOException { 121 if (s == null) { 122 return; 123 } 124 125 s = s.trim(); 126 127 if (s.length() < 2) { 128 return; 129 } 130 131 char mode = s.charAt(0); 132 s = s.substring(1).trim(); 133 134 if (mode == '@') { 135 addStringsFromFile(p, s); 136 137 } else if (mode == '-') { 138 s = s.replace('.', '/'); // transform FQCN into ASM internal name 139 if (s.endsWith("*")) { 140 p.getFilter().getExcludePrefix().add(s.substring(0, s.length() - 1)); 141 } else { 142 p.getFilter().getExcludeFull().add(s); 143 } 144 145 } else if (mode == '+') { 146 s = s.replace('.', '/'); // transform FQCN into ASM internal name 147 if (s.endsWith("*")) { 148 p.getFilter().getIncludePrefix().add(s.substring(0, s.length() - 1)); 149 } else { 150 p.getFilter().getIncludeFull().add(s); 151 } 152 } 153 } 154 155 /** 156 * Adds all the filter strings from the given file. 157 * 158 * @param p The params which filter to edit. 159 * @param osFilePath The OS path to the file containing the patterns. 160 * @throws IOException 161 * 162 * @see #addString(Params, String) 163 */ 164 private void addStringsFromFile(Params p, String osFilePath) 165 throws IOException { 166 BufferedReader br = null; 167 try { 168 br = new BufferedReader(new FileReader(osFilePath)); 169 String line; 170 while ((line = br.readLine()) != null) { 171 addString(p, line); 172 } 173 } finally { 174 br.close(); 175 } 176 } 177 178 /** 179 * Prints some help to stdout. 180 */ 181 private void usage() { 182 System.out.println("Usage: mkstub input.jar output.jar [excluded-class @excluded-classes-file ...]"); 183 184 System.out.println("Include syntax:\n" + 185 "+com.package.* : whole package, with glob\n" + 186 "+com.package.Class[$Inner] or ...Class*: whole classes with optional glob\n" + 187 "Inclusion is not supported at method/field level.\n\n"); 188 189 System.out.println("Exclude syntax:\n" + 190 "-com.package.* : whole package, with glob\n" + 191 "-com.package.Class[$Inner] or ...Class*: whole classes with optional glob\n" + 192 "-com.package.Class#method: whole method or field\n" + 193 "-com.package.Class#method(IILjava/lang/String;)V: specific method with signature.\n\n"); 194 System.exit(1); 195 } 196 197 /** 198 * Performs the main workflow of this app: 199 * <ul> 200 * <li> Read the input Jar to get all its classes. 201 * <li> Filter out all classes that should not be included or that should be excluded. 202 * <li> Goes thru the classes, filters methods/fields and generate their source 203 * in a directory called "<outpath_jar_path>_sources" 204 * <li> Does the same filtering on the classes but this time generates the real stubbed 205 * output jar. 206 * </ul> 207 */ 208 private void process(Params p) throws IOException { 209 AsmAnalyzer aa = new AsmAnalyzer(); 210 Map<String, ClassReader> classes = aa.parseInputJar(p.getInputJarPath()); 211 212 System.out.println(String.format("Classes loaded: %d", classes.size())); 213 214 aa.filter(classes, p.getFilter()); 215 216 System.out.println(String.format("Classes filtered: %d", classes.size())); 217 218 // dump as Java source files, mostly for debugging 219 SourceGenerator src_gen = new SourceGenerator(); 220 File dst_src_dir = new File(p.getOutputJarPath() + "_sources"); 221 dst_src_dir.mkdir(); 222 src_gen.generateSource(dst_src_dir, classes, p.getFilter()); 223 224 // dump the stubbed jar 225 StubGenerator stub_gen = new StubGenerator(); 226 File dst_jar = new File(p.getOutputJarPath()); 227 stub_gen.generateStubbedJar(dst_jar, classes, p.getFilter()); 228 } 229} 230