From db267bc191f906f55eaef21a27110cce2ec57fdf Mon Sep 17 00:00:00 2001
From: The Android Open Source Project
+Some notable optimizations that aren't supported yet:
+
+ProGuard also comes with an obfuscator plug-in for the JME Wireless
+Toolkit.
+
+
+
+With variable string arguments, it's generally not possible to determine their
+possible values. They might be read from a configuration file, for instance.
+However, ProGuard will note a number of constructs like
+"
+
+
+
+
+
+
+Version 2, June 1991
+
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+
+
+0.
+ This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+
+
+1.
+ You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+
+2.
+ You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+
+
+
+
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+
+
+3.
+ You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+
+
+
+
+
+
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+
+4.
+ You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+
+
+5.
+ You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+
+
+6.
+ Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+
+
+7.
+ If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+
+
+8.
+ If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+
+
+9.
+ The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+
+
+
+10.
+ If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+
+
+ NO WARRANTY
+
+11.
+ BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+
+
+12.
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+
+
+
+
+Copyright © 2002-2009 Eric Lafortune
+
+This program is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free Software
+Foundation; either version 2 of the License, or (at your option) any later
+version.
+
+This program is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along with
+this program; if not, write to the Free Software Foundation, Inc., 59 Temple
+Place, Suite 330, Boston, MA 02111-1307 USA
+
+In addition, as a special exception, Eric Lafortune gives permission to link
+the code of this program with the following stand-alone applications:
+Frequently Asked Questions
+
+Contents
+
+
+
+
+
+Class.forName
+ calls?
+What is shrinking?
+
+Java source code (.java files) is typically compiled to bytecode (.class
+files). Bytecode is more compact than Java source code, but it may still
+contain a lot of unused code, especially if it includes program libraries.
+Shrinking programs such as ProGuard can analyze bytecode and remove
+unused classes, fields, and methods. The program remains functionally
+equivalent, including the information given in exception stack traces.
+
+
+What is obfuscation?
+
+By default, compiled bytecode still contains a lot of debugging information:
+source file names, line numbers, field names, method names, argument names,
+variable names, etc. This information makes it straightforward to decompile
+the bytecode and reverse-engineer entire programs. Sometimes, this is not
+desirable. Obfuscators such as ProGuard can remove the debugging
+information and replace all names by meaningless character sequences, making
+it much harder to reverse-engineer the code. It further compacts the code as a
+bonus. The program remains functionally equivalent, except for the class
+names, method names, and line numbers given in exception stack traces.
+
+
+What is preverification?
+
+When loading class files, the class loader performs some sophisticated
+verification of the byte code. This analysis makes sure the code can't
+accidentally or intentionally break out of the sandbox of the virtual machine.
+Java Micro Edition and Java 6 introduced split verification. This means that
+the JME preverifier and the Java 6 compiler add preverification information to
+the class files (StackMap and StackMapTable attributes, respectively), in order
+to simplify the actual verification step for the class loader. Class files can
+then be loaded faster and in a more memory-efficient way. ProGuard can
+perform the preverification step too, for instance allowing to retarget older
+class files at Java 6.
+
+
+What kind of optimizations does ProGuard support?
+
+Apart from removing unused classes, fields, and methods in the shrinking step,
+ProGuard can also perform optimizations at the bytecode level, inside
+and across methods. Thanks to techniques like control flow analysis, data flow
+analysis, partial evaluation, static single assignment, global value numbering,
+and liveness analysis, ProGuard can:
+
+
+
+The positive effects of these optimizations will depend on your code and on
+the virtual machine on which the code is executed. Simple virtual machines may
+benefit more than advanced virtual machines with sophisticated JIT compilers.
+At the very least, your bytecode may become a bit smaller.
+
+
+
+
+Can I use ProGuard to process my commercial application?
+
+Yes, you can. ProGuard itself is distributed under the GPL, but this
+doesn't affect the programs that you process. Your code remains yours, and
+its license can remain the same.
+
+
+Does ProGuard work with Java 2? Java 5? Java 6?
+
+Yes, ProGuard supports all JDKs from 1.1 up to and including 6.0. Java 2
+introduced some small differences in the class file format. Java 5 added
+attributes for generics and for annotations. Java 6 introduced preverification
+attributes. ProGuard handles all versions correctly.
+
+
+Does ProGuard work with Java Micro Edition?
+
+Yes. ProGuard itself runs in Java Standard Edition, but you can freely
+specify the run-time environment at which your programs are targeted,
+including Java Micro Edition. ProGuard then also performs the required
+preverification, producing more compact results than the traditional external
+preverifier.
+Does ProGuard work for Google Android code?
+
+Yes. Google's dx compiler converts ordinary jar files into files
+that run on Android devices. By preprocessing the original jar files,
+ProGuard can significantly reduce the file sizes and boost the run-time
+performance of the code.
+
+
+Does ProGuard work for Blackberry code?
+
+It should. RIM's proprietary rapc compiler converts ordinary JME
+jar files into cod files that run on Blackberry devices. The compiler performs
+quite a few optimizations, but preprocessing the jar files with
+ProGuard can generally still reduce the final code size by a few
+percent. However, the rapc compiler also seems to contain some
+bugs. It sometimes fails on obfuscated code that is valid and accepted by other
+JME tools and VMs. Your mileage may therefore vary.
+
+
+Does ProGuard have support for Ant?
+
+Yes. ProGuard provides an Ant task, so that it integrates seamlessly
+into your Ant build processes. You can still use configurations in
+ProGuard's own readable format. Alternatively, if you prefer XML, you
+can specify the equivalent XML configuration.
+
+
+Does ProGuard come with a GUI?
+
+Yes. First of all, ProGuard is perfectly usable as a command-line tool
+that can easily be integrated into any automatic build process. For casual
+users, there's also a graphical user interface that simplifies creating,
+loading, editing, executing, and saving ProGuard configurations.
+
+
+Does ProGuard handle
+
+Yes. ProGuard automatically handles constructs like
+Class.forName calls?Class.forName("SomeClass") and SomeClass.class. The
+referenced classes are preserved in the shrinking phase, and the string
+arguments are properly replaced in the obfuscation phase.
+(SomeClass)Class.forName(variable).newInstance()". These might
+be an indication that the class or interface SomeClass and/or its
+implementations may need to be preserved. The user can adapt his configuration
+accordingly.
+
+
+Does ProGuard handle resource files?
+
+Yes. ProGuard copies all non-class resource files, optionally adapting
+their names and their contents to the obfuscation that has been applied.
+
+
+Does ProGuard encrypt strings constants?
+
+No. Storing encrypted string constants in program code is fairly futile, since
+the encryption has to be perfectly reversible by definition. Moreover, the
+decryption costs additional memory and computation at run-time. If this feature
+is ever incorporated, I'll provide a tool to decrypt the strings as well.
+
+
+Does ProGuard perform flow obfuscation?
+
+Not explicitly. Control flow obfuscation injects additional branches into the
+bytecode, in an attempt to fool decompilers. ProGuard does not do this,
+in order to avoid any negative effects on performance and size. However, the
+optimization step often already restructures the code to the point where most
+decompilers get confused.
+
+
+Does ProGuard support incremental obfuscation?
+
+Yes. This feature allows you to specify a previous obfuscation mapping file in
+a new obfuscation step, in order to produce add-ons or patches for obfuscated
+code.
+
+
+Can ProGuard obfuscate using reserved keywords?
+
+Yes. You can specify your own obfuscation dictionary, such as a list of
+reserved key words, identifiers with foreign characters, random source files,
+or a text by Shakespeare. Note that this hardly improves the obfuscation.
+Decent decompilers can automatically replace reserved keywords, and the effect
+can be undone fairly easily, by obfuscating again with simpler names.
+
+
+Can ProGuard reconstruct obfuscated stack traces?
+
+Yes. ProGuard comes with a companion tool, ReTrace, that can
+'de-obfuscate' stack traces produced by obfuscated applications. The
+reconstruction is based on the mapping file that ProGuard can write
+out. If line numbers have been obfuscated away, a list of alternative method
+names is presented for each obfuscated method name that has an ambiguous
+reverse mapping. Please refer to the ProGuard User
+Manual for more details.
+
+
+
+Copyright © 2002-2009
+Eric Lafortune.
+
+
+
diff --git a/docs/GPL.html b/docs/GPL.html
new file mode 100644
index 0000000..c7a2458
--- /dev/null
+++ b/docs/GPL.html
@@ -0,0 +1,406 @@
+
+
+
+GNU General Public License
+Table of Contents
+
+
+
+
+
+
+GNU GENERAL PUBLIC LICENSE
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+
+Everyone is permitted to copy and distribute verbatim copies
+of this license document, but changing it is not allowed.
+
+
+
+
+Preamble
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+
+
+
+
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+
+
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+END OF TERMS AND CONDITIONS
+
+
diff --git a/docs/GPL_exception.html b/docs/GPL_exception.html
new file mode 100644
index 0000000..0a44d66
--- /dev/null
+++ b/docs/GPL_exception.html
@@ -0,0 +1,52 @@
+
+
+
+Special Exception to the GNU General Public License
+
+
+
+and distribute linked combinations including the two. You must obey the GNU
+General Public License in all respects for all of the code used other than
+these programs. If you modify this file, you may extend this exception to your
+version of the file, but you are not obligated to do so. If you do not wish to
+do so, delete this exception statement from your version.
+
+ +Dirk Schnelle has contributed and maintained the first versions of the Ant +task. I have rewritten the implementation for version 3.0, but the XML schema +is still based on his work. +
+ +Since its first public release, many people have expressed their enthusiasm and +have chimed in with interesting ideas, bug reports, and bug fixes: Thorsten +Heit, Oliver Retzl, Jonathan Knudsen, Tarcisio Camara, Bob Drury, Dave Jarvis, +Marc Chapman, Dave Morehouse, Richard Osbaldeston, Peter Hawkins, Mark +Sherington, David Sitsky, James Manning, Ptolemy Oberin, Frank-Michael Moser, +QZ Shines, Thomas Singer, Michele Puccini, Roman Bednarek, Natalia Pujol, +Daniel Sjöblom, Jan Filipsky, Charles Smith, Gerrit Telkamp, Noel +Grandin, Torbjörn Söderstedt, Clemens Eisserer, Clark Bassett, +Eduard Welch, Dawid Weiss, Andrew Wilson, Sean Owen, Niels Gron, Ishan Mehta, +Steven Adams, Xavier Kral, +and many others. Thanks! Your feedback has been invaluable. +
+ +I am developing ProGuard in my spare time, which is possible thanks to my +day-time job at Luciad. +
+ +SourceForge is generously providing the resources for +hosting this project and many other projects. +
+ +JetBrains is kindly providing a license for its IntelliJ IDEA development +environment. +
+ +The code and these web pages were written using Sun's JDKs, Linux, IntelliJ +IDEA, GNU emacs, bash, sed, awk, and a whole host of other tools that continue +to make programming interesting. +
+ +And finally, I'm a great fan of the Java Docking Library. + +
+This is a list of the programs of which I'm aware. Obviously, I've never +personally tested all of them. Many programs, even commercial ones, have been +abandoned. Please drop me a note if you know of any other shrinkers, +optimizers, obfuscators, or preverifiers, or if some information provided +below is incorrect. +
+ +
| Author/Company | +Program | +Shrink. | +Optim. | +Obfusc. | +Preverif. | +License | +
|---|---|---|---|---|---|---|
| Eric Lafortune | +ProGuard | +Free (GPL) | +||||
| Jochen Hoenicke | +Jode | +Free (GPL) | +||||
| NQ4 | +Joga | +Free (no source) | +||||
| Nate Nystrom | +Bloat | +Free | +||||
| Hidetoshi Ohuchi | +Jarg | +Free (BSD) | +||||
| Alexander Shvets | +CafeBabe | +Free | +||||
| yWorks | +yGuard | +Free (no source) | +||||
| Christian Grothoff | +Jamit | +Free (GPL) | +||||
| Mojo | +Minijar | +Free (Apache) | +||||
| RiggsHill Software | +GenJar | +Free (Apache) | +||||
| Apache | +Ant Classfileset | +Free (Apache) | +||||
| Romain Guy | +Harvester | +Free (BSD) | +||||
| Emeric Vernat | +DCD | +Free (LGPL) | +||||
| Cristiano Sadun | +Pack | +Free (LGPL) | +||||
| Brian Alliet | +Gcclass | +Free (LGPL) | +||||
| Sable | +Soot | +Free (LGPL) | +||||
| Konstantin Knizhnik | +JavaGO | +Free | +||||
| Haruaki Tamada | +DonQuixote | +Free | +||||
| Sable | +JBCO | +Free (LGPL) | +||||
| Thorsten Heit | +JavaGuard | +Free (LGPL) | +||||
| Patrick Mueller | +Mwobfu | +Free (GPL) | +||||
| Elegant Software | +JMangle | +Free | +||||
| BebboSoft | +Bb_mug | +Free (no source) | +||||
| Dr. Java | +Marvin Obfuscator | +Free (no source) | +||||
| IBM | +WSDD | +Commercial | +||||
| PreEmptive | +DashOPro | +Commercial | +||||
| Zelix | +KlassMaster | +Commercial | +||||
| S5 Systems | +jPresto | +Commercial | +||||
| Sophia Cradle | +SophiaCompress | +Commercial | +||||
| RetroLogic | +RetroGuard | +Commercial | +||||
| CodingArt | +CodeShield | +Commercial | +||||
| Eastridge Technology | +Jshrink | +Commercial | +||||
| Helseth | +JObfuscator | +Commercial | +||||
| LeeSoftware | +Smokescreen Obfuscator | +Commercial | +||||
| Vega Technologies | +JZipper | +Commercial | +||||
| Innaworks | +mBooster | +Commercial | +||||
| Sergey Sverdlov | +J.Class Optimizer | +Commercial | +||||
| Smardec | +Allatori | +Commercial | +||||
| U. of Arizona | +SandMark | +Commercial | +||||
| Force 5 | +JCloak | +Commercial | +||||
| Semantic Designs | +Obfuscator | +Commercial | +||||
| Duckware | +Jobfuscate | +Commercial | +||||
| JProof | +JProof | +Commercial | +||||
| GITS | +Blurfuscator | +Commercial | +||||
| ChainKey | +Java Code Protector | +Commercial | +||||
| Alain Moran | +flmObf | +Free (BSD) | +||||
| Vasile Calmatui | +VasObfuLite | +Free | +||||
| IBM AlphaWorks | +JAX | +(discontinued) | +||||
| Markus Jansen | +Jopt | +(disappeared?) | +||||
| Eron Jokipii | +Jobe | +(disappeared?) | +||||
| JRC | +DeCaf | +(disappeared?) | +||||
| Bajie | +JCMP | +(disappeared?) | +||||
| Plumb Design | +Condensity | +Commercial (discontinued) | +||||
| 4th Pass | +SourceGuard | +Commercial (discontinued?) | +||||
| Software4j | +Obfuscate4j | +Commercial (discontinued?) | +||||
| JAMM Consulting | +ObfuscatePro | +Commercial (discontinued?) | +||||
| JDevelop | +JSCO | +Commercial (discontinued?) | +||||
| 4Fang | +JMix | +Commercial (discontinued?) | +||||
| 2LKit | +2LKit Obfuscator | +Commercial (disappeared?) | +||||
| WingSoft | +WingGuard | +Commercial (disappeared?) | +||||
| HashJava | +HashJava | +Commercial (disappeared?) | +
+All trademarks are property of their respective holders. + +
+ProGuard is written in Java, so it requires a Java Runtime Environment + (JRE 1.4 or higher). +
+You can download the latest release (containing the program jar, the +documentation you're reading now, examples, and the source code) from this +location: +
+
+ +If you're still working with an older version of ProGuard, check out +the summary of changes below, to see if you're missing something essential. +Better look at the up-to-date on-line version if +you're reading a local copy of this page. +
+The download section may also contain updates with sub-minor version numbers. +These versions are typically released shortly after their parent versions, for +applying emergency fixes. Please make sure to look at those if you are +encountering any problems with recent releases. +
+Finally, there may be beta versions of upcoming releases. They may be of +interest too, because they typically contain any less urgent bug fixes +collected since the previous release. +
+ +
-optimizations for fine-grained configuration of
+ optimizations.
+-adaptclassstrings for adapting string constants
+ that correspond to obfuscated classes.
+-keeppackagenames for keeping specified package
+ names from being obfuscated.
+-keepdirectories for keeping specified directory
+ entries in output jars.
+-dontnote and -dontwarn for
+ fine-grained configuration of notes and warnings.
+-regex in ReTrace, for specifying alternative
+ regular expressions to parse stack traces.
+EnclosingMethod
+ attributes are being kept.
+-keepclassmembers options.
+-classobfuscationdictionary and
+ -packageobfuscationdictionary.
+maximum.inlined.code.length (default is 8) and
+ maximum.resulting.code.length (defaults are 8000 for JSE and
+ 2000 for JME), for expert users who read release notes.
+<injars> and
+ <libraryjars> elements.
+-microedition and -dontpreverify.
+-target to modify java version of processed
+ class files.
+-keep options more orthogonal and flexible, with option
+ modifiers allowshrinking, allowoptimization, and
+ allowobfuscation.
+***",
+ matching any type, and "...", matching any number of
+ arguments.
+-forceprocessing.
+-flattenpackagehierarchy and
+ -repackageclasses (replacing -defaultpackage) to
+ control obfuscation of package names.
+-adaptresourcefilenames and
+ -adaptresourcefilecontents, with file filters, to update
+ resource files corresponding to obfuscated class names.
+Exceptions attributes as optional.
+EnclosingClass$InnerClass) in obfuscation step, if
+ InnerClasses attributes or EnclosingMethod
+ attributes are being kept.
+Exceptions attribute as
+ optional, you may have to specify -keepattributes Exceptions,
+ notably when processing code that is to be used as a library.
+
+-microedition. You then no longer need to process the
+ code with an external preverifier.
+
+-repackageclasses instead of the
+ old option name -defaultpackage.
+-dontusemixedcaseclassnames is specified.
+-useuniqueclassmembernames.
+Class.forName detection.
+-basedirectory option.
+-whyareyoukeeping option to get details on why given
+ classes and class members are being kept.
+Class.forName constructs.
+assumenosideeffects' nested element in Ant task.
+-resourcejars option. Resources should now be read
+ using regular -injars options, using filters if necessary.
+outjars' now nested element instead of attribute.
+ type' attribute of <method> element no
+ longer defaults to 'void'.
+ < and > characters now have to be
+ encoded in embedded configurations.
+ <proguardconfiguration> task no longer accepts
+ attributes.
+ -applymapping option for incremental obfuscation.
+SourceDir attribute.
+.class constructs.
+-defaultpackage bug for protected classes and class
+ members.
+-defaultpackage option.
+.class constructs in internal classes
+ targeted at JRE1.2 (the default in JDK1.4).
+-dump option when -outjar option is not
+ present.
+.class detection for classes compiled with
+ Jikes.
+Class.forName("MyClass"),
+ MyClass.class, and
+ (MyClass)Class.forName(variable).newInstance() constructs.
+ This greatly simplifies configuration.
++
+ +
+ +
+ +
+ +
+ +
+I can't promise a swift answer, or any answer at all, for that matter, but I +like seeing any constructive comments. +
+ +ProGuard isn't a typical open source project, in the sense that I am +not looking for code contributions. Developing on my own allows me to +do things my way, without the overhead and compromises associated with larger +projects. + +
+ProGuard is a free Java class file shrinker, optimizer, and obfuscator. +It can detect and remove unused classes, fields, methods, and attributes. It +can then optimize bytecode and remove unused instructions. Finally, it can +rename the remaining classes, fields, and methods using short meaningless +names. The resulting jars are smaller and harder to reverse-engineer. +
++Your browser doesn't support frames, but that's cool. +
+You can go straight to the main page. + +
+ +ProGuard itself is copyrighted, but its distribution license provides +you with some rights for modifying and redistributing its code and its +documentation. More specifically, ProGuard is distributed under the +terms of the GNU General Public License (GPL), version +2, as published by the Free +Software Foundation (FSF). In short, this means that you may freely +redistribute the program, modified or as is, on the condition that you make +the complete source code available as well. If you develop a program that is +linked with +ProGuard, the program as a whole has to be distributed at no charge +under the GPL. I am granting a special +exception to the latter clause (in wording suggested by +the FSF), for combinations with the following stand-alone +applications: Apache Ant, Apache Maven, the Eclipse ProGuardDT GUI, the +EclipseME JME IDE, the Sun NetBeans Java IDE, the Sun JME Wireless Toolkit, +and the Javaground Tools. + +
+The ProGuard user documentation represents an important part of this +work. It may only be redistributed without changes, along with the unmodified +version of the code. + +
+ProGuard is a free Java class file shrinker, optimizer, obfuscator, and +preverifier. It detects and removes unused classes, fields, methods, and +attributes. It optimizes bytecode and removes unused instructions. It renames +the remaining classes, fields, and methods using short meaningless names. +Finally, it preverifies the processed code for Java 6 or for Java Micro +Edition. +
+Some uses of ProGuard are: ++ProGuard's main advantage compared to other Java obfuscators is +probably its compact template-based configuration. A few intuitive command +line options or a simple configuration file are usually sufficient. For +instance, the following configuration option preserves all applets in a jar: +
+ -keep public class * extends java.applet.Applet ++The user manual explains all available options and shows more examples of this +powerful configuration style. +
+ProGuard is fast. It only takes seconds to process programs and +libraries of several megabytes. The results section presents actual figures +for a number of applications. +
+ProGuard is a command-line tool with an optional graphical user +interface. It also comes with plugins for Ant and for the JME Wireless +Toolkit. +
+
+Version 4.0 introduced preverification and more bytecode optimizations. Please +report any problems, so they can be fixed soon. +
+The following sections provide more detailed information: +
+
+Before you can use the proguard task, you have to tell Ant about
+this new task. The easiest way is to add the following line to your
+build.xml file:
+
+ +
+<taskdef resource="proguard/ant/task.properties" + classpath="/usr/local/java/proguard/lib/proguard.jar" /> ++
+ +Please make sure the class path is set correctly for your system. +
+ +There are three ways to configure the ProGuard task: using an external +configuration file, using embedded ProGuard configuration options, or using +the equivalent XML configuration tags. These three ways can be combined, +depending on practical circumstances and personal preference. +
+ +
configuration
+attribute of your
+proguard task. Your ant build file will then look like this:
++ +
+<taskdef resource="proguard/ant/task.properties" + classpath="/usr/local/java/proguard/lib/proguard.jar" /> +<proguard configuration="myconfigfile.pro"/> ++
+ +This is a convenient option if you prefer ProGuard's configuration style over +XML, if you want to keep your build file small, or if you have to share your +configuration with developers who don't use Ant. +
+ +
proguard task
+(the PCDATA area). Your Ant build file will then look like this:
++ +
+<taskdef resource="proguard/ant/task.properties"
+ classpath="/usr/local/java/proguard/lib/proguard.jar" />
+<proguard>
+ -libraryjars ${java.home}/lib/rt.jar
+ -injars in.jar
+ -outjars out.jar
+
+ -keepclasseswithmembers public class * {
+ public static void main(java.lang.String[]);
+ }
+</proguard>
+
++ +Some minor syntactical changes are required in order to conform with the XML +standard. +
+
+Firstly, the # character cannot be used for comments in an XML
+file. Comments must be enclosed by an opening <!-- and a
+closing -->. All occurrences of the # character
+can be removed.
+
+
+Secondly, the use of < and > characters would
+upset the structure of the XML build file. Environment variables are now
+enclosed by an opening ${ and a closing }. This
+syntax also allows you to use Ant properties within the ProGuard
+configuration. Other occurrences of < and >
+have to be encoded as < and >.
+
+ +
examples/ant directory of the ProGuard distribution.
+<proguard> task and the
+<proguardconfiguration> task can have the following
+attributes (only for <proguard>) and nested
+elements:
+
+configuration
+ = "filename"configuration
+ element.skipnonpubliclibraryclasses
+ = "boolean"
+ (default = true)skipnonpubliclibraryclassmembers
+ = "boolean"
+ (default = true)target
+ = "version"
+ (default = none)forceprocessing
+ = "boolean"
+ (default = false)printseeds
+ = "boolean or filename"
+ (default = false)keep
+ commands, to the standard output or to the given file.shrink
+ = "boolean"
+ (default = true)printusage
+ = "boolean or filename"
+ (default = false)optimize
+ = "boolean"
+ (default = true)optimizationpasses
+ = "n"
+ (default = 1)allowaccessmodification
+ = "boolean"
+ (default = false)mergeinterfacesaggressively
+ = "boolean"
+ (default = false)obfuscate
+ = "boolean"
+ (default = true)printmapping
+ = "boolean or filename"
+ (default = false)applymapping
+ = "filename"
+ (default = none)obfuscationdictionary
+ = "filename"
+ (default = none)classobfuscationdictionary
+ = "filename"
+ (default = none)packageobfuscationdictionary
+ = "filename"
+ (default = none)overloadaggressively
+ = "boolean"
+ (default = false)useuniqueclassmembernames
+ = "boolean"
+ (default = false)usemixedcaseclassnames
+ = "boolean"
+ (default = true)flattenpackagehierarchy
+ = "package_name"
+ (default = none)repackageclasses
+ = "package_name"
+ (default = none)renamesourcefileattribute
+ = "string"
+ (default = none)SourceFile
+ attributes.preverify
+ = "boolean"
+ (default = true)microedition
+ = "boolean"
+ (default = false)verbose
+ = "boolean"
+ (default = false)note
+ = "boolean"
+ (default = true)warn
+ = "boolean"
+ (default = true)ignorewarnings
+ = "boolean"
+ (default = false)printconfiguration
+ = "boolean or filename"
+ (default = false)dump
+ = "boolean or filename"
+ (default = false)<injar
+ class_path
+ /><outjar
+ class_path
+ /><libraryjar
+ class_path
+ /><keepdirectory name = "directory_name"
+ /><keepdirectories filter = "directory_filter"
+ /><keep
+ modifiers
+ class_specification
+ >
+ class_member_specifications
+ </keep><keepclassmembers
+ modifiers
+ class_specification
+ >
+ class_member_specifications
+ </keepclassmembers><keepclasseswithmembers
+ modifiers
+ class_specification
+ >
+ class_member_specifications
+ </keepclasseswithmembers><keepnames
+ class_specification
+ >
+ class_member_specifications
+ </keepnames><keepclassmembernames
+ class_specification
+ >
+ class_member_specifications
+ </keepclassmembernames><keepclasseswithmembernames
+ class_specification
+ >
+ class_member_specifications
+ </keepclasseswithmembernames><whyareyoukeeping
+ class_specification
+ >
+ class_member_specifications
+ </whyareyoukeeping><assumenosideeffects
+ class_specification
+ >
+ class_member_specifications
+ </assumenosideeffects><optimization name = "optimization_name"
+ /><optimizations filter = ""optimization_filter"
+ /><keeppackagename name = "package_name"
+ /><keeppackagenames filter = "package_filter"
+ /><keepattribute name = "attribute_name"
+ /><keepattributes filter = "attribute_filter"
+ /><adaptclassstrings filter = "class_filter"
+ /><adaptresourcefilenames filter = "file_filter"
+ /><adaptresourcefilecontents filter = "file_filter"
+ /><dontnote filter = "class_filter"
+ /><dontwarn filter = "class_filter"
+ /><configuration refid = "ref_id"
+ /><proguardconfiguration> task (or
+ <proguard> task) with the attribute id =
+ "ref_id". Note that only the nested elements of this configuration
+ are considered, not the attributes. Also note: for reading ProGuard-style
+ configuration files, use the configuration
+ attribute.path = "path"location = "name" (or file
+ = "name", or dir = "name", or
+ name = "name")refid = "ref_id"id = "ref_id".filter =
+ "file_filter"jarfilter =
+ "file_filter"warfilter =
+ "file_filter"earfilter =
+ "file_filter"zipfilter =
+ "file_filter"allowshrinking
+ = "boolean"
+ (default = false)allowoptimization
+ = "boolean"
+ (default = false)allowobfuscation
+ = "boolean"
+ (default = false)access = "access_modifiers"type = "type"name = "class_name"extends = "class_name"implements = "class_name"<field
+ class_member_specification
+ /><method
+ class_member_specification
+ /><constructor
+ class_member_specification
+ />access = "access_modifiers"type = "type"parameters attribute is specified.name = "name"parameters = "parameters"type attribute is
+ specified.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 '' +-allowaccessmodification ++These 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. +
+Note that you will still have to adapt the midlet jar size in the +corresponding jad file; ProGuard doesn't do that for you. + + +
mypackage.MyApplet:
++-injars in.jar +-outjars out.jar +-libraryjars /usr/local/java/javacard2.2.2/lib/api.jar +-dontwarn java.lang.Class +-overloadaggressively +-repackageclasses '' +-allowaccessmodification + +-keep public class mypackage.MyApplet ++
+The configuration is very similar to the configuration for midlets, except that +it now targets the Java Card run-time environment. This environment doesn't +have java.lang.Class, so we're telling ProGuard not to worry about it. + + +
mypackage.MyXlet:
++-injars in.jar +-outjars out.jar +-libraryjars /usr/local/java/jtv1.1/javatv.jar +-libraryjars /usr/local/java/cdc1.1/lib/cdc.jar +-libraryjars /usr/local/java/cdc1.1/lib/btclasses.zip +-overloadaggressively +-repackageclasses '' +-allowaccessmodification + +-keep public class mypackage.MyXlet ++
+The configuration is very similar to the configuration for midlets, except that +it now targets the CDC run-time environment with the Java TV API. + + +
mypackage.MyActivity:
++-injars in.jar +-outjars out.jar +-libraryjars /usr/local/java/android-1.5_r1/platforms/android-1.5/android.jar +-overloadaggressively +-repackageclasses '' +-allowaccessmodification +-optimizations !code/simplification/arithmetic + +-keep public class mypackage.MyActivity ++
+The configuration is very similar to the configuration for midlets, except that +it now targets the Android run-time environment. +
+The -optimizations option
+disables some arithmetic simplifications that Dalvik 1.0 and 1.5 can't handle.
+
+If applicable, you should add options for processing native +methods, callback methods, 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. + + +
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. +
+Note that you will still have to adapt the midlet jar size in the +corresponding jad file; ProGuard doesn't do that for you. + + +
in.jar:
++-injars in.jar +-outjars out.jar +-libraryjars /usr/local/java/javacard2.2.2/lib/api.jar +-dontwarn java.lang.Class +-overloadaggressively +-repackageclasses '' +-allowaccessmodification +-printseeds + +-keep public class * implements javacard.framework.Applet ++
+We're simply keeping all classes that implement the Applet
+interface.
+
+The -printseeds option prints
+out which applets exactly will be preserved.
+
+
+
in.jar:
++-injars in.jar +-outjars out.jar +-libraryjars /usr/local/java/jtv1.1/javatv.jar +-libraryjars /usr/local/java/cdc1.1/lib/cdc.jar +-libraryjars /usr/local/java/cdc1.1/lib/btclasses.zip +-overloadaggressively +-repackageclasses '' +-allowaccessmodification +-printseeds + +-keep public class * implements javax.tv.xlet.Xlet ++
+We're simply keeping all classes that implement the Xlet interface.
+
+The -printseeds option prints
+out which xlets exactly will be preserved.
+
+
+
in.jar:
++-injars in.jar +-outjars out.jar +-libraryjars /usr/local/java/android-1.5_r1/platforms/android-1.5/android.jar +-overloadaggressively +-repackageclasses '' +-allowaccessmodification +-optimizations !code/simplification/arithmetic +-printseeds + +-keep public class * extends android.app.Activity +-keep public class * extends android.app.Service +-keep public class * extends android.content.BroadcastReceiver +-keep public class * extends android.content.ContentProvider ++
+We're keeping all classes that extend the base classes that may be referenced
+by the AndroidManifest.xml file of the application.
+
+The -printseeds option prints
+out which implementations exactly will be preserved.
+
+If applicable, you should add options for processing native +methods, callback 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. + + +
+-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. + + +
+-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' +-allowaccessmodification ++The 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 '' +-allowaccessmodification ++The 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.
+
+ +
lib directory of the
+ProGuard distribution. To run the ProGuard graphical user interface, just type:
+
+java -jar proguardgui.jar [-nosplash] [configuration_file]
+
-nosplash option, you
+can switch off the short opening animation. If you have specified a ProGuard
+configuration file, it will be loaded. The GUI works like a wizard. You can
+edit the configuration and execute ProGuard through a few tabs:
++ +
| ProGuard | +Optionally load an existing configuration file. |
| Input/Output | +Specify the program jars and library jars. |
| Shrinking | +Specify the shrinking options. |
| Obfuscation | +Specify the obfuscation options. |
| Optimization | +Specify the optimization options. |
| Information | +Specify some options to get information. |
| Process | +View and save the resulting configuration, and run ProGuard. |
+ +In addition, there is a tab to execute ReTrace interactively: +
+ +
| ReTrace | +Set up and run ReTrace, to de-obfuscate stack traces. |
+ +You can freely toggle between the tabs by means of the buttons on the +left-hand side of the window, or by means of the Previous and +Next buttons at the bottom of the tabs. Tool tips briefly explain the +purpose of the numerous options and text fields, although a basic +understanding of the shrinking/optimization/obfuscation/preverification +process is assumed. Please refer to the Introduction of this manual. +
+ +
| Load configuration... | +opens a file chooser to load an existing ProGuard configuration + file. |
+ +If you don't want to load an existing configuration, you can just continue +creating a new configuration from scratch. +
+ +Each of these lists can be edited by means of a couple of buttons on the +right-hand side: +
+ +
| Add input... | opens a file chooser to add an + input entry to the list of program jars. |
| Add output... | opens a file chooser to add an + output entry to the list of program jars. |
| Add... | +opens a file chooser to add an entry to the list of library + jars. |
| Edit... | +opens a file chooser to edit the selected entry in the list. |
| Filter... | +opens a text entry field to add or edit the filters of the selected + entries in the list. |
| Remove | +removes the selected entries from the list. |
| Move up | +moves the selected entries one position up the list. |
| Move down | +moves the selected entries one position down the list. |
| Move to libraries | +moves the selected entries in the list of program jars to the list of + library jars. |
| Move to program | +moves the selected entries in the list of library jars to the list of + program jars. |
+ +Filters allow to filter files based on their names. One can specify filters +for class file names and resource file names, for jar file names, for war file +names, for ear file names, and for zip file names. Multiple entries in the +program list only make sense when combined with filters; each output file is +written to the first entry with a matching filter. +
+ +Input entries that are currently not readable are colored red. +
+ +The order of the entries in each list may matter, as the first occurrence of +any duplicate entries gets precedence, just as in conventional class paths. +
+ +Corresponding configuration options: +
+ +The fixed lists contain predefined entries that are typically useful for many +applications. Each of these entries can be toggled by means of a check box. +The text field following each entry allows to constrain the applicable classes +by means of a comma-separated list of wildcarded, fully-qualified class +names. The default is "*", which means that all input classes of the +corresponding type are considered. +
+ +For example, checking the Applications entry and filling in +"myapplications.**" after it would mean: keep all classes that have main +methods in the "myapplications" package and all of its subpackages. +
+ +The variable list at the bottom allows to define additional entries +yourself. The list can be edited by means of a couple of buttons on the +right-hand side: +
+ +
| Add... | +opens a window to add a new entry to the list. |
| Edit... | +opens a window to edit the selected entry in the list. |
| Remove | +removes the selected entries from the list. |
| Move up | +moves the selected entries one position up the list. |
| Move down | +moves the selected entries one position down the list. |
+ +The interface windows allow to specify classes, fields, and methods. They +contain text fields and check boxes to constrain these items. They have +Ok and Cancel buttons to apply or to cancel the operation. +
+
+For example, your application may be creating some classes dynamically using
+Class.forName. You should then specify them here, so they are kept
+by their original names. Press the Add... button to open the class
+window. Fill out the fully-qualified class name in the Code text field,
+and press the Ok button. Repeat this for all required classes. Wildcards
+can be helpful to specify a large number of related classes in one go. If you
+want to specify all implementations of a certain interface, fill out the
+fully qualified interface name in the Extends/implements class instead.
+
+ +For more advanced settings, it is advisable to become familiar with ProGuard's +configuration options through the Usage section and +the Examples section. We'll suffice with a brief +overview of the three dialogs provided by the GUI. +
+ +The keep class dialog appears when adding or editing new special keep +entries. It has text fields and selections for specifying and constraining +classes and class members to keep. The Advanced options / Basic +options button at the bottom of the dialog allows to toggle showing the +advanced options. + +
+ +The keep field dialog appears when adding or editing fields within the +above dialog. It has text fields and selections for specifying and +constraining fields to keep. Again, the Advanced options / Basic +options button at the bottom of the dialog allows to toggle showing the +advanced options. + +
+ +Similarly, the keep method dialog appears when adding or editing +methods within the keep class dialog. It has text fields and selections for +specifying and constraining methods to keep. Again, the Advanced +options / Basic options button at the bottom of the dialog allows +to toggle showing the advanced options. + +
+ +Corresponding configuration options: +
+ +The lists are manipulated in the same way as in the Shrinking Tab. +
+ +Corresponding configuration options: +
+ +The lists are manipulated in much the same way as in the Shrinking Tab. +
+ +Corresponding configuration options: +
+ +Corresponding configuration options: +
+ +
| View configuration | +displays the current ProGuard configuration in the console. |
| Save configuration... | +opens a file chooser to save the current ProGuard + configuration. |
| Process! | +executes ProGuard with the current configuration. |
+ +
| Load stack trace... | +opens a file chooser to load an obfuscated stack trace. |
| ReTrace! | +executes ReTrace with the current settings. |
+ +
+Each of these steps is optional. For instance, ProGuard can also be used to +just list dead code in an application, or to preverify class files for +efficient use in Java 6. +
+ +
| Input jars | ++ | |||||||
| + | Shrunk code | ++ | ||||||
| + | Optim. code | ++ | Output jars | +|||||
| - shrink → | +- optimize → | +- obfuscate → | +Obfusc. code | +- preverify → | +||||
| Library jars | +------------------------------- (unchanged) -------------------------------→ | +Library jars | +||||||
+ +ProGuard typically reads the input jars (or wars, ears, zips, or +directories). It then shrinks, optimizes, obfuscates, and preverifies them. +Optionally, multiple optimization passes can be performed, each typically +followed by another shrinking step. ProGuard writes the processed results to +one or more output jars (or wars, ears, zips, or directories). The +input may contain resource files, whose names and contents can optionally be +updated to reflect the obfuscated class names. +
+ProGuard requires the library jars (or wars, ears, zips, or +directories) of the input jars to be specified. These are essentially the +libraries that you would need for compiling the code. ProGuard uses them to +reconstruct the class dependencies that are necessary for proper processing. +The library jars themselves always remain unchanged. You should still put them +in the class path of your final application. +
+In order to determine which code has to be preserved and which code can be +discarded or obfuscated, you have to specify one or more entry points to +your code. These entry points are typically classes with main methods, applets, +midlets, etc. +
+The Usage section of this manual describes the
+necessary -keep options and
+the Examples section provides plenty of examples.
+
+
Class.forName() constructs may refer to any
+class at run-time. It is generally impossible to foresee which classes have to
+be preserved (with their original names), since the class names might be read
+from a configuration file, for instance. You therefore have to specify them in
+your ProGuard configuration, with the same simple -keep options.
++However, ProGuard will already detect and handle the following cases for you: + +
Class.forName("SomeClass")
+SomeClass.class
+SomeClass.class.getField("someField")
+SomeClass.class.getDeclaredField("someField")
+SomeClass.class.getMethod("someMethod", new Class[] {})
+SomeClass.class.getMethod("someMethod", new Class[] { A.class })
+SomeClass.class.getMethod("someMethod", new Class[] { A.class, B.class })
+SomeClass.class.getDeclaredMethod("someMethod", new Class[] {})
+SomeClass.class.getDeclaredMethod("someMethod", new Class[] { A.class })
+SomeClass.class.getDeclaredMethod("someMethod", new Class[] { A.class, B.class })
+
+Furthermore, ProGuard will offer some suggestions if keeping some classes or
+class members appears necessary. For example, ProGuard will note constructs
+like "(SomeClass)Class.forName(variable).newInstance()". These
+might be an indication that the class or interface SomeClass
+and/or its implementations may need to be preserved. You can then adapt your
+configuration accordingly.
+
+For proper results, you should at least be somewhat familiar with the code +that you are processing. Obfuscating code that performs a lot of introspection +may require trial and error, especially without the necessary information +about the internals of the code. +
+ +
+
-dontskipnonpubliclibraryclasses option, and
+ maybe even the -dontskipnonpubliclibraryclassmembers option.
+ The graphical user interface has checkboxes for these settings.
+ + +
myObject.myMethod() if that call
+ wouldn't have any effect. It ignores the possibility that
+ myObject might be null, causing a NullPointerException. In
+ some way this is a good thing: optimized code may throw fewer exceptions.
+ Should this entire assumption be false, you'll have to switch off
+ optimization using the -dontoptimize option.
+ + +
+ +
a.class", "b.class", etc. If there is a large
+ numbers of classes in the same package, it may also write out
+ "aux.class". Windows doesn't allow creating files with
+ this reserved name (among a few other names), so it's generally better to
+ write the output to a jar, in order to avoid such problems.
+ + +
+ +
-dontoptimize option. For
+more fine-grained control over individual optimizations, experts can use the
+-optimizations option,
+with a filter based on the optimization names listed below. The filter works
+like any filter in ProGuard.
++ +The following wildcards are supported: + +
? |
+ matches any single character in an optimization name. |
* |
+ matches any part of an optimization name. |
+
+For example,
+"code/simplification/variable,code/simplification/arithmetic"
+only performs the two specified peephole optimizations.
+
+
+For example, "!method/propagation/*" performs all optimizations,
+except the ones that propagate values between methods.
+
+
+For example,
+"!code/simplification/advanced,code/simplification/*" only
+performs all peephole optimizations.
+
+Some optimizations necessarily imply other optimizations. These are then +indicated. Note that the list is likely to change over time, as optimizations +are added and reorganized. +
+ +
class/marking/finalclass/merging/verticalclass/merging/horizontalcode/removal/advanced)field/removal/writeonlyfield/marking/privatecode/simplification/advanced)field/propagation/valuemethod/marking/privatecode/removal/advanced)method/marking/staticmethod/marking/finalcode/removal/advanced)method/removal/parametercode/simplification/advanced)method/propagation/parametercode/simplification/advanced)method/propagation/returnvaluemethod/inlining/shortmethod/inlining/uniquemethod/inlining/tailrecursioncode/mergingcode/simplification/variablecode/simplification/arithmeticcode/simplification/castcode/simplification/fieldcode/removal/simple)code/simplification/branchcode/removal/advanced)code/simplification/advancedcode/removal/exception)code/removal/advancedcode/removal/exception)code/removal/simplecode/removal/variablecode/removal/exceptioncode/allocation/variable+ +
java -jar proguard.jar options ...
++ Typically: +
+java -jar proguard.jar @myconfig.pro
+
+ +
@filename |
+
+Short for '-include filename'. |
+
-include
+ filename |
+
+Read configuration options from the given file. | +
-basedirectory
+ directoryname |
+
+Specifies the base directory for subsequent relative file names. | +
-injars
+ class_path |
+Specifies the program jars (or wars, ears, zips, or directories). | +
-outjars
+ class_path |
+Specifies the name of the output jars (or wars, ears, zips, or + directories). | +
-libraryjars
+ class_path |
+Specifies the library jars (or wars, ears, zips, or directories). | +
-dontskipnonpubliclibraryclasses |
+Don't ignore non-public library classes. | +
-dontskipnonpubliclibraryclassmembers |
+Don't ignore package visible library class members. | +
-keepdirectories
+ [directory_filter] |
+Keep the specified directories in the output jars (or wars, ears, zips, or + directories). | +
-target
+ version |
+Set the given version number in the processed classes. | +
-forceprocessing |
+Process the input, even if the output seems up to date. | +
-keep
+ [,modifier,...]
+ class_specification |
+Preserve the specified classes and class members. | + +
-keepclassmembers
+ [,modifier,...]
+ class_specification |
+Preserve the specified class members, if their classes are preserved as + well. | +
-keepclasseswithmembers
+ [,modifier,...]
+ class_specification |
+Preserve the specified classes and class members, if all of the + specified class members are present. | +
-keepnames
+ class_specification |
+Preserve the names of the specified classes and class members (if + they aren't removed in the shrinking step). | +
-keepclassmembernames
+ class_specification |
+Preserve the names of the specified class members (if they aren't removed + in the shrinking step). | +
-keepclasseswithmembernames
+ class_specification |
+Preserve the names of the specified classes and class members, if + all of the specified class members are present (after the shrinking + step). | +
-printseeds
+ [filename] |
+List classes and class members matched by the various -keep
+ options, to the standard output or to the given file. |
+
-dontshrink |
+Don't shrink the input class files. | +
-printusage
+ [filename] |
+List dead code of the input class files, to the standard output or to the + given file. | +
-whyareyoukeeping
+ class_specification |
+Print details on why the given classes and class members are being kept in + the shrinking step. | +
-dontoptimize |
+Don't optimize the input class files. | +
-optimizations
+ optimization_filter |
+The optimizations to be enabled and disabled. | +
-optimizationpasses
+ n |
+The number of optimization passes to be performed. | +
-assumenosideeffects
+ class_specification |
+Assume that the specified methods don't have any side effects, while + optimizing. | +
-allowaccessmodification |
+Allow the access modifiers of classes and class members to be modified, + while optimizing. | +
-mergeinterfacesaggressively |
+Allow any interfaces to be merged, while optimizing. | +
-dontobfuscate |
+Don't obfuscate the input class files. | +
-printmapping
+ [filename] |
+Print the mapping from old names to new names for classes and class members + that have been renamed, to the standard output or to the given file. | +
-applymapping
+ filename |
+Reuse the given mapping, for incremental obfuscation. | +
-obfuscationdictionary
+ filename |
+Use the words in the given text file as obfuscated field names and method names. | +
-classobfuscationdictionary
+ filename |
+Use the words in the given text file as obfuscated class names. | +
-packageobfuscationdictionary
+ filename |
+Use the words in the given text file as obfuscated package names. | +
-overloadaggressively |
+Apply aggressive overloading while obfuscating. | +
-useuniqueclassmembernames |
+Ensure uniform obfuscated class member names for subsequent incremental + obfuscation. |
-dontusemixedcaseclassnames |
+Don't generate mixed-case class names while obfuscating. | +
-keeppackagenames
+ [package_filter] |
+Keep the specified package names from being obfuscated. | +
-flattenpackagehierarchy
+ [package_name] |
+Repackage all packages that are renamed into the single given parent + package. | +
-repackageclasses
+ [package_name] |
+Repackage all class files that are renamed into the single given + package. | +
-keepattributes
+ [attribute_filter] |
+Preserve the given optional attributes; typically
+ Exceptions, InnerClasses,
+ Signature, Deprecated,
+ SourceFile, SourceDir,
+ LineNumberTable,
+ LocalVariableTable, LocalVariableTypeTable,
+ Synthetic, EnclosingMethod, and
+ *Annotation*. |
+
-renamesourcefileattribute
+ [string] |
+Put the given constant string in the SourceFile
+ attributes. |
+
-adaptclassstrings
+ [class_filter] |
+Adapt string constants in the specified classes, based on the obfuscated + names of any corresponding classes. | +
-adaptresourcefilenames
+ [file_filter] |
+Rename the specified resource files, based on the obfuscated names of the + corresponding class files. | +
-adaptresourcefilecontents
+ [file_filter] |
+Update the contents of the specified resource files, based on the + obfuscated names of the processed classes. | +
-dontpreverify |
+Don't preverify the processed class files. | +
-microedition |
+Target the processed class files at Java Micro Edition. | +
-verbose |
+Write out some more information during processing. | +
-dontnote
+ [class_filter] |
+Don't print notes about potential mistakes or omissions in the + configuration. | +
-dontwarn
+ [class_filter] |
+Don't warn about unresolved references at all. | +
-ignorewarnings |
+Print warnings about unresolved references, but continue processing + anyhow. | +
-printconfiguration
+ [filename] |
+Write out the internal structure of the processed class files, to the + standard output or to the given file. | +
-dump
+ [filename] |
+Write out the entire configuration in traditional ProGuard style, to the + standard output or to the given file. | +
+Notes: +
+ +
Keep Options| Keep | +From being removed or renamed | +From being renamed | +
|---|---|---|
| Classes and class members | +-keep |
+-keepnames |
+
| Class members only | +-keepclassmembers |
+-keepclassmembernames |
+
| Classes and class members, if class members present | +-keepclasseswithmembers |
+-keepclasseswithmembernames |
+
+ +
allowshrinking |
+The entry points specified in the keep tag may be shrunk. | +
allowoptimization |
+The entry points specified in the keep tag may be optimized. | +
allowobfuscation |
+The entry points specified in the keep tag may be obfuscated. | +
+ +
+[@annotationtype] [[!]public|final|abstract ...] [!]interface|class classname
+ [extends|implements [@annotationtype] classname]
+[{
+ [@annotationtype] [[!]public|private|protected|static|volatile|transient ...] <fields> |
+ (fieldtype fieldname);
+ [@annotationtype] [[!]public|private|protected|static|synchronized|native|abstract|strictfp ...] <methods> |
+ <init>(argumenttype,...) |
+ classname(argumenttype,...) |
+ (returntype methodname(argumenttype,...));
+ [@annotationtype] [[!]public|private|protected|static ... ] *;
+ ...
+}]
+
++Notes: +
?' for a
+ single character, '*' for any number of characters
+ (but not the package separator), '**' for any number
+ of (any) characters, '%' for any primitive type,
+ '***' for any type, and '...' for any number of arguments..
+?' for a single character and '*'
+ for any number of characters.
++ +
+-printmapping proguard.map + +-renamesourcefileattribute ProGuard +-keepattributes SourceFile,LineNumberTable ++
+
+Now assume the processed application throws an exception, and we have saved the
+stack trace in proguard.trace, shown below. Of course, in real
+life ProGuard rarely throws exceptions, so this is a purposely generated
+exception. :)
+
+
+Exception in thread "main" java.lang.Error: Random exception + at pro.bY.a(ProGuard:576) + at pro.bO.a(ProGuard:431) + at pro.bj.a(ProGuard:145) + at pro.bY.a(ProGuard:522) + at pro.bj.a(ProGuard:129) + at pro.bN.a(ProGuard:125) + at pro.bY.a(ProGuard:251) + at pro.bY.a(ProGuard:229) + at pro.l.a(ProGuard:55) + at pro.bo.b(ProGuard:405) + at pro.ci.a(ProGuard:51) + at pro.bo.a(ProGuard:356) + at pro.be.a(ProGuard:109) + at pro.bo.a(ProGuard:356) + at pro.be.a(ProGuard:186) + at pro.bg.a(ProGuard:369) + at pro.bY.a(ProGuard:286) + at pro.bh.a(ProGuard:55) + at pro.bg.b(ProGuard:408) + at pro.bY.a(ProGuard:190) + at pro.bg.a(ProGuard:369) + at pro.M.a(ProGuard:110) + at pro.bY.a(ProGuard:449) + at pro.M.a(ProGuard:99) + at pro.bo.a(ProGuard:372) + at pro.bY.a(ProGuard:649) + at pro.bY.a(ProGuard:112) + at pro.P.a(ProGuard:66) + at pro.p.a(ProGuard:83) + at pro.bU.a(ProGuard:69) + at pro.bo.a(ProGuard:356) + at pro.J.a(ProGuard:149) + at pro.I.a(ProGuard:49) + at pro.J.a(ProGuard:105) + at pro.cf.c(ProGuard:370) + at pro.cf.a(ProGuard:317) + at pro.bc.a(ProGuard:55) + at proguard.ProGuard.a(ProGuard:363) + at proguard.ProGuard.c(ProGuard:187) + at proguard.ProGuard.b(ProGuard:385) + at proguard.ProGuard.main(ProGuard:429) ++
+ +We can then use the following command to recover the stack trace: +
+java -jar retrace.jar proguard.map proguard.trace ++
+ +The output will look as follows: +
+Exception in thread "main" java.lang.Error: Random exception + at proguard.shrink.UsageMarker.visitInstruction(ProGuard:576) + at proguard.classfile.instruction.GenericInstruction.accept(ProGuard:431) + at proguard.classfile.CodeAttrInfo.instructionsAccept(ProGuard:145) + at proguard.shrink.UsageMarker.visitCodeAttrInfo(ProGuard:522) + at proguard.classfile.CodeAttrInfo.accept(ProGuard:129) + at proguard.classfile.ProgramMemberInfo.attributesAccept(ProGuard:125) + at proguard.shrink.UsageMarker.visitMemberInfo(ProGuard:251) + at proguard.shrink.UsageMarker.visitProgramMethodInfo(ProGuard:229) + at proguard.classfile.ProgramMethodInfo.accept(ProGuard:55) + at proguard.classfile.ProgramClassFile.methodAccept(ProGuard:405) + at proguard.classfile.visitor.NamedMethodVisitor.visitProgramClassFile(ProGuard:51) + at proguard.classfile.ProgramClassFile.accept(ProGuard:356) + at proguard.classfile.visitor.ClassFileUpDownTraveler.visitProgramClassFile(ProGuard:109) + at proguard.classfile.ProgramClassFile.accept(ProGuard:356) + at proguard.classfile.visitor.ClassFileUpDownTraveler.visitLibraryClassFile(ProGuard:186) + at proguard.classfile.LibraryClassFile.accept(ProGuard:369) + at proguard.shrink.UsageMarker.visitLibraryMethodInfo(ProGuard:286) + at proguard.classfile.LibraryMethodInfo.accept(ProGuard:55) + at proguard.classfile.LibraryClassFile.methodsAccept(ProGuard:408) + at proguard.shrink.UsageMarker.visitLibraryClassFile(ProGuard:190) + at proguard.classfile.LibraryClassFile.accept(ProGuard:369) + at proguard.classfile.ClassCpInfo.referencedClassAccept(ProGuard:110) + at proguard.shrink.UsageMarker.visitClassCpInfo(ProGuard:449) + at proguard.classfile.ClassCpInfo.accept(ProGuard:99) + at proguard.classfile.ProgramClassFile.constantPoolEntryAccept(ProGuard:372) + at proguard.shrink.UsageMarker.markCpEntry(ProGuard:649) + at proguard.shrink.UsageMarker.visitProgramClassFile(ProGuard:112) + at proguard.classfile.visitor.VariableClassFileVisitor.visitProgramClassFile(ProGuard:66) + at proguard.classfile.visitor.MultiClassFileVisitor.visitProgramClassFile(ProGuard:83) + at proguard.classfile.visitor.FilteredClassFileVisitor.visitProgramClassFile(ProGuard:69) + at proguard.classfile.ProgramClassFile.accept(ProGuard:356) + at proguard.classfile.ClassPool.classFileAccept(ProGuard:149) + at proguard.classfile.visitor.NamedClassFileVisitor.visitClassPool(ProGuard:49) + at proguard.classfile.ClassPool.accept(ProGuard:105) + at proguard.KeepCommand.executeShrinkingPhase(ProGuard:370) + at proguard.KeepCommand.execute(ProGuard:317) + at proguard.CompoundCommand.execute(ProGuard:55) + at proguard.ProGuard.executeCommands(ProGuard:363) + at proguard.ProGuard.shrink(ProGuard:187) + at proguard.ProGuard.execute(ProGuard:385) + at proguard.ProGuard.main(ProGuard:429) ++ + +
+java -jar retrace.jar -verbose proguard.map proguard.trace ++
+ +The output will then look as follows: +
+Exception in thread "main" java.lang.Error: Random exception + at proguard.shrink.UsageMarker.void visitInstruction(proguard.classfile.ClassFile,proguard.classfile.instruction.Instruction)(ProGuard:576) + at proguard.classfile.instruction.GenericInstruction.void accept(proguard.classfile.ClassFile,proguard.classfile.instruction.InstructionVisitor)(ProGuard:431) + at proguard.classfile.CodeAttrInfo.void instructionsAccept(proguard.classfile.ClassFile,proguard.classfile.instruction.InstructionVisitor)(ProGuard:145) + at proguard.shrink.UsageMarker.void visitCodeAttrInfo(proguard.classfile.ClassFile,proguard.classfile.CodeAttrInfo)(ProGuard:522) + at proguard.classfile.CodeAttrInfo.void accept(proguard.classfile.ClassFile,proguard.classfile.visitor.AttrInfoVisitor)(ProGuard:129) + at proguard.classfile.ProgramMemberInfo.void attributesAccept(proguard.classfile.ProgramClassFile,proguard.classfile.visitor.AttrInfoVisitor)(ProGuard:125) + at proguard.shrink.UsageMarker.void visitMemberInfo(proguard.classfile.ProgramClassFile,proguard.classfile.ProgramMemberInfo)(ProGuard:251) + at proguard.shrink.UsageMarker.void visitProgramMethodInfo(proguard.classfile.ProgramClassFile,proguard.classfile.ProgramMethodInfo)(ProGuard:229) + at proguard.classfile.ProgramMethodInfo.void accept(proguard.classfile.ProgramClassFile,proguard.classfile.visitor.MemberInfoVisitor)(ProGuard:55) + at proguard.classfile.ProgramClassFile.void methodAccept(proguard.classfile.visitor.MemberInfoVisitor,java.lang.String,java.lang.String)(ProGuard:405) + at proguard.classfile.visitor.NamedMethodVisitor.void visitProgramClassFile(proguard.classfile.ProgramClassFile)(ProGuard:51) + at proguard.classfile.ProgramClassFile.void accept(proguard.classfile.visitor.ClassFileVisitor)(ProGuard:356) + at proguard.classfile.visitor.ClassFileUpDownTraveler.void visitProgramClassFile(proguard.classfile.ProgramClassFile)(ProGuard:109) + at proguard.classfile.ProgramClassFile.void accept(proguard.classfile.visitor.ClassFileVisitor)(ProGuard:356) + at proguard.classfile.visitor.ClassFileUpDownTraveler.void visitLibraryClassFile(proguard.classfile.LibraryClassFile)(ProGuard:186) + at proguard.classfile.LibraryClassFile.void accept(proguard.classfile.visitor.ClassFileVisitor)(ProGuard:369) + at proguard.shrink.UsageMarker.void visitLibraryMethodInfo(proguard.classfile.LibraryClassFile,proguard.classfile.LibraryMethodInfo)(ProGuard:286) + at proguard.classfile.LibraryMethodInfo.void accept(proguard.classfile.LibraryClassFile,proguard.classfile.visitor.MemberInfoVisitor)(ProGuard:55) + at proguard.classfile.LibraryClassFile.void methodsAccept(proguard.classfile.visitor.MemberInfoVisitor)(ProGuard:408) + at proguard.shrink.UsageMarker.void visitLibraryClassFile(proguard.classfile.LibraryClassFile)(ProGuard:190) + at proguard.classfile.LibraryClassFile.void accept(proguard.classfile.visitor.ClassFileVisitor)(ProGuard:369) + at proguard.classfile.ClassCpInfo.void referencedClassAccept(proguard.classfile.visitor.ClassFileVisitor)(ProGuard:110) + at proguard.shrink.UsageMarker.void visitClassCpInfo(proguard.classfile.ClassFile,proguard.classfile.ClassCpInfo)(ProGuard:449) + at proguard.classfile.ClassCpInfo.void accept(proguard.classfile.ClassFile,proguard.classfile.visitor.CpInfoVisitor)(ProGuard:99) + at proguard.classfile.ProgramClassFile.void constantPoolEntryAccept(proguard.classfile.visitor.CpInfoVisitor,int)(ProGuard:372) + at proguard.shrink.UsageMarker.void markCpEntry(proguard.classfile.ClassFile,int)(ProGuard:649) + at proguard.shrink.UsageMarker.void visitProgramClassFile(proguard.classfile.ProgramClassFile)(ProGuard:112) + at proguard.classfile.visitor.VariableClassFileVisitor.void visitProgramClassFile(proguard.classfile.ProgramClassFile)(ProGuard:66) + at proguard.classfile.visitor.MultiClassFileVisitor.void visitProgramClassFile(proguard.classfile.ProgramClassFile)(ProGuard:83) + at proguard.classfile.visitor.FilteredClassFileVisitor.void visitProgramClassFile(proguard.classfile.ProgramClassFile)(ProGuard:69) + at proguard.classfile.ProgramClassFile.void accept(proguard.classfile.visitor.ClassFileVisitor)(ProGuard:356) + at proguard.classfile.ClassPool.void classFileAccept(proguard.classfile.visitor.ClassFileVisitor,java.lang.String)(ProGuard:149) + at proguard.classfile.visitor.NamedClassFileVisitor.void visitClassPool(proguard.classfile.ClassPool)(ProGuard:49) + at proguard.classfile.ClassPool.void accept(proguard.classfile.visitor.ClassPoolVisitor)(ProGuard:105) + at proguard.KeepCommand.void executeShrinkingPhase(proguard.classfile.ClassPool,proguard.classfile.ClassPool)(ProGuard:370) + at proguard.KeepCommand.void execute(int,proguard.classfile.ClassPool,proguard.classfile.ClassPool)(ProGuard:317) + at proguard.CompoundCommand.void execute(int,proguard.classfile.ClassPool,proguard.classfile.ClassPool)(ProGuard:55) + at proguard.ProGuard.void executeCommands(int)(ProGuard:363) + at proguard.ProGuard.void shrink()(ProGuard:187) + at proguard.ProGuard.void execute(java.lang.String[])(ProGuard:385) + at proguard.ProGuard.void main(java.lang.String[])(ProGuard:429) ++ + + +
+-printmapping proguard.map ++
+
+A stack trace proguard.trace will then lack line number
+information:
+
+Exception in thread "main" java.lang.Error: Random exception + at pro.bY.a(Unknown Source) + at pro.bO.a(Unknown Source) + at pro.bj.a(Unknown Source) + at pro.bY.a(Unknown Source) + at pro.bj.a(Unknown Source) + at pro.bN.a(Unknown Source) + at pro.bY.a(Unknown Source) + at pro.bY.a(Unknown Source) + at pro.l.a(Unknown Source) + at pro.bo.b(Unknown Source) + at pro.ci.a(Unknown Source) + at pro.bo.a(Unknown Source) + at pro.be.a(Unknown Source) + at pro.bo.a(Unknown Source) + at pro.be.a(Unknown Source) + at pro.bg.a(Unknown Source) + at pro.bY.a(Unknown Source) + at pro.bh.a(Unknown Source) + at pro.bg.b(Unknown Source) + at pro.bY.a(Unknown Source) + at pro.bg.a(Unknown Source) + at pro.M.a(Unknown Source) + at pro.bY.a(Unknown Source) + at pro.M.a(Unknown Source) + at pro.bo.a(Unknown Source) + at pro.bY.a(Unknown Source) + at pro.bY.a(Unknown Source) + at pro.P.a(Unknown Source) + at pro.p.a(Unknown Source) + at pro.bU.a(Unknown Source) + at pro.bo.a(Unknown Source) + at pro.J.a(Unknown Source) + at pro.I.a(Unknown Source) + at pro.J.a(Unknown Source) + at pro.cf.c(Unknown Source) + at pro.cf.a(Unknown Source) + at pro.bc.a(Unknown Source) + at proguard.ProGuard.a(Unknown Source) + at proguard.ProGuard.c(Unknown Source) + at proguard.ProGuard.b(Unknown Source) + at proguard.ProGuard.main(Unknown Source) ++
+ +We can still use the same command to recover the stack trace: +
+java -jar retrace.jar proguard.map proguard.trace ++
+ +The output will now give a list of alternative original method names for each +ambiguous obfuscated method name: +
+Exception in thread "main" java.lang.Error: Random exception + at proguard.shrink.UsageMarker.visitProgramClassFile(Unknown Source) + visitLibraryClassFile + visitProgramFieldInfo + visitProgramMethodInfo + visitMemberInfo + visitLibraryFieldInfo + visitLibraryMethodInfo + visitIntegerCpInfo + visitLongCpInfo + visitFloatCpInfo + visitDoubleCpInfo + visitStringCpInfo + visitUtf8CpInfo + visitFieldrefCpInfo + visitInterfaceMethodrefCpInfo + visitMethodrefCpInfo + visitClassCpInfo + visitNameAndTypeCpInfo + visitUnknownAttrInfo + visitInnerClassesAttrInfo + visitConstantValueAttrInfo + visitExceptionsAttrInfo + visitCodeAttrInfo + visitLineNumberTableAttrInfo + visitLocalVariableTableAttrInfo + visitSourceFileAttrInfo + visitDeprecatedAttrInfo + visitSyntheticAttrInfo + visitInstruction + visitCpInstruction + visitExceptionInfo + visitInnerClassesInfo + visitLocalVariableInfo + markCpEntry + markAsUnused + isUsed + at proguard.classfile.instruction.GenericInstruction.create(Unknown Source) + isWide + getLength + accept + at proguard.classfile.CodeAttrInfo.getAttribute(Unknown Source) + getAttrInfoLength + readInfo + accept + instructionsAccept + exceptionsAccept + [...] + at proguard.KeepCommand.executeShrinkingPhase(Unknown Source) + access$100 + at proguard.KeepCommand.keepField(Unknown Source) + ensureMultiClassFileVisitorForMembers + execute + executeObfuscationPhase + access$002 + access$000 + access$102 + access$108 + at proguard.CompoundCommand.addCommand(Unknown Source) + execute + at proguard.ProGuard.readCommands(Unknown Source) + obfuscate + executeCommands + at proguard.ProGuard.shrink(Unknown Source) + at proguard.ProGuard.check(Unknown Source) + execute + at proguard.ProGuard.main(Unknown Source) ++
+ +
+When an obfuscated program throws an exception, the resulting stack trace +typically isn't very informative. Class names and method names have been +replaced by short meaningless strings. Source file names and line numbers are +missing altogether. While this may be intentional, it can also be inconvenient +when debugging problems. +
+ +
| Original code | +- ProGuard → | +Obfuscated code | +
| + | ↓ | +↓ | +
| Mapping file | +↓ | +|
| ↓ | +↓ | +|
| Readable stack trace | +← ReTrace - | +Obfuscated stack trace | +
+ReTrace can read an obfuscated stack trace and restore it to what it would +look like without obfuscation. The restoration is based on the mapping file +that ProGuard can write out during obfuscation. The mapping file links the +original class names and class member names to their obfuscated names. +
+ +
lib directory of the
+ProGuard distribution. To run ReTrace, just type:
++
+java -jar retrace.jar [options...]
+ mapping_file [stacktrace_file]
+
-printmapping mapping_file",
+ while obfuscating the application that produced the stack trace.-verbose-regex regular_expression+ (?:\s*%c:.*)|(?:\s*at\s+%c.%m\s*\(.*?(?::%l)?\)\s*) ++ The regular expression is a Java regular expression (cfr. the documentation + of
java.util.regex.Pattern), with a few additional wildcards:
+ %c |
+ matches a class name (e.g.
+ "myapplication.MyClass"). |
%C |
+ matches a class name with slashes (e.g.
+ "myapplication/MyClass"). |
%t |
+ matches a field type or method return type (e.g.
+ "myapplication.MyClass[]"). |
%f |
+ matches a field name (e.g.
+ "myField"). |
%m |
+ matches a method name (e.g.
+ "myMethod"). |
%a |
+ matches a list of method arguments (e.g.
+ "boolean,int"). |
%l |
+ matches a line number inside a method (e.g.
+ "123"). |
(?:...)
+ + +
+ +Preserving line number tables is explained in detail in this example in the ProGuard User Manual. +
+ +Unobfuscated elements and obfuscated elements for which no mapping is available +will be left unchanged. +
+ +
+
+ + + + + + + + +
+
+
+
+
+
+
+
(MyClass)Class.forName(variable).newInstance()".
+ Depending on your application, you may need to keep the mentioned classes
+ with an option like "-keep class MyClass", or their
+ implementations with an option like "-keep class * implements
+ MyClass". You can switch off these notes by specifying the
+ -dontnote option..getField("myField")". Depending on your application, you
+ may need to figure out where the mentioned class members are defined and
+ keep them with an option like "-keep class MyClass { MyFieldType
+ myField; }". Otherwise, ProGuard might remove or obfuscate the
+ class members, since it can't know which ones they are exactly. It does
+ list possible candidates, for your information. You can switch off these
+ notes by specifying the -dontnote option.-keep option to preserve the
+ given method (or field), but no -keep option for the given
+ class that is an argument type or return type in the method's descriptor.
+ You may then want to keep the class too. Otherwise, ProGuard will
+ obfuscate its name, thus changing the method's signature. The method might
+ then become unfindable as an entry point, e.g. if it is part of a public
+ API. You can switch off these notes by specifying the -dontnote option.-dontnote option.+ +ProGuard may terminate when it encounters parsing errors or I/O errors, or +some more serious warnings: + +
-libraryjars option.
+
+ If the class that is reported as missing is a non-public library class,
+ you should specify the -dontskipnonpubliclibraryclasses
+ option. Common examples are the classes
+ javax.swing.TransferHandler$HasGetTransferHandler and
+ java.util.zip.ZipConstants, which are used as interfaces in
+ some public classes, even though they are only package visible. This
+ option is not set by default for reasons of efficiency. Setting it increases
+ the processing time a bit, but it won't hurt the output in any way.
+
+ If you're missing a library and you're absolutely sure it isn't used
+ anyway, you can try your luck with the -ignorewarnings option,
+ or even the -dontwarn
+ option. Only use these options if you really know what you're doing
+ though.
+ If the class member that is reported as missing is actually implemented in
+ a non-public library class, you should specify the
+ -dontskipnonpubliclibraryclasses option. A common example is
+ the method setLength(int) in the public class
+ java.lang.StringBuilder. This method is actually defined in
+ the package visible superclass
+ java.lang.AbstractStringBuilder, which ProGuard ignores by
+ default.
+
+ If your program classes reside in the same packages as library classes,
+ and refer to their package visible class members, then you should specify
+ the -dontskipnonpubliclibraryclassmembers
+ option.
WEB-INF/classes directory in a war should be packaged
+ in a jar and put in the WEB-INF/lib directory. If you don't
+ mind these classes not being written to the output, you can specify the -ignorewarnings option,
+ or even the -dontwarn
+ option.-keep option in the
+ configuration, and the mapping file, in the obfuscation step. The given
+ class name or class member name can't be kept by its original name, as
+ specified in the configuration, but it has to be mapped to the other given
+ name, as specified in the mapping file. You should adapt your
+ configuration or your mapping file to remove the conflict. Alternatively,
+ if you're sure the renaming won't hurt, you can specify the -ignorewarnings option,
+ or even the -dontwarn
+ option.-ignorewarnings option,
+ or even the -dontwarn
+ option. Note that you should always use the -useuniqueclassmembernames
+ option in the initial obfuscation step, in order to reduce the risk of
+ conflicts.-keep options, or you mistyped the
+ class names. ProGuard has to know exactly what you want to keep: an
+ application, an applet, a servlet, a midlet,..., or any combination of
+ these. Without the proper seed specifications, ProGuard would shrink,
+ optimize, or obfuscate all class files away.<java.home>/lib/rt.jar by
+ <java.home>/../Classes/classes.jar.+ +Should ProGuard crash while processing your application: + +
-Xms and -Xmx options). You can also
+ reduce the amount of memory that ProGuard needs by removing unnecessary
+ library jars from your configuration, or by filtering out unused library
+ packages and classes. Remember that only classes or interfaces that are
+ extended or implemented by classes in your input jars are required.-Xss option)
+ should help too. In practice however, the -Xss setting
+ doesn't have any effect on the main thread, due to Sun Bug
+ #4362291. As a result, this solution will only work when running
+ ProGuard in a different thread, e.g. from its GUI.-dontoptimize option. In
+ any case, please report the problem, preferably with the simplest example
+ that causes ProGuard to crash.-dontusemixedcaseclassnames
+ option.
+
+ Also, you should make sure your class files are in directories that
+ correspond to their package names. ProGuard will read misplaced class
+ files, but it will currently not write their processed versions. Notably,
+ class files that are in the WEB-INF/classes directory in a
+ war should be packaged in a jar and put in the WEB-INF/lib
+ directory.
-printseeds option to see
+ which elements are being kept exactly.
+ + If you are using marker interfaces to keep other classes, the marker + interfaces themselves are probably being removed in the shrinking step. + You should therefore always explicitly keep any marker interfaces.
LocalVariableTable or LocalVariableTypeTable
+ attributes.preverify tool always unpacks the jars, so class files with
+ similar lower-case and upper-case names overwrite each other. You can use
+ ProGuard's -dontusemixedcaseclassnames
+ option to work around this problem.
+
+ If the above doesn't help, there is probably a bug in the optimization
+ step of ProGuard. Make sure you are using the latest version. You should
+ be able to work around the problem by using the -dontoptimize option. You
+ can check the bug database to see if it is a known problem (often with a
+ fix). Otherwise, please report it, preferably with the simplest example on
+ which you can find ProGuard to fail.
-microedition option,
+ProGuard will preverify the class files for Java Micro Edition.
+Class.forName, trying to create
+ the missing class dynamically. ProGuard can only detect constant name
+ arguments, like Class.forName("mypackage.MyClass"). For
+ variable name arguments like Class.forName(someClass), you
+ have to keep all possible classes using the appropriate -keep option, e.g. "-keep
+ class mypackage.MyClass" or "-keep class * implements
+ mypackage.MyInterface".myClass.getMethod, trying to find some method dynamically.
+ Since ProGuard isn't detecting this (yet), you have to keep the missing
+ method in using the appropriate -keep option, e.g. "-keep
+ class mypackage.MyClass { void myMethod(); }".-adaptresourcefilenames
+ and/or -adaptresourcefilecontents.
+
+ Furthermore, directory entries in jar files aren't copied, unless you
+ specify the option -keepdirectories.
-jar instead of the option -classpath. The java
+ virtual machine returns with this error message if your jar doesn't
+ contain a manifest file (META-INF/MANIFEST.MF), if the
+ manifest file doesn't specify a main class (Main-Class: ...),
+ or if the jar doesn't contain this main class. You should then make sure
+ that the input jar contains a valid manifest file to start with, that this
+ manifest file is the one that is copied (the first manifest file that is
+ encountered), and that the main class is kept in your configuration,-microedition option, so
+ the processed class files are preverified properly.-repackageclasses
+ '' and -overloadaggressively.
+ If you're using the JME WTK plugin, you can adapt the configuration
+ proguard/wtk/default.pro that's inside the
+ proguard.jar.-useuniqueclassmembernames
+ option. It avoids overloading class member names, which triggers a bug in
+ their java virtual machine.
+
+ You might also try using the -dontusemixedcaseclassnames
+ option. Even if the midlet has been properly processed and then
+ preverified on a case-sensitive file system, the device itself might not
+ like the mixed-case class names. Notably, the Nokia N-Gage emulator works
+ fine, but the actual device seems to exhibit this problem.
volatile. If this is not
+ possible for some reason, you'll have to switch off optimization using the
+ -dontoptimize
+ option.-overloadaggressively
+ option. This option triggers a bug in
+ sun.tools.java.MethodSet.add in Sun's JDK 1.2.2, which is
+ used for (dynamic) compilation. You should then avoid this option.-overloadaggressively
+ option. You should then use the same option again in the second processing
+ round.+ Furthermore, you should check whether you have specified your program jars + and library jars properly. Program classes can refer to library classes, + but not the other way around. +
+ If all of this seems ok, perhaps there's a bug in ProGuard (gasp!). If so, + please report it, preferably with the simplest example on which you can + find ProGuard to fail.
-dontoptimize
+ option. You can check the bug database to see if it is a known problem
+ (often with a fix). Otherwise, please report it, preferably with the
+ simplest example on which ProGuard fails.
+java -jar proguard.jar options ...
+
lib directory of the
+ProGuard distribution. Options can also be put in one or more configuration
+files. Typically, you'll put most options in a configuration file (say,
+myconfig.pro), and just call:
+
+java -jar proguard.jar @myconfig.pro
+
+java -jar proguard.jar @myconfig.pro -verbose
+
+In a configuration file, a # sign and all remaining
+characters on that line are ignored, allowing you to add comments.
+
+Extra whitespace between words and delimiters is ignored. To specify file +names with spaces or special characters, words can be quoted with single or +double quotes. Note that the quotes may need to be escaped when used on the +command line, to avoid them being gobbled by the shell. +
+Options can be grouped arbitrarily in arguments on the command line and in +lines in configuration files. This means that you can quote any arbitrary +section of command line options, to avoid shell expansion of special +characters, for instance. +
+The order of the options is generally irrelevant. They can be abbreviated to +their first unique characters. +
+ +The sections below provide more details: +
Keep Options
+@filename-include
+ filename'.-include
+ filename-basedirectory
+ directoryname-injars
+ class_path-injars options.-outjars
+ class_path-injars
+ options will be written to the named jars. This allows you to collect the
+ contents of groups of input jars into corresponding groups of output jars.
+ In addition, the output entries can be filtered, as explained in
+ the filters section. Each processed class file
+ or resource file is then written to the first output entry with a matching
+ filter, within the group of output jars.
+
+ You must avoid letting the output files overwrite any input files. For
+ better readability, class path entries can be specified using multiple
+ -outjars options. Without any -outjars options,
+ no jars will be written.
-libraryjars
+ class_path-libraryjars options.
+ + Please note that the boot path and the class path set for running ProGuard + are not considered when looking for library classes. This means that you + explicitly have to specify the run-time jar that your code will use. + Although this may seem cumbersome, it allows you to process applications + targeted at different run-time environments. For example, you can process + J2SE applications as well as JME midlets, just by specifying the + appropriate run-time jar.
-dontskipnonpubliclibraryclasses-dontskipnonpubliclibraryclassmembers-keepdirectories
+ [directory_filter]MyClass.class.getResource("")". If the
+ option is specified without a filter, all directories are kept. With a
+ filter, only matching directories are kept.-target version1.0, 1.1,
+ 1.2, 1.3, 1.4, 1.5 (or
+ just 5), or 1.6 (or just 6). By
+ default, the version numbers of the class files are left unchanged. For
+ example, you may want to upgrade class
+ files to Java 6, by changing their version numbers and having them
+ preverified.-forceprocessing-keep
+ [,modifier,...]
+ class_specification-keepclassmembers
+ [,modifier,...]
+ class_specificationSerializable
+ interface.-keepclasseswithmembers
+ [,modifier,...]
+ class_specification-keepnames
+ class_specification-keep,allowshrinking
+ class_specification
+
+ Specifies classes and class members whose names are to be preserved, if
+ they aren't removed in the shrinking phase. For example, you may want to
+ keep all class names of classes
+ that implement the Serializable interface, so that the
+ processed code remains compatible with any originally serialized classes.
+ Classes that aren't used at all can still be removed. Only applicable when
+ obfuscating.
-keepclassmembernames
+ class_specification-keepclassmembers,allowshrinking
+ class_specification
+
+ Specifies class members whose names are to be preserved, if they aren't
+ removed in the shrinking phase. For example, you may want to preserve the
+ name of the synthetic class$ methods when processing a library, so obfuscators can
+ detect it again when processing an application that uses the processed
+ library (although ProGuard itself doesn't need this). Only applicable when
+ obfuscating.
-keepclasseswithmembernames
+ class_specification-keepclasseswithmembers,allowshrinking
+ class_specification
+ + Specifies classes and class members whose names are to be preserved, on + the condition that all of the specified class members are present after + the shrinking phase. For example, you may want to keep all native method names and the names + of their classes, so that the processed code can still link with the + native library code. Native methods that aren't used at all can still be + removed. If a class file is used, but none of its native methods are, its + name will still be obfuscated. Only applicable when obfuscating.
-printseeds
+ [filename]-keep options. The list is printed to the standard
+ output or to the given file. The list can be useful to verify if the
+ intended class members are really found, especially if you're using
+ wildcards. For example, you may want to list all the applications or all the applets that you are keeping.-dontshrink-keep options, and the ones on which
+ they depend, directly or indirectly. A shrinking step is also applied
+ after each optimization step, since some optimizations may open the
+ possibility to remove more classes and class members.-printusage
+ [filename]-whyareyoukeeping
+ class_specification-verbose option if specified, the traces
+ include full field and method signatures. Only applicable when
+ shrinking.-dontoptimize-optimizations
+ optimization_filter-optimizationpasses n-assumenosideeffects
+ class_specificationSystem.currentTimeMillis(), so that any idle calls to it will
+ be removed. Note that ProGuard applies the option to the entire hierarchy
+ of the specified methods. Only applicable when optimizing. In general,
+ making assumptions can be dangerous; you can easily break the processed
+ code. Only use this option if you know what you're doing!-allowaccessmodification-repackageclasses option).
+ + Counter-indication: you probably shouldn't use this option when + processing code that is to be used as a library, since classes and class + members that weren't designed to be public in the API may become + public.
-mergeinterfacesaggressively+ Counter-indication: setting this option can reduce the performance + of the processed code on some JVMs, since advanced just-in-time + compilation tends to favor more interfaces with fewer implementing + classes. Worse, some JVMs may not be able to handle the resulting code. + Notably: +
InternalError when
+ encountering more than 256 Miranda methods (interface methods
+ without implementations) in a class.
+ -dontobfuscate-keep options.
+ Internal attributes that are useful for debugging, such as source files
+ names, variable names, and line numbers are removed.-printmapping
+ [filename]-applymapping
+ filename-useuniqueclassmembernames.
+ Only applicable when obfuscating.-obfuscationdictionary
+ filename# sign are ignored. Note that an
+ obfuscation dictionary hardly improves the obfuscation. Decent compilers
+ can automatically replace them, and the effect can fairly simply be undone
+ by obfuscating again with simpler names. The most useful application is
+ specifying strings that are typically already present in class files (such
+ as 'Code'), thus reducing the class file sizes just a little bit more.
+ Only applicable when obfuscating.-classobfuscationdictionary
+ filename-obfuscationdictionary.
+ Only applicable when obfuscating.-packageobfuscationdictionary
+ filename-obfuscationdictionary.
+ Only applicable when obfuscating.-overloadaggressively+ Counter-indication: the resulting class files fall within the Java + bytecode specification (cfr. The Java Virtual Machine Specification, Second Edition, first + paragraphs of Section 4.5 and Section 4.6), even though this kind of overloading is not allowed in + the Java language (cfr. The Java Language Specification, Second Edition, Section 8.3 and Section 8.4.7). Still, some tools have problems with it. Notably: +
javac compiler produces an exception when
+ compiling with such a library (cfr. Bug
+ #4216736). You probably shouldn't use this option for processing
+ libraries.
+ pack200 tool reportedly has problems with
+ overloaded class members.
+ -useuniqueclassmembernames+ For instance, consider two distinct interfaces containing methods with the + same name and signature. Without this option, these methods may get + different obfuscated names in a first obfuscation step. If a patch is then + added containing a class that implements both interfaces, ProGuard will + have to enforce the same method name for both methods in an incremental + obfuscation step. The original obfuscated code is changed, in order to + keep the resulting code consistent. With this option in the initial + obfuscation step, such renaming will never be necessary. +
+ This option is only applicable when obfuscating. In fact, if you are + planning on performing incremental obfuscation, you probably want to avoid + shrinking and optimization altogether, since these steps could remove or + modify parts of your code that are essential for later additions.
-dontusemixedcaseclassnames-keeppackagenames
+ [package_filter]-flattenpackagehierarchy
+ [package_name]-repackageclasses
+ [package_name]-flattenpackagehierarchy
+ option. It is another example of further obfuscating package names. It can
+ make the processed code even smaller and less comprehensible. Its
+ deprecated name is -defaultpackage. Only applicable when
+ obfuscating.
+ + Counter-indication: classes that look for resource files in their + package directories will no longer work properly if they are moved + elsewhere. When in doubt, just leave the packaging untouched by not using + this option.
-keepattributes
+ [attribute_filter]-keepattributes directives. The
+ optional filter is a comma-separated list of attribute names. Attribute
+ names can contain ?, *, and ** wildcards, and they
+ can be preceded by the ! negator. Typical optional attributes are
+ Exceptions, Signature, Deprecated,
+ SourceFile, SourceDir,
+ LineNumberTable, LocalVariableTable,
+ LocalVariableTypeTable, Synthetic,
+ EnclosingMethod, RuntimeVisibleAnnotations,
+ RuntimeInvisibleAnnotations,
+ RuntimeVisibleParameterAnnotations,
+ RuntimeInvisibleParameterAnnotations, and
+ AnnotationDefault. The InnerClasses attribute
+ name can be specified as well, referring to the source name part of this
+ attribute. For example, you should at least keep the
+ Exceptions, InnerClasses, and
+ Signature attributes when processing a library. As another example,
+ you should keep the SourceFile and
+ LineNumberTable attributes for producing useful obfuscated stack
+ traces. Only applicable when obfuscating.-renamesourcefileattribute
+ [string]SourceFile
+ attributes (and SourceDir attributes) of the class files.
+ Note that the attribute has to be present to start with, so it also has to
+ be preserved explicitly using the -keepattributes directive.
+ For example, you may want to have your processed libraries and
+ applications produce useful obfuscated
+ stack traces. Only applicable when obfuscating.-adaptclassstrings
+ [class_filter]-adaptresourcefilenames
+ [file_filter]-adaptresourcefilecontents
+ [file_filter]LANG or the Java system
+ property file.encoding. For an example,
+ see processing resource files.
+ Only applicable when obfuscating.-dontpreverify-microedition-verbose-dontnote
+ [class_filter]-dontwarn
+ [class_filter]-ignorewarnings-printconfiguration
+ [filename]-dump
+ [filename]+Each input entry can be: +
+The paths of directly specified class files and resource files is ignored, so +class files should generally be part of a jar file, a war file, an ear file, a +zip file, or a directory. In addition, the paths of class files should not have +any additional directory prefixes inside the archives or directories. + +
+Each output entry can be: +
+When writing output entries, ProGuard will generally package the results in a +sensible way, reconstructing the input entries as much as required. Writing +everything to an output directory is the most straightforward option: the +output directory will contain a complete reconstruction of the input entries. +The packaging can be almost arbitrarily complex though: you could process an +entire application, packaged in a zip file along with its documentation, +writing it out as a zip file again. The Examples section shows a few ways +to restructure output archives. +
+Files and directories can be specified as discussed in the section on file names below. +
+In addition, ProGuard provides the possibility to filter the class path +entries and their contents, based on their full relative file names. Each +class path entry can be followed by up to 5 types of file filters between parentheses, separated by +semi-colons: +
+If fewer than 5 filters are specified, they are assumed to be the latter +filters. Any empty filters are ignored. More formally, a filtered class path +entry looks like this: +
+classpathentry([[[[zipfilter;]earfilter;]warfilter;]jarfilter;]filefilter) ++
+Square brackets "[]" mean that their contents are optional. +
+For example, "rt.jar(java/**.class,javax/**.class)" matches all
+class files in the java and javax directories inside
+the rt jar.
+
+For example, "input.jar(!**.gif,images/**)" matches all files in
+the images directory inside the input jar, except
+gif files.
+
+Note that the different filters are applied to all corresponding file types, +irrespective of their nesting levels in the input; they are orthogonal. +
+For example,
+"input.war(lib/**.jar,support/**.jar;**.class,**.gif)" only
+considers jar files in the lib and support
+directories in the input war, not any other jar files. It then
+matches all class files and gif files that are encountered.
+
+The filters allow for an almost infinite number of packaging and repackaging +possibilities. The Examples section provides a few more examples +for filtering input and output. +
+The names can contain Java system properties delimited by '<' and +'>'. The system properties +are automatically replaced by their respective values. +
+For example, <java.home>/lib/rt.jar will automatically be
+expanded to something like /usr/local/java/jdk/jre/lib/rt.jar.
+Similarly, <user.home> will be expanded to the user's home
+directory, and <user.dir> will be expanded to the current
+working directory.
+
+Names with special characters like spaces and parentheses must be quoted with +single or double quotes. Note that each file name in a list of names has to be +quoted individually. Also note that the quotes themselves may need to be +escaped when used on the command line, to avoid them being gobbled by the +shell. +
+For example, on the command line, you could use an option like '-injars
+"my program.jar":"/your directory/your program.jar"'.
+
? |
+ matches any single character in a file name. |
* |
+ matches any part of a filename not containing the directory + separator. |
** |
+ matches any part of a filename, possibly containing any number of + directory separators. |
java/**.class,javax/**.class" matches all
+class files in the java and javax.
++ +Furthermore, a file name can be preceded by an exclamation mark '!' to +exclude the file name from further attempts to match with +subsequent file names. +
+For example, "!**.gif,images/**" matches all files in the
+images directory, except gif files.
+
+The Examples section provides a few more examples for filtering input and output. + + +
+A filter is a list of comma-separated names that can contain wildcards. Only +names that match an item on the list pass the filter. The supported wildcards +depend on the type of names for which the filter is being used, but the +following wildcards are typical: + +
? |
+ matches any single character in a name. |
* |
+ matches any part of a name not containing the package separator or + directory separator. |
** |
+ matches any part of a name, possibly containing any number of + package separators or directory separators. |
foo,*bar" matches the name foo and
+all names ending with bar.
++ +Furthermore, a name can be preceded by a negating exclamation mark '!' +to exclude the name from further attempts to match +with subsequent names. So, if a name matches an item in the filter, it +is accepted or rejected right away, depending on whether the item has a +negator. If the name doesn't match the item, it is tested against the next +item, and so on. It if doesn't match any items, it is accepted or rejected, +depending on the whether the last item has a negator or not. +
+For example, "!foobar,*bar" matches all names ending with
+bar, except foobar.
+
Keep Options-keep options for shrinking and obfuscation may seem
+a bit confusing at first, but there's actually a pattern behind them. The
+following table summarizes how they are related:
++ +
| Keep | +From being removed or renamed | +From being renamed | +
|---|---|---|
| Classes and class members | +-keep |
+-keepnames |
+
| Class members only | +-keepclassmembers |
+-keepclassmembernames |
+
| Classes and class members, if class members present | +-keepclasseswithmembers |
+-keepclasseswithmembernames |
+
+
+Each of these -keep options is of course followed by a
+specification of the classes and class
+members (fields and methods) to which it should be applied.
+
+If you're not sure which option you need, you should probably simply use
+-keep. It will make sure the specified classes and class members
+are not removed in the shrinking step, and not renamed in the obfuscation step.
+
+
+
+ |
+Always remember:
+
|
allowshrinkingallowoptimizationallowobfuscation-keep options and in the
+-assumenosideeffects option. The corresponding option is only
+applied to classes and class members that match the template.
++The template was designed to look very Java-like, with some extensions for +wildcards. To get a feel for the syntax, you should probably look at the examples, but this is an attempt at a complete formal +definition: +
+ +
+[@annotationtype] [[!]public|final|abstract|@ ...] [!]interface|class|enum classname
+ [extends|implements [@annotationtype] classname]
+[{
+ [@annotationtype] [[!]public|private|protected|static|volatile|transient ...] <fields> |
+ (fieldtype fieldname);
+ [@annotationtype] [[!]public|private|protected|static|synchronized|native|abstract|strictfp ...] <methods> |
+ <init>(argumenttype,...) |
+ classname(argumenttype,...) |
+ (returntype methodname(argumenttype,...));
+ [@annotationtype] [[!]public|private|protected|static ... ] *;
+ ...
+}]
+
++Square brackets "[]" mean that their contents are optional. Ellipsis dots +"..." mean that any number of the preceding items may be specified. A vertical +bar "|" delimits two alternatives. Non-bold parentheses "()" just group parts +of the specification that belong together. The indentation tries to clarify +the intended meaning, but white-space is irrelevant in actual configuration +files. +
+
class keyword refers to any interface or class.
+ The interface keyword restricts matches to interface
+ classes. The enum keyword restricts matches to
+ enumeration classes. Preceding the interface or
+ enum keywords by a ! restricts
+ matches to classes that are not interfaces or enumerations, respectively.
+ + +
java.lang.String. Class names may be specified as regular
+ expressions containing the following wildcards:
+
+? |
+
+matches any single character in a class name, but not the package
+ separator. For example, "mypackage.Test?" matches
+ "mypackage.Test1" and "mypackage.Test2", but not
+ "mypackage.Test12". |
* |
+
+matches any part of a class name not containing the package separator. For
+ example, "mypackage.*Test*" matches
+ "mypackage.Test" and
+ "mypackage.YourTestApplication", but not
+ "mypackage.mysubpackage.MyTest". Or, more generally,
+ "mypackage.*" matches all classes in
+ "mypackage", but not in its subpackages. |
** |
+
+matches any part of a class name, possibly containing any number of
+ package separators. For example, "**.Test" matches all
+ Test classes in all packages except the root package. Or,
+ "mypackage.**" matches all classes in
+ "mypackage" and in its subpackages. |
! negators, just
+ like file name filters. This notation doesn't look very Java-like, so it
+ should be used with moderation.
+
+ For convenience and for backward compatibility, the class name
+ * refers to any class, irrespective of its package.
+
+ +
extends and implements
+ specifications are typically used to restrict classes with wildcards. They
+ are currently equivalent, specifying that only classes extending or
+ implementing the given class qualify. Note that the given class itself is
+ not included in this set. If required, it should be specified in a
+ separate option.
+ + +
@ specifications can be used to restrict classes
+ and class members to the ones that are annotated with the specified
+ annotation types. An annotationtype is specified just like a
+ classname.
+ + +
javadoc and javap). The specifications can
+ also contain the following catch-all wildcards:
+
+<init> |
+matches any constructor. |
<fields> |
+matches any field. |
<methods> |
+matches any method. |
* |
+matches any field or method. |
<init> wildcard has an argument list.
+ + + Fields and methods may also be specified using regular expressions. Names + can contain the following wildcards: + +
? |
+ matches any single character in a method name. |
* |
+ matches any part of a method name. |
% |
+ matches any primitive type ("boolean", "int",
+ etc, but not "void"). |
? |
+ matches any single character in a class name. |
* |
+ matches any part of a class name not containing the package separator. |
** |
+ matches any part of a class name, possibly containing any number of + package separators. |
*** |
+ matches any type (primitive or non-primitive, array or + non-array). |
... |
+ matches any number of arguments of any type. |
?, *, and **
+ wildcards will never match primitive types. Furthermore, only the
+ *** wildcards will match array types of any dimension. For
+ example, "** get*()" matches "java.lang.Object
+ getObject()", but not "float getFloat()", nor
+ "java.lang.Object[] getObjects()".
+ + +
+ +
! specifies that the corresponding access
+ flag should be unset.
+
+ Combining multiple flags is allowed (e.g. public static). It
+ means that both access flags have to be set (e.g. public
+ and static), except when they are conflicting, in
+ which case at least one of them has to be set (e.g. at least
+ public
+ or protected).
+
+
+ +
+ +The WTK already comes with a plug-in for ProGuard. Alternatively, ProGuard +offers its own plug-in. This latter implementation is recommended, as it more +up to date and it solves some problems. It is also somewhat more efficient, +invoking the ProGuard engine directly, instead of writing out a configuration +file and running ProGuard in a separate virtual machine. +
+
+In order to integrate this plug-in in the toolkit, you'll have to put the
+following lines in the file
+{j2mewtk.dir}/wtklib/Linux/ktools.properties or
+{j2mewtk.dir}\wtklib\Windows\ktools.properties (whichever is
+applicable).
+
+ +
+obfuscator.runner.class.name: proguard.wtk.ProGuardObfuscator +obfuscator.runner.classpath: /usr/local/java/proguard/lib/proguard.jar ++
+ +Please make sure the class path is set correctly for your system. +
+ +Once ProGuard has been set up, you can apply it to your projects as part of +the build process. The build process is started from the WTK menu bar: +
+
+This option will compile, shrink, obfuscate, verify, and install your midlets +for testing. +
+Should you ever need to customize your ProGuard configuration for the JME WTK,
+you can adapt the configuration file proguard/wtk/default.pro
+that's inside the proguard.jar.
+
+
+
+The pages will appear in a new window, which you probably want to view at +full-screen size. +
+ +In addition, ProGuard is tested against a constantly growing test suite +(more than 500 tests at this time of writing). These small programs contain a +wide range of common and uncommon constructs, in order to detect any regression +problems as soon as possible. + +
+ +
| Input Program | +Original size | +After shrinking | +After optim. | +After obfusc. | +Total reduction | +Time | +Memory usage | +
|---|---|---|---|---|---|---|---|
| Worm, a sample midlet from Sun's JME | +10.3 K | +9.8 K | +9.6 K | +8.5 K | +18 % | +2 s | +19 M | +
| Javadocking, a docking library | +290 K | +281 K | +270 K | +201 K | +30 % | +12 s | +32 M | +
| ProGuard itself | +648 K | +579 K | +557 K | +348 K | +46 % | +28 s | +66 M | +
| JDepend, a Java quality metrics tool | +57 K | +36 K | +33 K | +28 K | +51 % | +6 s | +24 M | +
| the run-time classes from Sun's Java 6 | +53 M | +23 M | +22 M | +18 M | +66 % | +16 min | +270 M | +
| Tomcat, the Apache servlet container | +1.1 M | +466 K | +426 K | +295 K | +74 % | +17 s | +44 M | +
| JavaNCSS, a Java source metrics tool | +632 K | +242 K | +212 K | +152 K | +75 % | +20 s | +36 M | +
| Ant, the Apache build tool | +2.4 M | +401 K | +325 K | +242 K | +90 % | +23 s | +61 M | +
+Results were measured with ProGuard 4.0 on a 2.6 GHz Pentium 4 with 512 MB +of memory, using Sun JDK 1.5.0 in Fedora Core 3 Linux. +
+The program sizes include companion libraries. The shrinking step produces the +best results for programs that use only small parts of their libraries. The +obfuscation step can significantly shrink large programs even further, since +the identifiers of their many internal references can be replaced by short +identifiers. +
+The Java 6 run-time classes are the most complex example. The classes perform +a lot of introspection, interacting with the native code of the virtual +machine. The 1500+ lines of configuration were largely composed by automated +analysis, complemented by a great deal of trial and error. The configuration +is probably not complete, but the resulting library successfully serves as a +run-time environment for running applications like ProGuard and the ProGuard +GUI. +
+For small inputs, timings are governed by the reading and parsing of the jars. +For large inputs, the optimization step becomes more important. For instance, +processing the Java 6 run-time classes without optimization only takes 2 +minutes. +
+Memory usage (the amount of physical memory used by ProGuard while processing) +is governed by the basic java virtual machine and by the total size of the +library jars and program jars. +
E z48@QY&BxR{Sf+NoX)lq{sMKPOpPVlHYg;`>sneRKtUp(rWSl#HhO}gLC?1;b->i{$ z<9ONPimt@44(7J5H8;{McXX|l=&o4SC9KxH&B86GqI=g1yXWC{jfWa}DmX(Ymp{Ci z%_!2fyQ6t&MNMx}+^OpUcf~DsKdas!Qu5++r=?TuNr}WQ5qa5%Thldq7GHC{>DC)1 zVOzVSm-&0wRu9X93f7m~I;2AUW~N)+Vh{iOO!MoJPTOx8e>+<4GirV*3lnhBezC&$ z)rnxf$+a)cn_fRH6I;+)(0r!|BcGoR-WxcL>;=FqyZd z_}>=}ujurq(hmDzR-fP!U*{KE#T~uohw_y@C*753QYfCJG=0+S8=g1YnhuCgj{K3~ zp=qT0u!)(uQ-q^z_jhZr9h2`L?z`9F8LlDjd2^DQxmwzfG+pr)*(Xy@Jendb*%zMd z8pg@I_l893XVDJP*36&c9GL=vnOfIe4eGZ~N)FbU<|rW{VbgeH+LMN DSqt5bDwf_Xl$FyaA8iump<(l zt**BI37P$6n)82gRQA8@pKUo|QrP_K5B*n#*1wzDIaSQ~?~-|{X|qGx^;vI}v(IpS zTj^3d)9Lb~^!-x}q&QQnIPyJ&JDOO1{jSW)5t@I(a?+cdA_A|P^;Raic@{Zbk1_b+ zBRI=pOGr$u {?y~6TPuYn(y$he_ zPc=*F7OVW 5SN>U0c<#9W+qRXP5A}Cl zn?F6P{>9Vz?~f*Tv9J0&ea#2Oj+ **lL0{XqRIEMVTI9jh$w8Wp_Ez5F-nGqK8+ghV%2cn~ zkdfaVRe9p@I$!hoQ-7@cbZq-~&zWqkGpk>R^j=#zX-EGn(N$lUZakd6j%D}iY1%4Y z)jMZJE9~91bKYzDnZI`~>Xx5ty=&QSRoQ5p{axl(zY~w0$UW+{-RMxuq8t63m5U~- zt+}JN=Hd6Ei=JyAz1m`uu{|hz(MszzZc=-mTFyDLbN98C|Mpz)URTJSt`@a(4_kSY zK!4va&3Di97-#l>58Lfwxc*1h`juOEFhs>Jsj6?Y-Xr3^cYF1oH eIee>Cu2_sG#SX`Z-Mx~AQD`w|P8{fcv@1}kn;@ZKh~r+rU!*&)Tn zCC}Dfa@a2=wW5&A&y+{$$m{aOnS0MxRP@|jQFps?!mdg`*L@RDSQM*Tb^Kf1xnkRn zg%X(ytfoxtZrivu{}1QUl0y@g&pxqW^|7uyCzyqH^~szpK6GMI%*pqRC#TIhd35>V zRgoKx?ApcSWS6?fKi%8POebeo)w-Rh_uL9oTqLu1>VgCHnH8735(2ts-||}W_|+bz zMb0P1)@+P9J^R^CuhTl0M4dkD*!#-;)R)ZZ3xq0b-mGM>TF~`sW{Kzynd2Q_XZ0tx zINg@Xf3x{e+#R{acM6TUkCmnDPkIxXd`sb2%mMa}!z^dQ4mCy99ho-i;Y1V3gW0>c zNJeizqO)J&`eA;ZO)n*nBnTI;QJB{9 xrLoGm`=)?+&$*N+qw5hDt7lHS#5Hx f7yOUW`~#g^_t(7jcfd;NgXK2-NDt`d9L!f zN#~l$(zBGl%zS+F;*>R2PuHAWA$_#dvZDL!jr71>bJbQ)(p8^Pb92^R&d$9z=gIQT zlvP`>=gN7BT9+Wz_0p>+u3a a)*wAFtn?Y;h&z zR!+c-jG4D=_p$CYs-LuH^{Q(;Sp`1d4sy-jzw}On%bET6i>|#`wwzD%Vqs;Q;Mu4e zix~@6+*`lvj&jY0aP9O!wi)MVT+!SfWz3?_qkAMbV%@`;7mKIg+3WLQZ{!*2HwT=w zE_rbE?Nn_p|Hv=?XN#Ed#{0Psjo)4})!nD@=yv<5llMAvtlu6M^u5N@zE*ACL5J0k zbUa*&f3DU4F*SgrYemg!xp!s3ckk$$Uvrl$6Fa-1uH$|r_d@Z`jg7w^MXb4BKSODC zZXMgc xCpgCl>dll^=)dMi)+ zyJ||_v-M{kj>hiD`g!|c?A&$Sr;pqA$;_L*v*z>$-MKsZUY%9D^O^T`n#}9vFCX6c z{rvUvGv|1B*rexwX7kDj-N$g!l5wlf1dZbleb>vCbUwPfKKtwXn60mx`CcAsI2W37 zjZ?!~m}?rtKd&gh0|k*0lI~B+y{5BozhCYZTYUTNfxKD1|1N5;o-}7&Q$pm^+}ll# z`;N4`KCFH6DF5~Qz*U|#S%>f4Tm0Z3_n&zWuJ}IY@hj(hchFht;lGIPQq9^(kL4c> zY^PYSI%r(^Z2rHcK5?&VT`u@q->$y-`0wk|`e~1%Jr4fa>snH|)pO1=x&JRYVjs+y zw>`@8SW)EFl8UR$_m^E|zj*kGYhrC K&N&i5bcy|2mHo1C{cVAIQv z{u65o 9`c9fj!D1Gy0mhsuOhqP+n^wjs9U2OA8*6!o9d*2zqeXW!K!QJqp>HW=? zxF559e{|H}Y`@>LIJfV7biYd8oQ3mzfAn3OH2=+(bKn1zzdG_H_uZ2tYwo>%`a=D} zues~*T=tptQ73=K-rU!_)>$9p{kiS!+vWbh=f-Vc 0ckq{%rnx`MUMT-cQ%v`*{b?k4Z1@z1rhz zqJ3XwcN53&gAsf8SDbkFQt`m8Cv8R6M_=w}Ec$%zXmymI-UIf(pE($I-T7y53bs;M z!GI;%bKWxp5f^O^*4JgNLD$WinJebM-hWgt?IvgA(awqAKP&e4r(W2%R9aa^b36 ziS6#IUGC&oosXO=V!Jfy>>bmyXKUNvL01ZMYiTrVsu;288|rox`#AeY<=Prf>G#Y@ z6`2~CF)_a=yJ+UrDT`+nEOE&VPOwZ7NLjhIBx`Bu)>W$`f)e7l$?Yjy)NthJvEwIB z{;O&|qoLj1+0N8!GtF^c(xSsN*2o<6n74hwoDFv-Ef3$k{qD@ZXLqi>pTGObo2Re$ ze!o(B{}gDcFc*u;3Q6NkgC&8dO^-~PRuO-#cV>BJ=r*;FC0lQ22+o<1`*NLQm+_<} zyc2j|-n1^i hix+svd1=E*4@F__HxOV zDUnehnJ&j#@lNxKVRZU>v9q}H>GYm5-&EyTV>Y)4@1naurg!Z-vf^5lOxg!8Ur)2l z2kk8l9QQgD&u+P@@+W0wpBL}6&8?;dPd5wAu lo7K!V%Ysvx z>FIJ(D)G5rc1}{&(m7fr<@fs^V_Bfb{g{ellUmm mtd%i7IltA(~d5?H0y^L|}~e^6-Fsb%|jUCEp`hp9jM-N8F^XTR(C^Q-mRR3FJ- z+|}FHd3=mpctt!dOv~-y^!~Uq(U1L=Hzic^UOm(jt7Nu5seNjnl8$je0N3^pXY6yk zKA*EWpR*>}we?1^zZHM(my2cdw4c^^JQgp${4c=0_WAjiJ33#lMx?JbW%Y{Q`}KN4 zd#%Y;&5FMX7Z2FzMxH*lwZp79t6TRwOYC{GrIU2Lewa;NwR7+Hxc@v=*Y2+sv@5@s z;+}4PxuU$&@4@P)=@vVzbB!yrrpxP<-gUg+`_kDq?@iYc*Cv~a$imGW785d8-Vy6F z^17$>a%$J}?GyBpIIDYOS|5Fk(%wC7=d%FyxTc%p6JDsjR^26iaoW3Or%p~UT*LX{ ztKr?(3Uk!fY5mxLe)E$WH_OKV(>_=Wt>2e!TD{$3{)|agwtH_necHS2N6;57*)wg5 z{)wGc?Mru=_8Bp*R(aIW{-x;hhOd?r7>`Z%eJ-J{`_s@nNcEpo_ZyZF$DXUld9GfX zvgTj &v6xvE36IxjBn z>|7zS+_Wt)AXRFCRD6c?r_hNNW-5MS-4_l_{ArlvvTEv+A41Ms=UhnC@QOU9Gi}=C zg?VLFiQbC7=|+8yMw0}X=bcbHw&@{j-lXzBw?rq3p9-4wX^L!?e3M7#sjyw2rZP!B z{q4efI_lP^X)J-NE7E4_#Qgd+{lJ|D9rYc@6Qn-RIB{li6ARNRyO=ZW87%)CjW17| z7p6DKBjTfqQMB 2QgN zN_XGUg*IKX@~o%i8u(t;{Hs*=;yR^RRcG9KW%a$Fk`9NU$UY_6B`a+vglN5;nYqmW z%#5EkUFUamILn*POe Mf6^c)k4*vf`2F^cRm(l~-j=TFtkz@y0DJ$C)XYxO)xOtau>GV4ZYLuXc;? z1eF>84o-P-jmdNCyi>1||8AJIiuY0e`Wwz~94?>I;$gd}+})(+z^<`(+qc|}8M$k! zW4s(q@+QWf{k%wIyVZj53D-J&R9@L8&C-9aIAPI(r77_*XIv7RbbiaKvuBdyqx|Y} zbS7(h>70CY=!9pSYjTsz-PatEn+{&g*!A+T-g~V`KOes>dg`v4ReZNkmZ)vFU8DP~ z`nmW0)wdLq#uVwyA9f|IsFS&pZ;Q Ak!PMl?((Mti<6Ig2rhFu>Avpb;g%fh zmtL05&s$eyo;dhOPW#zWuC~m8^eZBB*GX1+hGyi?y>r zaQj+jqvLmS)~kTOEMdYEZ!P#c^H$Vuy~4EBwYf~6_&3!4+cv!*=2ecCwRJ|=bf5Wq z_o& n^XnlXvcO jw^c@(hQ`H}pMB=m zzCQlfDe*@W|J+_LRei1bnbN;~`(#(T-!m Dv1@U-3NZ+Pvppp~S TbwFd7!50} zgPS~VwCr_k@sntET;38C(Q3}#dh|e5-16pc%h~r{%I=-qTD7?G-_nUjVJl*oc4VD( zkCmDl99iynDMjwx)v`)fj^r1mWgdCcFKVaXP<+uIko_Y)=evAcphjE;kIuGI9+SM< zA5xh!>R8w`o!eAhr&ZKXPZ1C=th!ifk|bYg9yohOYO{KC)w0;cgjk8Kg}v;7K514y z6XTlFJ=QGt>?`juzSi;VQ+u$h>eTYoQ 0jh^@JmWVvNqe0E3{Zd=aitP<=EOZ}zihN);=~>7GA Lg;ZP*|iaxH${FY~iIfv?YhELq5Dk$_L`hobQ zFVB2_t(a`**vkJrW~NHhv=vRwEQL>9yE{yRS7mf !lrr0gU zaXCAuDNVO7@SJWjqc^E@s`bk5385`195e2$nEEe#M%&E}n@R(phz`k}GuF4ym=rm) z_v(ykGiQEsnd!H^Bz}3v@oz=^E8_kqwwz9!HESiqjGqC8oslMN?a>`Q_n8#6OOqr1 zEpA (fIa6ZQR#zUe26EGkXD!uBMiSj8Ze0SGB!jD|l%!`@Q(0 zm93K&syPU7)m=FjHq(9Ly0nR1qKof5oY=h5)vj~W+ps0G*?q-1qjqi1XWuf3uVQ}F zw*`MB3&biK|81S2BAGSw p@D(EB{Y;}^ShRZU~miemxGTC!Gjy<%^c3YfTa#RM<&rjs!fqgL`?Sut(a$`0d| zuhMyT%#@qIs^s62((RVb%a^aTSiWS+tESu^buAT9i(i#xr!TR69Cx67)uAIRC;kZD zyR-LZL~`Ve_9Ejb!LHQ@7OtAB)p %VRYd*FL;DNBHFO+#Pe*+^S5O zIn75*)WBl-lE>Y5z2@C;T%(-1=D@<$YnH8fb+a?JOYQdec}`~OA5Jau_w1cf+WTxp z<<6t?gLf{Kcp-O7V(PJ#>jYZ Aa81aZ0cmrPz!+V}9dOaCny-<0{v z-sW7l*V+D@vcYY&{g&QhkELN&z2QF>&-%KB!^6qUa|!FE%?o~Pjyb%pu|s!#>4INu zOXrzw4)E6h*Ewae Vez_TCWVH7R2CEXUnj?|q#R`_tfD)!J;W<(D-B z>z@>C3toQt)bg5>vzlI)=UL9H*&RFAMWMTUNAYh4RqLJ8Zfnk{+Br*Ged_L=^KJ)~ zf(C^RK!d{7ax<&_+SyjrajsdH+5J#xXZb(Tpor+5H?&r-kF?}ykNGRU`rymiuT`i1 zT(xQ9teqk+RvlTnn`Oox8^dKAy7F_M?o`xfU*^oO31A=N?m=mln05aMqT! zQHD=W?U4E&`|xy=_N`S~llLvDl=07sn7o^5lR&nehveRfxgT%w7HrE*J>KB^D&2DR zqHfXs9};Z+zAW8qZEq*N%<5TBiO{N9JDIcs4vOU*WK>}KcfyJ7R}SCG=&w5$yp*by zx8c@|oczdbvR+Dc5Vv0N>%8lYzK?!yZ1XxOUUOJ^!9f-&d)YIKmoIX()H) FMuxOS?|lVzp{Tn9Xs^oZ}VDf?UhDKlr&wxpmqdNe@$trW;(wt0d+w|IztU zbcg$ _|Ta~P=-Y)ii^F*8DrF#x4aL@i#xXUd3ba>5aYo49E_L%LFDc;v} z_`sT@lG`?y%``p}bLMC18S^=3CTMKAP;>UQ+VM#`9FZ|+%ev1#u<^gO=j@d~y?eS! zn o2+%(>kwG^R`Z>r(u2 zcz5yg@|Wj%-ZK1%Imi_2Uvpyr*XKEXIrD|?T$ug)!X2NJ+D~HqUh5w=JtP%-iTA+8 z$v$5GyAIrGUixN1gIe!d4cUvqi%w{)(Y e4*eH9Cb}c U+s#>J@4Iny7k*tDcNfW*Cd7e zUgOBVc52PFytOe=e^1BkU6mhu{ix9O6E@dR{#wPOvbTKFwX(nGBi79NxyNAFq3hiz zHaD!@BkyL?-Mjgy^*KoyhAFlpvbDzjF kEYApIQy{Wt@ZrO!fr&jGbdumn9cBUz} zd%w)BTRn|Y>ed3L3nsSmVimW *2u(Ew;=93R=4#>sm^XgUg zAD;62_oZiD^QHCfZPV)Wop<2Q+oifMpGA2^zstQ`zHifKGxwbCUMBJ9P8rXFStr^z zo$7yGxG?74?%b4>xkX~)6}ghlTdcb(s!sO0?CX)P*m%Y-=xz*8_{-|;`xeDLe(>aW z@4bLc6}!*c&hp4@?YejM=|QPKA~!n9UYGcu+4XPro_nt=Y|sA5J$vrYjmIC)uvxz0 zdG@;c?VC$|JQwzDxLo&^CGgEVpOf>tc28aSroP+jrgZlWKl590H`4FE(&^q4mL8S* z?7AQOd%eD=Px)k?^Ibgt@2oD@n=`-Lo=3HPNdNcns~pGox|=Wm9oywRS2rSXvCj6* z67EwvlpdVyT4}d?iO0v;`(C+S+P5p#Ff-@Qqo{T1-f3pmAI=4S(5(FsJm*1MOpk}% zdB?r;YV7VY%T3Hv(kwam;M(11>UTE$3EOt=ddROsZ}!B!J)w91yXSh7?E0rCj;in+ z)Rv$4uC|`*+}5Ofiwk_-b4W2w%enF?Znp;iBlr3(k?$7>{jBx%-=gt*AqW4l&o@Jy zZRdSbe=NryXZkONyRjkO|8(N~_m*YH>h3LOoAb@=)=`U93xe)H61;UH?CHFOxKA?K zTWse&S37T7d;V?pn+I-x?bF)L@s=lqQieK0N``@2+$tf>lrf!^C ze`&h@wFP<87t3u9h@EzBuhj~>!UmB$wXa{zjN2^ff4^HiR<{0S)z4{j?kpDyKDp?| ztd0I3Zs&Z+J$u7_^Xtp;&YRbN`qyXDvibY%d>Oa(??3AOlk)iDV)rlk^55&{|9#N+ zc~}4BWBmV5@qab{|J&bLG_UhtU*;(ehOMkfgTmnBBfOjq*W{dLbMQLTwBbyPfKknr zo}S6i)Sn1+pHNX*k)tN NN&r?Q>V|IZD(Y@aOvWe%N>?o897Jo_8+?w zbXRiA{X1J9K6#pJfB3{xiI?-=JaGB?`slmohaYZw@aNA@ovYRAYfjrFFVeA$KCxlp zVLk0-o@?B41 b`w~hWoCbpLxp`CtG}f z^Vn!Z==tgV&0MlxCM-ugh1W0LxMK0dvoTDuOP?rjdgZv{;G?-;GK|x&_E)jUO|JSn z?PmP4h-Ip~FP`>a@TKe2T<`4}M;2EsG(3BZgGFo2vjcoPGee(kx3vCyQ1;*UNor2( z@}1;DDmgb@KNkIdPSxq1>zb#P->a4d14@6+O~)73T+xn3Ys}-Nz5EZrbz);e_p-TWpGU6agXeh2ZwqE zXEr7_nS6TzS}AO~px0s-gLBs{j+IX)`Ix dz zmy`dTctVtJUavMSOr4XTu iT|in% z)ixE@syA!W=GHS@ygt`|)s^qh7tBaom06T@=t0BWl3A6r7Db&ExioKa$I)fE2XbG% zTD^fyBaJ2K(aw-bOQM!#hc~R8C1tetnAV#ON32AaF6X~dsj_Itu`8<=eNi!)wq^gT zC21RPq^;JK3-l{kzQXtEtLy@kqZcp6?_%5icGsJ2vsosda58wh$@ 2$pd``;g#-g(h?0Y{Ivx^ &@v`+r^tKQFh{L_brOycLKeQ20` z?#xC(4QtE8j9;Hk3ahXGclkEo;>Rcc{P?%e{{Qot<@?NYe>3x`r7WItZU=vqPT?7c zQ$-SHN8RMw8at0Q9CE1N=w c1z+%`XvNE5D4>fb$KT&~hW&);EirS{max z#4oK(xvEtvR; coxv@&U5$U zE{T(Dn?BZS#)N8^o;>a<%dYV6@j^jw&VB6jx=sD>Ec_?GJ>$eq+jo8oxTbRYFSfK) zdfvSI(L4p^8|#w}8FhVee!4H~#`0iI z{1=6@Rv(UFE8cARiE&aj$~Qz zu6Tt`=_ oY1=_Mcw=6 zsi<$8W}ID ^={e zq< GMeF_4h-6 IeHj?60eFjLd;kCd literal 0 HcmV?d00001 diff --git a/docs/screenshot_console_small.gif b/docs/screenshot_console_small.gif new file mode 100644 index 0000000000000000000000000000000000000000..3f55f5b676478471b308c0b40103ccfed31c82e0 GIT binary patch literal 19730 zcmZ?wbhEHbbYSFR{Ho2sz`(%G%`Ge}EG;dqqO78!uA!~1ZDL|#ZEfx1;^OY%?(OXz z5)u*<6O$O9n4FxPot<4=TwGCJQCC;jSl`&v+|u3MJ!SHgnbT&@nLTIm{KZR`EM2{P z_1aZyH?G~db<5Tr+js2Tx%0sO1ILaYJ8|s9nKNfDUAlDZ)~$yRAHIM8{`2S0fByUd zIf7iQ_>%>!RR=_Z{KUZa@4(b7kC3G #;%-}>9{f9$C{|Ni*Hy2hsFme#f=MWu!!VJ#JD#fcN;B_~aimRFoP zQ&x7``~?f=&s@A{+42>W7c5!1YVG e l;iua^E z&xsoMwYL0tc=$-W)}P)LDM?4R@G5<&JTYx;^6k@=`qgLF?A&~6_dMrzhYJagjjZfq z9tznHomkfjta0*Ku l&-2gGoT-7^D1eb!l4VB&^GU@8 z4(wZO4_vD=_$T_f?nkQw6X%PL4+nK#sJ5M}7MVHW4CkAZ+vldvt2JNT<8Aiy49B@x z14j RgxW2h7_Jr}>ck?*qJA(!_)KS7#IXq{ zRqDTKzLf2$`*z&T_7ubQ#r9IBW@n8~`8=LD!LRGfqwc$btL`pY7&%LWGbixb4AUu7 z1({f0*nCc7mf6ypx%~6iSFhY>-4b8Ds{fcpw)YIRRRZ?s7c`vV5bAi)$STN?krQ!j zRqLCL{5}P5HeYble!JyLSoYhkH`2J*Zn|W5K(~R{V!_)TPo`zR+xhQB)a~GG_eg=0 zteH9t2ljlqmi>P3k7wEr`~G};{eC|KyUvFLEaEvI4sxikF*w8$(eUiBu+3!0BrcwU z1B|~PX&+!@VPPo9{OXp`u$Qr?W&;C@j!yxT=Jo7` 78jVnz{JYYFpELdW }Fu<0rXPr%nRKllt}8N&N>8jcz;J1{gxNf(sgVrK{_z0S%au!Av-`@wexMuiU! z55CBKa5&Y$onXPpx?>Lm3#&|n!zr2Db4?jIVrm*5GV*+|WMGfrVJJ#*f4B4By}8TJ z742jHQM===7{7qy?e)=TtBx|1y!-s|ggC>OPb#KChb~DQcpTtZ7OC)vMRLjB1MI97 z6QtPqISiPXnH>}w4}YD+@pDg`bAu7HfCK|0
?%6k$A==U5n1_1f z6%!rCm6i@nVhvJU7J3Ztq8*qe1Q@tA6dDC1=HDw~W?)U=$XanynprBKk=Y`E@k@0> zm%s^! r$97bjD&%@xj%0ch%E10)eNBe7%3lV{{cKb6 z86Gt~{>xP0@%jaC8VkdcTr~!U?>DopJWAqUN*EYjF8EXz@msOQrh<`y%`f-M;^ #5i7o0}MHvPyC#?j&a$U=_ -o%3DSr zjz!F~{%xDu#keJ#A@%vqS((y@mzETZm|Wf`)6P(*`ylOQNyFK9;dbnE-nnqLHmGK1 zZOXQqnh?$tWgZ&PlockJ_hnMFKkqhw-e=m3LI+;$ b~?$mEVbDti06@HBGOc6`7&VH`U z4F;FKZkq8{oUx(p`oh3aMy44IT}%$QDg`&YPWU<_)pPbn#+)nbr`?{O7?~Y)Q|mzT zO6S?BZ GDp$HiZCVwuA#k51*=-dip%o>c6p0c)@|rRyU?aj71Ea-bUnJR7 lmLT^~ zg!Q@Ib-Tc#X)cM^1qvE{ntxqCz_+e& 67gY%r*zEXK)An zWD#Ker`Ue?XU(GtsosvemWJHn@V7YF)|<39&Y)ll^V@3?X~wHVkLS Dj zJ6R?capqXSbO!H|{oigz&3#z2$!YoZIqzFHP5LJ~_f;_aj1%$GBaU A{1(FTdZCe(fQ1HBkE3Y=I9djBa7)rmXCndG%s6k8PQuf>dAf`pM5<3mo`<^sH^q zeB&6N`@7yu;R;S`yj${k&$7ppX8+A&y0fMFBc}uF1>29yl-DvmcP+c_7?+(fchB)U ziw3SIX$-~JZ#|#0jX}EkX5Mn=nb)PBpJ!$;E#EW$`q2x=wimITD=@wteM+ioJ@@21 z%c|eo9)B@oocyzQ!UXBPCg-31)V9{iIJs9rzqnj_>p^`}+rHxe2eJ+?J!+fd?xx7{ zF8A=)BHx>00ke6Ymy4)YO*ctD?6Aa`QRlJa y0V!N8G$sANwW@i4)zhBtlYNGU|R#}@Dwa2F> zc^fnS43SyN7%Q$2tMb(Fg^I87H6PREUPf2EG?@dQh8S*i_P%H4nB!` 2TV9@)7s^n7#bWZ|_Gu}81(SQe zJ*uwSY^=3J`r9{Wexr^zElyWmg6B2oHZjY}dB|RMaZ?sfF;=$ZE-=-a;+NGJ=%3R3 z?VGja$w1*LW>?z-c^srgRI*pO$$FU@-7C|#XJP2&u;N<5V5Z?X&DiOxV&a=Aoi`Z0 z<6Ps<{V-_TmZ&m0?23uQ!qDPxn{xfz&6t|Y{!OaWbN5^NH2lru@DR 8Wsyt0`vU%W2|J2C_ zl8>jGCPzh@M)bKd%yQRfQz&b1HT=9Ntu!;@Z&UH_OR Q9q2_cfi7p v1cH=!a!@o>6T)KGL z>d@(oKeOI!PH%l_cv!Q>cB%1cPxCFFbA>(3xiw?=saO3y9IX+e-}z%^bA^Z7w$LnL zn;=f(NX@yImz#HAX?l~~6!dfMw vf8>0LoEbNB>ITh5_RGcP zqW=u}mOD&6Jo!yZqU ?4B@WdtQepxOtOl<0%00A 6wg8qgt3{$>az21_uN5y=_Lq{F4sO?MF&5hb9;?f|>wk~7q z+Pck_0*`bJxA>JjUME&$d|_$d8c+QzB_58??54a}{$huIimR+*Hp9a!j(TSGU(4oQ z_?9asWHCv2X~&VIe>WJ7b$-`31vA 92%!=p{vYLZoNbGutmnsoS84pu1TinT@JY#xGUq^j MFn-`uWy*uJ*4NMms)XR8_81*4=eqa~pw2fKHL zeK(%SR(!!DldIS?u-#z!v*>8`h#6DOmPwlZYc=jIcJ}iuUHmyDba6@K%qdF83fx{6 zBp;8ssBOW#q-cfO-U%;bzwG)~%(kakUw!+p)q8qYnEn%CR61pqa@i-PTsA|w#bl+^ z`ok&Sr!7Q_4k+*(YMkXdd0RZgmISjaUTe36o)(?0vDNYOCX=eeUi%`9J} X=r{S)`*=5X%z0Ia3TPIsS^}aaWnro&Xqmz|xanbgrN6YqP@UWZYmq-2% zIbQzf_ TzSuln zaCy1%;)=@1-JeRVWDidlU%X@K&)8-cX~Taq7FTwqiMJZAkS=L@Zsf}xbgt5BW7Wi# z?^a7KYbLEZ{6s8MF!+%9 ~v0u zIz@PTeT}U>G5NyMZQHV%OQ$%Agap6b(#K?`e}78EKlvrS+N;GrZSLRjExAxb-%I1* zlIJH3WKS5d8&?0Bb=ffD)ywD}laABUX2r5*b57d*aGu7zD*d1NwEEEf1-v)X*B0kK zKWrOhlDaspwyn6$bSEeC$^O#Pp4m05G8wwT3}xSEc$nXq;d;_I_uBg6ez|2Q mbs*bsb|M_1PwZ9!^dT)}| zug>dPmp8WVcK2s-KO>uwsk@>*_uBjw##`QYPt9tVosrFYV6~-{hv^cNnH7!_Mvf_) z9XN%;tbX)KU+Gf$oIBq`Hp=v%+?9*^!Ls5Z`n5BRY^<*Qnh}t <1|)wVF$xwAyltZuXZ-nghf*_UVE8a!PV?jdOq zeR$eKHiwRyeJAQ7=gm!8VC%Agd0ME9fr*d7I@9d4Qw$}N+qEWJHy-n0dvLcaIrHkj zka@LJetwFzZ#{ciY*_|tOuMVEjIzVL;~l#VCz-lf?fV`UFB2tXaDZFPVx4IIm5V;} zF8gdxouBo&`TLT`{H=4htD3i8sc15ud_mMg#oQq0h3VHeqlcg0eaK@x-K=(Zcl?h> zTZCo&g N2ffQe8uKQzT6h~%MWv2-?MyzeKI^^tv{lP2%eT5r&_=lG9D7O_?44~^~9 z&K-Qf>v-UVA6tOKJte&ci7%R&2Or$@p1a)LLAAv+e}ZvxzH(Cj -Fj#N;zxJFdRDg zfRWMHF{vZrs=^|Nqz4D}7dSRCJbIw`a6uxI`J%F!9$`OZ?c(b0t$eq->5$N5mxp2D z+r!pIGyWIkNib;GS@-{iX~_=(CH Vl-tLbF1(PR`Gai{`orbnLP$ zc@e@;KFy5PqlN$Sq~!h^tFN$1&Sc>bk_fo4oR@`DNJOP#!2<>sCLtybfujplkFyC1 zh*%mgU~J;x< 8r!OvMWH2<;K#@oI#x}v(DTyPd~|g%<%%pgNA12wJu*11@hJ{W8>ms zaZ+G7ta)Ab bX_((-@qmB&vQ8cbCZ!kbO#ecD2qZL46R2QtQT9`0n0Q01f`REsQ?Y_0hu#-% z hZ~Wz*wP-NLdhdu>xb%YvNMoDMk&cJlH&3`7Gfx_d-EpK?Aw z ;hZ z?;)dTZw6*9g^!HiKd3lNV>jVyXpCWYNN8f@NHApNaM-h&k !_Ta3-^jTTKfrt%VuNXHGuV&}U(! zpvH29vEX=%z{by0s@!!O8kp|zdL>*Gc<6EG<+&-R9u$?ED8(>qsV<1=F1KQsSo~PE z@E+Ikqn*hU+Y{DZEqH9Y;A$AZ6@z$y+gaAfkCrW+HmQ2k*_MgM9oa007WX{(e13MX zos?X)h9%o%p${HS%l@mlGR3K~1&DHmulTU~MVw1<3$LQYiqtu*EpAe|8fr#rx^;6t z@#wS2X 7jEkJo*+`1v;3tzK?j&z#)^Z$~!AXCvN&<+K+?PvDf^5)A{ zN@!%|m>}I4dv?pl3Cb+J-;|~8y&4#6Qh#zV?lorr_u=I|r56#hFZR5&dV6Pf#BVv> z(>Gr9`!Xb3Ht>8*;$+B>a=9BU-`;SPLod3#^8?4TPZO{0nYMa??m~}D+d0oaeY)9n zex0%V=Q#PTH`cS=o8fS)Ozw62lzmpLo8t`FAADLDaQ5A6jk=c{Ulu&pxhq>xF6QCS zTK3Fr)x<{(7gluY%{e@GmBVqy6&W!r3=CRCT>I_sRI<&uwt$yaLoI}PMQpxPF@x99 z^%j*%u5ATZwenV65aFBL!?NnriWwHmf+_`=IxdN5>6gB8>1;fvSysxz7}Luy|77wN zfrz7 zP%~{sMFlCV# zvw&(t+su|u--V??8ft+Hw)wc5Y-|WwxKx+lgD3c#-c%d!rnDUw+naxA%?Mig#IGiF zh11kUL9;K+k+WAiv##)o!la2(yP`gNGdx@^`sz@t!Q-!jrB{74Uj14W%JOSt^(E~Y zI?NY}=S^5)9LP2C{=zifvyWJpt#sk6Uvou }c>I{+B$D3I6`v!zD!AdJ zRn7{}7ZH+LF^&q3 !$X$^hR za#e0sN^__vQc APq6?oSiMtuZI9yuIZJ@-Y{b{SdVd=!~ zUyqvh?_JqiHB*vxhE2tuB_R)NFMJABpL2GmSHcI8KBjf5Q@S?`iWz!#Y%~$-*d{+~ z;;E+GSvGBUVqETt4A-_8nJ_2&W=P6?>~m~V&HlJaKK>MQ>3$1Y?yXsd6~DVT zrgz|{WKc?S17k0RT56LJ2-u};JI3{2Lk1j+*jO8;WH7i;?;}T9I?SC;Nnic zV=g*TO*#M0F_dqS*ipu6d@-WO%s{(gL+#N`rXspcR~TIPE-T*ptLkL*53dsfO=k`G zr5V!hUTfVPWx7h>wbb3-2c?D;GsARlyi9*N&AeZ-S4_lxvj)FVl-y0r{QAq)hjMN| zV)Tm;H?J#X`sQ`?-zz04n-vS??*+)NzWZrfef*uSJ>Q&m8~@;1vulIa*HS;zEN<;B zVd=*hRy0^zU6hkemU*VSvOFVnrRDvAQ--rvxJqw3$Tlt2gZIfw@8*C)C(%#YAz!S@ z&&>4e?=rIz-of?ntkkhv zRaVdV sKAib#*MzMi!f&M8su}lkKH`Zudgs(bCX2{3egb)wAq$rstX^aE@4NQ_Av42S zEk|M=cn2I@TExoVb=3COf@> Rphk4d|Jq$kX7glaAZZ1&eF`IK^u`mRzvVuIE7LjU=-ximkKG`z>kRy5}L|vV-~>sv$W? z7-y{br{J`bub)+xU-io3OqKP~EQ kpXt?|raA;PnQ+sZ1(&Uh`~J__1fze+!=fJc67*JZH{YAhh~~U_6ts{6;yZ zzPma+>S@exJ-Dwgdf=_2XFP}Bifz5DT8HI~e-0<+FuAXI$rH0kD=Vqf;Unj+SBuWQ z<|q)5D)Mj?OEuPe=~MJHeFsb0E{7~01?C)AKCM S&3yNc%0EG$qAh zi=*-+&tj aX!S=%EE zBUU5|*DJm_x8N7k2f55Zk+ZMMCn+%TF)%AJFa6>gv8Mfjz!I4Rj jm2 zeH 3&&C38{T>c(a-ucZ1* zg%;m}IbDQb^;~|-r@(B+z3rZtfQGYik*Y}Fg3@0J#X*5 61(j}SW>e6 zB=_fGTrM`LE}s>|&lm}OVi7GkYP3!{ ?G$X1#w79tDmO1+CJ`%mqt?xRe*DJ#i2bIGiKG zoaS`Y_`nh|83r-wB*`yK;Z4DA>3W}}k_<0g330w0_0Ex3eWRv}CLP4~##Z>ohte zxHc_V-|$G>RvQ~uhbPZM)@$&(i)uVu@~PvcN5&%8w=Wgqe6%8(j`dxQD1E5=Lm=u_ zXM@m#uLg!+^;Veor5v2I$>5CQw=X9|n>kod7R+txwXL6Oe|bYLgNW;#^OhVTONEXa zuMucn dUFwlE~|Dkcn$?1=|ZJ0nXjm5?quYL`ghWG29f?<&sx) zv_{X>BS)3t*at;F7PILSVsB49q;}wXM5v40aryl%EnE{AwGKRAXEJk&>KBOx3>t@J z+!8r5TvV77C$Cs^i@}{iWp#+gNsc1dD V6>6}5l9 z(?rS-UEadjlQ&^o*hA}{j_|HF=VktmO-XJB>eIF@P`e#9Qm7f7ZiBXA{u`q3b4%6HUyS)Vv->teh(FQ$XG2P)zp% z)mNv5gU<{6-Ee=NheE;nF!57+L;PpxgqNP>6qXg2`}A7)rm4I( 0eQM?sSzAG_$v2ij}d%4^ Ko2KhO&39^o z>4`+<>5MK3ownVAC3VO2GMt&T66Q@i%(i^7mX-VIwga*PCm9)@I~?umZ3s4VIr{iQ z;W4Xe|JV%I^42`=WMdY2w%%L0$v-GvGB4C3Geju#fLr$hdDbINSzJAF#sU(H!%Y_H zU2f3jc(20QrzN3qIf}<{(sS#eGbdiD3m9DTa&>W(*`{{!op;S<5eJs=7!SM0tSV9x z@khR91r~?yp039GKy7ZmW9w7TtVsnMxbz#0s^v^H6jc1*w3y9!!c=fHeEq`E9nA4- zRxb%SSpUOCrn7;!Zgs>Rhn~+Vfj g8~^JoZUdv7C3VlY!weBfE#f zIW4+xgbEDW7iK(F4pY+D aBW+`_ZJJo2g|MSM0r+q&X zJjzq`{% `>#t3_f3QD$r^Sl~)7q(r 8%wrd`|!vnPV#ofQOB;w##T&szJzncInO;S>~!W>>lK!=$BZ$Lmic^kX;b(i zIA70nrMSy9#l&E(ojFG&w=pw{eX+lw!Tany`|HOFY$+Me>uMQ}XvHnp`oPfhZjs)l zh&Ah;nwB}ca4)qJQub_KHR)KR?e(cDaa){z9AGrKV0+_&_{GDE5++m?e(_%Pv`Y7@ zX}DtPA91U!L&9Mf7$rVA|NG2fn8fOkwe~y*qo5eGYlidE)I?sD!?lZa9X>rMU~o1$ zaAg@omWml;^0Lj|iw}jZ6EV3urLnDCJ>j&^DyB(MhnOD*HV8OKyF8bC6K3_0!NJT~ zKjVUakNU&7L_RAPejmokD$dGtR`LEjEwW+j#>}IO 9 zIJ>QGU2&mlTI mf1UO!Q5TjlE%9bQY!cxoSk`c%YRR$!n`W;I zPq^PD9P2q@>9tC$YAMIA$J!^K=%y*EpMR`kxj3kYqqT6ed|PsS*8|2wm-R0A>~6cF z?@+Y!Q |$=d!7ooI0n~ei}7sms#l8VQdV@ yL0>vE(rX0-=a##c}9Ps{iQ1?esPR{}cF(>;)b4=}&PtQ%>e!^OCOWV%m zqUrx6G;DZPBoFWz7_ran@IdYo(cMzKMPP^bK=w@Mj%zuSz)<@SHRB57RP5o1 z*HYA)aMa+~=PSF@%)W~;tO}}o8Szr#Aj6XD>EcnPqI1Mn-LEl-Hs_zycjm*Aq?_e? zofvp#tg78Hap%>uk7nJPw5>MrfY8Bh?TPx)wc$GzroW1bzGYHsCdqk?sj~aFZR- zZ!hF5h~lb|