3

I have the following setup for an Android app:

├── build.gradle
└── src
    └── main
        ├── AndroidManifest.xml
        ├── asm
        │   └── SomeJVMClass.j
        ├── kotlin
        │   └── activity.kt
        └── res
            └── values
                └── strings.xml

The source file in src/main/asm/SomeJVMClass.j is Krakatau format JVM assembly (it's almost the same as Jasmin). I would like to use the class it defines from my main Kotlin source activity.kt.

Before I even get to trying to automate this with Gradle, just assembling the file manually into build doesn't help. Here's what I tried:

  1. Do a gradle build to get the skeleton build directory (this of course fails with the Kotlin compiler not finding SomeJVMClass)
  2. I run Krakatau manually to put its class files in build/intermediates/classes/{debug,release}:

    $ for flavor in debug release; do ~/prog/jvm/krakatau/assemble.py -out build/intermediates/classes/$flavor -r src/main/asm ;done
    
    Processing file src/main/asm/SomeJVMClass.j, 1/1 remaining
    Class written to build/intermediates/classes/debug/com/example/JVMServer/SomeJVMClass.class
    
    Processing file src/main/asm/SomeJVMClass.j, 1/1 remaining
    Class written to build/intermediates/classes/release/com/example/JVMServer/SomeJVMClass.class
    
  3. Re-run grade build with the hope that Kotlin will now find the .classfiles; but no improvement whatsoever:

:compileDebugKotlin
e: /home/cactus/prog/android/kotlin-asm/src/main/kotlin/activity.kt (3,20): 
   Unresolved reference: JVMServer
e: /home/cactus/prog/android/kotlin-asm/src/main/kotlin/activity.kt: (12,14): 
   Unresolved reference: SomeJVMClass

So the first question is, obviously, where do I put the resulting .class files so that the Kotlin compiler can find them. And the second, follow-up question, is how do I automate that so that a single gradle build command will run the Krakatau assembler first, before running the Kotlin compiler.

For reference, here are my source files:

src/main/asm/SomeJVMClass.j:

.class public com/example/JVMServer/SomeJVMClass
.super java/lang/Object

.method public static foo: ()Ljava/lang/String;
  .code stack 2 locals 0
    ldc "JVM bytecode works"
    areturn
  .end code
.end method

.end class  

src/main/kotlin/activity.kt:

package com.example.JVMClient

import com.example.JVMServer.SomeJVMClass    
import android.app.Activity
import android.os.Bundle

class MainActivity: Activity() {
  protected override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    setTitle(SomeJVMClass.foo())
  }
}
Cactus
  • 27,075
  • 9
  • 69
  • 149

1 Answers1

0

Edit: turns out this approach does not work for android-gradle

This is more related to gradle than Kotlin, you can tell gradle to include .class files during compilation by adding the statement compile files("build/intermediates/classes/debug") to your dependencies.

This will place the .class files on the classpath where Kotlin should be able to find them.

Kiskae
  • 24,655
  • 2
  • 77
  • 74
  • Sounds promising, but then I get `Only Jar-type local dependencies are supported. Cannot handle: build/intermediates/classes/debug` from `:prepareDebugDependencies` – Cactus Mar 17 '16 at 12:56
  • Moreover, if I put my `SomeJVMClass.class` file in a `.jar` and use `compile files("build/intermediates/classes/debug/asm.jar")`, it fails later in `:transformClassesWithDexForDebug` with `Uncaught translation error: java.lang.IllegalArgumentException: already added: Lcom/example/JVMServer/SomeJVMClass` – Cactus Mar 17 '16 at 12:59
  • @Cactus Putting it as a .jar file in the build directory might trigger an automatic include, try removing the compile line but leaving the .jar – Kiskae Mar 17 '16 at 13:03
  • Nope, then it's back to `:compileDebugKotlin` not finding `JVMServer' – Cactus Mar 17 '16 at 13:05
  • @Cactus One solution I can think of is to create a gradle subproject as a normal java library project, including the .class files in the output of that project and the adding a dependency to that subproject. Because it seems android has removed some of the capabilities of gradle's dependency system. – Kiskae Mar 17 '16 at 13:18
  • I don't know the first thing about Gradle... do you have an example of such a subproject setup? Ideally including the actual assembly step as part of the build process as well. – Cactus Mar 17 '16 at 13:32
  • 1
    @Cactus refer to https://docs.gradle.org/current/userguide/multi_project_builds.html for subprojects and https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Exec.html for custom tasks. – Kiskae Mar 17 '16 at 13:49
  • 1
    @Cactus This documentation might actually solve your problem: https://docs.gradle.org/current/javadoc/org/gradle/api/tasks/SourceSetOutput.html – Kiskae Mar 17 '16 at 14:03