How do I determine buildscript classpath?

I’m using AspectJ and while weaving (my code here) it gets hold of a few jars that need to get recreated during build process. This means that I can only build once, and after that, unless I disable daemon, I get errors such as “The process cannot access the file because it is being used by another process.”

So I thought that I would run weaving in a separate process:

javaxec {
    classpath ?
    main = "org.aspectj.tools.ajc.Main"
    args args
}

Which works if I put this instead of the question mark above: “C:\Users\oakkitten\.gradle\caches\modules-2\files-2.1\org.aspectj\aspectjtools\1.9.5\dfca1edabdca4f262d7f7c45cd8e11e6b88b9f6\aspectjtools-1.9.5.jar”

But how do I access this programmatically?

P.S. this is the whole buildscript block from my project build.gradle:

buildscript {
    ext.kotlin_version = '1.4.10'

    repositories {
        google()
        jcenter()
    }

    dependencies {
        classpath 'com.android.tools.build:gradle:4.1.0'
        classpath 'org.aspectj:aspectjtools:1.9.5'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
    }
}

I haven’t found a way to obtain the build script class path, but I think I found a reasonable enough workaround for my use case.

I created another configuration & task simply to download the needed jar:

val weavingToolsClassPath = "$buildDir/weaving"
val weaving: Configuration by configurations.creating

dependencies {
    weaving("org.aspectj:aspectjtools:1.9.6")
}

tasks.register<Copy>("fetchAspectjToolsBecauseIHaveNoIdeaHowToGetBuildScriptClassPath") {
    from(weaving)
    into(weavingToolsClassPath)
}

Then I make sure that this runs some time before weaving:

tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
    if (name == "compileDebugKotlin") {
        ...
        dependsOn("fetchAspectjToolsBecauseIHaveNoIdeaHowToGetBuildScriptClassPath")
    }
}

tasks.withType<JavaCompile> {
    if (name == "compileDebugJavaWithJavac") {
        ...
        dependsOn("fetchAspectjToolsBecauseIHaveNoIdeaHowToGetBuildScriptClassPath")
    }
}

Then while weaving I can run it in a process:

fun weaveCats(classPath: String, aspectPath: String, inputOutput: String) {
    ...
    if (OperatingSystem.current().isWindows) {
        javaexec {
            classpath = fileTree(weavingToolsClassPath)
            main = "org.aspectj.tools.ajc.Main"
            args = arguments
        }
    } else {
        ...

This is probably stupid but hey it works ¯\_(ツ)_/¯

You shouldn’t try to do anything with the build script classpath. It’s purpose is only to be the classpath of the build script itself.

This is exactly what you should do. The entire purpose of a Configuration is to represent the files needed for a specific use, in this case the classpath of the javaexec.

You’ve actually removed the simplicity here. The classpath needs to be a FileCollection, but a Configuration is also a FileCollection. You don’t need the Copy task or anything like that.

Given:

val weaving: Configuration by configurations.creating

dependencies {
    weaving("org.aspectj:aspectjtools:1.9.6")
}

Your javaexec would just reference the weaving Configuration:

javaexec {
    classpath = weaving
    main = "org.aspectj.tools.ajc.Main"
    args = arguments
}

You don’t need to manually force the download to occur and Copy the file to yet another destination. The javaexec will resolve the Configuration and use the result from the cache as the classpath automatically when it needs it.

Your javaexec would just reference the weaving Configuration

Thanks, that works!

You shouldn’t try to do anything with the build script classpath. It’s purpose is only to be the classpath of the build script itself.

I thought that buildscript dependencies are the ones that are required for the build script to work, that is, to make builds? I was (and still am on Linux) calling ajc methods directly from the build script, for which I put aspectjtools as the dependency of the buildscript. Is this a wrong thing to do? If it is wrong, then how can I do weaving without launching a separate process?

Yes, buildscript dependencies are the ones that you’re using directly in your build script, which usually end up being items like plugins, tasks, etc. You declare the dependencies and they are available to you in the build script.

The concern is if you’re trying to do something with the built script classpath itself, like extracting the individual path to a JAR file in the cache from it. This was a direct response to the question of how to obtain the build script classpath and access "C:\Users\oakkitten\.gradle\caches\modules-2\files-2.1\org.aspectj\aspectjtools\1.9.5\dfca1edabdca4f262d7f7c45cd8e11e6b88b9f6\aspectjtools-1.9.5.jar"
from it. That’s the wrong level of abstraction. You should be using a Configuration that contains the dependencies for a particular need and not worrying about what’s happening with the underlying file paths that end up on a classpath.

In the original code I was calling new Main().run(args, handler), where this Main thing comes from aspectjtools. Here I am trying to do the very same thing, except in a process. Essentially I am running a part of buildscript in a process, so it makes sense to me that it should be able to access buildscript classpath. If I was on Linux, I could simply fork I guess.

(Of course it’s wrong to try to pull jars from gradle cache. I just put as much info as I could into the question.)