Too many classes in --main-dex-list, main dex capacity exceeded with javafxports-plugin - javafxports

I am trying to make my application run on an android device by using the jfxmobile-plugin.
When I do a
gradle android
on my project I get
Execution failed for task ':dex'.
> UNEXPECTED TOP-LEVEL EXCEPTION:
com.android.dex.DexException: Too many classes in --main-dex-list, main dex capacity exceeded
at com.android.dx.command.dexer.Main.processAllFiles(Main.java:546)
at com.android.dx.command.dexer.Main.runMultiDex(Main.java:366)
at com.android.dx.command.dexer.Main.run(Main.java:275)
at com.android.dx.command.dexer.Main.main(Main.java:245)
at com.android.dx.command.Main.main(Main.java:106)
My gradle file is this
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'org.javafxports:jfxmobile-plugin:1.3.2'
}
}
apply plugin: 'org.javafxports.jfxmobile'
mainClassName = 'de.package.of.application.ClientApp'
dependencies {
compile 'com.annimon:stream:1.0.1'
compile 'com.jakewharton.threetenabp:threetenabp:1.0.4'
compile 'com.gluonhq:charm:4.2.0'
androidRuntime 'com.gluonhq:charm-android:3.0.0'
iosRuntime 'com.gluonhq:charm-ios:3.0.0'
desktopRuntime 'com.gluonhq:charm-desktop:3.0.0'
compile fileTree(dir: 'target/dependencies', include: '*.jar')
runtime fileTree(dir: 'target/dependencies', include: '*.jar')
}
repositories {
jcenter()
maven {
url 'http://nexus.gluonhq.com/nexus/content/repositories/releases'
}
}
jfxmobile {
ios {
forceLinkClasses = ['ensemble.**.*']
}
android {
//manifest = 'AndroidManifest.xml'
compileSdkVersion = '24'
androidSdk='C:/Tools/android-sdk-windows'
packagingOptions {
exclude 'META-INF/DEPENDENCIES.txt'
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/NOTICE.txt'
exclude 'META-INF/NOTICE'
exclude 'META-INF/LICENSE'
//... some more excludes
}
dexOptions {
javaMaxHeapSize '3g'
// keepRuntimeAnnotatedClasses false
}
}
}
I found some similar questions about the exception logged by the gradle build but non brought a real solution for me so far especially in combination with the usage of the javafxports-plugin.
Some suggested to set the compileSdkVersion to a minumum of 21-> I
use 24 -> this does not help.
Using keepRuntimeAnnotatedClasses=false is not an option because the
application is build on some Reflection/injection-logic
(spring-framework).
As I understand the build process takes those classes into account to
put in the main-dex-file which are required to initially start up the
application. So I stripped down the application to an absolut minimum
with no other dependencies (direct or transitive) to the other
libraries of my application (no imports of classes from other
libraries, except to java/javafx). The other libraries are
included by the 'runtime fileTree...' part of the gradle-file but
are not really used at startup. Idea was to create a 2-phase
startup of the application: First a minimum-app and then, when
the minimum app is loaded, start the real application code. -> no
success, it seems all libraries are taken into account for the
main-dex-file.
I do not see how to solve it together with javafxports. Does anyone have suggestions?

Related

Use Gradle to generate Java classes from a Swagger definition into a standalone JAR

