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 typic 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 typically uses three configurations and attach project dependencies to them: api - makes a dependency available for main classes, tests and any other modules importing the project (exportable dependency), implementation - makes a dependency available for main classes, tests but will *not* export the dependency for other modules (so their compilation classpath won't contain it). testImplementation - makes a dependency only available for test classes. Adding a library dependency --------------------------- 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 and we would add (or modify) the dependency block as follows: dependencies { implementation "foo.bar:baz" } The "implementation" here is a named configuration; we don't need to declare it because it is declared for us by the java-library plugin. In "normal" gradle the version of the dependency would be present directly inside the declaration but we use a plugin (palantir-consistent-versions) to manage all dependency versions from the top-level (so that conflicts can be resolved globally). If this is the first time "foo.bar:baz" is added to the project, we'd have to add its version to "versions.props" file at the top level of the checkout: foo.bar:baz=1.2 and then regenerate the "versions.lock" file using the following command: gradlew --write-locks IMPORTANT: The versions.lock file will contain the actual version of the dependency picked based on other project dependencies and their transitive dependencies. This selected version may be different from what each of these actually requires (the highest version number will be typically selected). To see which dependencies require which version of the library use: gradlew why --hash=... where the hash code comes from versions.lock file. For example, at the time of writing, jackson-databind has the following entry: com.fasterxml.jackson.core:jackson-databind:2.10.0 (3 constraints: 931a7796) and "gradlew why --hash=931a7796" prints: com.fasterxml.jackson.core:jackson-databind:2.10.0 projects -> 2.10.0 net.thisptr:jackson-jq -> 2.7.0 org.carrot2:carrot2-mini -> 2.9.9.3 Once the dependency is added it always makes sense to see the tree of all module dependencies and maybe exclude transitive dependencies of foo.bar:baz that we won't need. 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 api And the "private" set of dependencies (real classpath) can be dumped with: gradlew -p lucene\analysis\icu dependencies --configuration runtimeClasspath 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("foo.bar: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