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