diff options
author | Raphael Moll <ralf@android.com> | 2012-06-13 00:22:47 -0700 |
---|---|---|
committer | Raphael Moll <ralf@android.com> | 2012-06-13 15:44:54 -0700 |
commit | 4306096a4351030cab4ea413b5e87460b60a84bf (patch) | |
tree | 359ee2d75645d0d641f052fde11469628194add8 /tools | |
parent | f01f6b6d379c911a2c1e350bc758cea26bfe6d9d (diff) | |
download | frameworks_base-4306096a4351030cab4ea413b5e87460b60a84bf.zip frameworks_base-4306096a4351030cab4ea413b5e87460b60a84bf.tar.gz frameworks_base-4306096a4351030cab4ea413b5e87460b60a84bf.tar.bz2 |
Layoutlib Create: Find dependencies.
Usage: layoutlib_create --list-deps /path/to/layoutlib.jar
Prints:
- all classes found in the Jar and the types they use (the dependencies).
- all the dependencies missing from the Jar and what uses them.
Change-Id: I8b2674df127e1494feed7a653282e88e4d2f5494
Diffstat (limited to 'tools')
4 files changed, 790 insertions, 52 deletions
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java index b197ea7..1549cf4 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java @@ -45,7 +45,7 @@ import java.util.zip.ZipFile; public class AsmAnalyzer { // Note: a bunch of stuff has package-level access for unit tests. Consider it private. - + /** Output logger. */ private final Log mLog; /** The input source JAR to parse. */ @@ -59,11 +59,11 @@ public class AsmAnalyzer { /** * Creates a new analyzer. - * + * * @param log The log output. * @param osJarPath The input source JARs to parse. * @param gen The generator to fill with the class list and dependency list. - * @param deriveFrom Keep all classes that derive from these one (these included). + * @param deriveFrom Keep all classes that derive from these one (these included). * @param includeGlobs Glob patterns of classes to keep, e.g. "com.foo.*" * ("*" does not matches dots whilst "**" does, "." and "$" are interpreted as-is) */ @@ -81,16 +81,13 @@ public class AsmAnalyzer { * Fills the generator with classes & dependencies found. */ public void analyze() throws IOException, LogAbortException { - - AsmAnalyzer visitor = this; - Map<String, ClassReader> zipClasses = parseZip(mOsSourceJar); mLog.info("Found %d classes in input JAR%s.", zipClasses.size(), mOsSourceJar.size() > 1 ? "s" : ""); - + Map<String, ClassReader> found = findIncludes(zipClasses); Map<String, ClassReader> deps = findDeps(zipClasses, found); - + if (mGen != null) { mGen.setKeep(found); mGen.setDeps(deps); @@ -117,10 +114,10 @@ public class AsmAnalyzer { } } } - + return classes; } - + /** * Utility that returns the fully qualified binary class name for a ClassReader. * E.g. it returns something like android.view.View. @@ -132,7 +129,7 @@ public class AsmAnalyzer { return classReader.getClassName().replace('/', '.'); } } - + /** * Utility that returns the fully qualified binary class name from a path-like FQCN. * E.g. it returns android.view.View from android/view/View. @@ -144,7 +141,7 @@ public class AsmAnalyzer { return className.replace('/', '.'); } } - + /** * Process the "includes" arrays. * <p/> @@ -162,11 +159,11 @@ public class AsmAnalyzer { for (String s : mDeriveFrom) { findClassesDerivingFrom(s, zipClasses, found); } - + return found; } - + /** * Uses ASM to find the class reader for the given FQCN class name. * If found, insert it in the in_out_found map. @@ -215,7 +212,7 @@ public class AsmAnalyzer { globPattern += "$"; Pattern regexp = Pattern.compile(globPattern); - + for (Entry<String, ClassReader> entry : zipClasses.entrySet()) { String class_name = entry.getKey(); if (regexp.matcher(class_name).matches()) { @@ -229,10 +226,9 @@ public class AsmAnalyzer { * determine if they are derived from the given FQCN super class name. * Inserts the super class and all the class objects found in the map. */ - void findClassesDerivingFrom(String super_name, Map<String, ClassReader> zipClasses, - Map<String, ClassReader> inOutFound) throws LogAbortException { - ClassReader super_clazz = findClass(super_name, zipClasses, inOutFound); - + void findClassesDerivingFrom(String super_name, + Map<String, ClassReader> zipClasses, + Map<String, ClassReader> inOutFound) { for (Entry<String, ClassReader> entry : zipClasses.entrySet()) { String className = entry.getKey(); if (super_name.equals(className)) { @@ -284,7 +280,7 @@ public class AsmAnalyzer { for (ClassReader cr : inOutKeepClasses.values()) { cr.accept(visitor, 0 /* flags */); } - + while (new_deps.size() > 0 || new_keep.size() > 0) { deps.putAll(new_deps); inOutKeepClasses.putAll(new_keep); @@ -308,12 +304,12 @@ public class AsmAnalyzer { return deps; } - + // ---------------------------------- - + /** - * Visitor to collect all the type dependencies from a class. + * Visitor to collect all the type dependencies from a class. */ public class DependencyVisitor implements ClassVisitor, FieldVisitor, MethodVisitor, SignatureVisitor, AnnotationVisitor { @@ -333,7 +329,7 @@ public class AsmAnalyzer { * Creates a new visitor that will find all the dependencies for the visited class. * Types which are already in the zipClasses, keepClasses or inDeps are not marked. * New dependencies are marked in outDeps. - * + * * @param zipClasses All classes found in the source JAR. * @param inKeep Classes from which dependencies are to be found. * @param inDeps Dependencies already known. @@ -350,7 +346,7 @@ public class AsmAnalyzer { mInDeps = inDeps; mOutDeps = outDeps; } - + /** * Considers the given class name as a dependency. * If it does, add to the mOutDeps map. @@ -361,7 +357,7 @@ public class AsmAnalyzer { } className = internalToBinaryClassName(className); - + // exclude classes that have already been found if (mInKeep.containsKey(className) || mOutKeep.containsKey(className) || @@ -384,7 +380,7 @@ public class AsmAnalyzer { } catch (ClassNotFoundException e) { // ignore } - + // accept this class: // - android classes are added to dependencies // - non-android classes are added to the list of classes to keep as-is (they don't need @@ -395,7 +391,7 @@ public class AsmAnalyzer { mOutKeep.put(className, cr); } } - + /** * Considers this array of names using considerName(). */ @@ -450,7 +446,7 @@ public class AsmAnalyzer { } } - + // --------------------------------------------------- // --- ClassVisitor, FieldVisitor // --------------------------------------------------- @@ -460,7 +456,7 @@ public class AsmAnalyzer { String signature, String superName, String[] interfaces) { // signature is the signature of this class. May be null if the class is not a generic // one, and does not extend or implement generic classes or interfaces. - + if (signature != null) { considerSignature(signature); } @@ -468,7 +464,7 @@ public class AsmAnalyzer { // superName is the internal of name of the super class (see getInternalName). // For interfaces, the super class is Object. May be null but only for the Object class. considerName(superName); - + // interfaces is the internal names of the class's interfaces (see getInternalName). // May be null. considerNames(interfaces); @@ -513,7 +509,7 @@ public class AsmAnalyzer { // signature is the method's signature. May be null if the method parameters, return // type and exceptions do not use generic types. considerSignature(signature); - + return this; // returns this to visit the method } @@ -525,7 +521,7 @@ public class AsmAnalyzer { // pass } - + // --------------------------------------------------- // --- MethodVisitor // --------------------------------------------------- @@ -601,7 +597,7 @@ public class AsmAnalyzer { // instruction that invokes a method public void visitMethodInsn(int opcode, String owner, String name, String desc) { - + // owner is the internal name of the method's owner class considerName(owner); // desc is the method's descriptor (see Type). @@ -610,7 +606,7 @@ public class AsmAnalyzer { // instruction multianewarray, whatever that is public void visitMultiANewArrayInsn(String desc, int dims) { - + // desc an array type descriptor. considerDesc(desc); } @@ -624,7 +620,7 @@ public class AsmAnalyzer { public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) { // pass -- table switch instruction - + } public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { @@ -641,10 +637,10 @@ public class AsmAnalyzer { } public void visitVarInsn(int opcode, int var) { - // pass -- local variable instruction + // pass -- local variable instruction } - + // --------------------------------------------------- // --- SignatureVisitor // --------------------------------------------------- @@ -716,8 +712,8 @@ public class AsmAnalyzer { public void visitTypeArgument() { // pass } - - + + // --------------------------------------------------- // --- AnnotationVisitor // --------------------------------------------------- @@ -746,6 +742,6 @@ public class AsmAnalyzer { // desc is the class descriptor of the enumeration class. considerDesc(desc); } - + } } diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DependencyFinder.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DependencyFinder.java new file mode 100755 index 0000000..2956958 --- /dev/null +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DependencyFinder.java @@ -0,0 +1,694 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.tools.layoutlib.create; + +import com.android.tools.layoutlib.annotations.VisibleForTesting; +import com.android.tools.layoutlib.annotations.VisibleForTesting.Visibility; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.Attribute; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.Label; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Type; +import org.objectweb.asm.signature.SignatureReader; +import org.objectweb.asm.signature.SignatureVisitor; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +/** + * Analyzes the input JAR using the ASM java bytecode manipulation library + * to list the classes and their dependencies. A "dependency" is a class + * used by another class. + */ +public class DependencyFinder { + + // Note: a bunch of stuff has package-level access for unit tests. Consider it private. + + /** Output logger. */ + private final Log mLog; + + /** + * Creates a new analyzer. + * + * @param log The log output. + */ + public DependencyFinder(Log log) { + mLog = log; + } + + /** + * Starts the analysis using parameters from the constructor. + * + * @param osJarPath The input source JARs to parse. + * @return A pair: [0]: map { class FQCN => set of FQCN class dependencies }. + * [1]: map { missing class FQCN => set of FQCN class that uses it. } + */ + public List<Map<String, Set<String>>> findDeps(List<String> osJarPath) throws IOException { + + Map<String, ClassReader> zipClasses = parseZip(osJarPath); + mLog.info("Found %d classes in input JAR%s.", + zipClasses.size(), + osJarPath.size() > 1 ? "s" : ""); + + Map<String, Set<String>> deps = findClassesDeps(zipClasses); + + Map<String, Set<String>> missing = findMissingClasses(deps, zipClasses.keySet()); + + List<Map<String, Set<String>>> result = new ArrayList<Map<String,Set<String>>>(2); + result.add(deps); + result.add(missing); + return result; + } + + /** + * Prints dependencies to the current logger, found stuff and missing stuff. + */ + public void printAllDeps(List<Map<String, Set<String>>> result) { + assert result.size() == 2; + Map<String, Set<String>> deps = result.get(0); + Map<String, Set<String>> missing = result.get(1); + + // Print all dependences found in the format: + // +Found: <FQCN from zip> + // uses: FQCN + + mLog.info("++++++ %d Entries found in source JARs", deps.size()); + mLog.info(""); + + for (Entry<String, Set<String>> entry : deps.entrySet()) { + mLog.info( "+Found : %s", entry.getKey()); + for (String dep : entry.getValue()) { + mLog.info(" uses: %s", dep); + } + + mLog.info(""); + } + + + // Now print all missing dependences in the format: + // -Missing <FQCN>: + // used by: <FQCN> + + mLog.info(""); + mLog.info("------ %d Entries missing from source JARs", missing.size()); + mLog.info(""); + + for (Entry<String, Set<String>> entry : missing.entrySet()) { + mLog.info( "-Missing : %s", entry.getKey()); + for (String dep : entry.getValue()) { + mLog.info(" used by: %s", dep); + } + + mLog.info(""); + } + } + + /** + * Prints only a summary of the missing dependencies to the current logger. + */ + public void printMissingDeps(List<Map<String, Set<String>>> result) { + assert result.size() == 2; + @SuppressWarnings("unused") Map<String, Set<String>> deps = result.get(0); + Map<String, Set<String>> missing = result.get(1); + + for (String fqcn : missing.keySet()) { + mLog.info("%s", fqcn); + } + } + + // ---------------- + + /** + * Parses a JAR file and returns a list of all classes founds using a map + * class name => ASM ClassReader. Class names are in the form "android.view.View". + */ + Map<String,ClassReader> parseZip(List<String> jarPathList) throws IOException { + TreeMap<String, ClassReader> classes = new TreeMap<String, ClassReader>(); + + for (String jarPath : jarPathList) { + ZipFile zip = new ZipFile(jarPath); + Enumeration<? extends ZipEntry> entries = zip.entries(); + ZipEntry entry; + while (entries.hasMoreElements()) { + entry = entries.nextElement(); + if (entry.getName().endsWith(".class")) { + ClassReader cr = new ClassReader(zip.getInputStream(entry)); + String className = classReaderToClassName(cr); + classes.put(className, cr); + } + } + } + + return classes; + } + + /** + * Utility that returns the fully qualified binary class name for a ClassReader. + * E.g. it returns something like android.view.View. + */ + static String classReaderToClassName(ClassReader classReader) { + if (classReader == null) { + return null; + } else { + return classReader.getClassName().replace('/', '.'); + } + } + + /** + * Utility that returns the fully qualified binary class name from a path-like FQCN. + * E.g. it returns android.view.View from android/view/View. + */ + static String internalToBinaryClassName(String className) { + if (className == null) { + return null; + } else { + return className.replace('/', '.'); + } + } + + /** + * Finds all dependencies for all classes in keepClasses which are also + * listed in zipClasses. Returns a map of all the dependencies found. + */ + Map<String, Set<String>> findClassesDeps(Map<String, ClassReader> zipClasses) { + + // The dependencies that we'll collect. + // It's a map Class name => uses class names. + Map<String, Set<String>> dependencyMap = new TreeMap<String, Set<String>>(); + + DependencyVisitor visitor = getVisitor(); + + int count = 0; + try { + for (Entry<String, ClassReader> entry : zipClasses.entrySet()) { + String name = entry.getKey(); + + TreeSet<String> set = new TreeSet<String>(); + dependencyMap.put(name, set); + visitor.setDependencySet(set); + + ClassReader cr = entry.getValue(); + cr.accept(visitor, 0 /* flags */); + + visitor.setDependencySet(null); + + mLog.debugNoln("Visited %d classes\r", ++count); + } + } finally { + mLog.debugNoln("\n"); + } + + return dependencyMap; + } + + /** + * Computes which classes FQCN were found as dependencies that are NOT listed + * in the original JAR classes. + * + * @param deps The map { FQCN => dependencies[] } returned by {@link #findClassesDeps(Map)}. + * @param zipClasses The set of all classes FQCN found in the JAR files. + * @return A map { FQCN not found in the zipClasses => classes using it } + */ + private Map<String, Set<String>> findMissingClasses( + Map<String, Set<String>> deps, + Set<String> zipClasses) { + Map<String, Set<String>> missing = new TreeMap<String, Set<String>>(); + + for (Entry<String, Set<String>> entry : deps.entrySet()) { + String name = entry.getKey(); + + for (String dep : entry.getValue()) { + if (!zipClasses.contains(dep)) { + // This dependency doesn't exist in the zip classes. + Set<String> set = missing.get(dep); + if (set == null) { + set = new TreeSet<String>(); + missing.put(dep, set); + } + set.add(name); + } + } + + } + + return missing; + } + + + // ---------------------------------- + + /** + * Instantiates a new DependencyVisitor. Useful for unit tests. + */ + @VisibleForTesting(visibility=Visibility.PRIVATE) + DependencyVisitor getVisitor() { + return new DependencyVisitor(); + } + + /** + * Visitor to collect all the type dependencies from a class. + */ + public class DependencyVisitor + implements ClassVisitor, FieldVisitor, MethodVisitor, SignatureVisitor, AnnotationVisitor { + + private Set<String> mCurrentDepSet; + + /** + * Creates a new visitor that will find all the dependencies for the visited class. + */ + public DependencyVisitor() { + } + + /** + * Sets the {@link Set} where to record direct dependencies for this class. + * This will change before each {@link ClassReader#accept(ClassVisitor, int)} call. + */ + public void setDependencySet(Set<String> set) { + mCurrentDepSet = set; + } + + /** + * Considers the given class name as a dependency. + */ + public void considerName(String className) { + if (className == null) { + return; + } + + className = internalToBinaryClassName(className); + + try { + // exclude classes that are part of the default JRE (the one executing this program) + if (getClass().getClassLoader().loadClass(className) != null) { + return; + } + } catch (ClassNotFoundException e) { + // ignore + } + + // Add it to the dependency set for the currently visited class, as needed. + assert mCurrentDepSet != null; + if (mCurrentDepSet != null) { + mCurrentDepSet.add(className); + } + } + + /** + * Considers this array of names using considerName(). + */ + public void considerNames(String[] classNames) { + if (classNames != null) { + for (String className : classNames) { + considerName(className); + } + } + } + + /** + * Considers this signature or type signature by invoking the {@link SignatureVisitor} + * on it. + */ + public void considerSignature(String signature) { + if (signature != null) { + SignatureReader sr = new SignatureReader(signature); + // SignatureReader.accept will call accessType so we don't really have + // to differentiate where the signature comes from. + sr.accept(this); + } + } + + /** + * Considers this {@link Type}. For arrays, the element type is considered. + * If the type is an object, it's internal name is considered. + */ + public void considerType(Type t) { + if (t != null) { + if (t.getSort() == Type.ARRAY) { + t = t.getElementType(); + } + if (t.getSort() == Type.OBJECT) { + considerName(t.getInternalName()); + } + } + } + + /** + * Considers a descriptor string. The descriptor is converted to a {@link Type} + * and then considerType() is invoked. + */ + public boolean considerDesc(String desc) { + if (desc != null) { + try { + if (desc.length() > 0 && desc.charAt(0) == '(') { + // This is a method descriptor with arguments and a return type. + Type t = Type.getReturnType(desc); + considerType(t); + + for (Type arg : Type.getArgumentTypes(desc)) { + considerType(arg); + } + + } else { + Type t = Type.getType(desc); + considerType(t); + } + return true; + } catch (ArrayIndexOutOfBoundsException e) { + // ignore, not a valid type. + } + } + return false; + } + + + // --------------------------------------------------- + // --- ClassVisitor, FieldVisitor + // --------------------------------------------------- + + // Visits a class header + public void visit(int version, int access, String name, + String signature, String superName, String[] interfaces) { + // signature is the signature of this class. May be null if the class is not a generic + // one, and does not extend or implement generic classes or interfaces. + + if (signature != null) { + considerSignature(signature); + } + + // superName is the internal of name of the super class (see getInternalName). + // For interfaces, the super class is Object. May be null but only for the Object class. + considerName(superName); + + // interfaces is the internal names of the class's interfaces (see getInternalName). + // May be null. + considerNames(interfaces); + } + + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + // desc is the class descriptor of the annotation class. + considerDesc(desc); + return this; // return this to visit annotion values + } + + public void visitAttribute(Attribute attr) { + // pass + } + + // Visits the end of a class + public void visitEnd() { + // pass + } + + public FieldVisitor visitField(int access, String name, String desc, + String signature, Object value) { + // desc is the field's descriptor (see Type). + considerDesc(desc); + + // signature is the field's signature. May be null if the field's type does not use + // generic types. + considerSignature(signature); + + return this; // a visitor to visit field annotations and attributes + } + + public void visitInnerClass(String name, String outerName, String innerName, int access) { + // name is the internal name of an inner class (see getInternalName). + // Note: outerName/innerName seems to be null when we're reading the + // _Original_ClassName classes generated by layoutlib_create. + if (outerName != null) { + considerName(name); + } + } + + public MethodVisitor visitMethod(int access, String name, String desc, + String signature, String[] exceptions) { + // desc is the method's descriptor (see Type). + considerDesc(desc); + // signature is the method's signature. May be null if the method parameters, return + // type and exceptions do not use generic types. + considerSignature(signature); + + return this; // returns this to visit the method + } + + public void visitOuterClass(String owner, String name, String desc) { + // pass + } + + public void visitSource(String source, String debug) { + // pass + } + + + // --------------------------------------------------- + // --- MethodVisitor + // --------------------------------------------------- + + public AnnotationVisitor visitAnnotationDefault() { + return this; // returns this to visit the default value + } + + + public void visitCode() { + // pass + } + + // field instruction + public void visitFieldInsn(int opcode, String owner, String name, String desc) { + // name is the field's name, not a type. + // desc is the field's descriptor (see Type). + considerDesc(desc); + } + + public void visitFrame(int type, int local, Object[] local2, int stack, Object[] stack2) { + // pass + } + + public void visitIincInsn(int var, int increment) { + // pass -- an IINC instruction + } + + public void visitInsn(int opcode) { + // pass -- a zero operand instruction + } + + public void visitIntInsn(int opcode, int operand) { + // pass -- a single int operand instruction + } + + public void visitJumpInsn(int opcode, Label label) { + // pass -- a jump instruction + } + + public void visitLabel(Label label) { + // pass -- a label target + } + + // instruction to load a constant from the stack + public void visitLdcInsn(Object cst) { + if (cst instanceof Type) { + considerType((Type) cst); + } + } + + public void visitLineNumber(int line, Label start) { + // pass + } + + public void visitLocalVariable(String name, String desc, + String signature, Label start, Label end, int index) { + // desc is the type descriptor of this local variable. + considerDesc(desc); + // signature is the type signature of this local variable. May be null if the local + // variable type does not use generic types. + considerSignature(signature); + } + + public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { + // pass -- a lookup switch instruction + } + + public void visitMaxs(int maxStack, int maxLocals) { + // pass + } + + // instruction that invokes a method + public void visitMethodInsn(int opcode, String owner, String name, String desc) { + + // owner is the internal name of the method's owner class + if (!considerDesc(owner) && owner.indexOf('/') != -1) { + considerName(owner); + } + // desc is the method's descriptor (see Type). + considerDesc(desc); + } + + // instruction multianewarray, whatever that is + public void visitMultiANewArrayInsn(String desc, int dims) { + + // desc an array type descriptor. + considerDesc(desc); + } + + public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, + boolean visible) { + // desc is the class descriptor of the annotation class. + considerDesc(desc); + return this; // return this to visit annotation values + } + + public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) { + // pass -- table switch instruction + + } + + public void visitTryCatchBlock(Label start, Label end, Label handler, String type) { + // type is the internal name of the type of exceptions handled by the handler, + // or null to catch any exceptions (for "finally" blocks). + considerName(type); + } + + // type instruction + public void visitTypeInsn(int opcode, String type) { + // type is the operand of the instruction to be visited. This operand must be the + // internal name of an object or array class. + if (!considerDesc(type) && type.indexOf('/') != -1) { + considerName(type); + } + } + + public void visitVarInsn(int opcode, int var) { + // pass -- local variable instruction + } + + + // --------------------------------------------------- + // --- SignatureVisitor + // --------------------------------------------------- + + private String mCurrentSignatureClass = null; + + // Starts the visit of a signature corresponding to a class or interface type + public void visitClassType(String name) { + mCurrentSignatureClass = name; + considerName(name); + } + + // Visits an inner class + public void visitInnerClassType(String name) { + if (mCurrentSignatureClass != null) { + mCurrentSignatureClass += "$" + name; + considerName(mCurrentSignatureClass); + } + } + + public SignatureVisitor visitArrayType() { + return this; // returns this to visit the signature of the array element type + } + + public void visitBaseType(char descriptor) { + // pass -- a primitive type, ignored + } + + public SignatureVisitor visitClassBound() { + return this; // returns this to visit the signature of the class bound + } + + public SignatureVisitor visitExceptionType() { + return this; // return this to visit the signature of the exception type. + } + + public void visitFormalTypeParameter(String name) { + // pass + } + + public SignatureVisitor visitInterface() { + return this; // returns this to visit the signature of the interface type + } + + public SignatureVisitor visitInterfaceBound() { + return this; // returns this to visit the signature of the interface bound + } + + public SignatureVisitor visitParameterType() { + return this; // returns this to visit the signature of the parameter type + } + + public SignatureVisitor visitReturnType() { + return this; // returns this to visit the signature of the return type + } + + public SignatureVisitor visitSuperclass() { + return this; // returns this to visit the signature of the super class type + } + + public SignatureVisitor visitTypeArgument(char wildcard) { + return this; // returns this to visit the signature of the type argument + } + + public void visitTypeVariable(String name) { + // pass + } + + public void visitTypeArgument() { + // pass + } + + + // --------------------------------------------------- + // --- AnnotationVisitor + // --------------------------------------------------- + + + // Visits a primitive value of an annotation + public void visit(String name, Object value) { + // value is the actual value, whose type must be Byte, Boolean, Character, Short, + // Integer, Long, Float, Double, String or Type + if (value instanceof Type) { + considerType((Type) value); + } + } + + public AnnotationVisitor visitAnnotation(String name, String desc) { + // desc is the class descriptor of the nested annotation class. + considerDesc(desc); + return this; // returns this to visit the actual nested annotation value + } + + public AnnotationVisitor visitArray(String name) { + return this; // returns this to visit the actual array value elements + } + + public void visitEnum(String name, String desc, String value) { + // desc is the class descriptor of the enumeration class. + considerDesc(desc); + } + } +} diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Log.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Log.java index 8efd871..c3ba591 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Log.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Log.java @@ -33,11 +33,19 @@ public class Log { } } + /** Similar to debug() but doesn't do a \n automatically. */ + public void debugNoln(String format, Object... args) { + if (mVerbose) { + String s = String.format(format, args); + System.out.print(s); + } + } + public void info(String format, Object... args) { String s = String.format(format, args); outPrintln(s); } - + public void error(String format, Object... args) { String s = String.format(format, args); errPrintln(s); @@ -50,15 +58,15 @@ public class Log { pw.flush(); error(format + "\n" + sw.toString(), args); } - + /** for unit testing */ protected void errPrintln(String msg) { System.err.println(msg); } - + /** for unit testing */ protected void outPrintln(String msg) { System.out.println(msg); } - + } diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java index 4b7a348..9cd74db 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java @@ -18,6 +18,8 @@ package com.android.tools.layoutlib.create; import java.io.IOException; import java.util.ArrayList; +import java.util.List; +import java.util.Map; import java.util.Set; @@ -47,6 +49,8 @@ public class Main { public static class Options { public boolean generatePublicAccess = true; + public boolean listAllDeps = false; + public boolean listOnlyMissingDeps = false; } public static final Options sOptions = new Options(); @@ -60,16 +64,29 @@ public class Main { if (!processArgs(log, args, osJarPath, osDestJar)) { log.error("Usage: layoutlib_create [-v] [-p] output.jar input.jar ..."); + log.error("Usage: layoutlib_create [-v] [--list-deps|--missing-deps] input.jar ..."); System.exit(1); } - log.info("Output: %1$s", osDestJar[0]); + if (sOptions.listAllDeps || sOptions.listOnlyMissingDeps) { + System.exit(listDeps(osJarPath, log)); + + } else { + System.exit(createLayoutLib(osDestJar[0], osJarPath, log)); + } + + + System.exit(1); + } + + private static int createLayoutLib(String osDestJar, ArrayList<String> osJarPath, Log log) { + log.info("Output: %1$s", osDestJar); for (String path : osJarPath) { log.info("Input : %1$s", path); } try { - AsmGenerator agen = new AsmGenerator(log, osDestJar[0], new CreateInfo()); + AsmGenerator agen = new AsmGenerator(log, osDestJar, new CreateInfo()); AsmAnalyzer aa = new AsmAnalyzer(log, osJarPath, agen, new String[] { // derived from @@ -116,17 +133,33 @@ public class Main { for (String path : osJarPath) { log.info("- Input JAR : %1$s", path); } - System.exit(1); + return 1; } - System.exit(0); + return 0; } catch (IOException e) { log.exception(e, "Failed to load jar"); } catch (LogAbortException e) { e.error(log); } - System.exit(1); + return 1; + } + + private static int listDeps(ArrayList<String> osJarPath, Log log) { + DependencyFinder df = new DependencyFinder(log); + try { + List<Map<String, Set<String>>> result = df.findDeps(osJarPath); + if (sOptions.listAllDeps) { + df.printAllDeps(result); + } else if (sOptions.listOnlyMissingDeps) { + df.printMissingDeps(result); + } + } catch (IOException e) { + log.exception(e, "Failed to load jar"); + } + + return 0; } /** @@ -138,14 +171,21 @@ public class Main { */ private static boolean processArgs(Log log, String[] args, ArrayList<String> osJarPath, String[] osDestJar) { + boolean needs_dest = true; for (int i = 0; i < args.length; i++) { String s = args[i]; if (s.equals("-v")) { log.setVerbose(true); } else if (s.equals("-p")) { sOptions.generatePublicAccess = false; + } else if (s.equals("--list-deps")) { + sOptions.listAllDeps = true; + needs_dest = false; + } else if (s.equals("--missing-deps")) { + sOptions.listOnlyMissingDeps = true; + needs_dest = false; } else if (!s.startsWith("-")) { - if (osDestJar[0] == null) { + if (needs_dest && osDestJar[0] == null) { osDestJar[0] = s; } else { osJarPath.add(s); @@ -160,7 +200,7 @@ public class Main { log.error("Missing parameter: path to input jar"); return false; } - if (osDestJar[0] == null) { + if (needs_dest && osDestJar[0] == null) { log.error("Missing parameter: path to output jar"); return false; } |