• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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;
18 
19 import static androidx.room.compiler.processing.compat.XConverters.getProcessingEnv;
20 import static com.google.common.base.Ascii.toUpperCase;
21 
22 import androidx.room.compiler.processing.XProcessingEnv;
23 import androidx.room.compiler.processing.XTypeElement;
24 import androidx.room.compiler.processing.compat.XConverters;
25 import com.google.common.collect.ImmutableSet;
26 import dagger.hilt.processor.internal.optionvalues.BooleanValue;
27 import dagger.hilt.processor.internal.optionvalues.GradleProjectType;
28 import dagger.internal.codegen.extension.DaggerStreams;
29 import java.util.Arrays;
30 import java.util.HashSet;
31 import java.util.Locale;
32 import java.util.Set;
33 import javax.tools.Diagnostic.Kind;
34 
35 /** Hilt annotation processor options. */
36 // TODO(danysantiago): Consider consolidating with Dagger compiler options logic.
37 public final class HiltCompilerOptions {
38 
39   /**
40    * Returns {@code true} if the superclass validation is disabled for {@link
41    * dagger.hilt.android.AndroidEntryPoint}-annotated classes.
42    *
43    * <p>This flag is for internal use only! The superclass validation checks that the super class is
44    * a generated {@code Hilt_} class. This flag is disabled by the Hilt Gradle plugin to enable
45    * bytecode transformation to change the superclass.
46    */
isAndroidSuperClassValidationDisabled(XTypeElement element)47   public static boolean isAndroidSuperClassValidationDisabled(XTypeElement element) {
48     EnumOption<BooleanValue> option = DISABLE_ANDROID_SUPERCLASS_VALIDATION;
49     XProcessingEnv processorEnv = getProcessingEnv(element);
50     return option.get(processorEnv) == BooleanValue.TRUE;
51   }
52 
53   /**
54    * Returns {@code true} if cross-compilation root validation is disabled.
55    *
56    * <p>This flag should rarely be needed, but may be used for legacy/migration purposes if tests
57    * require the use of {@link dagger.hilt.android.HiltAndroidApp} rather than {@link
58    * dagger.hilt.android.testing.HiltAndroidTest}.
59    *
60    * <p>Note that Hilt still does validation within a single compilation unit. In particular, a
61    * compilation unit that contains a {@code HiltAndroidApp} usage cannot have other {@code
62    * HiltAndroidApp} or {@code HiltAndroidTest} usages in the same compilation unit.
63    */
isCrossCompilationRootValidationDisabled( ImmutableSet<XTypeElement> rootElements, XProcessingEnv env)64   public static boolean isCrossCompilationRootValidationDisabled(
65       ImmutableSet<XTypeElement> rootElements, XProcessingEnv env) {
66     EnumOption<BooleanValue> option = DISABLE_CROSS_COMPILATION_ROOT_VALIDATION;
67     return option.get(env) == BooleanValue.TRUE;
68   }
69 
70   /** Returns {@code true} if the check for {@link dagger.hilt.InstallIn} is disabled. */
isModuleInstallInCheckDisabled(XProcessingEnv env)71   public static boolean isModuleInstallInCheckDisabled(XProcessingEnv env) {
72     return DISABLE_MODULES_HAVE_INSTALL_IN_CHECK.get(env) == BooleanValue.TRUE;
73   }
74 
75   /**
76    * Returns {@code true} of unit tests should try to share generated components, rather than using
77    * separate generated components per Hilt test root.
78    *
79    * <p>Tests that provide their own test bindings (e.g. using {@link
80    * dagger.hilt.android.testing.BindValue} or a test {@link dagger.Module}) cannot use the shared
81    * component. In these cases, a component will be generated for the test.
82    */
isSharedTestComponentsEnabled(XProcessingEnv env)83   public static boolean isSharedTestComponentsEnabled(XProcessingEnv env) {
84     return SHARE_TEST_COMPONENTS.get(env) == BooleanValue.TRUE;
85   }
86 
87   /**
88    * Returns {@code true} if the aggregating processor is enabled (default is {@code true}).
89    *
90    * <p>Note:This is for internal use only!
91    */
useAggregatingRootProcessor(XProcessingEnv env)92   public static boolean useAggregatingRootProcessor(XProcessingEnv env) {
93     return USE_AGGREGATING_ROOT_PROCESSOR.get(env) == BooleanValue.TRUE;
94   }
95 
96   /**
97    * Returns project type or null if Hilt Gradle Plugin is not applied.
98    *
99    * <p>Note:This is for internal use only!
100    */
getGradleProjectType(XProcessingEnv env)101   public static GradleProjectType getGradleProjectType(XProcessingEnv env) {
102     return GRADLE_PROJECT_TYPE.get(env);
103   }
104 
isAssistedInjectViewModelsEnabled(XTypeElement viewModelElement)105   public static boolean isAssistedInjectViewModelsEnabled(XTypeElement viewModelElement) {
106     boolean enabled =
107         ENABLE_ASSISTED_INJECT_VIEWMODELS.get(XConverters.getProcessingEnv(viewModelElement))
108             == BooleanValue.TRUE;
109     return enabled;
110   }
111 
112   /** Do not use! This is for internal use only. */
113   private static final EnumOption<BooleanValue> DISABLE_ANDROID_SUPERCLASS_VALIDATION =
114       new EnumOption<>("android.internal.disableAndroidSuperclassValidation", BooleanValue.FALSE);
115 
116   /** Do not use! This is for internal use only. */
117   private static final EnumOption<BooleanValue> USE_AGGREGATING_ROOT_PROCESSOR =
118       new EnumOption<>("internal.useAggregatingRootProcessor", BooleanValue.TRUE);
119 
120   private static final EnumOption<BooleanValue> DISABLE_CROSS_COMPILATION_ROOT_VALIDATION =
121       new EnumOption<>("disableCrossCompilationRootValidation", BooleanValue.FALSE);
122 
123   private static final EnumOption<BooleanValue> DISABLE_MODULES_HAVE_INSTALL_IN_CHECK =
124       new EnumOption<>("disableModulesHaveInstallInCheck", BooleanValue.FALSE);
125 
126   private static final EnumOption<BooleanValue> SHARE_TEST_COMPONENTS =
127       new EnumOption<>(
128           "shareTestComponents",
129           BooleanValue.TRUE);
130 
131   /** Do not use! This is for internal use only. */
132   private static final EnumOption<GradleProjectType> GRADLE_PROJECT_TYPE =
133       new EnumOption<>("android.internal.projectType", GradleProjectType.UNSET);
134 
135   private static final EnumOption<BooleanValue> ENABLE_ASSISTED_INJECT_VIEWMODELS =
136       new EnumOption<>(
137           "enableAssistedInjectViewModels", BooleanValue.TRUE );
138   private static final ImmutableSet<String> DEPRECATED_OPTIONS =
139       ImmutableSet.of("dagger.hilt.android.useFragmentGetContextFix");
140 
checkWrongAndDeprecatedOptions(XProcessingEnv env)141   public static void checkWrongAndDeprecatedOptions(XProcessingEnv env) {
142     Set<String> knownOptions = getProcessorOptions();
143     for (String option : env.getOptions().keySet()) {
144       if (knownOptions.contains(option)) {
145         continue;
146       }
147 
148       if (DEPRECATED_OPTIONS.contains(option)) {
149         env.getMessager()
150             .printMessage(
151                 Kind.ERROR,
152                 "The compiler option "
153                     + option
154                     + " is deprecated and no longer does anything. "
155                     + "Please do not set this option.");
156         continue;
157       }
158 
159       if (option.startsWith("dagger.hilt.")) {
160         env.getMessager()
161             .printMessage(
162                 Kind.ERROR,
163                 "The compiler option "
164                     + option
165                     + " is not a recognized Hilt option. Is there a typo?");
166       }
167     }
168   }
169 
170   /** A processor option that can be set on the command line. */
171   private static final class EnumOption<E extends Enum<E>> {
172     private final String name;
173 
174     private final E defaultValue;
175 
176     private static final Set<EnumOption<?>> options = new HashSet<>();
177 
EnumOption(String name, E defaultValue)178     EnumOption(String name, E defaultValue) {
179       this.name = name;
180       this.defaultValue = defaultValue;
181       options.add(this);
182     }
183 
getQualifiedName()184     String getQualifiedName() {
185       return "dagger.hilt." + name;
186     }
187 
get(XProcessingEnv env)188     E get(XProcessingEnv env) {
189       String value = env.getOptions().get(getQualifiedName());
190       if (value == null) {
191         return defaultValue;
192       }
193 
194       ImmutableSet<String> validOptionNames =
195           Arrays.stream(defaultValue.getDeclaringClass().getEnumConstants())
196               .map(Enum::name)
197               .collect(DaggerStreams.toImmutableSet());
198       String uppercaseValue = toUpperCase(value);
199       if (validOptionNames.contains(uppercaseValue)) {
200         return Enum.valueOf(defaultValue.getDeclaringClass(), uppercaseValue);
201       } else {
202         throw new IllegalStateException(
203             String.format(
204                 Locale.ROOT,
205                 "Expected a value of %s  for the flag \"%s\". Got instead: %s",
206                 String.join("/", validOptionNames),
207                 name,
208                 value));
209       }
210     }
211 
getAllOptions()212     static Set<EnumOption<?>> getAllOptions() {
213       return options;
214     }
215   }
216 
getProcessorOptions()217   public static Set<String> getProcessorOptions() {
218     return EnumOption.getAllOptions().stream()
219         .map(EnumOption::getQualifiedName)
220         .collect(DaggerStreams.toImmutableSet());
221   }
222 }
223