A scala-kotlin-java
joint compilation plugin built on Gradle
, for native Android
.
The plugin was built with ScalaBasePlugin
, which is also an official plugin of Gradle
, cooperates perfectly with the Android
official plugin, is a supplement. It
takes very little code to put the two together but functional maturation, includes androidTest
and unitTest
. There are no conflicts or incompatibilities, even if it
does, it's easy to update and maintain.
Now, this plugin is well developed and ready for official use.
- Also refers demo project scalroid-build-demo
Gradle | Android Plugin | Kotlin Plugin | Scala (this plugin compiles) |
---|---|---|---|
7.6.2 ~ 8.4 |
7.4.0 ~ 8.1.3 |
1.7.20 ~ 1.9.20 |
2.10.x ~ 3.x |
-
The Scala version fully supports the
ScalaPlugin
of gradle, see official documentation: https://docs.gradle.org/current/userguide/scala_plugin.html#sec:configure_zinc_compiler
For details about how to setzincVersion
, see the example code below. -
Known issues:
- Since the Android's built-in
JDK/JRE
does NOT have implements the classjava.lang.ClassValue
, but some classes require it, such asscala.reflect.ClassTag
. So i have made a copy here.
Or as an alternative, you can setcacheDisabled = true
inClassTag
to avoid method calls toClassValue
. To achieve this, you can useclassTagDisableCache
(it works well even afterProguard/R8
) at the very beginning of your app startup ( e.g.AbsApp
). But you still need to define a simple class so that it can be found at runtime:package java.lang; //import hobby.wei.c.anno.proguard.Keep$; //@Keep$ public abstract class ClassValue<T> { protected abstract T computeValue(Class<?> type); public T get(Class<?> type) { return null; } public void remove(Class<?> type) {} }
- Since the Gradle have bugs in
Scala incremental compilation
and did not fixed in version v7.x: #23202(but has been fixed in and after v7.6.2). Nevertheless, Gradle v8.0.1 ~ v8.4 is recommended, and scala incremental compilation works well.
- Since the Android's built-in
- Clone this repository to your project's
buildSrc
directory (optional):
cd <PATH/TO/YOUR_PROJECT>
git clone git@github.com:chenakam/scalroid.git buildSrc
- Below is your project's directory structure as possibly looks like:
/your_project
/app
/src
/main
/java # Your java/kotlin code dir
/scala # Your scala code dir (java files can also compile)
/build.gradle
/...
/buildSrc # This repo dir
/other_modules
/...
/build.gradle
/...
- Add
id 'cash.bdo.scalroid' xxx
to your/build.gradle
file:
// ...
plugins {
id 'com.android.application' version '8.1.3' apply false
id 'com.android.library' version '8.1.3' apply false
id 'org.jetbrains.kotlin.android' version '1.9.20' apply false
// TODO: if you have not clone the dir `buildSrc/`, you need to uncomment the `version` filed.
id 'cash.bdo.scalroid' /*version '[1.6-gradle8,)'*/ apply false
}
- Add
apply plugin: 'cash.bdo.scalroid'
to yourapp/build.gradle
file:
plugins {
id 'com.android.application'
// Alternatively, for your library subproject
//id 'com.android.library'
id 'org.jetbrains.kotlin.android' // Required
// or (abbr)
//id 'kotlin-android'
// This plugin
id 'cash.bdo.scalroid'
}
android {
scalroid {
scala.zincVersion = '1.8.0'
// scalaCodeReferToKt = false // Take looks below
// ktCodeReferToScala = true
// Whether to expand `R.jar`, so as to fix the problem of `R.id.xxx` marked red in Scala code.
// This will bring the `R.jar` under `External Libraries`.
// setAppRJarAsLib = true
// javaDirsExcludes = ['src/main/kotlin', 'src/aaa/xxx']
}
// ...
}
dependencies {
implementation "androidx.core:core-ktx:1.12.0"
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
// ...
// `scala-library` must be set. Scala 2 or Scala 3
// Scala 2
implementation "org.scala-lang:scala-library:2.12.18"
// Scala 3
// implementation 'org.scala-lang:scala3-library_3:3.2.0-RC2'
}
- You can edit any code in
your_project/buildSrc/src
as needed, and then click the button with tooltipSync Project with Gradle Files
in the toolbar of yourAndroid Studio
, your modified code will be applied immediately.
It's not that easy to get scala
and kotlin
code to compile together in one go. Especially when it comes to cross-referencing code. So, here are two options to help
you compile through.
You can use Java as a transition, scala and kotlin both refer to java instead of directly referencing each other. At most, you can also use kotlin code to refer directly to scala (by default). That should do it.
Don't forget the following settings if you put java source files in a non-default folder, e.g.src/aaa/xxx
. The default folders are src/main/java
and src/main/kotlin
and do not need to be set.
scalroid {
javaDirsExcludes = ['src/main/xxx', 'src/aaa/xxx']
}
Note: .java
(and .scala
) files written in src/main/scala
directory will be compiled and output by the scala compiler. .java
files written in other directories
will NOT be compiled and output by scala even if they are referenced by .scala
files (which would be compiled for output by the java compiler). But if NOT
configured in javaDirsExcludes
, output is compiled by scala by default, which may results in duplicate .class
files and an error is reported. If similar error occurs,
try adding the directories to javaDirsExcludes
.
In other words, if you want scala to compile, you should written .java
(and .scala
) files in the src/main/scala
directory (or other directories you configured
in sourceSets
), otherwise you should written them in the java/kotlin
directories.
There are two parameters you can use conveniently, and they are:
scalroid {
scalaCodeReferToKt = false // `false` by default
ktCodeReferToScala = true // `true` by default
}
The default value means the kotlin code references scala code, but it cannot be back referenced because of scala-kotlin cross compilation has not implemented. If this is the case, do nothing else, leave the defaults, and the project compiles. Or the opposite one-way reference works:
scalroid {
// One-way reference works
scalaCodeReferToKt = false // `false` by default
ktCodeReferToScala = true // `true` by default
// Alternatively:
// The opposite one-way reference also works too
scalaCodeReferToKt = true
ktCodeReferToScala = false
}
But given the existence of such a situation of scala/kotlin code tends to cross-reference, I also made compatible reluctantly so that we can compile finally. It's theoretically set up like this (but the truth is not so simple):
scalroid {
scalaCodeReferToKt = true
ktCodeReferToScala = true
}
- If this is set, once you
clean
the project, you will got errors the next time you compile.
The correct steps are (tentatively and reluctantly):
Clean
project (run./gradlew clean
);- Comment out any code in
scala
that referenceskotlin
(or vice versa) and uniformize the two parameters setting to maintain a one-way reference (no need to clickSync
, which doesn't matter); - Try compiling until you pass (
./gradlew :app:compile{VARIANT}JavaWithJavac
),kotlin-classes
andscala-classes
inapp/build/tmp/
have output associated withVARIANT
; - Sets the two parameters both to
true
and uncomment any code commented out in Step 2; - Try compiling until you pass.
- Note: repeat these steps each time you
clean
your project.