Cheng
Cheng

Reputation: 551

How to make aspect weave kotlin code every time with gradle

I am doing some research in AOP and use gradle + aspectJ to weave my code. So I did as some demo says, add aspectJtools to top build.gradle.

dependencies {
    ...
    classpath 'org.aspectj:aspectjtools:1.9.5'
}

And in app/build.gradle, add task to weave code:

variants.all { variant ->
    JavaCompile javaCompile
    if (variant.hasProperty('javaCompileProvider')) {
        //android gradle 3.3.0 +
        javaCompile = variant.javaCompileProvider.get()
    } else {
        javaCompile = variant.javaCompile
    }
    def buildType = variant.buildType.name

    javaCompile.doLast {

        MessageHandler handler = new MessageHandler(true)

        String[] javaArgs = [
                "-showWeaveInfo",
                "-1.8",
                "-inpath", javaCompile.destinationDir.toString(),
                "-aspectpath", javaCompile.classpath.asPath,
                "-d", javaCompile.destinationDir.toString(),
                "-classpath", javaCompile.classpath.asPath,
                "-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)
        ]
        new Main().run(javaArgs, handler)

        String[] kotlinArgs = [
                "-showWeaveInfo",
                "-1.8",
                "-inpath", project.buildDir.path + "/tmp/kotlin-classes/" + buildType,
                "-aspectpath", javaCompile.classpath.asPath,
                "-d", project.buildDir.path + "/tmp/kotlin-classes/" + buildType,
                "-classpath", javaCompile.classpath.asPath,
                "-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)
        ]
        new Main().run(kotlinArgs, handler)

        for (IMessage message : handler.getMessages(null, true)) {
            switch (message.getKind()) {
                case IMessage.ABORT:
                case IMessage.ERROR:
                case IMessage.FAIL:
                    log.error message.message, message.thrown
                    break
                case IMessage.WARNING:
                case IMessage.INFO:
                    log.info message.message, message.thrown
                    break
                case IMessage.DEBUG:
                    log.debug message.message, message.thrown
                    break
            }
        }
    }
}

Then I add my aspect like:

@Aspect
public class AspectInCommon extends BaseAspect {

    @Pointcut("execution(* com.example.cheng.test.MainActivityKt.on**(..))")
    public void kotlinMainOn() {
    }

    @After("kotlinMainOn()")
    public void hookKotlinMain(JoinPoint joinPoint) throws Throwable {
        log(joinPoint.getSignature().toLongString());
    }

    ...
}

And my MainActivity looks like:

    class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        ...
    }
}

In this way I indeed wove aspect into my code. And here is some output: enter image description here It works fine at first time until I click debug bottom again, then all the output are never triggered anymore. Then after few time's testing, I found that it only weave after clean build if one of my file is in kotlin. So I try to change mainActivity and aspect file to java or kotlin. And here is the test results:

MainActivity language       Aspect language        Weave results
Java                        Java                   Weave every time
Java                        Kotlin                 Weave after clean build *
Kotlin                      Java                   Weave after clean build
Kotlin                      Kotlin                 Weave after clean build

*:I put aspect file in subModule, and it crashed in second build after clean build. The error message is:

java.lang.NoSuchMethodError: No static method aspectOf()

I wonder how to make aspect weave kotlin code every time rather than only once after clean build, because most code of my real project is base in kotlin. It's perfect not to simply use a plugin. Many thanks.

Upvotes: 0

Views: 806

Answers (1)

kriegaex
kriegaex

Reputation: 67297

I neither use Gradle (rather Maven) nor Kotlin, but my experience with multiple JVM languages or multiple compilation steps is that is makes sense to do each compilation step in a separate module.

  1. So you could have one Java + Kotlin application class module, compiling it with normal Java/Kotlin compilers.
  2. Then you have an aspect module compiled with AspectJ compiler.
  3. Finally, there is a module doing binary weaving with AspectJ, using the first module on the inpath and the second on the aspect path.

This makes the build simpler and more stable. Other advantages are that you have an original, unwoven application module and an aspect-enhanced one. Depending on the situation, you could use the former or the latter in your application, e.g. if you weave debugging or tracing aspects which should not always be used. Last but not least, a separate aspect module makes the aspect library potentially re-usable.

Upvotes: 1

Related Questions