GithubHelp home page GithubHelp logo

foso / mpapt Goto Github PK

View Code? Open in Web Editor NEW
238.0 7.0 12.0 6.54 MB

(Deprecated) :wrench: Kotlin Native/JS/JVM Annotation Processor library for Kotlin compiler plugins

License: Apache License 2.0

Kotlin 100.00%
kotlin kotlin-plugin kotlin-multiplatform annotation-processor kotlin-native kotlin-native-plugin kotlinjs compiler-plugin kotlin-js

mpapt's Introduction

MpApt - Kotlin (Native/JS/JVM) Annotation Processor library

Introduction ๐Ÿ™‹โ€โ™‚๏ธ ๐Ÿ™‹โ€

Note This project is deprecated, when you are looking for a multiplatform annotation processor, please take a look at KSP

I wrote an annotation processing libary that can detect annotations in Kotlin Native/JS and Jvm projects, because Kapt is only working with KotlinJvm. The library can be used in Kotlin Compiler plugins. Tested with Kotlin 1.4.0,1.5.10

It can detect annotations with following targets:

(CLASS,FUNCTION,PROPERTY,VALUE_PARAMETER,PROPERTY_GETTER,PROPERTY_GETTER,CONSTRUCTOR)
(ANNOTATION_CLASS,TYPE_PARAMETER,FIELD,FILE,LocalVariable)

Example output of my example plugin on Kotlin Native:

Show some โค๏ธ and star the repo to support the project

GitHub stars GitHub forks GitHub watchers Twitter Follow

Projects that use MpApt:

Usage

These are the instructions for v0.8.7, check Changelog for changes on the active development branch

Inside your compiler plugin, add the dependency from MavenCentral

repositories {
    mavenCentral()
}

dependencies {
   compile 'de.jensklingenberg:mpapt-runtime:0.8.7'
}
  1. Create a class that extends de.jensklingenberg.mpapt.model.AbstractProcessor
class MpAptTestProcessor() : AbstractProcessor() {
  1. Add the names of your annotations that you want to detect:
override fun getSupportedAnnotationTypes(): Set<String> = setOf(TestClass::class.java.name, TestFunction::class.java.name)
  1. Do something with detected annotations:
override fun process(roundEnvironment: RoundEnvironment) {
roundEnvironment.getElementsAnnotatedWith(TestClass::class.java.name).forEach {
            when (it) {
                is Element.ClassElement -> {
                    log("Found Class: " + it.classDescriptor.name + " Module: " + it.classDescriptor.module.simpleName() + " platform   " + activeTargetPlatform.first().platformName)
                }
            }
        }

        roundEnvironment.getElementsAnnotatedWith(TestFunction::class.java.name).forEach {
            when (it) {
                is Element.FunctionElement -> {
                    log("Found Function: " + it.func.name + " Module: " + it.func.module.simpleName() + " platform   " + activeTargetPlatform.first().platformName)
                }
            }
        }
}
  1. Init MpApt inside your ComponentRegistrar:
  • Pass an instance of your processor and the CompilerConfiguration into MpAptProject
  • Then add an instance of MpAptProject to the following extension classes:

Inside a Kotlin Native Compiler Plugin:

override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
        val processor = MpAptTestProcessor()
        val mpapt = MpAptProject(processor,configuration)

        StorageComponentContainerContributor.registerExtension(project,mpapt)
        IrGenerationExtension.registerExtension(project,mpapt)
    }

Inside a Kotlin JVM/JS Compiler Plugin:

 override fun registerProjectComponents(
            project: MockProject,
            configuration: CompilerConfiguration
    ) {
        val processor = MpAptTestProcessor()
        val mpapt = MpAptProject(processor,configuration)
        StorageComponentContainerContributor.registerExtension(project,mpapt)
        ClassBuilderInterceptorExtension.registerExtension(project,mpapt)
        JsSyntheticTranslateExtension.registerExtension(project,mpapt)
    }
  1. That's it

Choose supported target platforms

By default your processor is enabled for every target. You can override

isTargetPlatformSupported(platform: TargetPlatform): Boolean

