avaje / avaje-inject

Dependency injection via APT (source code generation) ala "Server-Side Dagger DI"
https://avaje.io/inject
Apache License 2.0
236 stars 22 forks source link

Java with Kotlin in same project #525

Closed DaiYuANg closed 7 months ago

DaiYuANg commented 7 months ago

Do we support Java with Kotlin in same project? I try to use avaje inject with annotationProcessor and kapt, and it will generate xxxModule twice for some reason I have to keep Java and Kotlin in same package namespace. Do we have solution to fix this condition?

SentryMan commented 7 months ago

Hmm, perhaps try ksp?

DaiYuANg commented 7 months ago

Hmm, perhaps try ksp?

Maybe I can help a little, already create a fork

SentryMan commented 7 months ago

I don't know kotlin that well, but is running an annotation processor twice supposed to happen?

rob-bygrave commented 7 months ago

but is running an annotation processor twice supposed to happen?

No. I believe that in this Kotlin & Java scenario the annotation processor should be setup on the kotlin compiler which would compile both Java and Kotlin (my guess is that this is the setup being used). Kotlin KAPT as I understand it, is in maintenance mode though so it isn't clear how well this setup will work in the long term - my suspicion is that kotlin wants people to create ksp processors instead.

The feedback I've got from other projects is that they use only Java [not mixed java + kotlin] for things that use annotation processors (they stop using kotlin for that part of the code base that needs and uses annotation processors).

I have not had time to look at KSP and specifically if and how it supports the mixed Java + Kotlin case.

DaiYuANg commented 7 months ago

but is running an annotation processor twice supposed to happen?

No. I believe that in this Kotlin & Java scenario the annotation processor should be setup on the kotlin compiler which would compile both Java and Kotlin (my guess is that this is the setup being used). Kotlin KAPT as I understand it, is in maintenance mode though so it isn't clear how well this setup will work in the long term - my suspicion is that kotlin wants people to create ksp processors instead.

The feedback I've got from other projects is that they use only Java [not mixed java + kotlin] for things that use annotation processors (they stop using kotlin for that part of the code base that needs and uses annotation processors).

I have not had time to look at KSP and specifically if and how it supports the mixed Java + Kotlin case.

You are right that my bad

plugins {
    id("java")
    kotlin("jvm") version "1.9.23"
    kotlin("kapt") version "1.9.23"
}

repositories {
    mavenCentral()
}

dependencies {
    testImplementation(platform("org.junit:junit-bom:5.9.1"))
    testImplementation("org.junit.jupiter:junit-jupiter")
    implementation("io.avaje:avaje-inject:9.11")
    kapt("io.avaje:avaje-inject-generator:9.11")

    testImplementation("io.avaje:avaje-inject-test:9.11")
    testAnnotationProcessor("io.avaje:avaje-inject-generator:9.11")
}

tasks.test {
    useJUnitPlatform()
}

val dir: DirectoryProperty = tasks.compileJava.get().destinationDirectory

tasks.compileKotlin{
    destinationDirectory.set(dir)
}

kapt{
    keepJavacAnnotationProcessors= true
}

tasks.jar{
    duplicatesStrategy = DuplicatesStrategy.INCLUDE

I did a test that is my gradle build script only need setup kapt as annotation processor and every thing just work.

But I'm still looking forward to support ksp

SentryMan commented 7 months ago

But I'm still looking forward to support ksp

does it not work on ksp?

DaiYuANg commented 7 months ago

But I'm still looking forward to support ksp

does it not work on ksp?

No Ksp told me

e: [ksp] No providers found in processor classpath.
e: Error occurred in KSP, check log for detail
dependencies {
    implementation('io.avaje:avaje-inject:9.12-RC2')
    annotationProcessor('io.avaje:avaje-inject-generator:9.12-RC2')

    ksp('io.avaje:avaje-inject-generator:9.12-RC2')
    testImplementation('io.avaje:avaje-inject-test:9.12-RC2')
    testAnnotationProcessor('io.avaje:avaje-inject-generator:9.12-RC2')
    testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}")
    testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}")
}
rob-bygrave commented 7 months ago

not work on ksp

Sorry, I was not explicit in that NO THIS WILL NOT WORK with KSP.

It will NOT work with KSP because we don't have a KSP processor - we have not built one. I'm not sure why @SentryMan put those comments in there but no KSP is different from Java APT and we do not have a KSP processor.

SentryMan commented 7 months ago

WILL NOT WORK with KSP.

Well that's that then

rob-bygrave commented 7 months ago

To me, in general we let the person that opened the issue close the issue. We should not close the issue unless the person who opened the issue becomes unresponsive etc and we need to tidy up issues. The issue needs to be resolved from their perspective etc.

