realm / realm-java

Realm is a mobile database: a replacement for SQLite & ORMs
http://realm.io
Apache License 2.0
11.45k stars 1.75k forks source link

Realm-Plugin breaks apt if Kotlin-plugin is used. #2491

Closed marukami closed 8 years ago

marukami commented 8 years ago

Goal

Support mixed Java/Kotlin projects

Expected Results

Allow for the project to specify if apt or kapt should be used. Don't break Dagger2 or ButterKnife.

Actual Results

I did manage to get the project to compile by replacing any instance of apt with kapt. But since all my Realm Objects are still in Java I would then get "RealmTransformer doesn't seem to be applied." when trying to create a default RealmConfig.

Steps & Code to Reproduce

Have any Java Annotated class and the Kotlin Gradle plugin. I've also created a test project with a broken ButterKnife example. The ButterKnife helper class does not get generated.

Cause

The isKotlinProject assumes that if the project has the Kotlin plugin, then all annotation processing is done with kapt. I'm slowly moving a Java project over to Kotlin. So I need to maintain apt until I can switch to kapt.


def isKotlinProject = project.plugins.find {
    it.getClass().name == 'org.jetbrains.kotlin.gradle.plugin.KotlinAndroidPluginWrapper'
}

Fix?

Would it be possible to have an optional project variable that would override the current behaviour?

Version of Realm and tooling

Realm version(s): 0.88.x

Android Studio version: 15

Which Android version and device: OS X

cmelchior commented 8 years ago

Hi @marukami

Thanks for reporting this. We will look into this.

cmelchior commented 8 years ago

@realm/java how do you feel about adding a realm closure for additional configuration? That could also come in handy for other things:

apply plugin: 'realm-android'

realm {
  processor "apt" / "kapt"
}

Or something like that?

marukami commented 8 years ago

That's pretty much what I was thinking.

zaki50 commented 8 years ago

I tried to add that configuration but found that it's not so easy.

We can't modify dependency when plugin is applied since realm configuration block is not evaluated at that time. And we also can't modify dependencies after evaluation(project.afterEvaluate {}) since dependencies are fixed in the evaluation phase.

Is there any appropriate hook? @emanuelez

emanuelez commented 8 years ago

Not that I know of... this seems to be quite a pickle.

zaki50 commented 8 years ago

Now we always use kapt configuration if android-kotlin plugin was applied.

How about using apt configuration if com.neenbedankt.android-apt plugin was applied even if android-kotlin plugin was also applied.

Is that solve the issue?

emanuelez commented 8 years ago

hmmm.... that could actually work

cmelchior commented 8 years ago

So you would tell Kotlin developers: Don't use android-apt/kapt because we apply it automatically, but if your model classes are in Java, apply android-apt manually?

I am just concerned how that would affect mixed projects that e.g uses Dagger (which also uses android-apt).

I mean you could have your model classes in Kotlin but still depend on apt for some Dagger specific stuff. That would break with your proposal as far as I can tell?

zaki50 commented 8 years ago

@cmelchior The case that both apt and kapt are used in one project?

Kotlin plugin does not seem to positively support that case.

https://github.com/JetBrains/kotlin/blob/master/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/AnnotationProcessingManager.kt#L153

Please do not use `com.neenbedankt.android-apt` with kapt.
marukami commented 8 years ago

@cmelchior I'm using Dagger 2 in my mixed project, and I have mixed Java, and Kotlin classes with annotations. I'm using apt without any major issues. Interfaces are the only thing I found that must still be in Java when using apt; So, @Component and @Module. You will get warnings from apt like Note: Generating a MembersInjector or Factory for com.company.ui.main.MainPresenter. Prefer to run the dagger processor over that class instead.

If you write you Realm model classes in Kotlin, you may get name mangling. Here is a sanitized example of one of my dagger factories.

…
private final MembersInjector<MainPresenter> membersInjector;
  private final Provider<Activity> arg0Provider;
  private final Provider<GcmPlayServices> arg1Provider;
…

public MainPresenter_Factory(MembersInjector<MainPresenter> membersInjector, Provider<Activity> arg0Provider, Provider<GcmPlayServices> arg1Provider, Provider<UserService> arg2Provider, Provider<Navigator> arg3Provider, Provider<NotificationInteractor> arg4Provider, Provider<EventBus> arg5Provider) {  
    assert membersInjector != null;
    this.membersInjector = membersInjector;
    assert arg0Provider != null;
    this.arg0Provider = arg0Provider;
    assert arg1Provider != null;
    this.arg1Provider = arg1Provider;
    assert arg2Provider != null;
    this.arg2Provider = arg2Provider;
    assert arg3Provider != null;
    this.arg3Provider = arg3Provider;
    assert arg4Provider != null;
    this.arg4Provider = arg4Provider;
    assert arg5Provider != null;
    this.arg5Provider = arg5Provider;
  }

…

When running a mixed source, you need to work within constraints. If I have to write my database models in Java, that's fine.

Alternatively, is it possible to publish two plugins? One for kapt, and one for apt. Then if you are using a mixed project, you can pick whatever annotation processor works best.

cmelchior commented 8 years ago

In that case doing what @zaki50 proposes is probably OK. A configuration block would have been nicer IMO but Gradle ¯(ツ)

beeender commented 8 years ago

2568 solved this.