aboutsummaryrefslogtreecommitdiff
path: root/java/dagger/hilt/processor/internal/aggregateddeps
diff options
context:
space:
mode:
Diffstat (limited to 'java/dagger/hilt/processor/internal/aggregateddeps')
-rw-r--r--java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsGenerator.java18
-rw-r--r--java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsMetadata.java178
-rw-r--r--java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsProcessor.java87
-rw-r--r--java/dagger/hilt/processor/internal/aggregateddeps/BUILD27
-rw-r--r--java/dagger/hilt/processor/internal/aggregateddeps/ComponentDependencies.java329
-rw-r--r--java/dagger/hilt/processor/internal/aggregateddeps/PkgPrivateMetadata.java29
6 files changed, 349 insertions, 319 deletions
diff --git a/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsGenerator.java b/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsGenerator.java
index 84fb5a1ee..bcc2dfe04 100644
--- a/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsGenerator.java
+++ b/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsGenerator.java
@@ -19,6 +19,8 @@ package dagger.hilt.processor.internal.aggregateddeps;
import com.google.common.collect.ImmutableSet;
import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.ClassName;
+import com.squareup.javapoet.JavaFile;
+import com.squareup.javapoet.TypeSpec;
import dagger.hilt.processor.internal.Processors;
import java.io.IOException;
import java.util.Optional;
@@ -57,8 +59,20 @@ final class AggregatedDepsGenerator {
}
void generate() throws IOException {
- Processors.generateAggregatingClass(
- AGGREGATING_PACKAGE, aggregatedDepsAnnotation(), dependency, getClass(), processingEnv);
+ ClassName name =
+ ClassName.get(
+ AGGREGATING_PACKAGE, Processors.getFullEnclosedName(dependency) + "ModuleDeps");
+ TypeSpec.Builder generator =
+ TypeSpec.classBuilder(name.simpleName())
+ .addOriginatingElement(dependency)
+ .addAnnotation(aggregatedDepsAnnotation())
+ .addJavadoc("Generated class to pass information through multiple javac runs.\n");
+
+ Processors.addGeneratedAnnotation(generator, processingEnv, getClass());
+
+ JavaFile.builder(name.packageName(), generator.build())
+ .build()
+ .writeTo(processingEnv.getFiler());
}
private AnnotationSpec aggregatedDepsAnnotation() {
diff --git a/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsMetadata.java b/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsMetadata.java
deleted file mode 100644
index 09e1651fe..000000000
--- a/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsMetadata.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright (C) 2021 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.processor.internal.aggregateddeps;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
-
-import com.google.auto.value.AutoValue;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import dagger.hilt.processor.internal.AggregatedElements;
-import dagger.hilt.processor.internal.AnnotationValues;
-import dagger.hilt.processor.internal.ClassNames;
-import dagger.hilt.processor.internal.Processors;
-import java.util.Optional;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.AnnotationValue;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.util.Elements;
-
-/**
- * A class that represents the values stored in an {@link
- * dagger.hilt.processor.internal.aggregateddeps.AggregatedDeps} annotation.
- */
-@AutoValue
-abstract class AggregatedDepsMetadata {
- private static final String AGGREGATED_DEPS_PACKAGE = "hilt_aggregated_deps";
-
- enum DependencyType {
- MODULE,
- ENTRY_POINT,
- COMPONENT_ENTRY_POINT
- }
-
- abstract Optional<TypeElement> testElement();
-
- abstract ImmutableSet<TypeElement> componentElements();
-
- abstract DependencyType dependencyType();
-
- abstract TypeElement dependency();
-
- abstract ImmutableSet<TypeElement> replacedDependencies();
-
- /** Returns all aggregated deps in the aggregating package. */
- public static ImmutableSet<AggregatedDepsMetadata> from(Elements elements) {
- return AggregatedElements.from(AGGREGATED_DEPS_PACKAGE, ClassNames.AGGREGATED_DEPS, elements)
- .stream()
- .map(aggregatedElement -> create(aggregatedElement, elements))
- .collect(toImmutableSet());
- }
-
- private static AggregatedDepsMetadata create(TypeElement element, Elements elements) {
- AnnotationMirror annotationMirror =
- Processors.getAnnotationMirror(element, ClassNames.AGGREGATED_DEPS);
-
- ImmutableMap<String, AnnotationValue> values =
- Processors.getAnnotationValues(elements, annotationMirror);
-
- return new AutoValue_AggregatedDepsMetadata(
- getTestElement(values.get("test"), elements),
- getComponents(values.get("components"), elements),
- getDependencyType(
- values.get("modules"),
- values.get("entryPoints"),
- values.get("componentEntryPoints")),
- getDependency(
- values.get("modules"),
- values.get("entryPoints"),
- values.get("componentEntryPoints"),
- elements),
- getReplacedDependencies(values.get("replaces"), elements));
- }
-
- private static Optional<TypeElement> getTestElement(
- AnnotationValue testValue, Elements elements) {
- checkNotNull(testValue);
- String test = AnnotationValues.getString(testValue);
- return test.isEmpty() ? Optional.empty() : Optional.of(elements.getTypeElement(test));
- }
-
- private static ImmutableSet<TypeElement> getComponents(
- AnnotationValue componentsValue, Elements elements) {
- checkNotNull(componentsValue);
- ImmutableSet<TypeElement> componentNames =
- AnnotationValues.getAnnotationValues(componentsValue).stream()
- .map(AnnotationValues::getString)
- .map(
- // This is a temporary hack to map the old ApplicationComponent to the new
- // SingletonComponent. Technically, this is only needed for backwards compatibility
- // with libraries using the old processor since new processors should convert to the
- // new SingletonComponent when generating the metadata class.
- componentName ->
- componentName.contentEquals(
- "dagger.hilt.android.components.ApplicationComponent")
- ? ClassNames.SINGLETON_COMPONENT.canonicalName()
- : componentName)
- .map(elements::getTypeElement)
- .collect(toImmutableSet());
- checkState(!componentNames.isEmpty());
- return componentNames;
- }
-
- private static DependencyType getDependencyType(
- AnnotationValue modulesValue,
- AnnotationValue entryPointsValue,
- AnnotationValue componentEntryPointsValue) {
- checkNotNull(modulesValue);
- checkNotNull(entryPointsValue);
- checkNotNull(componentEntryPointsValue);
-
- ImmutableSet.Builder<DependencyType> dependencyTypes = ImmutableSet.builder();
- if (!AnnotationValues.getAnnotationValues(modulesValue).isEmpty()) {
- dependencyTypes.add(DependencyType.MODULE);
- }
- if (!AnnotationValues.getAnnotationValues(entryPointsValue).isEmpty()) {
- dependencyTypes.add(DependencyType.ENTRY_POINT);
- }
- if (!AnnotationValues.getAnnotationValues(componentEntryPointsValue).isEmpty()) {
- dependencyTypes.add(DependencyType.COMPONENT_ENTRY_POINT);
- }
- return getOnlyElement(dependencyTypes.build());
- }
-
- private static TypeElement getDependency(
- AnnotationValue modulesValue,
- AnnotationValue entryPointsValue,
- AnnotationValue componentEntryPointsValue,
- Elements elements) {
- checkNotNull(modulesValue);
- checkNotNull(entryPointsValue);
- checkNotNull(componentEntryPointsValue);
-
- return elements.getTypeElement(
- AnnotationValues.getString(
- getOnlyElement(
- ImmutableSet.<AnnotationValue>builder()
- .addAll(AnnotationValues.getAnnotationValues(modulesValue))
- .addAll(AnnotationValues.getAnnotationValues(entryPointsValue))
- .addAll(AnnotationValues.getAnnotationValues(componentEntryPointsValue))
- .build())));
- }
-
- private static ImmutableSet<TypeElement> getReplacedDependencies(
- AnnotationValue replacedDependenciesValue, Elements elements) {
- // Allow null values to support libraries using a Hilt version before @TestInstallIn was added
- return replacedDependenciesValue == null
- ? ImmutableSet.of()
- : AnnotationValues.getAnnotationValues(replacedDependenciesValue).stream()
- .map(AnnotationValues::getString)
- .map(elements::getTypeElement)
- .map(replacedDep -> getPublicDependency(replacedDep, elements))
- .collect(toImmutableSet());
- }
-
- /** Returns the public Hilt wrapper module, or the module itself if its already public. */
- private static TypeElement getPublicDependency(TypeElement dependency, Elements elements) {
- return PkgPrivateMetadata.of(elements, dependency, ClassNames.MODULE)
- .map(metadata -> elements.getTypeElement(metadata.generatedClassName().toString()))
- .orElse(dependency);
- }
-}
diff --git a/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsProcessor.java b/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsProcessor.java
index 151401e60..58c938f88 100644
--- a/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsProcessor.java
+++ b/java/dagger/hilt/processor/internal/aggregateddeps/AggregatedDepsProcessor.java
@@ -20,7 +20,7 @@ import static com.google.auto.common.AnnotationMirrors.getAnnotationValue;
import static com.google.auto.common.MoreElements.asType;
import static com.google.auto.common.MoreElements.getPackage;
import static com.google.common.collect.Iterables.getOnlyElement;
-import static dagger.hilt.processor.internal.HiltCompilerOptions.isModuleInstallInCheckDisabled;
+import static dagger.hilt.android.processor.internal.androidentrypoint.HiltCompilerOptions.BooleanOption.DISABLE_MODULES_HAVE_INSTALL_IN_CHECK;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
import static javax.lang.model.element.ElementKind.CLASS;
@@ -37,8 +37,10 @@ import com.squareup.javapoet.ClassName;
import dagger.hilt.processor.internal.BaseProcessor;
import dagger.hilt.processor.internal.ClassNames;
import dagger.hilt.processor.internal.Components;
+import dagger.hilt.processor.internal.KotlinMetadataUtils;
import dagger.hilt.processor.internal.ProcessorErrors;
import dagger.hilt.processor.internal.Processors;
+import dagger.internal.codegen.kotlin.KotlinMetadataUtil;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
@@ -63,7 +65,6 @@ public final class AggregatedDepsProcessor extends BaseProcessor {
private static final ImmutableSet<ClassName> ENTRY_POINT_ANNOTATIONS =
ImmutableSet.of(
ClassNames.ENTRY_POINT,
- ClassNames.EARLY_ENTRY_POINT,
ClassNames.GENERATED_ENTRY_POINT,
ClassNames.COMPONENT_ENTRY_POINT);
@@ -165,10 +166,7 @@ public final class AggregatedDepsProcessor extends BaseProcessor {
// Check that if Dagger needs an instance of the module, Hilt can provide it automatically by
// calling a visible empty constructor.
ProcessorErrors.checkState(
- // Skip ApplicationContextModule, since Hilt manages this module internally.
- ClassNames.APPLICATION_CONTEXT_MODULE.equals(ClassName.get(module))
- || !Processors.requiresModuleInstance(getElementUtils(), module)
- || hasVisibleEmptyConstructor(module),
+ !daggerRequiresModuleInstance(module) || hasVisibleEmptyConstructor(module),
module,
"Modules that need to be instantiated by Hilt must have a visible, empty constructor.");
@@ -188,14 +186,13 @@ public final class AggregatedDepsProcessor extends BaseProcessor {
ImmutableList<TypeElement> replacedModules = ImmutableList.of();
if (Processors.hasAnnotation(module, ClassNames.TEST_INSTALL_IN)) {
- Optional<TypeElement> originatingTestElement =
- Processors.getOriginatingTestElement(module, getElementUtils());
+ Optional<TypeElement> originatingTestElement = getOriginatingTestElement(module);
ProcessorErrors.checkState(
!originatingTestElement.isPresent(),
// TODO(b/152801981): this should really error on the annotation value
module,
"@TestInstallIn modules cannot be nested in (or originate from) a "
- + "@HiltAndroidTest-annotated class: %s",
+ + "@HiltAndroidTest-annotated class: %s",
originatingTestElement
.map(testElement -> testElement.getQualifiedName().toString())
.orElse(""));
@@ -265,10 +262,7 @@ public final class AggregatedDepsProcessor extends BaseProcessor {
// Prevent users from uninstalling test-specific @InstallIn modules.
ImmutableList<TypeElement> replacedTestSpecificInstallIn =
replacedModules.stream()
- .filter(
- replacedModule ->
- Processors.getOriginatingTestElement(replacedModule, getElementUtils())
- .isPresent())
+ .filter(replacedModule -> getOriginatingTestElement(replacedModule).isPresent())
.collect(toImmutableList());
ProcessorErrors.checkState(
@@ -310,28 +304,6 @@ public final class AggregatedDepsProcessor extends BaseProcessor {
element);
TypeElement entryPoint = asType(element);
- if (entryPointAnnotation.equals(ClassNames.EARLY_ENTRY_POINT)) {
- ImmutableSet<ClassName> components = Components.getComponents(getElementUtils(), element);
- ProcessorErrors.checkState(
- components.equals(ImmutableSet.of(ClassNames.SINGLETON_COMPONENT)),
- element,
- "@EarlyEntryPoint can only be installed into the SingletonComponent. Found: %s",
- components);
-
- Optional<TypeElement> optionalTestElement =
- Processors.getOriginatingTestElement(element, getElementUtils());
- ProcessorErrors.checkState(
- !optionalTestElement.isPresent(),
- element,
- "@EarlyEntryPoint-annotated entry point, %s, cannot be nested in (or originate from) "
- + "a @HiltAndroidTest-annotated class, %s. This requirement is to avoid confusion "
- + "with other, test-specific entry points.",
- asType(element).getQualifiedName().toString(),
- optionalTestElement
- .map(testElement -> testElement.getQualifiedName().toString())
- .orElse(""));
- }
-
generateAggregatedDeps(
entryPointAnnotation.equals(ClassNames.COMPONENT_ENTRY_POINT)
? "componentEntryPoints"
@@ -361,8 +333,7 @@ public final class AggregatedDepsProcessor extends BaseProcessor {
.generate();
}
} else {
- Optional<ClassName> testName =
- Processors.getOriginatingTestElement(element, getElementUtils()).map(ClassName::get);
+ Optional<ClassName> testName = getOriginatingTestElement(element).map(ClassName::get);
new AggregatedDepsGenerator(
key, element, testName, components, replacedModules, getProcessingEnv())
.generate();
@@ -391,6 +362,25 @@ public final class AggregatedDepsProcessor extends BaseProcessor {
return Optional.of(getOnlyElement(usedAnnotations));
}
+ private Optional<TypeElement> getOriginatingTestElement(Element element) {
+ TypeElement topLevelType = getOriginatingTopLevelType(element);
+ return Processors.hasAnnotation(topLevelType, ClassNames.HILT_ANDROID_TEST)
+ ? Optional.of(asType(topLevelType))
+ : Optional.empty();
+ }
+
+ private TypeElement getOriginatingTopLevelType(Element element) {
+ TypeElement topLevelType = Processors.getTopLevelType(element);
+ if (Processors.hasAnnotation(topLevelType, ClassNames.ORIGINATING_ELEMENT)) {
+ return getOriginatingTopLevelType(
+ Processors.getAnnotationClassValue(
+ getElementUtils(),
+ Processors.getAnnotationMirror(topLevelType, ClassNames.ORIGINATING_ELEMENT),
+ "topLevelClass"));
+ }
+ return topLevelType;
+ }
+
private static boolean isValidKind(Element element) {
// don't go down the rabbit hole of analyzing undefined types. N.B. we don't issue
// an error here because javac already has and we don't want to spam the user.
@@ -398,7 +388,7 @@ public final class AggregatedDepsProcessor extends BaseProcessor {
}
private boolean installInCheckDisabled(Element element) {
- return isModuleInstallInCheckDisabled(getProcessingEnv())
+ return DISABLE_MODULES_HAVE_INSTALL_IN_CHECK.get(getProcessingEnv())
|| Processors.hasAnnotation(element, ClassNames.DISABLE_INSTALL_IN_CHECK);
}
@@ -445,6 +435,27 @@ public final class AggregatedDepsProcessor extends BaseProcessor {
|| name.contentEquals("javax.annotation.processing.Generated");
}
+ private static boolean daggerRequiresModuleInstance(TypeElement module) {
+ return !module.getModifiers().contains(ABSTRACT)
+ && !hasOnlyStaticProvides(module)
+ // Skip ApplicationContextModule, since Hilt manages this module internally.
+ && !ClassNames.APPLICATION_CONTEXT_MODULE.equals(ClassName.get(module))
+ // Skip Kotlin object modules since all their provision methods are static
+ && !isKotlinObject(module);
+ }
+
+ private static boolean isKotlinObject(TypeElement type) {
+ KotlinMetadataUtil metadataUtil = KotlinMetadataUtils.getMetadataUtil();
+ return metadataUtil.isObjectClass(type) || metadataUtil.isCompanionObjectClass(type);
+ }
+
+ private static boolean hasOnlyStaticProvides(TypeElement module) {
+ // TODO(erichang): Check for @Produces too when we have a producers story
+ return ElementFilter.methodsIn(module.getEnclosedElements()).stream()
+ .filter(method -> Processors.hasAnnotation(method, ClassNames.PROVIDES))
+ .allMatch(method -> method.getModifiers().contains(STATIC));
+ }
+
private static boolean hasVisibleEmptyConstructor(TypeElement type) {
List<ExecutableElement> constructors = ElementFilter.constructorsIn(type.getEnclosedElements());
return constructors.isEmpty()
diff --git a/java/dagger/hilt/processor/internal/aggregateddeps/BUILD b/java/dagger/hilt/processor/internal/aggregateddeps/BUILD
index 5da2241f5..ebbc941bc 100644
--- a/java/dagger/hilt/processor/internal/aggregateddeps/BUILD
+++ b/java/dagger/hilt/processor/internal/aggregateddeps/BUILD
@@ -39,13 +39,14 @@ java_library(
"AggregatedDepsGenerator.java",
"AggregatedDepsProcessor.java",
"PkgPrivateEntryPointGenerator.java",
+ "PkgPrivateMetadata.java",
"PkgPrivateModuleGenerator.java",
],
deps = [
- ":pkg_private_metadata",
+ "//:dagger_with_compiler",
+ "//java/dagger/hilt/android/processor/internal/androidentrypoint:compiler_options",
"//java/dagger/hilt/processor/internal:base_processor",
"//java/dagger/hilt/processor/internal:classnames",
- "//java/dagger/hilt/processor/internal:compiler_options",
"//java/dagger/hilt/processor/internal:components",
"//java/dagger/hilt/processor/internal:kotlin",
"//java/dagger/hilt/processor/internal:processor_errors",
@@ -54,21 +55,10 @@ java_library(
"//java/dagger/internal/codegen/kotlin",
"//java/dagger/internal/guava:collect",
"@google_bazel_common//third_party/java/auto:service",
- "@google_bazel_common//third_party/java/incap",
- "@google_bazel_common//third_party/java/javapoet",
- "@maven//:com_google_auto_auto_common",
- ],
-)
-
-java_library(
- name = "pkg_private_metadata",
- srcs = ["PkgPrivateMetadata.java"],
- deps = [
- "//java/dagger/hilt/processor/internal:classnames",
- "//java/dagger/hilt/processor/internal:kotlin",
- "//java/dagger/hilt/processor/internal:processors",
"@google_bazel_common//third_party/java/auto:value",
+ "@google_bazel_common//third_party/java/incap",
"@google_bazel_common//third_party/java/javapoet",
+ "@google_bazel_common//third_party/java/jsr250_annotations",
"@maven//:com_google_auto_auto_common",
],
)
@@ -76,17 +66,14 @@ java_library(
java_library(
name = "component_dependencies",
srcs = [
- "AggregatedDepsMetadata.java",
"ComponentDependencies.java",
],
deps = [
- ":pkg_private_metadata",
- "//java/dagger/hilt/processor/internal:aggregated_elements",
+ ":processor_lib",
"//java/dagger/hilt/processor/internal:classnames",
"//java/dagger/hilt/processor/internal:component_descriptor",
+ "//java/dagger/hilt/processor/internal:processor_errors",
"//java/dagger/hilt/processor/internal:processors",
- "//java/dagger/hilt/processor/internal/earlyentrypoint:aggregated_early_entry_point_metadata",
- "//java/dagger/hilt/processor/internal/uninstallmodules:aggregated_uninstall_modules_metadata",
"//java/dagger/internal/codegen/extension",
"//java/dagger/internal/guava:base",
"//java/dagger/internal/guava:collect",
diff --git a/java/dagger/hilt/processor/internal/aggregateddeps/ComponentDependencies.java b/java/dagger/hilt/processor/internal/aggregateddeps/ComponentDependencies.java
index 897ffd144..23ed148c0 100644
--- a/java/dagger/hilt/processor/internal/aggregateddeps/ComponentDependencies.java
+++ b/java/dagger/hilt/processor/internal/aggregateddeps/ComponentDependencies.java
@@ -16,17 +16,36 @@
package dagger.hilt.processor.internal.aggregateddeps;
+import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.collect.Iterables.getOnlyElement;
+import static dagger.hilt.processor.internal.aggregateddeps.AggregatedDepsGenerator.AGGREGATING_PACKAGE;
+import static dagger.internal.codegen.extension.DaggerStreams.toImmutableList;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
import com.google.auto.value.AutoValue;
+import com.google.common.collect.HashMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
+import com.google.common.collect.SetMultimap;
import com.squareup.javapoet.ClassName;
+import dagger.hilt.processor.internal.AnnotationValues;
+import dagger.hilt.processor.internal.BadInputException;
import dagger.hilt.processor.internal.ClassNames;
import dagger.hilt.processor.internal.ComponentDescriptor;
-import dagger.hilt.processor.internal.earlyentrypoint.AggregatedEarlyEntryPointMetadata;
-import dagger.hilt.processor.internal.uninstallmodules.AggregatedUninstallModulesMetadata;
+import dagger.hilt.processor.internal.ProcessorErrors;
+import dagger.hilt.processor.internal.Processors;
+import dagger.hilt.processor.internal.aggregateddeps.ComponentDependencies.AggregatedDepMetadata.DependencyType;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ElementKind;
+import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
@@ -46,23 +65,6 @@ public abstract class ComponentDependencies {
/** Returns the component entry point associated with the given a component. */
public abstract Dependencies componentEntryPoints();
- /** Returns the set of early entry points */
- public abstract ImmutableSet<ClassName> earlyEntryPoints();
-
- /** Returns {@code true} if any entry points are annotated with {@code EarlyEntryPoints}. */
- public boolean hasEarlyEntryPoints() {
- return !earlyEntryPoints().isEmpty();
- }
-
- /**
- * Returns {@code true} if the test binds or uninstalls test-specific bindings that would prevent
- * it from sharing components with other test roots.
- */
- public final boolean includesTestDeps(ClassName root) {
- return modules().testDeps().keySet().stream().anyMatch((key) -> key.test().equals(root))
- || modules().uninstalledTestDeps().containsKey(root);
- }
-
@AutoValue.Builder
abstract static class Builder {
abstract Dependencies.Builder modulesBuilder();
@@ -71,9 +73,12 @@ public abstract class ComponentDependencies {
abstract Dependencies.Builder componentEntryPointsBuilder();
- abstract ImmutableSet.Builder<ClassName> earlyEntryPointsBuilder();
+ abstract ComponentDependencies autoBuild();
- abstract ComponentDependencies build();
+ ComponentDependencies build(Elements elements) {
+ validateModules(modulesBuilder().build(), elements);
+ return autoBuild();
+ }
}
/** A key used for grouping a test dependency by both its component and test name. */
@@ -118,17 +123,6 @@ public abstract class ComponentDependencies {
/** Returns the global uninstalled test deps. */
abstract ImmutableSet<TypeElement> globalUninstalledTestDeps();
- /** Returns the dependencies to be installed in the global singleton component. */
- ImmutableSet<TypeElement> getGlobalSingletonDeps() {
- return ImmutableSet.<TypeElement>builder()
- .addAll(
- globalDeps().get(ClassNames.SINGLETON_COMPONENT).stream()
- .filter(dep -> !globalUninstalledTestDeps().contains(dep))
- .collect(toImmutableSet()))
- .addAll(globalTestDeps().get(ClassNames.SINGLETON_COMPONENT))
- .build();
- }
-
/** Returns the dependencies to be installed in the given component for the given root. */
public ImmutableSet<TypeElement> get(ClassName component, ClassName root, boolean isTestRoot) {
if (!isTestRoot) {
@@ -181,10 +175,14 @@ public abstract class ComponentDependencies {
*/
public static ComponentDependencies from(
ImmutableSet<ComponentDescriptor> descriptors, Elements elements) {
- ImmutableSet<ClassName> componentNames =
- descriptors.stream().map(ComponentDescriptor::component).collect(toImmutableSet());
+ Map<String, ComponentDescriptor> descriptorLookup = descriptorLookupMap(descriptors);
+ ImmutableList<AggregatedDepMetadata> metadatas =
+ getAggregatedDeps(elements).stream()
+ .map(deps -> AggregatedDepMetadata.create(deps, descriptorLookup, elements))
+ .collect(toImmutableList());
+
ComponentDependencies.Builder componentDependencies = ComponentDependencies.builder();
- for (AggregatedDepsMetadata metadata : AggregatedDepsMetadata.from(elements)) {
+ for (AggregatedDepMetadata metadata : metadatas) {
Dependencies.Builder builder = null;
switch (metadata.dependencyType()) {
case MODULE:
@@ -197,46 +195,265 @@ public abstract class ComponentDependencies {
builder = componentDependencies.componentEntryPointsBuilder();
break;
}
- for (TypeElement componentElement : metadata.componentElements()) {
- ClassName componentName = ClassName.get(componentElement);
- checkState(
- componentNames.contains(componentName), "%s is not a valid Component.", componentName);
+
+ for (ComponentDescriptor componentDescriptor : metadata.componentDescriptors()) {
+ ClassName component = componentDescriptor.component();
if (metadata.testElement().isPresent()) {
// In this case the @InstallIn or @TestInstallIn applies to only the given test root.
ClassName test = ClassName.get(metadata.testElement().get());
- builder.testDepsBuilder().put(TestDepKey.of(componentName, test), metadata.dependency());
+ builder.testDepsBuilder().put(TestDepKey.of(component, test), metadata.dependency());
builder.uninstalledTestDepsBuilder().putAll(test, metadata.replacedDependencies());
} else {
// In this case the @InstallIn or @TestInstallIn applies to all roots
if (!metadata.replacedDependencies().isEmpty()) {
// If there are replacedDependencies() it means this is a @TestInstallIn
- builder.globalTestDepsBuilder().put(componentName, metadata.dependency());
+ builder.globalTestDepsBuilder().put(component, metadata.dependency());
builder.globalUninstalledTestDepsBuilder().addAll(metadata.replacedDependencies());
} else {
- builder.globalDepsBuilder().put(componentName, metadata.dependency());
+ builder.globalDepsBuilder().put(component, metadata.dependency());
}
}
}
}
- AggregatedUninstallModulesMetadata.from(elements)
+ // Collect all @UninstallModules.
+ // TODO(b/176438516): Filter @UninstallModules at the root.
+ metadatas.stream()
+ .filter(metadata -> metadata.testElement().isPresent())
+ .map(metadata -> metadata.testElement().get())
+ .distinct()
+ .filter(testElement -> Processors.hasAnnotation(testElement, ClassNames.IGNORE_MODULES))
.forEach(
- metadata ->
+ testElement ->
componentDependencies
.modulesBuilder()
.uninstalledTestDepsBuilder()
.putAll(
- ClassName.get(metadata.testElement()),
- metadata.uninstallModuleElements().stream()
- .map(module -> PkgPrivateMetadata.publicModule(module, elements))
- .collect(toImmutableSet())));
-
- AggregatedEarlyEntryPointMetadata.from(elements).stream()
- .map(AggregatedEarlyEntryPointMetadata::earlyEntryPoint)
- .map(entryPoint -> PkgPrivateMetadata.publicEarlyEntryPoint(entryPoint, elements))
- .map(ClassName::get)
- .forEach(componentDependencies.earlyEntryPointsBuilder()::add);
-
- return componentDependencies.build();
+ ClassName.get(testElement), getUninstalledModules(testElement, elements)));
+
+ return componentDependencies.build(elements);
+ }
+
+ private static ImmutableMap<String, ComponentDescriptor> descriptorLookupMap(
+ ImmutableSet<ComponentDescriptor> descriptors) {
+ ImmutableMap.Builder<String, ComponentDescriptor> builder = ImmutableMap.builder();
+ for (ComponentDescriptor descriptor : descriptors) {
+ // This is a temporary hack to map the old ApplicationComponent to the new SingletonComponent.
+ // Technically, this is only needed for backwards compatibility with libraries using the old
+ // processor since new processors should convert to the new SingletonComponent when generating
+ // the metadata class.
+ if (descriptor.component().equals(ClassNames.SINGLETON_COMPONENT)) {
+ builder.put("dagger.hilt.android.components.ApplicationComponent", descriptor);
+ }
+ builder.put(descriptor.component().toString(), descriptor);
+ }
+ return builder.build();
+ }
+
+ // Validate that the @UninstallModules doesn't contain any test modules.
+ private static Dependencies validateModules(Dependencies moduleDeps, Elements elements) {
+ SetMultimap<ClassName, TypeElement> invalidTestModules = HashMultimap.create();
+ moduleDeps.testDeps().entries().stream()
+ .filter(
+ e -> moduleDeps.uninstalledTestDeps().containsEntry(e.getKey().test(), e.getValue()))
+ .forEach(e -> invalidTestModules.put(e.getKey().test(), e.getValue()));
+
+ // Currently we don't have a good way to throw an error for all tests, so we sort (to keep the
+ // error reporting order stable) and then choose the first test.
+ // TODO(bcorso): Consider using ProcessorErrorHandler directly to report all errors at once?
+ Optional<ClassName> invalidTest =
+ invalidTestModules.keySet().stream()
+ .min((test1, test2) -> test1.toString().compareTo(test2.toString()));
+ if (invalidTest.isPresent()) {
+ throw new BadInputException(
+ String.format(
+ "@UninstallModules on test, %s, should not containing test modules, "
+ + "but found: %s",
+ invalidTest.get(),
+ invalidTestModules.get(invalidTest.get()).stream()
+ // Sort modules to keep stable error messages.
+ .sorted((test1, test2) -> test1.toString().compareTo(test2.toString()))
+ .collect(toImmutableList())),
+ elements.getTypeElement(invalidTest.get().toString()));
+ }
+ return moduleDeps;
+ }
+
+ private static ImmutableSet<TypeElement> getUninstalledModules(
+ TypeElement testElement, Elements elements) {
+ ImmutableList<TypeElement> userUninstallModules =
+ Processors.getAnnotationClassValues(
+ elements,
+ Processors.getAnnotationMirror(testElement, ClassNames.IGNORE_MODULES),
+ "value");
+
+ // For pkg-private modules, find the generated wrapper class and uninstall that instead.
+ return userUninstallModules.stream()
+ .map(uninstallModule -> getPublicDependency(uninstallModule, elements))
+ .collect(toImmutableSet());
+ }
+
+ /** Returns the public Hilt wrapper module, or the module itself if its already public. */
+ private static TypeElement getPublicDependency(TypeElement dependency, Elements elements) {
+ return PkgPrivateMetadata.of(elements, dependency, ClassNames.MODULE)
+ .map(metadata -> elements.getTypeElement(metadata.generatedClassName().toString()))
+ .orElse(dependency);
+ }
+
+ /** Returns the top-level elements of the aggregated deps package. */
+ private static ImmutableList<AnnotationMirror> getAggregatedDeps(Elements elements) {
+ PackageElement packageElement = elements.getPackageElement(AGGREGATING_PACKAGE);
+ checkState(
+ packageElement != null,
+ "Couldn't find package %s. Did you mark your @Module classes with @InstallIn annotations?",
+ AGGREGATING_PACKAGE);
+
+ List<? extends Element> aggregatedDepsElements = packageElement.getEnclosedElements();
+ checkState(
+ !aggregatedDepsElements.isEmpty(),
+ "No dependencies found. Did you mark your @Module classes with @InstallIn annotations?");
+
+ ImmutableList.Builder<AnnotationMirror> builder = ImmutableList.builder();
+ for (Element element : aggregatedDepsElements) {
+ ProcessorErrors.checkState(
+ element.getKind() == ElementKind.CLASS,
+ element,
+ "Only classes may be in package %s. Did you add custom code in the package?",
+ AGGREGATING_PACKAGE);
+
+ AnnotationMirror aggregatedDeps =
+ Processors.getAnnotationMirror(element, ClassNames.AGGREGATED_DEPS);
+ ProcessorErrors.checkState(
+ aggregatedDeps != null,
+ element,
+ "Classes in package %s must be annotated with @AggregatedDeps: %s. Found: %s.",
+ AGGREGATING_PACKAGE,
+ element.getSimpleName(),
+ element.getAnnotationMirrors());
+
+ builder.add(aggregatedDeps);
+ }
+ return builder.build();
+ }
+
+ @AutoValue
+ abstract static class AggregatedDepMetadata {
+ static AggregatedDepMetadata create(
+ AnnotationMirror aggregatedDeps,
+ Map<String, ComponentDescriptor> descriptorLookup,
+ Elements elements) {
+ ImmutableMap<String, AnnotationValue> aggregatedDepsValues =
+ Processors.getAnnotationValues(elements, aggregatedDeps);
+
+ return new AutoValue_ComponentDependencies_AggregatedDepMetadata(
+ getTestElement(aggregatedDepsValues.get("test"), elements),
+ getComponents(aggregatedDepsValues.get("components"), descriptorLookup),
+ getDependencyType(
+ aggregatedDepsValues.get("modules"),
+ aggregatedDepsValues.get("entryPoints"),
+ aggregatedDepsValues.get("componentEntryPoints")),
+ getDependency(
+ aggregatedDepsValues.get("modules"),
+ aggregatedDepsValues.get("entryPoints"),
+ aggregatedDepsValues.get("componentEntryPoints"),
+ elements),
+ getReplacedDependencies(aggregatedDepsValues.get("replaces"), elements));
+ }
+
+ enum DependencyType {
+ MODULE,
+ ENTRY_POINT,
+ COMPONENT_ENTRY_POINT
+ }
+
+ abstract Optional<TypeElement> testElement();
+
+ abstract ImmutableList<ComponentDescriptor> componentDescriptors();
+
+ abstract DependencyType dependencyType();
+
+ abstract TypeElement dependency();
+
+ abstract ImmutableSet<TypeElement> replacedDependencies();
+
+ private static Optional<TypeElement> getTestElement(
+ AnnotationValue testValue, Elements elements) {
+ checkNotNull(testValue);
+ String test = AnnotationValues.getString(testValue);
+ return test.isEmpty() ? Optional.empty() : Optional.of(elements.getTypeElement(test));
+ }
+
+ private static ImmutableList<ComponentDescriptor> getComponents(
+ AnnotationValue componentsValue, Map<String, ComponentDescriptor> descriptorLookup) {
+ checkNotNull(componentsValue);
+ ImmutableList<String> componentNames =
+ AnnotationValues.getAnnotationValues(componentsValue).stream()
+ .map(AnnotationValues::getString)
+ .collect(toImmutableList());
+
+ checkState(!componentNames.isEmpty());
+ ImmutableList.Builder<ComponentDescriptor> components = ImmutableList.builder();
+ for (String componentName : componentNames) {
+ checkState(
+ descriptorLookup.containsKey(componentName),
+ "%s is not a valid Component. Did you add or remove code in package %s?",
+ componentName,
+ AGGREGATING_PACKAGE);
+ components.add(descriptorLookup.get(componentName));
+ }
+ return components.build();
+ }
+
+ private static DependencyType getDependencyType(
+ AnnotationValue modulesValue,
+ AnnotationValue entryPointsValue,
+ AnnotationValue componentEntryPointsValue) {
+ checkNotNull(modulesValue);
+ checkNotNull(entryPointsValue);
+ checkNotNull(componentEntryPointsValue);
+
+ ImmutableSet.Builder<DependencyType> dependencyTypes = ImmutableSet.builder();
+ if (!AnnotationValues.getAnnotationValues(modulesValue).isEmpty()) {
+ dependencyTypes.add(DependencyType.MODULE);
+ }
+ if (!AnnotationValues.getAnnotationValues(entryPointsValue).isEmpty()) {
+ dependencyTypes.add(DependencyType.ENTRY_POINT);
+ }
+ if (!AnnotationValues.getAnnotationValues(componentEntryPointsValue).isEmpty()) {
+ dependencyTypes.add(DependencyType.COMPONENT_ENTRY_POINT);
+ }
+ return getOnlyElement(dependencyTypes.build());
+ }
+
+ private static TypeElement getDependency(
+ AnnotationValue modulesValue,
+ AnnotationValue entryPointsValue,
+ AnnotationValue componentEntryPointsValue,
+ Elements elements) {
+ checkNotNull(modulesValue);
+ checkNotNull(entryPointsValue);
+ checkNotNull(componentEntryPointsValue);
+
+ return elements.getTypeElement(
+ AnnotationValues.getString(
+ getOnlyElement(
+ ImmutableList.<AnnotationValue>builder()
+ .addAll(AnnotationValues.getAnnotationValues(modulesValue))
+ .addAll(AnnotationValues.getAnnotationValues(entryPointsValue))
+ .addAll(AnnotationValues.getAnnotationValues(componentEntryPointsValue))
+ .build())));
+ }
+
+ private static ImmutableSet<TypeElement> getReplacedDependencies(
+ AnnotationValue replacedDependenciesValue, Elements elements) {
+ // Allow null values to support libraries using a Hilt version before @TestInstallIn was added
+ return replacedDependenciesValue == null
+ ? ImmutableSet.of()
+ : AnnotationValues.getAnnotationValues(replacedDependenciesValue).stream()
+ .map(AnnotationValues::getString)
+ .map(elements::getTypeElement)
+ .map(replacedDep -> getPublicDependency(replacedDep, elements))
+ .collect(toImmutableSet());
+ }
}
}
diff --git a/java/dagger/hilt/processor/internal/aggregateddeps/PkgPrivateMetadata.java b/java/dagger/hilt/processor/internal/aggregateddeps/PkgPrivateMetadata.java
index ff344a651..66f175151 100644
--- a/java/dagger/hilt/processor/internal/aggregateddeps/PkgPrivateMetadata.java
+++ b/java/dagger/hilt/processor/internal/aggregateddeps/PkgPrivateMetadata.java
@@ -24,35 +24,16 @@ import com.google.auto.value.AutoValue;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.TypeName;
import dagger.hilt.processor.internal.ClassNames;
-import dagger.hilt.processor.internal.KotlinMetadataUtils;
import dagger.hilt.processor.internal.Processors;
import java.util.Optional;
import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
/** PkgPrivateModuleMetadata contains a set of utilities for processing package private modules. */
@AutoValue
-public abstract class PkgPrivateMetadata {
- /** Returns the public Hilt wrapped type or the type itself if it is already public. */
- public static TypeElement publicModule(TypeElement element, Elements elements) {
- return publicDep(element, elements, ClassNames.MODULE);
- }
-
- /** Returns the public Hilt wrapped type or the type itself if it is already public. */
- public static TypeElement publicEarlyEntryPoint(TypeElement element, Elements elements) {
- return publicDep(element, elements, ClassNames.EARLY_ENTRY_POINT);
- }
-
- private static TypeElement publicDep(
- TypeElement element, Elements elements, ClassName annotation) {
- return of(elements, element, annotation)
- .map(PkgPrivateMetadata::generatedClassName)
- .map(ClassName::canonicalName)
- .map(elements::getTypeElement)
- .orElse(element);
- }
-
+abstract class PkgPrivateMetadata {
private static final String PREFIX = "HiltWrapper_";
/** Returns the base class name of the elemenet. */
@@ -82,11 +63,9 @@ public abstract class PkgPrivateMetadata {
* Returns an Optional PkgPrivateMetadata requiring Hilt processing, otherwise returns an empty
* Optional.
*/
- static Optional<PkgPrivateMetadata> of(
- Elements elements, TypeElement element, ClassName annotation) {
+ static Optional<PkgPrivateMetadata> of(Elements elements, Element element, ClassName annotation) {
// If this is a public element no wrapping is needed
- if (effectiveVisibilityOfElement(element) == Visibility.PUBLIC
- && !KotlinMetadataUtils.getMetadataUtil().isVisibilityInternal(element)) {
+ if (effectiveVisibilityOfElement(element) == Visibility.PUBLIC) {
return Optional.empty();
}