CrossPlaformModule

Run with stack trace shows a command not found :

 What went wrong:
Could not determine the dependencies of task ':desktop:fatJar'.
> Resolving dependency configuration 'implementation' is not allowed as it is defined as 'canBeResolved=false'.
  Instead, a resolvable ('canBeResolved=true') dependency configuration that extends 'implementation' should be resolved.

* Try:
Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Exception is:
org.gradle.api.internal.tasks.TaskDependencyResolveException: Could not determine the dependencies of task ':desktop:fatJar'.
        at org.gradle.api.internal.tasks.CachingTaskDependencyResolveContext.getDependencies(CachingTaskDependencyResolveContext.java:71)
        at org.gradle.execution.plan.TaskDependencyResolver.resolveDependenciesFor(TaskDependencyResolver.java:46)
        at org.gradle.execution.plan.LocalTaskNode.getDependencies(LocalTaskNode.java:163)
        at org.gradle.execution.plan.LocalTaskNode.resolveDependencies(LocalTaskNode.java:131)
        at org.gradle.execution.plan.DefaultExecutionPlan.doAddNodes(DefaultExecutionPlan.java:186)
        at org.gradle.execution.plan.DefaultExecutionPlan.addEntryTasks(DefaultExecutionPlan.java:150)
        at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph.addEntryTasks(DefaultTaskExecutionGraph.java:157)
        at org.gradle.execution.TaskNameResolvingBuildConfigurationAction.configure(TaskNameResolvingBuildConfigurationAction.java:49)
        at org.gradle.execution.DefaultBuildConfigurationActionExecuter.configure(DefaultBuildConfigurationActionExecuter.java:55)
        at org.gradle.execution.DefaultBuildConfigurationActionExecuter.access$000(DefaultBuildConfigurationActionExecuter.java:26)
        at org.gradle.execution.DefaultBuildConfigurationActionExecuter$1.proceed(DefaultBuildConfigurationActionExecuter.java:63)
        at org.gradle.execution.DefaultTasksBuildExecutionAction.configure(DefaultTasksBuildExecutionAction.java:45)
        at org.gradle.execution.DefaultBuildConfigurationActionExecuter.configure(DefaultBuildConfigurationActionExecuter.java:55)
        at org.gradle.execution.DefaultBuildConfigurationActionExecuter.access$000(DefaultBuildConfigurationActionExecuter.java:26)
        at org.gradle.execution.DefaultBuildConfigurationActionExecuter$1.proceed(DefaultBuildConfigurationActionExecuter.java:63)
        at org.gradle.execution.ExcludedTaskFilteringBuildConfigurationAction.configure(ExcludedTaskFilteringBuildConfigurationAction.java:48)
        at org.gradle.execution.DefaultBuildConfigurationActionExecuter.configure(DefaultBuildConfigurationActionExecuter.java:55)
        at org.gradle.execution.DefaultBuildConfigurationActionExecuter.lambda$select$0(DefaultBuildConfigurationActionExecuter.java:42)
        at org.gradle.internal.Factories$1.create(Factories.java:31)
        at org.gradle.api.internal.project.DefaultProjectStateRegistry.withMutableStateOfAllProjects(DefaultProjectStateRegistry.java:141)
        at org.gradle.api.internal.project.DefaultProjectStateRegistry.withMutableStateOfAllProjects(DefaultProjectStateRegistry.java:128)
        at org.gradle.execution.DefaultBuildConfigurationActionExecuter.select(DefaultBuildConfigurationActionExecuter.java:40)

what about this:


task fatJar(type: Jar) {
    manifest {
        attributes(
                "Main-Class": mainClassName
        )
    }
    baseName = project.name + '-all'
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
    from { sourceSets.main.runtimeClasspath.collect { !it.isFile() ? it : zipTree(it) } }
    with jar
}

task copyDependencies(type: Copy) {
    mkdir('build/libs/dependencies')
    from { sourceSets.main.runtimeClasspath.filter { it.isFile() } }
    into('build/libs/dependencies')
}

task releaseJar(type: Jar, dependsOn: copyDependencies) {
    manifest {
        attributes(
                "Main-Class": mainClassName,
                "Class-Path": sourceSets.main.runtimeClasspath.filter {it.isFile() }.collect { "dependencies/${it.name}" }.join(' ')
        )
    }
    baseName = project.name
    with jar
}

