Skip to content

Commit

Permalink
Add AggregatedEarlyEntryPointMetadata to aggregate information about …
Browse files Browse the repository at this point in the history
…@EarlyEntryPoints.

Note: this is a breaking change in the rare case that an app that upgrades Hilt is relying on an @EarlyEntryPoint usage in a library that is not also updated. To fix, all usages of @EarlyEntryPoint must be using the upgraded version of Hilt or later. Otherwise, they may miss the @EarlyEntryPoint usage and fail at runtime during a test.
RELNOTES=This is a breaking change in the rare case that an app that upgrades Hilt is relying on an @EarlyEntryPoint usage in a library that is not also updated. To fix, all usages of @EarlyEntryPoint must be using the upgraded version of Hilt or later. Otherwise, they may miss the @EarlyEntryPoint usage and fail at runtime during a test.
PiperOrigin-RevId: 368322897
  • Loading branch information
bcorso authored and Dagger Team committed Apr 13, 2021
1 parent 41c4e04 commit 27a2827
Show file tree
Hide file tree
Showing 19 changed files with 380 additions and 34 deletions.
11 changes: 8 additions & 3 deletions java/dagger/hilt/android/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,19 @@ android_library(
)

android_library(
name = "early_test_entry_point",
name = "early_entry_point",
srcs = [
"EarlyEntryPoint.java",
"EarlyEntryPoints.java",
],
exported_plugins = [
"//java/dagger/hilt/processor/internal/aggregateddeps:plugin",
"//java/dagger/hilt/processor/internal/earlyentrypoint:processor",
],
proguard_specs = ["proguard-rules.pro"],
exports = [
"//java/dagger/hilt/android/internal/earlyentrypoint",
],
deps = [
":package_info",
"//:dagger_with_compiler",
Expand All @@ -151,7 +155,7 @@ android_library(
tags = ["maven_coordinates=com.google.dagger:hilt-android:" + POM_VERSION_BETA],
exports = [
":android_entry_point",
":early_test_entry_point",
":early_entry_point",
":entry_point_accessors",
":hilt_android_app",
":package_info",
Expand All @@ -171,13 +175,14 @@ gen_maven_artifact(
"//java/dagger/hilt/android:activity_retained_lifecycle",
"//java/dagger/hilt/android:android_entry_point",
"//java/dagger/hilt/android:hilt_android_app",
"//java/dagger/hilt/android:early_test_entry_point",
"//java/dagger/hilt/android:early_entry_point",
"//java/dagger/hilt/android:package_info",
"//java/dagger/hilt/android/components",
"//java/dagger/hilt/android/components:view_model_component",
"//java/dagger/hilt/android/components:package_info",
"//java/dagger/hilt/android/internal",
"//java/dagger/hilt/android/internal/builders",
"//java/dagger/hilt/android/internal/earlyentrypoint",
"//java/dagger/hilt/android/internal/lifecycle",
"//java/dagger/hilt/android/internal/managers",
"//java/dagger/hilt/android/internal/managers:component_supplier",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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.android.internal.earlyentrypoint;

import static java.lang.annotation.RetentionPolicy.CLASS;

import java.lang.annotation.Retention;

/** Holds aggregated data about {@link dagger.hilt.android.EarlyEntryPoint} elements. */
@Retention(CLASS)
public @interface AggregatedEarlyEntryPoint {

/** Returns the entry point annotated with {@link dagger.hilt.android.EarlyEntryPoint}. */
String earlyEntryPoint();
}
23 changes: 23 additions & 0 deletions java/dagger/hilt/android/internal/earlyentrypoint/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# 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.

# Description:
# A processor that aggregates metadata about Hilt @EarlyEntryPoint annotations

package(default_visibility = ["//:src"])

java_library(
name = "earlyentrypoint",
srcs = ["AggregatedEarlyEntryPoint.java"],
)
2 changes: 2 additions & 0 deletions java/dagger/hilt/android/processor/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ gen_maven_artifact(
"//java/dagger/hilt/processor/internal/aliasof:processor_lib",
"//java/dagger/hilt/processor/internal/definecomponent:define_components",
"//java/dagger/hilt/processor/internal/definecomponent:processor_lib",
"//java/dagger/hilt/processor/internal/earlyentrypoint:aggregated_early_entry_point_metadata",
"//java/dagger/hilt/processor/internal/earlyentrypoint:processor_lib",
"//java/dagger/hilt/processor/internal/generatesrootinput:generates_root_inputs",
"//java/dagger/hilt/processor/internal/generatesrootinput:processor_lib",
"//java/dagger/hilt/processor/internal/originatingelement:processor_lib",
Expand Down
3 changes: 3 additions & 0 deletions java/dagger/hilt/processor/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ java_library(
"//java/dagger/hilt/processor/internal/aggregateddeps:processor_lib",
"//java/dagger/hilt/processor/internal/aliasof:processor_lib",
"//java/dagger/hilt/processor/internal/definecomponent:processor_lib",
"//java/dagger/hilt/processor/internal/earlyentrypoint:processor_lib",
"//java/dagger/hilt/processor/internal/generatesrootinput:processor_lib",
"//java/dagger/hilt/processor/internal/originatingelement:processor_lib",
"//java/dagger/hilt/processor/internal/root:processor_lib",
Expand Down Expand Up @@ -80,6 +81,8 @@ gen_maven_artifact(
"//java/dagger/hilt/processor/internal/aliasof:processor_lib",
"//java/dagger/hilt/processor/internal/definecomponent:define_components",
"//java/dagger/hilt/processor/internal/definecomponent:processor_lib",
"//java/dagger/hilt/processor/internal/earlyentrypoint:aggregated_early_entry_point_metadata",
"//java/dagger/hilt/processor/internal/earlyentrypoint:processor_lib",
"//java/dagger/hilt/processor/internal/generatesrootinput:generates_root_inputs",
"//java/dagger/hilt/processor/internal/generatesrootinput:processor_lib",
"//java/dagger/hilt/processor/internal/originatingelement:processor_lib",
Expand Down
8 changes: 6 additions & 2 deletions java/dagger/hilt/processor/internal/ClassNames.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@

/** Holder for commonly used class names. */
public final class ClassNames {
public static final String AGGREGATED_EARLY_ENTRY_POINT_PACKAGE =
"dagger.hilt.android.internal.earlyentrypoint.codegen";
public static final ClassName AGGREGATED_EARLY_ENTRY_POINT =
get("dagger.hilt.android.internal.earlyentrypoint", "AggregatedEarlyEntryPoint");
public static final ClassName EARLY_ENTRY_POINT = get("dagger.hilt.android", "EarlyEntryPoint");

public static final String AGGREGATED_UNINSTALL_MODULES_PACKAGE =
"dagger.hilt.android.internal.uninstallmodules.codegen";
public static final ClassName AGGREGATED_UNINSTALL_MODULES =
Expand Down Expand Up @@ -120,8 +126,6 @@ public final class ClassNames {
public static final ClassName APPLICATION = get("android.app", "Application");
public static final ClassName MULTI_DEX_APPLICATION =
get("androidx.multidex", "MultiDexApplication");
public static final ClassName EARLY_ENTRY_POINT =
get("dagger.hilt.android", "EarlyEntryPoint");
public static final ClassName ANDROID_ENTRY_POINT =
get("dagger.hilt.android", "AndroidEntryPoint");
public static final ClassName HILT_ANDROID_APP =
Expand Down
1 change: 1 addition & 0 deletions java/dagger/hilt/processor/internal/aggregateddeps/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ java_library(
"//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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,15 @@
package dagger.hilt.processor.internal.aggregateddeps;

import static com.google.common.base.Preconditions.checkState;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableMap;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;

import com.google.auto.value.AutoValue;
import com.google.auto.value.extension.memoized.Memoized;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.squareup.javapoet.ClassName;
import dagger.hilt.processor.internal.ClassNames;
import dagger.hilt.processor.internal.ComponentDescriptor;
import dagger.hilt.processor.internal.Processors;
import dagger.hilt.processor.internal.earlyentrypoint.AggregatedEarlyEntryPointMetadata;
import dagger.hilt.processor.internal.uninstallmodules.AggregatedUninstallModulesMetadata;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
Expand All @@ -49,11 +46,12 @@ private static Builder builder() {
/** 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}. */
@Memoized
public boolean hasEarlySingletonEntryPoints() {
return entryPoints().getGlobalSingletonDeps().stream()
.anyMatch(entryPoint -> Processors.hasAnnotation(entryPoint, ClassNames.EARLY_ENTRY_POINT));
public boolean hasEarlyEntryPoints() {
return !earlyEntryPoints().isEmpty();
}

/**
Expand All @@ -73,6 +71,8 @@ abstract static class Builder {

abstract Dependencies.Builder componentEntryPointsBuilder();

abstract ImmutableSet.Builder<ClassName> earlyEntryPointsBuilder();

abstract ComponentDependencies build();
}

Expand Down Expand Up @@ -181,9 +181,8 @@ abstract static class Builder {
*/
public static ComponentDependencies from(
ImmutableSet<ComponentDescriptor> descriptors, Elements elements) {
ImmutableMap<ClassName, ComponentDescriptor> descriptorLookup =
descriptors.stream()
.collect(toImmutableMap(ComponentDescriptor::component, descriptor -> descriptor));
ImmutableSet<ClassName> componentNames =
descriptors.stream().map(ComponentDescriptor::component).collect(toImmutableSet());
ComponentDependencies.Builder componentDependencies = ComponentDependencies.builder();
for (AggregatedDepsMetadata metadata : AggregatedDepsMetadata.from(elements)) {
Dependencies.Builder builder = null;
Expand All @@ -201,9 +200,7 @@ public static ComponentDependencies from(
for (TypeElement componentElement : metadata.componentElements()) {
ClassName componentName = ClassName.get(componentElement);
checkState(
descriptorLookup.containsKey(componentName),
"%s is not a valid Component.",
componentName);
componentNames.contains(componentName), "%s is not a valid Component.", componentName);
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());
Expand Down Expand Up @@ -234,6 +231,12 @@ public static ComponentDependencies from(
.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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,19 @@
/** PkgPrivateModuleMetadata contains a set of utilities for processing package private modules. */
@AutoValue
public abstract class PkgPrivateMetadata {
/** Returns the public Hilt wrapper module, or the module itself if its already public. */
/** Returns the public Hilt wrapped type or the type itself if it is already public. */
public static TypeElement publicModule(TypeElement element, Elements elements) {
return of(elements, element, ClassNames.MODULE)
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)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* 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.earlyentrypoint;

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.ClassNames;
import dagger.hilt.processor.internal.Processors;
import java.io.IOException;
import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.TypeElement;

/**
* Generates an {@link dagger.hilt.android.internal.earlyentrypoint.AggregatedEarlyEntryPoint}
* annotation.
*/
final class AggregatedEarlyEntryPointGenerator {

private final ProcessingEnvironment env;
private final TypeElement earlyEntryPoint;

AggregatedEarlyEntryPointGenerator(TypeElement earlyEntryPoint, ProcessingEnvironment env) {
this.earlyEntryPoint = earlyEntryPoint;
this.env = env;
}

void generate() throws IOException {
ClassName name =
ClassName.get(
ClassNames.AGGREGATED_EARLY_ENTRY_POINT_PACKAGE,
Processors.getFullEnclosedName(earlyEntryPoint) + "_AggregatedEarlyEntryPoint");

TypeSpec.Builder builder =
TypeSpec.classBuilder(name)
.addOriginatingElement(earlyEntryPoint)
.addAnnotation(
AnnotationSpec.builder(ClassNames.AGGREGATED_EARLY_ENTRY_POINT)
.addMember("earlyEntryPoint", "$S", earlyEntryPoint.getQualifiedName())
.build());

Processors.addGeneratedAnnotation(builder, env, getClass());

JavaFile.builder(name.packageName(), builder.build()).build().writeTo(env.getFiler());
}
}

0 comments on commit 27a2827

Please sign in to comment.