|
4 | 4 | import static java.util.Map.entry;
|
5 | 5 | import static java.util.stream.Collectors.toList;
|
6 | 6 |
|
| 7 | +import java.io.FileWriter; |
| 8 | +import java.io.IOException; |
7 | 9 | import java.lang.reflect.Type;
|
8 | 10 | import java.util.ArrayList;
|
9 | 11 | import java.util.Arrays;
|
@@ -108,17 +110,9 @@ static void registerModuleProvidedTypes(Set<String> providedTypes) {
|
108 | 110 | * types and the only thing providing them is the plugin.
|
109 | 111 | */
|
110 | 112 | static void registerPluginProvidedTypes(ScopeInfo defaultScope) {
|
111 |
| - avajePlugins.forEach((k, v) -> { |
112 |
| - if (APContext.typeElement(k) != null) { |
113 |
| - APContext.logNote("Loaded Plugin: %s", k); |
114 |
| - v.forEach(defaultScope::pluginProvided); |
115 |
| - } |
116 |
| - }); |
117 |
| - defaultScope.pluginProvided("io.avaje.inject.event.ObserverManager"); |
118 | 113 | if (!INJECT_AVAILABLE) {
|
119 | 114 | if (!pluginExists("avaje-module-dependencies.csv")) {
|
120 |
| - APContext.logNote( |
121 |
| - "Unable to detect Avaje Inject in Annotation Processor ClassPath, use the Avaje Inject Maven/Gradle plugin for detecting Inject Plugins from dependencies"); |
| 115 | + APContext.logNote("Unable to detect Avaje Inject Maven/Gradle plugin, use the Avaje Inject Maven/Gradle plugin for auto detecting External Inject Plugins/Modules from dependencies"); |
122 | 116 | }
|
123 | 117 | return;
|
124 | 118 | }
|
@@ -168,67 +162,153 @@ static void readMetaDataProvides(Collection<String> providedTypes) {
|
168 | 162 | });
|
169 | 163 | }
|
170 | 164 |
|
171 |
| - static void scanTheWorld(Collection<String> providedTypes) { |
| 165 | + static void scanAllInjectPlugins(ScopeInfo defaultScope) { |
| 166 | + final var hasPlugins = !defaultScope.pluginProvided().isEmpty(); |
| 167 | + avajePlugins.forEach((k, v) -> { |
| 168 | + if (APContext.typeElement(k) != null) { |
| 169 | + APContext.logNote("Loaded Plugin: %s", k); |
| 170 | + v.forEach(defaultScope::pluginProvided); |
| 171 | + } |
| 172 | + }); |
| 173 | + defaultScope.pluginProvided("io.avaje.inject.event.ObserverManager"); |
| 174 | + if (hasPlugins) { |
| 175 | + return; |
| 176 | + } |
| 177 | + |
| 178 | + injectExtensions() |
| 179 | + .filter(PluginProvidesPrism::isPresent) |
| 180 | + .distinct() |
| 181 | + .forEach(pluginType -> addPluginToScope(defaultScope, pluginType)); |
| 182 | + |
| 183 | + if (defaultScope.pluginProvided().isEmpty()) { |
| 184 | + APContext.logNote("No external plugins detected"); |
| 185 | + } |
| 186 | + writePluginProvides(defaultScope); |
| 187 | + } |
| 188 | + |
| 189 | + private static void writePluginProvides(ScopeInfo defaultScope) { |
| 190 | + // write detected plugins to a text file for test compilation |
| 191 | + try (final var pluginWriter = new FileWriter(APContext.getBuildResource("avaje-plugin-provides.txt").toFile())) { |
| 192 | + for (var providedType : defaultScope.pluginProvided()) { |
| 193 | + pluginWriter.write(providedType); |
| 194 | + pluginWriter.write("\n"); |
| 195 | + } |
| 196 | + } catch (IOException e) { |
| 197 | + APContext.logWarn("Failed to write avaje-plugin-provides.txt due to %s", e.getMessage()); |
| 198 | + } |
| 199 | + } |
| 200 | + |
| 201 | + private static void addPluginToScope(ScopeInfo defaultScope, TypeElement pluginType) { |
| 202 | + final var name = pluginType.getQualifiedName().toString(); |
| 203 | + if (avajePlugins.containsKey(name)) { |
| 204 | + return; |
| 205 | + } |
| 206 | + var prism = PluginProvidesPrism.getInstanceOn(pluginType); |
| 207 | + for (final var provide : prism.provides()) { |
| 208 | + defaultScope.pluginProvided(provide.toString()); |
| 209 | + } |
| 210 | + for (final var provide : prism.providesStrings()) { |
| 211 | + defaultScope.pluginProvided(provide); |
| 212 | + } |
| 213 | + for (final var provide : prism.providesAspects()) { |
| 214 | + defaultScope.pluginProvided(Util.wrapAspect(provide.toString())); |
| 215 | + } |
| 216 | + APContext.logNote("Loaded Plugin: %s", name); |
| 217 | + } |
| 218 | + |
| 219 | + static void scanAllAvajeModules(Collection<String> providedTypes) { |
172 | 220 | if (!externalMeta.isEmpty()) {
|
173 | 221 | return;
|
174 | 222 | }
|
175 |
| - var allModules = |
| 223 | + final var types = APContext.types(); |
| 224 | + final var avajeModuleType = APContext.typeElement("io.avaje.inject.spi.AvajeModule").asType(); |
| 225 | + injectExtensions() |
| 226 | + .filter(t -> t.getInterfaces().stream().anyMatch(i -> types.isAssignable(i, avajeModuleType))) |
| 227 | + .distinct() |
| 228 | + .forEach(otherModule -> addOtherModuleProvides(providedTypes, otherModule)); |
| 229 | + |
| 230 | + if (externalMeta.isEmpty()) { |
| 231 | + APContext.logNote("No external modules detected"); |
| 232 | + } |
| 233 | + writeModuleDependencies(); |
| 234 | + } |
| 235 | + |
| 236 | + private static void writeModuleDependencies() { |
| 237 | + // write detected modules to a csv for test compilation |
| 238 | + try (final var moduleWriter = new FileWriter(APContext.getBuildResource("avaje-module-dependencies.csv").toFile())) { |
| 239 | + moduleWriter.write("External Module Type|Provides|Requires"); |
| 240 | + for (ModuleData avajeModule : ProcessingContext.modules()) { |
| 241 | + moduleWriter.write("\n"); |
| 242 | + moduleWriter.write(avajeModule.name()); |
| 243 | + moduleWriter.write("|"); |
| 244 | + var provides = String.join(",", avajeModule.provides()); |
| 245 | + moduleWriter.write(provides.isEmpty() ? " " : provides); |
| 246 | + moduleWriter.write("|"); |
| 247 | + var requires = String.join(",", avajeModule.requires()); |
| 248 | + moduleWriter.write(requires.isEmpty() ? " " : requires); |
| 249 | + } |
| 250 | + |
| 251 | + } catch (IOException e) { |
| 252 | + APContext.logWarn("Failed to write avaje-module-dependencies.csv due to %s", e.getMessage()); |
| 253 | + } |
| 254 | + } |
| 255 | + |
| 256 | + private static void addOtherModuleProvides(Collection<String> providedTypes, TypeElement otherModule) { |
| 257 | + final var provides = new HashSet<String>(); |
| 258 | + final var requires = new HashSet<String>(); |
| 259 | + |
| 260 | + ElementFilter.methodsIn(otherModule.getEnclosedElements()).stream() |
| 261 | + .map(DependencyMetaPrism::getInstanceOn) |
| 262 | + .filter(Objects::nonNull) |
| 263 | + .map(MetaData::new) |
| 264 | + .forEach(m -> { |
| 265 | + externalMeta.add(m); |
| 266 | + provides.addAll(m.autoProvides()); |
| 267 | + provides.addAll(m.provides()); |
| 268 | + m.dependsOn().stream() |
| 269 | + .filter(d -> !d.isSoftDependency()) |
| 270 | + .map(Dependency::name) |
| 271 | + .forEach(requires::add); |
| 272 | + |
| 273 | + providedTypes.add(m.key()); |
| 274 | + providedTypes.add(m.type()); |
| 275 | + providedTypes.addAll(Util.addQualifierSuffix(m.provides(), m.name())); |
| 276 | + providedTypes.addAll(Util.addQualifierSuffix(m.autoProvides(), m.name())); |
| 277 | + }); |
| 278 | + |
| 279 | + final var name = otherModule.getQualifiedName().toString(); |
| 280 | + APContext.logNote("Detected Module: %s", name); |
| 281 | + ProcessingContext.addModule(new ModuleData(name, List.copyOf(provides), List.copyOf(requires))); |
| 282 | + } |
| 283 | + |
| 284 | + private static Stream<TypeElement> injectExtensions() { |
| 285 | + final var allModules = |
176 | 286 | APContext.elements().getAllModuleElements().stream()
|
177 | 287 | .filter(m -> !m.getQualifiedName().toString().startsWith("java"))
|
178 | 288 | .filter(m -> !m.getQualifiedName().toString().startsWith("jdk"))
|
179 | 289 | // for whatever reason, compilation breaks if we don't filter out the current module
|
180 |
| - .filter(m -> m != APContext.getProjectModuleElement()) |
| 290 | + .filter(m -> !m.equals(APContext.getProjectModuleElement())) |
181 | 291 | .collect(toList());
|
182 | 292 |
|
183 |
| - var types = APContext.types(); |
184 |
| - var spi = APContext.typeElement("io.avaje.inject.spi.AvajeModule").asType(); |
| 293 | + final var types = APContext.types(); |
| 294 | + final var extensionType = APContext.typeElement("io.avaje.inject.spi.InjectExtension").asType(); |
| 295 | + |
185 | 296 | final var checkEnclosing =
|
186 | 297 | allModules.stream()
|
187 | 298 | .flatMap(m -> m.getEnclosedElements().stream())
|
188 | 299 | .flatMap(p -> p.getEnclosedElements().stream())
|
189 | 300 | .map(TypeElement.class::cast)
|
190 | 301 | .filter(t -> t.getKind() == ElementKind.CLASS)
|
191 |
| - .filter(t -> t.getModifiers().contains(Modifier.PUBLIC)); |
| 302 | + .filter(t -> t.getModifiers().contains(Modifier.PUBLIC)) |
| 303 | + .filter(t -> types.isAssignable(t.asType(), extensionType)); |
192 | 304 |
|
193 | 305 | final var checkDirectives =
|
194 | 306 | allModules.stream()
|
195 | 307 | .flatMap(m -> ElementFilter.providesIn(m.getDirectives()).stream())
|
196 | 308 | .filter(ExternalProvider::isInjectExtension)
|
197 | 309 | .flatMap(p -> p.getImplementations().stream());
|
198 | 310 |
|
199 |
| - Stream.concat(checkEnclosing, checkDirectives) |
200 |
| - .filter(t -> t.getInterfaces().stream().anyMatch(i -> types.isAssignable(i, spi))) |
201 |
| - .distinct() |
202 |
| - .forEach(t -> { |
203 |
| - final var provides = new HashSet<String>(); |
204 |
| - final var requires = new HashSet<String>(); |
205 |
| - |
206 |
| - ElementFilter.methodsIn(t.getEnclosedElements()).stream() |
207 |
| - .map(DependencyMetaPrism::getInstanceOn) |
208 |
| - .filter(Objects::nonNull) |
209 |
| - .map(MetaData::new) |
210 |
| - .forEach(m -> { |
211 |
| - externalMeta.add(m); |
212 |
| - provides.addAll(m.autoProvides()); |
213 |
| - provides.addAll(m.provides()); |
214 |
| - m.dependsOn().stream() |
215 |
| - .filter(d -> !d.isSoftDependency()) |
216 |
| - .map(Dependency::name) |
217 |
| - .forEach(requires::add); |
218 |
| - |
219 |
| - providedTypes.add(m.key()); |
220 |
| - providedTypes.add(m.type()); |
221 |
| - providedTypes.addAll(Util.addQualifierSuffix(m.provides(), m.name())); |
222 |
| - providedTypes.addAll(Util.addQualifierSuffix(m.autoProvides(), m.name())); |
223 |
| - }); |
224 |
| - |
225 |
| - final var name = t.getQualifiedName().toString(); |
226 |
| - APContext.logNote("Detected Module: %s", name); |
227 |
| - ProcessingContext.addModule(new ModuleData(name, List.copyOf(provides), List.copyOf(requires))); |
228 |
| - }); |
229 |
| - if (externalMeta.isEmpty()) { |
230 |
| - APContext.logNote("No external modules detected"); |
231 |
| - } |
| 311 | + return Stream.concat(checkEnclosing, checkDirectives); |
232 | 312 | }
|
233 | 313 |
|
234 | 314 | private static boolean isInjectExtension(ModuleElement.ProvidesDirective p) {
|
|
0 commit comments