Add support for JUnit tests with Hilt Plugin
Android transforms are not applied for JUnit tests which breaks when using
the Hilt Plugin and its transform. This CL adds a new custom test transform
task just for tests so that transformed classes are used in Android JUnit tests.
The task does not transform test sources, therefore temporarily allow specifying
a base class in @AndroidEntryPoint that will be used even if the superclass
validation option is given to the processor. This helps us still have test-only
Android Entry Points.
Closes https://github.com/google/dagger/pull/1811
RELNOTES=n/a
-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=309143731
diff --git a/.gitignore b/.gitignore
index 59acf87..cea18de 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,3 +33,5 @@
/bazel-*
*.pyc
+
+.gradle
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/build.gradle b/java/dagger/hilt/android/example/gradle/simple/app/build.gradle
index 932be76..16b9519 100644
--- a/java/dagger/hilt/android/example/gradle/simple/app/build.gradle
+++ b/java/dagger/hilt/android/example/gradle/simple/app/build.gradle
@@ -15,8 +15,7 @@
*/
apply plugin: 'com.android.application'
-// TODO(bcorso): Reenable this plugin once it works with Robolectric tests.
-// apply plugin: 'dagger.hilt.android.plugin'
+apply plugin: 'dagger.hilt.android.plugin'
android {
compileSdkVersion 29
@@ -33,7 +32,13 @@
sourceCompatibility 1.8
targetCompatibility 1.8
}
- testOptions.unitTests.includeAndroidResources = true
+ testOptions {
+ unitTests.includeAndroidResources = true
+ }
+}
+
+hilt {
+ enableTransformForLocalTests = true
}
dependencies {
@@ -46,11 +51,12 @@
testImplementation 'com.google.truth:truth:1.0.1'
testImplementation 'junit:junit:4.13'
- testImplementation 'org.robolectric:robolectric:4.3'
+ testImplementation 'org.robolectric:robolectric:4.3.1'
+ testImplementation 'androidx.core:core:1.2.0'
testImplementation 'androidx.test.ext:junit:1.1.1'
testImplementation 'androidx.test:runner:1.2.0'
// TODO(bcorso): This multidex dep shouldn't be required -- it's a dep for the generated code.
- testImplementation 'androidx.multidex:multidex:2.0.0'
+ testImplementation 'androidx.multidex:multidex:2.0.1'
testImplementation 'com.google.dagger:hilt-android-testing:LOCAL-SNAPSHOT'
testAnnotationProcessor 'com.google.dagger:dagger-compiler:LOCAL-SNAPSHOT'
testAnnotationProcessor 'com.google.dagger:hilt-android-compiler:LOCAL-SNAPSHOT'
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SettingsActivity.java b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SettingsActivity.java
index b7df457..f508e48 100644
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SettingsActivity.java
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SettingsActivity.java
@@ -23,8 +23,8 @@
import javax.inject.Inject;
/** The settings activity of the application. */
-@AndroidEntryPoint(AppCompatActivity.class)
-public class SettingsActivity extends Hilt_SettingsActivity {
+@AndroidEntryPoint
+public class SettingsActivity extends AppCompatActivity {
private static final String TAG = SettingsActivity.class.getSimpleName();
@Inject SettingsGreeter greeter;
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleActivity.java b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleActivity.java
index 8ad4c0d..70e2e57 100644
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleActivity.java
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleActivity.java
@@ -26,8 +26,8 @@
import javax.inject.Inject;
/** The main activity of the application. */
-@AndroidEntryPoint(AppCompatActivity.class)
-public class SimpleActivity extends Hilt_SimpleActivity {
+@AndroidEntryPoint
+public class SimpleActivity extends AppCompatActivity {
private static final String TAG = SimpleActivity.class.getSimpleName();
@Inject SimpleGreeter greeter;
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleApplication.java b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleApplication.java
index aa04fb5..b691401 100644
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleApplication.java
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/main/java/dagger/hilt/android/example/gradle/simple/SimpleApplication.java
@@ -26,8 +26,8 @@
* utilities in {@code Hilt} in Android.
*/
@GenerateComponents
-@AndroidEntryPoint(Application.class)
-public class SimpleApplication extends Hilt_SimpleApplication {
+@AndroidEntryPoint
+public class SimpleApplication extends Application {
// Shows that we can inject ApplicationComponent bindings into an application.
@Inject @Model String model;
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/Injection1Test.java b/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/Injection1Test.java
index c87f726..c07e00f 100644
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/Injection1Test.java
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/Injection1Test.java
@@ -71,6 +71,7 @@
}
/** Test activity used to test activity injection */
+ // TODO(user): Update to extend non-gen code when test sources are also transformed.
@AndroidEntryPoint(AppCompatActivity.class)
public static final class TestActivity extends Hilt_Injection1Test_TestActivity {
@Inject @Named(ACTIVITY_QUALIFIER) String activityValue;
@@ -94,4 +95,14 @@
scenario.onActivity(activity -> assertThat(activity.activityValue).isEqualTo(ACTIVITY_VALUE));
}
}
+
+ @Test
+ public void testSuperClassTransformation() {
+ try (ActivityScenario<TestActivity> scenario = ActivityScenario.launch(TestActivity.class)) {
+ scenario.onActivity(
+ activity ->
+ assertThat(activity.getClass().getSuperclass().getSimpleName())
+ .isEqualTo("Hilt_Injection1Test_TestActivity"));
+ }
+ }
}
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/Injection2Test.java b/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/Injection2Test.java
index 4b158d5..d43dcc0 100644
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/Injection2Test.java
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/Injection2Test.java
@@ -71,6 +71,7 @@
}
/** Test activity used to test activity injection */
+ // TODO(user): Update to extend non-gen code when test sources are also transformed.
@AndroidEntryPoint(AppCompatActivity.class)
public static final class TestActivity extends Hilt_Injection2Test_TestActivity {
@Inject @Named(ACTIVITY_QUALIFIER) String activityValue;
diff --git a/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/SettingsActivityTest.java b/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/SettingsActivityTest.java
index 6a23a96..5c7a146 100644
--- a/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/SettingsActivityTest.java
+++ b/java/dagger/hilt/android/example/gradle/simple/app/src/test/java/dagger/hilt/android/example/gradle/simple/SettingsActivityTest.java
@@ -63,4 +63,15 @@
.isEqualTo("ProdUser, you are on build FakeModel."));
}
}
+
+ @Test
+ public void testSuperClassTransformation() {
+ try (ActivityScenario<SettingsActivity> scenario =
+ ActivityScenario.launch(SettingsActivity.class)) {
+ scenario.onActivity(
+ activity ->
+ assertThat(activity.getClass().getSuperclass().getSimpleName())
+ .isEqualTo("Hilt_SettingsActivity"));
+ }
+ }
}
diff --git a/java/dagger/hilt/android/example/gradle/simple/build.gradle b/java/dagger/hilt/android/example/gradle/simple/build.gradle
index e473e22..cb6d02a 100644
--- a/java/dagger/hilt/android/example/gradle/simple/build.gradle
+++ b/java/dagger/hilt/android/example/gradle/simple/build.gradle
@@ -17,6 +17,7 @@
buildscript {
ext {
kotlin_version = '1.3.61'
+ agp_version = System.getenv('AGP_VERSION') ?: "3.6.3"
}
repositories {
google()
@@ -24,7 +25,7 @@
mavenLocal()
}
dependencies {
- classpath 'com.android.tools.build:gradle:3.5.3'
+ classpath "com.android.tools.build:gradle:$agp_version"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.google.dagger:hilt-android-gradle-plugin:LOCAL-SNAPSHOT'
}
diff --git a/java/dagger/hilt/android/example/gradle/simple/feature/build.gradle b/java/dagger/hilt/android/example/gradle/simple/feature/build.gradle
index 208882c..56fcdd8 100644
--- a/java/dagger/hilt/android/example/gradle/simple/feature/build.gradle
+++ b/java/dagger/hilt/android/example/gradle/simple/feature/build.gradle
@@ -17,8 +17,7 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
-// TODO(bcorso): Reenable this plugin once it works with Robolectric tests.
-// apply plugin: 'dagger.hilt.android.plugin'
+apply plugin: 'dagger.hilt.android.plugin'
android {
compileSdkVersion 29
@@ -39,6 +38,10 @@
correctErrorTypes true
}
+hilt {
+ enableTransformForLocalTests = true
+}
+
dependencies {
// This is api instead of implementation since Kotlin modules here consumed
// by the app need to expose @kotlin.Metadata
diff --git a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureActivity.kt b/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureActivity.kt
index 55e004a..03b9ae9 100644
--- a/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureActivity.kt
+++ b/java/dagger/hilt/android/example/gradle/simple/feature/src/main/java/dagger/hilt/android/example/gradle/simple/feature/FeatureActivity.kt
@@ -23,8 +23,8 @@
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
-@AndroidEntryPoint(AppCompatActivity::class)
-class FeatureActivity : Hilt_FeatureActivity() {
+@AndroidEntryPoint
+class FeatureActivity : AppCompatActivity() {
@Inject lateinit var counter: FeatureCounter
override fun onCreate(savedInstanceState: Bundle?) {
diff --git a/java/dagger/hilt/android/example/gradle/simple/gradle.properties b/java/dagger/hilt/android/example/gradle/simple/gradle.properties
index 66e648b..646c51b 100644
--- a/java/dagger/hilt/android/example/gradle/simple/gradle.properties
+++ b/java/dagger/hilt/android/example/gradle/simple/gradle.properties
@@ -1,3 +1,2 @@
android.useAndroidX=true
android.enableJetifier=true
-android.enableUnitTestBinaryResources=true
\ No newline at end of file
diff --git a/java/dagger/hilt/android/example/gradle/simple/gradle/wrapper/gradle-wrapper.properties b/java/dagger/hilt/android/example/gradle/simple/gradle/wrapper/gradle-wrapper.properties
index f4d7b2b..a4b4429 100644
--- a/java/dagger/hilt/android/example/gradle/simple/gradle/wrapper/gradle-wrapper.properties
+++ b/java/dagger/hilt/android/example/gradle/simple/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/BUILD b/java/dagger/hilt/android/example/gradle/simpleKotlin/BUILD
new file mode 100644
index 0000000..3671282
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/BUILD
@@ -0,0 +1,18 @@
+# Copyright (C) 2020 The Dagger Authors.
+#
+# 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.
+#
+# Description:
+# A skeletal Kotlin application that demonstrates wiring for an injected Application and Activity.
+
+package(default_visibility = ["//:src"])
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/build.gradle b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/build.gradle
new file mode 100644
index 0000000..52cce0d
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/build.gradle
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * 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.
+ */
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'dagger.hilt.android.plugin'
+apply plugin: 'kotlin-kapt'
+
+android {
+ compileSdkVersion 29
+ buildToolsVersion "29.0.3"
+
+ defaultConfig {
+ applicationId "dagger.hilt.android.example.gradle.simpleKotlin"
+ minSdkVersion 21
+ targetSdkVersion 29
+ versionCode 1
+ versionName "1.0"
+ }
+ compileOptions {
+ sourceCompatibility 1.8
+ targetCompatibility 1.8
+ }
+ testOptions {
+ unitTests.includeAndroidResources = true
+ }
+}
+
+hilt {
+ enableTransformForLocalTests = true
+}
+
+dependencies {
+ implementation fileTree(dir: "libs", include: ["*.jar"])
+ implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
+ implementation 'androidx.appcompat:appcompat:1.1.0'
+
+ implementation 'com.google.dagger:dagger:LOCAL-SNAPSHOT'
+ kapt 'com.google.dagger:dagger-compiler:LOCAL-SNAPSHOT'
+ implementation 'com.google.dagger:hilt-android:LOCAL-SNAPSHOT'
+ kapt 'com.google.dagger:hilt-android-compiler:LOCAL-SNAPSHOT'
+
+ testImplementation 'com.google.truth:truth:1.0.1'
+ testImplementation 'junit:junit:4.13'
+ testImplementation 'org.robolectric:robolectric:4.3.1'
+ testImplementation 'androidx.core:core:1.1.0'
+ // TODO(bcorso): This multidex dep shouldn't be required -- it's a dep for the generated code.
+ testImplementation 'androidx.multidex:multidex:2.0.0'
+ testImplementation 'com.google.dagger:hilt-android-testing:LOCAL-SNAPSHOT'
+ kaptTest 'com.google.dagger:dagger-compiler:LOCAL-SNAPSHOT'
+ kaptTest 'com.google.dagger:hilt-android-compiler:LOCAL-SNAPSHOT'
+}
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/AndroidManifest.xml b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..2e274ab
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Dagger Authors.
+ ~
+ ~ 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="dagger.hilt.android.example.gradle.simpleKotlin">
+
+ <application
+ android:name=".KotlinApplication"
+ android:allowBackup="true"
+ android:label="@string/app_name"
+ android:supportsRtl="true"
+ android:theme="@style/Theme.AppCompat.Light">
+ <activity android:name=".MainActivity" android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/ActivityModule.kt b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/ActivityModule.kt
new file mode 100644
index 0000000..8a85040
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/ActivityModule.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * 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.
+ */
+package dagger.hilt.android.example.gradle.simpleKotlin
+
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.components.ActivityComponent
+
+@Module
+@InstallIn(ActivityComponent::class)
+object ActivityModule {
+ @UserName
+ @Provides
+ fun provideUserName(): String {
+ return "Android User"
+ }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/ApplicationModule.kt b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/ApplicationModule.kt
new file mode 100644
index 0000000..00d9b35
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/ApplicationModule.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * 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.
+ */
+
+package dagger.hilt.android.example.gradle.simpleKotlin
+
+import android.os.Build
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.components.ApplicationComponent
+
+@Module
+@InstallIn(ApplicationComponent::class)
+object ApplicationModule {
+ @Provides
+ @Model
+ fun provideModel(): String {
+ return Build.MODEL
+ }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/KotlinApplication.kt b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/KotlinApplication.kt
new file mode 100644
index 0000000..aa413d9
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/KotlinApplication.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * 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.
+ */
+
+package dagger.hilt.android.example.gradle.simpleKotlin
+
+import android.app.Application
+import dagger.hilt.GenerateComponents
+import dagger.hilt.android.AndroidEntryPoint
+import javax.inject.Inject
+
+@GenerateComponents
+@AndroidEntryPoint
+class KotlinApplication : Application() {
+ // Shows that we can inject ApplicationComponent bindings into an application.
+ @Inject
+ @Model
+ @JvmField
+ var model: String? = null
+}
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/MainActivity.kt b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/MainActivity.kt
new file mode 100644
index 0000000..564f05a
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/MainActivity.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * 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.
+ */
+
+package dagger.hilt.android.example.gradle.simpleKotlin
+
+import android.os.Bundle
+import android.view.View
+import android.widget.TextView
+import androidx.appcompat.app.AppCompatActivity
+import dagger.hilt.android.AndroidEntryPoint
+import javax.inject.Inject
+
+@AndroidEntryPoint
+class MainActivity : AppCompatActivity() {
+ // Shows that we can inject Application/Activity bindings into an activity.
+ @JvmField
+ @Model
+ @Inject
+ var model: String? = null
+
+ @JvmField
+ @UserName
+ @Inject
+ var name: String? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_main)
+ val greeting = findViewById<View>(R.id.greeting) as TextView
+ val text = resources.getString(R.string.welcome, name, model)
+ greeting.text = text
+ }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/Model.kt b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/Model.kt
new file mode 100644
index 0000000..4fe168d
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/Model.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * 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.
+ */
+
+package dagger.hilt.android.example.gradle.simpleKotlin
+
+import javax.inject.Qualifier
+
+/** Qualifies bindings relating to [android.os.Build.MODEL]. */
+@Qualifier
+@Retention(AnnotationRetention.RUNTIME)
+internal annotation class Model
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/UserName.kt b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/UserName.kt
new file mode 100644
index 0000000..a061ab5
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/java/dagger/hilt/android/example/gradle/simpleKotlin/UserName.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * 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.
+ */
+
+package dagger.hilt.android.example.gradle.simpleKotlin
+
+import javax.inject.Qualifier
+
+/** Qualifies bindings relating to the user name. */
+@Qualifier
+@Retention(AnnotationRetention.RUNTIME)
+internal annotation class UserName
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/res/layout/activity_main.xml b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..0d7637d
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2020 The Dagger Authors.
+ ~
+ ~ 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.
+ -->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@android:color/background_light">
+
+ <TextView
+ android:id="@+id/greeting"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:textColor="@android:color/primary_text_light"
+ />
+</RelativeLayout>
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/res/values/strings.xml b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..c1e4f27
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/main/res/values/strings.xml
@@ -0,0 +1,23 @@
+<!--
+ ~ Copyright (C) 2020 The Dagger Authors.
+ ~
+ ~ 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.
+ -->
+
+<resources>
+ <!--The app name [CHAR_LIMIT=40]-->
+ <string name="app_name">Simple Hilt Kotlin Android App</string>
+
+ <!--The greeting message [CHAR_LIMIT=100]-->
+ <string name="welcome">Hello, %1$s! You are on build %2$s.</string>
+</resources>
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/test/java/dagger/hilt/android/example/gradle/simpleKotlin/SimpleTest.kt b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/test/java/dagger/hilt/android/example/gradle/simpleKotlin/SimpleTest.kt
new file mode 100644
index 0000000..4aeeb08
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/app/src/test/java/dagger/hilt/android/example/gradle/simpleKotlin/SimpleTest.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * 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.
+ */
+
+package dagger.hilt.android.example.gradle.simpleKotlin
+
+import android.os.Build
+import androidx.test.core.app.ActivityScenario
+import com.google.common.truth.Truth.assertThat
+import dagger.hilt.GenerateComponents
+import dagger.hilt.android.testing.AndroidRobolectricEntryPoint
+import dagger.hilt.android.testing.HiltTestRule
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * See [testing documentation](http://d.android.com/tools/testing).
+ */
+@GenerateComponents
+@AndroidRobolectricEntryPoint
+@RunWith(RobolectricTestRunner::class)
+// Robolectric requires Java9 to run API 29 and above, so use API 28 instead
+@Config(sdk = [Build.VERSION_CODES.P], application = SimpleTest_Application::class)
+class SimpleTest {
+ @Rule
+ @JvmField
+ var rule = HiltTestRule(this)
+
+ @Test
+ fun verifyMainActivity() {
+ ActivityScenario.launch(MainActivity::class.java).use { scenario ->
+ scenario.onActivity { activity ->
+ assertThat(activity::class.java.getSuperclass()?.getSimpleName())
+ .isEqualTo("Hilt_MainActivity")
+ assertThat(activity.model).isNotNull()
+ assertThat(activity.name).isNotNull()
+ }
+ }
+ }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/build.gradle b/java/dagger/hilt/android/example/gradle/simpleKotlin/build.gradle
new file mode 100644
index 0000000..e1edb8d
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/build.gradle
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * 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.
+ */
+
+buildscript {
+ ext {
+ kotlin_version = '1.3.61'
+ agp_version = System.getenv('AGP_VERSION') ?: "3.6.3"
+ }
+ repositories {
+ google()
+ jcenter()
+ mavenLocal()
+ }
+ dependencies {
+ classpath "com.android.tools.build:gradle:$agp_version"
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath 'com.google.dagger:hilt-android-gradle-plugin:LOCAL-SNAPSHOT'
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ mavenCentral()
+ mavenLocal()
+ }
+}
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle.properties b/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle.properties
new file mode 100644
index 0000000..646c51b
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle.properties
@@ -0,0 +1,2 @@
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle/wrapper/gradle-wrapper.jar b/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..5c2d1cf
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle/wrapper/gradle-wrapper.properties b/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..a4b4429
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/gradlew b/java/dagger/hilt/android/example/gradle/simpleKotlin/gradlew
new file mode 100644
index 0000000..b0d6d0a
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/gradlew
@@ -0,0 +1,188 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# 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.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/java/dagger/hilt/android/example/gradle/simpleKotlin/settings.gradle b/java/dagger/hilt/android/example/gradle/simpleKotlin/settings.gradle
new file mode 100644
index 0000000..e42f9d1
--- /dev/null
+++ b/java/dagger/hilt/android/example/gradle/simpleKotlin/settings.gradle
@@ -0,0 +1,2 @@
+rootProject.name='Simple Kotlin Hilt Android'
+include ':app'
diff --git a/java/dagger/hilt/android/plugin/build.gradle b/java/dagger/hilt/android/plugin/build.gradle
index bd9db51..80e5c3f 100644
--- a/java/dagger/hilt/android/plugin/build.gradle
+++ b/java/dagger/hilt/android/plugin/build.gradle
@@ -34,7 +34,7 @@
dependencies {
implementation gradleApi()
- implementation 'com.android.tools.build:gradle:3.5.3'
+ implementation 'com.android.tools.build:gradle:3.6.3'
implementation 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.61'
implementation 'org.javassist:javassist:3.26.0-GA'
diff --git a/java/dagger/hilt/android/plugin/gradle/wrapper/gradle-wrapper.properties b/java/dagger/hilt/android/plugin/gradle/wrapper/gradle-wrapper.properties
index f4d7b2b..a4b4429 100644
--- a/java/dagger/hilt/android/plugin/gradle/wrapper/gradle-wrapper.properties
+++ b/java/dagger/hilt/android/plugin/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointClassTransformer.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointClassTransformer.kt
new file mode 100644
index 0000000..7b7b7c4
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointClassTransformer.kt
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * 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.
+ */
+package dagger.hilt.android.plugin
+
+import dagger.hilt.android.plugin.util.isClassFile
+import dagger.hilt.android.plugin.util.isJarFile
+import java.io.File
+import java.io.FileInputStream
+import java.util.zip.ZipInputStream
+import javassist.ClassPool
+import javassist.CtClass
+import org.slf4j.LoggerFactory
+
+/**
+ * A helper class for performing the transform.
+ *
+ * Create it with the list of all available source directories along with the root output directory
+ * and use [AndroidEntryPointClassTransformer.transformFile] or
+ * [AndroidEntryPointClassTransformer.transformJarContents] to perform the actual transformation.
+ */
+internal class AndroidEntryPointClassTransformer(
+ val taskName: String,
+ allInputs: List<File>,
+ private val sourceRootOutputDir: File,
+ private val copyNonTransformed: Boolean
+) {
+ private val logger = LoggerFactory.getLogger(AndroidEntryPointClassTransformer::class.java)
+
+ // A ClassPool created from the given input files, this allows us to use the higher
+ // level Javaassit APIs, but requires class parsing/loading.
+ private val classPool: ClassPool = ClassPool(true).also { pool ->
+ allInputs.forEach {
+ pool.appendClassPath(it.path)
+ }
+ }
+
+ init {
+ sourceRootOutputDir.mkdirs()
+ }
+
+ /**
+ * Transforms the classes inside the jar and copies re-written class files if and only if they are
+ * transformed.
+ *
+ * @param inputFile The jar file to transform, must be a jar.
+ */
+ fun transformJarContents(inputFile: File) {
+ require(inputFile.isJarFile()) {
+ "Invalid file, '$inputFile' is not a jar."
+ }
+ // Validate transform is not applied to a jar when copying is enabled, meaning the transformer
+ // is being used in the Android transform API pipeline which does not need to transform jars
+ // and handles copying them.
+ check(!copyNonTransformed) {
+ "Transforming a jar is not supported with 'copyNonTransformed'."
+ }
+ ZipInputStream(FileInputStream(inputFile)).use { input ->
+ var entry = input.nextEntry
+ while (entry != null) {
+ if (entry.isClassFile()) {
+ val clazz = classPool.makeClass(input, false)
+ transformClassToOutput(clazz)
+ }
+ entry = input.nextEntry
+ }
+ }
+ }
+
+ /**
+ * Transform a single class file.
+ *
+ * @param inputFile The file to transform, must be a class file.
+ */
+ fun transformFile(inputFile: File) {
+ check(inputFile.isClassFile()) {
+ "Invalid file, '$inputFile' is not a class."
+ }
+ val clazz = inputFile.inputStream().use { classPool.makeClass(it, false) }
+ transformClassToOutput(clazz)
+ }
+
+ private fun transformClassToOutput(clazz: CtClass) {
+ val transformed = transformClass(clazz)
+ if (transformed || copyNonTransformed) {
+ clazz.writeFile(sourceRootOutputDir.path)
+ }
+ }
+
+ private fun transformClass(clazz: CtClass): Boolean {
+ if (!clazz.hasAnnotation("dagger.hilt.android.AndroidEntryPoint")) {
+ // Not a AndroidEntryPoint annotated class, don't do anything.
+ return false
+ }
+
+ // TODO(danysantiago): Handle classes with '$' in their name if they do become an issue.
+ val superclassName = clazz.classFile.superclass
+ val entryPointSuperclassName =
+ clazz.packageName + ".Hilt_" + clazz.simpleName.replace("$", "_")
+ logger.info(
+ "[$taskName] Transforming ${clazz.name} to extend $entryPointSuperclassName instead of " +
+ "$superclassName."
+ )
+ clazz.superclass = classPool.get(entryPointSuperclassName)
+ return true
+ }
+}
diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointTransform.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointTransform.kt
index 3927da3..bb35824 100644
--- a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointTransform.kt
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/AndroidEntryPointTransform.kt
@@ -16,18 +16,16 @@
package dagger.hilt.android.plugin
-import com.android.SdkConstants
+import com.android.build.api.transform.DirectoryInput
import com.android.build.api.transform.Format
+import com.android.build.api.transform.JarInput
import com.android.build.api.transform.QualifiedContent
import com.android.build.api.transform.Status
import com.android.build.api.transform.Transform
import com.android.build.api.transform.TransformInput
import com.android.build.api.transform.TransformInvocation
+import dagger.hilt.android.plugin.util.isClassFile
import java.io.File
-import java.util.zip.ZipEntry
-import javassist.ClassPool
-import javassist.CtClass
-import org.slf4j.LoggerFactory
/**
* Bytecode transformation to make @AndroidEntryPoint annotated classes extend the Hilt
@@ -43,9 +41,6 @@
* See: [TransformPublic Docs](https://google.github.io/android-gradle-dsl/javadoc/current/com/android/build/api/transform/Transform.html)
*/
class AndroidEntryPointTransform : Transform() {
-
- private val logger = LoggerFactory.getLogger(javaClass)
-
// The name of the transform. This name appears as a gradle task.
override fun getName() = "AndroidEntryPointTransform"
@@ -75,11 +70,6 @@
invocation.outputProvider.deleteAll()
}
- // Create a ClassPool with the given input and references, this allows us to use the higher
- // level Javaassit APIs, but requires class parsing/loading, note that since this is a PROJECT
- // scoped transform we can only load classes in the project and not its dependencies.
- val pool = createClassPool(invocation.inputs, invocation.referencedInputs)
-
invocation.inputs.forEach { transformInput ->
transformInput.jarInputs.forEach { jarInput ->
val outputJar =
@@ -91,7 +81,7 @@
)
if (invocation.isIncremental) {
when (jarInput.status) {
- Status.ADDED, Status.CHANGED -> copyJar(jarInput.file, outputJar, pool)
+ Status.ADDED, Status.CHANGED -> copyJar(jarInput.file, outputJar)
Status.REMOVED -> outputJar.delete()
Status.NOTCHANGED -> {
// No need to transform.
@@ -101,7 +91,7 @@
}
}
} else {
- copyJar(jarInput.file, outputJar, pool)
+ copyJar(jarInput.file, outputJar)
}
}
transformInput.directoryInputs.forEach { directoryInput ->
@@ -111,11 +101,14 @@
directoryInput.scopes,
Format.DIRECTORY
)
+ val classTransformer =
+ createHiltClassTransformer(invocation.inputs, invocation.referencedInputs, outputDir)
if (invocation.isIncremental) {
directoryInput.changedFiles.forEach { (file, status) ->
val outputFile = toOutputFile(outputDir, directoryInput.file, file)
when (status) {
- Status.ADDED, Status.CHANGED -> transformFile(file, outputFile.parentFile, pool)
+ Status.ADDED, Status.CHANGED ->
+ transformFile(file, outputFile.parentFile, classTransformer)
Status.REMOVED -> outputFile.delete()
Status.NOTCHANGED -> {
// No need to transform.
@@ -128,69 +121,54 @@
} else {
directoryInput.file.walkTopDown().forEach { file ->
val outputFile = toOutputFile(outputDir, directoryInput.file, file)
- transformFile(file, outputFile.parentFile, pool)
+ transformFile(file, outputFile.parentFile, classTransformer)
}
}
}
}
}
- // Create class pool using invocation inputs as classpath.
- private fun createClassPool(
+ // Create a transformer given an invocation inputs. Note that since this is a PROJECT scoped
+ // transform the actual transformation is only done on project files and not its dependencies.
+ private fun createHiltClassTransformer(
inputs: Collection<TransformInput>,
- referencedInputs: Collection<TransformInput>
- ) = ClassPool.getDefault().apply {
- (inputs + referencedInputs).flatMap { input ->
- (input.directoryInputs + input.jarInputs).map { it.file.path }
- }.forEach {
- appendClassPath(it)
+ referencedInputs: Collection<TransformInput>,
+ outputDir: File
+ ): AndroidEntryPointClassTransformer {
+ val classFiles = (inputs + referencedInputs).flatMap { input ->
+ (input.directoryInputs + input.jarInputs).map { it.file }
}
+ return AndroidEntryPointClassTransformer(
+ taskName = name,
+ allInputs = classFiles,
+ sourceRootOutputDir = outputDir,
+ copyNonTransformed = true
+ )
}
- // We are only interested in project compiled classes but we have to copy received jars to the
- // output.
- private fun copyJar(inputJar: File, outputJar: File, pool: ClassPool) {
- outputJar.parentFile?.mkdirs()
- inputJar.copyTo(target = outputJar, overwrite = true)
- }
-
- // Transform a single class file.
- private fun transformFile(inputFile: File, outputDir: File, pool: ClassPool) {
- outputDir.mkdirs()
+ // Transform a single file. If the file is not a class file it is just copied to the output dir.
+ private fun transformFile(
+ inputFile: File,
+ outputDir: File,
+ transformer: AndroidEntryPointClassTransformer
+ ) {
if (inputFile.isClassFile()) {
- val clazz = inputFile.inputStream().use { pool.makeClass(it, false) }
- transformClass(clazz, pool)
- clazz.writeFile(outputDir.path)
+ transformer.transformFile(inputFile)
} else if (inputFile.isFile) {
// Copy all non .class files to the output.
+ outputDir.mkdirs()
val outputFile = File(outputDir, inputFile.name)
inputFile.copyTo(target = outputFile, overwrite = true)
}
}
- // Transform a parsed class file.
- private fun transformClass(clazz: CtClass, pool: ClassPool) {
- if (!clazz.hasAnnotation("dagger.hilt.android.AndroidEntryPoint")) {
- // Not a AndroidEntryPoint annotated class, don't do anything.
- return
- }
-
- // TODO(danysantiago): Handle classes with '$' in their name if they do become an issue.
- val superclassName = clazz.classFile.superclass
- val entryPointSuperclassName =
- clazz.packageName + ".Hilt_" + clazz.simpleName.replace("$", "_")
- logger.info(
- "[$name] Transforming ${clazz.name} to extend $entryPointSuperclassName instead of " +
- "$superclassName."
- )
- clazz.superclass = pool.get(entryPointSuperclassName)
+ // We are only interested in project compiled classes but we have to copy received jars to the
+ // output.
+ private fun copyJar(inputJar: File, outputJar: File) {
+ outputJar.parentFile?.mkdirs()
+ inputJar.copyTo(target = outputJar, overwrite = true)
}
private fun toOutputFile(outputDir: File, inputDir: File, inputFile: File) =
File(outputDir, inputFile.relativeTo(inputDir).path)
-
- private fun ZipEntry.isClassFile() =
- !this.isDirectory && this.name.endsWith(SdkConstants.DOT_CLASS)
-
- private fun File.isClassFile() = this.isFile && this.name.endsWith(SdkConstants.DOT_CLASS)
}
diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltExtension.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltExtension.kt
new file mode 100644
index 0000000..4f6f578
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltExtension.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * 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.
+ */
+package dagger.hilt.android.plugin
+
+/**
+ * Configuration options for the Hilt Gradle Plugin
+ */
+interface HiltExtension {
+ /**
+ * If set to `true`, Hilt will register a transform task that will rewrite `@AndroidEntryPoint`
+ * annotated classes before the host-side JVM tests run. You should enable this option if you are
+ * running Robolectric UI tests as part of your JUnit tests.
+ */
+ var enableTransformForLocalTests: Boolean
+}
+
+internal open class HiltExtensionImpl : HiltExtension {
+ override var enableTransformForLocalTests: Boolean = false
+}
diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltGradlePlugin.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltGradlePlugin.kt
index c339431..2c9bfe4 100644
--- a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltGradlePlugin.kt
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltGradlePlugin.kt
@@ -17,6 +17,8 @@
package dagger.hilt.android.plugin
import com.android.build.gradle.BaseExtension
+import com.android.build.gradle.TestedExtension
+import com.android.build.gradle.api.AndroidBasePlugin
import org.gradle.api.Plugin
import org.gradle.api.Project
@@ -30,10 +32,50 @@
*/
class HiltGradlePlugin : Plugin<Project> {
override fun apply(project: Project) {
+ var configured = false
+ project.plugins.withType(AndroidBasePlugin::class.java) {
+ configured = true
+ configureHilt(project)
+ }
+ project.afterEvaluate {
+ check(configured) {
+ // Check if configuration was applied, if not inform the developer they have applied the
+ // plugin to a non-android project.
+ "The Hilt Android Gradle plugin can only be applied to an Android project."
+ }
+ }
+ }
+
+ private fun configureHilt(project: Project) {
+ val extension = project.extensions.create(
+ HiltExtension::class.java, "hilt", HiltExtensionImpl::class.java
+ )
+ configureTransform(project, extension)
+ configureProcessorFlags(project)
+ project.afterEvaluate {
+ verifyDependencies(it)
+ }
+ }
+
+ private fun configureTransform(project: Project, extension: HiltExtension) {
val androidExtension = project.extensions.findByType(BaseExtension::class.java)
- ?: throw error("The Hilt Android Gradle plugin can only be applied to an Android project.")
+ ?: throw error("Android BaseExtension not found.")
androidExtension.registerTransform(AndroidEntryPointTransform())
+ // Create and configure a task for applying the transform for host-side unit tests. b/37076369
+ val testedExtensions = project.extensions.findByType(TestedExtension::class.java)
+ testedExtensions?.unitTestVariants?.all { unitTestVariant ->
+ HiltTransformTestClassesTask.create(
+ project = project,
+ unitTestVariant = unitTestVariant,
+ extension = extension
+ )
+ }
+ }
+
+ private fun configureProcessorFlags(project: Project) {
+ val androidExtension = project.extensions.findByType(BaseExtension::class.java)
+ ?: throw error("Android BaseExtension not found.")
// Pass annotation processor flag to disable @AndroidEntryPoint superclass validation.
androidExtension.defaultConfig.apply {
javaCompileOptions.apply {
@@ -42,10 +84,6 @@
}
}
}
-
- project.afterEvaluate {
- verifyDependencies(it)
- }
}
private fun verifyDependencies(project: Project) {
diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltTransformTestClassesTask.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltTransformTestClassesTask.kt
new file mode 100644
index 0000000..28a7f75
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/HiltTransformTestClassesTask.kt
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2020 The Dagger Authors.
+ *
+ * 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.
+ */
+package dagger.hilt.android.plugin
+
+import com.android.build.gradle.api.UnitTestVariant
+import dagger.hilt.android.plugin.util.isClassFile
+import dagger.hilt.android.plugin.util.isJarFile
+import java.io.File
+import javax.inject.Inject
+import org.gradle.api.Action
+import org.gradle.api.DefaultTask
+import org.gradle.api.Project
+import org.gradle.api.file.ConfigurableFileCollection
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.file.FileCollection
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.Classpath
+import org.gradle.api.tasks.OutputDirectory
+import org.gradle.api.tasks.TaskAction
+import org.gradle.workers.WorkAction
+import org.gradle.workers.WorkParameters
+import org.gradle.workers.WorkerExecutor
+
+/**
+ * Task that transform classes used by host-side unit tests. See b/37076369
+ */
+@Suppress("UnstableApiUsage")
+abstract class HiltTransformTestClassesTask @Inject constructor(
+ private val workerExecutor: WorkerExecutor
+) : DefaultTask() {
+
+ @get:Classpath
+ abstract val compiledClasses: ConfigurableFileCollection
+
+ @get:OutputDirectory
+ abstract val outputDir: DirectoryProperty
+
+ internal interface Parameters : WorkParameters {
+ val name: Property<String>
+ val compiledClasses: ConfigurableFileCollection
+ val outputDir: DirectoryProperty
+ }
+
+ abstract class WorkerAction : WorkAction<Parameters> {
+ override fun execute() {
+ val outputDir = parameters.outputDir.asFile.get()
+ outputDir.deleteRecursively()
+ outputDir.mkdirs()
+
+ val classTransformer = AndroidEntryPointClassTransformer(
+ taskName = parameters.name.get(),
+ allInputs = parameters.compiledClasses.files.toList(),
+ sourceRootOutputDir = outputDir,
+ copyNonTransformed = false
+ )
+ // Parse the classpath in reverse so that we respect overwrites, if it ever happens.
+ parameters.compiledClasses.files.reversed().forEach {
+ if (it.isDirectory) {
+ it.walkTopDown().forEach { file ->
+ if (file.isClassFile()) {
+ classTransformer.transformFile(file)
+ }
+ }
+ } else if (it.isJarFile()) {
+ classTransformer.transformJarContents(it)
+ }
+ }
+ }
+ }
+
+ @TaskAction
+ fun transformClasses() {
+ workerExecutor.noIsolation().submit(WorkerAction::class.java) {
+ it.compiledClasses.from(compiledClasses)
+ it.outputDir.set(outputDir)
+ it.name.set(name)
+ }
+ }
+
+ internal class ConfigAction(
+ private val outputDir: File,
+ private val inputClasspath: FileCollection
+ ) : Action<HiltTransformTestClassesTask> {
+ override fun execute(transformTask: HiltTransformTestClassesTask) {
+ transformTask.description = "Transforms AndroidEntryPoint annotated classes for JUnit tests."
+ transformTask.outputDir.set(outputDir)
+ transformTask.compiledClasses.from(inputClasspath)
+ }
+ }
+
+ companion object {
+
+ private const val TASK_PREFIX = "hiltTransformForJUnit"
+
+ fun create(
+ project: Project,
+ unitTestVariant: UnitTestVariant,
+ extension: HiltExtension
+ ) {
+ if (!extension.enableTransformForLocalTests) {
+ // Not enabled, nothing to do here.
+ return
+ }
+ val outputDir =
+ project.buildDir.resolve("intermediates/hilt/${unitTestVariant.dirName}Output")
+ val outputFileCollection = project.objects.fileCollection()
+ val classpathKey = unitTestVariant.registerPreJavacGeneratedBytecode(
+ outputFileCollection.from(outputDir)
+ )
+ val hiltTransformProvider = project.tasks.register(
+ "$TASK_PREFIX${unitTestVariant.name.capitalize()}",
+ HiltTransformTestClassesTask::class.java,
+ ConfigAction(outputDir, unitTestVariant.getCompileClasspath(classpathKey))
+ )
+ outputFileCollection.builtBy(hiltTransformProvider)
+ }
+ }
+}
diff --git a/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/Files.kt b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/Files.kt
new file mode 100644
index 0000000..e5a101e
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/main/kotlin/dagger/hilt/android/plugin/util/Files.kt
@@ -0,0 +1,14 @@
+package dagger.hilt.android.plugin.util
+
+import com.android.SdkConstants
+import java.io.File
+import java.util.zip.ZipEntry
+
+/* Checks if a file is a .class file. */
+fun File.isClassFile() = this.isFile && this.extension == SdkConstants.EXT_CLASS
+
+/* Checks if a Zip entry is a .class file. */
+fun ZipEntry.isClassFile() = !this.isDirectory && this.name.endsWith(SdkConstants.DOT_CLASS)
+
+/* CHecks if a file is a .jar file. */
+fun File.isJarFile() = this.isFile && this.extension == SdkConstants.EXT_JAR
diff --git a/java/dagger/hilt/android/plugin/src/test/data/simple-project/gradle.properties b/java/dagger/hilt/android/plugin/src/test/data/simple-project/gradle.properties
new file mode 100644
index 0000000..5bac8ac
--- /dev/null
+++ b/java/dagger/hilt/android/plugin/src/test/data/simple-project/gradle.properties
@@ -0,0 +1 @@
+android.useAndroidX=true
diff --git a/java/dagger/hilt/android/plugin/src/test/kotlin/GradleTransformTestRunner.kt b/java/dagger/hilt/android/plugin/src/test/kotlin/GradleTransformTestRunner.kt
index 08a647d..f2f8ba0 100644
--- a/java/dagger/hilt/android/plugin/src/test/kotlin/GradleTransformTestRunner.kt
+++ b/java/dagger/hilt/android/plugin/src/test/kotlin/GradleTransformTestRunner.kt
@@ -27,6 +27,7 @@
private val activities = mutableListOf<String>()
private var buildFile: File? = null
+ private var gradlePropertiesFile: File? = null
private var manifestFile: File? = null
init {
@@ -75,6 +76,7 @@
private fun setupFiles() {
writeBuildFile()
+ writeGradleProperties()
writeAndroidManifest()
}
@@ -128,6 +130,17 @@
}
}
+ private fun writeGradleProperties() {
+ gradlePropertiesFile?.delete()
+ gradlePropertiesFile = tempFolder.newFile("gradle.properties").apply {
+ writeText(
+ """
+ android.useAndroidX=true
+ """.trimIndent()
+ )
+ }
+ }
+
private fun writeAndroidManifest() {
manifestFile?.delete()
manifestFile = tempFolder.newFile("/src/main/AndroidManifest.xml").apply {
@@ -168,7 +181,7 @@
projectRoot,
"build/intermediates/transforms/AndroidEntryPointTransform/debug"
).listFiles()?.first { it.isDirectory }?.let { transformedDir ->
- File(transformedDir, "minimal/$srcFilePath").also {
+ File(transformedDir, srcFilePath).also {
if (!it.exists()) {
error("Unable to find transformed class ${it.path}")
}
diff --git a/java/dagger/hilt/android/plugin/src/test/kotlin/IncrementalProcessorTest.kt b/java/dagger/hilt/android/plugin/src/test/kotlin/IncrementalProcessorTest.kt
index bc5c04e..b5b43f9 100644
--- a/java/dagger/hilt/android/plugin/src/test/kotlin/IncrementalProcessorTest.kt
+++ b/java/dagger/hilt/android/plugin/src/test/kotlin/IncrementalProcessorTest.kt
@@ -316,7 +316,6 @@
// * All aggregating processor gen sources are re-compiled
assertChangedFiles(
FileType.CLASS,
- classSrcApp, // re-compiles because superclass re-compiled
classSrcActivity1,
classGenHiltApp,
classGenHiltActivity1,
@@ -365,7 +364,6 @@
// * All aggregating processor gen sources are re-compiled
assertChangedFiles(
FileType.CLASS,
- classSrcApp, // re-compiles because superclass re-compiled
classSrcModule1,
classGenHiltApp,
classGenAppInjector,
@@ -457,7 +455,6 @@
)
assertChangedFiles(
FileType.CLASS,
- classSrcApp, // re-compiles because superclass re-compiled
classGenHiltApp,
classGenAppInjector,
classGenAppInjectorDeps,
@@ -500,7 +497,6 @@
)
assertChangedFiles(
FileType.CLASS,
- classSrcApp, // re-compiles because superclass re-compiled
classGenHiltApp,
classGenAppInjector,
classGenAppInjectorDeps,
diff --git a/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointMetadata.java b/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointMetadata.java
index 0b246ed..1621734 100644
--- a/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointMetadata.java
+++ b/java/dagger/hilt/android/processor/internal/androidentrypoint/AndroidEntryPointMetadata.java
@@ -208,17 +208,19 @@
annotationClassName.simpleName());
TypeElement androidEntryPointElement = (TypeElement) element;
+ final TypeElement androidEntryPointClassValue =
+ Processors.getAnnotationClassValue(
+ env.getElementUtils(),
+ Processors.getAnnotationMirror(androidEntryPointElement, annotationClassName),
+ "value");
final TypeElement baseElement;
final ClassName generatedClassName;
- if (DISABLE_ANDROID_SUPERCLASS_VALIDATION.get(env)) {
+ if (DISABLE_ANDROID_SUPERCLASS_VALIDATION.get(env)
+ && MoreTypes.isTypeOf(Void.class, androidEntryPointClassValue.asType())) {
baseElement = MoreElements.asType(env.getTypeUtils().asElement(androidEntryPointElement.getSuperclass()));
generatedClassName = generatedClassName(androidEntryPointElement);
} else {
- baseElement =
- Processors.getAnnotationClassValue(
- env.getElementUtils(),
- Processors.getAnnotationMirror(androidEntryPointElement, annotationClassName),
- "value");
+ baseElement = androidEntryPointClassValue;
ProcessorErrors.checkState(
!MoreTypes.isTypeOf(Void.class, baseElement.asType()),
androidEntryPointElement,
diff --git a/util/run-local-tests.sh b/util/run-local-tests.sh
index d7297a9..06208c0 100755
--- a/util/run-local-tests.sh
+++ b/util/run-local-tests.sh
@@ -7,19 +7,29 @@
# Run tests with bazel
bazel test $TEST_PARAMS //...
+# Install into local maven.
+util/install-local-snapshot.sh
+
# Also run the gradle examples on the local maven snapshots.
readonly _SIMPLE_EXAMPLE_DIR=java/dagger/example/gradle/simple
readonly _ANDROID_EXAMPLE_DIR=java/dagger/example/gradle/android/simple
-
-util/install-local-snapshot.sh
./$_SIMPLE_EXAMPLE_DIR/gradlew -p $_SIMPLE_EXAMPLE_DIR build --no-daemon --stacktrace
./$_ANDROID_EXAMPLE_DIR/gradlew -p $_ANDROID_EXAMPLE_DIR build --no-daemon --stacktrace
readonly _HILT_GRADLE_PLUGIN_DIR=java/dagger/hilt/android/plugin
readonly _HILT_ANDROID_EXAMPLE_DIR=java/dagger/hilt/android/example/gradle/simple
+readonly _HILT_KOTLIN_ANDROID_EXAMPLE_DIR=java/dagger/hilt/android/example/gradle/simpleKotlin
./$_HILT_GRADLE_PLUGIN_DIR/gradlew -p $_HILT_GRADLE_PLUGIN_DIR test --no-daemon --stacktrace
-./$_HILT_ANDROID_EXAMPLE_DIR/gradlew -p $_HILT_ANDROID_EXAMPLE_DIR buildDebug --no-daemon --stacktrace
-./$_HILT_ANDROID_EXAMPLE_DIR/gradlew -p $_HILT_ANDROID_EXAMPLE_DIR testDebug --no-daemon --stacktrace
+# Run gradle tests with different versions of Android Gradle Plugin
+agp_verions=("4.1.0-alpha07" "4.0.0-beta05" "3.6.3")
+for version in "${agp_verions[@]}"
+do
+ echo "Running tests with AGP $version"
+ AGP_VERSION=$version ./$_HILT_ANDROID_EXAMPLE_DIR/gradlew -p $_HILT_ANDROID_EXAMPLE_DIR buildDebug --no-daemon --stacktrace
+ AGP_VERSION=$version ./$_HILT_ANDROID_EXAMPLE_DIR/gradlew -p $_HILT_ANDROID_EXAMPLE_DIR testDebug --no-daemon --stacktrace
+ AGP_VERSION=$version ./$_HILT_KOTLIN_ANDROID_EXAMPLE_DIR/gradlew -p $_HILT_KOTLIN_ANDROID_EXAMPLE_DIR buildDebug --no-daemon --stacktrace
+ AGP_VERSION=$version ./$_HILT_KOTLIN_ANDROID_EXAMPLE_DIR/gradlew -p $_HILT_KOTLIN_ANDROID_EXAMPLE_DIR testDebug --no-daemon --stacktrace
+done
verify_version_file() {
local m2_repo=$1