• 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 com.google.common.base.Ascii;
20 import com.google.common.collect.ImmutableSet;
21 import java.util.Arrays;
22 import java.util.Set;
23 import java.util.stream.Collectors;
24 import javax.annotation.processing.ProcessingEnvironment;
25 import javax.lang.model.element.TypeElement;
26 import javax.tools.Diagnostic.Kind;
27 
28 /** Hilt annotation processor options. */
29 // TODO(danysantiago): Consider consolidating with Dagger compiler options logic.
30 public final class HiltCompilerOptions {
31 
32   /**
33    * Returns {@code true} if the superclass validation is disabled for
34    * {@link dagger.hilt.android.AndroidEntryPoint}-annotated classes.
35    *
36    * This flag is for internal use only! The superclass validation checks that the super class is a
37    * generated {@code Hilt_} class. This flag is disabled by the Hilt Gradle plugin to enable
38    * bytecode transformation to change the superclass.
39    */
isAndroidSuperclassValidationDisabled( TypeElement element, ProcessingEnvironment env)40   public static boolean isAndroidSuperclassValidationDisabled(
41       TypeElement element, ProcessingEnvironment env) {
42     BooleanOption option = BooleanOption.DISABLE_ANDROID_SUPERCLASS_VALIDATION;
43     return option.get(env);
44   }
45 
46   /**
47    * Returns {@code true} if cross-compilation root validation is disabled.
48    *
49    * <p>This flag should rarely be needed, but may be used for legacy/migration purposes if
50    * tests require the use of {@link dagger.hilt.android.HiltAndroidApp} rather than
51    * {@link dagger.hilt.android.testing.HiltAndroidTest}.
52    *
53    * <p>Note that Hilt still does validation within a single compilation unit. In particular,
54    * a compilation unit that contains a {@code HiltAndroidApp} usage cannot have other
55    * {@code HiltAndroidApp} or {@code HiltAndroidTest} usages in the same compilation unit.
56    */
isCrossCompilationRootValidationDisabled( ImmutableSet<TypeElement> rootElements, ProcessingEnvironment env)57   public static boolean isCrossCompilationRootValidationDisabled(
58       ImmutableSet<TypeElement> rootElements, ProcessingEnvironment env) {
59     BooleanOption option = BooleanOption.DISABLE_CROSS_COMPILATION_ROOT_VALIDATION;
60     return option.get(env);
61   }
62 
63   /** Returns {@code true} if the check for {@link dagger.hilt.InstallIn} is disabled. */
isModuleInstallInCheckDisabled(ProcessingEnvironment env)64   public static boolean isModuleInstallInCheckDisabled(ProcessingEnvironment env) {
65     return BooleanOption.DISABLE_MODULES_HAVE_INSTALL_IN_CHECK.get(env);
66   }
67 
68   /**
69    * Returns {@code true} of unit tests should try to share generated components, rather than using
70    * separate generated components per Hilt test root.
71    *
72    * <p>Tests that provide their own test bindings (e.g. using {@link
73    * dagger.hilt.android.testing.BindValue} or a test {@link dagger.Module}) cannot use the shared
74    * component. In these cases, a component will be generated for the test.
75    */
isSharedTestComponentsEnabled(ProcessingEnvironment env)76   public static boolean isSharedTestComponentsEnabled(ProcessingEnvironment env) {
77     return BooleanOption.SHARE_TEST_COMPONENTS.get(env);
78   }
79 
80   /**
81    * Returns {@code true} if the aggregating processor is enabled (default is {@code true}).
82    *
83    * <p>Note:This is for internal use only!
84    */
useAggregatingRootProcessor(ProcessingEnvironment env)85   public static boolean useAggregatingRootProcessor(ProcessingEnvironment env) {
86     return BooleanOption.USE_AGGREGATING_ROOT_PROCESSOR.get(env);
87   }
88 
89   /** Processor options which can have true or false values. */
90   private enum BooleanOption {
91     /** Do not use! This is for internal use only. */
92     DISABLE_ANDROID_SUPERCLASS_VALIDATION(
93         "android.internal.disableAndroidSuperclassValidation", false),
94 
95     /** Do not use! This is for internal use only. */
96     USE_AGGREGATING_ROOT_PROCESSOR("internal.useAggregatingRootProcessor", true),
97 
98     DISABLE_CROSS_COMPILATION_ROOT_VALIDATION("disableCrossCompilationRootValidation", false),
99 
100     DISABLE_MODULES_HAVE_INSTALL_IN_CHECK("disableModulesHaveInstallInCheck", false),
101 
102     SHARE_TEST_COMPONENTS(
103         "shareTestComponents", true);
104 
105     private final String name;
106     private final boolean defaultValue;
107 
BooleanOption(String name, boolean defaultValue)108     BooleanOption(String name, boolean defaultValue) {
109       this.name = name;
110       this.defaultValue = defaultValue;
111     }
112 
get(ProcessingEnvironment env)113     boolean get(ProcessingEnvironment env) {
114       String value = env.getOptions().get(getQualifiedName());
115       if (value == null) {
116         return defaultValue;
117       }
118 
119       // Using Boolean.parseBoolean will turn any non-"true" value into false. Strictly verify the
120       // inputs to reduce user errors.
121       String lowercaseValue = Ascii.toLowerCase(value);
122       switch (lowercaseValue) {
123         case "true":
124           return true;
125         case "false":
126           return false;
127         default:
128           throw new IllegalStateException(
129               "Expected a value of true/false for the flag \""
130                   + name
131                   + "\". Got instead: "
132                   + value);
133       }
134     }
135 
getQualifiedName()136     String getQualifiedName() {
137       return "dagger.hilt." + name;
138     }
139   }
140 
141   private static final ImmutableSet<String> DEPRECATED_OPTIONS = ImmutableSet.of(
142       "dagger.hilt.android.useFragmentGetContextFix");
143 
checkWrongAndDeprecatedOptions(ProcessingEnvironment env)144   public static void checkWrongAndDeprecatedOptions(ProcessingEnvironment env) {
145     Set<String> knownOptions = getProcessorOptions();
146     for (String option : env.getOptions().keySet()) {
147       if (knownOptions.contains(option)) {
148         continue;
149       }
150 
151       if (DEPRECATED_OPTIONS.contains(option)) {
152         env.getMessager().printMessage(
153             Kind.ERROR,
154             "The compiler option " + option + " 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().printMessage(
161             Kind.ERROR,
162             "The compiler option " + option + " is not a recognized Hilt option. Is there a typo?");
163       }
164     }
165   }
166 
getProcessorOptions()167   public static Set<String> getProcessorOptions() {
168     return Arrays.stream(BooleanOption.values())
169         .map(BooleanOption::getQualifiedName)
170         .collect(Collectors.toSet());
171   }
172 }
173