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 -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 all 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 writing out an obfuscation mapping file with -printmapping
, for
de-obfuscating any stack traces later on, or for incremental obfuscation of
extensions.
We can further improve the results with a few additional options:
-optimizationpasses 3 -overloadaggressively -repackageclasses '' -allowaccessmodificationThese options are not required; they just shave off some extra bytes from the output jar, by performing up to 3 optimization passes, and by aggressively obfuscating class members and package names.
In general, you might need a few additional options for processing native methods, callback methods, enumerations, serializable classes, bean classes, annotations, and resource files. 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 options for processing native methods, callback methods, enumerations, serializable classes, bean classes, annotations, and resource files.
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 -repackageclasses '' -allowaccessmodification -microedition -keep public class mypackage.MyMIDlet
Note how we're now targeting the Java Micro Edition run-time environment of
midpapi20.jar
and cldcapi11.jar
, instead of the Java
Standard Edition run-time environment rt.jar
. You can target
other JME 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
.
The -microedition
option
makes sure the class files are preverified for Java Micro Edition, producing
compact StackMap
attributes. It is no longer necessary to run an
external preverifier.
Be careful if you do use the external preverify
tool on a platform
with a case-insensitive filing system, such as Windows. Because this tool
unpacks your processed jars, you should then use ProGuard's -dontusemixedcaseclassnames
option.
If applicable, you should add options for processing native methods and resource files.
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 options for processing native methods, callback methods, enumerations, serializable classes, bean classes, annotations, and resource files.
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 options for processing native methods, callback methods, enumerations, serializable classes, bean classes, annotations, and resource files.
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 -repackageclasses '' -allowaccessmodification -microedition -printseeds -keep public class * extends javax.microedition.midlet.MIDlet
We're simply keeping all classes that extend the MIDlet
class.
The -microedition
option
makes sure the class files are preverified for Java Micro Edition, producing
compact StackMap
attributes. It is no longer necessary to run an
external preverifier.
Be careful if you do use the external preverify
tool on a platform
with a case-insensitive filing system, such as Windows. Because this tool
unpacks your processed jars, you should then use ProGuard's -dontusemixedcaseclassnames
option.
The -printseeds
option prints
out which midlets exactly will be preserved.
If applicable, you should add options for processing native methods and resource files.
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.
The -printseeds
option prints
out which servlets exactly will be preserved.
If applicable, you should add options for processing native methods, callback methods, enumerations, serializable classes, bean classes, annotations, and resource files.
-injars in.jar -outjars out.jar -libraryjars <java.home>/lib/rt.jar -printmapping out.map -renamesourcefileattribute SourceFile -keepattributes Exceptions,InnerClasses,Signature,Deprecated, SourceFile,LineNumberTable,*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 enum * { public static **[] values(); public static ** valueOf(java.lang.String); } -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(); }
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
options.
The -keepclassmembernames
option 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.
The "Exceptions" attribute has to be preserved, so the compiler knows which exceptions methods may throw.
The "InnerClasses" attribute (or more precisely, its source name part) has to
be preserved too, 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 and higher.
Finally, we're keeping the "Deprecated" attribute and the attributes for producing useful stack traces.
We've also added some options for for processing native methods, enumerations, serializable classes, and annotations, which are all discussed in their respective examples.
-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 look at your native code, so it won'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. Callback methods are discussed below as a typical example.
-keep
options, something like
the following option will keep the callback class and method:
-keep class mypackage.MyCallbackClass { void myCallbackMethod(java.lang.String); }
This will preserve the given class and method from being removed or renamed.
-keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); }
-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
option instead of the basic -keep
option, 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 options 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 final java.io.ObjectStreamField[] serialPersistentFields; !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
and serialPersistentFields
lines makes sure those fields are preserved, if they are present.
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 section on Stream
Unique Identifiers of Sun's Java
Object Serialization Specification. 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 final java.io.ObjectStreamField[] serialPersistentFields; !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 options 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 options 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
often produces 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 should encompasses all
possible setters and getters in classes in the package mybeans
:
-keep class mybeans.** { void set*(***); void set*(int, ***); boolean is*(); boolean is*(int); *** get*(); *** get*(int); }
The '***
' wildcard matches any type (primitive or non-primitive,
array or non-array). The methods with the 'int
' arguments matches
properties that are lists.
-keepattributes *Annotation*
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).
Some code may make further use of introspection to figure out the enclosing methods of anonymous inner classes. In that case, the corresponding attribute has to be preserved as well:
-keepattributes EnclosingMethod
Driver
interface.
Since they are often created dynamically, you may want to preserve any
implementations that you are processing as entry points:
-keep class * implements java.sql.Driver
This option also gets rid of the note that ProGuard prints out about
(java.sql.Driver)Class.forName
constructs, if you are
instantiating a driver in your code (without necessarily implementing any
drivers yourself).
ComponentUI
class. For some reason, these have to contain a
static method createUI
, which the Swing API invokes using
introspection. You should therefore always preserve the method as an entry
point, for instance like this:
-keep class * extends javax.swing.plaf.ComponentUI { public static javax.swing.plaf.ComponentUI createUI(javax.swing.JComponent); }
This option also keeps the classes themselves.
rmic
tool. If that is not
possible, you may want to try something like this:
-keepattributes Exceptions -keep interface * extends java.rmi.Remote { <methods>; } -keep class * implements java.rmi.Remote { <init>(java.rmi.activation.ActivationID, java.rmi.MarshalledObject); }
The first -keep
option keeps all your Remote interfaces and their
methods. The second one keeps all the implementations, along with their
particular RMI constructors, if any.
The Exceptions
attribute has to be kept too, because the RMI
handling code performs introspection to check whether the method signatures
are compatible.
-adaptresourcefilenames **.properties,**.gif,**.jpg -adaptresourcefilecontents **.properties,META-INF/MANIFEST.MF
The -adaptresourcefilenames option in this case renames properties files and image files in the processed output, based on the obfuscated names of their corresponding class files (if any). The -adaptresourcefilecontents option looks for class names in properties files and in the manifest file, and replaces these names by the obfuscated names (if any). You'll probably want to adapt the filters to suit your application. Note that package names of resource files that don't have corresponding class files are not updated in the current implementation.
-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.
mycompany.myapplication.MyMain mycompany.myapplication.Foo mycompany.myapplication.Bar mycompany.myapplication.extra.FirstExtra mycompany.myapplication.extra.SecondExtra mycompany.util.FirstUtil mycompany.util.SecondUtil
Let's assume the class name mycompany.myapplication.MyMain
is the
main application class that is kept by the configuration. All other class names
can be obfuscated.
By default, packages that contain classes that can't be renamed aren't renamed either, and the package hierarchy is preserved. This results in obfuscated class names like these:
mycompany.myapplication.MyMain mycompany.myapplication.a mycompany.myapplication.b mycompany.myapplication.a.a mycompany.myapplication.a.b mycompany.a.a mycompany.a.b
The -flattenpackagehierarchy
option obfuscates the package names further, by flattening the package
hierarchy of obfuscated packages:
-flattenpackagehierarchy 'myobfuscated'
The obfuscated class names then look as follows:
mycompany.myapplication.MyMain mycompany.myapplication.a mycompany.myapplication.b myobfuscated.a.a myobfuscated.a.b myobfuscated.b.a myobfuscated.b.b
Alternatively, the -repackageclasses
option
obfuscates the entire packaging, by combining obfuscated classes into a single
package:
-repackageclasses 'myobfuscated'The obfuscated class names then look as follows:
mycompany.myapplication.MyMain mycompany.myapplication.a mycompany.myapplication.b myobfuscated.a myobfuscated.b myobfuscated.c myobfuscated.d
Additionally specifying the -allowaccessmodification
option allows access permissions of classes and class members to
be broadened, opening up the opportunity to repackage all obfuscated classes:
-repackageclasses 'myobfuscated' -allowaccessmodificationThe obfuscated class names then look as follows:
mycompany.myapplication.MyMain myobfuscated.a myobfuscated.b myobfuscated.c myobfuscated.d myobfuscated.e myobfuscated.f
The specified target package can always be the root package. For instance:
-repackageclasses '' -allowaccessmodificationThe obfuscated class names are then the shortest possible names:
mycompany.myapplication.MyMain a b c d e f
Note that not all levels of obfuscation of package names may be acceptable for all code. Notably, you may have to take into account that your application may contain resource files that have to be adapted.
-injars classes -injars in1.jar -injars in2.jar -injars in3.jar -outjars out.jar
This configuration merges the processed versions of the files in the
classes
directory and the three jars into a single output jar
out.jar
.
If you want to preserve the structure of your input jars (and/or wars, ears, zips, or directories), you can specify an output directory (or a war, an ear, or a zip). For example:
-injars in1.jar -injars in2.jar -injars in3.jar -outjars out
The input jars will then be reconstructed in the directory out
,
with their original names.
You can also combine archives into higher level archives. For example:
-injars in1.jar -injars in2.jar -injars in3.jar -outjars out.war
The other way around, you can flatten the archives inside higher level archives into simple archives:
-injars in.war -outjars out.jar
This configuration puts the processed contents of all jars inside
in.war
(plus any other contents of in.war
) into
out.jar
.
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 base_in1.jar -injars base_in2.jar -injars base_in3.jar -outjars base_out.jar -injars extra_in.jar -outjars extra_out.jar
This configuration puts the processed results of all base_in*.jar
jars into base_out.jar
, and the processed results of the
extra_in.jar
into extra_out.jar
. Note that only the
order of the options matters; the additional whitespace is just for clarity.
This grouping, archiving, and flattening can be arbitrarily complex. ProGuard always tries to package output archives in a sensible way, reconstructing the input entries as much as required.
-injars in.jar(!images/**) -outjars out.jar
This configuration removes any files in the images
directory and
its subdirectories.
Such filters can be convenient for avoiding warnings about duplicate files in the output. For example, only keeping the manifest file from a first input jar:
-injars in1.jar -injars in2.jar(!META-INF/MANIFEST.MF) -injars in3.jar(!META-INF/MANIFEST.MF) -outjars out.jar
Another useful application is speeding up the processing by ProGuard, by disregarding a large number of irrelevant classes in the runtime library jar:
-libraryjars <java.home>/lib/rt.jar(java/**,javax/**)
The filter makes ProGuard disregard com.sun.**
classes, for
instance , which don't affect the processing of ordinary applications.
It is also possible to filter the jars (and/or wars, ears, zips) themselves, based on their names. For example:
-injars in(**/acme_*.jar;) -outjars out.jar
Note the semi-colon in the filter; the filter in front of it applies to jar
names. In this case, only acme_*.jar
jars are read from the
directory in
and its subdirectories. Filters for war names, ear
names, and zip names can be prefixed with additional semi-colons. All types of
filters can be combined. They are orthogonal.
On the other hand, you can also filter the output, in order to control what content goes where. For example:
-injars in.jar -outjars code_out.jar(**.class) -outjars resources_out.jar
This configuration splits the processed output, sending **.class
files to code_out.jar
, and all remaining files to
resources_out.jar
.
Again, the filtering can be arbitrarily complex, especially when combined with the grouping of input and output.
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 -outjars processed_applications
After processing, the directory processed_applications
will
contain the processed application jars, with their original names.
-injars proguardgui.jar -outjars proguardgui_out.jar -injars proguard.jar -outjars proguard_out.jar -libraryjars <java.home>/lib/rt.jar -applymapping proguard.map -keep public class proguard.gui.ProGuardGUI { public static void main(java.lang.String[]); }
We're reading both unprocessed jars as input. Their processed contents will go
to the respective output jars. 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.
The added code in this example is straightforward; it doesn't affect the
original code. The proguard_out.jar
will be identical to the one
produced in the initial processing step. If you foresee adding more complex
extensions to your code, you should specify the options -useuniqueclassmembernames
,
-dontshrink
, and -dontoptimize
in the
original processing step. These options ensure that the obfuscated base
jar will always remain usable without changes. You can then specify the base
jar as a library jar:
-injars proguardgui.jar -outjars proguardgui_out.jar -libraryjars proguard.jar -libraryjars <java.home>/lib/rt.jar -applymapping proguard.map -keep public class proguard.gui.ProGuardGUI { public static void main(java.lang.String[]); }
-injars in.jar -outjars out.jar -libraryjars /usr/local/java/wtk2.1/lib/midpapi20.jar -libraryjars /usr/local/java/wtk2.1/lib/cldcapi11.jar -dontshrink -dontoptimize -dontobfuscate -microedition
We're not processing the input, just making sure the class files are
preverified by targeting them at Java Micro Edition with the -microedition
option. Note
that we don't need any -keep
options to specify entry points; all
class files are simply preverified.
-injars in.jar -outjars out.jar -libraryjars <java.home>/lib/rt.jar -dontshrink -dontoptimize -dontobfuscate -target 1.6
We're not processing the input, just retargeting the class files with the -target
option. They will
automatically be preverified for Java 6 as a result. Note that we don't need
any -keep
options to specify entry points; all class files are
simply updated and preverified.
mypackage.MyApplication
:
-injars in.jar -libraryjars <java.home>/lib/rt.jar -dontoptimize -dontobfuscate -dontpreverify -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 some processing time by skipping the other processing steps.
The java compiler inlines primitive constants and String constants
(static final
fields). ProGuard would therefore list such fields
as not being used in the class files that it analyzes, even if they are
used in the source files. We can add a -keepclassmembers
option
that keeps those fields a priori, in order to avoid having them listed:
-keepclassmembers class * { static final % *; static final java.lang.String *; }
-injars in.jar -dontshrink -dontoptimize -dontobfuscate -dontpreverify -dump
Note how we don't need to specify the Java run-time jar, because we're not processing the input jar at all.
You can find a set of such predefined annotations in the directory
examples/annotations/lib
in the ProGuard distribution.
The annotation classes are defined in annotations.jar
. The
corresponding ProGuard configuration (or meta-configuration, if you prefer)
is specified in annotations.pro
. With these files, you can start
annotating your code. For instance, a java source file
Application.java
can be annotated as follows:
@KeepApplication public class Application { .... }
The ProGuard configuration file for the application can then be simplified by leveraging off these annotations:
-injars in.jar -outjars out.jar -libraryjars <java.home>/lib/rt.jar -include lib/annotations.pro
The annotations are effectively replacing the application-dependent
-keep
options. You may still wish to add traditional
-keep
options for processing native
methods, enumerations, serializable classes, and annotations.
The directory examples/annotations
contains more examples that
illustrate some of the possibilities.