Giter VIP home page Giter VIP logo

anvil's Introduction

Anvil

Maven Central CI

"When all you have is an anvil, every problem looks like a hammer." - Abraham Maslow

Anvil is a Kotlin compiler plugin to make dependency injection with Dagger easier by automatically merging Dagger modules and component interfaces. In a nutshell, instead of manually adding modules to a Dagger component and making the Dagger component extend all component interfaces, these modules and interfaces can be included in a component automatically:

@Module
@ContributesTo(AppScope::class)
class DaggerModule { .. }

@ContributesTo(AppScope::class)
interface ComponentInterface {
  fun getSomething(): Something
  fun injectActivity(activity: MyActivity)
}

// The real Dagger component.
@MergeComponent(AppScope::class)
interface AppComponent

The generated AppComponent interface that Dagger sees looks like this:

@Component(modules = [DaggerModule::class])
interface AppComponent : ComponentInterface

Notice that AppComponent automatically includes DaggerModule and extends ComponentInterface.

Setup

The plugin consists of a Gradle plugin and Kotlin compiler plugin. The Gradle plugin automatically adds the Kotlin compiler plugin and annotation dependencies. It needs to be applied in all modules that either contribute classes to the dependency graph or merge them:

plugins {
  id 'com.squareup.anvil' version "${latest_version}"
}

Or you can use the old way to apply a plugin:

buildscript {
  repositories {
    mavenCentral()
  }
  dependencies {
    classpath "com.squareup.anvil:gradle-plugin:${latest_version}"
  }
}

apply plugin: 'com.squareup.anvil'

Quick Start

There are three important annotations to work with Anvil.

@ContributesTo can be added to Dagger modules and component interfaces that should be included in the Dagger component. Classes with this annotation are automatically merged by the compiler plugin as long as they are on the compile classpath.

@MergeComponent is used instead of the Dagger annotation @Component. Anvil will generate the Dagger annotation and automatically include all modules and component interfaces that were contributed the same scope.

@MergeSubcomponent is similar to @MergeComponent and should be used for subcomponents instead.

Scopes

Scope classes are only markers. The class AppScope from the sample could look like this:

abstract class AppScope private constructor()

These scope classes help Anvil make a connection between the Dagger component and which Dagger modules and other component interfaces to include.

Scope classes are independent of the Dagger scopes. It's still necessary to set a scope for the Dagger component, e.g.

@Singleton
@MergeComponent(AppScope::class)
interface AppComponent

Contributed bindings

The @ContributesBinding annotation generates a Dagger binding method for an annotated class and contributes this binding method to the given scope. Imagine this example:

interface Authenticator

class RealAuthenticator @Inject constructor() : Authenticator

@Module
@ContributesTo(AppScope::class)
abstract class AuthenticatorModule {
  @Binds abstract fun bindRealAuthenticator(authenticator: RealAuthenticator): Authenticator
}

This is a lot of boilerplate if you always want to use RealAuthenticator when injecting Authenticator. You can replace this entire Dagger module with the @ContributesBinding annotation. The equivalent would be:

interface Authenticator

@ContributesBinding(AppScope::class)
class RealAuthenticator @Inject constructor() : Authenticator

@ContributesBinding also supports qualifiers. You can annotate the class with any qualifier and the generated binding method will preserve the qualifier, e.g.

@ContributesBinding(AppScope::class)
@Named("Prod")
class RealAuthenticator @Inject constructor() : Authenticator

// Will generate:
@Binds @Named("Prod") 
abstract fun bindRealAuthenticator(authenticator: RealAuthenticator): Authenticator

Contributed multibindings

Similar to contributed bindings, @ContributesMultibinding will generate a multibindings method for (all/an) annotated class(es). Qualifiers are supported the same way as normal bindings.

@ContributesMultibinding(AppScope::class)
@Named("Prod")
class MainListener @Inject constructor() : Listener

// Will generate this binding method.
@Binds @IntoSet @Named("Prod")
abstract fun bindMainListener(listener: MainListener): Listener

If the class is annotated with a map key annotation, then Anvil will generate a maps multibindings method instead of adding the element to a set:

@MapKey
annotation class BindingKey(val value: String)

@ContributesMultibinding(AppScope::class)
@BindingKey("abc")
class MainListener @Inject constructor() : Listener

// Will generate this binding method.
@Binds @IntoMap @BindingKey("abc")
abstract fun bindMainListener(listener: MainListener): Listener

Exclusions

Dagger modules and component interfaces can be excluded in two different levels.

One class can always replace another one. This is especially helpful for modules that provide different bindings for instrumentation tests, e.g.

@Module
@ContributesTo(
    scope = AppScope::class,
    replaces = [DevelopmentApplicationModule::class]
)
object DevelopmentApplicationTestModule {
  @Provides
  fun provideEndpointSelector(): EndpointSelector = TestingEndpointSelector
}

The compiler plugin will find both classes on the classpath. Adding both modules DevelopmentApplicationModule and DevelopmentApplicationTestModule to the Dagger graph would lead to duplicate bindings. Anvil sees that the test module wants to replace the other and ignores it. This replacement rule has a global effect for all applications which are including the classes on the classpath.

Applications can exclude Dagger modules and component interfaces individually without affecting other applications.

@MergeComponent(
  scope = AppScope::class,
  exclude = [
    DaggerModule::class
  ]
)
interface AppComponent

In a perfect build graph it’s unlikely that this feature is needed. However, due to legacy modules, wrong imports and deeply nested dependency chains applications might need to make use of it. The exclusion rule does what it implies. In this specific example DaggerModule wishes to be contributed to this scope, but it has been excluded for this component and thus is not added.

Dagger Factory Generation

Anvil allows you to generate Factory classes that usually the Dagger annotation processor would generate for @Provides methods, @Inject constructors and @Inject fields. The benefit of this feature is that you don't need to enable the Dagger annotation processor in this module. That often means you can skip KAPT and the stub generating task. In addition Anvil generates Kotlin instead of Java code, which allows Gradle to skip the Java compilation task. The result is faster builds.

Gradle DSL
// build.gradle
anvil {
  generateDaggerFactories = true // default is false
}
Gradle Properties
# gradle.properties
com.squareup.anvil.generateDaggerFactories=true # default is false

In our codebase we measured that modules using Dagger build 65% faster with this new Anvil feature compared to using the Dagger annotation processor:

Stub generation Kapt Javac Kotlinc Sum
Dagger 12.976 40.377 8.571 10.241 72.165
Anvil 0 0 6.965 17.748 24.713

For full builds of applications we measured savings of 16% on average.

Benchmark Dagger Factories

This feature can only be enabled in Gradle modules that don't compile any Dagger component. Since Anvil only processes Kotlin code, you shouldn't enable it in modules with mixed Kotlin / Java sources either.

When you enable this feature, don't forget to remove the Dagger annotation processor. You should keep all other dependencies.

Extending Anvil

Every codebase has its own dependency injection patterns where certain code structures need to be repeated over and over again. Here Anvil comes to the rescue and you can extend the compiler plugin with your own CodeGenerator. For usage please take a look at the compiler-api artifact

Advantages of Anvil

Adding Dagger modules to components in a large modularized codebase with many application targets is overhead. You need to know where components are defined when creating a new Dagger module and which modules to add when setting up a new application. This task involves many syncs in the IDE after adding new module dependencies in the build graph. The process is tedious and cumbersome. With Anvil you only add a dependency in your build graph and then you can immediately test the build.

Aligning the build graph and Dagger's dependency graph brings a lot of consistency. If code is on the compile classpath, then it's also included in the Dagger dependency graph.

Modules implicitly have a scope, if provided objects are tied to a scope. Now the scope of a module is clear without looking at any binding.

With Anvil you don't need any composite Dagger module anymore, which only purpose is to combine multiple modules to avoid repeating the setup for multiple applications. Composite modules easily become hairballs. If one application wants to exclude a module, then it has to repeat the setup. These forked graphs are painful and confusing. With Dagger you want to make the decision which modules fulfill dependencies as late as possible, ideally in the application module. Anvil makes this approach a lot easier by generating the code for included modules. Composite modules are redundant. You make the decision which bindings to use by importing the desired module in the application module.

Performance

Anvil is a convenience tool. Similar to Dagger it doesn't improve build speed compared to writing all code manually before running a build. The savings are in developer time.

The median overhead of Anvil is around 4%, which often means only a few hundred milliseconds on top. The overhead is marginal, because Kotlin code is still compiled incrementally and Kotlin compile tasks are skipped entirely, if nothing has changed. This doesn't change with Anvil.

Benchmark

On top of that, Anvil provides actual build time improvements by replacing the Dagger annotation processor in many modules if you enable Dagger Factory generation.

Kotlin compiler plugin

We investigated whether other alternatives like a bytecode transformer and an annotation processor would be a better option, but ultimately decided against them. For what we tried to achieve a bytecode transformer runs too late in the build process; after the Dagger components have been generated. An annotation processor especially when using KAPT would be too slow. Even though the Kotlin compiler plugin API isn't stable and contains bugs we decided to write a compiler plugin.

Limitations

No Java support

Anvil is a Kotlin compiler plugin, thus Java isn’t supported. You can use Anvil in modules with mixed Java and Kotlin code for Kotlin classes, though.

Correct error types disabled

KAPT has the option to correct non-existent types. This option however changes order of how compiler plugins and KAPT itself are invoked. The result is that Anvil cannot merge supertypes before the Dagger annotation processor runs and abstract functions won't be implemented properly in the final Dagger component.

Anvil will automatically set correctErrorTypes to false to avoid this issue.

Incremental Kotlin compilation breaks Anvil's feature to merge contributions

Tip

Anvil now experimentally supports incremental compilation and Gradle's build caching, as of v2.5.0.

This feature is disabled by default. It can be enabled via a Gradle property or the Gradle DSL:

