```println(tasks.asMap.keys)``` ^ running this one...
# gradle
r
Copy code
println(tasks.asMap.keys)
^ running this one inside a function called on toplevel, gives me
[]
🤨
s
Is it running in configuration phase ?
r
Yeah. I wanna modify signing a bit.
v
Even if it worked, that's an extremely bad idea. You totally disable task-configuration avoidance, meaning on each Gradle invocation you need to configure each and ever task, wasting everyone's time that runs the build. You could instead use
tasks.names
which is lazy-safe. But even that most probably will not work as you intend, as you only get the tasks that are already registered at the time you call it and not any tasks that get registered after that. What is the actual use-case you try to solve?
r
Trying to hook yubikeys into signing via gradle, so I can use it to sign production apks.
Gives me "task not found":
Copy code
val signApk = tasks.register("signApk", Exec::class.java) {
    group = "build"
    description = "Signs the APK using a yubikey"
    doFirst {
        var pass: CharArray? = null
        SwingUtilities.invokeAndWait {
            val dialog = javax.swing.JDialog()

            dialog.apply {
                isModal = true
                title = "Enter password"
                isAlwaysOnTop = true
                isResizable = false
                defaultCloseOperation = javax.swing.WindowConstants.DISPOSE_ON_CLOSE
                val vbox = javax.swing.Box(javax.swing.BoxLayout.Y_AXIS)
                vbox.add(javax.swing.JLabel("Please enter key passphrase:"))
                val input = javax.swing.JPasswordField(20)
                vbox.add(input)
                val button = javax.swing.JButton("OK")
                button.addActionListener {
                    pass = input.password
                    dialog.dispose()
                }
                vbox.add(button)
                add(vbox)
                pack()
                setLocationRelativeTo(null)
                isVisible = true
            }
        }

        if (pass == null) {
            throw InvalidUserDataException("You must enter a password to proceed.")
        }

        val taskName = when (val variantName = project.properties["signApkVariant"] as? String) {
            "release" -> "assembleRelease"
            "debug" -> "assembleDebug"
            else -> throw IllegalArgumentException("Invalid build variant: $variantName")
        }

        val apkPath = project.tasks.getByName(taskName)
            .outputs.files.singleFile.absolutePath

        commandLine(
            "apksigner",
            "-J-add-opens=jdk.crypto.cryptoki/sun.security.pkcs11=ALL-UNNAMED",
            "sign",
            "--provider-class sun.security.pkcs11.SunPKCS11",
            "--provider-arg",
            "~/dev/reach/repo/pkcs11_java.cfg",
            "--ks",
            "NONE",
            "--ks-type",
            "PKCS11",
            apkPath
        )
    }
}

fun configureSignApkForVariant(variant: String) {
    tasks.named(":app:assemble${variant.capitalize()}") {
        finalizedBy(signApk)
        signApk.configure {
            project.extensions.extraProperties.set("signApkVariant", variant)
        }
    }
}
v
Also not the best tactic to be honest. Using
project
at task execution phase is deprecated (or forbidden already, don't remember) and if you ever hope to use configuration cache cannot be used. Also changing the output of another task is very bad practice, this disturbs up-to-date checks, caching, and correctness. To mitigate the "task no found", you could do
tasks.named { it == ":app:assemble${variant.capitalize()}" }.configureEach { ... }
but the other parts stay valid. Better for example add the signing as
doLast
action to the actual task that creates the apk, so that it is part of the task execution. For doing things like getting password only once, use a shared build service that determines and provides the value.
r
The only non-throwing one I found was
findByName
, which gives me a
Task
directoyl.
What's the best way to change the singing? I could also add another independent task and configure intellij to manually run it via run configurations, would that be cleaner?
v
findByName
again completely disables task-configuration avoidance and also only gets the tasks already registered at the time you call it.
I could also add another independent task and configure intellij to manually run it via run configurations, would that be cleaner?
If the task modifies the output of another task, that would not be any better, but have the exact same problems.
What's the best way to change the singing?
Other than what I already recommended above?
r
Better for example add the signing as doLast action to the actual task that creates the apk, so that it is part of the task execution.
This part? The task is defined in the library, can I modify it afterwards?
s
Task.doLast is public so I would assume so ?
v
Yes, you can add actions to any task in front or back of existing actions. You can even clear out all existing actions and completely replace same with custom actions, but there is just a handfull of valid use-cases for that.
r
So go find the original task (as defined in the classpath, not via task registry), pull it out, change at will, and use that one to generate the apk instead?
v
... no
You cannot change the task class, only task instances. And I said modify the actual task. With named and configureEach as I showed above
Please read full messages, not only keywords :-P ;-)
r
I read "`findByName` again completely disables task-configuration avoidance" ... as "`named` disables configuration avoidance too"
v
I have no idea why anyone should get the idea to read it like that.
r
I read the "again" part as you repeating yourself in reference to
named
as well.
v
Ah, now I see your confusion.
That "again" was referring to your initial snippet which I said breaks it.
My version is safe
r
So using
named
is fine, and use
doLast
instead to attach the additional signing?
v
If you use the right
named
, yes.
r
🤔
Can't find the filter one
v
Then either update Gradle, it's only in latest 1 or 2 versions, or use
matching
instead
But make sure to still use
configureEach
after it
r
I'm at 8.2, lemme see what AGP supports
Yeah, understood. Now there's no more
commandLine()
available O.o
Apparently it's part of
AbstractExecTask
, which the default builder one is... not?
v
Use
exec { ... }
instead
r
Perfect, thanks.
v
Unless you care about configuration cache already that is. Then you would need to get an
ExecOperations
and call
exec { ... }
on that instead of calling it on
project
implicitly.