examples
directory of the ProGuard distribution.
proguard.pro
and then type:
java -jar proguard.jar @proguard.pro
The configuration file would contain the following options:
-injars proguard.jar -outjars proguard_out.jar -libraryjars <java.home>/lib/rt.jar -printmapping proguard.map -overloadaggressively -defaultpackage '' -allowaccessmodification -keep public class proguard.ProGuard { public static void main(java.lang.String[]); }
Note the use of the <java.home>
system property; it is
replaced automatically.
Also note that the type names are fully specified:
proguard.ProGuard
and java.lang.String[]
.
The access modifiers public
and static
are not
really required in this case, since we know a priori that the specified class
and method have the proper access flags. It just looks more familiar this way.
We're using the -overloadaggressively
,
-defaultpackage
, and -allowaccessmodification
options to shave off some extra bytes, but we could leave them out as well.
The -defaultpackage
directive moves all classes to the given
package, in this case the root package. Only proguard.ProGuard
keeps its original name.
We're writing out an obfuscation mapping file with -printmapping
,
for de-obfuscating any stack traces later on, or for incremental obfuscation of
extensions.
In general, you might need a few additional directives for processing native methods, serializable classes, bean classes, or annotations. For processing 'simple' applications like ProGuard, that is not required.
mypackage.MyApplet
:
-injars in.jar -outjars out.jar -libraryjars <java.home>/lib/rt.jar -keep public class mypackage.MyApplet
The typical applet methods will be preserved automatically, since
mypackage.MyApplet
is an extension of the Applet
class in the library rt.jar
.
If applicable, you should add directives for processing native methods, serializable classes, bean classes, or annotations.
mypackage.MyMIDlet
:
-injars in.jar -outjars out.jar -libraryjars /usr/local/java/wtk2.1/lib/midpapi20.jar -libraryjars /usr/local/java/wtk2.1/lib/cldcapi11.jar -overloadaggressively -defaultpackage '' -allowaccessmodification -keep public class mypackage.MyMIDlet
Note how we're now targeting the J2ME run-time environment of
midpapi20.jar
and cldcapi11.jar
, instead of the J2SE
run-time environment rt.jar
. You can target other J2ME
environments by picking the appropriate jars.
The typical midlet methods will be preserved automatically, since
mypackage.MyMIDlet
is an extension of the MIDlet
class in the library midpapi20.jar
.
If applicable, you should add directives for processing native methods, serializable classes, bean classes, or annotations.
You must preverify your midlets after having processed them, using
J2ME's preverify
tool. Because this tool unpacks your processed
jars, you should use ProGuard's -dontusemixedcaseclassnames
option on platforms with case-insensitive filing systems, such as Windows.
Please note the in-depth article "Obfuscating MIDlet Suites with ProGuard" at developers.sun.com.
in.jar
:
-injars in.jar -outjars out.jar -libraryjars <java.home>/lib/rt.jar -printseeds -keepclasseswithmembers public class * { public static void main(java.lang.String[]); }
Note the use of -keepclasseswithmembers
. We don't want to preserve
all classes, just all classes that have main methods, and those methods.
The -printseeds
option prints out which classes exactly will
be preserved, so we know for sure we're getting what we want.
If applicable, you should add directives for processing native methods, serializable classes, bean classes, or annotations.
in.jar
:
-injars in.jar -outjars out.jar -libraryjars <java.home>/lib/rt.jar -printseeds -keep public class * extends java.applet.Applet
We're simply keeping all classes that extend the Applet
class.
Again, the -printseeds
option prints out which applets exactly
will be preserved.
If applicable, you should add directives for processing native methods, serializable classes, bean classes, or annotations.
in.jar
:
-injars in.jar -outjars out.jar -libraryjars /usr/local/java/wtk2.1/lib/midpapi20.jar -libraryjars /usr/local/java/wtk2.1/lib/cldcapi11.jar -overloadaggressively -defaultpackage '' -allowaccessmodification -printseeds -keep public class * extends javax.microedition.midlet.MIDlet
We're simply keeping all classes that extend the MIDlet
class.
And again, the -printseeds
option prints out which midlets exactly
will be preserved.
If applicable, you should add directives for processing native methods, serializable classes, bean classes, or annotations.
You must preverify your midlets after having processed them, using
J2ME's preverify
tool. Because this tool unpacks your processed
jars, you should use ProGuard's -dontusemixedcaseclassnames
option on platforms with case-insensitive filing systems, such as Windows.
in.jar
:
-injars in.jar -outjars out.jar -libraryjars <java.home>/lib/rt.jar -libraryjars /usr/local/java/servlet/servlet.jar -printseeds -keep public class * implements javax.servlet.Servlet
Keeping all servlets is very similar to keeping all applets. The servlet API is not part of the standard run-time jar, so we're specifying it as a library. Don't forget to use the right path name.
We're then keeping all classes that implement the Servlet
interface. We're using the implements
keyword because it looks
more familiar in this context, but it is equivalent to extends
,
as far as ProGuard is concerned.
And again, the -printseeds
option prints out which servlets
exactly will be preserved.
If applicable, you should add directives for processing native methods, serializable classes, bean classes, or annotations.
-injars in.jar -outjars out.jar -libraryjars <java.home>/lib/rt.jar -printmapping out.map -renamesourcefileattribute SourceFile -keepattributes InnerClasses,SourceFile,LineNumberTable,Deprecated, Signature,*Annotation*,EnclosingMethod -keep public class * { public protected *; } -keepclassmembernames class * { java.lang.Class class$(java.lang.String); java.lang.Class class$(java.lang.String, boolean); } -keepclasseswithmembernames class * { native <methods>; } -keepclassmembers class * implements java.io.Serializable { static final long serialVersionUID; private void writeObject(java.io.ObjectOutputStream); private void readObject(java.io.ObjectInputStream); java.lang.Object writeReplace(); java.lang.Object readResolve(); } -keepclassmembers class * extends java.lang.Enum { public **[] values(); }
This configuration should preserve everything we'll ever want to access in the
library. Only if there are any other non-public classes or methods that are
invoked dynamically, they should be specified using additional
-keep
directives.
The -keepclassmembernames
directive for the class$
methods is not strictly necessary. These methods are inserted by the
javac
compiler and the jikes
compiler respectively,
to implement the .class
construct. ProGuard will automatically
detect them and deal with them, even when their names have been obfuscated.
However, older versions of ProGuard and other obfuscators may rely on the
original method names. It may therefore be helpful to preserve them, in case
these other obfuscators are ever used for further obfuscation of the library.
We've added some directives for keeping the "Deprecated" attribute, for useful stack traces, for native methods, serializable classes, and for annotations (possibly with enumerations), which are all discussed in their respective examples.
The "InnerClasses" attribute (or more precisely, its source name part) has to
be preserved as well, for any inner classes that can be referenced from
outside the library. The javac
compiler would be unable to find
the inner classes otherwise.
The "Signature" attribute is required to be able to access generic types when compiling in JDK 5.0.
-keepclasseswithmembernames class * { native <methods>; }
Note the use of -keepclasseswithmembernames
. We don't want to
preserve all classes or all native methods; we just want to keep the relevant
names from being obfuscated.
ProGuard doesn't have your native code, so it can't automatically preserve the classes or class members that are invoked by the native code. These are entry points, which you'll have to specify explicitly.
-keepclassmembers class * implements java.io.Serializable { private void writeObject(java.io.ObjectOutputStream); private void readObject(java.io.ObjectInputStream); java.lang.Object writeReplace(); java.lang.Object readResolve(); }
The -keepclassmembers
option makes sure that any
serialization methods are kept. By using this directive instead of the
basic -keep
directive, we're not forcing preservation of
all serializable classes, just preservation of the listed members
of classes that are actually used.
serialVersionUID
fields. The following directives should
then be sufficient to ensure compatibility over time:
-keepnames class * implements java.io.Serializable -keepclassmembers class * implements java.io.Serializable { static final long serialVersionUID; !static !transient <fields>; private void writeObject(java.io.ObjectOutputStream); private void readObject(java.io.ObjectInputStream); java.lang.Object writeReplace(); java.lang.Object readResolve(); }
The serialVersionUID
line makes sure that field is preserved.
The <fields>
line preserves all non-static,
non-transient fields, with their original names. The introspection of the
serialization process and the de-serialization process will then find
consistent names.
serialVersionUID
fields. I imagine the
original code will then be hard to maintain, since the serial version UID
is then computed from a list of features the serializable class. Changing
the class ever so slightly may change the computed serial version UID. The
list of features is specified in the chapter on Stream
Unique Identifiers of Sun's Object
Serialization Guide. The following directives should at least
partially ensure compatibility with the original classes
-keepnames class * implements java.io.Serializable -keepclassmembers class * implements java.io.Serializable { static final long serialVersionUID; !static !transient <fields>; !private <fields>; !private <methods>; private void writeObject(java.io.ObjectOutputStream); private void readObject(java.io.ObjectInputStream); java.lang.Object writeReplace(); java.lang.Object readResolve(); }
The new directives force preservation of the elements involved in the UID
computation. In addition, the user will have to manually specify all
interfaces of the serializable classes (using something like "-keep
interface MyInterface
"), since these names are also used when
computing the UID. A fast but sub-optimal alternative would be simply
keeping all interfaces with "-keep interface *
".
Note that the above directives may preserve more classes and class members
than strictly necessary. For instance, a large number of classes may implement
the Serialization
interface, yet only a small number may actually
ever be serialized. Knowing your application and tuning the configuration will
often produce more compact results.
-keep public class mypackage.MyBean { public void setMyProperty(int); public int getMyProperty(); } -keep public class mypackage.MyBeanEditor
If there are too many elements to list explicitly, wildcards in class names and method signatures might be helpful. This example encompasses a wide range of setters and getters:
-keep class mybeans.** { void set*(%); void set*(**); void set*(%[]); void set*(**[]); void set*(int, %); void set*(int, **); % get*(); ** get*(); %[] get*(); **[] get*(); % get*(int); ** get*(int); }
The '%
' wildcard matches any primitive type, while the
'**
' wildcard matches any class name. Array types have to be
specified explicitly. This results in a short list of alternative method
signatures.
-keepattributes *Annotation* -keepclassmembers class * extends java.lang.Enum { public static **[] values(); }
For brevity, we're specifying a wildcarded attribute name, which will match
RuntimeVisibleAnnotations
,
RuntimeInvisibleAnnotations
,
RuntimeVisibleParameterAnnotations
,
RuntimeInvisibleParameterAnnotations
, and
AnnotationDefault
. Depending on the purpose of the processed
code, you could refine this selection, for instance not keeping the run-time
invisible annotations (which are only used at compile-time).
If your annotations contain enumeration classes, you'll have to preserve the
values()
method in these classes. The run-time environment
accesses this method by introspection (Isn't that just grand? Introspection is
the self-modifying code of a new generation).
Some code may make further use of introspection to figure out the enclosing methods of anonymous inner classes. In that case, the corresponding attribute will have to be preserved as well:
-keepattributes EnclosingMethod
rmic
tool. If that is not
possible, you may want to try something like this:
-keep interface * extends java.rmi.Remote { <methods>; } -keep class * implements java.rmi.Remote { <init>(java.rmi.activation.ActivationID, java.rmi.MarshalledObject); }
The first directive keeps all your Remote interfaces and their methods. The second one keeps all the implementations, along with their particular RMI constructors, if any.
-printmapping out.map -renamesourcefileattribute SourceFile -keepattributes SourceFile,LineNumberTable
We're keeping all source file attributes, but we're replacing their values by the string "SourceFile". We could use any string. This string is already present in all class files, so it doesn't take up any extra space. If you're working with J++, you'll want to keep the "SourceDir" attribute as well.
We're also keeping the line number tables of all methods.
Whenever both of these attributes are present, the Java run-time environment will include line number information when printing out exception stack traces.
The information will only be useful if we can map the obfuscated names back to
their original names, so we're saving the mapping to a file
out.map
. The information can then be used by the ReTrace tool to restore the original stack trace.
The easiest way is to specify your input jars (and/or wars, ears, zips, and directories) and a single output directory. ProGuard will then reconstruct the input in this directory, using the original jar names. For example, showing just the input and output options:
-injars application1.jar -injars application2.jar -injars application3.jar -injars application.war -outjars processed_applications
After processing, the directory processed_applications
will
contain the processed application jars and war, with their original names.
If you want explicit control over the output names, or if you want to combine
input jars (and/or wars, ears, zips, or directories) into output jars (and/or
wars, ears, zips, or directories), you can group the -injars
and
-outjars
options. For example:
-injars application1.jar -injars application2.jar -injars application3.jar -outjars application_out1.war -injars application.war -outjars application_out2.war
This configuration creates two wars: one containing all processed application jars, and one containing the processed contents of the input war.
Note that ProGuard will try to package output archives in a sensible way, reconstructing the input entries as much as required. If you want even greater control, you can add filters to the input and the output, filtering out zips, ears, wars, jars, and/or ordinary files.
-injars proguardgui.jar -outjars proguardgui_out.jar -libraryjars <java.home>/lib/rt.jar -libraryjars proguard.jar -applymapping proguard.map -keep public class proguard.gui.ProGuardGUI { public static void main(java.lang.String[]); }
We're reading both unprocessed jars: the new one as the input jar, and the old
one as a library jar. The -applymapping
option then makes sure
the ProGuard part of the code gets the previously produced obfuscation
mapping. The final application will consist of the obfuscated ProGuard jar and
the additional obfuscated GUI jar.
mypackage.MyApplication
:
-injars in.jar -libraryjars <java.home>/lib/rt.jar -dontoptimize -dontobfuscate -printusage -keep public class mypackage.MyApplication { public static void main(java.lang.String[]); }
We're not specifying an output jar, just printing out some results.
We're saving a little bit of time by skipping the optimization and obfuscation steps.
-injars in.jar -dontshrink -dontoptimize -dontobfuscate -dump
Note how we don't need to specify the Java run-time jar, because we're not processing the input jar at all.