• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Dagger Authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package dagger.hilt.processor.internal.root;
18 
19 import static com.google.common.base.Preconditions.checkState;
20 import static dagger.hilt.processor.internal.HiltCompilerOptions.isCrossCompilationRootValidationDisabled;
21 import static dagger.hilt.processor.internal.HiltCompilerOptions.isSharedTestComponentsEnabled;
22 import static dagger.hilt.processor.internal.HiltCompilerOptions.useAggregatingRootProcessor;
23 import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
24 import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.AGGREGATING;
25 import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.DYNAMIC;
26 import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.ISOLATING;
27 
28 import com.google.auto.common.MoreElements;
29 import com.google.auto.service.AutoService;
30 import com.google.common.collect.ImmutableSet;
31 import dagger.hilt.processor.internal.BadInputException;
32 import dagger.hilt.processor.internal.BaseProcessor;
33 import dagger.hilt.processor.internal.aggregateddeps.AggregatedDepsMetadata;
34 import dagger.hilt.processor.internal.aliasof.AliasOfPropagatedDataMetadata;
35 import dagger.hilt.processor.internal.definecomponent.DefineComponentClassesMetadata;
36 import dagger.hilt.processor.internal.earlyentrypoint.AggregatedEarlyEntryPointMetadata;
37 import dagger.hilt.processor.internal.generatesrootinput.GeneratesRootInputs;
38 import dagger.hilt.processor.internal.root.ir.AggregatedDepsIr;
39 import dagger.hilt.processor.internal.root.ir.AggregatedEarlyEntryPointIr;
40 import dagger.hilt.processor.internal.root.ir.AggregatedRootIr;
41 import dagger.hilt.processor.internal.root.ir.AggregatedRootIrValidator;
42 import dagger.hilt.processor.internal.root.ir.AggregatedUninstallModulesIr;
43 import dagger.hilt.processor.internal.root.ir.AliasOfPropagatedDataIr;
44 import dagger.hilt.processor.internal.root.ir.ComponentTreeDepsIr;
45 import dagger.hilt.processor.internal.root.ir.ComponentTreeDepsIrCreator;
46 import dagger.hilt.processor.internal.root.ir.DefineComponentClassesIr;
47 import dagger.hilt.processor.internal.root.ir.InvalidRootsException;
48 import dagger.hilt.processor.internal.root.ir.ProcessedRootSentinelIr;
49 import dagger.hilt.processor.internal.uninstallmodules.AggregatedUninstallModulesMetadata;
50 import java.util.Arrays;
51 import java.util.Set;
52 import javax.annotation.processing.ProcessingEnvironment;
53 import javax.annotation.processing.Processor;
54 import javax.annotation.processing.RoundEnvironment;
55 import javax.lang.model.element.Element;
56 import javax.lang.model.element.TypeElement;
57 import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
58 
59 /** Processor that outputs dagger components based on transitive build deps. */
60 @IncrementalAnnotationProcessor(DYNAMIC)
61 @AutoService(Processor.class)
62 public final class RootProcessor extends BaseProcessor {
63 
64   private boolean processed;
65   private GeneratesRootInputs generatesRootInputs;
66 
67   @Override
init(ProcessingEnvironment processingEnvironment)68   public synchronized void init(ProcessingEnvironment processingEnvironment) {
69     super.init(processingEnvironment);
70     generatesRootInputs = new GeneratesRootInputs(processingEnvironment);
71   }
72 
73   @Override
additionalProcessingOptions()74   public ImmutableSet<String> additionalProcessingOptions() {
75     return useAggregatingRootProcessor(getProcessingEnv())
76         ? ImmutableSet.of(AGGREGATING.getProcessorOption())
77         : ImmutableSet.of(ISOLATING.getProcessorOption());
78   }
79 
80   @Override
getSupportedAnnotationTypes()81   public ImmutableSet<String> getSupportedAnnotationTypes() {
82     return ImmutableSet.<String>builder()
83         .addAll(
84             Arrays.stream(RootType.values())
85                 .map(rootType -> rootType.className().toString())
86                 .collect(toImmutableSet()))
87         .build();
88   }
89 
90   @Override
processEach(TypeElement annotation, Element element)91   public void processEach(TypeElement annotation, Element element) throws Exception {
92     TypeElement rootElement = MoreElements.asType(element);
93     // TODO(bcorso): Move this logic into a separate isolating processor to avoid regenerating it
94     // for unrelated changes in Gradle.
95     RootType rootType = RootType.of(rootElement);
96     if (rootType.isTestRoot()) {
97       new TestInjectorGenerator(
98               getProcessingEnv(), TestRootMetadata.of(getProcessingEnv(), rootElement))
99           .generate();
100     }
101     TypeElement originatingRootElement =
102         Root.create(rootElement, getProcessingEnv()).originatingRootElement();
103     new AggregatedRootGenerator(rootElement, originatingRootElement, annotation, getProcessingEnv())
104         .generate();
105   }
106 
107   @Override
postRoundProcess(RoundEnvironment roundEnv)108   public void postRoundProcess(RoundEnvironment roundEnv) throws Exception {
109     if (!useAggregatingRootProcessor(getProcessingEnv())) {
110       return;
111     }
112     Set<Element> newElements = generatesRootInputs.getElementsToWaitFor(roundEnv);
113     if (processed) {
114       checkState(
115           newElements.isEmpty(),
116           "Found extra modules after compilation: %s\n"
117               + "(If you are adding an annotation processor that generates root input for hilt, "
118               + "the annotation must be annotated with @dagger.hilt.GeneratesRootInput.\n)",
119           newElements);
120     } else if (newElements.isEmpty()) {
121       processed = true;
122 
123       ImmutableSet<AggregatedRootIr> rootsToProcess = rootsToProcess();
124       if (rootsToProcess.isEmpty()) {
125         return;
126       }
127 
128       // Generate an @ComponentTreeDeps for each unique component tree.
129       ComponentTreeDepsGenerator componentTreeDepsGenerator =
130           new ComponentTreeDepsGenerator(getProcessingEnv());
131       for (ComponentTreeDepsMetadata metadata : componentTreeDepsMetadatas(rootsToProcess)) {
132         componentTreeDepsGenerator.generate(metadata);
133       }
134 
135       // Generate a sentinel for all processed roots.
136       for (AggregatedRootIr ir : rootsToProcess) {
137         TypeElement rootElement = getElementUtils().getTypeElement(ir.getRoot().canonicalName());
138         new ProcessedRootSentinelGenerator(rootElement, getProcessingEnv()).generate();
139       }
140     }
141   }
142 
rootsToProcess()143   private ImmutableSet<AggregatedRootIr> rootsToProcess() {
144     ImmutableSet<ProcessedRootSentinelIr> processedRoots =
145         ProcessedRootSentinelMetadata.from(getElementUtils()).stream()
146             .map(ProcessedRootSentinelMetadata::toIr)
147             .collect(toImmutableSet());
148     ImmutableSet<AggregatedRootIr> aggregatedRoots =
149         AggregatedRootMetadata.from(processingEnv).stream()
150             .map(AggregatedRootMetadata::toIr)
151             .collect(toImmutableSet());
152 
153     boolean isCrossCompilationRootValidationDisabled =
154         isCrossCompilationRootValidationDisabled(
155             aggregatedRoots.stream()
156                 .map(ir -> getElementUtils().getTypeElement(ir.getRoot().canonicalName()))
157                 .collect(toImmutableSet()),
158             processingEnv);
159     try {
160       return ImmutableSet.copyOf(
161           AggregatedRootIrValidator.rootsToProcess(
162               isCrossCompilationRootValidationDisabled, processedRoots, aggregatedRoots));
163     } catch (InvalidRootsException ex) {
164       throw new BadInputException(ex.getMessage());
165     }
166   }
167 
componentTreeDepsMetadatas( ImmutableSet<AggregatedRootIr> aggregatedRoots)168   private ImmutableSet<ComponentTreeDepsMetadata> componentTreeDepsMetadatas(
169       ImmutableSet<AggregatedRootIr> aggregatedRoots) {
170     ImmutableSet<DefineComponentClassesIr> defineComponentDeps =
171         DefineComponentClassesMetadata.from(getElementUtils()).stream()
172             .map(DefineComponentClassesMetadata::toIr)
173             .collect(toImmutableSet());
174     ImmutableSet<AliasOfPropagatedDataIr> aliasOfDeps =
175         AliasOfPropagatedDataMetadata.from(getElementUtils()).stream()
176             .map(AliasOfPropagatedDataMetadata::toIr)
177             .collect(toImmutableSet());
178     ImmutableSet<AggregatedDepsIr> aggregatedDeps =
179         AggregatedDepsMetadata.from(getElementUtils()).stream()
180             .map(AggregatedDepsMetadata::toIr)
181             .collect(toImmutableSet());
182     ImmutableSet<AggregatedUninstallModulesIr> aggregatedUninstallModulesDeps =
183         AggregatedUninstallModulesMetadata.from(getElementUtils()).stream()
184             .map(AggregatedUninstallModulesMetadata::toIr)
185             .collect(toImmutableSet());
186     ImmutableSet<AggregatedEarlyEntryPointIr> aggregatedEarlyEntryPointDeps =
187         AggregatedEarlyEntryPointMetadata.from(getElementUtils()).stream()
188             .map(AggregatedEarlyEntryPointMetadata::toIr)
189             .collect(toImmutableSet());
190 
191     // We should be guaranteed that there are no mixed roots, so check if this is prod or test.
192     boolean isTest = aggregatedRoots.stream().anyMatch(AggregatedRootIr::isTestRoot);
193     Set<ComponentTreeDepsIr> componentTreeDeps =
194         ComponentTreeDepsIrCreator.components(
195             isTest,
196             isSharedTestComponentsEnabled(processingEnv),
197             aggregatedRoots,
198             defineComponentDeps,
199             aliasOfDeps,
200             aggregatedDeps,
201             aggregatedUninstallModulesDeps,
202             aggregatedEarlyEntryPointDeps);
203     return componentTreeDeps.stream()
204         .map(it -> ComponentTreeDepsMetadata.from(it, getElementUtils()))
205         .collect(toImmutableSet());
206   }
207 }
208