I am fairly new to both Gradle and Swagger code generator plugin for it (concretely the one that is linked from Swagger's website, i.e. https://github.com/thebignet/swagger-codegen-gradle-plugin), so I'm not sure whether my problem is with Gradle in general or with that particular plugin.
I've created a simple multi-module Spring Boot application (but the fact that I'm using Spring Boot or even Spring doesn't matter much). It's a console application; i.e. it doesn't start a webserver. In fact, it's actually a REST client consuming someone else's interface.
The application consists of four modules: spc-parent (which is just an envelope for the rest) containing spc-boot, spc-service, and spc-integration-model. Spc-boot contains just the starting point of the application, spc-service now contains a single Spring service, and spc-integration-model is meant to contain classes needed to consume the REST interface. The resulting structure will be much more complicated but I've tried to create a sort of a minimal example.
The problem lies within the spc-integration-model module. It consists of a single source file, petstore.json, and a build.gradle copied from https://github.com/thebignet/swagger-codegen-gradle-plugin (and only slightly modified). There are actually two problems (but they may have the same underlying cause).
When running gradle build (from spc-parent) for the very first time, it fails. Java sources are generated from petstore.json but they don't get compiled, which is why the service in spc-service doesn't see needed classes. However, running gradle build a second time fixes this (generated Java sources get compiled which makes it possible to compile spc-service, too).
The created JAR of spc-integration-model never contains anything besides Manifest.
My goal here is to persuade Gradle to compile the generated classes right away during the first build and also to put them into the JAR.
Now for some concrete Gradle tasks. The most interesting is spc-integration-model's build.gradle:
plugins {
id 'org.detoeuf.swagger-codegen' version '1.7.4'
id 'java'
}
apply plugin: 'org.detoeuf.swagger-codegen'
repositories {
mavenCentral()
jcenter()
}
swagger {
inputSpec = 'http://petstore.swagger.io/v2/swagger.json'
outputDir = file('build/swagger')
lang = 'java'
additionalProperties = [
'apiPackage' : 'ondra.spc.integration.client.api',
'dateLibrary' : 'java8',
'hideGenerationTimestamp': 'true',
'invokerPackage' : 'ondra.spc.integration.client',
'library' : 'resttemplate',
'modelNameSuffix' : 'Dto',
'modelPackage' : 'ondra.spc.integration.client.model'
]
importMappings = [
'Dog': 'io.swagger.petstore.client.model.Dog'
]
}
sourceSets {
swagger {
java {
srcDir file("${project.buildDir.path}/swagger/src/main/java")
}
}
}
classes.dependsOn('swagger')
ext {
spring_boot_version = springBootVersion
jackson_version = jacksonVersion
junit_version = jUnitVersion
swagger_annotations_version = swaggerAnnotationsVersion
swagger_codegen_version = swaggerCodegenVersion
}
dependencies {
swaggerCompile "org.springframework.boot:spring-boot-starter-web:$spring_boot_version"
swaggerCompile "io.swagger:swagger-annotations:$swagger_annotations_version"
compile sourceSets.swagger.output
compile "com.fasterxml.jackson.core:jackson-core:$jackson_version"
compile "com.fasterxml.jackson.core:jackson-annotations:$jackson_version"
compile "com.fasterxml.jackson.core:jackson-databind:$jackson_version"
compile "io.swagger:swagger-codegen:$swagger_codegen_version"
testCompile "junit:junit:$junit_version"
}
(Now that I'm re-reading my question I see that the local version of petstore.json is actually not used and an online version is used instead but let's leave that aside.)
The rest should be quite straightforward. spc-service:
dependencies {
compile "org.springframework:spring-context:$springVersion"
compile "org.springframework:spring-web:$springVersion"
compile project (":spc-integration-model")
}
spc-boot:
dependencies {
compile "org.springframework.boot:spring-boot:$springBootVersion"
compile "org.springframework.boot:spring-boot-autoconfigure:$springBootVersion"
compile "org.springframework:spring-web:$springVersion"
runtime "org.hibernate.validator:hibernate-validator:$hibernateVersion"
runtime "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion"
compile project (":spc-service")
testCompile("org.junit.jupiter:junit-jupiter-api:$jUnitVersion")
testRuntime("org.junit.jupiter:junit-jupiter-engine:$jUnitVersion")
}
test {
useJUnitPlatform()
}
spc-parent:
subprojects {
apply plugin: 'java'
group 'ondra'
version '1.0-SNAPSHOT'
buildscript {
ext {
hibernateVersion = '6.0.9.Final'
jacksonVersion = '2.9.4'
springBootVersion = '2.0.0.RELEASE'
springVersion = '5.0.5.RELEASE'
swaggerAnnotationsVersion = '1.5.16'
swaggerCodegenVersion = '2.2.3'
jUnitVersion = '5.1.1'
}
repositories {
mavenCentral()
}
}
sourceCompatibility = 1.8
targetCompatibility = 1.8
repositories {
mavenCentral()
}
}
And spc-parent's settings.gradle:
rootProject.name = 'spc-parent'
include 'spc-boot'
include 'spc-service'
include 'spc-integration-model'
I've also put the whole application into a single archive: https://drive.google.com/open?id=1cOYIcaxnhik548w0wEGswgD2g4udATdD

Gradle not finding org.apache.commons:commons-imaging

Gradle cannot resolve org.apache.commons:commons-imaging, yet it is available in all 3 of the provided repositories:
http://jump-pilot.sourceforge.net/repository
https://repo.adobe.com/nexus/content/repositories/public/
https://repository.apache.org/content/repositories/snapshots/
This part of the log is interesting, Gradle looks only in jcenter and the local filesystem:
https://jcenter.bintray.com/org/apache/commons/commons-imaging/1.0-SNAPSHOT/maven-metadata.xml
https://jcenter.bintray.com/org/apache/commons/commons-imaging/1.0-SNAPSHOT/commons-imaging-1.0-SNAPSHOT.pom
https://jcenter.bintray.com/org/apache/commons/commons-imaging/1.0-SNAPSHOT/commons-imaging-1.0-SNAPSHOT.jar
file:/Users/user/Library/Android/sdk/extras/android/m2repository/org/apache/commons/commons-imaging/1.0-SNAPSHOT/maven-metadata.xml
file:/Users/user/Library/Android/sdk/extras/android/m2repository/org/apache/commons/commons-imaging/1.0-SNAPSHOT/commons-imaging-1.0-SNAPSHOT.pom
file:/Users/user/Library/Android/sdk/extras/android/m2repository/org/apache/commons/commons-imaging/1.0-SNAPSHOT/commons-imaging-1.0-SNAPSHOT.jar
file:/Users/user/Library/Android/sdk/extras/google/m2repository/org/apache/commons/commons-imaging/1.0-SNAPSHOT/maven-metadata.xml
file:/Users/user/Library/Android/sdk/extras/google/m2repository/org/apache/commons/commons-imaging/1.0-SNAPSHOT/commons-imaging-1.0-SNAPSHOT.pom
file:/Users/user/Library/Android/sdk/extras/google/m2repository/org/apache/commons/commons-imaging/1.0-SNAPSHOT/commons-imaging-1.0-SNAPSHOT.jar
Console log (requesting org.apache.commons:commons-imaging:1.0-SNAPSHOT):
$ react-native run-android
JS server already running.
Building and installing the app on the device (cd android && ./gradlew installDebug)...
FAILURE: Build failed with an exception.
* What went wrong:
A problem occurred configuring project ':app'.
> Could not resolve all dependencies for configuration ':app:_debugCompile'.
> Could not find org.apache.commons:commons-imaging:1.0-SNAPSHOT.
Searched in the following locations:
https://jcenter.bintray.com/org/apache/commons/commons-imaging/1.0-SNAPSHOT/maven-metadata.xml
https://jcenter.bintray.com/org/apache/commons/commons-imaging/1.0-SNAPSHOT/commons-imaging-1.0-SNAPSHOT.pom
https://jcenter.bintray.com/org/apache/commons/commons-imaging/1.0-SNAPSHOT/commons-imaging-1.0-SNAPSHOT.jar
file:/Users/user/Library/Android/sdk/extras/android/m2repository/org/apache/commons/commons-imaging/1.0-SNAPSHOT/maven-metadata.xml
file:/Users/user/Library/Android/sdk/extras/android/m2repository/org/apache/commons/commons-imaging/1.0-SNAPSHOT/commons-imaging-1.0-SNAPSHOT.pom
file:/Users/user/Library/Android/sdk/extras/android/m2repository/org/apache/commons/commons-imaging/1.0-SNAPSHOT/commons-imaging-1.0-SNAPSHOT.jar
file:/Users/user/Library/Android/sdk/extras/google/m2repository/org/apache/commons/commons-imaging/1.0-SNAPSHOT/maven-metadata.xml
file:/Users/user/Library/Android/sdk/extras/google/m2repository/org/apache/commons/commons-imaging/1.0-SNAPSHOT/commons-imaging-1.0-SNAPSHOT.pom
file:/Users/user/Library/Android/sdk/extras/google/m2repository/org/apache/commons/commons-imaging/1.0-SNAPSHOT/commons-imaging-1.0-SNAPSHOT.jar
Required by:
ReactNativeProject:app:unspecified > ReactNativeProject:react-native-image-store-ext:unspecified
Console log (requesting org.apache.commons:commons-imaging:+):
$ react-native run-android
JS server already running.
Building and installing the app on the device (cd android && ./gradlew installDebug)...
FAILURE: Build failed with an exception.
* What went wrong:
A problem occurred configuring project ':app'.
> Could not resolve all dependencies for configuration ':app:_debugCompile'.
> Could not find any matches for org.apache.commons:commons-imaging:+ as no versions of org.apache.commons:commons-imaging are available.
Required by:
ReactNativeProject:app:unspecified > ReactNativeProject:react-native-image-store-ext:unspecified
build.gradle:
buildscript {
repositories { jcenter() }
dependencies { classpath 'com.android.tools.build:gradle:2.1.0' }
}
apply plugin: 'com.android.library'
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
defaultConfig {
minSdkVersion 16
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
lintOptions { abortOnError false }
}
repositories {
mavenCentral()
maven { url "http://jump-pilot.sourceforge.net/repository" }
maven { url "https://repo.adobe.com/nexus/content/repositories/public/" }
maven { url "https://repository.apache.org/content/repositories/snapshots/" }
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.4.0'
compile "com.facebook.react:react-native:+"
compile 'org.apache.commons:commons-imaging:+'
}
There are 2 build.gradle file in android project: the one inside main project folder and the one inside app folder. The repository should be defined under allprojects block inside project folder. Here, example of my build.gradle which successful build apache commons imaging.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
maven { url "http://jump-pilot.sourceforge.net/repository" }
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
Apparently, there is no oficial version for apache commons imaging yet.
The latest unoficial version I have found, is on this repo
maven { url "http://wcm.io/maven/repositories/apache-intermediate-release/" }
Add this line on the build.gradle as opris said.
Then, in your gradle app/module file, add this
dependencies {
...
implementation 'org.apache.commons:commons-imaging:1.0-R1725432'
...
}
I haven't tested it yet, so I will have to choose between this one and the oficial version of sanselan,
compile 'org.apache.sanselan:sanselan:0.97-incubator'
which has no more support

JavaxPorts jfxmobile-plugin: Failure if classes more than 65535

When I do a
gradle android
with the jfxmobile-plugin on my JavaFX-application I get
...
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':mergeClassesIntoJar'.
> archive contains more than 65535 entries.
To build this archive, please enable the zip64 extension.
See: https://docs.gradle.org/3.2.1/dsl/org.gradle.api.tasks.bundling.Zip.html#org.gradle.api.tasks.bundling.Zip:zip64
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
BUILD FAILED
The gradle file looks like this:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'org.javafxports:jfxmobile-plugin:1.3.2'
}
}
apply plugin: 'org.javafxports.jfxmobile'
mainClassName = 'de.package.of.application.ClientApp'
dependencies {
compile 'com.annimon:stream:1.0.1'
compile 'com.jakewharton.threetenabp:threetenabp:1.0.4'
compile 'com.gluonhq:charm:3.0.0'
androidRuntime 'com.gluonhq:charm-android:3.0.0'
iosRuntime 'com.gluonhq:charm-ios:3.0.0'
desktopRuntime 'com.gluonhq:charm-desktop:3.0.0'
compile fileTree(dir: 'target/dependencies', include: '*.jar')
runtime fileTree(dir: 'target/dependencies', include: '*.jar')
}
repositories {
jcenter()
maven {
url 'http://nexus.gluonhq.com/nexus/content/repositories/releases'
}
}
jfxmobile {
ios {
forceLinkClasses = ['ensemble.**.*']
}
android {
applicationPackage = 'org.javafxports.ensemble'
compileSdkVersion = '21'
androidSdk='C:/Tools/android-sdk-windows'
}
}
Over the last years since Javafx exists the application has become, I would say, a 'bigger' one; a fat-client with many own classes but also many third-party dependencies.
I found an article about the jfxmobile-plugin and thought 'Why not give it a try?'. The current issue is the one described in this question.
I already stripped down the application to reduce classes to a minimum so the application still makes some sense in some way but it does not help.
Does anyone have a suggestions how to solve it?

