0

I'm trying to generate two different jars from a project which is a monorepo that includes dependencies to many many great things in this world, so needless to say i do not want these executable jars to have big sizes and would rather have them include only classes that they actually use.

I followed instructions here https://imperceptiblethoughts.com/shadow/configuration/minimizing/.

In order to test if shadowJar task actually works i created a simple Main java file that practically uses nothing other than java SDK.

public class Main {
    public static void main(String[] args) {
        System.out.println("client");
    }
}

and added the actual shadow jar task and pointed to this as a main class

shadowJar {
    archiveBaseName.set('client')
    archiveClassifier.set('')
    archiveVersion.set('0.1')
    minimize()

    manifest {
        attributes 'Main-Class': 'my.package.Main'
    }
}

which generates the uber jar correctly (i was able to run it simply with java -jar ...), however the size of the file is 10Mb.

then i generated a new version without minimize() part and it was 15Mb.

Is there something i do wrong here? Maybe i'm expecting too much from this tool and proguard (as tedious as it is to setup) is the only way?

vach
  • 10,571
  • 12
  • 68
  • 106

1 Answers1

1

Since you did not provide us with your build file I can only give some general advice:

  • make sure this Main class is the only file in the source tree (shadow does not care what the main class is)
  • make sure your dependencies are not marked as api

As you have stated yourself, the minimizing works. I also did some tests myself (see below) with many dependencies. The jar file was at 187MB without minimizing and had so many entries I needed to enable zip64. Shadow minimized it to just 25MB.

Now, where do those 25MB come from? From what I have seen in my testing there are two types of files that stay:

  • general resource files (in fact the META-INF folder was at 24MB in my test case)
  • package-info.class and module-info.class (though there size is probably far less)

Those can not be stripped automatically since it is unclear whether they are used or not.

If you are 100% sure you do not need some of those files you could filter them out from your jar manually. Although I doubt that it is worth the effort.

If you think there are other files included as well, you could extract the Jar (a jar is just a zip with a different name extension) and look at its content.

Test Case

First off, I cloned Spring Boot, then I removed all unnecessary modules (that is everything except spring-boot-project:spring-boot, spring-boot-project:spring-boot-dependencies and spring-boot-project:spring-boot-parent) and changed all dependencies of spring-boot-project:spring-boot to be of type implementation. Then I removed the current source code and replaced it with your sample Main class. After adding the shadow plugin I got a jar with a size of 187MB. When I then applied minimize it was only 25MB.

I then extracted the Jar and ran some commands to remove every package-info, module-info and general resource file:

$> find \( \( -not -name "*.class" -or -name "package-info.class" -or -name "module-info.class" \) -and -not -type d \) -delete
$> find -empty -delete

I do not know if there is an equivalent command on Windows. In any case, doing the above leaves only a few number of class files left (144kB!), so I think it is save to say that shadow is doing its job.

N1K-x
  • 46
  • 1
  • 3
  • so if it "does not care what the main class is" seems i misjudged what this is supposed to do, its pointless the way it works, you could simply remove those jars manually by hand and have the same outcome... seems i have to waste my time with proguard afterall – vach Feb 08 '22 at 10:52
  • I do not think it is pointless. It does more than removing whole jars, it in fact removes only the single classes from the jar which you do not use. But if you want to remove more then jeah, probably proguard can handle it. – N1K-x Feb 09 '22 at 11:05
  • I guess it does start at your source set and not your main class because it just assumes that everything in your sources is there for a good reason and that you want to include them in your jar. Same for your API dependencies, those do not get removed and instead used as entry point for classpath scanning, because shadow assumes they need to be available (they are API after all). – N1K-x Feb 09 '22 at 11:14