Hello :wave::skin-tone-3: What is the most pragmat...
# gradle
s
Hello 👋🏼 What is the most pragmatical solution for getting some environment variable or Gradle command line parameter into compiled Kotlin code? I'm not talking about reading an environment variable during runtime. Let's say I want to have a
const val VERSION = "1.0.0"
in my code that can be specified during compile time. Of course I could use
sed
or something similar to dynamically edit a file before compilation but is there maybe a nicer way, something that the Kotlin Gradle plugin offers, maybe?
d
I think this is the one (excellent) Gradle plugin that everyone is using for that task: https://github.com/gmazzo/gradle-buildconfig-plugin
gratitude thank you 1
s
Thanks, this looks promising! I will have a look 👀
kodee happy 1
when Jake Wharton is involved, it must be good 😂
a
It's also possible with a little bit of Gradle https://stackoverflow.com/a/74771876/4161471 (which might be nice if you want to avoid extra dependencies, or you need something more customisable)
v
I think this is the one (excellent) Gradle plugin that everyone is using for that task
Definitely not. I only know very few people using it. I usually just use existing things, no extra plugins, no extra tasks. Just a
foo.properties
file in the normal resources directory with placeholders, configuring the
processResources
task with
filesMatching
,
expand
(or any other filter), and
filteringCharset
to fill in the placeholders, and then reading the file from classpath at runtime.
d
@Vampire for sure, of course you can do it with Gradle directly (otherwise BuildConfig could not be a Gradle plugin). It is not complicated at all and it works well if you have one value to set. As soon as you have more than one value to generate, or more that one project that needs this, maintenance has its cost. I just prefer not to reinvent the wheel every time, and use that mature and tested BuildConfig gradle plugin, in order to concentrate on the project I am working on. I don't re-program Gradle either (except if the project I am working on is a build system...). But that's a choice. 😛 > and then reading the file from classpath at runtime I would like to see how that goes in Kotlin multiplatform!
s
Thanks for the additional responses. The Gradle solution also looks good.
and then reading the file from classpath at runtime.
I would actually prefer a compiled file to not introduce any (performance) overhead here for reading a file.
I would like to see how that goes in Kotlin multiplatform
That's a very good point because I actually need a solution for KMP. Reading via classpath probably only works for Android & JVM targets at the moment.
r
FWIW, and I know Vampire disapproves of this solution, we are doing this: `build.gradle.kts`:
Copy code
val templateKotlin by tasks.registering(Sync::class) {
  from(layout.projectDirectory.dir("src/main/kotlin-templates"))
  into(layout.buildDirectory.dir("generated/src/from-templates"))
  filteringCharset = "UTF-8"
  expand(
    "VERSION" to (System.getenv("VERSION") ?: ""),
  )
}

sourceSets {
  main {
    kotlin.srcDir(templateKotlin)
  }
}
Where
src/main/kotlin-templates
contains a single file `Version.kt`:
Copy code
package my.app

val version = VersionInfo(
  version = "${VERSION}".takeUnless { it.isEmpty() },
)
my.app.VersionInfo
being a data class in `src/main/kotlin`:
Copy code
package my.app

data class VersionInfo(
  val version: String?,
)
Then we just use
my.app.version
directly in code.
v
Remind me please, why do I disapprove that solution? :-D It is basically the same I suggested, just filling in a code template instead of a properties template and registering the task as source directory, that's fine too. It has some minor quirks, like when browsing the code on GitHub or similar you don't find the class, you need to ensure to generate the code before trying to use IDE on the project, for example using
idea-ext
Gradle plugin, ... But besides that, generating code should also be fine if living with the drawbacks is fine, especially when you maybe anyway also generate other code.
s
you need to ensure to generate the code before trying to use IDE on the project, for example using idea-ext Gradle plugin, ...
This is what the buildconfig plugin ensures, for example.
r
Mmm - not keen on the fact that it generates something that isn't trivially replaceable in tests.
v
That's not a disapproval, just another of those quirks you have to live with. If you for example generate a build timestamp into that generated source file, anything using that source set like tests and so on will never be up to date or cacheable, but always run, while with a properties file your can use runtime normalization to prevent that.
r
True, and fair enough
Though surely tests will also not be up to date if processResources has changed?
v
As I said, for that Gradle has the "runtime classpath normalization" feature. You can exclude specific properties of properties files or whole files from up-to-date calculation, so the test task has the file on the runtime classpath, but the normalization you configured says that if only file X changed or only property Y in file X changed, then the tasks output is not affected and so could be up-to-date or iirc also taken from cache of only that runtime classpath detail changed and everything else is the same.
👍 1