Open xiaos opened 5 years ago
@HGuillemet @Nackloose Could you please make sure that we don't need to update any rules here? https://github.com/bytedeco/javacv/wiki/Configuring-Proguard-for-JavaCV
This answer is not specific to OpenCV but to any JavaCPP preset, and not really specific to Android either.
If you keep the default configuration proguard-android.txt
or proguard-android-optimize.txt
, the annotations and the native name (for linkage) are already preserved, at least with recent versions of Android Studio.
What must be added is the preservation of the classes loaded by JavaCPP by Class.forName
, of the members accessed by reflection, and of the methods and fields accessed from native code.
Any class targeted by a "global" or "target" property could be loaded using Class.forName
.
Such class doesn't necessary have an annotation, or even a native method. The only way I see to target such class with a proguard directive is that it extends a class with a @Properties
annotation.
@saudet, is this always the case ?
Also, the type of the members of the classes mapping native classes are loaded with Class.forName
(in Loader.putMemberOffset
). We must thus preserve all classes mapping native type with their members and associated descriptor classes. These classes are JavaCPP-annotated and we can use this to target them with Proguard. This will somewhat limit the shrinking since a lot of unused classes could be preserved this way, but I don't think there is another option.
Java Method called by native code and that are not standard classes are Loader.putMemberOffset
and Pointer.init
.
There are also callbacks. But I guess those are always in annotated classes.
Java Fields accessed from native code are the Pointer
fields, Pointer.NativeDeallocator.ownerAddress
and the value
of Generator.*Enum
.
A method accessed by reflection from Java code is the constructor of Pointer
subclasses taking a Pointer
.
@saudet, can you think of something else ?
So putting it all together, if we preserve:
Loader.putMemberOffset
Pointer.init
Pointer
fieldsNativeDeallocator
and its fieldsGenerator.*Enum
and their fieldPointer
subclasses taking a Pointer
we should be safe.
# The following should already be in proguard-android.txt or proguard-android-optimize.txt
-keepattributes *Annotation*,EnclosingMethod
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
# Add includedescriptorclasses, compared to proguard-android.txt
-keepclasseswithmembernames,includedescriptorclasses class * {
native <methods>;
}
# JavaCPP specific
-keep,includedescriptorclasses @org.bytedeco.javacpp.annotation.* class * {
*;
}
-keep,includedescriptorclasses class * extends @org.bytedeco.javacpp.annotation.* * {
*;
}
-keep class org.bytedeco.javacpp.Loader {
java.lang.Class putMemberOffset(java.lang.String, java.lang.String, int);
}
-keep class org.bytedeco.javacpp.Pointer {
void init(long, long, long, long);
<fields>;
}
-keep class org.bytedeco.javacpp.Pointer$NativeDeallocator {
<fields>;
}
-keep class org.bytedeco.javacpp.tools.Generator$*Enum {
<fields>;
}
-keepclassmembers class * extends org.bytedeco.javacpp.Pointer {
<init>(org.bytedeco.javacpp.Pointer);
}
-dontwarn org.bytedeco.javacpp.**
-dontnote org.bytedeco.javacpp.**
Could you give it a try ? And it it doesn't work, what are the symptoms ? Any warning or exception ?
@HGuillemet Thanks for looking into this! It sounds about right, but I don't know exactly how specific we need to get with those rules. In the case of GraalVM Native Image, we only need to specify the names to keep for JNI and reflection, but just adding rules to keep all annotations from JavaCPP to that list should be enough, without making things too complicated:
https://github.com/bytedeco/javacpp/blob/master/src/main/java/org/bytedeco/javacpp/tools/Generator.java#L1910-L1942
One thing missing from the rules above are subclasses from LoadEnabled
and FunctionPointer
, but also classes with @Virtual
methods in them. Is there a way to tell ProGuard to keep the names of the methods of those classes?
In any case, let's try to make those rules as simple as possible by making them as general as possible, for example by just keeping everything (or almost everything) from defined sets of classes, for sets that are small enough. That will help reduce maintenance cost. If someone really needs to chop down things even further, it will always be possible to do it, if/when the need arises.
I wasn't aware of these json files aiming GraalVM. The generic rules above that keep everything of the JavaCPP-annotated classes or their subclass should give similar result. I tested them on one of my application using the Pytorch, OpenCV and FFmpeg presets on Linux, shrinking all classes but the Java runtime ones, and it works fine.
Don't all classes implementing LoadEnabled
or extending FunctionPointer
also have a JavaCPP annotation ? If yes, this case is covered by the rule above.
Does it make sense to have a class with a @Virtual
method that doesn't have an annotation itself ?
Once we converged on the proguard config, I suggest to replace the old wiki entry from Nackloose by one more general and up to date about Proguard and JavaCPP.
LoadEnabled
probably always have annotations, but FunctionPointer
and classes with @Virtual
don't necessarily have annotations if they are enclosed in a class with annotations. I'm not sure if it's a good idea or not to keep everything for all Pointer
classes either. In the case of GraalVM Native Image, the binary size severely blows up, but I don't think it's the case with Android, so that should be alright...
BTW, why did you include special rules for Pointer
, Generator
, and Loader
if the idea is just to keep everything anyway?
Once we converged on the proguard config, I suggest to replace the old wiki entry from Nackloose by one more general and up to date about Proguard and JavaCPP.
Yup, that's the plan.
LoadEnabled
probably always have annotations, butFunctionPointer
and classes with@Virtual
don't necessarily have annotations if they are enclosed in a class with annotations.
Ok. I'm not sure it's possible to target "classes enclosed in a class with a JavaCPP annotation". So I added specific rules for @Virtual
and FunctionPointer
.
I also added InnerClasses
in the list of kept attributes, because intropection is used in the code to find enclosing classes.
I'm not sure if it's a good idea or not to keep everything for all
Pointer
classes either. In the case of GraalVM Native Image, the binary size severely blows up, but I don't think it's the case with Android, so that should be alright...
I'm realizing that Pointer
is annotated with @Properties
. So indeed all Pointer subclasses will be kept, which is not necessary.
This rule about subclasses of annotated classes is here for keeping classes that are "global" or "target".
For instance if I remove it, my app fails with ClassNotFoundException: org.bytedeco.openblas.global.openblas_nolapack
because openblas_nolapack is loaded with Class.forName
when openblas is first loaded and it has no annotations.
We could add a dummy @Keep
annotations or @Target
just for this use case. Do you have a better idea ?
BTW, why did you include special rules for
Pointer
,Generator
, andLoader
if the idea is just to keep everything anyway?
The rules on Pointer
and Loader
are redundant indeed since they are annotated. I removed them.
The rules for enums in Generator
and for NativeDeallocator
are needed,
New version:
# The following should already by in proguard-android.txt or proguard-android-optimize.txt
-keepattributes *Annotation*,EnclosingMethod,InnerClasses
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
# Add includedescriptorclasses, compared to proguard-android.txt
-keepclasseswithmembernames,includedescriptorclasses class * {
native <methods>;
}
# JavaCPP specific
-keepclasseswithmember,includedescriptorclasses class * {
@org.bytedeco.javacpp.annotation.Virtual <methods>;
}
-keepclasseswithmember,includedescriptorclasses class * extends org.bytedeco.javacpp.FunctionPointer {
native <methods>;
}
-keep,includedescriptorclasses @org.bytedeco.javacpp.annotation.* class * {
*;
}
-keep,includedescriptorclasses class * extends @org.bytedeco.javacpp.annotation.* * {
*;
}
-keep class org.bytedeco.javacpp.Pointer$NativeDeallocator {
<fields>;
}
-keep class org.bytedeco.javacpp.tools.Generator$*Enum {
<fields>;
}
-dontwarn org.bytedeco.javacpp.**
-dontnote org.bytedeco.javacpp.**
This still seems unnecessarily long, for example, doesn't InnerClasses keep org.bytedeco.javacpp.Pointer$NativeDeallocator?
No, InnerClasses
is just the class attribute, that is the link between an enclosed class and the enclosing class, so that the information is available by reflection. It does't keep the inner classes themselves.
It is worked for me!
# JavaCV
-keep @org.bytedeco.javacpp.annotation interface * {
*;
}
-keep @org.bytedeco.javacpp.annotation.Platform public class *
-keepclasseswithmembernames class * {
@org.bytedeco.* <fields>;
}
-keepclasseswithmembernames class * {
@org.bytedeco.* <methods>;
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keepclasseswithmembernames,includedescriptorclasses class * {
native <methods>;
}
-keepclasseswithmembernames,includedescriptorclasses class * {
@org.bytedeco.javacpp.annotation.Virtual <methods>;
}
-keepclasseswithmembernames,includedescriptorclasses class * extends org.bytedeco.javacpp.FunctionPointer {
native <methods>;
}
-keep,includedescriptorclasses @org.bytedeco.javacpp.annotation.* class * {
*;
}
-keep,includedescriptorclasses class * extends @org.bytedeco.javacpp.annotation.* * {
*;
}
-keep class org.bytedeco.javacpp.Pointer$NativeDeallocator {
<fields>;
}
-keep class org.bytedeco.javacpp.tools.Generator$*Enum {
<fields>;
}
-keepattributes *Annotation*,EnclosingMethod,InnerClasses
-keep @interface org.bytedeco.javacpp.annotation.*,javax.inject.*
-keepattributes *Annotation*, Exceptions, Signature, Deprecated, SourceFile, SourceDir, LineNumberTable, LocalVariableTable, LocalVariableTypeTable, Synthetic, EnclosingMethod, RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations, RuntimeVisibleParameterAnnotations, RuntimeInvisibleParameterAnnotations, AnnotationDefault, InnerClasses
-keep class org.bytedeco.javacpp.** {*;}
-dontwarn java.awt.**
-dontwarn org.bytedeco.javacv.**
-dontwarn org.bytedeco.javacpp.**
-dontwarn org.bytedeco.openblas.**
-dontwarn org.bytedeco.opencv.**
In the latest version 1.5, the package names are changed, anyone can provide the proguard file on Android platform?
I tried to changed the package name in proguard file, but not working.