diff options
| author | Tor Norbye <tnorbye@google.com> | 2013-08-13 14:47:52 -0700 |
|---|---|---|
| committer | Tor Norbye <tnorbye@google.com> | 2013-08-13 14:56:13 -0700 |
| commit | 11835f7fcb47d44b1f70f154aec9a597e9541321 (patch) | |
| tree | 3919cc4f9405243d994a8a14e1a750902a601c64 /tools | |
| parent | abb260a280af25500b2d5c95890c9af116981f4d (diff) | |
Add typedef removal tool
The rmtypedefs tool is inteded only for the Android build itself.
It is given a set of .class file folders, and it finds any .class
files within those folders that correspond to "typedef annotations":
these are annotation classes that themselves have been annotated with
@IntDef or @StringDef. These typedefs should all be using
@Retention(RetentionPolicy.SOURCE), something this tool will check and
report if not the case.
However, even if an annotation only has source level retention, the
annotation class *itself*, will still be created as a .class file. We
don't want these annotations in the Android system image, so this tool
is used to find and nuke the corresponding .class files for the
annotations themselves.
Change-Id: Ia343cc5bdbf215ded24b4354f3d92f5a9076eae3
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/rmtypedefs/.gitignore | 3 | ||||
| -rw-r--r-- | tools/rmtypedefs/.idea/compiler.xml | 23 | ||||
| -rw-r--r-- | tools/rmtypedefs/.idea/copyright/profiles_settings.xml | 5 | ||||
| -rw-r--r-- | tools/rmtypedefs/.idea/encodings.xml | 5 | ||||
| -rw-r--r-- | tools/rmtypedefs/.idea/misc.xml | 10 | ||||
| -rw-r--r-- | tools/rmtypedefs/.idea/modules.xml | 9 | ||||
| -rw-r--r-- | tools/rmtypedefs/.idea/scopes/scope_settings.xml | 5 | ||||
| -rw-r--r-- | tools/rmtypedefs/.idea/uiDesigner.xml | 125 | ||||
| -rw-r--r-- | tools/rmtypedefs/.idea/vcs.xml | 8 | ||||
| -rw-r--r-- | tools/rmtypedefs/Android.mk | 46 | ||||
| -rw-r--r-- | tools/rmtypedefs/README.txt | 13 | ||||
| -rw-r--r-- | tools/rmtypedefs/etc/manifest.txt | 2 | ||||
| -rwxr-xr-x | tools/rmtypedefs/etc/rmtypedefs | 46 | ||||
| -rw-r--r-- | tools/rmtypedefs/rmtypedefs.iml | 36 | ||||
| -rw-r--r-- | tools/rmtypedefs/src/Android.mk | 31 | ||||
| -rw-r--r-- | tools/rmtypedefs/src/com/android/tools/rmtypedefs/RmTypeDefs.java | 224 |
16 files changed, 591 insertions, 0 deletions
diff --git a/tools/rmtypedefs/.gitignore b/tools/rmtypedefs/.gitignore new file mode 100644 index 000000000..04d423fd6 --- /dev/null +++ b/tools/rmtypedefs/.gitignore @@ -0,0 +1,3 @@ +out +.idea/workspace.xml +.DS_Store diff --git a/tools/rmtypedefs/.idea/compiler.xml b/tools/rmtypedefs/.idea/compiler.xml new file mode 100644 index 000000000..217af471a --- /dev/null +++ b/tools/rmtypedefs/.idea/compiler.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="CompilerConfiguration"> + <option name="DEFAULT_COMPILER" value="Javac" /> + <resourceExtensions /> + <wildcardResourcePatterns> + <entry name="!?*.java" /> + <entry name="!?*.form" /> + <entry name="!?*.class" /> + <entry name="!?*.groovy" /> + <entry name="!?*.scala" /> + <entry name="!?*.flex" /> + <entry name="!?*.kt" /> + <entry name="!?*.clj" /> + </wildcardResourcePatterns> + <annotationProcessing> + <profile default="true" name="Default" enabled="false"> + <processorPath useClasspath="true" /> + </profile> + </annotationProcessing> + </component> +</project> + diff --git a/tools/rmtypedefs/.idea/copyright/profiles_settings.xml b/tools/rmtypedefs/.idea/copyright/profiles_settings.xml new file mode 100644 index 000000000..3572571ad --- /dev/null +++ b/tools/rmtypedefs/.idea/copyright/profiles_settings.xml @@ -0,0 +1,5 @@ +<component name="CopyrightManager"> + <settings default=""> + <module2copyright /> + </settings> +</component>
\ No newline at end of file diff --git a/tools/rmtypedefs/.idea/encodings.xml b/tools/rmtypedefs/.idea/encodings.xml new file mode 100644 index 000000000..e206d70d8 --- /dev/null +++ b/tools/rmtypedefs/.idea/encodings.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" /> +</project> + diff --git a/tools/rmtypedefs/.idea/misc.xml b/tools/rmtypedefs/.idea/misc.xml new file mode 100644 index 000000000..97320410e --- /dev/null +++ b/tools/rmtypedefs/.idea/misc.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="EntryPointsManager"> + <entry_points version="2.0" /> + </component> + <component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="1.6" project-jdk-type="JavaSDK"> + <output url="file://$PROJECT_DIR$/out" /> + </component> +</project> + diff --git a/tools/rmtypedefs/.idea/modules.xml b/tools/rmtypedefs/.idea/modules.xml new file mode 100644 index 000000000..52f04c30f --- /dev/null +++ b/tools/rmtypedefs/.idea/modules.xml @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectModuleManager"> + <modules> + <module fileurl="file://$PROJECT_DIR$/rmtypedefs.iml" filepath="$PROJECT_DIR$/rmtypedefs.iml" /> + </modules> + </component> +</project> + diff --git a/tools/rmtypedefs/.idea/scopes/scope_settings.xml b/tools/rmtypedefs/.idea/scopes/scope_settings.xml new file mode 100644 index 000000000..922003b84 --- /dev/null +++ b/tools/rmtypedefs/.idea/scopes/scope_settings.xml @@ -0,0 +1,5 @@ +<component name="DependencyValidationManager"> + <state> + <option name="SKIP_IMPORT_STATEMENTS" value="false" /> + </state> +</component>
\ No newline at end of file diff --git a/tools/rmtypedefs/.idea/uiDesigner.xml b/tools/rmtypedefs/.idea/uiDesigner.xml new file mode 100644 index 000000000..3b0002030 --- /dev/null +++ b/tools/rmtypedefs/.idea/uiDesigner.xml @@ -0,0 +1,125 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="Palette2"> + <group name="Swing"> + <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" /> + </item> + <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" /> + </item> + <item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" /> + </item> + <item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true"> + <default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" /> + </item> + <item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" /> + <initial-values> + <property name="text" value="Button" /> + </initial-values> + </item> + <item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" /> + <initial-values> + <property name="text" value="RadioButton" /> + </initial-values> + </item> + <item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" /> + <initial-values> + <property name="text" value="CheckBox" /> + </initial-values> + </item> + <item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" /> + <initial-values> + <property name="text" value="Label" /> + </initial-values> + </item> + <item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> + <preferred-size width="150" height="-1" /> + </default-constraints> + </item> + <item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> + <preferred-size width="150" height="-1" /> + </default-constraints> + </item> + <item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1"> + <preferred-size width="150" height="-1" /> + </default-constraints> + </item> + <item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" /> + </item> + <item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3"> + <preferred-size width="150" height="50" /> + </default-constraints> + </item> + <item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3"> + <preferred-size width="200" height="200" /> + </default-constraints> + </item> + <item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3"> + <preferred-size width="200" height="200" /> + </default-constraints> + </item> + <item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" /> + </item> + <item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" /> + </item> + <item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" /> + </item> + <item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" /> + </item> + <item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1"> + <preferred-size width="-1" height="20" /> + </default-constraints> + </item> + <item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false"> + <default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" /> + </item> + <item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false"> + <default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" /> + </item> + </group> + </component> +</project> + diff --git a/tools/rmtypedefs/.idea/vcs.xml b/tools/rmtypedefs/.idea/vcs.xml new file mode 100644 index 000000000..a5dd08645 --- /dev/null +++ b/tools/rmtypedefs/.idea/vcs.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="VcsDirectoryMappings"> + <mapping directory="" vcs="" /> + <mapping directory="$PROJECT_DIR$/../.." vcs="Git" /> + </component> +</project> + diff --git a/tools/rmtypedefs/Android.mk b/tools/rmtypedefs/Android.mk new file mode 100644 index 000000000..d79d2fffa --- /dev/null +++ b/tools/rmtypedefs/Android.mk @@ -0,0 +1,46 @@ +# Copyright (C) 2013 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. + +LOCAL_PATH := $(call my-dir) + +# We use copy-file-to-new-target so that the installed +# script file's timestamp is at least as new as the +# .jar file it wraps. + +# the execution script +# ============================================================ +include $(CLEAR_VARS) +LOCAL_IS_HOST_MODULE := true +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_MODULE := rmtypedefs + +#LOCAL_STATIC_JAVA_LIBRARIES := \ +# asm-tools \ +# guavalib + +include $(BUILD_SYSTEM)/base_rules.mk + +$(LOCAL_BUILT_MODULE): $(HOST_OUT_JAVA_LIBRARIES)/rmtypedefs$(COMMON_JAVA_PACKAGE_SUFFIX) +$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/etc/rmtypedefs | $(ACP) + @echo "Copy: $(PRIVATE_MODULE) ($@)" + $(copy-file-to-new-target) + $(hide) chmod 755 $@ + +# the other stuff +# ============================================================ +subdirs := $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk, \ + src \ + )) + +include $(subdirs) diff --git a/tools/rmtypedefs/README.txt b/tools/rmtypedefs/README.txt new file mode 100644 index 000000000..9f3fb8b54 --- /dev/null +++ b/tools/rmtypedefs/README.txt @@ -0,0 +1,13 @@ +Android TypeDef Remover 1.0 + +This utility finds and removes all .class files that have been +annotated with the @IntDef annotation (android.annotations.IntDef) or +the @StringDef annotation (android.annotations.StringDef). + +It also makes sure that these annotations have source level retention +(@Retention(RetentionPolicy.SOURCE)), since otherwise uses of the +typedef will appear in .class files as well. + +This is intended to be used during the build to strip out any typedef +annotation classes, since these are not needed (or desirable) in the +system image. diff --git a/tools/rmtypedefs/etc/manifest.txt b/tools/rmtypedefs/etc/manifest.txt new file mode 100644 index 000000000..39f2e29f7 --- /dev/null +++ b/tools/rmtypedefs/etc/manifest.txt @@ -0,0 +1,2 @@ +Manifest-Version: 1.0 +Main-Class: com.android.tools.rmtypedefs.RmTypeDefs diff --git a/tools/rmtypedefs/etc/rmtypedefs b/tools/rmtypedefs/etc/rmtypedefs new file mode 100755 index 000000000..bc0cbe22e --- /dev/null +++ b/tools/rmtypedefs/etc/rmtypedefs @@ -0,0 +1,46 @@ +#!/bin/bash +# +# Copyright (C) 2013 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. + +# Set up prog to be the path of this script, including following symlinks, +# and set up progdir to be the fully-qualified pathname of its directory. +prog="$0" +while [ -h "${prog}" ]; do + newProg=`/bin/ls -ld "${prog}"` + newProg=`expr "${newProg}" : ".* -> \(.*\)$"` + if expr "x${newProg}" : 'x/' >/dev/null; then + prog="${newProg}" + else + progdir=`dirname "${prog}"` + prog="${progdir}/${newProg}" + fi +done +oldwd=`pwd` +progdir=`dirname "${prog}"` +cd "${progdir}" +progdir=`pwd` +prog="${progdir}"/`basename "${prog}"` +cd "${oldwd}" + +libdir=`dirname $progdir`/framework + +javaOpts="" +while expr "x$1" : 'x-J' >/dev/null; do + opt=`expr "$1" : '-J\(.*\)'` + javaOpts="${javaOpts} -${opt}" + shift +done + +exec java $javaOpts -jar $libdir/rmtypedefs.jar "$@" diff --git a/tools/rmtypedefs/rmtypedefs.iml b/tools/rmtypedefs/rmtypedefs.iml new file mode 100644 index 000000000..6e0f0fcff --- /dev/null +++ b/tools/rmtypedefs/rmtypedefs.iml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="JAVA_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="false"> + <output url="file://$MODULE_DIR$/../../../out/rmtypedefs" /> + <output-test url="file://$MODULE_DIR$/../../../out/rmtypedefs" /> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + <orderEntry type="module-library"> + <library> + <CLASSES> + <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/asm-tools/asm-4.0.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/asm-tools/src-4.0.zip!/" /> + </SOURCES> + </library> + </orderEntry> + <orderEntry type="module-library"> + <library> + <CLASSES> + <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/guava-tools/guava-13.0.1.jar!/" /> + </CLASSES> + <JAVADOC /> + <SOURCES> + <root url="jar://$MODULE_DIR$/../../../prebuilts/tools/common/guava-tools/src.zip!/" /> + </SOURCES> + </library> + </orderEntry> + </component> +</module> + diff --git a/tools/rmtypedefs/src/Android.mk b/tools/rmtypedefs/src/Android.mk new file mode 100644 index 000000000..067a2e655 --- /dev/null +++ b/tools/rmtypedefs/src/Android.mk @@ -0,0 +1,31 @@ +# Copyright (C) 2013 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. + +LOCAL_PATH := $(call my-dir) + + +# rmtypedefs java library +# ============================================================ +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-subdir-java-files) +LOCAL_JAR_MANIFEST := ../etc/manifest.txt +LOCAL_STATIC_JAVA_LIBRARIES := \ + asm-tools \ + guava-tools + +LOCAL_MODULE:= rmtypedefs + +include $(BUILD_HOST_JAVA_LIBRARY) + diff --git a/tools/rmtypedefs/src/com/android/tools/rmtypedefs/RmTypeDefs.java b/tools/rmtypedefs/src/com/android/tools/rmtypedefs/RmTypeDefs.java new file mode 100644 index 000000000..937559036 --- /dev/null +++ b/tools/rmtypedefs/src/com/android/tools/rmtypedefs/RmTypeDefs.java @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2013 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.rmtypedefs; + +import com.google.common.io.Files; + +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; + +import java.io.File; +import java.io.IOException; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.List; + +import static org.objectweb.asm.Opcodes.ASM4; + +/** + * Finds and deletes typedef annotation classes (and also warns if their + * retention was wrong, such that uses embeds + */ +public class RmTypeDefs { + + private static final String ANNOTATION = "java/lang/annotation/Annotation"; + private static final String STRING_DEF = "android/annotation/StringDef"; + private static final String INT_DEF = "android/annotation/IntDef"; + private static final String INT_DEF_DESC = "L" + INT_DEF + ";"; + private static final String STRING_DEF_DESC = "L" + STRING_DEF + ";"; + private static final String RETENTION_DESC = "Ljava/lang/annotation/Retention;"; + private static final String RETENTION_POLICY_DESC = "Ljava/lang/annotation/RetentionPolicy;"; + private static final String SOURCE_RETENTION_VALUE = "SOURCE"; + + private boolean mQuiet; + private boolean mVerbose; + private boolean mHaveError; + private boolean mDryRun; + + public static void main(String[] args) { + new RmTypeDefs().run(args); + } + + private void run(String[] args) { + if (args.length == 0) { + usage(System.err); + System.exit(1); + } + + List<File> dirs = new ArrayList<File>(); + for (String arg : args) { + if (arg.equals("--help") || arg.equals("-h")) { + usage(System.out); + return; + } else if (arg.equals("-q") || arg.equals("--quiet") || arg.equals("--silent")) { + mQuiet = true; + } else if (arg.equals("-v") || arg.equals("--verbose")) { + mVerbose = true; + } else if (arg.equals("-n") || arg.equals("--dry-run")) { + mDryRun = true; + } else if (arg.startsWith("-")) { + System.err.println("Unknown argument " + arg); + usage(System.err); + System.exit(1); + + } else { + // Other arguments should be file names + File file = new File(arg); + if (file.exists()) { + dirs.add(file); + } else { + System.err.println(file + " does not exist"); + usage(System.err); + System.exit(1); + } + } + } + + if (!mQuiet) { + System.out.println("Deleting @IntDef and @StringDef annotation class files"); + } + + for (File dir : dirs) { + find(dir); + } + + System.exit(mHaveError ? -1 : 0); + } + + private void find(File file) { + if (file.isDirectory()) { + File[] files = file.listFiles(); + if (files != null) { + for (File f : files) { + find(f); + } + } + } else if (file.isFile()) { + String path = file.getPath(); + if (path.endsWith(".class")) { + checkClass(file); + } else if (path.endsWith(".jar")) { + System.err.println(path + ": Warning: Encountered .jar file; .class files " + + "are not scanned and removed inside .jar files"); + } + } + } + + private void checkClass(File file) { + try { + byte[] bytes = Files.toByteArray(file); + ClassReader classReader = new ClassReader(bytes); + classReader.accept(new MyVisitor(file), 0); + } catch (IOException e) { + System.err.println("Could not read " + file + ": " + e.getLocalizedMessage()); + System.exit(1); + } + } + + /** + * Prints usage statement. + */ + static void usage(PrintStream out) { + out.println("Android TypeDef Remover 1.0"); + out.println("Copyright (C) 2013 The Android Open Source Project\n"); + out.println("Usage: rmtypedefs folder1 [folder2 [folder3...]]\n"); + out.println("Options:"); + out.println(" -h,--help show this message"); + out.println(" -q,--quiet quiet"); + out.println(" -v,--verbose verbose"); + out.println(" -n,--dry-run dry-run only, leaves files alone"); + } + + private class MyVisitor extends ClassVisitor { + + /** Class file name */ + private File mFile; + + /** Class name */ + private String mName; + + /** Is this class an annotation? */ + private boolean mAnnotation; + + /** Is this annotation a typedef? Only applies if {@link #mAnnotation} */ + private boolean mTypedef; + + /** Does the annotation have source retention? Only applies if {@link #mAnnotation} */ + private boolean mSourceRetention; + + public MyVisitor(File file) { + super(ASM4); + mFile = file; + } + + public void visit( + int version, + int access, + String name, + String signature, + String superName, + String[] interfaces) { + mName = name; + mAnnotation = interfaces != null && interfaces.length >= 1 + && ANNOTATION.equals(interfaces[0]); + + // Special case: Also delete the actual @IntDef and @StringDef .class files. + // These have class file retention + mTypedef = name.equals(INT_DEF) || name.equals(STRING_DEF); + } + + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + mTypedef = desc.equals(INT_DEF_DESC) || desc.equals(STRING_DEF_DESC); + if (desc.equals(RETENTION_DESC)) { + return new AnnotationVisitor(ASM4) { + public void visitEnum(String name, String desc, String value) { + if (desc.equals(RETENTION_POLICY_DESC)) { + mSourceRetention = SOURCE_RETENTION_VALUE.equals(value); + } + } + }; + } + return null; + } + + public void visitEnd() { + if (mAnnotation && mTypedef) { + if (!mSourceRetention && !mName.equals(STRING_DEF) && !mName.equals(INT_DEF)) { + System.err.println(mFile + ": Warning: Annotation should be annotated " + + "with @Retention(RetentionPolicy.SOURCE)"); + mHaveError = true; + } + if (mVerbose) { + if (mDryRun) { + System.out.println("Would delete " + mFile); + } else { + System.out.println("Deleting " + mFile); + } + } + if (!mDryRun) { + boolean deleted = mFile.delete(); + if (!deleted) { + System.err.println("Could not delete " + mFile); + mHaveError = true; + } + } + } + } + } +} + |
