Skip to content

Commit 5642e01

Browse files
authored
Replace failOnVersionConflict() with custom requireUpperBoundDeps
failOnVersionConflict has never been good for us. It is equivalent to Maven dependencyConvergence which we discourage our users to use because it is too tempermental and _creates_ version skew issues over time. However, we had no real alternative for determining if our deps would be misinterpeted by Maven. failOnVersionConflict has been a constant drain and makes it really hard to do seemingly-trivial upgrades. As evidenced by protobuf/build.gradle in this change, it also caused _us_ to introduce a version downgrade. This introduces our own custom requireUpperBoundDeps implementation so that we can get back to simple dependency upgrades _and_ increase our confidence in a consistent dependency tree.
1 parent aa18b2c commit 5642e01

File tree

23 files changed

+114
-157
lines changed

23 files changed

+114
-157
lines changed

Diff for: alts/build.gradle

+3-3
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ dependencies {
2121
project(':grpc-protobuf'),
2222
project(':grpc-stub'),
2323
libraries.protobuf,
24-
libraries.conscrypt
24+
libraries.conscrypt,
25+
libraries.guava,
26+
libraries.google_auth_oauth2_http
2527
def nettyDependency = implementation project(':grpc-netty')
26-
googleOauth2Dependency 'implementation'
27-
guavaDependency 'implementation'
2828
compileOnly libraries.javax_annotation
2929

3030
shadow configurations.implementation.getDependencies().minus(nettyDependency)

Diff for: android-interop-testing/build.gradle

+2-2
Original file line numberDiff line numberDiff line change
@@ -63,12 +63,12 @@ dependencies {
6363
project(':grpc-stub'),
6464
project(':grpc-testing'),
6565
libraries.junit,
66-
libraries.truth
66+
libraries.truth,
67+
libraries.opencensus_contrib_grpc_metrics
6768

6869
implementation (libraries.google_auth_oauth2_http) {
6970
exclude group: 'org.apache.httpcomponents'
7071
}
71-
censusGrpcMetricDependency 'implementation'
7272

7373
compileOnly libraries.javax_annotation
7474

Diff for: android/build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ repositories {
3131

3232
dependencies {
3333
api project(':grpc-core')
34-
guavaDependency 'implementation'
34+
implementation libraries.guava
3535
testImplementation project('::grpc-okhttp')
3636
testImplementation libraries.androidx_test
3737
testImplementation libraries.junit

Diff for: api/build.gradle

+2-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ evaluationDependsOn(project(':grpc-context').path)
1313
dependencies {
1414
api project(':grpc-context'),
1515
libraries.jsr305
16-
guavaDependency 'implementation'
16+
implementation libraries.guava,
17+
libraries.errorprone
1718

1819
testImplementation project(':grpc-context').sourceSets.test.output,
1920
project(':grpc-testing'),

Diff for: auth/build.gradle

+3-3
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ description = "gRPC: Auth"
1010
dependencies {
1111
api project(':grpc-api'),
1212
libraries.google_auth_credentials
13-
guavaDependency 'implementation'
14-
testImplementation project(':grpc-testing')
15-
googleOauth2Dependency 'testImplementation'
13+
implementation libraries.guava
14+
testImplementation project(':grpc-testing'),
15+
libraries.google_auth_oauth2_http
1616
signature "org.codehaus.mojo.signature:java17:1.0@signature"
1717
signature "net.sf.androidscents.signature:android-api-level-14:4.0_r4@signature"
1818
}

Diff for: binder/build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,9 @@ repositories {
4747
dependencies {
4848
api project(':grpc-core')
4949

50-
guavaDependency 'implementation'
5150
implementation libraries.androidx_annotation
5251
implementation libraries.androidx_core
52+
implementation libraries.guava
5353
testImplementation libraries.androidx_core
5454
testImplementation libraries.androidx_test
5555
testImplementation libraries.junit

Diff for: build.gradle

+66-81
Original file line numberDiff line numberDiff line change
@@ -205,68 +205,6 @@ subprojects {
205205
jetty_alpn_agent: 'org.mortbay.jetty.alpn:jetty-alpn-agent:2.0.10'
206206
]
207207

208-
// A util function to config guava dependency with transitive dependencies
209-
// properly resolved for the failOnVersionConflict strategy.
210-
guavaDependency = { configurationName ->
211-
dependencies."$configurationName"(libraries.guava) {
212-
exclude group: 'com.google.code.findbugs', module: 'jsr305'
213-
exclude group: 'com.google.errorprone', module: 'error_prone_annotations'
214-
exclude group: 'org.codehaus.mojo', module: 'animal-sniffer-annotations'
215-
}
216-
dependencies."$configurationName" libraries.errorprone
217-
dependencies.runtimeOnly libraries.animalsniffer_annotations
218-
dependencies.runtimeOnly libraries.jsr305
219-
}
220-
221-
// A util function to config opencensus_api dependency with transitive
222-
// dependencies properly resolved for the failOnVersionConflict strategy.
223-
censusApiDependency = { configurationName ->
224-
dependencies."$configurationName"(libraries.opencensus_api) {
225-
exclude group: 'com.google.code.findbugs', module: 'jsr305'
226-
exclude group: 'com.google.guava', module: 'guava'
227-
// we'll always be more up-to-date
228-
exclude group: 'io.grpc', module: 'grpc-context'
229-
}
230-
dependencies.runtimeOnly project(':grpc-context')
231-
dependencies.runtimeOnly libraries.jsr305
232-
guavaDependency 'runtimeOnly'
233-
}
234-
235-
// A util function to config opencensus_contrib_grpc_metrics dependency
236-
// with transitive dependencies properly resolved for the failOnVersionConflict
237-
// strategy.
238-
censusGrpcMetricDependency = { configurationName ->
239-
dependencies."$configurationName"(libraries.opencensus_contrib_grpc_metrics) {
240-
exclude group: 'com.google.code.findbugs', module: 'jsr305'
241-
exclude group: 'com.google.guava', module: 'guava'
242-
// we'll always be more up-to-date
243-
exclude group: 'io.grpc', module: 'grpc-context'
244-
}
245-
dependencies.runtimeOnly project(':grpc-context')
246-
dependencies.runtimeOnly libraries.jsr305
247-
guavaDependency 'runtimeOnly'
248-
}
249-
250-
googleOauth2Dependency = { configurationName ->
251-
dependencies."$configurationName"(libraries.google_auth_oauth2_http) {
252-
exclude group: 'com.google.guava', module: 'guava'
253-
exclude group: 'io.grpc', module: 'grpc-context'
254-
exclude group: 'io.opencensus', module: 'opencensus-api'
255-
}
256-
dependencies.runtimeOnly project(':grpc-context')
257-
censusApiDependency 'runtimeOnly'
258-
guavaDependency 'runtimeOnly'
259-
}
260-
261-
// A util function to config perfmark dependency with transitive
262-
// dependencies properly resolved for the failOnVersionConflict strategy.
263-
perfmarkDependency = { configurationName ->
264-
dependencies."$configurationName"(libraries.perfmark) {
265-
exclude group: 'com.google.errorprone', module: 'error_prone_annotations'
266-
}
267-
dependencies.runtimeOnly libraries.errorprone
268-
}
269-
270208
appendToProperty = { Property<String> property, String value, String separator ->
271209
if (property.present) {
272210
property.set(property.get() + separator + value)
@@ -276,25 +214,6 @@ subprojects {
276214
}
277215
}
278216

279-
configurations {
280-
// Detect Maven Enforcer's dependencyConvergence failures. We only
281-
// care for artifacts used as libraries by others.
282-
if (isAndroid && !(project.name in ['grpc-android-interop-testing'])) {
283-
releaseRuntimeClasspath {
284-
resolutionStrategy.failOnVersionConflict()
285-
}
286-
}
287-
if (!isAndroid && !(project.name in [
288-
'grpc-benchmarks',
289-
'grpc-interop-testing',
290-
'grpc-gae-interop-testing-jdk8',
291-
])) {
292-
runtimeClasspath {
293-
resolutionStrategy.failOnVersionConflict()
294-
}
295-
}
296-
}
297-
298217
// Disable JavaDoc doclint on Java 8. It's annoying.
299218
if (JavaVersion.current().isJava8Compatible()) {
300219
allprojects {
@@ -406,6 +325,19 @@ subprojects {
406325
}
407326
}
408327

328+
plugins.withId("java-library") {
329+
// Detect Maven Enforcer's dependencyConvergence failures. We only care
330+
// for artifacts used as libraries by others with Maven.
331+
tasks.register('checkUpperBoundDeps') {
332+
doLast {
333+
requireUpperBoundDepsMatch(configurations.runtimeClasspath, project)
334+
}
335+
}
336+
tasks.named('compileJava') {
337+
dependsOn checkUpperBoundDeps
338+
}
339+
}
340+
409341
plugins.withId("me.champeau.gradle.jmh") {
410342
dependencies {
411343
jmh 'org.openjdk.jmh:jmh-core:1.19',
@@ -582,3 +514,56 @@ subprojects {
582514
}
583515
}
584516
}
517+
518+
class DepAndParents {
519+
DependencyResult dep
520+
List<String> parents
521+
}
522+
523+
/**
524+
* Make sure that Maven would select the same versions as Gradle selected.
525+
* This is essentially the same as if we used Maven Enforcer's
526+
* requireUpperBoundDeps for our artifacts.
527+
*/
528+
def requireUpperBoundDepsMatch(Configuration conf, Project project) {
529+
// artifact name => version
530+
Map<String,String> golden = conf.resolvedConfiguration.resolvedArtifacts.collectEntries {
531+
ResolvedArtifact it ->
532+
ModuleVersionIdentifier id = it.moduleVersion.id
533+
[id.group + ":" + id.name, id.version]
534+
}
535+
// Breadth-first search like Maven for dependency resolution
536+
Queue<DepAndParents> queue = new ArrayDeque<>()
537+
conf.incoming.resolutionResult.root.dependencies.each {
538+
queue.add(new DepAndParents(dep: it, parents: [project.displayName]))
539+
}
540+
Set<String> found = new HashSet<>()
541+
while (!queue.isEmpty()) {
542+
DepAndParents depAndParents = queue.remove()
543+
ResolvedDependencyResult result = (ResolvedDependencyResult) depAndParents.dep
544+
ModuleVersionIdentifier id = result.selected.moduleVersion
545+
String artifact = id.group + ":" + id.name
546+
if (found.contains(artifact))
547+
continue
548+
found.add(artifact)
549+
String version
550+
if (result.requested instanceof ProjectComponentSelector) {
551+
ProjectComponentSelector selector = (ProjectComponentSelector) result.requested
552+
version = project.findProject(selector.projectPath).version
553+
} else {
554+
version = ((ModuleComponentSelector) result.requested).version
555+
}
556+
String goldenVersion = golden[artifact]
557+
if (goldenVersion != version && "[$goldenVersion]" != version) {
558+
throw new RuntimeException(
559+
"Maven version skew: $artifact ($version != $goldenVersion) "
560+
+ "Bad version dependency path: " + depAndParents.parents
561+
+ " Run './gradlew $project.path:dependencies --configuration $conf.name' "
562+
+ "to diagnose")
563+
}
564+
result.selected.dependencies.each {
565+
queue.add(new DepAndParents(
566+
dep: it, parents: depAndParents.parents + [artifact + ":" + version]))
567+
}
568+
}
569+
}

Diff for: buildscripts/kokoro/unix.sh

-2
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,6 @@ export LDFLAGS=-L/tmp/protobuf/lib
4646
export CXXFLAGS="-I/tmp/protobuf/include"
4747

4848
./gradlew clean $GRADLE_FLAGS
49-
# Ensure dependency convergence
50-
./gradlew :grpc-all:dependencies $GRADLE_FLAGS
5149

5250
if [[ -z "${SKIP_TESTS:-}" ]]; then
5351
# Ensure all *.proto changes include *.java generated code

Diff for: census/build.gradle

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ evaluationDependsOn(project(':grpc-api').path)
99

1010
dependencies {
1111
api project(':grpc-api')
12-
guavaDependency 'implementation'
13-
censusApiDependency 'implementation'
14-
censusGrpcMetricDependency 'implementation'
12+
implementation libraries.guava,
13+
libraries.opencensus_api,
14+
libraries.opencensus_contrib_grpc_metrics
1515

1616
testImplementation project(':grpc-api').sourceSets.test.output,
1717
project(':grpc-context').sourceSets.test.output,

Diff for: core/build.gradle

+3-3
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ dependencies {
2727
implementation libraries.gson,
2828
libraries.android_annotations,
2929
libraries.animalsniffer_annotations,
30-
libraries.errorprone
31-
guavaDependency 'implementation'
32-
perfmarkDependency 'implementation'
30+
libraries.errorprone,
31+
libraries.guava,
32+
libraries.perfmark
3333
testImplementation project(':grpc-context').sourceSets.test.output,
3434
project(':grpc-api').sourceSets.test.output,
3535
project(':grpc-testing'),

Diff for: cronet/build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ android {
3535
dependencies {
3636
api project(':grpc-core'),
3737
libraries.cronet_api
38-
guavaDependency 'implementation'
38+
implementation libraries.guava
3939
testImplementation project(':grpc-testing')
4040

4141
testImplementation libraries.cronet_embedded

Diff for: examples/example-gauth/pom.xml

-5
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,6 @@
4949
<groupId>io.grpc</groupId>
5050
<artifactId>grpc-auth</artifactId>
5151
</dependency>
52-
<dependency>
53-
<groupId>com.google.protobuf</groupId>
54-
<artifactId>protobuf-java-util</artifactId>
55-
<version>${protobuf.version}</version>
56-
</dependency>
5752
<dependency>
5853
<groupId>org.apache.tomcat</groupId>
5954
<artifactId>annotations-api</artifactId>

Diff for: grpclb/build.gradle

+3-7
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,9 @@ dependencies {
1414
implementation project(':grpc-core'),
1515
project(':grpc-protobuf'),
1616
project(':grpc-stub'),
17-
libraries.protobuf
18-
implementation (libraries.protobuf_util) {
19-
// prefer our own versions instead of protobuf-util's dependency
20-
exclude group: 'com.google.guava', module: 'guava'
21-
exclude group: 'com.google.errorprone', module: 'error_prone_annotations'
22-
}
23-
guavaDependency 'implementation'
17+
libraries.protobuf,
18+
libraries.protobuf_util,
19+
libraries.guava
2420
runtimeOnly libraries.errorprone
2521
compileOnly libraries.javax_annotation
2622
testImplementation libraries.truth,

Diff for: interop-testing/build.gradle

+3-3
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,9 @@ dependencies {
2828
project(':grpc-testing'),
2929
project(path: ':grpc-xds', configuration: 'shadow'),
3030
libraries.junit,
31-
libraries.truth
32-
censusGrpcMetricDependency 'implementation'
33-
googleOauth2Dependency 'implementation'
31+
libraries.truth,
32+
libraries.opencensus_contrib_grpc_metrics,
33+
libraries.google_auth_oauth2_http
3434
compileOnly libraries.javax_annotation
3535
// TODO(sergiitk): replace with com.google.cloud:google-cloud-logging
3636
// Used instead of google-cloud-logging because it's failing

Diff for: netty/build.gradle

+4-3
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ evaluationDependsOn(project(':grpc-core').path)
1818
dependencies {
1919
api project(':grpc-core'),
2020
libraries.netty
21-
implementation libraries.netty_proxy_handler
22-
guavaDependency 'implementation'
23-
perfmarkDependency 'implementation'
21+
implementation libraries.netty_proxy_handler,
22+
libraries.guava,
23+
libraries.errorprone,
24+
libraries.perfmark
2425

2526
// Tests depend on base class defined by core module.
2627
testImplementation project(':grpc-core').sourceSets.test.output,

Diff for: okhttp/build.gradle

+5-8
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,11 @@ description = "gRPC: OkHttp"
1111
evaluationDependsOn(project(':grpc-core').path)
1212

1313
dependencies {
14-
api project(':grpc-core')
15-
api (libraries.okhttp) {
16-
// prefer our own versions instead of okhttp's dependency
17-
exclude group: 'com.squareup.okio', module: 'okio'
18-
}
19-
implementation libraries.okio
20-
guavaDependency 'implementation'
21-
perfmarkDependency 'implementation'
14+
api project(':grpc-core'),
15+
libraries.okhttp
16+
implementation libraries.okio,
17+
libraries.guava,
18+
libraries.perfmark
2219
// Tests depend on base class defined by core module.
2320
testImplementation project(':grpc-core').sourceSets.test.output,
2421
project(':grpc-api').sourceSets.test.output,

Diff for: protobuf-lite/build.gradle

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ description = 'gRPC: Protobuf Lite'
1212
dependencies {
1313
api project(':grpc-api'),
1414
libraries.protobuf_lite
15-
implementation libraries.jsr305
16-
guavaDependency 'implementation'
15+
implementation libraries.jsr305,
16+
libraries.guava
1717

1818
testImplementation project(':grpc-core')
1919

Diff for: protobuf/build.gradle

+1-3
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,12 @@ dependencies {
1313
api project(':grpc-api'),
1414
libraries.jsr305,
1515
libraries.protobuf
16-
guavaDependency 'implementation'
16+
implementation libraries.guava
1717

1818
api (libraries.google_api_protos) {
1919
// 'com.google.api:api-common' transitively depends on auto-value, which breaks our
2020
// annotations.
2121
exclude group: 'com.google.api', module: 'api-common'
22-
// Prefer our more up-to-date protobuf over 3.2.0
23-
exclude group: 'com.google.protobuf', module: 'protobuf-java'
2422
}
2523

2624
api (project(':grpc-protobuf-lite')) {

0 commit comments

Comments
 (0)