Dependencies ============ Each gradle project can have multiple (named) "configurations" and each configuration can have dependencies attached to it. There are some standard conventions so, for example, the Java plugin adds standard configurations such as "api", "implementation", "testImplementation" and others. These configurations can also inherit from each other; more about this topic can be found here: https://docs.gradle.org/current/userguide/dependency_management_for_java_projects.html#dependency_management_for_java_projects https://docs.gradle.org/current/userguide/java_library_plugin.html#sec:java_library_separation https://docs.gradle.org/current/userguide/java_plugin.html#sec:java_plugin_and_dependency_management Lucene uses the following configurations and attach project dependencies to them: moduleApi - makes the dependency available to main classes, tests and any other modules importing the project (exportable dependency), moduleImplementation - makes the dependency available to main classes, tests but will *not* export the dependency to other modules (so their compilation classpath won't contain it). moduleTestImplementation - makes the dependency available for test classes only. The "module" prefix is used to distinguish configurations which apply to modular builds, compared to the regular classpath-configurations defined by gradle's java module. Some Lucene modules may define regular classpath entries to bypass the limitations of the module system (or gradle's). Adding a library dependency --------------------------- Lucene dependencies and their versions are managed globally using version catalogs (in versions.toml) [https://docs.gradle.org/current/userguide/platforms.html]. Let's say we wish to add a dependency on library "foo.bar:baz" in version 1.2 to :lucene:core. Let's assume this library is only used internally by the project. The :lucene:core project is configured by lucene/core/build.gradle, so we add (or modify) the dependency block as follows: dependencies { moduleImplementation deps.baz } The "moduleImplementation" here is a named configuration explained in the section above. The "deps.baz" refers to the version catalog named "deps", in which the dependency "baz" should be declared. If this is the first reference to this library, then we have to add it to "versions.toml" catalog: the version goes under the "versions" and module coordinates under the "libraries" section: [versions] baz = "1.2" ... [libraries] baz = { module = "foo.bar:baz", version.ref = "baz" } The version defined in the "versions" section is the preferred version of the library we wish to use. Finally, run tidy to sort all entries in versions.toml: gradlew tidy Gradle will try to consolidate different versions across different configurations to make sure they're compatible and may complain if it encounters conflicting versions in the dependency tree. We want all dependencies to be consistent, so we use an additional build plugin to ensure no accidental version changes occur. Whenever we add or remove dependencies, we have to follow-up with lock file regeneration: gradlew writeLocks git diff versions.* IMPORTANT: The versions.lock file will contain a list of actual library versions and configurations they occurred in. Once a new dependency is added it always makes sense to regenerate the lock file and look at which dependencies have changed (and why). Inspecting current dependencies ------------------------------- The tree of dependencies of a project (in all configurations) can be dumped by the following command (example): gradlew -p lucene\analysis\icu dependencies But this can be a bit overwhelming; we will most likely be interested in just the "publicly visible" and "classpath-visible" configurations. The publicly visible project dependencies (classes shared by other modules importing our module) can be displayed with: gradlew -p lucene\analysis\icu dependencies --configuration moduleApi And the "private" set of dependencies (real classpath) can be dumped with: gradlew -p lucene\analysis\icu dependencies --configuration moduleRuntimePath Excluding a transitive dependency --------------------------------- Let's say "foo.bar:baz" has a transitive dependency on project "foo.bar:irrelevant" and we know the transitive dependency is not crucial for the functioning of "foo.bar:baz". We can exclude it by adding an exclusion block to the original declaration: dependencies { implementation(deps.baz, { exclude group: "foo.bar", module: "irrelevant" }) } Note the brackets - they are important and prevent accidental mistakes of applying the exclusion to the wrong scope. Updating dependency checksum and licenses ----------------------------------------- The last step is to make sure the licenses, notice files and checksums are in place for any new dependencies. This command will print what's missing and where: gradlew licenses To update JAR checksums for licenses use: gradlew updateLicenses