Gradle Properties
# gradle.properties
com.squareup.anvil.trackSourceFiles=true # default is false
Gradle DSL
// build.gradle
anvil {
  trackSourceFiles = true // default is false
}

Anvil merges Dagger component interfaces and Dagger modules during the stub generating task when @MergeComponent is used. This requires scanning the compile classpath for any contributions. Assume the scenario that a contributed type in a module dependency has changed, but the module using @MergeComponent itself didn't change. With Kotlin incremental compilation enabled the compiler will notice that the module using @MergeComponent doesn't need to be recompiled and therefore doesn't invoke compiler plugins. Anvil will miss the new contributed type from the module dependency.

To avoid this issue, Anvil must disable incremental compilation for the stub generating task, which runs right before Dagger processes annotations. Normal Kotlin compilation isn't impacted by this workaround. The issue is captured in KT-54850 Provide mechanism for compiler plugins to add custom information into binaries.

Disabling incremental compilation for the stub generating task could have a negative impact on compile times, if you heavily rely on KAPT. While Anvil can significantly help to improve build times, the wrong configuration and using KAPT in most modules could make things worse. The suggestion is to extract and isolate annotation processors in separate modules and avoid using Anvil in the same modules, e.g. a common practice is to move the Dagger component using @MergeComponent into the final application module with little to no other code in the app module.

Hilt

Hilt is Google's opinionated guide how to dependency injection on Android. It provides a similar feature with @InstallIn for entry points and modules as Anvil. If you use Hilt, then you don't need to use Anvil.

Hilt includes many other features and comes with some restrictions. For us it was infeasible to migrate a codebase to Hilt with thousands of modules and many Dagger components while we only needed the feature to merge modules and component interfaces automatically. We also restrict the usage of the Dagger annotation processor to only specific modules for performance reasons. With Hilt we wouldn't be able to enforce this requirement anymore for component interfaces. The development of Anvil started long before Hilt was announced and the internal version is being used in production for a while.

License

Copyright 2020 Square, Inc.

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.

anvil's People

Contributors

aderington avatar ahmedre avatar bddckr avatar bryanstern avatar digitalbuddha avatar gabrielittner avatar gmarques33 avatar ilyagulya avatar jhperezd avatar joelwilcox avatar mezpahlan avatar quiro91 avatar rafaeltoledo avatar rbusarow avatar renovate[bot] avatar rharter avatar stephanenicolas avatar steve-the-edwards avatar turansky avatar vlad-kasatkin avatar vrallev avatar zach-klippenstein avatar zacsweers 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  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

anvil's Issues

About the name...

Hephaestus.

It's hard to pronounce and spell. (I had to copy and paste it above, after I manually typed it wrong the first time.) Would you consider renaming this project to something simpler? Slightly tongue-in-cheek request, but in all sincerity, the name is a (mild) usability impediment. It makes it hard to discuss the library.

Fails when using gradle worker api

Using the Gradle worker api, each worker gets it's own classpath, so trying to run Hephaestus fails with the following error message.

Cannot set the value of task ':some:module:compileReleaseKotlinCheckIncrementalCompilationHephaestus' property 'incrementalSignal' of type com.squareup.hephaestus.plugin.IncrementalSignal using a provider of type com.squareup.hephaestus.plugin.IncrementalSignal.

This is in a project with kapt using the worker API, and Room.

I've attempted to implement this in my branch here, but need to take off for the day.

Generated Dagger factory should respect class internal visibility

Let's say we have a following a Gradle module with following Dagger module:

@Module
abstract class SomeModule {

    @Binds
    internal abstract fun bindSomeManager(impl: DelegatingSomeManager): SomeManager
}

where DelegatingSomeManager has internal visiblity plus @Inject annotation on constructor. SomeManager interface has public visibility.

Generated DelegatingSomeManager_Factory.kt looks follwoing:

@Generated(
  value = ["com.squareup.anvil.compiler.AnvilComponentRegistrar"],
  comments = "https://github.com/square/anvil"
)
class DelegatingSomeManager_Factory() : Factory<DelegatingSomeManager> {
  override fun get(): DelegatingSomeManager = newInstance()

  companion object {
    @JvmStatic
    fun create(): DelegatingSomeManager_Factory = DelegatingSomeManager_Factory()

    @JvmStatic
    fun newInstance(): DelegatingSomeManager = DelegatingSomeManager()
  }
}

Kotlin compile task fails with following message:

'public' function exposes its 'internal' return type DelegatingSomeManager

Module compilation via Dagger kapt completes without error.

Most probably solution would be to generate factories with the same visibility modifier that class has.

Issue with Multibindings

I am using Anvil + dagger multibindings to build a plugin system and I've encountered the following issue.

Consider the following setup (omitting the AppComponent for brevity)

interface PluginPoint<T> {
  fun getPlugins(): List<T>
}

interface PrinterPlugin {
  fun print()
}

@Module
@ContributesTo(AppScope::class)
class ExamplePluginsModule {

  @Provides
  @IntoSet
  fun provideFoo(): PrinterPlugin = Foo()

  @Provides
  @IntoSet
  fun provideBar(): PrinterPlugin = Bar()

}

class PrinterPluginPoint @Inject constructor(
  private val plugins: Set<PrinterPlugin>
): PluginPoint<PrinterPlugin> {
  override fun getPlugins(): List<PrinterPlugin> {
    return plugins.toList()
  }
}

class Foo: PrinterPlugin {
  override fun print() {
    Timber.d("Foo")
  }
}
class Bar: PrinterPlugin {
  override fun print() {
    Timber.d("Bar")
  }
}

Then in the Application class

open class App : HasAndroidInjector, Application() {
  //...

  @Inject
  lateinit var printerPluginPoint: PrinterPluginPoint

  override fun onCreate() {
    super.onCreate()

    // HERE configure DI...

    printerPluginPoint.getPlugins().forEach { it.print() }
  }

//...
}

Result: [Dagger/MissingBinding] java.util.Set<? extends io.avs.android.plugins.PrinterPlugin>

However, replacing the dependency of PrinterPluginPoint to a Lazy<Set<PrinterPlugin>> fixes the issue. I guess the codegen path is different

Is this known? or maybe I am missing something?

Factory generation: lowercase class names

class myclass @Inject constructor()

This simple reproducer fails inside your codegen with exception.

at com.squareup.kotlinpoet.ClassName$Companion.bestGuess(TypeName.kt:459)
        at com.squareup.anvil.compiler.codegen.dagger.KotlinPoetKt.asClassName(KotlinPoet.kt:37)
        at com.squareup.anvil.compiler.codegen.dagger.InjectConstructorFactoryGenerator.generateFactoryClass(InjectConstructorFactoryGenerator.kt:55)

Dagger modules that are interfaces are treated as component interfaces

If you have a module that is an interface Hephaestus will treat it as component interface and make the component extend it.

This can be reproduced by changing FatherProviderModule in the sample to an interface. You'll get this error:

hephaestus-main/sample/app/build/tmp/kapt3/stubs/debug/com/squareup/hephaestus/sample/AppComponent.java:9: error: [com.squareup.hephaestus.sample.father.FatherProviderModule.bindFatherProvider(com.squareup.hephaestus.sample.father.RealFatherProvider)] Members injection methods may only return the injected type or void. public abstract interface AppComponent extends com.squareup.hephaestus.sample.DescriptionComponent, com.squareup.hephaestus.sample.father.FatherProviderModule {

FR: Module -> Module contribution

A common case I've run into is wanting to have modules contribute to other modules, rather than components. I often use modules that are intentionally reusable, and generating a module that contributes to them would be immensely helpful.

@Module
class DataModule { .. }

@Module
@ContributesTo(DataModule::class)
class VariantDataModule { .. }

// Results in 
@Module(includes = [VariantDataModule::class])
class DataModule { .. }

Anvil is incompatible with Kotlin 1.4.0 "-Xexplicit-api=strict" flag

When using Anvil in a module that has "-Xexplicit-api=strict" the project fails to compile with the following message:

Visibility must be specified in explicit API mode
Return type must be specified in explicit API mode

This is happening due to the following snippet of code (for both ContributesBindingGenerator and ContributesToGenerator):

         val content = """
              package $generatedPackage
              
              val ${className.replace('.', '_')}$REFERENCE_SUFFIX = $className::class
              val ${className.replace('.', '_')}$SCOPE_SUFFIX = $scope::class
          """.trimIndent()

The reason is because the variables do not have visibility modifiers neither return types but are defined inside the module defined as strict mode. Other than that, I didn't experience any other issues with strict mode so far.

To simulate, just turn on the strict mode in a module that uses Anvil. For example:

compileKotlin {
    kotlinOptions {
        jvmTarget = JavaVersion.VERSION_1_8.toString()
        freeCompilerArgs = [
                "-Xexplicit-api=strict"
        ]
    }
}

Support for Kotlin IR Backend

Kotlin 1.4 brings us new *shiny* IR backend which has it's own set of extensions for codegen. Current ones (ExpressionCodegen and the one for js) doesn't seem to be supported anymore.

Noticed it in practice while trying this with IR enabled. I know that it is a bit too early, but would be nice to have :)

General advice on structuring

Hi.
I know this is not really an issue, but this library implies it.

I presume since you made this library, your issue was a multi-module project, and you got sick of attaching partial component interfaces + modules onto the implementing "merged" component in :app.

My question is -- should it even be a thing? i.e. should library modules own their DI stuff? Shouldn't it only be :app concern? Retrofit doesnt provide a dagger module, so why should my let's say :payments do that?

I'm toying with this idea. I kind of like it. But then again, if I were to implement this, and presuming the multi-module project is multiple apps, not just one, I'd need to duplicate a lot of dagger module provider functions etc, per app

Support named import

The following class is processed successfully by Dagger compiler but not by Anvil

package com.squareup.test

import dagger.Module
import dagger.Provides
import java.lang.Runnable as NamedRunnable
        
