I wonder if there are any plans for introducing features for conditional compiling so we can support both Java and Javascript targets in the same project?
Maybe something like a convention based on file extensions? For example,
Canvas.js.kt
Canvas.java.kt
and then the compiler choses file accordingly to what the current backend is.
How does this sound? If not, what is/will the standard practice be for this problem?
Incidentally I did just that recently on a project (I fixed up the maven plugin so it can support different source paths for different execution phases (JVM 'compile' versus 'js' etc) such as in this pom.xml. I did cheat in the standard library in kotlin and use a naming convention (excluding *JVM.kt files on the JS compile) which lets you keep code closer together; but I guess separate directory trees is maybe cleaner.
It might be nice for IDEA to have a view where the ‘common’, ‘jvm’ and ‘js’ kotlin code could be viewed in a single source tree to avoid having to do too much tree expanding/collapsing.
The common answer to this is reflection. I know java 8 code can run on newer versions, but some times its nice to have enhancements based off the version compiled for.
@JvmField
val option = System.getProperty("my.option")
// JIT removes unnecessary branch at first execution
if (option) {
// ...
} else {
// ...
}
Now pass your configurations values to the application using CLI argument -D<option_name>:
java -jar jarName -Dmy.option=true
Initialize “my.option” system property in any way you want, most importantly, do this before loading java class in which option property is defined (JVM loads classes lazily, at first access).
fun main() {
System.setProperty("my.option", "true")
// ...
}
Android:
build.gradle:
buildConfigField "boolean", "MY_OPTION", "true"
In your code:
// unnecessary branch removed at compile time
if (BuildConfig.MY_OPTION) {
// ...
} else {
// ...
}
Yes, it is a good variant to set an option in build.gradle. It will be useful for a simple use-case when we want to set constants. In this case we can set several constants (but as variables) eighter with repeating a block, or destructing them:
1.
val const1 = if (BuildConfig.MY_OPTION) {
// ...
} else {
// ...
}
val const2 = ...
val (const1, const2, ...) = if (BuildConfig.MY_OPTION) {
...
} else {
...
}
@wdoker , Your method works, but it is not good as the #if conditional compilation. Just think about what if that optional feature needs a bulk array? That bulk array can be wiped out if the #if feature is available. For that reason, Kotlin should add the #if conditional compiltion feature.
I understand that we have system properties that enable jit to get rid of blocks, but our code is cross platform. We use hotspot and graal native image for desktops, ART for android, and graal native image for iOS. So we need a “cross VM solution”. For now we don’t have better option than a compiler plugin.
PS: I think that’s one essential feature for game dev c# has that Kotlin miss
You can very easily turn that into a singular constant if-check in release builds and the fully-fledged normalised checking in debug builds.
Simply have a custom require function that knows about a const val DEBUG: Boolean like so:
IIRC, the compiler folds constants at compile time, and so the requireIfDebug function and all its usages will turn into an if(false) in release builds, which, IIRC again, makes the compiler completely take out the code (and regardless if it doesn’t or not, it’s trivial to run something like proguard first to minimise and optimise the code, and then run it through graalvm
It seems you remember well !
I made a quick test with this
const val DEBUG = true
Then in Vector2:
/**
* Returns the result of spherical linear interpolation between this vector and b, by amount t.
* t is in the range of 0.0 - 1.0, representing the amount of interpolation.
*
* Note: Both vectors must be normalized.
*/
fun slerp(b: Vector2, t: RealT): Vector2 {
if (DEBUG) {
require(this.isNormalized() && b.isNormalized()) { "Both this and b vector must be normalized!" }
}
val theta: RealT = angleTo(b)
return rotated((theta * t))
}