From 4d3040235ef3588dccc0748f85b396a4a50f2df9 Mon Sep 17 00:00:00 2001 From: Dawid Weiss Date: Sun, 8 Dec 2019 18:34:12 +0100 Subject: [PATCH] Add initial guidelines concerning dependency management. --- gradle/help.gradle | 3 +- help/dependencies.txt | 106 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 help/dependencies.txt diff --git a/gradle/help.gradle b/gradle/help.gradle index e5f8014dd0d..e52638941bd 100644 --- a/gradle/help.gradle +++ b/gradle/help.gradle @@ -5,8 +5,9 @@ configure(rootProject) { ["Workflow", "help/workflow.txt", "Typical workflow commands."], ["Ant", "help/ant.txt", "Ant-gradle migration help."], ["Tests", "help/tests.txt", "Tests, filtering, beasting, etc."], + ["Deps", "help/dependencies.txt", "Declaring, inspecting and excluding dependencies."], ["ForbiddenApis", "help/forbiddenApis.txt", "How to add/apply rules for forbidden APIs."], - ["LocalSettings", "help/localSettings.txt", "Local settings, overrides and build performance tweaks."] + ["LocalSettings", "help/localSettings.txt", "Local settings, overrides and build performance tweaks."], ] helpFiles.each { section, path, sectionInfo -> diff --git a/help/dependencies.txt b/help/dependencies.txt new file mode 100644 index 00000000000..b68750c9aac --- /dev/null +++ b/help/dependencies.txt @@ -0,0 +1,106 @@ +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: + +[TODO: add conigurations link, java plugin configs] + +For the needs of Lucene and Solr we will typically focus on 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. + +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. \ No newline at end of file