and return "true" if you want to support the target or "false" you don't.

   override fun isTargetPlatformSupported(platform: TargetPlatform): Boolean {
         val targetName = platform.first().platformName
 
         return when (targetName) {
             KotlinPlatformValues.JS -> true
             KotlinPlatformValues.JVM -> true
             KotlinPlatformValues.NATIVE -> {
                 return when (configuration.nativeTargetPlatformName()) {
                     KonanTargetValues.LINUX_X64, KonanTargetValues.MACOS_X64 -> {
                         true
                     }
                     else -> {
                         true
                     }
                 }
             }
             else -> {
                 log(targetName)
                 true
             }
         }
 
     }

You can distinguish between the native target platforms you want to support.

configuration.nativeTargetPlatformName() will get you the names of the Native Targets(macos_x64,linux_x64,etc). The values are defined in KonanTargetValues. It needs to be used only on Kotlin Native otherwise it will return an empty string

โœ๏ธ Feedback

Feel free to send feedback on Twitter or file an issue or join the Kotlin Slack and the Kotlinlang slack channel. Feature requests are always welcome. If you wish to contribute, please take a quick look at How to develop?

๐Ÿ‘ท Development Project Structure

  • demoProject - An example project that is using MpApt+KotlinPoet to generate code on KotlinJS
  • annotations - A Kotlin Multiplatform project which contains test annotations
  • example - A Kotlin Multiplatform project which applies a gradle plugin(de.jensklingenberg.mpapt) whichs triggers the compiler plugin.
  • buildSrc - This module contains the gradle plugin which trigger the compiler plugin
  • kotlin-plugin - This module contains the Kotlin Compiler Plugin for JVM/JS targets, it implements the kotlin-plugin-shared-module
  • kotlin-compiler-native-plugin - This module contains the Kotlin Compiler Plugin for Native targets, it implements the kotlin-plugin-shared-module
  • kotlin-plugin-shared Contains an implementation of MpApt

Testing

The CompileTest shows you, how you can use Kotlin Compile Testing to test your Processor/Compiler Plugin

See also

๐Ÿ“œ License

This project is licensed under the Apache License, Version 2.0 - see the LICENSE.md file for details


Copyright 2019 Jens Klingenberg

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

mpapt's People

Contributors

chippmann avatar foso avatar jensklingenbergedeka avatar ronntor avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

mpapt's Issues

GradlePlugin -> KotlinCompilerPlugin fails

Hi,

I have tried following the steps but get the following error:

> Task :compileKotlinJs FAILED
e: java.lang.NoClassDefFoundError: de/jensklingenberg/mpapt/model/AbstractProcessor
	at java.base/java.lang.ClassLoader.defineClass1(Native Method)
	at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1016)
	at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:151)
	at java.base/java.net.URLClassLoader.defineClass(URLClassLoader.java:514)
	at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:422)
	at java.base/java.net.URLClassLoader$1.run(URLClassLoader.java:416)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:691)
	at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:415)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:589)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
	at com.fab1an.myplugin.MyPluginComponentRegistrar.registerProjectComponents(MyPluginComponentRegistrar.kt:18)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment$Companion.registerExtensionsFromPlugins$cli(KotlinCoreEnvironment.kt:584)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment$ProjectEnvironment.registerExtensionsFromPlugins(KotlinCoreEnvironment.kt:132)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment.<init>(KotlinCoreEnvironment.kt:172)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment.<init>(KotlinCoreEnvironment.kt)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment$Companion.createForProduction(KotlinCoreEnvironment.kt:426)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment.createForProduction(KotlinCoreEnvironment.kt)
	at org.jetbrains.kotlin.cli.js.K2JSCompiler.doExecute(K2JSCompiler.java:207)
	at org.jetbrains.kotlin.cli.js.K2JSCompiler.doExecute(K2JSCompiler.java:74)
	at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:88)
	at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:44)
	at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:98)
	at org.jetbrains.kotlin.incremental.IncrementalJsCompilerRunner.runCompiler(IncrementalJsCompilerRunner.kt:178)
	at org.jetbrains.kotlin.incremental.IncrementalJsCompilerRunner.runCompiler(IncrementalJsCompilerRunner.kt:75)
	at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileIncrementally(IncrementalCompilerRunner.kt:286)
	at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileImpl$rebuild(IncrementalCompilerRunner.kt:99)
	at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileImpl(IncrementalCompilerRunner.kt:114)
	at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:74)
	at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile$default(IncrementalCompilerRunner.kt:65)
	at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execJsIncrementalCompiler(CompileServiceImpl.kt:550)
	at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execJsIncrementalCompiler(CompileServiceImpl.kt:96)
	at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1747)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:564)
	at java.rmi/sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:359)
	at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:200)
	at java.rmi/sun.rmi.transport.Transport$1.run(Transport.java:197)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:691)
	at java.rmi/sun.rmi.transport.Transport.serviceCall(Transport.java:196)
	at java.rmi/sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:587)
	at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:828)
	at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:705)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
	at java.rmi/sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:704)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
	at java.base/java.lang.Thread.run(Thread.java:832)
