Add initial guidelines concerning dependency management.

This commit is contained in:
Dawid Weiss 2019-12-08 18:34:12 +01:00
parent 1021f04d1a
commit 4d3040235e
2 changed files with 108 additions and 1 deletions

View File

@ -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 ->

106
help/dependencies.txt Normal file
View File

@ -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.