dweiss commented on a change in pull request #571: URL: https://github.com/apache/lucene/pull/571#discussion_r776605201
########## File path: gradle/java/modules.gradle ########## @@ -237,24 +185,282 @@ allprojects { } +// +// For a source set, create explicit configurations for declaring modular dependencies. +// +// These "modular" configurations correspond 1:1 to Gradle's conventions but have a 'module' prefix +// and a capitalized remaining part of the conventional name. For example, an 'api' configuration in +// the main source set would have a corresponding 'moduleApi' configuration for declaring modular +// dependencies. +// +// Gradle's java plugin "convention" configurations extend from their modular counterparts +// so all dependencies end up on classpath by default for backward compatibility with other +// tasks and gradle infrastructure. +// +// At the same time, we also know which dependencies (and their transitive graph of dependencies!) +// should be placed on module-path only. +// +// Note that an explicit configuration of modular dependencies also opens up the possibility of automatically +// validating whether the dependency configuration for a gradle project is consistent with the information in +// the module-info descriptor because there is a (nearly?) direct correspondence between the two: +// +// moduleApi - 'requires transitive' +// moduleImplementation - 'requires' +// moduleCompileOnly - 'requires static' +// class ModularPathsExtension { + /** + * Determines how paths are split between module path and classpath. + */ + enum Mode { + /** + * Dependencies and source set outputs are placed on classpath, even if declared on modular + * configurations. This would be the 'default' backward-compatible mode. + */ + CLASSPATH_ONLY, + + /** + * Dependencies from modular configurations are placed on module path. Source set outputs + * are placed on classpath. + */ + DEPENDENCIES_ON_MODULE_PATH + } + Project project SourceSet sourceSet Configuration compileModulePathConfiguration Configuration runtimeModulePathConfiguration + Configuration modulePatchOnlyConfiguration + + // The mode of splitting paths for this source set. + Mode mode = Mode.DEPENDENCIES_ON_MODULE_PATH + + /** + * A list of module name - path provider entries that will be converted + * into {@code --patch-module} options. + */ + private List<Map.Entry<String, Provider<Path>>> modulePatches = new ArrayList<>() - ModularPathsExtension(Project project, SourceSet sourceSet, - Configuration compileModulePathConfiguration, - Configuration runtimeModulePathConfiguration) { - this.project = project + ModularPathsExtension(Project project, SourceSet sourceSet) { + this.project = project this.sourceSet = sourceSet - this.compileModulePathConfiguration = compileModulePathConfiguration - this.runtimeModulePathConfiguration = runtimeModulePathConfiguration + + ConfigurationContainer configurations = project.configurations + + // Create modular configurations for gradle's java plugin convention configurations. + Configuration moduleApi = createModuleConfigurationForConvention(sourceSet.apiConfigurationName) + Configuration moduleImplementation = createModuleConfigurationForConvention(sourceSet.implementationConfigurationName) + Configuration moduleRuntimeOnly = createModuleConfigurationForConvention(sourceSet.runtimeOnlyConfigurationName) + Configuration moduleCompileOnly = createModuleConfigurationForConvention(sourceSet.compileOnlyConfigurationName) + + + // Apply hierarchy relationships to modular configurations. + moduleImplementation.extendsFrom(moduleApi) + + // Patched modules have to end up in the implementation configuration for IDEs, which + // otherwise get terribly confused. + Configuration modulePatchOnly = createModuleConfigurationForConvention( + SourceSet.isMain(sourceSet) ? "patchOnly" : sourceSet.name + "PatchOnly") + modulePatchOnly.canBeResolved(true) + moduleImplementation.extendsFrom(modulePatchOnly) + this.modulePatchOnlyConfiguration = modulePatchOnly + + // This part of convention configurations seems like a very esoteric use case, leave out for now. + // sourceSet.compileOnlyApiConfigurationName + + // We have to ensure configurations are using assembled resources and classes (jar variant) as a single + // module can't be expanded into multiple folders. + Closure<Void> ensureJarVariant = { Configuration c -> + c.attributes.attribute(LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE, project.objects.named(LibraryElements, LibraryElements.JAR)) + } + + // Set up compilation module path configuration combining corresponding convention configurations. + Closure<Configuration> createResolvableModuleConfiguration = { String configurationName -> + Configuration conventionConfiguration = configurations.maybeCreate(configurationName) + Configuration moduleConfiguration = configurations.maybeCreate(moduleConfigurationNameFor(conventionConfiguration.name)) + moduleConfiguration.canBeConsumed(false) + moduleConfiguration.canBeResolved(true) + ensureJarVariant(moduleConfiguration) + + project.logger.info("Created resolvable module configuration for '${conventionConfiguration.name}': ${moduleConfiguration.name}") + return moduleConfiguration + } + + ensureJarVariant(configurations.maybeCreate(sourceSet.compileClasspathConfigurationName)) + ensureJarVariant(configurations.maybeCreate(sourceSet.runtimeClasspathConfigurationName)) + + this.compileModulePathConfiguration = createResolvableModuleConfiguration(sourceSet.compileClasspathConfigurationName) + compileModulePathConfiguration.extendsFrom(moduleCompileOnly, moduleImplementation) + + this.runtimeModulePathConfiguration = createResolvableModuleConfiguration(sourceSet.runtimeClasspathConfigurationName) + runtimeModulePathConfiguration.extendsFrom(moduleRuntimeOnly, moduleImplementation) + } + + /** + * Adds {@code --patch-module} option for the provided module name and the provider of a + * folder or JAR file. + * + * @param moduleName + * @param pathProvider + */ + void patchModule(String moduleName, Provider<Path> pathProvider) { + modulePatches.add(Map.entry(moduleName, pathProvider)); + } Review comment: Thanks for trying it out, @mocobeta. Nice catch - I'll correct this when I get a chance. It sadly does not work for me in IDEs - I've tried and had to revert the test module because Idea went nuts. Also, what I added is for external dependencies - the internal sourceset outputs are treated differently (eh...) and they will require a slightly different arrangement I think. ECJ stopped working too, argh. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: issues-unsubscr...@lucene.apache.org For queries about this service, please contact Infrastructure at: us...@infra.apache.org --------------------------------------------------------------------- To unsubscribe, e-mail: issues-unsubscr...@lucene.apache.org For additional commands, e-mail: issues-h...@lucene.apache.org