fatJar should create a single jar with all dependencies
releaseJar - small starter jar of the module + dependencies folder with other jars

1 Like

hi @wizzardo well i did copy the jars then traverse the folder /dependencies & concat the names of the jars into a string then pass the string to the class-Path of manifest, but your solution is far more better :slight_smile: thanks for your time :hearts:, and unless with jar is appended to the solution, the system would have created its own jar without encapsulating the compiled class files of the current module.

the code :

plugins {
    id 'application'
}

group 'com.desktopModule'
version '3.2-testcase'
mainClassName = 'com.example.desktopmodule.DesktopLauncher'
String dependenciesString = ""

java {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
}

dependencies {
    implementation project(path: ':game')
    implementation 'org.jmonkeyengine:jme3-core:3.4.0-stable'
    implementation "org.jmonkeyengine:jme3-effects:3.4.0-stable"
    implementation "org.jmonkeyengine:jme3-lwjgl:3.4.0-stable"
    implementation "org.jmonkeyengine:jme3-desktop:3.4.0-stable"
}
task copyJars(type: Copy) {
    from (sourceSets.main.runtimeClasspath){
        include '**/*.jar'
    }
    from(sourceSets.main.compileClasspath){
        include '**/*.jar'
    }
    into('build/libs/dependencies')
    includeEmptyDirs = false
    setDuplicatesStrategy(DuplicatesStrategy.EXCLUDE)
}
/**
 * Better approach described below, but the 2 of them are vial ones
 */
//task createJar(type : Jar, dependsOn : copyJars){
//    //get the copied jars
//    final String project = rootDir.getPath()
//    final File dependencies = new File(project + "/desktop/build/libs/dependencies/")
//    if(dependencies.exists()) {
//        if (dependencies.listFiles().length > 0) {
//            final File[] files = dependencies.listFiles()
//            for (int i = 0; i < files.length; i++) {
//                final String fileName = files[i].getName()
//                if (fileName.contains(".jar")) {
//                    dependenciesString += "dependencies/" + fileName + " "
//                }
//            }
//        }
//    }
//    manifest{
//        attributes('Main-Class': mainClassName)
//        //pass the jars to the manifest
//        attributes('Class-Path' : dependenciesString)
//        attributes('Created-By' : 'Jme3-Gradle')
//    }
//    with jar
//}

task releaseJar(type: Jar, dependsOn: copyJars) {
    manifest {
        attributes("Main-Class": mainClassName,
                "Class-Path": sourceSets.main.runtimeClasspath.filter {it.isFile() }.collect { "dependencies/${it.name}" }.join(' '))
    }
    with jar
}

And note about mkdirs, it appears that it automatically creates one if it doesnā€™t exist. thats a good pointā€¦
Thank you !

1 Like

@Ali_RS as you can see, its now working fine on desktop and the structure is very similar to the output jar of the jme3 SDK :slight_smile: , i have documented the repo based on this good disscussion, BTW I have also separated the game module from the desktop one as you have suggested above, so now we have an up to date cross platform repo can be cloned directly from android studio & fully supports the arctic fox environment, (nb :currently no intellij support for android).

1 Like

Should maybe also have three assets (shared, android-only and desktop-only).
It depends on the game, but probably you want less heavy models for android, I guess.

On the other hand that may be a lot more complex for beginners, so not sure if we should have multiple projects.

1 Like

Yes it supports this, the multiple project (or a better name ā€˜modules approachā€™) greatly supports this feature, where the assets on game module (:game) are included on both of desktop (:desktop) & android (:app) while also android has its own assets also at (/res/assets) or (/raw) for vids assets) which is not included on desktop build as well as desktop has its own gradle resources folder :wink:.

Yes, gradle beginners not jme beginners, I made sure that everything is well documented on the repo, and I am ready to document more whenever needed.

One of the good outcomes also of using the modules approach is that you can now code using Dependant GUI platforms such as swing/jfx/android views & etc, while the game remains the same, I have approved this on this example using android view on the same game, we can even have multiple UI teams working on a single game making native GUI.

And everything is coded only using Android Studio

2 Likes