Caused by: java.lang.ClassNotFoundException: de.jensklingenberg.mpapt.model.AbstractProcessor
	at java.base/java.net.URLClassLoader.findClass(URLClassLoader.java:435)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:589)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
	... 49 more

Compilation faillure on classes without properties

If you have an annotation on a class or constructor of a class that doesn't have any properties declared inside it's body, method SyntheticResolveExtensionImpl.generateSyntheticProperties fails on this line, because getProperties returns an empty collection:
val isLastProperty = ((thisDescriptor.source as KotlinSourceElement).psi as KtClass).getProperties().last().equals(name)

[Question/Help wanted] Resolving dependencies between the different Gradle modules

KUDOS for all the work you've put into this project. Great effort and result.

I am trying to integrate it in a simple framework I've been trying to develop for Kotlin MPP. However, I have run into an issue and can't seem to find a way around it so far.

I have the following project structure:

root
    - demo - Kotlin MPP project
       * dependency implementation project(":annotations")
       * dependency implementation project(":core)
       * applies 'gradle-plugin'
    - annotations - Kotlin MPP projects, contains annotations that have to be processed at build time
    - compiler-plugin-common - Koltin JVM project, the annotation processor implementation
       * dependency implementation "com.something:annotations-jvm:0.0.1"
    - compiler-plugin-jvm-js - Plugging in the processor for JVM/JS
    - compiler-plugin-native - Pluggin in the processor for Kotlin / Native
    - core - Kotlin MPP project, the framework implementation
    - gradle-plugin - Gradle compiler plugin, resolved dependencies to the per platform annotation processing logic

When I try and run a gradle build on demo, it fails to resolve the transitive dependency on com.something:annotations-jvm:0.0.1 with :

org.gradle.internal.component.AmbiguousConfigurationSelectionException: Cannot choose between the following variants of com.something:annotations-jvm.0.0.1:

Help Wanted: Only one Expression Annotation is detected

I would appreciated some help with the following:

When I create two annotated expressions like

fun t1() = @ExprAnn "test"
fun t2() = @ExprAnn "test"

roundEnvironment.getElementsAnnotatedWith(annotationName) only reports one of them.

override fun getSupportedAnnotationTypes(): Set<String> {
    return setOf(annotationName)
}
override fun process(roundEnvironment: RoundEnvironment) {
    roundEnvironment.getElementsAnnotatedWith(annotationName).forEach { element ->
        messageCollector().report(WARNING, "in Loop")
        when (element) {
            is Element.ExpressionElement -> expressions.add(element.ktExpression)
        }
    }
}
override fun processingOver() {
    messageCollector().report(WARNING, "Over")
}

Did I get something wrong here?

[Question/Help Wanted] Annotation Processor get's called to late in the process?

I'm actually not quite sure what problem I'm facing.
But to me it seems that the annotation processor gets called to late on a native target.

A bit of context:
I collect all classes, functions, and properties annotated in the processors process function (like in the examples).
In the annotation processors processingOver function i generate code (like in the examples).
This generated code is put into a subfolder of the build folder of the project. This subfolder is added as a src directory.
But on a clean build this generated code is not compiled into the final binary.
But if i don't clean the project first and build again (so the generated code is still in that subdir) it gets compiled into the final binary.

So my assumption is that my code gets compiled first, then the annotation processing happens, then the linking and so on.

I think i'm just misconfiguring something so that the annotation processing happens too late.
What am I doing wrong?
Also i don't really know what source code snippets you maybe need to help with my problem.

annotation processor
native component registrar
subplugin

ComponentRegistrar is not being called

Hi
I have done all setup, but while building the project there is no activity in compiler module
I have annotated ComponentRegistrar class with @autoservice and published to local maven (and it exists) but it doesn't work
KotlinGradleSubplugin is alive and it is working however
Thanks in advance

[Question/Help Wanted] Running apt in the same sourceset while compiling it

Hi!

I've faced problem implementing APT with your library. Looks like it's normal behavior of kotlin's compiler, I dunno.

Project: mpp with common/frontend/backend source sets.

What I need? I have classes in backend source set, that should be weaved by my APT plugin.
And here is an egg-chicken trouble :) Kotlin code I generate with APT should be included in compiler on the current run...

