1

Given a regular java gradle project, we have some entry main method which is defined in the jar manifest, and bunch of dependencies.

Now normally when we create a distribution we may use application plugin and have some structure like this as output

/bin  (some splatform specific scripts that point to our jar)
/lib  (our jar + all dependencies with their dependencies etc...)

Problem is that even though we used just few classes from our direct dependencies, they and all the transitive dependencies will end up in a distribution. For example from 100M of distribution only 1M is actually code that can possibly run and may have any relation to our application...

Tool like proguard can shrink code in a particular jar with various optimizations but it won't care if the code is unused. By unused i mean the main method is the entry point of the jar, and depending on the code in main it may never ever use majority of the code in the actual application jar and a lot more in library jars...

Our goal is to create this 1M jar that has only code that it actually will use, taking into consideration what is the main method in the jar manifest...

I've searched a lot but couldn't find any solution to this, proguard was very close, but i can't find any way to make it only care about specific flow of the code not all the code.

Are there any tools that can do this?

vach
  • 10,571
  • 12
  • 68
  • 106
  • Just a side note: keep in mind that there is a) reflection ... making it very hard to determine which classes are *really* required ... and then b) the halting problem which translates to: for arbitrary programs and input, you can **not** determine if some function (aka class) is ever used or not. So, what I am saying: maybe there are pragmatic answers to your problem, but the big theory is against you. – GhostCat Jul 06 '16 at 13:22
  • @Jägermeister usually if a library depends on reflection, you figure it out and specifically tell proguard not to remove this or that class... in my case i dont use it at all... regarding b) how is that? if proguard already does that, it goes trough bytecode, and if it detects dead code it will get rid of it, or any unused classes... so proguard does what i want its just it does not care about entry point... – vach Jul 06 '16 at 13:50
  • possible duplicate of [stackoverflow how-to-find-unused-dead-code-in-java-projects](http://stackoverflow.com/questions/162551/how-to-find-unused-dead-code-in-java-projects) which cares about source-code. The question is about compiled code. the answers might be helpfull anyway. – k3b Jul 06 '16 at 14:18
  • absolutely not a duplicate, this is about shrinking using application specific main method, not just eliminating dead code... – vach Jul 06 '16 at 14:24
  • I think i'll just make question more specific to proguard, cos as far as i see only choice for me is if i can make it get rid of anything not used by specific code flow ... – vach Jul 06 '16 at 14:27

1 Answers1

1

Your description of what ProGuard does is wrong:

Tool like proguard can shrink code in a particular jar with various optimizations but it won't care if the code is unused. By unused i mean the main method is the entry point of the jar, and depending on the code in main it may never ever use majority of the code in the actual application jar and a lot more in library jars...

What you describe as unused code is exactly what ProGuard does in its shrinking step. Starting from a seed (in your case the main method), it will look for any class/method that is being used and remove everything else.

If too many classes are being kept, it is most likely due to some -keep rules. You will have to adjust them as needed. If the 3rd-party libraries do not use reflection for example, no keep should be needed.

ProGuard can also remove dead code in its optimization step: any instructions inside a method that will never be executed according to flow analysis will get removed. This technique operates on instruction-level, whereas shrinking works on class-level.

T. Neidhart
  • 6,060
  • 2
  • 15
  • 38
  • But i dont see where i can give it my main method? this is what i do – vach Jul 07 '16 at 13:57
  • injars "$buildDir/libs/${project.name}.jar", injars configurations.compile.all, outjars 'build/libs/proguard-gradle-example.out.jar' – vach Jul 07 '16 at 13:57
  • i want it to take all jars including dependencies, and give out only one jar... where do i tell proguard that this specific X.Jar is my actuall application? – vach Jul 07 '16 at 13:58
  • I've looked at https://github.com/loopj/proguard-gradle-example/blob/master/build.gradle to get an example, but i dont see how to specify exact jar that proguard needs to start with... – vach Jul 07 '16 at 14:02
  • 1
    You have to specify your entry point, e.g. the main method that you want to keep: -keep class xxx.yyy.YourApplication { void main(...); } – T. Neidhart Jul 07 '16 at 14:06
  • Oh thank you very much, i was looking at that stupid gradle example and thinking that keep is something like "dont modify this source cos i might debug it" or similar stuff :) – vach Jul 07 '16 at 14:11