/*
* Copyright (C) 2014 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.internal.codegen;
import static net.ltgt.gradle.incap.IncrementalAnnotationProcessorType.DYNAMIC;
import com.google.auto.common.BasicAnnotationProcessor;
import com.google.auto.service.AutoService;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import com.google.errorprone.annotations.CheckReturnValue;
import dagger.BindsInstance;
import dagger.Component;
import dagger.Module;
import dagger.Provides;
import dagger.internal.codegen.SpiModule.TestingPlugins;
import dagger.spi.BindingGraphPlugin;
import java.util.Arrays;
import java.util.Optional;
import java.util.Set;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.lang.model.SourceVersion;
import net.ltgt.gradle.incap.IncrementalAnnotationProcessor;
/**
* The annotation processor responsible for generating the classes that drive the Dagger 2.0
* implementation.
*
*
TODO(gak): give this some better documentation
*/
@IncrementalAnnotationProcessor(DYNAMIC)
@AutoService(Processor.class)
public class ComponentProcessor extends BasicAnnotationProcessor {
private final Optional> testingPlugins;
@Inject InjectBindingRegistry injectBindingRegistry;
@Inject SourceFileGenerator factoryGenerator;
@Inject SourceFileGenerator membersInjectorGenerator;
@Inject ImmutableList processingSteps;
@Inject BindingGraphPlugins bindingGraphPlugins;
@Inject CompilerOptions compilerOptions;
@Inject DaggerStatisticsCollector statisticsCollector;
@Inject Set clearableCaches;
public ComponentProcessor() {
this.testingPlugins = Optional.empty();
}
private ComponentProcessor(Iterable testingPlugins) {
this.testingPlugins = Optional.of(ImmutableSet.copyOf(testingPlugins));
}
/**
* Creates a component processor that uses given {@link BindingGraphPlugin}s instead of loading
* them from a {@link java.util.ServiceLoader}.
*/
@VisibleForTesting
public static ComponentProcessor forTesting(BindingGraphPlugin... testingPlugins) {
return forTesting(Arrays.asList(testingPlugins));
}
/**
* Creates a component processor that uses given {@link BindingGraphPlugin}s instead of loading
* them from a {@link java.util.ServiceLoader}.
*/
@VisibleForTesting
public static ComponentProcessor forTesting(Iterable testingPlugins) {
return new ComponentProcessor(testingPlugins);
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@Override
public Set getSupportedOptions() {
ImmutableSet.Builder options = ImmutableSet.builder();
options.addAll(ProcessingEnvironmentCompilerOptions.supportedOptions());
options.addAll(bindingGraphPlugins.allSupportedOptions());
if (compilerOptions.useGradleIncrementalProcessing()) {
options.add("org.gradle.annotation.processing.isolating");
}
return options.build();
}
@Override
protected Iterable extends ProcessingStep> initSteps() {
ProcessorComponent.builder()
.processingEnvironmentModule(new ProcessingEnvironmentModule(processingEnv))
.testingPlugins(testingPlugins)
.build()
.inject(this);
statisticsCollector.processingStarted();
bindingGraphPlugins.initializePlugins();
return Iterables.transform(
processingSteps,
step -> new DaggerStatisticsCollectingProcessingStep(step, statisticsCollector));
}
@Singleton
@Component(
modules = {
BindingGraphValidationModule.class,
BindingMethodValidatorsModule.class,
InjectBindingRegistryModule.class,
ProcessingEnvironmentModule.class,
ProcessingRoundCacheModule.class,
ProcessingStepsModule.class,
SourceFileGeneratorsModule.class,
SpiModule.class,
SystemComponentsModule.class,
TopLevelImplementationComponent.InstallationModule.class,
})
interface ProcessorComponent {
void inject(ComponentProcessor processor);
static Builder builder() {
return DaggerComponentProcessor_ProcessorComponent.builder();
}
@CanIgnoreReturnValue
@Component.Builder
interface Builder {
Builder processingEnvironmentModule(ProcessingEnvironmentModule module);
@BindsInstance
Builder testingPlugins(
@TestingPlugins Optional> testingPlugins);
@CheckReturnValue ProcessorComponent build();
}
}
@Module
interface ProcessingStepsModule {
@Provides
static ImmutableList processingSteps(
MapKeyProcessingStep mapKeyProcessingStep,
InjectProcessingStep injectProcessingStep,
MonitoringModuleProcessingStep monitoringModuleProcessingStep,
MultibindingAnnotationsProcessingStep multibindingAnnotationsProcessingStep,
BindsInstanceProcessingStep bindsInstanceProcessingStep,
ModuleProcessingStep moduleProcessingStep,
ComponentProcessingStep componentProcessingStep,
ComponentHjarProcessingStep componentHjarProcessingStep,
BindingMethodProcessingStep bindingMethodProcessingStep,
CompilerOptions compilerOptions) {
return ImmutableList.of(
mapKeyProcessingStep,
injectProcessingStep,
monitoringModuleProcessingStep,
multibindingAnnotationsProcessingStep,
bindsInstanceProcessingStep,
moduleProcessingStep,
compilerOptions.headerCompilation()
// Ahead Of Time subcomponents use the regular hjar filtering in
// HjarSourceFileGenerator since they must retain protected implementation methods
// between subcomponents
&& !compilerOptions.aheadOfTimeSubcomponents()
? componentHjarProcessingStep
: componentProcessingStep,
bindingMethodProcessingStep);
}
}
@Override
protected void postRound(RoundEnvironment roundEnv) {
statisticsCollector.roundFinished();
if (roundEnv.processingOver()) {
statisticsCollector.processingStopped();
} else {
try {
injectBindingRegistry.generateSourcesForRequiredBindings(
factoryGenerator, membersInjectorGenerator);
} catch (SourceFileGenerationException e) {
e.printMessageTo(processingEnv.getMessager());
}
}
clearableCaches.forEach(ClearableCache::clearCache);
}
}