From b65f6826fb04d2c634a61549541ac6b3489aa226 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Fri, 2 Aug 2024 15:23:37 -0400 Subject: [PATCH 1/8] preparing release 7.0.0-lts --- boms/cloud-lts-bom/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boms/cloud-lts-bom/pom.xml b/boms/cloud-lts-bom/pom.xml index 8ab5121d3..5ac91da2a 100644 --- a/boms/cloud-lts-bom/pom.xml +++ b/boms/cloud-lts-bom/pom.xml @@ -7,7 +7,7 @@ com.google.cloud gcp-lts-bom - 7.0.0-SNAPSHOT + 7.0.0 pom Google Cloud Long Term Support BOM From 87fc40c390f36e6c595038c7b6f65eddcf8a8e0d Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Fri, 2 Aug 2024 15:23:39 -0400 Subject: [PATCH 2/8] 7.0.1-SNAPSHOT --- boms/cloud-lts-bom/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boms/cloud-lts-bom/pom.xml b/boms/cloud-lts-bom/pom.xml index 5ac91da2a..61f7c8d21 100644 --- a/boms/cloud-lts-bom/pom.xml +++ b/boms/cloud-lts-bom/pom.xml @@ -7,7 +7,7 @@ com.google.cloud gcp-lts-bom - 7.0.0 + 7.0.1-SNAPSHOT pom Google Cloud Long Term Support BOM From 17e08dd983a15834e615c71e458b7214532ba136 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Thu, 19 Sep 2024 11:58:51 -0400 Subject: [PATCH 3/8] attempt to reproduce issue. compile with no error. --- boms/test/README.md | 18 +++++ boms/test/pom.xml | 75 +++++++++++++++++++ .../google/cloud/tools/opensource/Main.java | 8 ++ boms/test/src/main/proto/book.proto | 19 +++++ 4 files changed, 120 insertions(+) create mode 100644 boms/test/README.md create mode 100644 boms/test/pom.xml create mode 100644 boms/test/src/main/java/com/google/cloud/tools/opensource/Main.java create mode 100644 boms/test/src/main/proto/book.proto diff --git a/boms/test/README.md b/boms/test/README.md new file mode 100644 index 000000000..ffeff0b7e --- /dev/null +++ b/boms/test/README.md @@ -0,0 +1,18 @@ + +This is a test aiming to reproduce an issue with dependency conflict with a "google/api/field_behavior.proto" file. + +pom included dependencies of question: +proto-google-common-protos and beam-vendor-grpc-1_60_1: +``` +[INFO] --- dependency:3.7.0:tree (default-cli) @ test --- +[INFO] com.google.cloud.tools.opensource:test:jar:1.0-SNAPSHOT +[INFO] \- org.apache.beam:beam-sdks-java-core:jar:2.57.0:compile +[INFO] \- org.apache.beam:beam-vendor-grpc-1_60_1:jar:0.2:compile +``` + +``` +[INFO] com.google.cloud.tools.opensource:test:jar:1.0-SNAPSHOT +[INFO] \- com.google.api.grpc:proto-google-common-protos:jar:2.39.0:compile +``` + +Use the mvn clean compile command to trigger the Protocol Buffer compilation. This will generate Java source files in the target/generated-sources/protobuf directory. \ No newline at end of file diff --git a/boms/test/pom.xml b/boms/test/pom.xml new file mode 100644 index 000000000..3656e464d --- /dev/null +++ b/boms/test/pom.xml @@ -0,0 +1,75 @@ + + + 4.0.0 + + com.google.cloud.tools.opensource + cloud-tools-opensource-boms + 1.0-SNAPSHOT + + + test + + + + + com.google.cloud + gcp-lts-bom + 7.0.0 + pom + import + + + + + + + com.google.api.grpc + proto-google-common-protos + + + org.apache.beam + beam-sdks-java-core + + + + com.google.protobuf + protobuf-java + + + + + + + + kr.motd.maven + os-maven-plugin + 1.7.0 + + + + + com.github.os72 + protoc-jar-maven-plugin + 3.11.4 + + + generate-sources + + run + + + + src/main/proto + + direct + true + ${project.build.directory}/generated-sources/protobuf + + + + + + + \ No newline at end of file diff --git a/boms/test/src/main/java/com/google/cloud/tools/opensource/Main.java b/boms/test/src/main/java/com/google/cloud/tools/opensource/Main.java new file mode 100644 index 000000000..5e4f72233 --- /dev/null +++ b/boms/test/src/main/java/com/google/cloud/tools/opensource/Main.java @@ -0,0 +1,8 @@ +package com.google.cloud.tools.opensource; + +public class Main { + + public static void main(String[] args) { + System.out.println("Hello world!"); + } +} \ No newline at end of file diff --git a/boms/test/src/main/proto/book.proto b/boms/test/src/main/proto/book.proto new file mode 100644 index 000000000..9db1c70ae --- /dev/null +++ b/boms/test/src/main/proto/book.proto @@ -0,0 +1,19 @@ +syntax = "proto3"; + +package google.api; + +import "google/api/resource.proto"; +import "google/api/field_behavior.proto"; + +message Book { + option (google.api.resource) = { + type: "library.googleapis.com/Book" + pattern: + "shelves/{shelf}/books/{book}" + }; + + string name = 1 + [(google.api.field_behavior) = IDENTIFIER]; + string title = 2; + string author = 3; +} \ No newline at end of file From 750ce2ac9f09726d3e6748ec40d7f184f7fd7af5 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Wed, 25 Sep 2024 11:02:29 -0400 Subject: [PATCH 4/8] test module to build with gradle plugin. --- .gitignore | 3 +++ boms/test-gradle/build.gradle | 24 +++++++++++++++++++ boms/test-gradle/settings.gradle | 2 ++ .../src/main/java/org/example/Main.java | 8 +++++++ boms/test-gradle/src/main/proto/sample.proto | 7 ++++++ 5 files changed, 44 insertions(+) create mode 100644 boms/test-gradle/build.gradle create mode 100644 boms/test-gradle/settings.gradle create mode 100644 boms/test-gradle/src/main/java/org/example/Main.java create mode 100644 boms/test-gradle/src/main/proto/sample.proto diff --git a/.gitignore b/.gitignore index f40f7ab85..c09c4a5be 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,6 @@ example-problems/*/gradlew example-problems/*/gradlew.bat gradle-plugin/.gradle/ .mvn/wrapper/maven-wrapper.jar + +.gradle/ +build/ \ No newline at end of file diff --git a/boms/test-gradle/build.gradle b/boms/test-gradle/build.gradle new file mode 100644 index 000000000..a831c4254 --- /dev/null +++ b/boms/test-gradle/build.gradle @@ -0,0 +1,24 @@ +plugins { + id 'java' + id "com.google.protobuf" version "0.9.4" +} + +group 'org.example' +version '1.0-SNAPSHOT' + +repositories { + mavenCentral() +} + + +dependencies { + implementation 'org.apache.beam:beam-sdks-java-core:2.57.0' + implementation 'com.google.api.grpc:proto-google-common-protos:2.39.0' + + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' +} + +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/boms/test-gradle/settings.gradle b/boms/test-gradle/settings.gradle new file mode 100644 index 000000000..cdffb8690 --- /dev/null +++ b/boms/test-gradle/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'test-gradle' + diff --git a/boms/test-gradle/src/main/java/org/example/Main.java b/boms/test-gradle/src/main/java/org/example/Main.java new file mode 100644 index 000000000..3016be694 --- /dev/null +++ b/boms/test-gradle/src/main/java/org/example/Main.java @@ -0,0 +1,8 @@ +package org.example; + +public class Main { + + public static void main(String[] args) { + System.out.println("Hello world!"); + } +} \ No newline at end of file diff --git a/boms/test-gradle/src/main/proto/sample.proto b/boms/test-gradle/src/main/proto/sample.proto new file mode 100644 index 000000000..58a30037d --- /dev/null +++ b/boms/test-gradle/src/main/proto/sample.proto @@ -0,0 +1,7 @@ +syntax = "proto3"; + +import "google/api/field_behavior.proto"; + +message SomeMessage { + int64 some_id = 1 [(google.api.field_behavior) = IDENTIFIER]; +} \ No newline at end of file From 639db16cb9b5d23d55b71cd2a73fc8c910cb0d56 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Wed, 25 Sep 2024 16:51:56 -0400 Subject: [PATCH 5/8] use bom. --- boms/test-gradle/README.md | 7 +++++++ boms/test-gradle/build.gradle | 8 ++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 boms/test-gradle/README.md diff --git a/boms/test-gradle/README.md b/boms/test-gradle/README.md new file mode 100644 index 000000000..a294aa837 --- /dev/null +++ b/boms/test-gradle/README.md @@ -0,0 +1,7 @@ + + +run `gradle build` should succeed. +Comment out "exclude" line 18 in build.gradle, expect `gradle build` fail with +``` +> protoc: stdout: . stderr: sample.proto:6:52: Enum type "google.api.FieldBehavior" has no value named "IDENTIFIER" for option "google.api.field_behavior". +``` \ No newline at end of file diff --git a/boms/test-gradle/build.gradle b/boms/test-gradle/build.gradle index a831c4254..a34a4eff1 100644 --- a/boms/test-gradle/build.gradle +++ b/boms/test-gradle/build.gradle @@ -12,8 +12,12 @@ repositories { dependencies { - implementation 'org.apache.beam:beam-sdks-java-core:2.57.0' - implementation 'com.google.api.grpc:proto-google-common-protos:2.39.0' + implementation platform('com.google.cloud:gcp-lts-bom:7.0.1') + implementation('org.apache.beam:beam-sdks-java-core') { + // build excluding this deps with older copy works + exclude group: 'org.apache.beam', module: 'beam-vendor-grpc-1_60_1' + } + implementation 'com.google.api.grpc:proto-google-common-protos' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' From 06abc512af2ab2393afc3fbc6bb5dd8c9222266f Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Thu, 26 Sep 2024 21:41:15 -0400 Subject: [PATCH 6/8] use custom configuration for protoc dependencies. --- boms/test-gradle/build.gradle | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/boms/test-gradle/build.gradle b/boms/test-gradle/build.gradle index a34a4eff1..1109291ee 100644 --- a/boms/test-gradle/build.gradle +++ b/boms/test-gradle/build.gradle @@ -9,15 +9,33 @@ version '1.0-SNAPSHOT' repositories { mavenCentral() } +protobuf { + protoc { + artifact = "com.google.protobuf:protoc:3.25.5" + } +} +// create a custom configuration to use for protobuf dependencies. +// `protobuf`, is a custom configuration introduced by the Protobuf plugin +// Not using it because protobuf configuration doesn't inherit from the BOM. +configurations { + protobufPlatform + protobuf.extendsFrom(protobufPlatform) +} dependencies { + // dependencies used for main project's compilation implementation platform('com.google.cloud:gcp-lts-bom:7.0.1') - implementation('org.apache.beam:beam-sdks-java-core') { - // build excluding this deps with older copy works + implementation('org.apache.beam:beam-sdks-java-core') + implementation('com.google.api.grpc:proto-google-common-protos') + + // dependencies used for protoc + protobufPlatform platform('com.google.cloud:gcp-lts-bom:7.0.1') + protobufPlatform('org.apache.beam:beam-sdks-java-core') { exclude group: 'org.apache.beam', module: 'beam-vendor-grpc-1_60_1' } - implementation 'com.google.api.grpc:proto-google-common-protos' + protobufPlatform 'com.google.api.grpc:proto-google-common-protos' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' From f012589ec4e9848e97a01613ee51272d42afdca4 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Fri, 27 Sep 2024 10:51:47 -0400 Subject: [PATCH 7/8] update readme. --- boms/test-gradle/README.md | 141 ++++++++++++++++++++++++++++++++++++- 1 file changed, 139 insertions(+), 2 deletions(-) diff --git a/boms/test-gradle/README.md b/boms/test-gradle/README.md index a294aa837..67bdb1a5e 100644 --- a/boms/test-gradle/README.md +++ b/boms/test-gradle/README.md @@ -1,7 +1,144 @@ - +## original issue run `gradle build` should succeed. -Comment out "exclude" line 18 in build.gradle, expect `gradle build` fail with +If comment out protobufPlatform configurations in L33-37, expect `gradle build` fail with ``` > protoc: stdout: . stderr: sample.proto:6:52: Enum type "google.api.FieldBehavior" has no value named "IDENTIFIER" for option "google.api.field_behavior". +``` + + +## Now with separate configurations for protobuf plugin +dependencies for main project compilation is not changed, `org.apache.beam:beam-vendor-grpc-1_60_1:0.2` is available. +``` +compileClasspath - Compile classpath for source set 'main'. ++--- com.google.cloud:gcp-lts-bom:7.0.1 +| +--- com.google.api.grpc:proto-google-common-protos:2.39.0 (c) +| +--- org.apache.beam:beam-sdks-java-core:2.57.0 (c) +| +--- com.google.protobuf:protobuf-java:3.25.5 (c) +| \--- com.google.auto.value:auto-value-annotations:1.10.4 (c) ++--- org.apache.beam:beam-sdks-java-core -> 2.57.0 +| +--- org.apache.beam:beam-model-pipeline:2.57.0 +| | +--- org.apache.beam:beam-vendor-grpc-1_60_1:0.2 +| | +--- com.google.auto.value:auto-value-annotations:1.8.2 -> 1.10.4 +| | +--- com.google.errorprone:error_prone_annotations:2.20.0 +| | +--- org.apache.httpcomponents:httpclient:4.5.13 +| | | +--- org.apache.httpcomponents:httpcore:4.4.13 -> 4.4.15 +| | | +--- commons-logging:commons-logging:1.2 +| | | \--- commons-codec:commons-codec:1.11 -> 1.17.0 +| | +--- org.apache.httpcomponents:httpcore:4.4.15 +| | \--- org.conscrypt:conscrypt-openjdk-uber:2.5.2 +| +--- org.apache.beam:beam-model-fn-execution:2.57.0 +| | +--- org.apache.beam:beam-vendor-grpc-1_60_1:0.2 +| | +--- com.google.auto.value:auto-value-annotations:1.8.2 -> 1.10.4 +| | +--- com.google.errorprone:error_prone_annotations:2.20.0 +| | +--- org.apache.httpcomponents:httpclient:4.5.13 (*) +| | +--- org.apache.httpcomponents:httpcore:4.4.15 +| | \--- org.conscrypt:conscrypt-openjdk-uber:2.5.2 +| +--- org.apache.beam:beam-model-job-management:2.57.0 +| | +--- org.apache.beam:beam-vendor-grpc-1_60_1:0.2 +| | +--- com.google.auto.value:auto-value-annotations:1.8.2 -> 1.10.4 +| | +--- com.google.errorprone:error_prone_annotations:2.20.0 +| | +--- org.apache.httpcomponents:httpclient:4.5.13 (*) +| | +--- org.apache.httpcomponents:httpcore:4.4.15 +| | \--- org.conscrypt:conscrypt-openjdk-uber:2.5.2 +| +--- org.apache.beam:beam-sdks-java-transform-service-launcher:2.57.0 +| | +--- org.checkerframework:checker-qual:3.42.0 +| | +--- org.apache.beam:beam-vendor-guava-32_1_2-jre:0.1 +| | +--- org.slf4j:slf4j-api:1.7.30 +| | \--- args4j:args4j:2.33 +| +--- org.apache.beam:beam-vendor-grpc-1_60_1:0.2 +| +--- org.apache.beam:beam-vendor-guava-32_1_2-jre:0.1 +| +--- net.bytebuddy:byte-buddy:1.14.12 +| +--- org.antlr:antlr4-runtime:4.7 +| +--- org.apache.commons:commons-compress:1.26.2 +| | +--- commons-codec:commons-codec:1.17.0 +| | +--- commons-io:commons-io:2.16.1 +| | \--- org.apache.commons:commons-lang3:3.14.0 +| +--- org.apache.commons:commons-lang3:3.14.0 +| +--- io.github.classgraph:classgraph:4.8.162 +| +--- com.google.code.findbugs:jsr305:3.0.2 +| +--- com.google.errorprone:error_prone_annotations:2.10.0 -> 2.20.0 +| +--- com.fasterxml.jackson.core:jackson-core:2.15.4 +| | \--- com.fasterxml.jackson:jackson-bom:2.15.4 +| | +--- com.fasterxml.jackson.core:jackson-annotations:2.15.4 (c) +| | +--- com.fasterxml.jackson.core:jackson-core:2.15.4 (c) +| | \--- com.fasterxml.jackson.core:jackson-databind:2.15.4 (c) +| +--- com.fasterxml.jackson.core:jackson-annotations:2.15.4 +| | \--- com.fasterxml.jackson:jackson-bom:2.15.4 (*) +| +--- com.fasterxml.jackson.core:jackson-databind:2.15.4 +| | +--- com.fasterxml.jackson.core:jackson-annotations:2.15.4 (*) +| | +--- com.fasterxml.jackson.core:jackson-core:2.15.4 (*) +| | \--- com.fasterxml.jackson:jackson-bom:2.15.4 (*) +| +--- org.slf4j:slf4j-api:1.7.30 +| +--- org.xerial.snappy:snappy-java:1.1.10.4 +| \--- joda-time:joda-time:2.10.10 +\--- com.google.api.grpc:proto-google-common-protos -> 2.39.0 + \--- com.google.protobuf:protobuf-java:3.25.3 -> 3.25.5 +``` + +`gradle build` can successfully generate proto java classes, because +for protobuf plugin, uses these dependencies. `org.apache.beam:beam-vendor-grpc-1_60_1:0.2` is +excluded. +``` +protobufPlatform ++--- com.google.cloud:gcp-lts-bom:7.0.1 +| +--- com.google.api.grpc:proto-google-common-protos:2.39.0 (c) +| +--- org.apache.beam:beam-sdks-java-core:2.57.0 (c) +| +--- com.google.protobuf:protobuf-java:3.25.5 (c) +| \--- com.google.auto.value:auto-value-annotations:1.10.4 (c) ++--- org.apache.beam:beam-sdks-java-core -> 2.57.0 +| +--- org.apache.beam:beam-model-pipeline:2.57.0 +| | +--- com.google.auto.value:auto-value-annotations:1.8.2 -> 1.10.4 +| | +--- com.google.errorprone:error_prone_annotations:2.20.0 +| | +--- org.apache.httpcomponents:httpclient:4.5.13 +| | | +--- org.apache.httpcomponents:httpcore:4.4.13 -> 4.4.15 +| | | +--- commons-logging:commons-logging:1.2 +| | | \--- commons-codec:commons-codec:1.11 -> 1.17.0 +| | +--- org.apache.httpcomponents:httpcore:4.4.15 +| | \--- org.conscrypt:conscrypt-openjdk-uber:2.5.2 +| +--- org.apache.beam:beam-model-fn-execution:2.57.0 +| | +--- com.google.auto.value:auto-value-annotations:1.8.2 -> 1.10.4 +| | +--- com.google.errorprone:error_prone_annotations:2.20.0 +| | +--- org.apache.httpcomponents:httpclient:4.5.13 (*) +| | +--- org.apache.httpcomponents:httpcore:4.4.15 +| | \--- org.conscrypt:conscrypt-openjdk-uber:2.5.2 +| +--- org.apache.beam:beam-model-job-management:2.57.0 +| | +--- com.google.auto.value:auto-value-annotations:1.8.2 -> 1.10.4 +| | +--- com.google.errorprone:error_prone_annotations:2.20.0 +| | +--- org.apache.httpcomponents:httpclient:4.5.13 (*) +| | +--- org.apache.httpcomponents:httpcore:4.4.15 +| | \--- org.conscrypt:conscrypt-openjdk-uber:2.5.2 +| +--- org.apache.beam:beam-sdks-java-transform-service-launcher:2.57.0 +| | +--- org.checkerframework:checker-qual:3.42.0 +| | +--- org.apache.beam:beam-vendor-guava-32_1_2-jre:0.1 +| | +--- org.slf4j:slf4j-api:1.7.30 +| | \--- args4j:args4j:2.33 +| +--- org.apache.beam:beam-vendor-guava-32_1_2-jre:0.1 +| +--- net.bytebuddy:byte-buddy:1.14.12 +| +--- org.antlr:antlr4-runtime:4.7 +| +--- org.apache.commons:commons-compress:1.26.2 +| | +--- commons-codec:commons-codec:1.17.0 +| | +--- commons-io:commons-io:2.16.1 +| | \--- org.apache.commons:commons-lang3:3.14.0 +| +--- org.apache.commons:commons-lang3:3.14.0 +| +--- io.github.classgraph:classgraph:4.8.162 +| +--- com.google.code.findbugs:jsr305:3.0.2 +| +--- com.google.errorprone:error_prone_annotations:2.10.0 -> 2.20.0 +| +--- com.fasterxml.jackson.core:jackson-core:2.15.4 +| | \--- com.fasterxml.jackson:jackson-bom:2.15.4 +| | +--- com.fasterxml.jackson.core:jackson-annotations:2.15.4 (c) +| | +--- com.fasterxml.jackson.core:jackson-core:2.15.4 (c) +| | \--- com.fasterxml.jackson.core:jackson-databind:2.15.4 (c) +| +--- com.fasterxml.jackson.core:jackson-annotations:2.15.4 +| | \--- com.fasterxml.jackson:jackson-bom:2.15.4 (*) +| +--- com.fasterxml.jackson.core:jackson-databind:2.15.4 +| | +--- com.fasterxml.jackson.core:jackson-annotations:2.15.4 (*) +| | +--- com.fasterxml.jackson.core:jackson-core:2.15.4 (*) +| | \--- com.fasterxml.jackson:jackson-bom:2.15.4 (*) +| +--- org.slf4j:slf4j-api:1.7.30 +| +--- org.xerial.snappy:snappy-java:1.1.10.4 +| \--- joda-time:joda-time:2.10.10 +\--- com.google.api.grpc:proto-google-common-protos -> 2.39.0 + \--- com.google.protobuf:protobuf-java:3.25.3 -> 3.25.5 + ``` \ No newline at end of file From 32e2fdd8051b3c260411662d433b0d1c12c816f9 Mon Sep 17 00:00:00 2001 From: Min Zhu Date: Fri, 27 Sep 2024 12:15:33 -0400 Subject: [PATCH 8/8] add to readme. --- boms/test-gradle/README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/boms/test-gradle/README.md b/boms/test-gradle/README.md index 67bdb1a5e..7fa08b5f1 100644 --- a/boms/test-gradle/README.md +++ b/boms/test-gradle/README.md @@ -8,6 +8,10 @@ If comment out protobufPlatform configurations in L33-37, expect `gradle build` ## Now with separate configurations for protobuf plugin +Run `gradle dependencies` and observe that +beam-vendor-grpc-1_60_1 is included for compileClasspath configuration (normal Java app) +while the "protobuf" configuration does not have it. + dependencies for main project compilation is not changed, `org.apache.beam:beam-vendor-grpc-1_60_1:0.2` is available. ``` compileClasspath - Compile classpath for source set 'main'. @@ -140,5 +144,4 @@ protobufPlatform | \--- joda-time:joda-time:2.10.10 \--- com.google.api.grpc:proto-google-common-protos -> 2.39.0 \--- com.google.protobuf:protobuf-java:3.25.3 -> 3.25.5 - ``` \ No newline at end of file