Hello, I’m looking to how inject a .klib during th...
# gradle
f
Hello, I’m looking to how inject a .klib during the Gradle Sync Task after
afterEvaluate
. I’m planning a new feature for my plugin spmForKmp. I want to short-circuit the cinterop by keeping the .klibs. So instead of building everything each time the same klib, I’m going to use the cached ones. The gradle cache is working fine, but I want to go further.
Currently using the
freeCompilerArgs -library
which solve half of the issue, It’s ok for the binary linking task but useless for code completion, it’s not indexed.
a
Just add it as file dependency to implementation configuration. https://docs.gradle.org/current/userguide/declaring_dependencies.html#3_file_dependencies It should work with klibs too, if not. It is a problem to report.
f
I need to add it dynamically like the cinterop task do during the gradle sync.
a
short-circuit the cinterop Tasks.
If enabled and not present, the cinterop final output files are stored beside the project source.
On next build, we directly use the stored files.
That sounds like task execution avoidance ( up-to-date checks) & build cache built-in Gradle features. Dont they work for you?
f
Yes, it’s working but I want to speed up the build time by avoiding build the same klib. For generating a klib, my plugin needs to build a swift package, then call cinterop for generating klib. Until the plugin config change, it will be the same klibs. If I could keep them beside the source code, reusable and shareable, it will reduce the build time by a lot.
The plug-in’s user will have the choice to use the feature or not. I’m going further than the cocoapod plugin.
a
For generating a klib, my plugin needs to build a swift package, then call cinterop for generating klib. Until the plugin config change, it will be the same klibs.
so that is how Task Up-to-date check works. roughly it should look like this:
Copy code
abstract class KlibProvider : DefaultTask() {

   @get:Input
   abstract val pluginConfigurationKey: Provider<String /* or some other type */>

   @get:OutputFile
   abstract val outputKlib: RegularFileProvider

   @TaskAction
   fun action() {
      // do anything you need to generate outputKlib file.
   }
}
And then you can declare dependency to such KlibProvider like following for example:
Copy code
kotlin.targets.withType(KotlinNativeTarget::class.java).configureEach { target ->
   val apiConfigurationName = target.compilations["main"].defaultSourceSet.apiConfigurationName
   project.dependencies.add(apiConfigurationName, klibProvider.map { it.outputKlib })
}
^ the code is written from top of my head, it may not be compilable but can illustrate things you may need. so then if Klib is already "built" and the Input of this task doesn't change, then it will stay up-to-date. OR, perhaps I got what you're doing. You have your own gradle plugin that can be shared across multiple & different gradle projects. They depend on your plugin and you want to avoid duplicate compilations of the same thing, I get it. Question: Can't you pre-built this "same thing" and include it as part of your Gradle Plugin? If not, then you can check how we're doing it with NativeDistributionCommonization TL;DR: we store it in global directory and have task that is doing simillar thing as I described above. But for UP-TO-DATE checks we have to create our own and solve concurrency problem with "file lock". But that is considered as not so good approach. and it is better to prebuilt & publish.
f
My plugin spmforkmp aims to be an alternative of the dying cocoapods plugins. they both basically work the same way. The prebuilt klib is the cinterop tasks output file, where I don’t want to call if the klib is already present outside of the gradle tasks.
Thanks, I will take a look of what you gave.
@Anton Lakotka [JB] It’s not possible to add dependency.
Cannot change dependencies of dependency configuration ':example:iosSimulatorArm64MainApi' after it has been included in dependency resolution.
The cinterop tasks can do it, I would like to know how...
a
Looks like you've added it too late. I.e. dependency resolution started. Make sure you add it during configuration phase and, preferable, outside afterEvaluate blocks.
f
I know, but everything is dynamic, I can’t do that. The only thing I have successfully done is linking the klib during the sync.
Copy code
freeCompilerArgs.addAll("-library", klib.absolutePath)
But, the klib is not indexed by the IDE, there are missing in the project.
v
Just add it as file dependency
That's generally almost always a bad idea. File dependencies have quite some drawbacks, including not being listed in things like build scans or the
dependencies
output.
The prebuilt klib is the cinterop tasks output file, where I don’t want to call if the klib is already present outside of the gradle tasks.
I don't understand all you said as I don't know what cinterop and cocoapods and so on are. But where is that task you are trying to provide prebuilt result? And what is happening with that task? Can you maybe just replace that tasks action with an own action that copies the pre-built file to the output file location?
f
@Vampire The Kotlin Cinterop tasks are really complex, in the latest tasks of a build. • 1. It generates a .klib from a C definition configuration. • 2. Link the .klib to the kotlin library • 3. The klib is indexed by the IDE My point is, I want to avoid the 1er step by keeping aside the generated klib for later usage for optimization. Is it even possible without rewrite the cinterop task?
v
Probably
f
Ok, I found the solution, but the result is not good enough to be working like I needed.
v
Maybe someone can help if you knit an MCVE 🤷‍♂️