What are .kotlin_builtins files and can I omit them from my uberjars?

I'm working on integrating proguard to my gradle build for an application written in Kotlin. I'm finding that proguard is stripping out the Kotlin standard library (as it should in my simple Hello World program) but it's leaving a bunch of files in my jar that have the file extension .kotlin_builtins. When I configure my gradle task to exclude those files, the program still appears to work fine. What are those files and must they ship with my executable uberjar?
Here's my build.gradle file's contents for reference:
buildscript {
ext.kotlin_version = '1.0.5'
ext.shadow_version = '1.2.4'
repositories {
mavenCentral()
maven {
url "https://plugins.gradle.org/m2/"
}
flatDir dirs: "gradle/proguard"
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "com.github.jengelman.gradle.plugins:shadow:$shadow_version"
classpath ":proguard:"
}
}
apply plugin: 'kotlin'
apply plugin: 'application'
apply plugin: 'com.github.johnrengelman.shadow'
mainClassName = 'namespace.MainKt'
defaultTasks 'run'
repositories {
mavenCentral()
}
dependencies {
compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
testCompile "junit:junit:4.12"
testCompile "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version"
}
shadowJar {
exclude 'kotlin/**/*.kotlin_builtins'
exclude '.keep'
}
task minify(type: proguard.gradle.ProGuardTask, dependsOn: 'shadowJar') {
libraryjars "${System.getProperty('java.home')}/lib/rt.jar"
injars 'build/libs/artful-all.jar'
outjars 'build/libs/artful-all.out.jar'
printmapping 'build/libs/out.map'
keepclasseswithmembers 'public class * { \
public static void main(java.lang.String[]); \
}'
assumenosideeffects 'class kotlin.jvm.internal.Intrinsics { \
static void checkParameterIsNotNull(java.lang.Object, java.lang.String); \
}'
}
These files contain data for declarations of standard ("built-in") Kotlin classes which are not compiled to .class files, but rather are mapped to the existing types on the platform (in this case, JVM). For example, kotlin/kotlin.kotlin_builtins contains the information for non-physical classes in package kotlin: Int, String, Enum, Annotation, Collection, etc.
There are two main scenarios when these files are used:
The compiler looks them up from kotlin-stdlib on the classpath to determine which built-in declarations are available.
The reflection library (kotlin-reflect) loads these files as resources to provide reflection capabilities for built-in declarations. For example, String::class.members returns all members of the class kotlin.String exactly in the same way as the Kotlin compiler sees those members (despite the fact that there's no kotlin/String.class file and it's erased to java.lang.String in bytecode).
The first point is clearly not applicable in your case. And if you don't use reflection on built-in classes, I think it's safe to exclude .kotlin_builtins files completely from the resulting jar.
You can optimize/omit these from yours JARs/APKs:
packagingOptions {
exclude "/META-INF/*.kotlin_module"
exclude "**/kotlin/**"
}
Even better:
packagingOptions {
exclude "/META-INF/*.kotlin_module"
exclude "**/kotlin/**"
exclude "**/*.txt"
exclude "**/*.xml"
exclude "**/*.properties"
}
Source: https://github.com/jaredsburrows/android-gif-example/blob/master/build.gradle.kts#L127