@DaiYuANg Are we done here? Is this issue resolved?

SentryMan commented 7 months ago

The initial issue where kapt wasn't working was resolved.

rob-bygrave commented 7 months ago

The initial issue where kapt wasn't working was resolved.

Yes, I dig that. I think we are very likely resolved in this case too. I'm saying that as a policy we really want people to close their own tickets though because that explicitly means that the issue is resolved as they see it. It is funny how little things / opportunities / misunderstandings etc can get missed if we start "shutting people down" [by closing the issue] before they are truly done with their issue and I really don't want us to do that.

DaiYuANg commented 7 months ago

The initial issue where kapt wasn't working was resolved.

Yes, I dig that. I think we are very likely resolved in this case too. I'm saying that as a policy we really want people to close their own tickets though because that explicitly means that the issue is resolved as they see it. It is funny how little things / opportunities / misunderstandings etc can get missed if we start "shutting people down" [by closing the issue] before they are truly done with their issue and I really don't want us to do that.

One more question: Support ksp on the roadmap?

SentryMan commented 7 months ago

From what I'm gathering, ksp requires you to rewrite your entire processor in Kotlin so I doubt it.

DaiYuANg commented 7 months ago

Guys, I find a new question with kapt. https://github.com/DaiYuANg/scenic-view/blob/master/build.gradle.kts When I change annotationProcessor to kapt

//  annotationProcessor(libs.avajeInjectGenerator)
  kapt(libs.avajeInjectGenerator)
//factory
package org.scenicview.factory;

import io.avaje.inject.Bean;
import io.avaje.inject.Factory;
import io.avaje.inject.Lazy;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

@Factory
@Lazy
public class UIFactory {

  @Bean
  Scene scene() {
    return new Scene(new Pane());
  }

  @Bean
  Stage stage() {
    return new Stage();
  }
}
//javafx controller
package org.scenicview.controller;

import jakarta.inject.Singleton;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.CheckMenuItem;
import javafx.scene.control.MenuItem;
import javafx.scene.input.KeyCombination;
import javafx.stage.Stage;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Singleton
@Slf4j
@RequiredArgsConstructor
//constructor inject
public class MenuBarController implements Initializable {
  final Stage stage;

  @FXML CheckMenuItem showBoundsOverlaysItem;

  @FXML MenuItem exitItem;

  @Override
  public void initialize(URL url, ResourceBundle resourceBundle) {
    exitItem.setAccelerator(KeyCombination.keyCombination("CTRL+Q"));
  }

  public void onExit(ActionEvent actionEvent) {}

  public void showAbout(ActionEvent actionEvent) {}
}

Generate code

package org.scenicview.controller;

import io.avaje.inject.spi.Builder;
import io.avaje.inject.spi.Generated;
import javafx.fxml.Initializable;
import org.scenicview.controller.MenuBarController;

/**
 * Generated source - dependency injection builder for MenuBarController.
 */
@Generated("io.avaje.inject.generator")
public final class MenuBarController$DI  {

  /**
   * Create and register MenuBarController.
   */
  public static void build(Builder builder) {
    if (builder.isAddBeanFor(MenuBarController.class, Initializable.class)) {
    //there must have a stage parameter
      var bean = new MenuBarController();
      builder.register(bean);
    }
  }
}

Error

> Task :compileJava FAILED
scenic-view/build/generated/source/kapt/main/org/scenicview/controller/MenuBarController$DI.java:19: error: constructor MenuBarController in class MenuBarController cannot be applied to given types;
      var bean = new MenuBarController();
                 ^
  required: Stage
  found:    no arguments
  reason: actual and formal argument lists differ in length

The error disappeared after switching to java annotation processor. I guess it maybe lombok with kotlin?

SentryMan commented 7 months ago

you must ensure that the lombok processor runs before the avaje ones

DaiYuANg commented 7 months ago

you must ensure that the lombok processor runs before the avaje ones

I sure the lombok processor run before avaje generator the problem in kotlin-lombok (gradle plugin name.remal.lombok). I find The Lombok compiler plugin works correctly with [kapt](https://kotlinlang.org/docs/kapt.html) if annotation processors don't depend on the code generated by Lombok. on https://kotlinlang.org/docs/lombok.html#using-with-kapt

Avaje generator depend on lombok generated constructor but kapt did not pass byte code to avaje generator, so we lost constructor argument on generated code (I guessed) Should we write docs on the website for this situation that kotlin mixed java with lombok + kapt did not work perfect, The error is not here?

SentryMan commented 7 months ago

sure I can add something to the docs.