NightRa
NightRa

Reputation: 11841

Proguard with Scala - Error 4

Proguard 5.1 fails during the shrinking phase with "Error: 4".
The proguard configuration file is as follows:

-injars      ReversiRaw.jar
-outjars     Reversi.jar
-libraryjars <java.home>/lib/rt.jar
-libraryjars <java.home>/lib/ext/jfxrt.jar

-dontwarn scala.**

-dontnote
-dontwarn
-ignorewarnings

-keepclasseswithmembers public class * {
    public static void main(java.lang.String[]);
}

-keepclassmembers class * {
    ** MODULE$;
}

Which is mostly the default Scala configuration given in the documentation.
A download link to the jar file being compreseed for reproducing: download

What is that "Error: 4", and how can it be fixed or avoided?

Upvotes: 3

Views: 561

Answers (3)

Richard Goulter
Richard Goulter

Reputation: 51

While prosseek's answers was helpful, I was able to get a simple Hello-World proguard configuration working without having to resort to class files. (Unlike prosseek, I didn't try an Uberjar, though).

Adapting from the Scala example in the Proguard manual, I got a simple *.pro would look something like:

-injars      build/install/HelloScala/lib/helloscala-1.0.jar
-injars      build/install/HelloScala/lib/scala-library-2.11.6.jar(!META-INF/MANIFEST.MF)
-outjars     helloscalapro.jar
-libraryjars <java.home>/lib/rt.jar

-dontwarn scala.**

//etc.

Filtering out the manifest from the Scala library.

I also got Error 4 when the version of the Scala library I passed to injar was a different (patch) version than the Scala version I'd compiled it with.

Upvotes: 1

prosseek
prosseek

Reputation: 191099

The other solution might be using gradle.

This is the gradle source to build, get the fat file (shadowJar file), and shrink the jar file with proguard.

buildscript {
    repositories { 
        flatDir dirs: '/Users/smcho/Dropbox/smcho/bin/jar/proguard5.2/lib'
        mavenCentral()
        jcenter()
    }
    dependencies {
        classpath ':proguard'
        classpath 'com.github.jengelman.gradle.plugins:shadow:1.2.0'
    }
}

apply plugin: 'scala'
apply plugin: 'com.github.johnrengelman.shadow'

repositories{
    mavenCentral()
}

shadowJar {
  manifest {
    attributes 'Main-Class': 'HelloWorld'
  }
}

dependencies{
    compile "org.scala-lang:scala-library:2.11.5"
}

task proguard(type: proguard.gradle.ProGuardTask) {
//    injars './build/classes'
    injars './build/libs/scala_all_jar-all.jar'
    outjars './build/libs/main.jar'
    libraryjars "${System.getProperty('java.home')}/lib/rt.jar"

    dontwarn
    dontnote
    ignorewarnings

    keepattributes 'SourceFile,LineNumberTable'

    keepclasseswithmembers 'public class * { \
        public static void main(java.lang.String[]); \
    }'
}

You need to set flatDir dirs to point to where your proguard files are located.

gradle build will get you the compiled classes and jar file, gradle shadowJar will get you big jar file, and gradle proguard will give you the slimmed jar file named main.jar.

I also uploaded the source: https://dl.dropboxusercontent.com/u/10773282/share/2015/scala_all_jar.zip

Upvotes: 1

prosseek
prosseek

Reputation: 191099

I also had the same issue when my injar jar file is a fat jar from plugins assembly through sbt assembly command. The solution to this problem is not to use the fat jar, but to use the classes generated from scala, and other java/scala libraries to get slimmed jar file.

On my mac (OS X 10.10), I have this proguard file (named your.pro).

-injar classes
-injar /usr/local/Cellar/scala/2.11.5/libexec/lib/scala-library.jar(!META-INF/MANIFEST.MF)
-outjar main.jar
-libraryjar /System/Library/Frameworks/JavaVM.framework/Versions/1.6/Classes/classes.jar

-dontwarn
-dontnote
-ignorewarnings

-optimizationpasses 2
-optimizations !code/allocation/variable

-keep,allowoptimization,allowshrinking class * { *; }
-keepattributes SourceFile,LineNumberTable

-keepclasseswithmembers public class * { public static void main(java.lang.String[]); }

This is the directory structure.

.
├── classes
│   ├── HelloWorld$.class
│   ├── HelloWorld$delayedInit$body.class
│   ├── HelloWorld.class
│   └── META-INF
│       └── MANIFEST.MF
├── src
│   └── hello.scala
└── your.pro

hello.scala is as follows:

object HelloWorld extends App 
{
    println("Hello, world")
}

You can get the class files in the classes directory with scalac src/hello.scala -d classes. The MANIFEST file should be added to tell which is the main class. Careful with the last blank line.

Manifest-Version: 1.0
Main-Class: HelloWorld
... blank line should be here ...

The classes file is generated from scala. With proguard @your.pro, I could get working jar file.

I uploaded the example in https://dl.dropboxusercontent.com/u/10773282/share/2015/scala_class.zip

Upvotes: 0

Related Questions