@Module
object DaggerModule1 {
    @Provides fun provideRunner(): NamedRunnable = NamedRunnable {}
}

generateDaggerFactories fails when module return type is inferred

@AppScope
    @JvmStatic
    @Provides
    fun appSlotsRepository(
        dao: Dao,
        apiClient: ApiClient,
        refreshHelper: RefreshHelper
    ) = AppSlotRepository(apiClient, dao, refreshHelper)
e: com.squareup.anvil.compiler.AnvilCompilationException: Back-end (JVM) Internal error: Couldn't resolve type of function: sk.o2.mojeo2.base.di.DataModule.appSlotsRepository
File being compiled: (186,5) in C:/Users/ursus/AndroidStudioProjects/o2-selfcare-android/legacy/src/main/java/sk/o2/mojeo2/base/di/DataModule.kt

	at com.squareup.anvil.compiler.codegen.KotlinPoetKt$requireTypeName$1.invoke(KotlinPoet.kt:48)
	at com.squareup.anvil.compiler.codegen.KotlinPoetKt.requireTypeName(KotlinPoet.kt:53)
	at com.squareup.anvil.compiler.codegen.KotlinPoetKt.requireTypeName$default(KotlinPoet.kt:46)
	at com.squareup.anvil.compiler.codegen.dagger.ProvidesMethodFactoryGenerator.generateFactoryClass(ProvidesMethodFactoryGenerator.kt:92)
	at com.squareup.anvil.compiler.codegen.dagger.ProvidesMethodFactoryGenerator.access$generateFactoryClass(ProvidesMethodFactoryGenerator.kt:42)
	at com.squareup.anvil.compiler.codegen.dagger.ProvidesMethodFactoryGenerator$generateCode$3$2.invoke(ProvidesMethodFactoryGenerator.kt:58)
	at com.squareup.anvil.compiler.codegen.dagger.ProvidesMethodFactoryGenerator$generateCode$3$2.invoke(ProvidesMethodFactoryGenerator.kt:42)
	at kotlin.sequences.TransformingSequence$iterator$1.next(Sequences.kt:210)
	at kotlin.sequences.FlatteningSequence$iterator$1.next(Sequences.kt:299)
	at kotlin.sequences.SequencesKt___SequencesKt.toCollection(_Sequences.kt:742)
	at kotlin.sequences.SequencesKt___SequencesKt.toMutableList(_Sequences.kt:772)
	at kotlin.sequences.SequencesKt___SequencesKt.toList(_Sequences.kt:763)
	at com.squareup.anvil.compiler.codegen.dagger.ProvidesMethodFactoryGenerator.generateCode(ProvidesMethodFactoryGenerator.kt:61)
	at com.squareup.anvil.compiler.codegen.CodeGenerationExtension$analysisCompleted$2.invoke(CodeGenerationExtension.kt:57)
	at com.squareup.anvil.compiler.codegen.CodeGenerationExtension.analysisCompleted(CodeGenerationExtension.kt:62)
	at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM$analyzeFilesWithJavaIntegration$2.invoke(TopDownAnalyzerFacadeForJVM.kt:108)
	at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(TopDownAnalyzerFacadeForJVM.kt:118)
	at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration$default(TopDownAnalyzerFacadeForJVM.kt:93)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:526)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:90)
	at org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport.analyzeAndReport(AnalyzerWithCompilerReport.kt:115)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.analyze(KotlinToJVMBytecodeCompiler.kt:517)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli(KotlinToJVMBytecodeCompiler.kt:185)
	at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:164)
	at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:51)
	at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:86)
	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.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:346)
	at org.jetbrains.kotlin.incremental.IncrementalJvmCompilerRunner.runCompiler(IncrementalJvmCompilerRunner.kt:102)
	at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compileIncrementally(IncrementalCompilerRunner.kt:240)
	at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.access$compileIncrementally(IncrementalCompilerRunner.kt:39)
	at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner$compile$2.invoke(IncrementalCompilerRunner.kt:81)
	at org.jetbrains.kotlin.incremental.IncrementalCompilerRunner.compile(IncrementalCompilerRunner.kt:93)
	at org.jetbrains.kotlin.daemon.CompileServiceImplBase.execIncrementalCompiler(CompileServiceImpl.kt:601)
	at org.jetbrains.kotlin.daemon.CompileServiceImplBase.access$execIncrementalCompiler(CompileServiceImpl.kt:93)
	at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1633)
	at sun.reflect.GeneratedMethodAccessor99.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:357)
	at sun.rmi.transport.Transport$1.run(Transport.java:200)
	at sun.rmi.transport.Transport$1.run(Transport.java:197)
	at java.security.AccessController.doPrivileged(Native Method)
	at sun.rmi.transport.Transport.serviceCall(Transport.java:196)
	at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:573)
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:834)
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:688)
	at java.security.AccessController.doPrivileged(Native Method)
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:687)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)

If I declare the return type explicity then it works. Its good do it anyways, however dagger can work throught it without a problem

Gradle plugin 1.0.5-1.4-M3 throws NoSuchMethodError on non-Android Kotlin project

I haven't looked too hard, but I think any AGP dependencies in AnvilPlugin need to be done in an optionally configured way?

I think this is the related file:
https://github.com/square/anvil/blob/3b58a5758b6d1fc56b388ff82980f435e39114b7/gradle-plugin/src/main/java/com/squareup/anvil/plugin/IncrementalSignal.kt

java.lang.NoSuchMethodError: 'java.io.Closeable com.android.build.gradle.internal.workeractions.WorkerActionServiceRegistry.registerServiceAsCloseable(com.android.build.gradle.internal.workeractions.WorkerActionServiceRegistry$ServiceKey, java.lang.Object)'
        at com.squareup.anvil.plugin.IncrementalSignalBuildService.registerIncrementalSignalService(IncrementalSignal.kt:69)
        at com.squareup.anvil.plugin.IncrementalSignalBuildService.registerIncrementalSignalService$default(IncrementalSignal.kt:64)
        at com.squareup.anvil.plugin.IncrementalSignalKt.registerIncrementalSignalBuildService(IncrementalSignal.kt:27)
        at com.squareup.anvil.plugin.AnvilPlugin.disableIncrementalKotlinCompilation(AnvilPlugin.kt:119)
        at com.squareup.anvil.plugin.AnvilPlugin.realApply(AnvilPlugin.kt:64)
        at com.squareup.anvil.plugin.AnvilPlugin.access$realApply(AnvilPlugin.kt:25)
        at com.squareup.anvil.plugin.AnvilPlugin$apply$3.invoke(AnvilPlugin.kt:47)
        at com.squareup.anvil.plugin.AnvilPlugin$apply$3.invoke(AnvilPlugin.kt:25)
        at com.squareup.anvil.plugin.AnvilPlugin$apply$1$1.execute(AnvilPlugin.kt:35)
        at com.squareup.anvil.plugin.AnvilPlugin$apply$1$1.execute(AnvilPlugin.kt:25)

Could not find plugin

Wanted to play around the plugin but I am running into this issue, where the plugin cannot be found.

Could not find com.squareup.hepheastus:gradle-plugin:1.0.0.
Searched in the following locations:
  - https://dl.google.com/dl/android/maven2/com/squareup/hepheastus/gradle-plugin/1.0.0/gradle-plugin-1.0.0.pom
  - https://jcenter.bintray.com/com/squareup/hepheastus/gradle-plugin/1.0.0/gradle-plugin-1.0.0.pom
  - https://repo.maven.apache.org/maven2/com/squareup/hepheastus/gradle-plugin/1.0.0/gradle-plugin-1.0.0.pom
Required by:
    project :
repositories {
        ...
        mavenCentral()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:4.0.0"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "com.squareup.hepheastus:gradle-plugin:1.0.0"
    }

Generated code with Anvil is larger than Dagger generated code

This output is from the integration test library. I need to investigate where and how we can safe. There are a couple of ideas.

Report

ralf$ diffuse diff --jar old.jar new.jar
OLD: old.jar
NEW: new.jar

 JAR   │ old      │ new      │ diff
───────┼──────────┼──────────┼──────────
 class │ 27.4 KiB │ 32.1 KiB │ +4.7 KiB
 other │    468 B │    468 B │      0 B
───────┼──────────┼──────────┼──────────
 total │ 27.9 KiB │ 32.6 KiB │ +4.7 KiB


 CLASSES │ old │ new │ diff
─────────┼─────┼─────┼──────────────
 classes │  27 │  27 │   0 (+3 -3)
 methods │  68 │  78 │ +10 (+19 -9)
  fields │  21 │  24 │  +3 (+6 -3)


=================
====   JAR   ====
=================

 size     │ diff     │ path
──────────┼──────────┼──────────────────────────────────────────────────────────────────────────────────
  2.2 KiB │ +2.2 KiB │ + com/squareup/anvil/test/AppModule2_ProvideFunctionFactory$Companion.class
  1.5 KiB │ +1.5 KiB │ + com/squareup/anvil/test/SubcomponentBinding_Factory$Companion.class
  1.4 KiB │ +1.4 KiB │ + com/squareup/anvil/test/AppBinding_Factory$Companion.class
          │   -788 B │ - com/squareup/anvil/test/AppModule2_ProvideFunctionFactory$InstanceHolder.class
          │   -752 B │ - com/squareup/anvil/test/SubcomponentBinding_Factory$InstanceHolder.class
          │   -698 B │ - com/squareup/anvil/test/AppBinding_Factory$InstanceHolder.class
  1.6 KiB │   +664 B │ ∆ com/squareup/anvil/test/AppBinding_Factory.class
  1.7 KiB │   +673 B │ ∆ com/squareup/anvil/test/SubcomponentBinding_Factory.class
  1.9 KiB │   +473 B │ ∆ com/squareup/anvil/test/AppModule2_ProvideFunctionFactory.class
──────────┼──────────┼──────────────────────────────────────────────────────────────────────────────────
 10.4 KiB │ +4.7 KiB │ (total)



