https://kotlinlang.org logo
Title
c

Charlie Tapping

04/16/2023, 11:10 AM
I’ve defined Gradle Task in my root gradle scipt which works
tasks.register<Exec>("skynet") {
    dependsOn(":bin:nativeBinaries")

    val bin = "./bin/build/bin/native/debugExecutable/skynet.kexe"

    executable = "sh"
    args = listOf("-c", bin)
}
However I wanted to declare it like so
val nativeBinaries by project("bin").tasks.existing
val nativeBinaries by tasks.existing
tasks.register<Exec>("skynet") {
    dependsOn(nativeBinaries)
But I can’t seem to get a reference to it? For context the nativeBinaries task exists in a kotlin native submodule called “bin”
if you zoom out on your problem, can you explain what you’re trying to achieve? What does skynet.kexe do? Why are you trying to run it in a separate project?
c

Charlie Tapping

04/16/2023, 11:20 AM
I feel like we should be okay here because the root project isn’t configuring any subprojects, instead I’m trying to declare a task which when executed triggers a subproject task
So this is my project structure
------------------------------------------------------------ Root project ‘skynet’ ------------------------------------------------------------ Root project ‘skynet’ +--- Project ‘:bin’ \--- Project ‘:lib’
bin is a kotlin native module
e

ephemient

04/16/2023, 11:21 AM
depending on a task is not the same as depending on a task's outputs, even within the same project
don't do that. use the proper way of exporting configurations/artifacts
c

Charlie Tapping

04/16/2023, 11:21 AM
I want to ensure that specifically the kotlin native task nativeBinaries is ran before i try to exec one of its outputs
This is the full command
tasks.register<Exec>("skynet") {
    dependsOn(":bin:nativeBinaries")

    val bin = "./bin/build/bin/native/debugExecutable/skynet.kexe"

    executable = "sh"
    args = listOf("-c", bin)
}
So I would remove depends on and put what exactly
?
a

Adam S

04/16/2023, 11:29 AM
there’s a few different options, so it depends…
if you want to depend on the compiled executable, then maybe set up variant-aware sharing https://docs.gradle.org/current/userguide/cross_project_publications.html#sec:variant-aware-sharing or maybe use composite builds, so you can run the task directly https://docs.gradle.org/current/userguide/composite_builds.html#included_build_task_dependencies or set up your
:bin
project so it doesn’t produce an executable, it produces a library. And then it the root project also set up Kotlin/Native, add a dependency on
:bin
, and a custom K/N target with an exec This stuff can be really complicated in Gradle, but if you explained what’s going on, or what error you get, or why the task needs to be run in a separate subproject, then maybe there’s a better way
c

Charlie Tapping

04/16/2023, 12:06 PM
Thank you for the help
The wider on all of this was that I simply wanted to run the native executable from gradle but gradle skipping that task, I found that I could run it from cli though and then tried to call cli from gradle.
The actual solution to the original problem was
changing
val nativeTarget = when { hostOs == “Mac OS X” -> macosX64(“native”)
to
val nativeTarget = when { hostOs == “Mac OS X” -> when (System.getProperty(“os.arch”)) { “aarch64” -> macosArm64(“native”) else -> macosX64(“native”) }
it turns out the intelli j wizard doesnt generate the right config for apple silicon targets
e

ephemient

04/16/2023, 12:13 PM
https://melix.github.io/blog/2021/10/gradle-quickie-dependson.html dependsOn is the wrong thing regardless of whether it's in one project or not
what you should do is to register every host binary type in the producer - the ones that can't be built on the current platform will be automatically disabled
then pick out the right one for local execution
e.g.
kotlin {
    for (target in listOf(linuxX64(), linuxArm64(), macosArm64(), macosX64(), mingwX64())) {
        target.binaries.executable {
            entryPoint("...")
        }
    }
}
you can configure
executable { runTaskProvider?.configure { ... } }
if you want to specify args or environment variables
or, if you really want to run it from another project, export the binaries
kotlin {
    targets.withType<KotlinNativeTarget> {
        binaries.matching { it.outputKind == NativeOutputKind.EXECUTABLE }.all {
            artifacts.add(
                configurations.create("${this@withType.name}${name.capitalized()}") {
                    isCanBeConsumed = true
                    isCanBeResolved = false
                    attributes {
                        attribute(KotlinPlatformType.attribute, KotlinPlatformType.native)
                        attribute(KotlinNativeTarget.konanTargetAttribute, konanTarget.name)
                        attribute(KotlinNativeTarget.kotlinNativeBuildTypeAttribute, buildType.name)
                        attribute(Usage.USAGE_ATTRIBUTE, objects.named("native-binary"))
                    }
                }.name,
                this.linkTaskProvider
            )
        }
    }
}
and consume the specific one,
val hostSkynetBinary by configurations.creating {
    isCanBeConsumed = false
    isCanBeResolved = true
    attributes {
        attribute(KotlinPlatformType.attribute, KotlinPlatformType.native)
        attribute(KotlinNativeTarget.konanTargetAttribute, HostManager.host.name)
        attribute(KotlinNativeTarget.kotlinNativeBuildTypeAttribute, "DEBUG")
        attribute(Usage.USAGE_ATTRIBUTE, objects.named("native-binary"))
    }
}

dependencies {
    hostSkynetBinary(project(":bin"))
}

tasks.register("skynet", Exec::class) {
    inputs.files(hostSkynetBinary)
    commandLine("/bin/sh", "-c")
    argumentProviders.add { listOf("${hostSkynetBinary.singleFile}/*.kexe") }
}
c

Charlie Tapping

04/16/2023, 12:29 PM
Wow thats super useful to know thank you, there is actually a pair of tasks for running the binaries generated.
runDebugExecutableNative
and
runReleaseExecutableNative
. It just I couldn’t get these working at first because of the wizard generated config which targeted x86 binaries