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