=====================
====   CLASSES   ====
=====================

CLASSES:

   old │ new │ diff
  ─────┼─────┼───────────
   27  │ 27  │ 0 (+3 -3)

  + com.squareup.anvil.test.AppBinding_Factory$Companion
  + com.squareup.anvil.test.AppModule2_ProvideFunctionFactory$Companion
  + com.squareup.anvil.test.SubcomponentBinding_Factory$Companion

  - com.squareup.anvil.test.AppModule2_ProvideFunctionFactory$InstanceHolder
  - com.squareup.anvil.test.AppBinding_Factory$InstanceHolder
  - com.squareup.anvil.test.SubcomponentBinding_Factory$InstanceHolder


METHODS:

   old │ new │ diff
  ─────┼─────┼──────────────
   68  │ 78  │ +10 (+19 -9)

  + com.squareup.anvil.test.AppBinding_Factory <clinit>()
  + com.squareup.anvil.test.AppBinding_Factory access$getInstance$cp() → AppBinding_Factory
  + com.squareup.anvil.test.AppBinding_Factory$Companion <init>()
  + com.squareup.anvil.test.AppBinding_Factory$Companion <init>(DefaultConstructorMarker)
  + com.squareup.anvil.test.AppBinding_Factory$Companion create() → AppBinding_Factory
  + com.squareup.anvil.test.AppBinding_Factory$Companion newInstance() → AppBinding
  + com.squareup.anvil.test.AppModule2_ProvideFunctionFactory <clinit>()
  + com.squareup.anvil.test.AppModule2_ProvideFunctionFactory access$getInstance$cp() → AppModule2_ProvideFunctionFactory
  + com.squareup.anvil.test.AppModule2_ProvideFunctionFactory$Companion <init>()
  + com.squareup.anvil.test.AppModule2_ProvideFunctionFactory$Companion <init>(DefaultConstructorMarker)
  + com.squareup.anvil.test.AppModule2_ProvideFunctionFactory$Companion create() → AppModule2_ProvideFunctionFactory
  + com.squareup.anvil.test.AppModule2_ProvideFunctionFactory$Companion provideFunction() → Function1
  + com.squareup.anvil.test.SubcomponentBinding_Factory <clinit>()
  + com.squareup.anvil.test.SubcomponentBinding_Factory access$getInstance$cp() → SubcomponentBinding_Factory
  + com.squareup.anvil.test.SubcomponentBinding_Factory$Companion <init>()
  + com.squareup.anvil.test.SubcomponentBinding_Factory$Companion <init>(DefaultConstructorMarker)
  + com.squareup.anvil.test.SubcomponentBinding_Factory$Companion create() → SubcomponentBinding_Factory
  + com.squareup.anvil.test.SubcomponentBinding_Factory$Companion newInstance() → SubcomponentBinding
  + kotlin.jvm.internal.Intrinsics checkExpressionValueIsNotNull(Object, String)

  - com.squareup.anvil.test.AppBinding_Factory$InstanceHolder <clinit>()
  - com.squareup.anvil.test.AppBinding_Factory$InstanceHolder <init>()
  - com.squareup.anvil.test.AppBinding_Factory$InstanceHolder access$000() → AppBinding_Factory
  - com.squareup.anvil.test.AppModule2_ProvideFunctionFactory$InstanceHolder <clinit>()
  - com.squareup.anvil.test.AppModule2_ProvideFunctionFactory$InstanceHolder <init>()
  - com.squareup.anvil.test.AppModule2_ProvideFunctionFactory$InstanceHolder access$000() → AppModule2_ProvideFunctionFactory
  - com.squareup.anvil.test.SubcomponentBinding_Factory$InstanceHolder <clinit>()
  - com.squareup.anvil.test.SubcomponentBinding_Factory$InstanceHolder <init>()
  - com.squareup.anvil.test.SubcomponentBinding_Factory$InstanceHolder access$000() → SubcomponentBinding_Factory


FIELDS:

   old │ new │ diff
  ─────┼─────┼────────────
   21  │ 24  │ +3 (+6 -3)

  + com.squareup.anvil.test.AppBinding_Factory Companion: AppBinding_Factory$Companion
  + com.squareup.anvil.test.AppBinding_Factory instance: AppBinding_Factory
  + com.squareup.anvil.test.AppModule2_ProvideFunctionFactory Companion: AppModule2_ProvideFunctionFactory$Companion
  + com.squareup.anvil.test.AppModule2_ProvideFunctionFactory instance: AppModule2_ProvideFunctionFactory
  + com.squareup.anvil.test.SubcomponentBinding_Factory Companion: SubcomponentBinding_Factory$Companion
  + com.squareup.anvil.test.SubcomponentBinding_Factory instance: SubcomponentBinding_Factory

  - com.squareup.anvil.test.AppBinding_Factory$InstanceHolder INSTANCE: AppBinding_Factory
  - com.squareup.anvil.test.AppModule2_ProvideFunctionFactory$InstanceHolder INSTANCE: AppModule2_ProvideFunctionFactory
  - com.squareup.anvil.test.SubcomponentBinding_Factory$InstanceHolder INSTANCE: SubcomponentBinding_Factory

Factory generation: private typealias

import javax.inject.Inject

private typealias Foo = MyClassFoo

class MyClassFoo

class MyClass @Inject constructor(foo: Foo)

Error:

Cannot access 'Foo': it is private in file

Improve error message for implicit return types

Provider methods for Dagger modules require an explicit return type. The current error message isn't userfriendly at all:

e: com.squareup.anvil.compiler.AnvilCompilationException: Back-end (JVM) Internal error: Couldn't obtain type reference.
File being compiled: (41,5) in /../File.kt

Factory generation: unncecessary private imports

package com.example.myapplication

import javax.inject.Inject
import com.example.myapplication.MyClass.Result.*

class MyClass @Inject constructor() {

  private sealed class Result {
    class Some : Result()
    class Other : Result()
  }

  private fun some(): Result = Some()
  private fun other(): Result = Other()
}

Generated factory contains unnecessary(for compilation) import import com.example.myapplication.MyClass.Result.*, which fails to compile.

Feature request: support methods annotated with `@ContributesAndroidInjector`

Currently Anvil generates @Inject, @Provides factories, but not code that Dagger Android generates for @ContributesAndroidInjector. The reason why I'm asking is that a lot of our feature module contains this annotation and it would be a great improvement to be able to remove kapt from those modules.

Would you consider supporting those? (I would be happy to contribute to this in case)

Feature Request: Auto Generated Components

I'm starting to migrate a large project to Anvil and I'm converting each Application Component, from all "Feature Applications" to use @MergeComponent. It is working as expected, really good job!

However, I notice that I have to duplicate in each application module the same component definition that looks somehow like this (a little more elaborate to say):

@Singleton
@MergeComponent(
    // I'm using the scope annotation itself here.
    scope = Singleton::class
)
interface AppComponent {

    @Component.Factory
    interface Factory {
        // Simple example, might have more params for other cases...
        fun create(@BindsInstance app: Application): AppComponent
    }
}

As the definition is always the same, I wonder if Anvil could have a mechanism to "contribute with a component", or better, auto generate a given component inside a custom module so Dagger can process it from the root and therefore, we can reuse it. For example, something like:

// AppComponent lives inside a `android.library` base module
@GeneratesComponent(
    generateIn = GeneratesAppComponent::class,
    scope = Singleton::class // to allow contributions
)
interface AppComponent {

    @Component.Factory
    interface Factory {
        fun create(@BindsInstance app: Application): AppComponent
    }
}

// Application lives inside an `android.application` root module.
// To use, in any class, you would annotate with the generation marker, and Anvil would generate an `AnvilAppComponent` merge component:
@GeneratesAppComponent // simple annotation
class App: Application(), ComponentOwner<AppComponent> {

    // Basic component accessor for example.
    override val component = Dagger_AnvilAppComponent
        .factory()
        .create(app)
}

This doesn't sounds a lot but we have more than 50 Feature Apps and this would be useful to not need to duplicate these components, that are literally the same. Also it allow us to use the same interface, defined in a "bottom module" from the root app module and contribute to it, respecting the same AppComponent interface.

TL;DR: I'd like to have to create "predefined components" designed by myself - that will be mount using @ContributesTo - that will be auto generated inside the App module.

Does it make sense or it goes out of the scope of this library? 🤔

Issues with generic type parameters

After upgrading from 2.0.1 to 2.0.3 I'm seeing this issue with the generated factories:

class DelegatingWorkerFactory @Inject constructor(
    private val delegates: Map<Class<out ListenableWorker>, @JvmSuppressWildcards Provider<WorkerFactoryDelegate>>
) : WorkerFactory() { ... }

will generate

class DelegatingWorkerFactory_Factory(
  private val param0: Provider<@JvmSuppressWildcards Map<Class<ListenableWorker>,
      Provider<WorkerFactoryDelegate>>>
) : Factory<DelegatingWorkerFactory> {
  override fun get(): DelegatingWorkerFactory = newInstance(param0.get())

  companion object {
    @JvmStatic
    fun create(param0: Provider<@JvmSuppressWildcards Map<Class<ListenableWorker>,
        Provider<WorkerFactoryDelegate>>>): DelegatingWorkerFactory_Factory =
        DelegatingWorkerFactory_Factory(param0)

    @JvmStatic
    fun newInstance(param0: @JvmSuppressWildcards Map<Class<ListenableWorker>,
        Provider<WorkerFactoryDelegate>>): DelegatingWorkerFactory = DelegatingWorkerFactory(param0)
  }
}

which fails to compile with this error

Type mismatch: inferred type is Map<Class<ListenableWorker>, Provider<WorkerFactoryDelegate>> but Map<Class<out ListenableWorker>, Provider<WorkerFactoryDelegate>

Nested module compiler error

@Singleton
@MergeComponent(Singleton::class, modules = [MyComponent.Module::class])
interface MyComponent {
  
