dagger.hilt.processor.internal.root.RootProcessor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hilt-android-compiler Show documentation
Show all versions of hilt-android-compiler Show documentation
A fast dependency injector for Android and Java.
/*
* Copyright (C) 2019 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.root;
import static com.google.common.base.Preconditions.checkState;
import static dagger.hilt.processor.internal.HiltCompilerOptions.isCrossCompilationRootValidationDisabled;
import static dagger.hilt.processor.internal.HiltCompilerOptions.isSharedTestComponentsEnabled;
import static dagger.hilt.processor.internal.HiltCompilerOptions.useAggregatingRootProcessor;
import static dagger.internal.codegen.extension.DaggerStreams.toImmutableSet;
import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.AGGREGATING;
import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.DYNAMIC;
import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.ISOLATING;
import com.google.auto.common.MoreElements;
import com.google.auto.service.AutoService;
import com.google.common.collect.ImmutableSet;
import dagger.hilt.processor.internal.BadInputException;
import dagger.hilt.processor.internal.BaseProcessor;
import dagger.hilt.processor.internal.aggregateddeps.AggregatedDepsMetadata;
import dagger.hilt.processor.internal.aliasof.AliasOfPropagatedDataMetadata;
import dagger.hilt.processor.internal.definecomponent.DefineComponentClassesMetadata;
import dagger.hilt.processor.internal.earlyentrypoint.AggregatedEarlyEntryPointMetadata;
import dagger.hilt.processor.internal.generatesrootinput.GeneratesRootInputs;
import dagger.hilt.processor.internal.root.ir.AggregatedDepsIr;
import dagger.hilt.processor.internal.root.ir.AggregatedEarlyEntryPointIr;
import dagger.hilt.processor.internal.root.ir.AggregatedRootIr;
import dagger.hilt.processor.internal.root.ir.AggregatedRootIrValidator;
import dagger.hilt.processor.internal.root.ir.AggregatedUninstallModulesIr;
import dagger.hilt.processor.internal.root.ir.AliasOfPropagatedDataIr;
import dagger.hilt.processor.internal.root.ir.ComponentTreeDepsIr;
import dagger.hilt.processor.internal.root.ir.ComponentTreeDepsIrCreator;
import dagger.hilt.processor.internal.root.ir.DefineComponentClassesIr;
import dagger.hilt.processor.internal.root.ir.InvalidRootsException;
import dagger.hilt.processor.internal.root.ir.ProcessedRootSentinelIr;
import dagger.hilt.processor.internal.uninstallmodules.AggregatedUninstallModulesMetadata;
import java.util.Arrays;
import java.util.Set;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
/** Processor that outputs dagger components based on transitive build deps. */
@IncrementalAnnotationProcessor(DYNAMIC)
@AutoService(Processor.class)
public final class RootProcessor extends BaseProcessor {
private boolean processed;
private GeneratesRootInputs generatesRootInputs;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
generatesRootInputs = new GeneratesRootInputs(processingEnvironment);
}
@Override
public ImmutableSet additionalProcessingOptions() {
return useAggregatingRootProcessor(getProcessingEnv())
? ImmutableSet.of(AGGREGATING.getProcessorOption())
: ImmutableSet.of(ISOLATING.getProcessorOption());
}
@Override
public ImmutableSet getSupportedAnnotationTypes() {
return ImmutableSet.builder()
.addAll(
Arrays.stream(RootType.values())
.map(rootType -> rootType.className().toString())
.collect(toImmutableSet()))
.build();
}
@Override
public void processEach(TypeElement annotation, Element element) throws Exception {
TypeElement rootElement = MoreElements.asType(element);
// TODO(bcorso): Move this logic into a separate isolating processor to avoid regenerating it
// for unrelated changes in Gradle.
RootType rootType = RootType.of(rootElement);
if (rootType.isTestRoot()) {
new TestInjectorGenerator(
getProcessingEnv(), TestRootMetadata.of(getProcessingEnv(), rootElement))
.generate();
}
TypeElement originatingRootElement =
Root.create(rootElement, getProcessingEnv()).originatingRootElement();
new AggregatedRootGenerator(rootElement, originatingRootElement, annotation, getProcessingEnv())
.generate();
}
@Override
public void postRoundProcess(RoundEnvironment roundEnv) throws Exception {
if (!useAggregatingRootProcessor(getProcessingEnv())) {
return;
}
Set newElements = generatesRootInputs.getElementsToWaitFor(roundEnv);
if (processed) {
checkState(
newElements.isEmpty(),
"Found extra modules after compilation: %s\n"
+ "(If you are adding an annotation processor that generates root input for hilt, "
+ "the annotation must be annotated with @dagger.hilt.GeneratesRootInput.\n)",
newElements);
} else if (newElements.isEmpty()) {
processed = true;
ImmutableSet rootsToProcess = rootsToProcess();
if (rootsToProcess.isEmpty()) {
return;
}
// Generate an @ComponentTreeDeps for each unique component tree.
ComponentTreeDepsGenerator componentTreeDepsGenerator =
new ComponentTreeDepsGenerator(getProcessingEnv());
for (ComponentTreeDepsMetadata metadata : componentTreeDepsMetadatas(rootsToProcess)) {
componentTreeDepsGenerator.generate(metadata);
}
// Generate a sentinel for all processed roots.
for (AggregatedRootIr ir : rootsToProcess) {
TypeElement rootElement = getElementUtils().getTypeElement(ir.getRoot().canonicalName());
new ProcessedRootSentinelGenerator(rootElement, getProcessingEnv()).generate();
}
}
}
private ImmutableSet rootsToProcess() {
ImmutableSet processedRoots =
ProcessedRootSentinelMetadata.from(getElementUtils()).stream()
.map(ProcessedRootSentinelMetadata::toIr)
.collect(toImmutableSet());
ImmutableSet aggregatedRoots =
AggregatedRootMetadata.from(processingEnv).stream()
.map(AggregatedRootMetadata::toIr)
.collect(toImmutableSet());
boolean isCrossCompilationRootValidationDisabled =
isCrossCompilationRootValidationDisabled(
aggregatedRoots.stream()
.map(ir -> getElementUtils().getTypeElement(ir.getRoot().canonicalName()))
.collect(toImmutableSet()),
processingEnv);
try {
return ImmutableSet.copyOf(
AggregatedRootIrValidator.rootsToProcess(
isCrossCompilationRootValidationDisabled, processedRoots, aggregatedRoots));
} catch (InvalidRootsException ex) {
throw new BadInputException(ex.getMessage());
}
}
private ImmutableSet componentTreeDepsMetadatas(
ImmutableSet aggregatedRoots) {
ImmutableSet defineComponentDeps =
DefineComponentClassesMetadata.from(getElementUtils()).stream()
.map(DefineComponentClassesMetadata::toIr)
.collect(toImmutableSet());
ImmutableSet aliasOfDeps =
AliasOfPropagatedDataMetadata.from(getElementUtils()).stream()
.map(AliasOfPropagatedDataMetadata::toIr)
.collect(toImmutableSet());
ImmutableSet aggregatedDeps =
AggregatedDepsMetadata.from(getElementUtils()).stream()
.map(AggregatedDepsMetadata::toIr)
.collect(toImmutableSet());
ImmutableSet aggregatedUninstallModulesDeps =
AggregatedUninstallModulesMetadata.from(getElementUtils()).stream()
.map(AggregatedUninstallModulesMetadata::toIr)
.collect(toImmutableSet());
ImmutableSet aggregatedEarlyEntryPointDeps =
AggregatedEarlyEntryPointMetadata.from(getElementUtils()).stream()
.map(AggregatedEarlyEntryPointMetadata::toIr)
.collect(toImmutableSet());
// We should be guaranteed that there are no mixed roots, so check if this is prod or test.
boolean isTest = aggregatedRoots.stream().anyMatch(AggregatedRootIr::isTestRoot);
Set componentTreeDeps =
ComponentTreeDepsIrCreator.components(
isTest,
isSharedTestComponentsEnabled(processingEnv),
aggregatedRoots,
defineComponentDeps,
aliasOfDeps,
aggregatedDeps,
aggregatedUninstallModulesDeps,
aggregatedEarlyEntryPointDeps);
return componentTreeDeps.stream()
.map(it -> ComponentTreeDepsMetadata.from(it, getElementUtils()))
.collect(toImmutableSet());
}
}