objectbox / objectbox-java

Android Database - first and fast, lightweight on-device vector database
https://objectbox.io
Apache License 2.0
4.41k stars 303 forks source link

Support new Gradle plugins syntax (benefits like classpath isolation) #960

Open realdadfish opened 3 years ago

realdadfish commented 3 years ago

With OkHttp3 4.x on the build classpath (buildSrc, build-conventions, ...) and the (closed source) objectbox-gradle-plugin being on the same classpath as well, custom task code that utilizes OkHttp3 4.x APIs will fail on runtime with exceptions like

java.lang.NoClassDefFoundError: Could not initialize class okhttp3.internal.Util

or alternatively

java.lang.NoSuchFieldError: Companion

This seems to be the reason because somehow an old OkHttp3 3.x version lands on the same classpath (which I cannot see anywhere, neither through ./gradlew buildEnvironment nor by looking at the printed classpaths from my Gradle's --debug run) and this OkHttp3 3.x version is still written in Java, not knowing anything of the 4.x Kotlin rewrite of the OkHttp team. In essence, a OkHttp3 4.x Kotlin API call to some of the extension functions in okhttp3.internal.Util fails to find the Companion object if the OkHttp3 3.x version of the same okhttp3.internal.Util class is in the classpath.

As weird and confusing this all sounds, I could fix my classpath issue by explicitely excluding the okio-1.x dependency from the objectbox-gradle-plugin and adding a 2.x binary-compatible version, like so:

classpath("io.objectbox:objectbox-gradle-plugin:2.9.0") {
    exclude group: "com.squareup.okio"
}
classpath "com.squareup.okio:okio:2.10.0"

I don't actually know whats going on in objectbox-gradle-plugin as it is proprietary and I don't even know if there is anything Objectbox could do (except for updating to a newer okio / okhttp), but I still wanted to have this issue being documented (and googleable) somewhere.

You may want to close this issue.

greenrobot-team commented 3 years ago

The plugin uses the latest moshi, which depends on an old version of okio.

+--- com.squareup.moshi:moshi:1.11.0
|    \--- com.squareup.okio:okio:1.17.5

As Gradle plugins applied using the legacy syntax share the same classpath the above fix to exclude that dependency is the correct solution.

Did you try to apply plugins using the new syntax? Would be interesting to know if that works using the ObjectBox plugin.

realdadfish commented 3 years ago

Interesting info, no, I'm working my way through a lot of legacy (build) code, the plugin is still set on the main buildscript class path and applied using the old mechanism. I'll post here if I come around fixing that (it's planned).

Am 16. März 2021 14:29:45 MEZ schrieb Uwe - ObjectBox @.***>:

The plugin uses the latest moshi, which depends on an old version of okio.

+--- com.squareup.moshi:moshi:1.11.0
|    \--- com.squareup.okio:okio:1.17.5

As Gradle plugins applied using the legacy syntax share the same classpath the above fix to exclude that dependency is the correct solution.

Did you try to apply plugins using the new syntax? Would be interesting to know if that works using the ObjectBox plugin.

-- You are receiving this because you authored the thread. Reply to this email directly or view it on GitHub: https://github.com/objectbox/objectbox-java/issues/960#issuecomment-800258316

-- Diese Nachricht wurde von meinem Android-Gerät mit K-9 Mail gesendet.

realdadfish commented 3 years ago

I updated to a much newer version of the plugin, 2.9.1, and the problem seem to be gone there. I couldn't however not find a suitable way to use the new, preferred way of defining the Gradle plugin via the plugins {} block, i.e.

plugins {
  id("io.objectbox") version "2.9.1"
}

failed to resolve the plugin even though MavenCentral is in the plugin classpath (pluginManagement.repositories {} in settings.gradle.kts).

greenrobot-team commented 3 years ago

Thanks! In the meanwhile tried this myself and it seems a specially named (additional) marker artifact is required for the new plugin syntax to work. So the current ObjectBox Gradle plugin does not work with the new plugin syntax.

I guess will keep this open and change to support the new plugin syntax.

This should "only" require switching to the Gradle Plugin Development Plugin.

Edit: need to make sure this doesn't break Android plugin integration.

MrSlateZB commented 3 years ago

Just as an FYI, I was able to use the plugins block with the ObjectBox plugin and it does not work. The ObjectBox plugin tries to resolve if you already have added the java / kotlin / android plugins and it is unable to find those plugins when used this way.

If you want to test it, you can add the following to your settings.gradle.kts

pluginManagement {
    resolutionStrategy {
        eachPlugin {
            if (requested.id.id == "io.objectbox") {
                useModule("io.objectbox:objectbox-gradle-plugin:${requested.version}")
            }
        }
    }
    repositories {
        gradlePluginPortal()
        mavenCentral()
    }
}

Once this is done, you can use the newer plugins block syntax and remove the buildscript block entry

plugins {
  id("io.objectbox") version "2.9.1"
}

The error you get is:

Failed to apply plugin 'io.objectbox'. 'io.objectbox' expects one of the following plugins to be applied to the project:

  • java
  • kotlin
  • android
  • android-library
  • com.android.application
  • com.android.library
  • com.android.feature

If you'd like I can make this a separate ticket.

greenrobot-team commented 3 years ago

@MrSlateZB Thanks for figuring this out. In my attempts using resolutionStrategy did not work. I'll have a look again.

Note: Being able to detect applied plugins is necessary so they can be configured correctly. If that is not possible using the plugins syntax, then this likely can never be supported.

realdadfish commented 3 years ago

Gradle allows to configure dependent plug-ins lazily with project.plugins.withId("plugin.id") {...}, even when these plug-ins are added through a plugin block, so this shouldn't be a problem.

Am 4. Mai 2021 13:04:58 MESZ schrieb Uwe - ObjectBox @.***>:

@MrSlateZB Thanks for figuring this out. In my attempts using resolutionStrategy did not work. I'll have a look again.

Note: Being able to detect applied plugins is necessary so they can be configured correctly. If that is not possible using the plugins syntax, then this likely can never be supported.

-- You are receiving this because you authored the thread. Reply to this email directly or view it on GitHub: https://github.com/objectbox/objectbox-java/issues/960#issuecomment-831858292

-- Diese Nachricht wurde von meinem Android-Gerät mit K-9 Mail gesendet.

greenrobot-team commented 3 years ago

Interestingly enough using a combination of buildscript block and plugins block works for an Android project (based on Android Studio 4.1.3 new project template):

# build.gradle
buildscript {
    ext.kotlin_version = "1.4.32"
    ext.objectboxVersion = "2.9.1"

    repositories {
        google()
        mavenCentral()
        jcenter()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:4.1.3"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "io.objectbox:objectbox-gradle-plugin:$objectboxVersion"
    }
}

# app/build.gradle
plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'io.objectbox'
}

But I doubt this isolates classpaths of the added plugins.

Which brings up the question: does the Android plugin support being added using the new plugins syntax? Edit: and even if, does it work in combination with the Kotlin plugin?

MrSlateZB commented 3 years ago

@greenrobot-team Yea, our build use this methodology. You can't specify a version this way, but I don't think this is the preferred method for adding plugins anymore. You also can't set a version on the plugin using this method (its determined by the buildscript classpath block).

souvikhazra1 commented 2 years ago

Quick way to load gradle plugins which doesn't support plugin block.

https://gist.github.com/souvikhazra1/65782bf89dcc39618f46acd3ea2c0202

greenrobot-team commented 2 years ago

@souvikhazra1 Thanks! Did you test this using the ObjectBox plugin? A similar approach was tried before and didn't work.

Update: the Gradle docs recommend a similar approach, so this should work.

souvikhazra1 commented 2 years ago

@greenrobot-team yes I tried with objectbox plz check my gist, I used objectbox as example but any plugin can be used this way. Only thing is I applied this custom- prefix in the plugin name to make it generic.

obvionaoe commented 2 years ago

Any development regarding this issue? Any chance the plugin could be open sourced? I'd be happy to help implement this

greenrobot commented 2 years ago

Any chance the plugin could be open sourced?

We wanted to do this for quite some time, but just didn't find the time to do it. Maybe this a good nudge... :) cc @greenrobot-team

mimoccc commented 1 year ago

Cannot invoke "org.gradle.api.Project.getProjectDir()" because the return value of "io.objectbox.gradle.ProjectEnv.getProject()" is null

greenrobot-team commented 1 year ago

@mimoccc I assume this happens when trying to use the ObjectBox plugin with the new Gradle plugin syntax? I also assume this happens when using version 3.6.0?

ZyElite commented 1 year ago

build.gradle.kts

  plugins {
      id("com.android.application") version "8.1.1" apply false
      id("io.objectbox") version "3.7.0" apply false
  }

setting.gradle.kts

pluginManagement {
    ...

    resolutionStrategy {
        eachPlugin {
            if (requested.id.id == "io.objectbox") {
                useModule("io.objectbox:objectbox-gradle-plugin:${requested.version}")
            }
        }
    }
}

result

image