  @dagger.Module
  abstract class Module
}

produces:

Caused by: java.lang.IllegalStateException: Recursive call in a lazy value under LockBasedStorageManager@3f27bee2 (TopDownAnalyzer for JVM)
        at org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor.getAllValueArguments(LazyAnnotations.kt)
        at com.squareup.anvil.compiler.UtilsKt.getAnnotationValue(Utils.kt:123)
        at com.squareup.anvil.compiler.UtilsKt.scope(Utils.kt:126)
        at com.squareup.anvil.compiler.InterfaceMerger.addSyntheticSupertypes(InterfaceMerger.kt:36)

@JvmSuppressWildcards is placed in the wrong position in generated membersInjector

Given I have a class

class Box<T: CharSequence> @Inject constructor(val element: T)

and a second class where the first is injected:

class Fragment {
    @Inject lateinit var box: Box<String>

then I see a members injector generated as:

class Fragment_MembersInjector(
  private val param0: Provider<@JvmSuppressWildcards Box<String>>
) : MembersInjector<Fragment> {

but I think the position of @JvmSuppressWildcards is incorrect and should be instead

class Fragment_MembersInjector(
  private val param0: Provider<Box<@JvmSuppressWildcards String>>
) : MembersInjector<Fragment> {

generateDaggerFactories doesn't seem to work on windows (?)

https://github.com/ursusursus/AnvilRepro

:base contains the flag, and has removed the dagger compiler, and kapt altogether
dagger compiler and kapt is still present in :app

I think this is the correct usage, and therefore a bug in the library

> Task :base:compileDebugKotlin FAILED
e: C:\Users\ursus\AndroidStudioProjects\MyApplication9\base\build\anvil\src-gen-compileDebugKotlin\sk\foo\base\di\BaseModule_BarFactory.kt: (8, 14): Unresolved reference: annotation
e: C:\Users\ursus\AndroidStudioProjects\MyApplication9\base\build\anvil\src-gen-compileDebugKotlin\sk\foo\base\di\BaseModule_BarFactory.kt: (16, 2): Unresolved reference: Generated

In my real project I kept getting

error: incompatible types: NonExistentClass cannot be converted to Annotation` 

on a similar setup

Replaced classes must use the same scope

It's very confusing and bindings hard to debug if a contributed class replaces a class with another module. Both classes should use the same scope.

This applies for modules, component interfaces and contributed bindings.

Example Project doesn't support single instance providers

The example project demonstrates how to create custom scopes, but not now to effectively use them to ensure single instance providers.

To solve the problem, I added @Scope to SingleIn annotation and also added a random number provider to RandomModule to demonstrate how to set up single instance bindings.

I'll submit a PR with the fix and tests with this issue

Factory generation: inject nested class into nested class

Anvil: 2.0,2

File1:

package com.example.myapplication

import com.example.myapplication.foo.Foo
import javax.inject.Inject

class MyClass (bar: Bar): Foo(bar) {
  class MyClassInner @Inject constructor(bar: Bar)
}

File2

package com.example.myapplication.foo

import javax.inject.Inject

abstract class Foo constructor(bar: Bar) {
    class Bar @Inject constructor()
}

Generated MyClass_MyClassInner_Factory has incorrect imports

Factory generation: provide a way to disable it in androidTest

I have module which uses anvil Factory generation for it's main source set and an androidTest source set which wants to generate(with Dagger) TestComponen" and get the dependencies from it to test them.

Currently this leads to an error inside androidTest compilation, because Factories for @Provides functions in a TestModule generated twice(by Dagger and Anvil).

Currently I'm using a wierd workaround(which is another Anvil bug I believe):
If I mark @Provides functions in a TestModule as internal, dagger generates Factories with different names for them, and so the duplicate class exception doesn't happen.
image

Remove KotlinGradleSubplugin with Kotlin 1.4

The class KotlinGradleSubplugin is deprecated in favor of KotlinCompilerPluginSupportPlugin. When Kotlin 1.4 is out this project is migrated, then HephaestusSubplugin should be removed and the new API be used instead.

Allow using Anvil to generate Factories for subcomponents

At the moment if a module contains a @Component or @Subcomponent class and Anvil is setup to generate Dagger factories the compiler plugin throws an error, because it cannot generate this code. This check is correct for @Components, but not subcomponents. Subcomponents are generated in the same module as the root component is generated.

Usage of absolute path as input

Recently after comparing build scans produced on developer machine and CI node we noticed following:

Input 'kotlinCompile.com.squareup.anvil.compiler.src-gen-dir' is in both builds but the values are different.

This produces remote build cache misses for following tasks as paths are different:

  • org.jetbrains.kotlin.gradle.internal.KaptGenerateStubsTask
  • org.jetbrains.kotlin.gradle.internal.KaptWithoutKotlincTask
  • org.jetbrains.kotlin.gradle.tasks.KotlinCompileWithWorkers

I took a look in the source code and found this line:

that returns absolute path to the Kotlin plugin. Kotlin plugin itself sets this as task inputs via https://github.com/JetBrains/kotlin/blob/34b55dbeb3a65b39b3585a646b5a0d2b1ce4dd96/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinPlugin.kt#L1078

I am not sure what proper fix should be. Probably either using srcGenDir.relativeTo(project.buildDir) or asking Kotlin team to add relative kind type to FilesSubpluginOptions.

Configuration Caching: the anvil plugin is adding a doFirst block to the task KotlinCompileWithWorkers from KGP and this block uses the project object at execution time

The bug filed against KGP: https://youtrack.jetbrains.com/issue/KT-41783 was indeed a bug created by the Anvil plugin.

The line https://github.com/square/anvil/blob/main/gradle-plugin/src/main/java/com/squareup/anvil/plugin/AnvilPlugin.kt#L86
is adding a doFirst block to the compileTask of KGP (in our case: KotlinCompileWithWorkers , but the problem would be the same if no workers would be used).

And this block is using the project object to access source files and plugins: https://github.com/square/anvil/blob/main/gradle-plugin/src/main/java/com/squareup/anvil/plugin/CheckMixedSourceSet.kt#L25

The problem is that the project object is not availalbe during execution time with Configuration Caching.

Factory generation: incorrect import for nested classes

Class 1:

package com.example.myapplication

import com.example.myapplication.foo.MyClassFoo
import javax.inject.Inject

class MyClass @Inject constructor(d: Dependencies) : MyClassFoo(d)

Class 2:

package com.example.myapplication.foo

import javax.inject.Inject

open class MyClassFoo(d: Dependencies) {
  class Dependencies @Inject constructor()
}

Generated MyClass_Factory references Dependencies directly, without outer class name, which resuls in compilation error. Both files are otherwise valid Kotlin.

Not picking up modules from :app gradle module [Windows]

Hi, I was doing exactly what the library is doing but manually before, so I gave it a try. (So pardon the impl, and base interfaces, those shouldnt matter in this case I think)

I might be doing something wrong, however hephaestus doesnt pickup modules in the same gradle module as the mergecomponent is

This is what were the module includes manually

@AppScope
@Component(modules = [
	DataModule::class, 
	NetModule::class, 
	MiscModule::class, 
	DomainModule::class, 
	ViewModelsModule::class, 
	ChatModule::class, 
	PaymentModule::class, 
	ExtrasModule::class,
	AppModuleImpl::class 
])
interface AppComponentImpl : AppComponent, million <X>AppComponents {

On compilation compiler complains that some bindings are missing, basically the issue is the AppModuleImpl is not there

This is what the generated java with hepheastus is, as you can see the module really is missing, every other module from other gradle modules gets included okay

@dagger.Component(modules = 
{
	sk.o2.mojeo2.base.di.DataModule.class, 
	sk.o2.mojeo2.base.di.NetModule.class, 
	sk.o2.mojeo2.base.di.MiscModule.class, 
	sk.o2.mojeo2.base.di.DomainModule.class, 
	sk.o2.mojeo2.base.di.ViewModelsModule.class, 
	sk.o2.mojeo2.base.di.ChatModule.class, 
	sk.o2.payment.data.di.PaymentModule.class, 
	sk.o2.extra.data.di.ExtrasModule.class
}, dependencies = {})
@sk.o2.mojeo2.base.di.AppScope()
public abstract interface AppComponentImpl extends ...

image

@AppScope
@MergeComponent(AppScope::class)
interface AppComponentImpl : AppComponent {
@Module
@ContributesTo(AppScope::class)
object AppModuleImpl {
    ...
}

Factory generation: nested class with lowercase name

Anvil: 2.0,2

Reproducer:

class MyClass @Inject constructor(a: asg) {
  class asg @Inject constructor()
}

Exception:

e: java.lang.IllegalArgumentException: couldn't make a guess for com.example.myapplication.MyClass.asg
        at com.squareup.kotlinpoet.ClassName$Companion.bestGuess(TypeName.kt:465)
        at com.squareup.anvil.compiler.codegen.KotlinPoetKt.requireTypeName(KotlinPoet.kt:65)
        at com.squareup.anvil.compiler.codegen.KotlinPoetKt.mapToParameter(KotlinPoet.kt:110)
        at com.squareup.anvil.compiler.codegen.dagger.InjectConstructorFactoryGenerator.generateFactoryClass(InjectConstructorFactoryGenerator.kt:63)

Should display error when plugin not applied

Hi, since using this I shot my self in the foot bunch of times on this issue

for whatever reason I can include @ContributesTo annotation, and it compiles, however at runtime looking up components via the given annotated componen type fails

And then I noticed I didnt apply plugin: "com.squareup.anvil" in the given module. I apply it. Then it works

Unless plugin is applied, @ContributesTo should not be visible. I'm including all my project dependencies via implementation, so it could not have leaked as a transitive dependency? I'm not sure how plugins are handled though, or however else does the annotation get on class path for the incriminated module

Incorrect factory generated for class with type parameters

Given I have a class with a type parameter:

abstract class MyFragment<T> : Fragment() {

    @Inject
    lateinit var something: Something

Anvil will generated a factory as:

class MyFragment_MembersInjector(
        private val param0: Provider<Something>
) : MembersInjector<MyFragment> {
    override fun injectMembers(instance: MyFragment) {
        injectSomething(instance, param0.get())
    }

    companion object {
        @JvmStatic
        fun create(param0: Provider<Something>): MyFragment_MembersInjector =
            MyFragment_MembersInjector(param0)

        @JvmStatic
        @InjectedFieldSignature("com.foo.MyFragment")
        fun injectSomething(instance: MyFragment, something: Something) {
            instance.something = something
        }
    }
}

which doesn't compile. I'm no expert, but I believe adding a <*> to the generated code when there's a type parameter should be enough to fix this

Kotlin 1.4.20 API incompatibility

Using kotlin 1.4.20 current master, I see the following issue:
(with AGP 4.2 C10, gradle 6.7 last nightly / RC)

e: java.lang.NoSuchMethodError: org.jetbrains.kotlin.descriptors.EffectiveVisibilityKt.effectiveVisibility$default(Lorg/jetbrains/kotlin/descriptors/ClassDescriptor;ZILjava/lang/Object;)Lorg/jetbrains/kotlin/descriptors/EffectiveVisibility;
        at com.squareup.anvil.compiler.InterfaceMerger$addSyntheticSupertypes$classes$3.invoke(InterfaceMerger.kt:59)
        at com.squareup.anvil.compiler.InterfaceMerger$addSyntheticSupertypes$classes$3.invoke(InterfaceMerger.kt:17)

Love the project - Different name?

Love the project works great!!

I really struggle with the name, I'm guessing I'm not the only one? - I personally have to Google for Square's Github org and search for it that way... maybe don't change the name but come up with a short hand or something more SEO friendly?

Dyslexic and non-native English speakers will thank you tremendously 💚

Unable to nest the @ContributesTo annotation

@UserScope
@MergeSubcomponent(UserScope::class)
interface UserComponent {

    @ContributesTo(AppScope::class)
    interface Parent {
        val userComponentFactory: UserComponent.Factory
    }

    @Subcomponent.Factory
    interface Factory {
        fun create(): UserComponent
    }
}

This would be my usual setup for multi module, where each subcomponent declares its parent pseudo component, which contains the factory needed to create given subcomponent. I then attach this interface to the final AppComponentImp.

When moving to anvil, only slapping on the @ContributesTo(Scope:) should be necessary, however doesn't work if the Parent interface is nested inside

If I pull out the Parent interface out of UserComponent to the same level of indentation, then it works

> Task :radost:app:kaptGenerateStubsMockDebugKotlin FAILED
e: java.lang.AssertionError: Recursion detected on input: ANNOTATION_ENTRY under LockBasedStorageManager@11759153 (TopDownAnalyzer for JVM)
	at kotlin.sequences.TransformingSequence$iterator$1.next(Sequences.kt:172)
	at org.jetbrains.kotlin.descriptors.annotations.Annotations$DefaultImpls.findAnnotation(Annotations.kt:106)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotations.findAnnotation(LazyAnnotations.kt:51)
	at com.squareup.anvil.compiler.UtilsKt.findAnnotation(Utils.kt:46)
	at com.squareup.anvil.compiler.UtilsKt.findAnnotation$default(Utils.kt:41)
	at com.squareup.anvil.compiler.ClassScanner$findContributedClasses$1$5.invoke(ClassScanner.kt:37)
	at com.squareup.anvil.compiler.ClassScanner$findContributedClasses$1$5.invoke(ClassScanner.kt:11)
	at kotlin.sequences.FilteringSequence$iterator$1.calcNext(Sequences.kt:133)
	at kotlin.sequences.FilteringSequence$iterator$1.hasNext(Sequences.kt:156)
	at kotlin.sequences.SequencesKt___SequencesKt.toCollection(_Sequences.kt:722)
	at kotlin.sequences.SequencesKt___SequencesKt.toMutableList(_Sequences.kt:752)
	at kotlin.sequences.SequencesKt___SequencesKt.toList(_Sequences.kt:743)
	at com.squareup.anvil.compiler.ClassScanner.findContributedClasses(ClassScanner.kt:38)
	at com.squareup.anvil.compiler.ClassScanner.findContributedClasses$default(ClassScanner.kt:21)
	at com.squareup.anvil.compiler.InterfaceMerger.addSyntheticSupertypes(InterfaceMerger.kt:45)
	at org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension$Companion$getInstance$1.addSyntheticSupertypes(SyntheticResolveExtension.kt:75)
	at org.jetbrains.kotlin.resolve.DescriptorResolver.resolveSupertypes(DescriptorResolver.java:157)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor.computeSupertypes(LazyClassDescriptor.java:721)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor$LazyClassTypeConstructor.computeSupertypes(LazyClassDescriptor.java:624)
	at org.jetbrains.kotlin.types.AbstractTypeConstructor$supertypes$1.invoke(AbstractTypeConstructor.kt:80)
	at org.jetbrains.kotlin.types.AbstractTypeConstructor$supertypes$1.invoke(AbstractTypeConstructor.kt:26)
	at org.jetbrains.kotlin.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:355)
	at org.jetbrains.kotlin.storage.LockBasedStorageManager$LockBasedLazyValueWithPostCompute.invoke(LockBasedStorageManager.java:428)
	at org.jetbrains.kotlin.storage.LockBasedStorageManager$LockBasedNotNullLazyValueWithPostCompute.invoke(LockBasedStorageManager.java:459)
	at org.jetbrains.kotlin.types.AbstractTypeConstructor.getSupertypes(AbstractTypeConstructor.kt:27)
	at org.jetbrains.kotlin.types.AbstractTypeConstructor.getSupertypes(AbstractTypeConstructor.kt:26)
	at org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt.getSuperClassNotAny(DescriptorUtils.kt:141)
	at org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt.getAllSuperclassesWithoutAny(DescriptorUtils.kt:356)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.ClassResolutionScopesSupport$inheritanceScopeWithoutMe$1.invoke(ClassResolutionScopesSupport.kt:52)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.ClassResolutionScopesSupport$inheritanceScopeWithoutMe$1.invoke(ClassResolutionScopesSupport.kt:31)
	at org.jetbrains.kotlin.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:355)
	at org.jetbrains.kotlin.storage.LockBasedStorageManager$LockBasedNotNullLazyValue.invoke(LockBasedStorageManager.java:474)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.ClassResolutionScopesSupport$inheritanceScopeWithMe$1.invoke(ClassResolutionScopesSupport.kt:58)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.ClassResolutionScopesSupport$inheritanceScopeWithMe$1.invoke(ClassResolutionScopesSupport.kt:31)
	at org.jetbrains.kotlin.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:355)
	at org.jetbrains.kotlin.storage.LockBasedStorageManager$LockBasedNotNullLazyValue.invoke(LockBasedStorageManager.java:474)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.ClassResolutionScopesSupport$scopeForStaticMemberDeclarationResolution$1.invoke(ClassResolutionScopesSupport.kt:82)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.ClassResolutionScopesSupport$scopeForStaticMemberDeclarationResolution$1.invoke(ClassResolutionScopesSupport.kt:31)
	at org.jetbrains.kotlin.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:355)
	at org.jetbrains.kotlin.storage.LockBasedStorageManager$LockBasedNotNullLazyValue.invoke(LockBasedStorageManager.java:474)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor.getScopeForStaticMemberDeclarationResolution(LazyClassDescriptor.java:373)
	at org.jetbrains.kotlin.resolve.lazy.DeclarationScopeProviderImpl.getResolutionScopeForDeclaration(DeclarationScopeProviderImpl.java:77)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor.getOuterScope(LazyClassDescriptor.java:343)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor$1.getScope(LazyClassDescriptor.java:180)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor.<init>(LazyAnnotations.kt:86)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotations$annotation$1.invoke(LazyAnnotations.kt:58)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotations$annotation$1.invoke(LazyAnnotations.kt:51)
	at org.jetbrains.kotlin.storage.LockBasedStorageManager$MapBasedMemoizedFunction.invoke(LockBasedStorageManager.java:512)
	at org.jetbrains.kotlin.storage.LockBasedStorageManager$MapBasedMemoizedFunctionToNotNull.invoke(LockBasedStorageManager.java:587)
	at kotlin.sequences.TransformingSequence$iterator$1.next(Sequences.kt:172)
	at org.jetbrains.kotlin.descriptors.annotations.Annotations$DefaultImpls.findAnnotation(Annotations.kt:106)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotations.findAnnotation(LazyAnnotations.kt:51)
	at com.squareup.anvil.compiler.UtilsKt.findAnnotation(Utils.kt:46)
	at com.squareup.anvil.compiler.UtilsKt.findAnnotation$default(Utils.kt:41)
	at com.squareup.anvil.compiler.ClassScanner$findContributedClasses$1$5.invoke(ClassScanner.kt:37)
	at com.squareup.anvil.compiler.ClassScanner$findContributedClasses$1$5.invoke(ClassScanner.kt:11)
	at kotlin.sequences.FilteringSequence$iterator$1.calcNext(Sequences.kt:133)
	at kotlin.sequences.FilteringSequence$iterator$1.hasNext(Sequences.kt:156)
	at kotlin.sequences.SequencesKt___SequencesKt.toCollection(_Sequences.kt:722)
	at kotlin.sequences.SequencesKt___SequencesKt.toMutableList(_Sequences.kt:752)
	at kotlin.sequences.SequencesKt___SequencesKt.toList(_Sequences.kt:743)
	at com.squareup.anvil.compiler.ClassScanner.findContributedClasses(ClassScanner.kt:38)
	at com.squareup.anvil.compiler.ClassScanner.findContributedClasses$default(ClassScanner.kt:21)
	at com.squareup.anvil.compiler.InterfaceMerger.addSyntheticSupertypes(InterfaceMerger.kt:45)
	at org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension$Companion$getInstance$1.addSyntheticSupertypes(SyntheticResolveExtension.kt:75)
	at org.jetbrains.kotlin.resolve.DescriptorResolver.resolveSupertypes(DescriptorResolver.java:157)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor.computeSupertypes(LazyClassDescriptor.java:721)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor$LazyClassTypeConstructor.computeSupertypes(LazyClassDescriptor.java:624)
	at org.jetbrains.kotlin.types.AbstractTypeConstructor$supertypes$1.invoke(AbstractTypeConstructor.kt:80)
	at org.jetbrains.kotlin.types.AbstractTypeConstructor$supertypes$1.invoke(AbstractTypeConstructor.kt:26)
	at org.jetbrains.kotlin.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:355)
	at org.jetbrains.kotlin.storage.LockBasedStorageManager$LockBasedLazyValueWithPostCompute.invoke(LockBasedStorageManager.java:428)
	at org.jetbrains.kotlin.storage.LockBasedStorageManager$LockBasedNotNullLazyValueWithPostCompute.invoke(LockBasedStorageManager.java:459)
	at org.jetbrains.kotlin.types.AbstractTypeConstructor.getSupertypes(AbstractTypeConstructor.kt:27)
	at org.jetbrains.kotlin.types.AbstractTypeConstructor.getSupertypes(AbstractTypeConstructor.kt:26)
	at org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt.getSuperClassNotAny(DescriptorUtils.kt:141)
	at org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt.getAllSuperclassesWithoutAny(DescriptorUtils.kt:356)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.ClassResolutionScopesSupport$inheritanceScopeWithoutMe$1.invoke(ClassResolutionScopesSupport.kt:52)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.ClassResolutionScopesSupport$inheritanceScopeWithoutMe$1.invoke(ClassResolutionScopesSupport.kt:31)
	at org.jetbrains.kotlin.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:355)
	at org.jetbrains.kotlin.storage.LockBasedStorageManager$LockBasedNotNullLazyValue.invoke(LockBasedStorageManager.java:474)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.ClassResolutionScopesSupport$inheritanceScopeWithMe$1.invoke(ClassResolutionScopesSupport.kt:58)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.ClassResolutionScopesSupport$inheritanceScopeWithMe$1.invoke(ClassResolutionScopesSupport.kt:31)
	at org.jetbrains.kotlin.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:355)
	at org.jetbrains.kotlin.storage.LockBasedStorageManager$LockBasedNotNullLazyValue.invoke(LockBasedStorageManager.java:474)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.ClassResolutionScopesSupport$scopeForMemberDeclarationResolution$1.invoke(ClassResolutionScopesSupport.kt:67)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.ClassResolutionScopesSupport$scopeForMemberDeclarationResolution$1.invoke(ClassResolutionScopesSupport.kt:31)
	at org.jetbrains.kotlin.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:355)
	at org.jetbrains.kotlin.storage.LockBasedStorageManager$LockBasedNotNullLazyValue.invoke(LockBasedStorageManager.java:474)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor.getScopeForMemberDeclarationResolution(LazyClassDescriptor.java:367)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor.resolveMemberHeaders(LazyClassDescriptor.java:594)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor.doForceResolveAllContents(LazyClassDescriptor.java:565)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor.lambda$new$4(LazyClassDescriptor.java:216)
	at org.jetbrains.kotlin.storage.LockBasedStorageManager$LockBasedLazyValue.invoke(LockBasedStorageManager.java:355)
	at org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor.forceResolveAllContents(LazyClassDescriptor.java:561)
	at org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil.doForceResolveAllContents(ForceResolveUtil.java:78)
	at org.jetbrains.kotlin.resolve.lazy.ForceResolveUtil.forceResolveAllContents(ForceResolveUtil.java:42)
	at org.jetbrains.kotlin.resolve.jvm.extensions.PartialAnalysisHandlerExtension$doAnalysis$1.invoke(PartialAnalysisHandlerExtension.kt:68)
	at org.jetbrains.kotlin.resolve.jvm.extensions.PartialAnalysisHandlerExtension$doAnalysis$1.invoke(PartialAnalysisHandlerExtension.kt:34)
	at org.jetbrains.kotlin.resolve.jvm.extensions.PartialAnalysisHandlerExtension.doForEachDeclaration(PartialAnalysisHandlerExtension.kt:121)
	at org.jetbrains.kotlin.resolve.jvm.extensions.PartialAnalysisHandlerExtension.doForEachDeclaration(PartialAnalysisHandlerExtension.kt:135)
	at org.jetbrains.kotlin.resolve.jvm.extensions.PartialAnalysisHandlerExtension.doAnalysis(PartialAnalysisHandlerExtension.kt:63)
	at org.jetbrains.kotlin.kapt3.AbstractKapt3Extension.doAnalysis(Kapt3Extension.kt:147)
	at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration(TopDownAnalyzerFacadeForJVM.kt:105)
	at org.jetbrains.kotlin.cli.jvm.compiler.TopDownAnalyzerFacadeForJVM.analyzeFilesWithJavaIntegration$default(TopDownAnalyzerFacadeForJVM.kt:82)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:554)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler$analyze$1.invoke(KotlinToJVMBytecodeCompiler.kt:81)
	at org.jetbrains.kotlin.cli.common.messages.AnalyzerWithCompilerReport.analyzeAndReport(AnalyzerWithCompilerReport.kt:107)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.analyze(KotlinToJVMBytecodeCompiler.kt:545)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.repeatAnalysisIfNeeded(KotlinToJVMBytecodeCompiler.kt:483)
	at org.jetbrains.kotlin.cli.jvm.compiler.KotlinToJVMBytecodeCompiler.compileModules$cli(KotlinToJVMBytecodeCompiler.kt:176)
	at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:163)
	at org.jetbrains.kotlin.cli.jvm.K2JVMCompiler.doExecute(K2JVMCompiler.kt:51)
	at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:85)
	at org.jetbrains.kotlin.cli.common.CLICompiler.execImpl(CLICompiler.kt:43)
	at org.jetbrains.kotlin.cli.common.CLITool.exec(CLITool.kt:105)
	at org.jetbrains.kotlin.daemon.CompileServiceImpl.compile(CompileServiceImpl.kt:1558)
	at sun.reflect.GeneratedMethodAccessor98.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:357)
	at sun.rmi.transport.Transport$1.run(Transport.java:200)
	at sun.rmi.transport.Transport$1.run(Transport.java:197)
	at java.security.AccessController.doPrivileged(Native Method)
	at sun.rmi.transport.Transport.serviceCall(Transport.java:196)
	at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:573)
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:834)
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:688)
	at java.security.AccessController.doPrivileged(Native Method)
	at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:687)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)


e: java.lang.AssertionError: Recursion detected on input: ANNOTATION_ENTRY under LockBasedStorageManager@11759153 (TopDownAnalyzer for JVM)

Add a sample comparison of code written with Anvil and without it

Hi. Thank you for the great tool.

When I was discovering it I faced that is hard to compare samples with Anvil with something with Dagger but in my head.
In other words, reading README and looking at samples more difficult than simple Before and After pattern

If you consider useful those examples I could make a contribution or help you with it

Component Factories and Builders

I'm wondering if there should be documentation around how anvil works with @Component.Factory and @Component.Builder.

Without anvil, you'll get the following compiler error from dagger if you attempt to use either annotation on an interface whose containing interface isn't annotated with @Component:

@Component.Factory types must be nested within a @Component

So it may seem a bit odd when using anvil since the compiler plugin adds the @Component annotation, making it perfectly legal to do something dagger explicitly guards against.

Thoughts?

"Only Android and Java modules are supported for now" in new Android Project

When adding plugin to newly created Android project, I get the following exception:

plugins {
    id 'com.squareup.hephaestus' version "1.0.0"
}
FAILURE: Build failed with an exception.

* Where:
Build file '/Users/.../TestApp/app/build.gradle' line: 3

* What went wrong:
An exception occurred applying plugin request [id: 'com.squareup.hephaestus', version: '1.0.0']
> Failed to apply plugin [id 'com.squareup.hephaestus']
   > Only Android and Java modules are supported for now.

* Try:
Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Exception is:
org.gradle.api.plugins.InvalidPluginException: An exception occurred applying plugin request [id: 'com.squareup.hephaestus', version: '1.0.0']
	at org.gradle.plugin.use.internal.DefaultPluginRequestApplicator.exceptionOccurred(DefaultPluginRequestApplicator.java:253)
	at org.gradle.plugin.use.internal.DefaultPluginRequestApplicator.applyPlugin(DefaultPluginRequestApplicator.java:235)
	at org.gradle.plugin.use.internal.DefaultPluginRequestApplicator.applyPlugins(DefaultPluginRequestApplicator.java:151)
	at org.gradle.configuration.DefaultScriptPluginFactory$ScriptPluginImpl.apply(DefaultScriptPluginFactory.java:216)
	at org.gradle.configuration.BuildOperationScriptPlugin$1$1.run(BuildOperationScriptPlugin.java:69)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92)
	at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
	at org.gradle.configuration.BuildOperationScriptPlugin$1.execute(BuildOperationScriptPlugin.java:66)
	at org.gradle.configuration.BuildOperationScriptPlugin$1.execute(BuildOperationScriptPlugin.java:63)
	at org.gradle.configuration.internal.DefaultUserCodeApplicationContext.apply(DefaultUserCodeApplicationContext.java:49)
	at org.gradle.configuration.BuildOperationScriptPlugin.apply(BuildOperationScriptPlugin.java:63)
	at org.gradle.configuration.project.BuildScriptProcessor$1.run(BuildScriptProcessor.java:45)
	at org.gradle.internal.Factories$1.create(Factories.java:26)
	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withMutableState(DefaultProjectStateRegistry.java:212)
	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withMutableState(DefaultProjectStateRegistry.java:193)
	at org.gradle.configuration.project.BuildScriptProcessor.execute(BuildScriptProcessor.java:42)
	at org.gradle.configuration.project.BuildScriptProcessor.execute(BuildScriptProcessor.java:26)
	at org.gradle.configuration.project.ConfigureActionsProjectEvaluator.evaluate(ConfigureActionsProjectEvaluator.java:35)
	at org.gradle.configuration.project.LifecycleProjectEvaluator$EvaluateProject$1.run(LifecycleProjectEvaluator.java:107)
	at org.gradle.internal.Factories$1.create(Factories.java:26)
	at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:189)
	at org.gradle.internal.work.StopShieldingWorkerLeaseService.withLocks(StopShieldingWorkerLeaseService.java:40)
	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withProjectLock(DefaultProjectStateRegistry.java:238)
	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withMutableState(DefaultProjectStateRegistry.java:232)
	at org.gradle.api.internal.project.DefaultProjectStateRegistry$ProjectStateImpl.withMutableState(DefaultProjectStateRegistry.java:193)
	at org.gradle.configuration.project.LifecycleProjectEvaluator$EvaluateProject.run(LifecycleProjectEvaluator.java:96)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92)
	at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
	at org.gradle.configuration.project.LifecycleProjectEvaluator.evaluate(LifecycleProjectEvaluator.java:68)
	at org.gradle.api.internal.project.DefaultProject.evaluate(DefaultProject.java:699)
	at org.gradle.api.internal.project.DefaultProject.evaluate(DefaultProject.java:142)
	at org.gradle.execution.TaskPathProjectEvaluator.configure(TaskPathProjectEvaluator.java:36)
	at org.gradle.execution.TaskPathProjectEvaluator.configureHierarchy(TaskPathProjectEvaluator.java:64)
	at org.gradle.configuration.DefaultProjectsPreparer.prepareProjects(DefaultProjectsPreparer.java:61)
	at org.gradle.configuration.BuildOperatingFiringProjectsPreparer$ConfigureBuild.run(BuildOperatingFiringProjectsPreparer.java:52)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92)
	at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
	at org.gradle.configuration.BuildOperatingFiringProjectsPreparer.prepareProjects(BuildOperatingFiringProjectsPreparer.java:40)
	at org.gradle.initialization.DefaultGradleLauncher.prepareProjects(DefaultGradleLauncher.java:207)
	at org.gradle.initialization.DefaultGradleLauncher.doClassicBuildStages(DefaultGradleLauncher.java:145)
	at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:130)
	at org.gradle.initialization.DefaultGradleLauncher.getConfiguredBuild(DefaultGradleLauncher.java:104)
	at org.gradle.internal.invocation.GradleBuildController$2.execute(GradleBuildController.java:70)
	at org.gradle.internal.invocation.GradleBuildController$2.execute(GradleBuildController.java:67)
	at org.gradle.internal.invocation.GradleBuildController$3.create(GradleBuildController.java:85)
	at org.gradle.internal.invocation.GradleBuildController$3.create(GradleBuildController.java:78)
	at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:189)
	at org.gradle.internal.work.StopShieldingWorkerLeaseService.withLocks(StopShieldingWorkerLeaseService.java:40)
	at org.gradle.internal.invocation.GradleBuildController.doBuild(GradleBuildController.java:78)
	at org.gradle.internal.invocation.GradleBuildController.configure(GradleBuildController.java:67)
	at org.gradle.tooling.internal.provider.runner.ClientProvidedPhasedActionRunner.run(ClientProvidedPhasedActionRunner.java:62)
	at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
	at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
	at org.gradle.launcher.exec.BuildOutcomeReportingBuildActionRunner.run(BuildOutcomeReportingBuildActionRunner.java:63)
	at org.gradle.tooling.internal.provider.ValidatingBuildActionRunner.run(ValidatingBuildActionRunner.java:32)
	at org.gradle.launcher.exec.BuildCompletionNotifyingBuildActionRunner.run(BuildCompletionNotifyingBuildActionRunner.java:39)
	at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner$3.call(RunAsBuildOperationBuildActionRunner.java:51)
	at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner$3.call(RunAsBuildOperationBuildActionRunner.java:45)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:416)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:406)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:102)
	at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36)
	at org.gradle.launcher.exec.RunAsBuildOperationBuildActionRunner.run(RunAsBuildOperationBuildActionRunner.java:45)
	at org.gradle.launcher.exec.InProcessBuildActionExecuter$1.transform(InProcessBuildActionExecuter.java:50)
	at org.gradle.launcher.exec.InProcessBuildActionExecuter$1.transform(InProcessBuildActionExecuter.java:47)
	at org.gradle.composite.internal.DefaultRootBuildState.run(DefaultRootBuildState.java:80)
	at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:47)
	at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:31)
	at org.gradle.launcher.exec.BuildTreeScopeBuildActionExecuter.execute(BuildTreeScopeBuildActionExecuter.java:42)
	at org.gradle.launcher.exec.BuildTreeScopeBuildActionExecuter.execute(BuildTreeScopeBuildActionExecuter.java:28)
	at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:78)
	at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:52)
	at org.gradle.tooling.internal.provider.SubscribableBuildActionExecuter.execute(SubscribableBuildActionExecuter.java:60)
	at org.gradle.tooling.internal.provider.SubscribableBuildActionExecuter.execute(SubscribableBuildActionExecuter.java:38)
	at org.gradle.tooling.internal.provider.SessionScopeBuildActionExecuter.execute(SessionScopeBuildActionExecuter.java:68)
	at org.gradle.tooling.internal.provider.SessionScopeBuildActionExecuter.execute(SessionScopeBuildActionExecuter.java:38)
	at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:37)
	at org.gradle.tooling.internal.provider.GradleThreadBuildActionExecuter.execute(GradleThreadBuildActionExecuter.java:26)
	at org.gradle.tooling.internal.provider.ParallelismConfigurationBuildActionExecuter.execute(ParallelismConfigurationBuildActionExecuter.java:43)
	at org.gradle.tooling.internal.provider.ParallelismConfigurationBuildActionExecuter.execute(ParallelismConfigurationBuildActionExecuter.java:29)
	at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:60)
	at org.gradle.tooling.internal.provider.StartParamsValidatingActionExecuter.execute(StartParamsValidatingActionExecuter.java:32)
	at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:55)
	at org.gradle.tooling.internal.provider.SessionFailureReportingActionExecuter.execute(SessionFailureReportingActionExecuter.java:41)
	at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:48)
	at org.gradle.tooling.internal.provider.SetupLoggingActionExecuter.execute(SetupLoggingActionExecuter.java:32)
	at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:68)
	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:39)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:27)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:35)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.create(ForwardClientInput.java:78)
	at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.create(ForwardClientInput.java:75)
	at org.gradle.util.Swapper.swap(Swapper.java:38)
	at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:75)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.LogAndCheckHealth.execute(LogAndCheckHealth.java:55)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:63)
	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:82)
	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:37)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:104)
	at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:52)
	at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:297)
	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
	at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
	at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
Caused by: org.gradle.api.internal.plugins.PluginApplicationException: Failed to apply plugin [id 'com.squareup.hephaestus']
	at org.gradle.api.internal.plugins.DefaultPluginManager.doApply(DefaultPluginManager.java:167)
	at org.gradle.api.internal.plugins.DefaultPluginManager.apply(DefaultPluginManager.java:136)
	at org.gradle.plugin.use.internal.DefaultPluginRequestApplicator$3.run(DefaultPluginRequestApplicator.java:155)
	at org.gradle.plugin.use.internal.DefaultPluginRequestApplicator.applyPlugin(DefaultPluginRequestApplicator.java:231)
	... 129 more
Caused by: org.gradle.api.GradleException: Only Android and Java modules are supported for now.
	at com.squareup.hephaestus.plugin.HephaestusPlugin.apply(HephaestusPlugin.kt:22)
	at com.squareup.hephaestus.plugin.HephaestusPlugin.apply(HephaestusPlugin.kt:18)
	at org.gradle.api.internal.plugins.ImperativeOnlyPluginTarget.applyImperative(ImperativeOnlyPluginTarget.java:43)
	at org.gradle.api.internal.plugins.RuleBasedPluginTarget.applyImperative(RuleBasedPluginTarget.java:51)
	at org.gradle.api.internal.plugins.DefaultPluginManager.addPlugin(DefaultPluginManager.java:181)
	at org.gradle.api.internal.plugins.DefaultPluginManager.access$300(DefaultPluginManager.java:51)
	at org.gradle.api.internal.plugins.DefaultPluginManager$AddPluginBuildOperation.run(DefaultPluginManager.java:276)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
	at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92)
	at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
	at org.gradle.api.internal.plugins.DefaultPluginManager$2.execute(DefaultPluginManager.java:159)
	at org.gradle.api.internal.plugins.DefaultPluginManager$2.execute(DefaultPluginManager.java:156)
	at org.gradle.configuration.internal.DefaultUserCodeApplicationContext.apply(DefaultUserCodeApplicationContext.java:49)
	at org.gradle.api.internal.plugins.DefaultPluginManager.doApply(DefaultPluginManager.java:156)
	... 132 more


* Get more help at https://help.gradle.org

CONFIGURE FAILED in 204ms

Factory generation: overloaded Provides functions

If a module has multiple overloaded @Provides functions with the same, only one Factory gets generated.
Dagger gives an error Cannot have more than one binding method with the same name in a single module for such modules.

@Module
object MyModule {
    @Provides fun s(): String = ""
    @Provides fun s(s: String): Int = 1
}

Anvil treats named imports as normal imports

Assume we have

import java.util.Date as Instant
import com.squareup.test.*

where the package com.squareup.test contains a Date class, then Anvil will resolve the class as java.util.Date. This is because named imports are not filtered out when we try to resolve a class

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.