On the first run APT works well, but rendered classes not includes in compiler and it fails with "Reference not resolved" error.
On the second run everything compiles fine, as compiler knows about rendered files already.

Any way this behavior could be achieved? :)

Just discovered the DeclarationChecker Class

I just discovered that the class "org.jetbrains.kotlin.resolve.checkers.DeclarationChecker" can also be used to get the classes, functions,properties. It gets called on Jvm/JS/Native. Looks likes it's simpler to use than SyntheticResolveExtension

You can register it inside the registerModuleComponents() of a StorageComponentContainerContributor.

override fun registerModuleComponents(container: StorageComponentContainer, platform: TargetPlatform, moduleDescriptor: ModuleDescriptor) {
        container.useInstance(DeclarationCheckerImpl())
    }
class DeclarationCheckerImpl() : DeclarationChecker{
    override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {

        when(descriptor){
            is SimpleFunctionDescriptor->{
                println("SimpleFunctionDescriptor  "+descriptor.name)
            }
            is ClassConstructorDescriptor->{
                println("ClassConstructorDescriptor  "+descriptor.name)
            }
            is ClassDescriptor ->{
                println("ClassDescriptor  "+descriptor.name)
            }
            is PropertyDescriptor->{
                println("PropertyDescriptor  "+descriptor.name)

            }
            is PropertyGetterDescriptor->{
                println("PropertyGetterDescriptor  "+descriptor.name)
            }
            is PropertySetterDescriptor->{
                println("PropertySetterDescriptor  "+descriptor.name)
            }

            is LocalVariableDescriptor->{
                println("LocalVariableDescriptor  "+descriptor.name)

            }
        }
    }

}

I will use this class to detect the annotations.

Support warning/error messages

A java annotation processor can report warnings/errors with javax.annotation.processing.Messager which can include a message as well as what element that message is on. Would be nice to have a similar api available. You seem to have a log() method on AbstractProcessor() but it seems pretty limited.

Problem guessing build folder when deploying to Heroku

I've created the compiler plugin and it works fine when the build process is run locally. But when I try to deploy my application to Heroku the build process fails, because the guessingBuildFolder() function is not able to correctly identify build directory. It returns something like this:

/tmp/build_e07308a302dcea2bb1f4d06d3a3f73ab/src/commonMain/kotlin/com/example/Service.ktbuild

Help wanted

Hi there are some things i need help with:

  1. I need a better way to detect when the last class of a module/platform was read and the processing is finished, right now i'm relying on fact that the classes NativeIrGenerationExtension, DebugLogClassGenerationInterceptor and JsSyntheticTranslateExtensionExt are called after the SyntheticResolveExtensionImpl class. I would like to find to have solution that doesn't need these extra classes.

  2. I would like to have a solution where you don't have to manually register every extension classes and just pass the instance of MpAptProject in only one class and this class will register all needed extension classes.

Maybe something like this:

val processor = MpAptTestProcessor(configuration)
val mpapt = MpAptProject(processor)
Mpapt.init(mpapt)

My problem is that i need the project file for the "Register"-classes (I don't know what's the correct name for this classes "IrGenerationExtension.registerExtension()" ) and the kotlin-compiler is using com.intellij.mock.MockProject but the kotlin-compiler-embeddable is using org.jetbrains.kotlin.com.intellij.mock.MockProject and they both have the "Register"-classes with the same name but they need the different project objects. Right now i'm using only the kotlin-compiler-embeddable as dependency in MpApt

  1. Testing
    I don't know what's the best way to write tests for this library

I will the change the checking for supported target platforms

I will add a function to the Abstractprocessor where you get the active target platform and can return true if you want to enable the processor or false if you don't. The default is every target will be enabled

fun isTargetPlatformSupported(platform: TargetPlatform) : Boolean

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.