Building a fully executable Spring Boot 1.3 war from a Gradle multi project build

I'm trying to build a fully executable WAR using Spring Boot 1.3 as per https://docs.spring.io/spring-boot/docs/current/reference/html/deployment-install.html. If I build a single Gradle project, it all works fine, but I havea multi project build, where I have a "root" project and then several projects underneath it, and I cannot get it to build anything but a standard, "fat" WAR file, without the providedRuntime of Jetty and without the scripts to make it run.
Does anyone know how to do this?
In my root project, I have the following (abridged):
buildscript {
repositories {
mavenCentral()
}
ext {
springBootVersion = '1.3.0.RELEASE'
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion"
}
}
allprojects {
//Put instructions for all projects
repositories {
mavenCentral() // jcenter is missing spring-orm.4.1.6.RELEASE jar file so try mavenCentral first
jcenter {
url "http://jcenter.bintray.com/"
}
maven { url 'http://repo.opensourceagility.com/release' }
}
}
subprojects {
apply plugin: 'java'
apply plugin: 'spring-boot'
}
and then in the subproject which is a web project, and which I'm trying to build, I have:
apply plugin: 'war'
dependencies {
// Include related projects
compile project(':project-model')
compile project(':project-dynamoDB')
// Core Spring Boot - note version is set in main build.gradle file
compile 'org.springframework.boot:spring-boot-starter-web'
// Remove Tomcat (included in -web) and include Jetty instead
providedRuntime 'org.springframework.boot:spring-boot-starter-jetty'
// Other Spring modules
compile 'org.springframework.boot:spring-boot-starter-social-facebook'
compile 'org.springframework.boot:spring-boot-starter-social-linkedin'
compile 'org.springframework.social:spring-social-google:1.0.0.RELEASE'
compile 'org.springframework.boot:spring-boot-starter-thymeleaf'
compile 'org.springframework.boot:spring-boot-starter-security'
compile 'org.springframework.boot:spring-boot-starter-actuator'
compile 'org.springframework.boot:spring-boot-devtools'
compile 'org.springframework:spring-context'
compile 'org.springframework:spring-context-support'
}
configurations {
providedRuntime.exclude group: 'org.springframework.boot', module:'spring-boot-starter-tomcat'
all*.exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' // exclude when using log4j
}
springBoot {
mainClass = 'rs.web.Weblication'
executable = true
}
bootRun {
addResources = true
}
processResources {
// exclude resources if they look like they're profile dependent but don't match the current env/profile
eachFile { d ->
if(d.name.endsWith('.xml') || d.name.endsWith('.yaml') || d.name.endsWith('.properties')) {
//def fname = d.name.replaceFirst(~/\.[^\.]+$/, '')
//if(fname.indexOf("-") > -1 && ! fname.endsWith("-" + environment)) {
// d.exclude()
//} else {
// replace #variables# listed below in properties/config files
filter(org.apache.tools.ant.filters.ReplaceTokens, tokens: [
activeProfiles: environment
])
//}
}
}
}
war {
baseName = 'project-web'
version = '1.0.0'
manifest {
attributes 'Implementation-Title': baseName,
'Implementation-Version': version
}
webXml = file('src/main/resources/web.xml')
// rename the war task which has profiles appended from warName-profile,profile2.war
// to warName-profile.profile2.war
classifier = environment.replaceAll(',','-')
}
but when I build it (./gradlew build, or ./gradlew subprojectname:build), all is well and a working WAR is created, but not an executable one.
With a single project, I have it working fine.
Ah ha, right well I build a test multi-project build and it worked OK, so it was clearly the configuration above.
I worked through a process of elimination and it turns out that the problematic area was the line
classifier = environment.replaceAll(',','-')
which is intended to rename files with environment variables as part of the name. This process seems to get in the way of the script addition; perhaps it could be applied afterwards if it's really necessary.

Resources