diff --git a/.agents/tasks/lazy-task-wiring.md b/.agents/tasks/lazy-task-wiring.md new file mode 100644 index 00000000000..baf7b1a6799 --- /dev/null +++ b/.agents/tasks/lazy-task-wiring.md @@ -0,0 +1,61 @@ +--- +slug: lazy-task-wiring +branch: claude/lazy-task-wiring-a189e2 +owner: claude +status: in-review +started: 2026-10-29 +--- + +## Goal + +The Compiler Gradle plugin creates no tasks by name while the projects are +configured (SpineEventEngine/compiler#98). The dependencies between the launch, +`generate*Proto`, `compile*Java`, `compile*Kotlin`, and `ksp*Kotlin` tasks stay +as they are. + +## Context + +- Gradle forbids `named(…).configure {}`, `configureEach {}`, and + `afterEvaluate {}` inside a lazy task configuration action (probed on 9.8.2: + "cannot be executed in the current context"). So the `generateProto` + `configureEach` action cannot wire the launch task, as the issue suggests. + The launch task wires itself to its `generateProto` task instead. +- The `afterEvaluate` fallback for late source sets depended on that wiring. + `sourceSets.configureEach` replaces it. +- `configureGenerateProtoTasks()` stays eager. Configuring a `GenerateProtoTask` + rewrites its source set's directories (tool-base `configureSourceSetDirs()`). + That must happen before protobuf-gradle-plugin's `postConfigure` hands the + directories to the IDEA plugin. With `configureEach`, reading them creates + the task mid-iteration and the build fails with `ConcurrentModificationException`. + The Protobuf plugin creates the tasks there anyway when `idea` is applied. + +## Plan + +- [x] `TaskLocator`: add `exists()` and `named()`; keep `find()` / `get()`. +- [x] `createTasks()`: `sourceSets.configureEach`; wire compile and KSP tasks with + `tasks.named { … }.configureEach { dependsOn(launchTask) }`. +- [x] `configureGenerateProtoTasks()`: the action configures only the + `GenerateProtoTask` itself; iteration stays eager (see Context). +- [x] `LaunchSpineCompiler.setDependencies()`: depend on `generate*Proto` by provider; + drop `findJavaCompileFor()` / `findKotlinCompileFor()`. +- [x] Tests: no tasks created at configuration time; dependencies still set; + late source set and late KSP task; `generateProto` gets the Compiler plugin + and the source set gets the generated directories; `idea` applied. +- [x] `./gradlew build`; measure a real consumer with the task stats. + `ksp-test` fixture, `./gradlew help`: `.081` created 14 of 53 tasks, `.082` + created 6 (the `launch*`, `compile*Java`, `compile*Kotlin`, `ksp*Kotlin` tasks + are no longer created). In `tests/`, the same tasks are now created first by + `buildSrc`'s `DokkaExts.disableDocumentationTasks` (SpineEventEngine/config#820). + +## Log + +- 2026-10-29 — drafted and started. +- 2026-10-29 — `configureEach` for `generateProto` broke `integrationTest` (CME with + `idea`); reverted that call site to eager iteration, added a regression test. +- 2026-10-29 — `gradle-review`: no blockers. Added tests for the `clean` wiring, + `protoSourceDirs` contents, and the Kotlin compile task; `createCleanTask()` takes + the launch task provider. Not staged for commit: the version bump and dependency + reports (separate commits per the version policy). +- 2026-10-29 — pre-PR review (`spine-code-review`, `kotlin-engineer`, `review-docs`): no + Must-fix. Added a test creating each task as soon as it is registered, made + `consumeProtoFrom()` private, and fixed KDoc wording. diff --git a/.github/workflows/increment-guard.yml b/.github/workflows/increment-guard.yml index 9a6333f0ff4..56f30088541 100644 --- a/.github/workflows/increment-guard.yml +++ b/.github/workflows/increment-guard.yml @@ -1,9 +1,13 @@ # Guards the project version by executing the Gradle `checkVersionIncrement` task, # which verifies that the version is both (a) strictly greater than the base branch # version in `version.gradle.kts` and (b) not already published. The result is -# published as the `Version Guard` commit status — the context required by branch -# protection and re-published by `revalidate-versions.yml` on the heads of other open -# PRs when the base branch advances (so a stale duplicate bump turns red before merge). +# published as the `Version bump` commit status, which `revalidate-versions.yml` also +# re-publishes on the heads of other open PRs when the base branch advances (so a stale +# duplicate bump turns red before merge). +# +# Branch protection must require the `Version bump` status, not the job's own +# `Check version increment` check run: only the status carries the revalidation verdict, +# and a job skipped by its `if` reports a passing check run. # # The check runs only for pull requests targeting a default (`master`/`main`) or # a release-line (e.g. `2.x-jdk8-master`) branch. It is the responsibility of a branch @@ -21,7 +25,7 @@ on: pull_request: # Beyond the default activity types (`opened`, `synchronize`, `reopened`), two more are # needed because they change what the guard must compare against without a new head SHA, - # which would otherwise leave a stale-green `Version Guard` status mergeable: + # which would otherwise leave a stale-green `Version bump` status mergeable: # * `ready_for_review` — a draft that went stale while in draft becomes ready; and # * `edited` — the base branch is retargeted (e.g. a release line -> `master`), so the # strict comparison must be recomputed against the new base. @@ -39,7 +43,7 @@ jobs: (endsWith(github.base_ref, 'master') || endsWith(github.base_ref, 'main')) && (github.event.action != 'edited' || github.event.changes.base.ref.from != '') - # `statuses: write` lets the job publish the `Version Guard` commit status that + # `statuses: write` lets the job publish the `Version bump` commit status that # branch protection requires. permissions: contents: read @@ -76,7 +80,7 @@ jobs: VERSION_GUARD: "true" run: ./gradlew checkVersionIncrement --stacktrace - # Publish the verdict as the `Version Guard` commit status on the PR head. Posting it + # Publish the verdict as the `Version bump` commit status on the PR head. Posting it # on every run (success or failure) is what lets a later re-bump clear a failure that # `revalidate-versions.yml` set when the base branch advanced. # @@ -84,7 +88,7 @@ jobs: # posted (and a fork head SHA is not in this repo). The Spine agent workflow pushes PR # branches to the same repository; fork contributions are handled by a maintainer, who # owns the version bump. - - name: Report the Version Guard status + - name: Report the Version bump status if: always() && github.event.pull_request.head.repo.fork == false shell: bash env: @@ -96,5 +100,5 @@ jobs: fi gh api -X POST "repos/${{ github.repository }}/statuses/${{ github.event.pull_request.head.sha }}" \ -f state="${state}" \ - -f context="Version Guard" \ + -f context="Version bump" \ -f description="Version increment check" diff --git a/.github/workflows/publish-api-docs.yml b/.github/workflows/publish-api-docs.yml new file mode 100644 index 00000000000..1f6f17a18f2 --- /dev/null +++ b/.github/workflows/publish-api-docs.yml @@ -0,0 +1,100 @@ +name: Publish API docs + +# Publishes the API documentation of the published modules to the `gh-pages` +# branch, which GitHub Pages serves under `spine.io`. +# +# The job runs apart from `publish.yml`, and neither waits for the other: a +# failure here leaves the Maven publication intact, and the other way round. +# `:publishAllApiDocs` is registered in the root project by `spinePublishing { }`, +# so it exists in every repository that publishes, and does nothing where no +# module documents itself. It runs the `publishApiDocs` task of each published +# module. +# +# Only release versions are published. For a snapshot version, which most merges +# produce, the task of each module only says that it skips the documentation, +# and the run ends green without generating or pushing any. +# +# A failed run can be re-run as it is: each module commits its documentation +# even when nothing changed, and retries a failed push after rebasing onto +# `gh-pages`. +# +# Each module clones only the tip of `gh-pages` and checks out only its own most +# recent documentation and the folder of its version, so that a run does not +# fetch the documentation of every version of every module. +# +# This job does not ask whether `publish.yml` could publish the version, as that +# would couple the two again. When `publish.yml` stops because the version is +# already taken, which the version checks of pull requests normally prevent, +# this job still copies the documentation of the commit over that of the version. +# +# Unlike `publish.yml`, this file is not a caller of a reusable workflow hosted +# by `config`. That split exists to make `config` the signer of the attestation +# of the published artifacts, and the documentation is not attested. + +on: + push: + branches: [master] + +jobs: + publish-api-docs: + name: Publish API docs + runs-on: ubuntu-latest + + # One run at a time per repository, since every run pushes to `gh-pages`. + # `queue: max` keeps up to 100 runs waiting, instead of letting a newer run + # cancel the waiting one: each run also adds the documentation of its own + # version, which no later run would publish. The waiting runs may start in + # another order than their commits, so a run replaces the most recent + # documentation of a module only if no newer version is published already. + concurrency: + group: api-docs-publishing-${{ github.repository }} + queue: max + + # The push goes over SSH with the deploy key, not with `GITHUB_TOKEN`, which + # only reads the dependencies published to GitHub Packages. + permissions: + contents: read + packages: read + + steps: + - uses: actions/checkout@v6 + with: + submodules: 'true' + + - uses: actions/setup-java@v5 + with: + java-version: 17 + distribution: zulu + + # The cache stays on, unlike in `publishing.yml`, which turns it off to keep + # its attestation trustworthy: nothing here is attested. A run on `master` + # reads only the caches written by the workflows that run on `master`. + - uses: gradle/actions/setup-gradle@v6 + + # The same step as in `build-on-ubuntu.yml`. For a release version, the build + # runs Dokka for every published module, in parallel, and the Linux runner + # ships with effectively no swap, so a memory peak would be an instant OOM + # kill. It goes before the deploy key is decrypted, so that the action never + # runs with the key on disk. + - name: Add swap space + uses: pierotofy/set-swap-space@v1.0 + with: + swap-size-gb: 8 + + - name: Decrypt Git SSH credentials + run: ./config/scripts/decrypt.sh "$GIT_CREDENTIALS_KEY" ./.github/keys/deploy_key_rsa.gpg ./deploy_key_rsa + env: + GIT_CREDENTIALS_KEY: ${{ secrets.GIT_CREDENTIALS_KEY }} + + # The SSH agent does not accept a key that others can read. The build runs + # `register-ssh-key.sh` to hand the key to the agent. + - name: Set file system permissions + run: chmod 400 ./deploy_key_rsa && chmod +x ./config/scripts/register-ssh-key.sh + + - name: Publish API docs + run: ./gradlew :publishAllApiDocs --stacktrace + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + FORMAL_GIT_HUB_PAGES_AUTHOR: developers@spine.io + # https://docs.github.com/en/actions/reference/environment-variables + REPO_SLUG: ${{ github.repository }} # e.g. SpineEventEngine/core-jvm diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 8dc7c913bbf..ba2a29ac66b 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -30,7 +30,7 @@ on: jobs: publish: name: Publish to Maven repositories - uses: SpineEventEngine/config/.github/workflows/publishing.yml@ad3dff0890c7716cbba3fe2ce17bf4fe5905a318 + uses: SpineEventEngine/config/.github/workflows/publishing.yml@ba47b5fb7a92ad1e5399c8714edd2160e0265a82 permissions: contents: read packages: write diff --git a/.github/workflows/revalidate-versions.yml b/.github/workflows/revalidate-versions.yml index 2d2ecb9c6d4..59f81803cc8 100644 --- a/.github/workflows/revalidate-versions.yml +++ b/.github/workflows/revalidate-versions.yml @@ -5,7 +5,7 @@ # now stale: its publish would collide. GitHub does not re-run a PR's checks when its base # advances, so this workflow does it actively — for each other open PR whose # `version.gradle.kts` version is `<=` the new base version, it posts a failing -# `Version Guard` commit status on the PR head, blocking the merge until the author +# `Version bump` commit status on the PR head, blocking the merge until the author # re-bumps. The status self-clears: the re-bump push runs `increment-guard.yml`, which # posts a fresh `success` on the new head. # @@ -40,14 +40,14 @@ concurrency: jobs: revalidate: - name: Revalidate open PRs + name: Mark stale version bumps runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 with: submodules: 'true' - - name: Revalidate open PRs against the new base version + - name: Mark open PRs whose version is not above the new base shell: bash env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/buildSrc/src/main/kotlin/BuildExtensions.kt b/buildSrc/src/main/kotlin/BuildExtensions.kt index a69dd1a044d..cedacd25886 100644 --- a/buildSrc/src/main/kotlin/BuildExtensions.kt +++ b/buildSrc/src/main/kotlin/BuildExtensions.kt @@ -27,7 +27,6 @@ import io.spine.dependency.test.Kotest import io.spine.dependency.test.Kover import io.spine.gradle.repo.standardToSpineSdk import org.gradle.api.Project -import org.gradle.api.Task import org.gradle.api.artifacts.ModuleDependency import org.gradle.api.file.DuplicatesStrategy import org.gradle.api.tasks.JavaExec @@ -168,7 +167,8 @@ fun Project.configureTaskDependencies() { * Creates a dependency between the Gradle task of *this* name * onto the task with `taskName`. * - * If either of the tasks does not exist in the enclosing `Project`, + * Neither task is created by this method. A task registered later is matched too. + * If either of the tasks never exists in the enclosing `Project`, * this method does nothing. * * This extension is kept local to the `configureTaskDependencies` extension @@ -176,9 +176,8 @@ fun Project.configureTaskDependencies() { */ fun String.dependOn(taskName: String) { val whoDepends = this - val dependOntoTask: Task? = tasks.findByName(taskName) - dependOntoTask?.let { - tasks.findByName(whoDepends)?.dependsOn(it) + tasks.named { it == whoDepends }.configureEach { + dependsOn(tasks.named { it == taskName }) } } @@ -267,19 +266,20 @@ fun JavaExec.remoteDebug(enabled: Boolean = true) { * * The port number is [5566][BuildSettings.REMOTE_DEBUG_PORT]. * + * The task is configured when Gradle creates it, so calling this function + * does not create the task. + * * @param enabled If `true` the task will be suspended. - * @throws IllegalStateException if the task with the given name is not found, or, - * if the task is not of [JavaExec] type. + * @throws IllegalStateException if the task with the given name is not found. + * @throws org.gradle.api.InvalidUserDataException if the task is not of [JavaExec] type. */ fun Project.setRemoteDebug(taskName: String, enabled: Boolean = true) { - val task = tasks.findByName(taskName) - check(task != null) { + check(taskName in tasks.names) { "Could not find a task named `$taskName` in the project `$name`." } - check(task is JavaExec) { - "The task `$taskName` is not of type `JavaExec`." + tasks.named(taskName, JavaExec::class.java).configure { + remoteDebug(enabled) } - task.remoteDebug(enabled) } /** diff --git a/buildSrc/src/main/kotlin/DokkaExts.kt b/buildSrc/src/main/kotlin/DokkaExts.kt index 5aeba8d5f0b..849fea6cb28 100644 --- a/buildSrc/src/main/kotlin/DokkaExts.kt +++ b/buildSrc/src/main/kotlin/DokkaExts.kt @@ -25,6 +25,7 @@ import org.gradle.api.artifacts.dsl.DependencyHandler import org.gradle.api.execution.TaskExecutionGraph import org.gradle.api.publish.maven.tasks.PublishToMavenLocal import org.gradle.api.publish.maven.tasks.PublishToMavenRepository +import org.gradle.api.tasks.TaskCollection import org.gradle.api.tasks.TaskContainer import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.bundling.Jar @@ -183,14 +184,22 @@ fun DokkaExtension.configureForJava(project: Project, sourceLinkRemoteUrl: Strin } /** - * Finds the `dokkaGenerateHtml` Gradle task. + * Finds the `dokkaGeneratePublicationHtml` Gradle task without creating it. + * + * The returned collection is live: it holds the task once Dokka registers it, + * and stays empty in a project without Dokka. */ -fun TaskContainer.dokkaHtmlTask(): Task? = this.findByName("dokkaGeneratePublicationHtml") +fun TaskContainer.dokkaHtmlTask(): TaskCollection = + named { it == "dokkaGeneratePublicationHtml" } /** - * Finds the `dokkaGeneratePublicationJavadoc` Gradle task. + * Finds the `dokkaGeneratePublicationJavadoc` Gradle task without creating it. + * + * The returned collection is live: it holds the task once Dokka registers it, + * and stays empty in a project without Dokka. */ -fun TaskContainer.dokkaJavadocTask(): Task? = this.findByName("dokkaGeneratePublicationJavadoc") +fun TaskContainer.dokkaJavadocTask(): TaskCollection = + named { it == "dokkaGeneratePublicationJavadoc" } /** * Locates or creates the `htmlDocsJar` task in this [Project]. @@ -205,9 +214,7 @@ fun Project.htmlDocsJar(): TaskProvider = tasks.getOrCreate("htmlDocsJar") archiveClassifier.set("html-docs") from(files(dokkaHtmlOutput())) - tasks.dokkaHtmlTask()?.let { dokkaTask -> - this@getOrCreate.dependsOn(dokkaTask) - } + this@getOrCreate.dependsOn(tasks.dokkaHtmlTask()) } /** @@ -238,7 +245,7 @@ fun Task.isInPublishingGraph(): Boolean = * Such a build usually feeds integration tests, which need the published code, * but not its documentation. So the Dokka tasks are skipped when the execution graph * contains a [PublishToMavenLocal] task and no task that ships the documentation - * elsewhere: `publish`, a [PublishToMavenRepository] task, or `updateGitHubPages`. + * elsewhere: `publish`, a [PublishToMavenRepository] task, or `publishApiDocs`. * They still run if a task named on the command line selects a documentation task of * the graph: one that has `dokka` in its name, ignoring case, or a documentation JAR, * `javadocJar` or `htmlDocsJar`. The name may be the full one, e.g., `dokkaGenerate`, @@ -276,7 +283,7 @@ fun Project.skipDokkaWhenPublishingToMavenLocal() { /** * The names of the lifecycle tasks that ship the documentation beyond Maven Local. */ -private val docsShippingTaskNames = setOf("publish", TaskName.updateGitHubPages) +private val docsShippingTaskNames = setOf("publish", TaskName.publishApiDocs) /** * Tells if this graph publishes to Maven Local and does not contain a task that ships diff --git a/buildSrc/src/main/kotlin/dokka-setup.gradle.kts b/buildSrc/src/main/kotlin/dokka-setup.gradle.kts index c66e9f2f4d4..43eb56fa75b 100644 --- a/buildSrc/src/main/kotlin/dokka-setup.gradle.kts +++ b/buildSrc/src/main/kotlin/dokka-setup.gradle.kts @@ -41,10 +41,7 @@ afterEvaluate { DocumentationSettings.SourceLink.url(project) ) } - val kspKotlin = tasks.findByName("kspKotlin") - kspKotlin?.let { - tasks.withType().configureEach { - dependsOn(kspKotlin) - } + tasks.withType().configureEach { + dependsOn(tasks.named { it == "kspKotlin" }) } } diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/Base.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/Base.kt index baaf5c70531..e58ab1415ef 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/Base.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/Base.kt @@ -25,7 +25,7 @@ package io.spine.dependency.local ) object Base { const val version = "2.0.0-SNAPSHOT.462" - const val versionForBuildScript = "2.0.0-SNAPSHOT.461" + const val versionForBuildScript = "2.0.0-SNAPSHOT.462" const val group = Spine.group private const val prefix = "spine" const val libModule = "$prefix-base" diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/BaseTypes.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/BaseTypes.kt index e0a828061a8..5f08d0bb3e0 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/BaseTypes.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/BaseTypes.kt @@ -23,7 +23,7 @@ package io.spine.dependency.local "ConstPropertyName" /* We use a custom convention for artifact properties. */, ) object BaseTypes { - const val version = "2.0.0-SNAPSHOT.225" + const val version = "2.0.0-SNAPSHOT.227" const val group = Spine.group const val artifact = "spine-base-types" const val lib = "$group:$artifact:$version" diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/Change.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/Change.kt index 1f6ebe1c627..91864be3550 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/Change.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/Change.kt @@ -23,7 +23,7 @@ package io.spine.dependency.local "ConstPropertyName" /* We use a custom convention for artifact properties. */, ) object Change { - const val version = "2.0.0-SNAPSHOT.208" + const val version = "2.0.0-SNAPSHOT.209" const val group = Spine.group const val artifact = "spine-change" const val lib = "$group:$artifact:$version" diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/Compiler.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/Compiler.kt index 137e8301075..b75c0f1ef9c 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/Compiler.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/Compiler.kt @@ -60,7 +60,7 @@ object Compiler : Dependency() { * The version of the Compiler dependencies. */ override val version: String - private const val fallbackVersion = "2.0.0-SNAPSHOT.069" + private const val fallbackVersion = "2.0.0-SNAPSHOT.081" /** * The distinct version of the Compiler used by other build tools. @@ -69,7 +69,7 @@ object Compiler : Dependency() { * transitive dependencies, this is the version used to build the project itself. */ val dogfoodingVersion: String - private const val fallbackDfVersion = "2.0.0-SNAPSHOT.069" + private const val fallbackDfVersion = "2.0.0-SNAPSHOT.081" /** * The artifact for the Compiler Gradle plugin. diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvm.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvm.kt index 35246a7793b..94f3d9d6827 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvm.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvm.kt @@ -30,7 +30,7 @@ typealias CoreJava = CoreJvm ) object CoreJvm { const val group = Spine.group - const val version = "2.0.0-SNAPSHOT.571" + const val version = "2.0.0-SNAPSHOT.577" const val coreArtifact = "spine-core" const val clientArtifact = "spine-client" diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt index 1af2694bb15..aa4d53ec75f 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/CoreJvmCompiler.kt @@ -34,12 +34,12 @@ object CoreJvmCompiler { /** * The version used in the build classpath. */ - const val dogfoodingVersion = "2.0.0-SNAPSHOT.097" + const val dogfoodingVersion = "2.0.0-SNAPSHOT.101" /** * The version to be used for integration tests. */ - const val version = "2.0.0-SNAPSHOT.097" + const val version = "2.0.0-SNAPSHOT.101" /** * The ID of the Gradle plugin. diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/DokkaTools.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/DokkaTools.kt index 6026d49f57e..4591fac501e 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/DokkaTools.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/DokkaTools.kt @@ -25,7 +25,7 @@ package io.spine.dependency.local ) object DokkaTools { const val group = Spine.toolsGroup - const val version = "2.0.0-SNAPSHOT.10" + const val version = "2.0.0-SNAPSHOT.12" /** * The artifact dropped its `spine-` prefix in `2.0.0-SNAPSHOT.9`, to diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/Logging.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/Logging.kt index 96fbbb284c8..b84652a1942 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/Logging.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/Logging.kt @@ -42,7 +42,22 @@ object Logging { // Transitive dependencies. // Make `public` and use them to force a version in a particular repository, if needed. internal const val julBackend = "$group:spine-logging-jul-backend:$version" + + /** + * The former `middleware` module of Spine Logging. + * + * In `2.0.0-SNAPSHOT.270`, its code moved into the `logging` module, which is + * published as [lib]. The last published version of this artifact is + * `2.0.0-SNAPSHOT.263`, so these coordinates do not resolve at [version]. + * + * @deprecated Use [lib] instead. + */ + @Deprecated( + "The module was merged into `spine-logging` and is no longer published.", + ReplaceWith("lib"), + ) const val middleware = "$group:spine-logging-middleware:$version" + internal const val platformGenerator = "$group:spine-logging-platform-generator:$version" internal const val jvmDefaultPlatform = "$group:spine-logging-jvm-default-platform:$version" } diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/Time.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/Time.kt index 3e8e39938d6..d9221c3d7bb 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/Time.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/Time.kt @@ -28,7 +28,7 @@ import io.spine.dependency.Dependency ) object Time : Dependency() { override val group = Spine.group - override val version = "2.0.0-SNAPSHOT.251" + override val version = "2.0.0-SNAPSHOT.253" private const val infix = "spine-time" fun lib(version: String): String = "$group:$infix:$version" diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/ToolBase.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/ToolBase.kt index bb675bca2d1..0a95889ea1c 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/ToolBase.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/ToolBase.kt @@ -32,8 +32,8 @@ package io.spine.dependency.local ) object ToolBase { const val group = Spine.toolsGroup - const val version = "2.0.0-SNAPSHOT.428" - const val dogfoodingVersion = "2.0.0-SNAPSHOT.426" + const val version = "2.0.0-SNAPSHOT.440" + const val dogfoodingVersion = "2.0.0-SNAPSHOT.440" const val archive = "$group:archive:$version" const val code = "$group:code:$version" @@ -56,11 +56,28 @@ object ToolBase { const val rootGradlePlugins = "$group:root-gradle-plugins:$version" const val gradlePluginApi = "$group:gradle-plugin-api:$version" + + /** + * Stub Gradle plugins for the tests of the `gradle-plugin-api` module. + * + * The `gradle-plugin-api-test-fixtures` module that provides them is internal + * to `tool-base`, which never publishes it to a remote repository. Only + * `publishToMavenLocal` installs it, so these coordinates resolve only from + * a local `~/.m2` copy. + * + * @deprecated There is no replacement. Remove the usage. + */ + @Deprecated( + "The artifact is internal to `tool-base` and is not published remotely. Remove the usage." + ) const val gradlePluginApiTestFixtures = "$group:gradle-plugin-api-test-fixtures:$version" const val jvmTools = "$group:jvm-tools:$version" - const val jvmToolPluginDogfooding = "$group:jvm-tool-plugins-all:$dogfoodingVersion" - const val jvmToolPlugins = "$group:jvm-tool-plugins-all:$version" + + // The plain plugin JAR. `jvm-tool-plugins-all` is no longer published + // as of `2.0.0-SNAPSHOT.440`. See https://github.com/SpineEventEngine/tool-base/pull/201 + const val jvmToolPluginDogfooding = "$group:jvm-tool-plugins:$dogfoodingVersion" + const val jvmToolPlugins = "$group:jvm-tool-plugins:$version" const val protobufSetupPlugins = "$group:protobuf-setup-plugins:$version" diff --git a/buildSrc/src/main/kotlin/io/spine/dependency/local/Validation.kt b/buildSrc/src/main/kotlin/io/spine/dependency/local/Validation.kt index 155cbbc6020..1be9aff47a5 100644 --- a/buildSrc/src/main/kotlin/io/spine/dependency/local/Validation.kt +++ b/buildSrc/src/main/kotlin/io/spine/dependency/local/Validation.kt @@ -27,7 +27,7 @@ object Validation { /** * The version of the Validation library artifacts. */ - const val version = "2.0.0-SNAPSHOT.464" + const val version = "2.0.0-SNAPSHOT.469" const val group = Spine.toolsGroup private const val prefix = "validation" diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/Cli.kt b/buildSrc/src/main/kotlin/io/spine/gradle/Cli.kt index 0e43f3f71f5..36ba14ebe34 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/Cli.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/Cli.kt @@ -20,7 +20,6 @@ import java.io.File import java.io.InputStream import java.io.StringWriter import java.lang.ProcessBuilder.Redirect.PIPE -import java.util.* /** * Executes a process from Gradle code. @@ -32,20 +31,34 @@ class Cli(private val workingFolder: File) { /** * Executes the given terminal command and retrieves the command output. * - * [Executes][Runtime.exec] the given `String` array as a CLI command. + * Runs the given command, the program followed by its arguments, in + * the [workingFolder]. * * If the execution is successful, returns the command output. * Throws an [IllegalStateException] otherwise. * * @param command the command to execute. - * @return the command line output. + * @return the standard output as the command wrote it, line breaks included, + * decoded as UTF-8. * @throws IllegalStateException if the execution fails. */ - fun execute(vararg command: String): String { + fun execute(vararg command: String): String = execute(command.asList()) + + /** + * Executes the given terminal command and retrieves the command output. + * + * Works like the `vararg` overload, for a command assembled as a list. + * + * @param command the command to execute, the program followed by its arguments. + * @return the standard output as the command wrote it, line breaks included, + * decoded as UTF-8. + * @throws IllegalStateException if the execution fails. + */ + fun execute(command: List): String { val outWriter = StringWriter() val errWriter = StringWriter() - val process = ProcessBuilder(*command).apply { + val process = ProcessBuilder(command).apply { directory(workingFolder) redirectOutput(PIPE) redirectError(PIPE) @@ -66,22 +79,22 @@ class Cli(private val workingFolder: File) { val commandLine = command.joinToString(" ") val nl = System.lineSeparator() val errorMsg = "Command `$commandLine` finished with exit code $exitCode:" + - "$nl$errWriter" + - "$nl$outWriter." + "$nl${errWriter.toString().trimEnd()}" + + "$nl${outWriter.toString().trimEnd()}." throw IllegalStateException(errorMsg) } } } /** - * Starts a background thread that reads all lines from this [InputStream] and - * appends them to [dest], returning the thread so the caller can [join][Thread.join] - * it once the process has exited, ensuring the buffer holds the complete output. + * Starts a background thread that reads all of this [InputStream] and appends it + * to [dest], returning the thread so the caller can [join][Thread.join] it once + * the process has exited, ensuring the buffer holds the complete output. + * + * The text is appended as the process wrote it, line breaks included, so that + * a multi-line output can be split into lines. */ private fun InputStream.pourTo(dest: StringWriter): Thread = Thread { - val sc = Scanner(this) - while (sc.hasNextLine()) { - dest.append(sc.nextLine()) - } + bufferedReader().use { dest.append(it.readText()) } }.also { it.start() } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/ProjectExtensions.kt b/buildSrc/src/main/kotlin/io/spine/gradle/ProjectExtensions.kt index 41cac887581..5c16bd56610 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/ProjectExtensions.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/ProjectExtensions.kt @@ -59,7 +59,11 @@ fun Project.applyPlugin(cls: Class>) { * * The task must be present. Also, a caller is responsible for using the proper value of * the generic parameter `T`. + * + * Gradle creates the task if it is only registered so far, even in a build that never + * runs it. Prefer `tasks.named(name)`, which finds the task without creating it. */ +@Deprecated(message = "Please use `tasks.named(name)`, which does not create the task.") @Suppress("UNCHECKED_CAST") /* See the method docs. */ fun Project.getTask(name: String): T { val task = this.tasks.findByName(name) diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/TaskName.kt b/buildSrc/src/main/kotlin/io/spine/gradle/TaskName.kt index 0a595b3b407..763b9832b23 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/TaskName.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/TaskName.kt @@ -17,6 +17,7 @@ package io.spine.gradle import kotlin.reflect.KClass import org.gradle.api.Task import org.gradle.api.tasks.TaskContainer +import org.gradle.api.tasks.TaskProvider import org.gradle.kotlin.dsl.named import org.gradle.kotlin.dsl.register @@ -45,3 +46,22 @@ internal fun TaskContainer.named(name: TaskName) = named(name.valu */ internal fun TaskContainer.register(name: TaskName, init: T.() -> Unit) = register(name.value, name.clazz, init) + +/** + * Returns the task named [name] in this container, registering it with [init] + * if it is not there yet. + * + * Unlike [register][TaskContainer.register], this does not fail when the task is + * already registered, so it suits code that may reach the same task more than once. + * [init] applies only when this call registers the task; for an existing task it is + * ignored, so a setup needed in both cases goes to [TaskProvider.configure]. + */ +internal fun TaskContainer.getOrRegister( + name: String, + init: Task.() -> Unit = {} +): TaskProvider = + if (name in names) { + named(name) + } else { + register(name, init) + } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/checkstyle/CheckStyleConfig.kt b/buildSrc/src/main/kotlin/io/spine/gradle/checkstyle/CheckStyleConfig.kt index b8232683fab..79351fbf164 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/checkstyle/CheckStyleConfig.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/checkstyle/CheckStyleConfig.kt @@ -64,7 +64,7 @@ object CheckStyleConfig { "checkstyleTest", "checkstyleTestFixtures" ).forEach { - task -> tasks.findByName(task)?.enabled = false + task -> tasks.named { it == task }.configureEach { enabled = false } } } } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/git/Repository.kt b/buildSrc/src/main/kotlin/io/spine/gradle/git/Repository.kt index 1b7e09abf9b..6630f985f2a 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/git/Repository.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/git/Repository.kt @@ -61,14 +61,33 @@ class Repository private constructor( repoExecute("git", "clone", sshUrl, ".") } + /** + * Clones the tip of the [branch], or of the default branch if it is `null`, + * into the [temporary folder][location]. + * + * The blobs are fetched only when the working tree needs them, and the working + * tree holds only the top-level files until [checkOutOnly] replaces them with + * the selected paths. + */ + private fun cloneSparsely(branch: String?) { + val options = listOf("--depth", "1", "--filter=blob:none", "--sparse", "--single-branch") + val branchOption = branch?.let { listOf("--branch", it) } ?: emptyList() + repoExecute(listOf("git", "clone") + options + branchOption + listOf("--", sshUrl, ".")) + } + /** * Executes a command in the [location]. */ - private fun repoExecute(vararg command: String): String { - val cmd = command.toList().joinToString(" ") + private fun repoExecute(vararg command: String): String = repoExecute(command.asList()) + + /** + * Executes a command, assembled as a list, in the [location]. + */ + private fun repoExecute(command: List): String { + val cmd = command.joinToString(" ") val msg = "[Repo (${project.path})] Executing command: `$cmd`." System.err.println(msg) - return Cli(location.toFile()).execute(*command) + return Cli(location.toFile()).execute(command) } /** @@ -125,12 +144,59 @@ class Repository private constructor( * match a namespaced branch such as `feature/$name`. Relies on `git ls-remote` * returning an empty output with a zero exit code when the branch is absent, * so the check does not raise an exception. + * + * @param name the name of the branch. + * @param remote the name of the remote or its URL; `origin` by default. */ - private fun remoteHasBranch(name: String): Boolean { - val output = repoExecute("git", "ls-remote", "--heads", "origin", "refs/heads/$name") + private fun remoteHasBranch(name: String, remote: String = "origin"): Boolean { + val output = repoExecute("git", "ls-remote", "--heads", remote, "refs/heads/$name") return output.isNotBlank() } + /** + * Checks out only the paths of the current branch that match the given [patterns], + * leaving the rest of the branch out of the working tree. + * + * The call replaces the paths checked out before, including the top-level files + * of a [sparse clone][Factory.cloneSparsely], so it has to be given every path + * to keep at once. + * + * The patterns follow the `.gitignore` syntax, so a folder can be checked out + * without some of its subfolders. The paths left out stay in the branch: committing + * [all changes][commitAllChanges] does not delete them. + * + * Leaving out some subfolders of a folder takes the non-cone mode of + * `git sparse-checkout`, which Git documents as deprecated, though Git 2.54 still + * supports it without a warning. The mode needs Git 2.35 or newer. + */ + fun checkOutOnly(patterns: List) { + repoExecute(listOf("git", "sparse-checkout", "set", "--no-cone") + patterns) + } + + /** + * Obtains the names of the folders in the given [folder] of the current commit, + * whether they are checked out or not. + * + * Reads the tree of the current commit, so the changes in the working tree that + * are not committed yet are not reflected. The files in the [folder] are skipped. + * + * @param folder the path of the folder relative to the repository root. + * @return the names of the folders in [folder], or an empty list if there is + * no such folder. + */ + fun listFolders(folder: String): List { + require(folder.isNotBlank()) { "The folder to list cannot be blank." } + // Each entry is ` \t`, the entries are separated + // by `NUL`, and the paths are not quoted. + return repoExecute("git", "ls-tree", "-z", "HEAD", "${folder.trimEnd('/')}/") + .split('\u0000') + .filter { it.isNotBlank() } + .mapNotNull { entry -> + val (info, path) = entry.split('\t', limit = 2) + path.substringAfterLast('/').takeIf { info.split(' ')[1] == "tree" } + } + } + /** * Creates the [branch] as an orphan branch seeded with [initialFiles] and * pushes it to the remote. @@ -173,7 +239,6 @@ class Repository private constructor( // If the branch now exists on the remote, another module won the // creation race and we adopt its branch; otherwise the failure is // genuine and is rethrown. - repoExecute("git", "fetch", "origin") if (!remoteHasBranch(branch)) { throw e } @@ -184,8 +249,14 @@ class Repository private constructor( /** * Discards the local orphan branch in favour of the same-named branch that * already exists on the remote, keeping the local branch in sync with it. + * + * The branch is added to the branches fetched from the remote first: a single-branch + * clone, which a shallow one is too, fetches no other branch, and Git does not + * accept a branch it does not fetch as the upstream of a local one. */ private fun adoptRemoteBranch(branch: String) { + repoExecute("git", "remote", "set-branches", "--add", "origin", branch) + repoExecute("git", "fetch", "origin", "+refs/heads/$branch:refs/remotes/origin/$branch") repoExecute("git", "reset", "--hard", "origin/$branch") repoExecute("git", "branch", "--set-upstream-to=origin/$branch", branch) } @@ -274,6 +345,53 @@ class Repository private constructor( return repo } + + /** + * Clones the tip of the [branch] of the repository with the provided SSH URL + * in a temporary folder, with only its top-level files checked out. + * + * Suits a branch with a large tip of which a change touches only a small part, + * such as the branch with the documentation of every published version. + * Further blobs are fetched only for the paths that [checkOutOnly] selects. + * The commits are made and [pushed][push] as in a full clone. + * + * A [branch] that does not exist yet is created and seeded with [initialFiles], + * which are top-level files. + * + * Configures the username and the email of the Git user. + * See [configureUser] documentation for more information. + * + * @throws IllegalArgumentException if SSH URL is a blank string. + */ + fun cloneSparsely( + project: Project, + sshUrl: String, + user: UserInfo, + branch: String, + initialFiles: Map = emptyMap(), + ): Repository { + require(sshUrl.isNotBlank()) { "SSH URL cannot be an empty string." } + + val repo = Repository(project, sshUrl = sshUrl, user = user, currentBranch = branch) + var cloned = false + try { + // Ask the remote first: an existing branch is cloned alone, while + // a missing one needs the default branch to start from. + val branchExists = repo.remoteHasBranch(branch, remote = sshUrl) + repo.cloneSparsely(branch.takeIf { branchExists }) + repo.configureUser(user) + if (!branchExists) { + repo.createOrphanBranch(branch, initialFiles) + } + cloned = true + return repo + } finally { + // The caller cannot close a repository it never gets. + if (!cloned) { + repo.close() + } + } + } } } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/AuthorEmail.kt b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/AuthorEmail.kt index 39d16ac8fd8..cec12afdc92 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/AuthorEmail.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/AuthorEmail.kt @@ -15,7 +15,8 @@ package io.spine.gradle.github.pages /** - * An author of updates to GitHub pages. + * The email of the author of the commits that publish the API documentation + * to GitHub Pages. */ class AuthorEmail(val value: String) { diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/DocsPublication.kt b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/DocsPublication.kt new file mode 100644 index 00000000000..11f37da3b09 --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/DocsPublication.kt @@ -0,0 +1,254 @@ +/* + * Copyright 2026 CodeMatters, Lda. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +package io.spine.gradle.github.pages + +import io.spine.gradle.VersionComparator +import io.spine.gradle.git.Repository +import java.io.File +import java.nio.file.Path +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.FileCollection +import org.gradle.api.logging.Logger + +/** + * Publishes the API documentation of the given [project] to GitHub Pages. + * + * The documentation in each format is copied into a clone of the `gh-pages` branch + * and committed there, and the commits are pushed. + * + * The clone has only the most recent documentation of the [project] and the folder of + * its version checked out, without the folders of the versions published earlier. + * The branch holds the documentation of every version of every module, which is too + * much to check out for a change to a single module. + */ +internal fun Task.publishToGitHubPages(project: Project) { + val plugin = project.plugins.getPlugin(PublishApiDocs::class.java) + + SshKey(plugin.rootFolder, logger).register() + + val repository = Repository.forPublishingDocumentation(project) + + val javadoc = JavadocPublication(project, plugin.javadocOutputFolder, repository, logger) + val html = HtmlPublication(project, plugin.htmlOutputFolder, repository, logger) + + repository.use { + repository.checkOutOnly(javadoc.checkoutPatterns() + html.checkoutPatterns()) + javadoc.publish() + html.publish() + repository.push() + } +} + +/** + * Publishes the documentation of a [project] in one format to the clone of + * the [repository] serving GitHub Pages. + * + * The documentation goes to the folder of its version, and also replaces the most + * recent documentation of the project, unless a newer version is already published. + */ +private abstract class DocsPublication( + private val project: Project, + private val docsSourceFolder: Path, + private val repository: Repository, + private val logger: Logger +) { + + /** + * The folder under the repository's root(`/`) for storing documentation. + * + * The value should not contain any leading or trailing file separators. + * + * The absolute path to the project's documentation is made by appending its + * name to the end, making `/docsDestinationFolder/project.name`. + */ + protected abstract val docsDestinationFolder: String + + /** + * The name of the format of the documentation to publish. + * + * This name will appear in logs as part of a message. + */ + protected abstract val formatName: String + + /** + * The path of the folder with the documentation of the [project] in this format, + * relative to the repository root. + */ + private val folder: String + get() = "$docsDestinationFolder/${project.name}" + + private val mostRecentFolder by lazy { + File("${repository.location}/$folder") + } + + /** + * The folder holding the documentation of each published version in a subfolder + * named after the version. + */ + private val versionsFolder by lazy { + File(mostRecentFolder, VERSIONS_FOLDER) + } + + private fun log(message: () -> String) { + if (logger.isDebugEnabled) { + logger.debug(message()) + } + } + + /** + * Obtains the patterns of the paths this publication writes, for + * [checking out][Repository.checkOutOnly] just those paths. + */ + fun checkoutPatterns(): List = + docsCheckoutPatterns(folder, project.version.toString()) + + /** + * Copies the documentation into the repository and commits it. + */ + fun publish() { + val module = project.name + log { "Publishing the `$formatName` documentation of the module `$module`." } + + val documentation = replaceMostRecentDocs() + copyIntoVersionDir(documentation) + + val version = project.version + val commitMessage = + "Publish the `$formatName` documentation of the module" + + " `$module` for the version `$version`." + repository.commitAllChanges(commitMessage) + + log { "Published the `$formatName` documentation of the module `$module`." } + } + + /** + * Replaces the most recent documentation with the one of this version, unless + * the documentation of a newer version is already published. + * + * The runs of `publish-api-docs.yml` are serialized, but not necessarily in the + * order of their commits, and a release of an older line may come after a newer + * one. Either way, the most recent documentation stays that of the newest version. + */ + private fun replaceMostRecentDocs(): ConfigurableFileCollection { + val generatedDocs = project.files(docsSourceFolder) + + val version = project.version.toString() + val newer = publishedVersions().newestAbove(version) + if (newer != null) { + logger.lifecycle( + "The most recent `$formatName` documentation of `${project.name}` stays" + + " that of `$newer`, which is newer than `$version`." + ) + return generatedDocs + } + + log { "Replacing the most recent `$formatName` documentation in `$mostRecentFolder`." } + copyDocs(generatedDocs, mostRecentFolder) + + return generatedDocs + } + + /** + * Obtains the versions whose documentation is published, as named by the folders + * under `v/`. + * + * The names are read from the tree of the current commit, as the folders of + * the other versions are not checked out. + */ + private fun publishedVersions(): List = + repository.listFolders("$folder/$VERSIONS_FOLDER") + + private fun copyDocs(source: FileCollection, destination: File) { + destination.mkdirs() + project.copy { + from(source) + into(destination) + } + } + + private fun copyIntoVersionDir(generatedDocs: ConfigurableFileCollection) { + val versionedDocDir = File(versionsFolder, "${project.version}") + + log { "Storing the new version of `$formatName` documentation in `${versionedDocDir}`." } + copyDocs(generatedDocs, versionedDocDir) + } +} + +private class JavadocPublication( + project: Project, + docsSourceFolder: Path, + repository: Repository, + logger: Logger +) : DocsPublication(project, docsSourceFolder, repository, logger) { + + override val docsDestinationFolder: String + get() = "javadoc" + override val formatName: String + get() = "javadoc" +} + +private class HtmlPublication( + project: Project, + docsSourceFolder: Path, + repository: Repository, + logger: Logger +) : DocsPublication(project, docsSourceFolder, repository, logger) { + + override val docsDestinationFolder: String + get() = "reference" + override val formatName: String + get() = "html" +} + +/** + * Returns the newest of these versions if it is newer than the given [version], + * or `null` otherwise. + * + * The versions are compared by [VersionComparator], so that `2.10.0` is newer than + * `2.9.0`, and a release is newer than the snapshots leading to it. + */ +internal fun Iterable.newestAbove(version: String): String? = + maxWithOrNull(VersionComparator)?.takeIf { VersionComparator.compare(it, version) > 0 } + +/** + * The name of the folder that holds the documentation of each published version + * in a subfolder named after the version. + */ +private const val VERSIONS_FOLDER = "v" + +/** + * Returns the patterns, in the `.gitignore` syntax, of the paths that a publication + * of the documentation in the given [folder] writes: the most recent documentation, + * and the folder of the given [version] under `v/`, but not the folders of the other + * versions. + * + * The folders under `v/` are excluded one by one, and the folder of the [version] + * is included again after them, as the last matching pattern wins. This is how + * `git sparse-checkout` lays out its patterns in cone mode. + * + * For example, for the `javadoc/core` folder and the version `1.2.0`, the patterns + * select everything in `javadoc/core`, exclude each folder in `javadoc/core/v`, and + * select `javadoc/core/v/1.2.0` again. + * + * @param folder the path of the documentation folder, relative to the repository root. + * @param version the version whose folder is included. + */ +internal fun docsCheckoutPatterns(folder: String, version: String): List = listOf( + "/$folder/*", + "!/$folder/$VERSIONS_FOLDER/*/", + "/$folder/$VERSIONS_FOLDER/$version/", +) diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/PublishAllApiDocs.kt b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/PublishAllApiDocs.kt new file mode 100644 index 00000000000..e610c5861ee --- /dev/null +++ b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/PublishAllApiDocs.kt @@ -0,0 +1,76 @@ +/* + * Copyright 2026 CodeMatters, Lda. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +package io.spine.gradle.github.pages + +import io.spine.gradle.SpineTaskGroup +import io.spine.gradle.getOrRegister +import io.spine.gradle.github.pages.TaskName.publishAllApiDocs +import io.spine.gradle.github.pages.TaskName.publishApiDocs +import org.gradle.api.Project + +/** + * Registers the [TaskName.publishAllApiDocs] task in the root project, which publishes + * the API documentation of all the published projects of the build. + * + * Usage: + * ``` + * PublishAllApiDocs.registerIn(project.rootProject, projectsToPublish) + * ``` + * + * `spinePublishing { }` makes this call, as it knows which projects are published. + * The task is not a part of the publication to Maven repositories: the + * `publish-api-docs.yml` workflow runs it apart from `publish`. + * + * The task is a lifecycle one: it has no action of its own, and publishes + * the documentation through the [TaskName.publishApiDocs] tasks it depends on, + * which the [PublishApiDocs] plugin registers in each documented project. + */ +internal object PublishAllApiDocs { + + /** + * Registers the [TaskName.publishAllApiDocs] task in the [root] project. + * The task publishes the API documentation of the [published] projects. + * + * The task depends on the [TaskName.publishApiDocs] task of each of + * the [published] projects that has one, that is, applies the [PublishApiDocs] + * plugin. This includes [root] when it is published itself, as in a single-project + * build. A project that applies the plugin, but is not published, such as a module + * of integration tests, is left out. The task of a project with a snapshot version + * publishes nothing. + * + * The task is registered even when none of the projects has the documentation + * to publish, and then does nothing. This lets the `publish-api-docs.yml` + * workflow run it in any repository that publishes: Gradle fails on a task + * that is missing from the build. + * + * The plugin registers the task of a project once that project is evaluated, + * so the tasks to depend on are looked up only when Gradle resolves + * the dependencies of this task. Repeated calls add their projects to the same + * task, as `spinePublishing { }` may run more than once in a build. + */ + fun registerIn(root: Project, published: Set) { + val task = root.tasks.getOrRegister(publishAllApiDocs) { + group = SpineTaskGroup.name + description = "Publishes the API documentation of all the published modules" + } + task.configure { + dependsOn(root.provider { + published + .filter { publishApiDocs in it.tasks.names } + .map { it.tasks.named(publishApiDocs) } + }) + } + } +} diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/UpdateGitHubPages.kt b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/PublishApiDocs.kt similarity index 65% rename from buildSrc/src/main/kotlin/io/spine/gradle/github/pages/UpdateGitHubPages.kt rename to buildSrc/src/main/kotlin/io/spine/gradle/github/pages/PublishApiDocs.kt index e4960aaf713..f4ca18bc31c 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/UpdateGitHubPages.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/PublishApiDocs.kt @@ -15,12 +15,12 @@ package io.spine.gradle.github.pages import dokkaHtmlTask -import dokkaJavadocTask import io.spine.gradle.SpineTaskGroup import io.spine.gradle.fs.LazyTempPath import io.spine.gradle.github.pages.TaskName.copyHtmlDocs import io.spine.gradle.github.pages.TaskName.copyJavadocDocs -import io.spine.gradle.github.pages.TaskName.updateGitHubPages +import io.spine.gradle.github.pages.TaskName.publishApiDocs +import io.spine.gradle.isSnapshot import java.io.File import org.gradle.api.Plugin import org.gradle.api.Project @@ -30,12 +30,18 @@ import org.gradle.api.tasks.TaskContainer import org.gradle.api.tasks.TaskProvider /** - * Registers the `updateGitHubPages` task that performs the update of - * the GitHub Pages with the documentation generated in Javadoc and HTML format - * for a particular Gradle project. + * Registers the `publishApiDocs` task that publishes the API documentation + * of a particular Gradle project, generated in Javadoc and HTML format. * * The generated documentation is appended to the `spine.io` site - * via GitHub pages by pushing commits to the `gh-pages` branch. + * via GitHub Pages by pushing commits to the `gh-pages` branch. + * The documentation of a snapshot version is not published to the site. Its Javadoc and + * HTML archives are still built by Dokka and published to Maven repositories by `publish`. + * + * The task is not a part of the Maven publication. It is run by the `publish-api-docs.yml` + * workflow, apart from `publish.yml`, through the `publishAllApiDocs` task of the root + * project, which [publishes the documentation of the published modules that apply + * this plugin][PublishAllApiDocs]. * * In order to work, the script needs a `deploy_key_rsa` private RSA key file in the * repository root. It is recommended to encrypt it in the repository and then decrypt @@ -47,18 +53,18 @@ import org.gradle.api.tasks.TaskProvider * the SSH private (encrypted `deploy_key_rsa`) and the public * ("GitHub Pages publisher" on GitHub) keys. * - * Another requirement is an environment variable `REPO_SLUG`, which is set by the CI - * environment, such as `Publish` GitHub Actions workflow. It points to the repository - * for which the update is executed. E.g.: + * Another requirement is an environment variable `REPO_SLUG`, which a CI workflow sets, + * such as the `Publish API docs` GitHub Actions workflow. It points to + * the repository for which the documentation is published. E.g.: * * ``` * REPO_SLUG: SpineEventEngine/base * ``` * - * @see UpdateGitHubPagesExtension for the extension that is used to configure + * @see PublishApiDocsExtension for the extension that is used to configure * this plugin */ -class UpdateGitHubPages : Plugin { +class PublishApiDocs : Plugin { /** * Root folder of the repository, to which this `Project` belongs. @@ -74,13 +80,13 @@ class UpdateGitHubPages : Plugin { /** * Path to the temp folder used to gather the Javadoc format output (generated by Dokka) - * before submitting it to the GitHub Pages update. + * before publishing it. */ internal val javadocOutputFolder = LazyTempPath("javadoc") /** * Path to the temp folder used to gather the HTML documentation - * generated by Dokka before submitting it to the GitHub Pages update. + * generated by Dokka before publishing it. */ internal val htmlOutputFolder = LazyTempPath("html") @@ -94,41 +100,45 @@ class UpdateGitHubPages : Plugin { * when the project version changes to non-snapshot. */ override fun apply(project: Project) { - val extension = UpdateGitHubPagesExtension.createIn(project) + val extension = PublishApiDocsExtension.createIn(project) project.afterEvaluate { - registerTasks(extension) + if (version.toString().isSnapshot()) { + registerNoOpTask() + } else { + registerTasks(extension) + } } } /** - * Registers the `updateGitHubPages` task that performs no actual update, but prints - * the message telling the update is skipped, since the project is in - * its `SNAPSHOT` version. + * Registers the [TaskName.publishApiDocs] task that publishes nothing, but tells + * that the documentation is not published, since the project is in its `SNAPSHOT` + * version. */ - @Suppress("unused") private fun Project.registerNoOpTask() { - tasks.register(updateGitHubPages) { + val module = name + val snapshotVersion = version.toString() + tasks.register(publishApiDocs) { group = SpineTaskGroup.name - description = "Skips the GitHub Pages update for snapshot project versions" + description = "Skips publishing the API documentation of snapshot versions" doLast { - val project = this@registerNoOpTask - println( - "GitHub Pages update will be skipped since this project is a snapshot: " + - "`${project.name}-${project.version}`." + logger.lifecycle( + "The API documentation of `$module` is not published, since" + + " `$snapshotVersion` is a snapshot version." ) } } } - private fun Project.registerTasks(extension: UpdateGitHubPagesExtension) { + private fun Project.registerTasks(extension: PublishApiDocsExtension) { rootFolder = extension.rootFolder() includedInputs = extension.includedInputs() tasks.registerCopyJavadoc() tasks.registerCopyDokka() - val updatePagesTask = tasks.registerUpdateTask() - updatePagesTask.configure { + val publishTask = tasks.registerPublishTask() + publishTask.configure { dependsOn(copyJavadocDocs) dependsOn(copyHtmlDocs) } @@ -147,7 +157,8 @@ class UpdateGitHubPages : Plugin { private fun TaskContainer.composeJavadocInputs(): List { val inputs = mutableListOf() - inputs.add(dokkaJavadocTask()!!) + // Unlike the HTML docs, the Javadoc is required, so a missing task fails the build. + inputs.add(named("dokkaGeneratePublicationJavadoc")) inputs.addAll(includedInputs) return inputs } @@ -165,22 +176,19 @@ class UpdateGitHubPages : Plugin { private fun TaskContainer.composeDokkaInputs(): List { val inputs = mutableListOf() - - dokkaHtmlTask()?.let { - inputs.add(it) - } + inputs.add(dokkaHtmlTask()) inputs.addAll(includedInputs) return inputs } - private fun TaskContainer.registerUpdateTask(): TaskProvider { - return register(updateGitHubPages) { + private fun TaskContainer.registerPublishTask(): TaskProvider { + return register(publishApiDocs) { group = SpineTaskGroup.name - description = "Publishes the generated documentation to the `gh-pages` branch" + description = "Publishes the API documentation to the `gh-pages` branch" doLast { try { - updateGhPages(project) + publishToGitHubPages(project) } finally { cleanup() } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/UpdateGitHubPagesExtension.kt b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/PublishApiDocsExtension.kt similarity index 70% rename from buildSrc/src/main/kotlin/io/spine/gradle/github/pages/UpdateGitHubPagesExtension.kt rename to buildSrc/src/main/kotlin/io/spine/gradle/github/pages/PublishApiDocsExtension.kt index 5a7b1a88f02..fbf0d9bc96d 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/UpdateGitHubPagesExtension.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/PublishApiDocsExtension.kt @@ -23,44 +23,44 @@ import org.gradle.kotlin.dsl.getByType import org.gradle.kotlin.dsl.property /** - * Configures the `updateGitHubPages` extension. + * Configures the `publishApiDocs` extension. */ @Suppress("unused") -fun Project.updateGitHubPages( - action: UpdateGitHubPagesExtension.() -> Unit +fun Project.publishApiDocs( + action: PublishApiDocsExtension.() -> Unit ) { - apply() + apply() - val extension = extensions.getByType(UpdateGitHubPagesExtension::class) + val extension = extensions.getByType(PublishApiDocsExtension::class) extension.action() } /** - * The extension for configuring the [UpdateGitHubPages] plugin. + * The extension for configuring the [PublishApiDocs] plugin. * - * @property rootFolder The root folder of the repository to which the updated `Project` belongs. + * @property rootFolder The root folder of the repository to which the documented `Project` belongs. * @property includeInputs The external inputs, which output should be included - * into the GitHub Pages update. The values are interpreted according to + * into the published documentation. The values are interpreted according to * [Copy.from][org.gradle.api.tasks.Copy.from] specification. * This property is optional. */ -class UpdateGitHubPagesExtension private constructor( - var rootFolder: Property, - var includeInputs: SetProperty +class PublishApiDocsExtension private constructor( + val rootFolder: Property, + val includeInputs: SetProperty ) { internal companion object { /** * The name of the extension. */ - const val name = "updateGitHubPages" + const val name = "publishApiDocs" /** * Creates a new extension and adds it to the passed project. */ - fun createIn(project: Project): UpdateGitHubPagesExtension { + fun createIn(project: Project): PublishApiDocsExtension { val factory = project.objects - val result = UpdateGitHubPagesExtension( + val result = PublishApiDocsExtension( rootFolder = factory.property(File::class), includeInputs = factory.setProperty(Any::class.java) ) @@ -76,8 +76,8 @@ class UpdateGitHubPagesExtension private constructor( fun rootFolder(): File = rootFolder.get() /** - * Returns the external inputs, which results should be included into the - * GitHub Pages update. + * Returns the external inputs, whose results should be included into the + * published documentation. */ fun includedInputs(): Set = includeInputs.get() } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/RepositoryExtensions.kt b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/RepositoryExtensions.kt index 0be661d2e6b..8a704cb9496 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/RepositoryExtensions.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/RepositoryExtensions.kt @@ -21,17 +21,19 @@ import io.spine.gradle.repo.RepoSlug import org.gradle.api.Project /** - * Clones the current project repository with the branch dedicated to publishing - * documentation to GitHub Pages checked out. + * Clones the branch of the current project repository dedicated to publishing + * documentation to GitHub Pages. * * The repository's GitHub SSH URL is derived from the `REPO_SLUG` environment * variable. The [branch][Branch.documentation] dedicated to publishing documentation - * is automatically checked out in this repository, and created if it does not exist - * yet. A freshly created branch is seeded with a `CNAME` file so that GitHub Pages - * serves the documentation under the `spine.io` custom domain. Also, the username - * and the email of the git user are automatically configured. + * is cloned [sparsely][Repository.Factory.cloneSparsely]: its tip holds the documentation + * of every published version of every module, while a publication touches the folders + * of one module only; the caller [checks them out][Repository.checkOutOnly]. The branch + * is created if it does not exist yet. A freshly created branch is seeded with a `CNAME` + * file so that GitHub Pages serves the documentation under the `spine.io` custom domain. + * Also, the username and the email of the Git user are automatically configured. * - * The username is set to `"UpdateGitHubPages Plugin"`, and the email is derived from + * The username is set to `"PublishApiDocs Plugin"`, and the email is derived from * the `FORMAL_GIT_HUB_PAGES_AUTHOR` environment variable. * * @throws org.gradle.api.GradleException if any of the environment variables described above @@ -40,7 +42,7 @@ import org.gradle.api.Project internal fun Repository.Factory.forPublishingDocumentation(project: Project): Repository { val host = RepoSlug.fromVar().gitHost() - val username = "UpdateGitHubPages Plugin" + val username = "PublishApiDocs Plugin" val userEmail = AuthorEmail.fromVar().toString() val user = UserInfo(username, userEmail) @@ -51,5 +53,11 @@ internal fun Repository.Factory.forPublishingDocumentation(project: Project): Re // custom domain. val initialFiles = mapOf("CNAME" to "spine.io\n") - return clone(project, host, user, branch, initialFiles) + return cloneSparsely( + project, + sshUrl = host, + user = user, + branch = branch, + initialFiles = initialFiles + ) } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/TaskName.kt b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/TaskName.kt index cc453f0bf8c..2d7d20de25e 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/TaskName.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/TaskName.kt @@ -17,19 +17,25 @@ package io.spine.gradle.github.pages object TaskName { /** - * The name of the task that updates the GitHub Pages. + * The name of the task that publishes the API documentation of a project. */ - const val updateGitHubPages = "updateGitHubPages" + const val publishApiDocs = "publishApiDocs" /** - * The name of the helper task to gather the generated Javadoc format - * documentation generated by Dokka before updating GitHub Pages. + * The name of the task of the root project that publishes the API documentation + * of all the published projects of the build. + */ + const val publishAllApiDocs = "publishAllApiDocs" + + /** + * The name of the helper task to gather the Javadoc format documentation + * generated by Dokka before publishing it to GitHub Pages. */ const val copyJavadocDocs = "copyJavadocDocs" /** * The name of the helper task to gather HTML documentation - * generated by Dokka before updating GitHub Pages. + * generated by Dokka before publishing it to GitHub Pages. */ const val copyHtmlDocs = "copyHtmlDocs" } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/Update.kt b/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/Update.kt deleted file mode 100644 index 29896d159f9..00000000000 --- a/buildSrc/src/main/kotlin/io/spine/gradle/github/pages/Update.kt +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2026 CodeMatters, Lda. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file - * except in compliance with the License. You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under - * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, - * either express or implied. See the License for the specific language governing permissions - * and limitations under the License. - */ - -package io.spine.gradle.github.pages - -import io.spine.gradle.git.Repository -import java.io.File -import java.nio.file.Path -import org.gradle.api.Project -import org.gradle.api.Task -import org.gradle.api.file.ConfigurableFileCollection -import org.gradle.api.file.FileCollection -import org.gradle.api.logging.Logger - -/** - * Performs the update of GitHub pages. - */ -fun Task.updateGhPages(project: Project) { - val plugin = project.plugins.getPlugin(UpdateGitHubPages::class.java) - - SshKey(plugin.rootFolder, logger).register() - - val repository = Repository.forPublishingDocumentation(project) - - val updateJavadocFormat = - UpdateJavadocFormat(project, plugin.javadocOutputFolder, repository, logger) - - val updateHtmlFormat = - UpdateHtmlFormat(project, plugin.htmlOutputFolder, repository, logger) - - repository.use { - updateJavadocFormat.run() - updateHtmlFormat.run() - repository.push() - } -} - -private abstract class UpdateDocumentation( - private val project: Project, - private val docsSourceFolder: Path, - private val repository: Repository, - private val logger: Logger -) { - - /** - * The folder under the repository's root(`/`) for storing documentation. - * - * The value should not contain any leading or trailing file separators. - * - * The absolute path to the project's documentation is made by appending its - * name to the end, making `/docsDestinationFolder/project.name`. - */ - protected abstract val docsDestinationFolder: String - - /** - * The name of the format of the documentation to update. - * - * This name will appear in logs as part of a message. - */ - protected abstract val formatName: String - - private val mostRecentFolder by lazy { - File("${repository.location}/${docsDestinationFolder}/${project.name}") - } - - private fun log(message: () -> String) { - if (logger.isDebugEnabled) { - logger.debug(message()) - } - } - - fun run() { - val module = project.name - log { "Update of the `$formatName` documentation for the module `$module` started." } - - val documentation = replaceMostRecentDocs() - copyIntoVersionDir(documentation) - - val version = project.version - val updateMessage = - "Update `$formatName` documentation for the module" + - " `$module` with the version `$version`." - repository.commitAllChanges(updateMessage) - - log { "Update of the `$formatName` documentation for `$module` successfully finished." } - } - - private fun replaceMostRecentDocs(): ConfigurableFileCollection { - val generatedDocs = project.files(docsSourceFolder) - - log { "Replacing the most recent `$formatName` documentation in `$mostRecentFolder`." } - copyDocs(generatedDocs, mostRecentFolder) - - return generatedDocs - } - - private fun copyDocs(source: FileCollection, destination: File) { - destination.mkdir() - project.copy { - from(source) - into(destination) - } - } - - private fun copyIntoVersionDir(generatedDocs: ConfigurableFileCollection) { - val versionedDocDir = File("$mostRecentFolder/v/${project.version}") - - log { "Storing the new version of `$formatName` documentation in `${versionedDocDir}`." } - copyDocs(generatedDocs, versionedDocDir) - } -} - -private class UpdateJavadocFormat( - project: Project, - docsSourceFolder: Path, - repository: Repository, - logger: Logger -) : UpdateDocumentation(project, docsSourceFolder, repository, logger) { - - override val docsDestinationFolder: String - get() = "javadoc" - override val formatName: String - get() = "javadoc" -} - -private class UpdateHtmlFormat( - project: Project, - docsSourceFolder: Path, - repository: Repository, - logger: Logger -) : UpdateDocumentation(project, docsSourceFolder, repository, logger) { - - override val docsDestinationFolder: String - get() = "reference" - override val formatName: String - get() = "html" -} diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/javadoc/ExcludeInternalDoclet.kt b/buildSrc/src/main/kotlin/io/spine/gradle/javadoc/ExcludeInternalDoclet.kt index 825d3254fca..d3463285379 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/javadoc/ExcludeInternalDoclet.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/javadoc/ExcludeInternalDoclet.kt @@ -89,7 +89,7 @@ private fun Project.appendCustomJavadocTask(excludeInternalDoclet: Configuration !it.absolutePath.contains("generated") }.asFileTree - classpath = javadocTask.classpath + classpath = javadocTask.get().classpath options { encoding = JavadocConfig.encoding.name diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/javadoc/JavadocConfig.kt b/buildSrc/src/main/kotlin/io/spine/gradle/javadoc/JavadocConfig.kt index cb6998de2f7..35ff1fbc3ae 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/javadoc/JavadocConfig.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/javadoc/JavadocConfig.kt @@ -19,19 +19,25 @@ import java.io.File import org.gradle.api.JavaVersion import org.gradle.api.Project import org.gradle.api.tasks.TaskContainer +import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.javadoc.Javadoc import org.gradle.external.javadoc.StandardJavadocDocletOptions import productionModules /** - * Finds a [Javadoc] Gradle task by the passed name. + * Finds a [Javadoc] Gradle task by the passed name without creating it. + * + * The task must be registered. */ -fun TaskContainer.javadocTask(named: String) = this.getByName(named) as Javadoc +fun TaskContainer.javadocTask(named: String): TaskProvider = + this.named(named, Javadoc::class.java) /** - * Finds a default [Javadoc] Gradle task. + * Finds a default [Javadoc] Gradle task without creating it. + * + * The task must be registered. */ -fun TaskContainer.javadocTask() = this.getByName("javadoc") as Javadoc +fun TaskContainer.javadocTask(): TaskProvider = javadocTask("javadoc") /** * Javadoc processing settings. @@ -57,15 +63,23 @@ object JavadocConfig { val encoding = Encoding("UTF-8") + /** + * Applies the Javadoc settings to the `javadoc` task of the given [project], + * or disables the task if the project is not a production module. + * + * The task must be registered. It is configured when Gradle creates it, + * so calling this function does not create the task. + */ fun applyTo(project: Project) { - val javadocTask = project.tasks.javadocTask() - if (!isProductionModule(project)) { - javadocTask.enabled = false - return + project.tasks.javadocTask().configure { + if (!isProductionModule(project)) { + enabled = false + return@configure + } + discardJavaModulesInLinks(this) + val docletOptions = options as StandardJavadocDocletOptions + configureDoclet(docletOptions) } - discardJavaModulesInLinks(javadocTask) - val docletOptions = javadocTask.options as StandardJavadocDocletOptions - configureDoclet(docletOptions) } private fun isProductionModule(project: Project) = project.run { diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/publish/ProtoExts.kt b/buildSrc/src/main/kotlin/io/spine/gradle/publish/ProtoExts.kt index fd5dba83fc3..0ade0727b75 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/publish/ProtoExts.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/publish/ProtoExts.kt @@ -20,6 +20,7 @@ import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.file.FileTreeElement import org.gradle.api.file.SourceDirectorySet +import org.gradle.api.tasks.TaskCollection import org.gradle.api.tasks.bundling.Jar /** @@ -67,15 +68,18 @@ internal fun FileTreeElement.isGoogleProtoSource(): Boolean { } /** - * The reference to the `generateProto` task of a `main` source set. + * The `generateProto` task of a `main` source set, found without creating it. + * + * The returned collection is live: it holds the task once it is registered, + * and stays empty in a project without Protobuf. */ -internal fun Project.generateProto(): Task? = tasks.findByName("generateProto") +internal fun Project.generateProto(): TaskCollection = + tasks.named { it == "generateProto" } /** - * Makes this [Jar] task depend on the [generateProto] task, if it exists in the same project. + * Makes this [Jar] task depend on the [generateProto] task, once that task is registered + * in the same project. */ internal fun Jar.dependOnGenerateProto() { - project.generateProto()?.let { - this.dependsOn(it) - } + this.dependsOn(project.generateProto()) } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/publish/PublicationChecksums.kt b/buildSrc/src/main/kotlin/io/spine/gradle/publish/PublicationChecksums.kt index 7d6cf7e998a..d083018b970 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/publish/PublicationChecksums.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/publish/PublicationChecksums.kt @@ -15,6 +15,7 @@ package io.spine.gradle.publish import io.spine.gradle.SpineTaskGroup +import io.spine.gradle.getOrRegister import java.io.File import java.security.MessageDigest import java.util.Locale @@ -98,6 +99,13 @@ internal object PublicationChecksums { * the root project of a multi-module build, which is where `publish.yml` * expects the manifest. A module configuring the extension for itself also * gets an aggregator of its own, describing only that module. + * + * The registration is idempotent. `spinePublishing { }` configures the extension + * anew on every call, and the same project can be reached by two such calls: + * the root extension lists a module in [SpinePublishing.modulesWithCustomPublishing], + * while the module itself opens the extension with `customPublishing = true` — + * which is what `uber-jar-module.gradle.kts` does. Plain registration would fail + * on the second call, during configuration. */ fun registerTasks(host: Project, published: Set) { val collectors = published.map { registerCollectorIn(it) } @@ -177,10 +185,10 @@ internal object PublicationChecksums { // build directory afterwards. // // The list belongs to the project rather than to this call, because the - // task is registered once while `registerTasks` may run several times — - // see `getOrRegister`. Were the action to close over one call's projects, - // a later call adding a module would register its collector and still - // leave it out of the manifest. + // task is registered once while `registerTasks` may run several times, as + // the documentation of `registerTasks` explains. Were the action to close + // over one call's projects, a later call adding a module would register its + // collector and still leave it out of the manifest. val sources = host.attestationSources() sources += published.map { collectorOutput(it) } val target = host.layout.buildDirectory.file(aggregatePath) @@ -256,27 +264,6 @@ internal object PublicationChecksums { } } -/** - * Returns the task named [name] in this container, registering it with [init] - * if it is not there yet. - * - * `spinePublishing { }` configures the extension anew on every call, and the - * same project can be reached by two such calls: the root extension lists a - * module in [SpinePublishing.modulesWithCustomPublishing], while the module - * itself opens the extension with `customPublishing = true` — which is what - * `uber-jar-module.gradle.kts` does. Plain registration fails on the second - * call, during configuration, so registration has to be idempotent. - */ -private fun TaskContainer.getOrRegister( - name: String, - init: Task.() -> Unit -): TaskProvider = - if (names.contains(name)) { - named(name) - } else { - register(name, init) - } - /** * Returns the `clean` task of this container, if it has one. * @@ -345,7 +332,7 @@ private fun Project.publishedArtifacts(): Map { */ private fun Project.pomFileOf(publication: MavenPublication): File? = tasks.withType(GenerateMavenPom::class.java) - .findByName("generatePomFileFor${publication.taskSuffix()}") + .findByName(publication.pomTaskName()) ?.destination ?.takeIf { it.exists() } @@ -390,6 +377,11 @@ private fun MavenPublication.rejectMavenSnapshot() { private fun MavenPublication.taskSuffix(): String = "${name.replaceFirstChar { it.titlecase(Locale.ROOT) }}Publication" +/** + * Returns the name of the task that writes the POM of this publication. + */ +internal fun MavenPublication.pomTaskName(): String = "generatePomFileFor${taskSuffix()}" + /** * Returns the SHA-256 digest of this file as a lowercase hexadecimal string. * diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/publish/PublicationSbom.kt b/buildSrc/src/main/kotlin/io/spine/gradle/publish/PublicationSbom.kt index e32c622fdf2..64ea35ec768 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/publish/PublicationSbom.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/publish/PublicationSbom.kt @@ -154,7 +154,7 @@ internal object PublicationSbom { * Whether a project is a JVM or a multiplatform module is known only once it is * evaluated, so the registration waits until then. It is idempotent, because * `spinePublishing { }` may reach one project twice, as the documentation of - * `getOrRegister` in `PublicationChecksums.kt` explains. + * [PublicationChecksums.registerTasks] explains. */ fun registerTasks(host: Project, published: Set) { published.forEach { project -> diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/publish/SpinePublishing.kt b/buildSrc/src/main/kotlin/io/spine/gradle/publish/SpinePublishing.kt index 4af48a56337..31a7a9fcd4d 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/publish/SpinePublishing.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/publish/SpinePublishing.kt @@ -17,6 +17,7 @@ package io.spine.gradle.publish import io.spine.dependency.local.Spine +import io.spine.gradle.github.pages.PublishAllApiDocs import io.spine.gradle.repo.Repository import java.util.Locale import org.gradle.api.Project @@ -332,6 +333,9 @@ open class SpinePublishing(private val project: Project) { // After `PublicationSbom`: its `projectsEvaluated` callback adds the SBOM artifacts // that the one of `PublicationState` collects. PublicationState.registerOn(project.rootProject) + // `publishAllApiDocs` is registered here, as this is where the published modules + // are known. It is not tied to `publish`: neither task depends on the other. + PublishAllApiDocs.registerIn(project.rootProject, projectsToPublish) } /** diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/publish/StandardJavaPublicationHandler.kt b/buildSrc/src/main/kotlin/io/spine/gradle/publish/StandardJavaPublicationHandler.kt index f17170a0711..030d03470c5 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/publish/StandardJavaPublicationHandler.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/publish/StandardJavaPublicationHandler.kt @@ -17,9 +17,11 @@ package io.spine.gradle.publish import io.spine.gradle.repo.Repository import org.gradle.api.Project import org.gradle.api.publish.maven.MavenPublication +import org.gradle.api.publish.maven.tasks.GenerateMavenPom import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.bundling.Jar import org.gradle.kotlin.dsl.create +import org.gradle.kotlin.dsl.withType /** * A publication for a typical Java project. @@ -58,6 +60,12 @@ class StandardJavaPublicationHandler private constructor( */ const val PUBLICATION_NAME = "mavenJava" + /** + * The packaging of a POM whose main artifact is a JAR, + * and the extension of that artifact. + */ + private const val JAR = "jar" + override fun create( project: Project, destinations: Set, @@ -108,6 +116,7 @@ class StandardJavaPublicationHandler private constructor( val javaComponent = project.components.findByName("java") javaComponent?.let { from(it) + declareJarPackaging() } /* @@ -118,4 +127,63 @@ class StandardJavaPublicationHandler private constructor( artifact(it) } } + + /** + * Sets the `jar` packaging of the POM, which Gradle would calculate from + * the JAR of the `java` component. + * + * Until the packaging is set, reading it makes Gradle calculate it from the artifacts + * of the publication, which creates the tasks producing them. [PublicationSbom] reads + * the packaging in every build, so that the SBOM it adds does not change it. + * + * Writing the POM fails if the publication turns out to have a main artifact other + * than one JAR, so that a misleading POM is not published. The artifacts are checked + * when the POM is written, so that the check neither creates the tasks producing them + * nor takes the artifacts of the component into the publication before it is final. + * The result is an input of the task, so an up-to-date task does not hide a mismatch. + */ + private fun MavenPublication.declareJarPackaging() { + pom.packaging = JAR + val publication = this + val pomTask = pomTaskName() + project.tasks.withType() + .named { it == pomTask } + .configureEach { + val mismatch = project.provider { publication.jarPackagingMismatch() } + inputs.property("jarPackagingMismatch", mismatch.orElse("")) + doFirst { + mismatch.orNull?.let { throw IllegalStateException(it) } + } + } + } + + /** + * Describes how this publication contradicts the `jar` packaging, or returns `null` + * if it does not. + * + * The publication contradicts it if it keeps the `jar` packaging, but does not have + * exactly one main artifact, a JAR, the SBOM not counted. Such is the case if + * the build gives the `jar` task a classifier, or adds an artifact without one. + */ + private fun MavenPublication.jarPackagingMismatch(): String? { + if (pom.packaging != JAR) { + // The build has set a packaging of its own. + return null + } + val mainArtifacts = artifacts.filter { + it.classifier.isNullOrEmpty() && it.extension != PublicationSbom.sbomExtension + } + return if (mainArtifacts.singleOrNull()?.extension == JAR) { + null + } else { + val found = if (mainArtifacts.isEmpty()) { + "no main artifact" + } else { + "the main artifacts ${mainArtifacts.map { it.file.name }}" + } + "The POM of the `$name` publication of `${project.path}` declares the `jar`" + + " packaging, but the publication has $found. Please keep the `jar`" + + " task without a classifier, or publish the module with custom publishing." + } + } } diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/report/license/LicenseReporter.kt b/buildSrc/src/main/kotlin/io/spine/gradle/report/license/LicenseReporter.kt index 65d8d69a14b..c2af416980a 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/report/license/LicenseReporter.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/report/license/LicenseReporter.kt @@ -20,7 +20,6 @@ import com.github.jk1.license.LicenseReportPlugin import io.spine.dependency.local.Spine import io.spine.gradle.SpineTaskGroup import io.spine.gradle.applyPlugin -import io.spine.gradle.getTask import io.spine.gradle.report.license.Paths.outputFilename import io.spine.gradle.report.license.Paths.relativePath import java.io.File @@ -130,10 +129,13 @@ object LicenseReporter { group = SpineTaskGroup.name description = "Merges per-project license reports into a single repository-wide report" val consolidationTask = this - val assembleTask = project.getTask("assemble") + val assembleTask = project.tasks.named("assemble") val sourceProjects: Iterable = sourceProjects(rootProject) sourceProjects.forEach { - val perProjectTask = it.getTask(projectTaskName) + // Obtained rather than configured lazily: in a single-project build, this + // action belongs to the container that holds the task, and Gradle forbids + // configuring a task of a container from such an action. + val perProjectTask = it.tasks.named(projectTaskName).get() consolidationTask.dependsOn(perProjectTask) perProjectTask.dependsOn(assembleTask) } @@ -142,8 +144,9 @@ object LicenseReporter { } dependsOn(assembleTask) } - project.getTask("build") - .finalizedBy(mergeTask) + project.tasks.named("build").configure { + finalizedBy(mergeTask) + } } /** diff --git a/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/PomGenerator.kt b/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/PomGenerator.kt index 479e4f5f207..234a3af44ff 100644 --- a/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/PomGenerator.kt +++ b/buildSrc/src/main/kotlin/io/spine/gradle/report/pom/PomGenerator.kt @@ -100,11 +100,9 @@ object PomGenerator { writer.writeTo(pomFile) } - val assembleTask = project.tasks.findByName("assemble")!! - dependsOn(assembleTask) + dependsOn(project.tasks.named("assemble")) } - val buildTask = project.tasks.findByName("build")!! - buildTask.finalizedBy(task) + project.tasks.named("build").configure { finalizedBy(task) } } } diff --git a/buildSrc/src/main/kotlin/jvm-module.gradle.kts b/buildSrc/src/main/kotlin/jvm-module.gradle.kts index 830335fbd89..11777117af8 100644 --- a/buildSrc/src/main/kotlin/jvm-module.gradle.kts +++ b/buildSrc/src/main/kotlin/jvm-module.gradle.kts @@ -26,7 +26,7 @@ import io.spine.dependency.local.Reflect import io.spine.dependency.test.Jacoco import io.spine.gradle.SpineTaskGroup import io.spine.gradle.checkstyle.CheckStyleConfig -import io.spine.gradle.github.pages.updateGitHubPages +import io.spine.gradle.github.pages.publishApiDocs import io.spine.gradle.javac.configureErrorProne import io.spine.gradle.javac.configureJavac import io.spine.gradle.javadoc.JavadocConfig @@ -58,7 +58,7 @@ project.run { val generatedDir = "$projectDir/generated" setTaskDependencies(generatedDir) - configureGitHubPages() + configureApiDocs() } typealias Module = Project @@ -150,19 +150,21 @@ fun Module.setTaskDependencies(generatedDir: String) { clean.configure { dependsOn(cleanGenerated) } - - project.afterEvaluate { - val publish = tasks.findByName("publish") - publish?.dependsOn("${project.path}:updateGitHubPages") - } } afterEvaluate { configureTaskDependencies() } } -fun Module.configureGitHubPages() { - updateGitHubPages { +/** + * Applies the plugin that registers the `publishApiDocs` task of this module. + * + * The `publish-api-docs.yml` workflow runs the task through `publishAllApiDocs` of + * the root project, which depends on it for the published modules only. It is not + * a part of `publish`. + */ +fun Module.configureApiDocs() { + publishApiDocs { rootFolder.set(rootDir) } } diff --git a/buildSrc/src/main/kotlin/module.gradle.kts b/buildSrc/src/main/kotlin/module.gradle.kts index bf1eab838b0..f7018cb3ff5 100644 --- a/buildSrc/src/main/kotlin/module.gradle.kts +++ b/buildSrc/src/main/kotlin/module.gradle.kts @@ -33,7 +33,7 @@ import io.spine.dependency.local.Base import io.spine.dependency.local.CoreJvm import io.spine.dependency.local.ToolBase import io.spine.dependency.local.Validation -import io.spine.gradle.github.pages.updateGitHubPages +import io.spine.gradle.github.pages.publishApiDocs import io.spine.gradle.javac.configureErrorProne import io.spine.gradle.javac.configureJavac import io.spine.gradle.javadoc.JavadocConfig @@ -150,11 +150,7 @@ fun Module.configureKotlin() { * Configures documentation publishing for this subproject. */ fun Module.setupDocPublishing() { - updateGitHubPages { + publishApiDocs { rootFolder.set(rootDir) } - - tasks.named("publish") { - dependsOn("${project.path}:updateGitHubPages") - } } diff --git a/buildSrc/src/test/kotlin/BuildExtensionsSpec.kt b/buildSrc/src/test/kotlin/BuildExtensionsSpec.kt new file mode 100644 index 00000000000..4ac7a556709 --- /dev/null +++ b/buildSrc/src/test/kotlin/BuildExtensionsSpec.kt @@ -0,0 +1,73 @@ +/* + * Copyright 2026 CodeMatters, Lda. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.matchers.shouldBe +import org.gradle.api.DefaultTask +import org.gradle.api.InvalidUserDataException +import org.gradle.api.Project +import org.gradle.api.tasks.JavaExec +import org.gradle.testfixtures.ProjectBuilder +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test + +@DisplayName("Build extensions should") +internal class BuildExtensionsSpec { + + private val project: Project = ProjectBuilder.builder().build() + + @Nested inner class + `set remote debug options` { + + @Test + fun `on a JavaExec task once it is created`() { + project.tasks.register("launch", JavaExec::class.java) + + project.setRemoteDebug("launch") + + val debug = project.tasks.named("launch", JavaExec::class.java).get().debugOptions + debug.enabled.get() shouldBe true + debug.port.get() shouldBe BuildSettings.REMOTE_DEBUG_PORT + } + + @Test + fun `without creating the task`() { + var created = false + project.tasks.register("launch", JavaExec::class.java) { created = true } + + project.setRemoteDebug("launch") + + created shouldBe false + } + + @Test + fun `failing at once for a missing task`() { + shouldThrow { + project.setRemoteDebug("missing") + } + } + + @Test + fun `failing at once for a task other than JavaExec`() { + var created = false + project.tasks.register("plain", DefaultTask::class.java) { created = true } + + shouldThrow { + project.setRemoteDebug("plain") + } + created shouldBe false + } + } +} diff --git a/buildSrc/src/test/kotlin/io/spine/gradle/CliSpec.kt b/buildSrc/src/test/kotlin/io/spine/gradle/CliSpec.kt new file mode 100644 index 00000000000..69f03ba85b1 --- /dev/null +++ b/buildSrc/src/test/kotlin/io/spine/gradle/CliSpec.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2026 CodeMatters, Lda. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +package io.spine.gradle + +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldContain +import java.io.File +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.io.TempDir + +@DisplayName("`Cli` should") +internal class CliSpec { + + @TempDir + lateinit var workingFolder: File + + @Test + fun `return the output as the command wrote it, line breaks included`() { + // `printf` turns the `\n` sequences of its format into line breaks. + Cli(workingFolder).execute("printf", "one\\ntwo\\n") shouldBe "one\ntwo\n" + } + + @Test + fun `run a command assembled as a list`() { + Cli(workingFolder).execute(listOf("printf", "%s", "one")) shouldBe "one" + } + + @Test + fun `fail with the exit code and the error output of a failed command`() { + val error = shouldThrow { + Cli(workingFolder).execute("sh", "-c", "echo 'went wrong' >&2; exit 3") + } + + error.message shouldContain "exit code 3" + error.message shouldContain "went wrong" + } +} diff --git a/buildSrc/src/test/kotlin/io/spine/gradle/docs/DokkaSetupIgTest.kt b/buildSrc/src/test/kotlin/io/spine/gradle/docs/DokkaSetupIgTest.kt index 6ec5effe990..150fe348f30 100644 --- a/buildSrc/src/test/kotlin/io/spine/gradle/docs/DokkaSetupIgTest.kt +++ b/buildSrc/src/test/kotlin/io/spine/gradle/docs/DokkaSetupIgTest.kt @@ -52,7 +52,7 @@ private const val PROBE_PATH = ":dokka-sample:dokkaGeneratePublicationProbe" * The build publishes a `javadocJar` that depends on the probe, just as the `javadocJar` * that `spinePublishing` creates depends on `dokkaGeneratePublicationJavadoc`. The build * publishes to a file-based repository and to its own Maven Local repository, so it never - * touches `~/.m2`. The `updateGitHubPages` task of the build stands in for the one that + * touches `~/.m2`. The `publishApiDocs` task of the build stands in for the one that * copies the Dokka output to GitHub Pages. * * The project with the probe is named `dokka-sample`, like the modules of `dokka-tools`, @@ -105,10 +105,10 @@ internal class DokkaSetupIgTest { } @Test - fun `run a Dokka task when the build updates GitHub Pages and publishes to Maven Local`() { + fun `run a Dokka task when the build publishes the API docs and publishes to Maven Local`() { runStoringThenReusing( ":dokka-sample:publishToMavenLocal", - ":dokka-sample:updateGitHubPages" + ":dokka-sample:publishApiDocs" ) { it.probeOutcome shouldBe SUCCESS } @@ -227,7 +227,7 @@ internal class DokkaSetupIgTest { } } - tasks.register("updateGitHubPages") { + tasks.register("publishApiDocs") { dependsOn(docsProbe) } """.trimIndent() diff --git a/buildSrc/src/test/kotlin/io/spine/gradle/git/RepositorySpec.kt b/buildSrc/src/test/kotlin/io/spine/gradle/git/RepositorySpec.kt new file mode 100644 index 00000000000..e84919f5558 --- /dev/null +++ b/buildSrc/src/test/kotlin/io/spine/gradle/git/RepositorySpec.kt @@ -0,0 +1,313 @@ +/* + * Copyright 2026 CodeMatters, Lda. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +package io.spine.gradle.git + +import io.kotest.matchers.collections.shouldBeEmpty +import io.kotest.matchers.collections.shouldContain +import io.kotest.matchers.collections.shouldContainExactly +import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder +import io.kotest.matchers.file.shouldExist +import io.kotest.matchers.file.shouldNotExist +import io.kotest.matchers.shouldBe +import io.spine.gradle.Cli +import io.spine.gradle.github.pages.docsCheckoutPatterns +import java.io.File +import org.gradle.api.Project +import org.gradle.testfixtures.ProjectBuilder +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.io.TempDir + +/** + * Verifies a [sparse clone][Repository.Factory.cloneSparsely] against a local bare + * repository, reached by a `file:` URL the way Git reaches a remote one: with a plain + * path, Git would ignore `--depth` and `--filter`. + * + * The `gh-pages` branch of the repository holds a top-level `CNAME`, the documentation + * of the module `m`, with the folders of its versions `1.0.0` and `1.1.0` and a file + * next to them, and the documentation of the module `other`. The tests check out + * the paths that [docsCheckoutPatterns] selects for `m`. + */ +@DisplayName("A sparse clone of a `Repository` should") +internal class RepositorySpec { + + @TempDir + lateinit var tempDir: File + + private lateinit var remote: File + + /** A full clone of the [remote], standing in for the publications of other modules. */ + private lateinit var seed: File + + private lateinit var project: Project + + private val user = UserInfo("Docs Publisher", "docs@example.com") + + @BeforeEach + fun createRemote() { + remote = tempDir.resolve("remote.git") + git(tempDir, "init", "--bare", "--initial-branch=master", remote.path) + // Allow partial clones, with the blobs fetched on demand, as GitHub does. + git(remote, "config", "uploadpack.allowFilter", "true") + git(remote, "config", "uploadpack.allowAnySHA1InWant", "true") + + seed = tempDir.resolve("seed") + git(tempDir, "clone", remote.path, seed.path) + git(seed, "config", "user.name", "Seed") + git(seed, "config", "user.email", "seed@example.com") + seed.write("README.md", "The sources.") + seed.commit("Add the sources.") + git(seed, "push", "origin", "HEAD:refs/heads/master") + + git(seed, "switch", "--orphan", "gh-pages") + seed.write("CNAME", "spine.io\n") + seed.write("javadoc/m/index.html", "m, 1.1.0") + seed.write("javadoc/m/v/1.0.0/index.html", "m, 1.0.0") + seed.write("javadoc/m/v/1.1.0/index.html", "m, 1.1.0") + seed.write("javadoc/m/v/notes.txt", "Not a version.") + seed.write("javadoc/other/index.html", "other, 1.1.0") + seed.commit("Publish the documentation.") + git(seed, "push", "origin", "gh-pages") + + project = ProjectBuilder.builder() + .withProjectDir(tempDir.resolve("project")) + .build() + } + + @Test + fun `check out only the top-level files`() { + cloneDocs().use { repo -> + val root = repo.location.toFile() + + root.resolve("CNAME").shouldExist() + root.resolve("javadoc").shouldNotExist() + } + } + + @Test + fun `check out only the paths of a publication`() { + cloneDocs().use { repo -> + repo.checkOutOnly(docsCheckoutPatterns("javadoc/m", "2.0.0")) + val root = repo.location.toFile() + + root.resolve("javadoc/m/index.html").shouldExist() + root.resolve("javadoc/m/v/1.0.0").shouldNotExist() + root.resolve("javadoc/other").shouldNotExist() + } + } + + /** + * Publishing a version again, as a re-run of a failed publication does, writes + * into the folder of that version, which is in the branch already. + */ + @Test + fun `check out and update the folder of a version published before`() { + cloneDocs().use { repo -> + repo.checkOutOnly(docsCheckoutPatterns("javadoc/m", "1.1.0")) + val root = repo.location.toFile() + + root.resolve("javadoc/m/v/1.1.0/index.html").shouldExist() + root.resolve("javadoc/m/v/1.0.0").shouldNotExist() + + root.write("javadoc/m/v/1.1.0/index.html", "m, 1.1.0, again") + repo.commitAllChanges("Publish `m` 1.1.0 again.") + + changedPaths(root) shouldContainExactly listOf("M\tjavadoc/m/v/1.1.0/index.html") + } + } + + @Test + fun `list the folders that are not checked out, without the files`() { + cloneDocs().use { repo -> + repo.listFolders("javadoc/m/v") shouldContainExactly listOf("1.0.0", "1.1.0") + repo.listFolders("javadoc/absent").shouldBeEmpty() + } + } + + @Test + fun `commit only the changed paths, keeping those not checked out`() { + cloneDocs().use { repo -> + repo.checkOutOnly(docsCheckoutPatterns("javadoc/m", "2.0.0")) + val root = repo.location.toFile() + root.write("javadoc/m/index.html", "m, 2.0.0") + root.write("javadoc/m/v/2.0.0/index.html", "m, 2.0.0") + + repo.commitAllChanges("Publish `m` 2.0.0.") + + changedPaths(root) shouldContainExactlyInAnyOrder listOf( + "M\tjavadoc/m/index.html", + "A\tjavadoc/m/v/2.0.0/index.html" + ) + } + } + + @Test + fun `push onto a commit that another publication pushed meanwhile`() { + cloneDocs().use { repo -> + repo.checkOutOnly(docsCheckoutPatterns("javadoc/m", "2.0.0")) + repo.location.toFile().write("javadoc/m/v/2.0.0/index.html", "m, 2.0.0") + repo.commitAllChanges("Publish `m` 2.0.0.") + + seed.write("javadoc/other/index.html", "other, 2.0.0") + seed.commit("Publish `other` 2.0.0.") + git(seed, "push", "origin", "gh-pages") + + repo.push() + } + + val log = git(remote, "log", "--format=%s", "gh-pages").lines() + log shouldContain "Publish `m` 2.0.0." + log shouldContain "Publish `other` 2.0.0." + git(remote, "show", "gh-pages:javadoc/m/v/2.0.0/index.html") shouldBe "m, 2.0.0" + git(remote, "show", "gh-pages:javadoc/other/index.html") shouldBe "other, 2.0.0" + git(remote, "show", "gh-pages:javadoc/m/v/1.0.0/index.html") shouldBe "m, 1.0.0" + } + + @Test + fun `create a branch that does not exist yet, and push to it`() { + val cname = "spine.io\n" + + cloneSite(initialFiles = mapOf("CNAME" to cname)).use { repo -> + git(remote, "ls-tree", "--name-only", "site").trim() shouldBe "CNAME" + git(remote, "show", "site:CNAME") shouldBe cname + + repo.checkOutOnly(docsCheckoutPatterns("javadoc/m", "1.0.0")) + repo.location.toFile().write("javadoc/m/index.html", "m, 1.0.0") + repo.commitAllChanges("Publish `m` 1.0.0.") + repo.push() + } + + git(remote, "show", "site:javadoc/m/index.html") shouldBe "m, 1.0.0" + } + + @Test + fun `adopt a branch that another publication created first, and push to it`() { + val preparedCommit = raceForSiteBranch() + + cloneSite(initialFiles = mapOf("CNAME" to "spine.io\n")).use { repo -> + val root = repo.location.toFile() + git(root, "rev-parse", "HEAD").trim() shouldBe preparedCommit + git(root, "rev-parse", "--abbrev-ref", "site@{upstream}").trim() shouldBe + "origin/site" + + raceHook.delete() + repo.checkOutOnly(docsCheckoutPatterns("javadoc/m", "1.0.0")) + root.write("javadoc/m/index.html", "m, 1.0.0") + repo.commitAllChanges("Publish `m` 1.0.0.") + repo.push() + } + + git(remote, "show", "site:javadoc/m/index.html") shouldBe "m, 1.0.0" + } + + /** + * The full [clone][Repository.Factory.clone] shares the adoption of a branch with + * the sparse one. + */ + @Test + fun `adopt a branch that another publication created first, in a full clone`() { + val preparedCommit = raceForSiteBranch() + + Repository.clone( + project, + sshUrl = url, + user = user, + branch = "site", + initialFiles = mapOf("CNAME" to "spine.io\n") + ).use { repo -> + val root = repo.location.toFile() + git(root, "rev-parse", "HEAD").trim() shouldBe preparedCommit + git(root, "rev-parse", "--abbrev-ref", "site@{upstream}").trim() shouldBe + "origin/site" + } + } + + /** + * Makes another publication create the `site` branch after this one has found it + * missing, and push first. + * + * The `pre-receive` hook of the [remote] stands in for that publication: it creates + * the branch from a prepared commit, and rejects the push of this publication. + * + * @return the prepared commit. + */ + private fun raceForSiteBranch(): String { + git(seed, "switch", "--orphan", "prepared") + seed.write("CNAME", "spine.io\n") + seed.commit("Initialize the `site` branch.") + git(seed, "push", "origin", "prepared:refs/heads/prepared-site") + // Git keeps the objects a push brings in a quarantine until the hook accepts + // them, and refuses to update a reference from there, so the hook leaves it. + raceHook.apply { + writeText( + """ + #!/bin/sh + env -u GIT_QUARANTINE_PATH -u GIT_OBJECT_DIRECTORY \ + -u GIT_ALTERNATE_OBJECT_DIRECTORIES \ + git update-ref refs/heads/site refs/heads/prepared-site + exit 1 + """.trimIndent() + ) + setExecutable(true) + } + return git(seed, "rev-parse", "HEAD").trim() + } + + private val raceHook: File + get() = remote.resolve("hooks/pre-receive") + + private val url: String + get() = remote.toPath().toUri().toString() + + /** + * Obtains the paths the last commit in the [root] of a clone changed, each + * with the letter of its change. + */ + private fun changedPaths(root: File): List = + git(root, "show", "--name-status", "--format=", "HEAD") + .lines() + .filter { it.isNotBlank() } + + private fun cloneDocs(): Repository = + Repository.cloneSparsely(project, sshUrl = url, user = user, branch = "gh-pages") + + /** + * Clones the `site` branch, which the [remote] does not have at first. + */ + private fun cloneSite(initialFiles: Map): Repository = + Repository.cloneSparsely( + project, + sshUrl = url, + user = user, + branch = "site", + initialFiles = initialFiles + ) +} + +private fun git(directory: File, vararg arguments: String): String = + Cli(directory).execute("git", *arguments) + +private fun File.write(path: String, content: String) { + resolve(path).apply { + parentFile.mkdirs() + writeText(content) + } +} + +private fun File.commit(message: String) { + git(this, "add", "--all") + git(this, "commit", "--message=$message") +} diff --git a/buildSrc/src/test/kotlin/io/spine/gradle/github/pages/NewestAboveSpec.kt b/buildSrc/src/test/kotlin/io/spine/gradle/github/pages/NewestAboveSpec.kt new file mode 100644 index 00000000000..ad5f631703f --- /dev/null +++ b/buildSrc/src/test/kotlin/io/spine/gradle/github/pages/NewestAboveSpec.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2026 CodeMatters, Lda. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +package io.spine.gradle.github.pages + +import io.kotest.matchers.nulls.shouldBeNull +import io.kotest.matchers.shouldBe +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +/** + * Verifies how the publication of the API documentation decides whether the most + * recent documentation of a module may be replaced by that of the published version. + */ +@DisplayName("`newestAbove` should") +internal class NewestAboveSpec { + + @Test + fun `find no newer version when nothing is published`() { + emptyList().newestAbove("2.0.0").shouldBeNull() + } + + @Test + fun `find no newer version for the newest one`() { + listOf("1.9.0", "2.0.0-SNAPSHOT.575", "2.0.0-SNAPSHOT.99") + .newestAbove("2.0.0").shouldBeNull() + } + + @Test + fun `find no newer version for a version published again`() { + listOf("1.9.0", "2.0.0").newestAbove("2.0.0").shouldBeNull() + } + + @Test + fun `find the newest version above an older one`() { + listOf("1.9.0", "2.1.0", "2.0.0").newestAbove("1.9.5") shouldBe "2.1.0" + } + + @Test + fun `compare versions by their meaning rather than as text`() { + listOf("2.9.0").newestAbove("2.10.0").shouldBeNull() + listOf("2.10.0").newestAbove("2.9.0") shouldBe "2.10.0" + } +} diff --git a/buildSrc/src/test/kotlin/io/spine/gradle/github/pages/PublishApiDocsIgTest.kt b/buildSrc/src/test/kotlin/io/spine/gradle/github/pages/PublishApiDocsIgTest.kt new file mode 100644 index 00000000000..9fc3a345f1f --- /dev/null +++ b/buildSrc/src/test/kotlin/io/spine/gradle/github/pages/PublishApiDocsIgTest.kt @@ -0,0 +1,349 @@ +/* + * Copyright 2026 CodeMatters, Lda. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +package io.spine.gradle.github.pages + +import io.kotest.matchers.collections.shouldBeEmpty +import io.kotest.matchers.collections.shouldContain +import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder +import io.kotest.matchers.collections.shouldNotContain +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldContain +import java.io.File +import org.gradle.testkit.runner.GradleRunner +import org.gradle.testkit.runner.TaskOutcome +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.io.TempDir + +/** + * Verifies which tasks the `publishAllApiDocs` task of the root project, registered by + * `spinePublishing { }`, brings into a build: the `publishApiDocs` tasks of the projects. + * + * The builds that would publish documentation run with `--dry-run`, so that nothing + * is pushed to GitHub Pages. The builds of a snapshot version run for real, as they + * publish nothing. + * + * In the multi-module build, `api` and `backend` are published and document + * themselves, `testing` documents itself, but is not published, as a module of + * integration tests, and `bare` is published, but does not document itself. + * + * No build applies Dokka. A task named like the Dokka one stands in for it, as + * the plugin and the `javadocJar` of `spinePublishing` look the task up by name. + */ +@DisplayName("`publishAllApiDocs` task should") +internal class PublishApiDocsIgTest { + + @TempDir + lateinit var projectDir: File + + @Test + fun `publish the docs of each published module that documents itself`() { + writeMultiModuleBuild(documented = setOf("api", "backend", "testing")) + + val planned = dryRun(":$aggregate") + + planned shouldContain ":$aggregate" + planned.docsTasks() shouldContainExactlyInAnyOrder listOf( + ":api:$taskName", + ":backend:$taskName" + ) + // `testing` has the task, so the aggregate left it out only for not being published. + dryRun(":testing:$taskName").docsTasks() shouldBe listOf(":testing:$taskName") + } + + /** + * `lib` opens `spinePublishing { }` on its own, so the second call reaches + * the task of the root project while `lib` is evaluated, with projects of its own. + */ + @Test + fun `publish the docs of a module that configures its own publishing`() { + file("settings.gradle.kts").writeText( + """ + rootProject.name = "own-publishing-sample" + include("api", "lib") + """.trimIndent() + ) + file("build.gradle.kts").writeText( + """ + ${buildscript()} + + import io.spine.gradle.github.pages.publishApiDocs + import io.spine.gradle.publish.spinePublishing + + allprojects { + group = "io.spine.sample" + version = "1.0.0" + } + + subprojects { + apply(plugin = "java-library") + $generatorStub + } + + project("api").publishApiDocs { + rootFolder.set(rootDir) + } + + spinePublishing { + modules = setOf("api") + destinations = emptySet() + } + """.trimIndent() + ) + file("lib/build.gradle.kts").apply { + parentFile.mkdirs() + writeText( + """ + import io.spine.gradle.github.pages.publishApiDocs + import io.spine.gradle.publish.spinePublishing + + publishApiDocs { + rootFolder.set(rootDir) + } + + spinePublishing { + destinations = emptySet() + } + """.trimIndent() + ) + } + file("api").mkdirs() + + dryRun(":$aggregate").docsTasks() shouldContainExactlyInAnyOrder listOf( + ":api:$taskName", + ":lib:$taskName" + ) + } + + @Test + fun `not be a part of the publication to Maven repositories`() { + writeMultiModuleBuild(documented = setOf("api", "backend", "testing")) + + val planned = dryRun("publish") + + planned shouldContain ":api:publish" + planned shouldNotContain ":$aggregate" + planned.docsTasks().shouldBeEmpty() + } + + @Test + fun `do nothing in a build that documents no module`() { + writeMultiModuleBuild(documented = emptySet()) + + val result = runner(":$aggregate").build() + + result.tasks.map { it.path } shouldBe listOf(":$aggregate") + result.task(":$aggregate")?.outcome shouldBe TaskOutcome.UP_TO_DATE + } + + @Test + fun `publish the docs of the project in a single-project build`() { + writeSingleProjectBuild(version = "1.0.0") + + val planned = dryRun(":$aggregate") + + planned.docsTasks() shouldBe listOf(":$taskName") + planned shouldContain ":${TaskName.copyJavadocDocs}" + } + + @Test + fun `publish no docs of a snapshot version`() { + writeMultiModuleBuild(documented = setOf("api", "backend", "testing"), version = snapshot) + + val result = runner(":$aggregate").build() + + val paths = result.tasks.map { it.path } + paths.docsTasks() shouldContainExactlyInAnyOrder listOf( + ":api:$taskName", + ":backend:$taskName" + ) + paths shouldNotContain ":api:${TaskName.copyJavadocDocs}" + result.output shouldContain "`$snapshot` is a snapshot version" + } + + /** + * Only the publication to GitHub Pages skips a snapshot version. The archives of + * its documentation, built by Dokka, are still published to Maven repositories. + */ + @Test + fun `leave the docs archives of a snapshot version to the Maven publication`() { + writeMultiModuleBuild(documented = setOf("api"), version = snapshot) + file("build.gradle.kts").appendText( + "\n" + """ + project("api") { + apply(plugin = "maven-publish") + the().repositories.maven { + name = "remote" + url = uri(rootDir.resolve("remote-repo")) + } + } + """.trimIndent() + ) + + val planned = dryRun(":api:publish") + + planned shouldContain ":api:javadocJar" + planned shouldContain ":api:dokkaGeneratePublicationJavadoc" + planned.docsTasks().shouldBeEmpty() + } + + @Test + fun `publish no docs of a snapshot version in a single-project build`() { + writeSingleProjectBuild(version = snapshot) + + val result = runner(":$aggregate").build() + + result.tasks.map { it.path } shouldContainExactlyInAnyOrder listOf( + ":$taskName", + ":$aggregate" + ) + result.output shouldContain "`$snapshot` is a snapshot version" + } + + private fun writeSingleProjectBuild(version: String) { + file("settings.gradle.kts").writeText("""rootProject.name = "single-sample"""") + file("build.gradle.kts").writeText( + """ + ${buildscript()} + + import io.spine.gradle.github.pages.publishApiDocs + import io.spine.gradle.publish.spinePublishing + + group = "io.spine.sample" + version = "$version" + + apply(plugin = "java-library") + $generatorStub + + publishApiDocs { + rootFolder.set(rootDir) + } + + spinePublishing { + destinations = emptySet() + } + """.trimIndent() + ) + } + + private fun writeMultiModuleBuild(documented: Set, version: String = "1.0.0") { + val modules = listOf("api", "backend", "bare", "testing") + modules.forEach { file(it).mkdirs() } + file("settings.gradle.kts").writeText( + """ + rootProject.name = "docs-sample" + include(${modules.joinToString { "\"$it\"" }}) + """.trimIndent() + ) + file("build.gradle.kts").writeText( + """ + ${buildscript()} + + import io.spine.gradle.github.pages.publishApiDocs + import io.spine.gradle.publish.spinePublishing + + allprojects { + group = "io.spine.sample" + version = "$version" + } + + subprojects { + apply(plugin = "java-library") + $generatorStub + } + + listOf(${documented.joinToString { "\"$it\"" }}).forEach { + project(it).publishApiDocs { + rootFolder.set(rootDir) + } + } + + spinePublishing { + modules = setOf("api", "backend", "bare") + destinations = emptySet() + } + """.trimIndent() + ) + } + + /** + * Runs the build with `--dry-run` and returns the paths of the tasks + * it would run. + * + * TestKit reports no tasks for a dry run, so they are read from the output, + * which lists each of them as ` SKIPPED`. + */ + private fun dryRun(task: String): List = + runner(task, "--dry-run").build().output.lines() + .filter { it.startsWith(":") && it.endsWith(" SKIPPED") } + .map { it.substringBefore(' ') } + + private fun runner(vararg args: String): GradleRunner = + GradleRunner.create() + .withProjectDir(projectDir) + .withArguments(*args, "--stacktrace") + + /** + * Selects the paths of the tasks that publish documentation. + */ + private fun List.docsTasks(): List = + filter { it.substringAfterLast(':') == taskName } + + private fun file(relativePath: String): File = projectDir.resolve(relativePath) + + private fun buildscript(): String = + """ + buildscript { + dependencies { + classpath(files( + ${buildSrcClasspath()} + )) + } + } + """.trimIndent() + + /** + * Renders the classpath with the production classes of `buildSrc` as + * arguments of `files(...)`, for injection into the build script classpath + * of the generated build. + * + * The classpath comes from the `test` task in `buildSrc/build.gradle.kts`. + */ + private fun buildSrcClasspath(): String { + val classpath = requireNotNull(System.getProperty("buildSrc.classpath")) { + "The `buildSrc.classpath` system property is not set." + + " It is supplied by the `test` task in `buildSrc/build.gradle.kts`." + } + return classpath.split(File.pathSeparator).joinToString(",\n") { + " \"${File(it).invariantSeparatorsPath}\"" + } + } + + private companion object { + + const val taskName = TaskName.publishApiDocs + + const val aggregate = TaskName.publishAllApiDocs + + /** A version in the format the Spine SDK publishes from every merge. */ + const val snapshot = "1.0.0-SNAPSHOT.1" + + /** + * A task standing in for the Dokka one whose output the plugin copies, and on + * which the `javadocJar` of `spinePublishing` depends. + */ + const val generatorStub = """tasks.register("dokkaGeneratePublicationJavadoc")""" + } +} diff --git a/buildSrc/src/test/kotlin/io/spine/gradle/javadoc/JavadocConfigSpec.kt b/buildSrc/src/test/kotlin/io/spine/gradle/javadoc/JavadocConfigSpec.kt new file mode 100644 index 00000000000..19b9e973166 --- /dev/null +++ b/buildSrc/src/test/kotlin/io/spine/gradle/javadoc/JavadocConfigSpec.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2026 CodeMatters, Lda. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +package io.spine.gradle.javadoc + +import io.kotest.matchers.shouldBe +import org.gradle.api.Project +import org.gradle.api.plugins.JavaPlugin +import org.gradle.testfixtures.ProjectBuilder +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +@DisplayName("`JavadocConfig` should") +internal class JavadocConfigSpec { + + private val project: Project = ProjectBuilder.builder().build().also { + it.pluginManager.apply(JavaPlugin::class.java) + } + + /** + * The project of a build without subprojects is not a production module, + * so its `javadoc` task is disabled. + */ + @Test + fun `configure the javadoc task once Gradle creates it`() { + var created = false + project.tasks.named { it == "javadoc" }.configureEach { created = true } + + JavadocConfig.applyTo(project) + + created shouldBe false + project.tasks.javadocTask().get().enabled shouldBe false + } +} diff --git a/buildSrc/src/test/kotlin/io/spine/gradle/publish/ProtoExtsSpec.kt b/buildSrc/src/test/kotlin/io/spine/gradle/publish/ProtoExtsSpec.kt new file mode 100644 index 00000000000..3859d28ab36 --- /dev/null +++ b/buildSrc/src/test/kotlin/io/spine/gradle/publish/ProtoExtsSpec.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2026 CodeMatters, Lda. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +package io.spine.gradle.publish + +import io.kotest.matchers.collections.shouldBeEmpty +import io.kotest.matchers.collections.shouldContainExactly +import io.kotest.matchers.shouldBe +import org.gradle.api.Project +import org.gradle.api.plugins.BasePlugin +import org.gradle.api.tasks.bundling.Jar +import org.gradle.testfixtures.ProjectBuilder +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +@DisplayName("Proto extensions should") +internal class ProtoExtsSpec { + + private val project: Project = ProjectBuilder.builder().build().also { + it.pluginManager.apply(BasePlugin::class.java) + } + + private val jar: Jar = project.tasks.register("protoJar", Jar::class.java).get() + + @Test + fun `make a JAR depend on generateProto registered later, without creating it`() { + jar.dependOnGenerateProto() + var created = false + project.tasks.register("generateProto") { created = true } + + created shouldBe false + jar.dependencyNames() shouldContainExactly listOf("generateProto") + } + + @Test + fun `add no dependency to a JAR in a project without generateProto`() { + jar.dependOnGenerateProto() + + jar.dependencyNames().shouldBeEmpty() + } + + private fun Jar.dependencyNames(): List = + taskDependencies.getDependencies(this).map { it.name } +} diff --git a/buildSrc/src/test/kotlin/io/spine/gradle/publish/PublicationSbomIgTest.kt b/buildSrc/src/test/kotlin/io/spine/gradle/publish/PublicationSbomIgTest.kt index d64306ebb67..60e0ff7445d 100644 --- a/buildSrc/src/test/kotlin/io/spine/gradle/publish/PublicationSbomIgTest.kt +++ b/buildSrc/src/test/kotlin/io/spine/gradle/publish/PublicationSbomIgTest.kt @@ -30,6 +30,7 @@ import io.kotest.matchers.file.shouldNotExist import io.kotest.matchers.nulls.shouldBeNull import io.kotest.matchers.nulls.shouldNotBeNull import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldContain import io.kotest.matchers.string.shouldNotContain import java.io.File import java.util.jar.JarOutputStream @@ -199,6 +200,73 @@ internal class PublicationSbomIgTest { packagingOf("osgi", osgiBundle) shouldBe "bundle" } + /** + * Until the packaging of a POM is set, reading it creates the tasks that produce + * the artifacts of the publication. A standard publication sets its packaging + * as it is created, so a build that does not publish creates none of them. + * + * The init script reports the tasks created before the task graph is ready, which + * excludes those that the fixture creates later, to report the SBOMs. + */ + @Test + fun `create no archive task of a standard publication in a build that does not publish`() { + val initScript = file("report-created-tasks.gradle") + initScript.writeText( + """ + def graphReady = false + gradle.taskGraph.whenReady { graphReady = true } + allprojects { + tasks.configureEach { task -> + if (!graphReady) println("$createdPrefix${'$'}{task.path}") + } + } + """.trimIndent() + ) + val result = GradleRunner.create() + .withProjectDir(projectDir) + .withArguments("help", "--init-script", initScript.path, "--offline", "--stacktrace") + .build() + + val created = result.output.lines() + .filter { it.startsWith(createdPrefix) } + .map { it.removePrefix(createdPrefix) } + created shouldContain ":help" + created shouldNotContainAnyOf listOf("api", "impl").flatMap { module -> + listOf("jar", "sourcesJar", "javadocJar", "htmlDocsJar").map { ":$module:$it" } + } + } + + /** + * A standard publication declares the `jar` packaging before its artifacts are known, + * so writing its POM fails if the build gives the `jar` task a classifier, + * rather than publish a POM that names no main artifact. + * + * The case checks `StandardJavaPublicationHandler`, but shares the fixture of this + * class, whose `api` module is a standard publication with an SBOM. + * + * The build of the fixture has written the POM of `api` already. So the case also shows + * that the result of the check is an input of the task: without it, the task would be + * up to date, and skip the check. + */ + @Test + fun `fail to write the POM of a standard publication whose JAR is classified`() { + val script = file("api/build.gradle.kts") + val original = script.readText() + script.appendText("\ntasks.jar { archiveClassifier.set(\"plain\") }\n") + try { + val result = GradleRunner.create() + .withProjectDir(projectDir) + .withArguments(":api:${pomTaskOf("mavenJava")}", "--offline", "--stacktrace") + .buildAndFail() + + result.output shouldContain + "The POM of the `mavenJava` publication of `:api` declares the `jar`" + + " packaging, but the publication has no main artifact." + } finally { + script.writeText(original) + } + } + @Test fun `describe runtime dependencies only`() { val names = implSbom().packageNames() @@ -908,6 +976,9 @@ internal class PublicationSbomIgTest { /** Marks the lines in which the fixture reports the SBOMs of each publication. */ const val reportPrefix = "SBOM-REPORT " + /** Marks the lines in which an init script reports the tasks created. */ + const val createdPrefix = "CREATED-TASK " + /** The publication of a thin JAR with a hand-written POM. */ const val pluginJar = "pluginJar" diff --git a/buildSrc/src/test/kotlin/io/spine/gradle/report/license/DependencyReportOutputTest.kt b/buildSrc/src/test/kotlin/io/spine/gradle/report/license/DependencyReportOutputTest.kt index a6213ab9127..96f756d75df 100644 --- a/buildSrc/src/test/kotlin/io/spine/gradle/report/license/DependencyReportOutputTest.kt +++ b/buildSrc/src/test/kotlin/io/spine/gradle/report/license/DependencyReportOutputTest.kt @@ -74,6 +74,29 @@ class DependencyReportOutputTest { reportFile.readText() shouldBe "license report" } + /** + * In a build of one project, the report to merge is that of the root project, whose + * task is in the same task container as the merging task. + */ + @Test + fun `merge the license report of a single-project build`() { + project.pluginManager.apply(BasePlugin::class.java) + LicenseReporter.generateReportIn(project) + val sourceReport = project.layout.buildDirectory.asFile.get() + .resolve(Paths.relativePath) + .resolve(Paths.outputFilename) + sourceReport.parentFile.mkdirs() + sourceReport.writeText("license report") + + LicenseReporter.mergeAllReports(project) + + project.tasks.named("mergeAllLicenseReports").get() + .executeActions() + + val reportFile = projectDir.resolve("docs/dependencies/dependencies.md") + reportFile.readText() shouldBe "license report" + } + private fun subproject(name: String): Project { val subprojectDir = projectDir.resolve(name) subprojectDir.mkdirs() diff --git a/config b/config index ad3dff0890c..ba47b5fb7a9 160000 --- a/config +++ b/config @@ -1 +1 @@ -Subproject commit ad3dff0890c7716cbba3fe2ce17bf4fe5905a318 +Subproject commit ba47b5fb7a92ad1e5399c8714edd2160e0265a82 diff --git a/docs/dependencies/dependencies.md b/docs/dependencies/dependencies.md index f3b1f99ca8e..e7fe4cd0634 100644 --- a/docs/dependencies/dependencies.md +++ b/docs/dependencies/dependencies.md @@ -1,6 +1,6 @@ -# Dependencies of `io.spine.tools:compiler-api:2.0.0-SNAPSHOT.081` +# Dependencies of `io.spine.tools:compiler-api:2.0.0-SNAPSHOT.082` ## Runtime 1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3. @@ -560,7 +560,7 @@ * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-bom](https://github.com/FasterXML/jackson-bom) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -587,7 +587,7 @@ * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-core](https://github.com/FasterXML/jackson-core) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -605,7 +605,7 @@ * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -617,19 +617,19 @@ * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-yaml. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-yaml. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-dataformats-text](https://github.com/FasterXML/jackson-dataformats-text) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-guava. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-guava. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-datatypes-collections](https://github.com/FasterXML/jackson-datatypes-collections) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-jdk8. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-jdk8. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8](https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8) * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -653,7 +653,7 @@ * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-parameter-names. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-parameter-names. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names](https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names) * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -720,7 +720,7 @@ * **Project URL:** [https://github.com/google/gson](https://github.com/google/gson) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : com.google.devtools.ksp.gradle.plugin. **Version** : 2.3.11. +1. **Group** : com.google.devtools.ksp. **Name** : com.google.devtools.ksp.gradle.plugin. **Version** : 2.3.12. * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -817,31 +817,16 @@ * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 4.36.0. - * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - 1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 4.36.0. - * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - 1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 4.36.0. - * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - 1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) @@ -888,11 +873,11 @@ * **License:** [http://www.debian.org/misc/bsd.license](http://www.debian.org/misc/bsd.license) * **License:** [BSD](http://www.debian.org/misc/bsd.license) -1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format. **Version** : 2.97.0. +1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format. **Version** : 2.102.0. * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) -1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format-spi. **Version** : 2.97.0. +1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format-spi. **Version** : 2.102.0. * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) @@ -1123,18 +1108,10 @@ * **Project URL:** [https://detekt.dev](https://detekt.dev) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -1143,10 +1120,6 @@ * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -1159,10 +1132,6 @@ * **Project URL:** [https://github.com/grpc/grpc-kotlin](https://github.com/grpc/grpc-kotlin) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -1171,10 +1140,6 @@ * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -1463,10 +1428,6 @@ * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-bom. **Version** : 2.4.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-build-tools-api. **Version** : 2.4.10. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -1587,10 +1548,6 @@ * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-test. **Version** : 2.4.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-test-junit5. **Version** : 2.4.10. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -1860,14 +1817,14 @@ The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using +This report was generated on **Fri Oct 30 16:39:00 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:compiler-api-tests:2.0.0-SNAPSHOT.081` +# Dependencies of `io.spine.tools:compiler-api-tests:2.0.0-SNAPSHOT.082` ## Runtime ## Compile, tests, and tooling @@ -2136,18 +2093,10 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** Indiana University Extreme! Lab Software License * **License:** [Indiana University Extreme! Lab Software License](https://raw.githubusercontent.com/x-stream/mxparser/master/LICENSE.txt) -1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -2156,10 +2105,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -2172,10 +2117,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/grpc/grpc-kotlin](https://github.com/grpc/grpc-kotlin) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -2184,10 +2125,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -2578,14 +2515,14 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using +This report was generated on **Fri Oct 30 16:39:00 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:compiler-backend:2.0.0-SNAPSHOT.081` +# Dependencies of `io.spine.tools:compiler-backend:2.0.0-SNAPSHOT.082` ## Runtime 1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3. @@ -3149,7 +3086,7 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-bom](https://github.com/FasterXML/jackson-bom) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -3176,7 +3113,7 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-core](https://github.com/FasterXML/jackson-core) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -3194,7 +3131,7 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -3206,19 +3143,19 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-yaml. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-yaml. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-dataformats-text](https://github.com/FasterXML/jackson-dataformats-text) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-guava. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-guava. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-datatypes-collections](https://github.com/FasterXML/jackson-datatypes-collections) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-jdk8. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-jdk8. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8](https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8) * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -3242,7 +3179,7 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-parameter-names. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-parameter-names. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names](https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names) * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -3309,7 +3246,7 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/google/gson](https://github.com/google/gson) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : com.google.devtools.ksp.gradle.plugin. **Version** : 2.3.11. +1. **Group** : com.google.devtools.ksp. **Name** : com.google.devtools.ksp.gradle.plugin. **Version** : 2.3.12. * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -3406,31 +3343,16 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 4.36.0. - * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - 1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 4.36.0. - * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - 1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 4.36.0. - * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - 1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) @@ -3477,11 +3399,11 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [http://www.debian.org/misc/bsd.license](http://www.debian.org/misc/bsd.license) * **License:** [BSD](http://www.debian.org/misc/bsd.license) -1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format. **Version** : 2.97.0. +1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format. **Version** : 2.102.0. * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) -1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format-spi. **Version** : 2.97.0. +1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format-spi. **Version** : 2.102.0. * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) @@ -3712,18 +3634,10 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://detekt.dev](https://detekt.dev) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -3732,10 +3646,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -3748,10 +3658,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/grpc/grpc-kotlin](https://github.com/grpc/grpc-kotlin) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -3760,10 +3666,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -4052,10 +3954,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-bom. **Version** : 2.4.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-build-tools-api. **Version** : 2.4.10. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -4176,10 +4074,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-test. **Version** : 2.4.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-test-junit5. **Version** : 2.4.10. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -4449,14 +4343,14 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using +This report was generated on **Fri Oct 30 16:39:00 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:compiler-cli:2.0.0-SNAPSHOT.081` +# Dependencies of `io.spine.tools:compiler-cli:2.0.0-SNAPSHOT.082` ## Runtime 1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3. @@ -5124,11 +5018,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-bom](https://github.com/FasterXML/jackson-bom) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-bom](https://github.com/FasterXML/jackson-bom) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -5150,12 +5039,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-core](https://github.com/FasterXML/jackson-core) - * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-core](https://github.com/FasterXML/jackson-core) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -5168,12 +5051,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) - * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -5186,30 +5063,18 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-yaml. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-yaml. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-dataformats-text](https://github.com/FasterXML/jackson-dataformats-text) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-guava. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-datatypes-collections](https://github.com/FasterXML/jackson-datatypes-collections) - * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-guava. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-datatypes-collections](https://github.com/FasterXML/jackson-datatypes-collections) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-jdk8. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8](https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8) - * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-jdk8. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8](https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8) * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -5234,12 +5099,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-parameter-names. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names](https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names) - * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-parameter-names. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names](https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names) * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -5375,7 +5234,7 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/google/gson](https://github.com/google/gson) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : com.google.devtools.ksp.gradle.plugin. **Version** : 2.3.11. +1. **Group** : com.google.devtools.ksp. **Name** : com.google.devtools.ksp.gradle.plugin. **Version** : 2.3.12. * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -5472,31 +5331,16 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 4.36.0. - * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - 1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 4.36.0. - * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - 1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 4.36.0. - * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - 1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) @@ -5547,18 +5391,10 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) -1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format. **Version** : 2.97.0. - * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) - * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) - 1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format-spi. **Version** : 2.102.0. * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) -1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format-spi. **Version** : 2.97.0. - * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) - * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) - 1. **Group** : com.sksamuel.aedile. **Name** : aedile-core. **Version** : 3.0.4. * **Project URL:** [https://www.github.com/sksamuel/aedile](https://www.github.com/sksamuel/aedile) * **License:** [The Apache 2.0 License](https://opensource.org/licenses/Apache-2.0) @@ -5786,18 +5622,10 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://detekt.dev](https://detekt.dev) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -5806,10 +5634,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -5822,10 +5646,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/grpc/grpc-kotlin](https://github.com/grpc/grpc-kotlin) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -5834,10 +5654,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -6131,10 +5947,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-bom. **Version** : 2.4.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-build-tools-api. **Version** : 2.4.10. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -6255,10 +6067,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-test. **Version** : 2.4.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-test-junit5. **Version** : 2.4.10. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -6528,14 +6336,14 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using +This report was generated on **Fri Oct 30 16:39:00 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:compiler-gradle-api:2.0.0-SNAPSHOT.081` +# Dependencies of `io.spine.tools:compiler-gradle-api:2.0.0-SNAPSHOT.082` ## Runtime 1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3. @@ -7549,18 +7357,10 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://detekt.dev](https://detekt.dev) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -7569,10 +7369,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -7585,10 +7381,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/grpc/grpc-kotlin](https://github.com/grpc/grpc-kotlin) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -7597,10 +7389,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -8277,14 +8065,14 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using +This report was generated on **Fri Oct 30 16:39:00 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:compiler-gradle-plugin:2.0.0-SNAPSHOT.081` +# Dependencies of `io.spine.tools:compiler-gradle-plugin:2.0.0-SNAPSHOT.082` ## Runtime 1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3. @@ -8367,6 +8155,10 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://errorprone.info/error_prone_annotations](https://errorprone.info/error_prone_annotations) * **License:** [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) +1. **Group** : com.google.gradle. **Name** : osdetector-gradle-plugin. **Version** : 1.7.3. + * **Project URL:** [https://github.com/google/osdetector-gradle-plugin](https://github.com/google/osdetector-gradle-plugin) + * **License:** [Apache License 2.0](http://opensource.org/licenses/Apache-2.0) + 1. **Group** : com.google.guava. **Name** : failureaccess. **Version** : 1.0.3. * **Project URL:** [https://github.com/google/guava/](https://github.com/google/guava/) * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -8384,6 +8176,10 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/google/j2objc/](https://github.com/google/j2objc/) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) +1. **Group** : com.google.protobuf. **Name** : protobuf-gradle-plugin. **Version** : 0.10.0. + * **Project URL:** [https://github.com/google/protobuf-gradle-plugin](https://github.com/google/protobuf-gradle-plugin) + * **License:** [BSD 3-Clause](http://opensource.org/licenses/BSD-3-Clause) + 1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) @@ -8611,6 +8407,10 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [https://raw.githubusercontent.com/jaxen-xpath/jaxen/master/LICENSE.txt](https://raw.githubusercontent.com/jaxen-xpath/jaxen/master/LICENSE.txt) * **License:** [BSD License 2.0](https://raw.githubusercontent.com/jaxen-xpath/jaxen/master/LICENSE.txt) +1. **Group** : kr.motd.maven. **Name** : os-maven-plugin. **Version** : 1.7.1. + * **Project URL:** [https://github.com/trustin/os-maven-plugin/](https://github.com/trustin/os-maven-plugin/) + * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + 1. **Group** : net.bytebuddy. **Name** : byte-buddy-agent. **Version** : 1.10.1. * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -9302,18 +9102,10 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://detekt.dev](https://detekt.dev) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -9322,10 +9114,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -9338,10 +9126,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/grpc/grpc-kotlin](https://github.com/grpc/grpc-kotlin) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -9350,10 +9134,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -10066,14 +9846,14 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using +This report was generated on **Fri Oct 30 16:39:00 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:compiler-jvm:2.0.0-SNAPSHOT.081` +# Dependencies of `io.spine.tools:compiler-jvm:2.0.0-SNAPSHOT.082` ## Runtime 1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3. @@ -10672,11 +10452,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-bom](https://github.com/FasterXML/jackson-bom) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-bom](https://github.com/FasterXML/jackson-bom) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -10698,12 +10473,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-core](https://github.com/FasterXML/jackson-core) - * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-core](https://github.com/FasterXML/jackson-core) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -10716,12 +10485,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) - * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) @@ -10734,30 +10497,18 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-yaml. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-yaml. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-dataformats-text](https://github.com/FasterXML/jackson-dataformats-text) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-guava. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-datatypes-collections](https://github.com/FasterXML/jackson-datatypes-collections) - * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-guava. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-datatypes-collections](https://github.com/FasterXML/jackson-datatypes-collections) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-jdk8. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8](https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8) - * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-jdk8. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8](https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8) * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -10782,12 +10533,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-parameter-names. **Version** : 2.22.2. - * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names](https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names) - * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-parameter-names. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names](https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names) * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -10855,7 +10600,7 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/google/gson](https://github.com/google/gson) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : com.google.devtools.ksp.gradle.plugin. **Version** : 2.3.11. +1. **Group** : com.google.devtools.ksp. **Name** : com.google.devtools.ksp.gradle.plugin. **Version** : 2.3.12. * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -10952,31 +10697,16 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 4.36.0. - * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - 1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 4.36.0. - * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - 1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 4.36.0. - * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - 1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) @@ -11027,18 +10757,10 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) -1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format. **Version** : 2.97.0. - * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) - * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) - 1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format-spi. **Version** : 2.102.0. * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) -1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format-spi. **Version** : 2.97.0. - * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) - * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) - 1. **Group** : com.sksamuel.aedile. **Name** : aedile-core. **Version** : 3.0.4. * **Project URL:** [https://www.github.com/sksamuel/aedile](https://www.github.com/sksamuel/aedile) * **License:** [The Apache 2.0 License](https://opensource.org/licenses/Apache-2.0) @@ -11266,18 +10988,10 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://detekt.dev](https://detekt.dev) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -11286,10 +11000,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -11302,10 +11012,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/grpc/grpc-kotlin](https://github.com/grpc/grpc-kotlin) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -11314,10 +11020,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -11606,10 +11308,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-bom. **Version** : 2.4.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-build-tools-api. **Version** : 2.4.10. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -11730,10 +11428,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-test. **Version** : 2.4.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-test-junit5. **Version** : 2.4.10. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -12003,14 +11697,14 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using +This report was generated on **Fri Oct 30 16:39:00 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:compiler-params:2.0.0-SNAPSHOT.081` +# Dependencies of `io.spine.tools:compiler-params:2.0.0-SNAPSHOT.082` ## Runtime 1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3. @@ -12570,7 +12264,7 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-bom](https://github.com/FasterXML/jackson-bom) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -12597,7 +12291,7 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-core](https://github.com/FasterXML/jackson-core) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -12615,7 +12309,7 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -12627,19 +12321,19 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-yaml. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-yaml. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-dataformats-text](https://github.com/FasterXML/jackson-dataformats-text) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-guava. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-guava. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-datatypes-collections](https://github.com/FasterXML/jackson-datatypes-collections) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-jdk8. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-jdk8. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8](https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8) * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -12663,7 +12357,7 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-parameter-names. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-parameter-names. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names](https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names) * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -12730,7 +12424,7 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/google/gson](https://github.com/google/gson) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : com.google.devtools.ksp.gradle.plugin. **Version** : 2.3.11. +1. **Group** : com.google.devtools.ksp. **Name** : com.google.devtools.ksp.gradle.plugin. **Version** : 2.3.12. * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -12827,31 +12521,16 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 4.36.0. - * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - 1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 4.36.0. - * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - 1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 4.36.0. - * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - 1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) @@ -12898,11 +12577,11 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [http://www.debian.org/misc/bsd.license](http://www.debian.org/misc/bsd.license) * **License:** [BSD](http://www.debian.org/misc/bsd.license) -1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format. **Version** : 2.97.0. +1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format. **Version** : 2.102.0. * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) -1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format-spi. **Version** : 2.97.0. +1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format-spi. **Version** : 2.102.0. * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) @@ -13133,18 +12812,10 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://detekt.dev](https://detekt.dev) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -13153,10 +12824,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -13169,10 +12836,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/grpc/grpc-kotlin](https://github.com/grpc/grpc-kotlin) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -13181,10 +12844,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -13465,10 +13124,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-bom. **Version** : 2.4.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-build-tools-api. **Version** : 2.4.10. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -13589,10 +13244,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-test. **Version** : 2.4.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-test-junit5. **Version** : 2.4.10. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -13862,14 +13513,14 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using +This report was generated on **Fri Oct 30 16:39:00 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:compiler-protoc-plugin:2.0.0-SNAPSHOT.081` +# Dependencies of `io.spine.tools:compiler-protoc-plugin:2.0.0-SNAPSHOT.082` ## Runtime 1. **Group** : com.google.code.findbugs. **Name** : jsr305. **Version** : 3.0.2. @@ -14270,18 +13921,10 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://detekt.dev](https://detekt.dev) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -14290,10 +13933,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -14306,10 +13945,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/grpc/grpc-kotlin](https://github.com/grpc/grpc-kotlin) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -14318,10 +13953,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -14764,14 +14395,14 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using +This report was generated on **Fri Oct 30 16:39:00 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:compiler-test-env:2.0.0-SNAPSHOT.081` +# Dependencies of `io.spine.tools:compiler-test-env:2.0.0-SNAPSHOT.082` ## Runtime 1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3. @@ -15335,7 +14966,7 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-bom](https://github.com/FasterXML/jackson-bom) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -15362,7 +14993,7 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-core](https://github.com/FasterXML/jackson-core) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -15380,7 +15011,7 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -15392,19 +15023,19 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-yaml. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-yaml. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-dataformats-text](https://github.com/FasterXML/jackson-dataformats-text) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-guava. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-guava. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-datatypes-collections](https://github.com/FasterXML/jackson-datatypes-collections) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-jdk8. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-jdk8. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8](https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8) * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -15428,7 +15059,7 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-parameter-names. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-parameter-names. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names](https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names) * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -15499,7 +15130,7 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/google/gson](https://github.com/google/gson) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : com.google.devtools.ksp.gradle.plugin. **Version** : 2.3.11. +1. **Group** : com.google.devtools.ksp. **Name** : com.google.devtools.ksp.gradle.plugin. **Version** : 2.3.12. * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -15596,31 +15227,16 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 4.36.0. - * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - 1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 4.36.0. - * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - 1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 4.36.0. - * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - 1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) @@ -15667,11 +15283,11 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [http://www.debian.org/misc/bsd.license](http://www.debian.org/misc/bsd.license) * **License:** [BSD](http://www.debian.org/misc/bsd.license) -1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format. **Version** : 2.97.0. +1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format. **Version** : 2.102.0. * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) -1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format-spi. **Version** : 2.97.0. +1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format-spi. **Version** : 2.102.0. * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) @@ -16214,10 +15830,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-bom. **Version** : 2.4.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-build-tools-api. **Version** : 2.4.10. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -16338,10 +15950,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-test. **Version** : 2.4.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-test-junit5. **Version** : 2.4.10. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -16611,14 +16219,14 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using +This report was generated on **Fri Oct 30 16:39:00 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). -# Dependencies of `io.spine.tools:compiler-testlib:2.0.0-SNAPSHOT.081` +# Dependencies of `io.spine.tools:compiler-testlib:2.0.0-SNAPSHOT.082` ## Runtime 1. **Group** : be.cyberelf.nanoxml. **Name** : nanoxml. **Version** : 2.2.3. @@ -17283,7 +16891,7 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson. **Name** : jackson-bom. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-bom](https://github.com/FasterXML/jackson-bom) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -17310,7 +16918,7 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-core. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-core](https://github.com/FasterXML/jackson-core) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -17328,7 +16936,7 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.core. **Name** : jackson-databind. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson](https://github.com/FasterXML/jackson) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -17340,19 +16948,19 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-yaml. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.dataformat. **Name** : jackson-dataformat-yaml. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-dataformats-text](https://github.com/FasterXML/jackson-dataformats-text) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-guava. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-guava. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-datatypes-collections](https://github.com/FasterXML/jackson-datatypes-collections) * **License:** [https://www.apache.org/licenses/LICENSE-2.0.txt](https://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-jdk8. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.datatype. **Name** : jackson-datatype-jdk8. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8](https://github.com/FasterXML/jackson-modules-java8/jackson-datatype-jdk8) * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -17376,7 +16984,7 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-parameter-names. **Version** : 2.22.2. +1. **Group** : com.fasterxml.jackson.module. **Name** : jackson-module-parameter-names. **Version** : 2.22.3. * **Project URL:** [https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names](https://github.com/FasterXML/jackson-modules-java8/jackson-module-parameter-names) * **License:** [http://www.apache.org/licenses/LICENSE-2.0.txt](http://www.apache.org/licenses/LICENSE-2.0.txt) * **License:** [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -17443,7 +17051,7 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/google/gson](https://github.com/google/gson) * **License:** [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : com.google.devtools.ksp. **Name** : com.google.devtools.ksp.gradle.plugin. **Version** : 2.3.11. +1. **Group** : com.google.devtools.ksp. **Name** : com.google.devtools.ksp.gradle.plugin. **Version** : 2.3.12. * **Project URL:** [https://goo.gle/ksp](https://goo.gle/ksp) * **License:** [The Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -17540,31 +17148,16 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 4.36.0. - * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - 1. **Group** : com.google.protobuf. **Name** : protobuf-java. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 4.36.0. - * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - 1. **Group** : com.google.protobuf. **Name** : protobuf-java-util. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) -1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 4.36.0. - * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) - * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - * **License:** [BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) - 1. **Group** : com.google.protobuf. **Name** : protobuf-kotlin. **Version** : 4.36.2. * **Project URL:** [https://developers.google.com/protocol-buffers/](https://developers.google.com/protocol-buffers/) * **License:** [https://opensource.org/licenses/BSD-3-Clause](https://opensource.org/licenses/BSD-3-Clause) @@ -17611,11 +17204,11 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **License:** [http://www.debian.org/misc/bsd.license](http://www.debian.org/misc/bsd.license) * **License:** [BSD](http://www.debian.org/misc/bsd.license) -1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format. **Version** : 2.97.0. +1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format. **Version** : 2.102.0. * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) -1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format-spi. **Version** : 2.97.0. +1. **Group** : com.palantir.javaformat. **Name** : palantir-java-format-spi. **Version** : 2.102.0. * **Project URL:** [https://github.com/palantir/palantir-java-format](https://github.com/palantir/palantir-java-format) * **License:** [The Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0) @@ -17846,18 +17439,10 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://detekt.dev](https://detekt.dev) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-api. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-bom. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -17866,10 +17451,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-core. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -17882,10 +17463,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/grpc/grpc-kotlin](https://github.com/grpc/grpc-kotlin) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-protobuf. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -17894,10 +17471,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) -1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.83.1. - * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) - * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) - 1. **Group** : io.grpc. **Name** : grpc-stub. **Version** : 1.85.0. * **Project URL:** [https://github.com/grpc/grpc-java](https://github.com/grpc/grpc-java) * **License:** [Apache 2.0](https://opensource.org/licenses/Apache-2.0) @@ -18186,10 +17759,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-bom. **Version** : 2.4.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [The Apache Software License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-build-tools-api. **Version** : 2.4.10. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -18310,10 +17879,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) -1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-test. **Version** : 2.4.21. - * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) - * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) - 1. **Group** : org.jetbrains.kotlin. **Name** : kotlin-test-junit5. **Version** : 2.4.10. * **Project URL:** [https://kotlinlang.org/](https://kotlinlang.org/) * **License:** [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0.txt) @@ -18583,6 +18148,6 @@ This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using The dependencies distributed under several licenses, are used according their commercial-use-friendly license. -This report was generated on **Thu Oct 22 15:15:27 WEST 2026** using +This report was generated on **Fri Oct 30 16:39:00 WET 2026** using [Gradle-License-Report plugin](https://github.com/jk1/Gradle-License-Report) by Evgeny Naumenko, licensed under [Apache 2.0 License](https://github.com/jk1/Gradle-License-Report/blob/master/LICENSE). \ No newline at end of file diff --git a/docs/dependencies/pom.xml b/docs/dependencies/pom.xml index d5ccfdcf7f6..f12af6f7a79 100644 --- a/docs/dependencies/pom.xml +++ b/docs/dependencies/pom.xml @@ -10,7 +10,7 @@ all modules and does not describe the project structure per-subproject. --> io.spine.tools compiler -2.0.0-SNAPSHOT.081 +2.0.0-SNAPSHOT.082 2015 @@ -122,25 +122,25 @@ all modules and does not describe the project structure per-subproject. io.spine spine-server - 2.0.0-SNAPSHOT.571 + 2.0.0-SNAPSHOT.577 compile io.spine spine-time - 2.0.0-SNAPSHOT.251 + 2.0.0-SNAPSHOT.253 compile io.spine spine-time-java - 2.0.0-SNAPSHOT.251 + 2.0.0-SNAPSHOT.253 compile io.spine spine-validation-jvm-runtime - 2.0.0-SNAPSHOT.464 + 2.0.0-SNAPSHOT.469 compile @@ -152,37 +152,37 @@ all modules and does not describe the project structure per-subproject. io.spine.tools code - 2.0.0-SNAPSHOT.428 + 2.0.0-SNAPSHOT.440 compile io.spine.tools fs - 2.0.0-SNAPSHOT.428 + 2.0.0-SNAPSHOT.440 compile io.spine.tools gradle-plugin-api - 2.0.0-SNAPSHOT.428 + 2.0.0-SNAPSHOT.440 compile io.spine.tools java-code - 2.0.0-SNAPSHOT.428 + 2.0.0-SNAPSHOT.440 compile io.spine.tools jvm-tools - 2.0.0-SNAPSHOT.428 + 2.0.0-SNAPSHOT.440 compile io.spine.tools kotlin-code - 2.0.0-SNAPSHOT.428 + 2.0.0-SNAPSHOT.440 compile @@ -194,19 +194,19 @@ all modules and does not describe the project structure per-subproject. io.spine.tools plugin-base - 2.0.0-SNAPSHOT.428 + 2.0.0-SNAPSHOT.440 compile io.spine.tools proto-code - 2.0.0-SNAPSHOT.428 + 2.0.0-SNAPSHOT.440 compile io.spine.tools protobuf-setup-plugins - 2.0.0-SNAPSHOT.428 + 2.0.0-SNAPSHOT.440 compile @@ -218,19 +218,19 @@ all modules and does not describe the project structure per-subproject. io.spine.tools psi-java - 2.0.0-SNAPSHOT.428 + 2.0.0-SNAPSHOT.440 compile io.spine.tools server-testlib - 2.0.0-SNAPSHOT.571 + 2.0.0-SNAPSHOT.577 compile org.jetbrains.kotlin kotlin-bom - 2.4.21 + 2.4.10 compile @@ -308,13 +308,13 @@ all modules and does not describe the project structure per-subproject. io.spine.tools plugin-testlib - 2.0.0-SNAPSHOT.428 + 2.0.0-SNAPSHOT.440 test io.spine.tools time-testlib - 2.0.0-SNAPSHOT.251 + 2.0.0-SNAPSHOT.253 test @@ -405,27 +405,27 @@ all modules and does not describe the project structure per-subproject. io.spine.tools compiler-cli-all - 2.0.0-SNAPSHOT.069 + 2.0.0-SNAPSHOT.081 io.spine.tools compiler-protoc-plugin - 2.0.0-SNAPSHOT.069 + 2.0.0-SNAPSHOT.081 io.spine.tools core-jvm-gradle-plugin - 2.0.0-SNAPSHOT.097 + 2.0.0-SNAPSHOT.101 io.spine.tools core-jvm-plugins - 2.0.0-SNAPSHOT.097 + 2.0.0-SNAPSHOT.101 io.spine.tools dokka-extensions - 2.0.0-SNAPSHOT.10 + 2.0.0-SNAPSHOT.12 io.spine.tools @@ -435,12 +435,12 @@ all modules and does not describe the project structure per-subproject. io.spine.tools time-validation - 2.0.0-SNAPSHOT.251 + 2.0.0-SNAPSHOT.253 io.spine.tools validation-java-bundle - 2.0.0-SNAPSHOT.464 + 2.0.0-SNAPSHOT.469 org.jacoco diff --git a/gradle-api/src/main/kotlin/io/spine/tools/compiler/gradle/api/TaskLocator.kt b/gradle-api/src/main/kotlin/io/spine/tools/compiler/gradle/api/TaskLocator.kt index 91927ee2c3d..f0a61a96bd5 100644 --- a/gradle-api/src/main/kotlin/io/spine/tools/compiler/gradle/api/TaskLocator.kt +++ b/gradle-api/src/main/kotlin/io/spine/tools/compiler/gradle/api/TaskLocator.kt @@ -1,27 +1,15 @@ /* - * Copyright 2026, TeamDev. All rights reserved. + * Copyright 2026 CodeMatters, Lda. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * - * Redistribution and use in source and/or binary forms, with or without - * modification, must retain the above copyright notice and the following - * disclaimer. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. */ package io.spine.tools.compiler.gradle.api @@ -29,10 +17,14 @@ package io.spine.tools.compiler.gradle.api import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.tasks.SourceSet +import org.gradle.api.tasks.TaskProvider /** * Abstract base for utilities finding tasks of a certain type by * the [provided name pattern][nameFor]. + * + * The [exists] and [named] functions do not make Gradle create a task that is registered + * but not yet created. Prefer them to [find] and [get] during the configuration phase. */ public abstract class TaskLocator { @@ -43,6 +35,11 @@ public abstract class TaskLocator { /** * Obtains an instance of the task in the given project for the specified source set. + * + * Creates the task if it is registered but not yet created. + * + * @throws org.gradle.api.UnknownTaskException if there is no task for this source set + * @see named */ public fun get(project: Project, sourceSet: SourceSet): Task { val name = nameFor(sourceSet) @@ -52,10 +49,33 @@ public abstract class TaskLocator { /** * Obtains an instance of the task in the given project for the specified source set. * - * @return the task or `null` if there is no task created for this source set + * Creates the task if it is registered but not yet created. + * + * @return the task or `null` if there is no task for this source set + * @see exists */ public fun find(project: Project, sourceSet: SourceSet): Task? { val name = nameFor(sourceSet) return project.tasks.findByName(name) } + + /** + * Tells if the given project has the task for the specified source set. + * + * Unlike [find], this function does not create the task if it is registered + * but not yet created. + */ + public fun exists(project: Project, sourceSet: SourceSet): Boolean = + nameFor(sourceSet) in project.tasks.names + + /** + * Obtains a provider of the task in the given project for the specified source set. + * + * Unlike [get], this function does not create the task if it is registered + * but not yet created. + * + * @throws org.gradle.api.UnknownTaskException if there is no task for this source set + */ + public fun named(project: Project, sourceSet: SourceSet): TaskProvider = + project.tasks.named(nameFor(sourceSet)) } diff --git a/gradle-api/src/test/kotlin/io/spine/tools/compiler/gradle/api/TaskLocatorSpec.kt b/gradle-api/src/test/kotlin/io/spine/tools/compiler/gradle/api/TaskLocatorSpec.kt index 41c95b930b3..4a65e0a5333 100644 --- a/gradle-api/src/test/kotlin/io/spine/tools/compiler/gradle/api/TaskLocatorSpec.kt +++ b/gradle-api/src/test/kotlin/io/spine/tools/compiler/gradle/api/TaskLocatorSpec.kt @@ -1,33 +1,23 @@ /* - * Copyright 2026, TeamDev. All rights reserved. + * Copyright 2026 CodeMatters, Lda. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * - * Redistribution and use in source and/or binary forms, with or without - * modification, must retain the above copyright notice and the following - * disclaimer. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. */ package io.spine.tools.compiler.gradle.api import io.kotest.assertions.throwables.shouldThrow +import io.kotest.matchers.collections.shouldBeEmpty import io.kotest.matchers.nulls.shouldBeNull +import io.kotest.matchers.shouldBe import io.kotest.matchers.types.shouldBeSameInstanceAs import io.spine.tools.gradle.project.sourceSets import org.gradle.api.Project @@ -58,6 +48,14 @@ internal class TaskLocatorSpec { private val launchTask = project.tasks.register(CompilerTask.nameFor(main)) private val cleanTask = project.tasks.register(SpineCompilerCleanTask.nameFor(main)) + /** + * The names of the [launchTask] and the [cleanTask], recorded when Gradle creates them. + */ + private val created = mutableListOf().also { names -> + project.tasks.named { it == launchTask.name || it == cleanTask.name } + .configureEach { names.add(it.name) } + } + @Test fun `get the launch task via 'CompilerTask'`() { CompilerTask.get(project, main) shouldBeSameInstanceAs launchTask.get() @@ -95,4 +93,30 @@ internal class TaskLocatorSpec { shouldThrow { CompilerTask.get(project, test) } shouldThrow { SpineCompilerCleanTask.get(project, test) } } + + @Test + fun `tell if a task exists without creating it`() { + CompilerTask.exists(project, main) shouldBe true + SpineCompilerCleanTask.exists(project, main) shouldBe true + CompilerTask.exists(project, test) shouldBe false + SpineCompilerCleanTask.exists(project, test) shouldBe false + + created.shouldBeEmpty() + } + + @Test + fun `provide a task without creating it`() { + val namedLaunchTask = CompilerTask.named(project, main) + val namedCleanTask = SpineCompilerCleanTask.named(project, main) + + created.shouldBeEmpty() + namedLaunchTask.get() shouldBeSameInstanceAs launchTask.get() + namedCleanTask.get() shouldBeSameInstanceAs cleanTask.get() + } + + @Test + fun `fail to provide a task for a source set that does not have one`() { + shouldThrow { CompilerTask.named(project, test) } + shouldThrow { SpineCompilerCleanTask.named(project, test) } + } } diff --git a/gradle-plugin/src/main/kotlin/io/spine/tools/compiler/gradle/plugin/LaunchSpineCompiler.kt b/gradle-plugin/src/main/kotlin/io/spine/tools/compiler/gradle/plugin/LaunchSpineCompiler.kt index 6724b033a42..19ed3222ba7 100644 --- a/gradle-plugin/src/main/kotlin/io/spine/tools/compiler/gradle/plugin/LaunchSpineCompiler.kt +++ b/gradle-plugin/src/main/kotlin/io/spine/tools/compiler/gradle/plugin/LaunchSpineCompiler.kt @@ -28,8 +28,7 @@ import io.spine.tools.compiler.gradle.api.info import io.spine.tools.compiler.params.ParametersFileParam import io.spine.tools.compiler.params.WorkingDirectory import io.spine.tools.compiler.params.pipelineParameters -import io.spine.tools.gradle.project.findJavaCompileFor -import io.spine.tools.gradle.project.findKotlinCompileFor +import io.spine.tools.protobuf.gradle.ProtobufTaskName import io.spine.tools.protobuf.gradle.containsProtoFiles import java.io.File import java.io.File.pathSeparator @@ -55,6 +54,8 @@ import org.gradle.api.tasks.OutputDirectories import org.gradle.api.tasks.PathSensitive import org.gradle.api.tasks.PathSensitivity import org.gradle.api.tasks.SourceSet +import org.gradle.api.tasks.TaskProvider +import org.gradle.kotlin.dsl.named import org.gradle.process.CommandLineArgumentProvider /** @@ -306,17 +307,19 @@ internal fun LaunchSpineCompiler.applyDefaults(sourceSet: SourceSet) { } /** - * Arranges the dependencies of this task that can be resolved by the time the - * Compiler plugin is applied. + * Arranges the dependencies of this task. * * Makes this task depend on the build of the configurations used to run the Compiler, - * and makes the Java and Kotlin compilation tasks depend on this task. + * and on the [GenerateProtoTask] of the given [sourceSet], if the project has one. * - * The dependency of the Kotlin Symbol Processing (KSP) task is arranged separately - * after the project is evaluated because the KSP task is not yet registered by the - * time the Compiler plugin is applied. + * The [GenerateProtoTask] is registered by the time Gradle creates this task: + * the Protobuf Gradle Plugin, which must be applied before the Compiler plugin, + * registers it once the source set is added, before the Compiler plugin registers + * this task. * - * @see [Project.arrangeKspTaskDependency][io.spine.tools.compiler.gradle.plugin.arrangeKspTaskDependency] + * The plugin makes other tasks depend on this task right after registering it. + * + * @see [Project.makeCodeTasksDependOn][io.spine.tools.compiler.gradle.plugin.makeCodeTasksDependOn] */ private fun LaunchSpineCompiler.setDependencies(sourceSet: SourceSet) { val project = project @@ -324,9 +327,10 @@ private fun LaunchSpineCompiler.setDependencies(sourceSet: SourceSet) { project.compilerRawArtifact.buildDependencies, project.userClasspath.buildDependencies, ) - val launchTask = this - project.findJavaCompileFor(sourceSet)?.dependsOn(launchTask) - project.findKotlinCompileFor(sourceSet)?.dependsOn(launchTask) + val generateProtoName = ProtobufTaskName.generateProto(SourceSetName(sourceSet.name)).value() + if (generateProtoName in project.tasks.names) { + consumeProtoFrom(project.tasks.named(generateProtoName)) + } } /** @@ -338,10 +342,12 @@ private fun LaunchSpineCompiler.setDependencies(sourceSet: SourceSet) { * at configuration time, so that the parameters file can be written without * querying `Task.dependsOn` at execution time. Such a query is deprecated, * fails in Gradle 10, and is incompatible with the configuration cache. + * + * The call does not make Gradle create the [generateProto] task. */ -internal fun LaunchSpineCompiler.consumeProtoFrom(generateProto: GenerateProtoTask) { +private fun LaunchSpineCompiler.consumeProtoFrom(generateProto: TaskProvider) { dependsOn(generateProto) - protoSourceDirs.from(generateProto.sourceDirs) + protoSourceDirs.from(generateProto.map { it.sourceDirs }) } /** @@ -350,8 +356,7 @@ internal fun LaunchSpineCompiler.consumeProtoFrom(generateProto: GenerateProtoTa * The function obtains the list of compiled proto files from * [protoSourceDirs][LaunchSpineCompiler.protoSourceDirs] populated from the * [GenerateProtoTask] on which the receiver task depends (as arranged by - * [consumeProtoFrom] called from the `handleLaunchTaskDependency` - * extension in `Plugin.kt`). + * [consumeProtoFrom]). */ private fun LaunchSpineCompiler.createParametersFile() { val params = pipelineParameters { diff --git a/gradle-plugin/src/main/kotlin/io/spine/tools/compiler/gradle/plugin/Plugin.kt b/gradle-plugin/src/main/kotlin/io/spine/tools/compiler/gradle/plugin/Plugin.kt index b53ec6ea726..96fa2468e55 100644 --- a/gradle-plugin/src/main/kotlin/io/spine/tools/compiler/gradle/plugin/Plugin.kt +++ b/gradle-plugin/src/main/kotlin/io/spine/tools/compiler/gradle/plugin/Plugin.kt @@ -18,13 +18,13 @@ package io.spine.tools.compiler.gradle.plugin import com.google.common.collect.ImmutableList -import com.google.errorprone.annotations.CanIgnoreReturnValue import com.google.protobuf.gradle.GenerateProtoTask import com.google.protobuf.gradle.ProtobufExtension import io.spine.annotation.VisibleForTesting import io.spine.format.Format import io.spine.io.replaceExtension import io.spine.string.toBase64Encoded +import io.spine.tools.code.Kotlin import io.spine.tools.code.SourceSetName import io.spine.tools.compiler.gradle.api.Artifacts import io.spine.tools.compiler.gradle.api.Artifacts.compilerBackend @@ -225,56 +225,49 @@ private fun Project.requireProtobufRuntimeOf(source: Configuration, target: Conf } /** - * Creates the [SpineCompilerCleanTask] and [LaunchSpineCompiler] tasks for all source sets - * in this project available by the time of the call. + * Registers the [SpineCompilerCleanTask] and [LaunchSpineCompiler] tasks for each source set + * of this project, including the source sets added by other plugins after this call. * - * There may be cases of source sets added by other plugins after this function is invoked. - * Such cases are handled by the [handleLaunchTaskDependency] function. + * Gradle creates none of the tasks by this call. Making other tasks depend on + * the [LaunchSpineCompiler] tasks creates no tasks either. * - * The dependency of the Kotlin Symbol Processing (KSP) task on the [LaunchSpineCompiler] - * task is arranged on [Project.afterEvaluate] because the KSP task is not yet registered - * by the time this plugin is applied. - * - * @see [Project.handleLaunchTaskDependency] - * @see [Project.arrangeKspTaskDependency] + * @see [Project.makeCodeTasksDependOn] */ private fun Project.createTasks() { - sourceSets.forEach { sourceSet -> - createLaunchTask(sourceSet) - createCleanTask(sourceSet) - } - afterEvaluate { - sourceSets.forEach { sourceSet -> - arrangeKspTaskDependency(sourceSet) - } + sourceSets.configureEach { sourceSet -> + val launchTask = createLaunchTask(sourceSet) + makeCodeTasksDependOn(sourceSet, launchTask) + createCleanTask(sourceSet, launchTask) } } /** - * Makes the Kotlin Symbol Processing (KSP) task for the given [sourceSet] depend on - * the [LaunchSpineCompiler] task, so that the Compiler runs before the generated code - * is processed by KSP. - * - * Does nothing if the [LaunchSpineCompiler] task was not created for the [sourceSet], - * or if the KSP task is not present in the project. + * Makes the tasks processing the code of the given [sourceSet] depend on + * the [launchTask], so that the Compiler runs before them. * - * This function must be called after the project is evaluated because the KSP task is - * not yet registered by the time the Compiler plugin is applied. The dependencies of - * the compilation tasks are arranged earlier, when the [LaunchSpineCompiler] task is - * configured. + * These are the Java and Kotlin compilation tasks and the Kotlin Symbol Processing (KSP) task. * - * @see [LaunchSpineCompiler.setDependencies][io.spine.tools.compiler.gradle.plugin.setDependencies] + * The tasks are matched by name and configured once Gradle creates them, so this call + * creates no tasks. The match also covers the tasks registered after this call, such as + * the KSP task, which the KSP plugin registers after the Compiler plugin is applied. */ -private fun Project.arrangeKspTaskDependency(sourceSet: SourceSet) { - val launchTask = CompilerTask.find(this, sourceSet) ?: return - val kspTaskName = KspTaskName.of(sourceSet).value() - tasks.findByName(kspTaskName)?.dependsOn(launchTask) +private fun Project.makeCodeTasksDependOn( + sourceSet: SourceSet, + launchTask: TaskProvider, +) { + val dependents = setOf( + sourceSet.compileJavaTaskName, + sourceSet.getCompileTaskName(Kotlin.name), + KspTaskName.of(sourceSet).value() + ) + tasks.named { it in dependents }.configureEach { + it.dependsOn(launchTask) + } } /** * Creates [LaunchSpineCompiler] to serve the given [sourceSet]. */ -@CanIgnoreReturnValue private fun Project.createLaunchTask( sourceSet: SourceSet, ): TaskProvider { @@ -290,10 +283,13 @@ private fun Project.createLaunchTask( /** * Creates a task that deletes the files generated for the given [sourceSet]. * - * Makes the `clean` task depend on the created task, and orders the task that - * launches the Compiler CLI to run after it when both are scheduled. + * Makes the `clean` task depend on the created task, and orders the [launchTask] + * to run after it when both are scheduled. */ -private fun Project.createCleanTask(sourceSet: SourceSet) { +private fun Project.createCleanTask( + sourceSet: SourceSet, + launchTask: TaskProvider, +) { val cleanSourceSet = SpineCompilerCleanTask.nameFor(sourceSet) val spineCompilerCleanTask = tasks.register(cleanSourceSet) { group = SpineTaskGroup.name @@ -306,7 +302,7 @@ private fun Project.createCleanTask(sourceSet: SourceSet) { tasks.named("clean") { it.dependsOn(spineCompilerCleanTask) } - tasks.named(CompilerTask.nameFor(sourceSet)) { + launchTask.configure { it.mustRunAfter(spineCompilerCleanTask) } } @@ -440,9 +436,21 @@ context(_: GeneratedDirectoryContext) private fun Project.configureGenerateProtoTasks() { protobufExtension?.apply { /* The below block adds a configuration action for the `GenerateProtoTaskCollection`. - We cannot do it like `generateProtoTasks.all().forEach { ... }` because it - breaks the configuration order of the `GenerateProtoTaskCollection`. - This, in turn, leads to missing generated sources in the `compileJava` task. */ + We cannot call `generateProtoTasks.all().forEach { ... }` outside the block + because it breaks the configuration order of the `GenerateProtoTaskCollection`. + This, in turn, leads to missing generated sources in the `compileJava` task. + + Inside the block, the tasks are configured right away rather than via `configureEach`. + Configuring a task rewrites the source directories of its source set + (see `configureSourceSetDirs()`). That must happen before the Protobuf Gradle + Plugin passes the source directories to the IDEA plugin, right after running + this block. Otherwise, reading the directories makes Gradle create the task, + and the rewrite breaks the iteration over the directories with + `ConcurrentModificationException`. If the IDEA plugin is applied, the Protobuf + Gradle Plugin creates the tasks at that point anyway. + + The result of `all()` is copied so that configuring the tasks does not iterate + over a live collection. */ generateProtoTasks { val all = ImmutableList.copyOf(it.all()) all.forEach { task -> @@ -456,10 +464,12 @@ private fun Project.configureGenerateProtoTasks() { * Configures the given [task] by enabling Kotlin code generation and adding and * configuring the Compiler `protoc` plugin for the task. * - * The function also handles the exclusion of duplicated source code and task dependencies. + * The function also handles the exclusion of duplicated source code. + * + * The dependency of the [LaunchSpineCompiler] task on the given [task] is arranged + * by the [LaunchSpineCompiler] task itself. * * @see [GenerateProtoTask.configureSourceSetDirs] - * @see [Project.handleLaunchTaskDependency] */ context(_: GeneratedDirectoryContext) private fun Project.configureProtoTask(task: GenerateProtoTask) { @@ -468,7 +478,6 @@ private fun Project.configureProtoTask(task: GenerateProtoTask) { } task.addProtocPlugin() task.configureSourceSetDirs() - handleLaunchTaskDependency(task) } private fun GenerateProtoTask.addProtocPlugin() { @@ -516,35 +525,3 @@ private fun GenerateProtoTask.declareRequestFileOutputs(requestFile: File) { outputs.file(requestFile.replaceExtension(Format.ProtoJson.extension)) .withPropertyName(REQUEST_FILE_JSON_PROPERTY) } - -/** - * Makes a [LaunchSpineCompiler], if it exists for the source set of the given [GenerateProtoTask], - * depend on the given [generateProto] task and consume the proto source directories it compiles. - * - * If the [LaunchSpineCompiler] task does not exist (which may be the case for custom source sets - * created by other plugins), arranges the task creation on [Project.afterEvaluate]. - * In this case the [SpineCompilerCleanTask] is also created with appropriate dependencies. - * - * @see [consumeProtoFrom] - */ -private fun Project.handleLaunchTaskDependency(generateProto: GenerateProtoTask) { - val sourceSet = generateProto.sourceSet - val launchTask = CompilerTask.find(this, sourceSet) - check(launchTask is LaunchSpineCompiler?) { - "The task `${launchTask?.path}` is expected to be an instance of" + - " `${LaunchSpineCompiler::class.qualifiedName}`," + - " but was `${launchTask?.javaClass?.name}`." - } - if (launchTask != null) { - launchTask.consumeProtoFrom(generateProto) - } else { - afterEvaluate { - val newTask = createLaunchTask(sourceSet) - newTask.configure { - it.consumeProtoFrom(generateProto) - } - createCleanTask(sourceSet) - arrangeKspTaskDependency(sourceSet) - } - } -} diff --git a/gradle-plugin/src/test/kotlin/io/spine/tools/compiler/gradle/plugin/ExcludeProtocOutputSpec.kt b/gradle-plugin/src/test/kotlin/io/spine/tools/compiler/gradle/plugin/ExcludeProtocOutputSpec.kt index 52e77aff11d..8d2f62f06fc 100644 --- a/gradle-plugin/src/test/kotlin/io/spine/tools/compiler/gradle/plugin/ExcludeProtocOutputSpec.kt +++ b/gradle-plugin/src/test/kotlin/io/spine/tools/compiler/gradle/plugin/ExcludeProtocOutputSpec.kt @@ -1,33 +1,22 @@ /* - * Copyright 2026, TeamDev. All rights reserved. + * Copyright 2026 CodeMatters, Lda. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * - * Redistribution and use in source and/or binary forms, with or without - * modification, must retain the above copyright notice and the following - * disclaimer. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. */ package io.spine.tools.compiler.gradle.plugin import com.google.protobuf.gradle.ProtobufPlugin -import io.kotest.matchers.shouldBe +import io.kotest.matchers.collections.shouldContain +import io.kotest.matchers.collections.shouldNotContain import java.io.File import org.gradle.api.internal.project.ProjectInternal import org.gradle.api.tasks.SourceSetContainer @@ -46,10 +35,20 @@ internal class ExcludeProtocOutputSpec { * source directories of the `main` source set, and verifies that the file it * contains does not reach the `compileJava` task — see * [issue #19](https://github.com/SpineEventEngine/compiler/issues/19). + * + * The directory is added after the project is evaluated: the plugin removes it + * from the source set during the evaluation, so a directory added earlier would + * not reach the task even without the filter applied to the task itself. + * + * The paths are resolved against [Project.projectDir][org.gradle.api.Project.getProjectDir], + * which Gradle canonicalizes, rather than against the given temporary directory, + * which may be reached via a symbolic link, such as `/var` on macOS. Otherwise, the + * paths of the same file may differ, and `shouldNotContain` would pass whatever the + * task source contains. */ @Test - fun `keeping it out of the Java compile task source`(@TempDir projectDir: File) { - val project = ProjectBuilder.builder().withProjectDir(projectDir).build() + fun `keeping it out of the Java compile task source`(@TempDir tempDir: File) { + val project = ProjectBuilder.builder().withProjectDir(tempDir).build() project.group = "io.spine.stubs" with(project) { apply(plugin = "java") @@ -57,19 +56,29 @@ internal class ExcludeProtocOutputSpec { apply() repositories.mavenLocal() } + val projectDir = project.projectDir - // A file under the default `protoc` output directory, added to the source set - // as if the source-set-level deduplication had not removed it. + // A file under the default `protoc` output directory. val protocJavaDir = projectDir.resolve("build/generated/sources/proto/main/java") protocJavaDir.mkdirs() val leaked = protocJavaDir.resolve("Leaked.java") leaked.writeText("class Leaked {}") - val sourceSets = project.extensions.getByType(SourceSetContainer::class.java) - sourceSets.getByName("main").java.srcDir(protocJavaDir) + + // A hand-written file, which must reach the task. + val handWritten = projectDir.resolve("src/main/java/HandWritten.java") + handWritten.parentFile.mkdirs() + handWritten.writeText("class HandWritten {}") (project as ProjectInternal).evaluate() + // Re-add the `protoc` output directory after the plugin removed it from + // the source set during the evaluation, as another plugin may do. + val sourceSets = project.extensions.getByType(SourceSetContainer::class.java) + sourceSets.getByName("main").java.srcDir(protocJavaDir) + val compileJava = project.tasks.getByName("compileJava") as JavaCompile - compileJava.source.contains(leaked) shouldBe false + val sources = compileJava.source.files + sources shouldContain handWritten + sources shouldNotContain leaked } } diff --git a/gradle-plugin/src/test/kotlin/io/spine/tools/compiler/gradle/plugin/ProjectConfigSpec.kt b/gradle-plugin/src/test/kotlin/io/spine/tools/compiler/gradle/plugin/ProjectConfigSpec.kt index 5e57003dd94..93ddda5af50 100644 --- a/gradle-plugin/src/test/kotlin/io/spine/tools/compiler/gradle/plugin/ProjectConfigSpec.kt +++ b/gradle-plugin/src/test/kotlin/io/spine/tools/compiler/gradle/plugin/ProjectConfigSpec.kt @@ -1,27 +1,15 @@ /* - * Copyright 2025, TeamDev. All rights reserved. + * Copyright 2026 CodeMatters, Lda. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * - * Redistribution and use in source and/or binary forms, with or without - * modification, must retain the above copyright notice and the following - * disclaimer. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. */ package io.spine.tools.compiler.gradle.plugin @@ -100,7 +88,7 @@ class ProjectConfigSpec { @Test fun `bind 'launchSpineCompiler' to Java compilation`() { val task = project.tasks.getByName("compileJava") - assertThat(task.dependsOn) + assertThat(task.taskDependencies.getDependencies(task)) .comparingElementsUsing(taskNames) .contains(CompilerTaskName(SourceSetName.main).value()) } diff --git a/gradle-plugin/src/test/kotlin/io/spine/tools/compiler/gradle/plugin/TaskWiringSpec.kt b/gradle-plugin/src/test/kotlin/io/spine/tools/compiler/gradle/plugin/TaskWiringSpec.kt new file mode 100644 index 00000000000..25c6fccf94d --- /dev/null +++ b/gradle-plugin/src/test/kotlin/io/spine/tools/compiler/gradle/plugin/TaskWiringSpec.kt @@ -0,0 +1,300 @@ +/* + * Copyright 2026 CodeMatters, Lda. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +package io.spine.tools.compiler.gradle.plugin + +import com.google.protobuf.gradle.GenerateProtoTask +import com.google.protobuf.gradle.ProtobufPlugin +import io.kotest.matchers.collections.shouldContain +import io.kotest.matchers.collections.shouldContainAll +import io.kotest.matchers.collections.shouldNotContainAnyOf +import io.kotest.matchers.shouldBe +import io.spine.tools.code.SourceSetName +import io.spine.tools.compiler.gradle.api.CompilerTaskName +import io.spine.tools.compiler.gradle.api.Names.SPINE_COMPILER_PROTOC_PLUGIN +import io.spine.tools.compiler.gradle.api.SpineCompilerCleanTask +import io.spine.tools.compiler.gradle.api.builtins +import io.spine.tools.gradle.project.sourceSets +import java.io.File +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.internal.project.ProjectInternal +import org.gradle.api.tasks.TaskProvider +import org.gradle.api.tasks.compile.JavaCompile +import org.gradle.kotlin.dsl.apply +import org.gradle.testfixtures.ProjectBuilder +import org.junit.jupiter.api.BeforeEach +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.io.TempDir + +/** + * Verifies that the plugin arranges the dependencies between the tasks without making + * Gradle create them while the project is configured. + * + * Each test records the names of the tasks Gradle creates, starting before + * the plugins are applied. See [issue #98](https://github.com/SpineEventEngine/compiler/issues/98). + * + * The end-to-end counterpart, which also covers the Kotlin and Kotlin Symbol Processing (KSP) + * tasks of the real plugins, is `PluginSpec` in the `functionalTest` suite. + */ +@DisplayName("The plugin should") +internal class TaskWiringSpec { + + private lateinit var projectDir: File + private lateinit var project: Project + + /** + * The names of the tasks created by Gradle, in the order of creation. + */ + private val created = mutableListOf() + + private val launchMain = CompilerTaskName(SourceSetName.main).value() + private val launchTest = CompilerTaskName(SourceSetName.test).value() + + @BeforeEach + fun createProject(@TempDir projectDir: File) { + this.projectDir = projectDir + project = ProjectBuilder.builder() + .withProjectDir(projectDir) + .build() + project.group = "io.spine.stubs" + project.tasks.configureEach { + created.add(it.name) + } + with(project) { + apply(plugin = "java") + apply() + apply() + repositories.mavenLocal() + } + } + + private fun evaluate() = (project as ProjectInternal).evaluate() + + /** + * Obtains the names of the tasks on which the task with the given [name] depends. + * + * Creates the task. + */ + private fun dependenciesOf(name: String, project: Project = this.project): List { + val task = project.tasks.getByName(name) + return task.taskDependencies.getDependencies(task).map { it.name } + } + + /** + * Obtains the names of the tasks passed to `dependsOn()` of the task with + * the given [name], as tasks or task providers. + * + * Unlike [dependenciesOf], this function does not resolve other dependencies, + * such as the build dependencies of the configurations, which would require + * the Compiler artifacts in Maven Local. + */ + private fun declaredDependenciesOf(name: String, project: Project = this.project) = + project.tasks.getByName(name).dependsOn.mapNotNull { + when (it) { + is Task -> it.name + is TaskProvider<*> -> it.name + else -> null + } + } + + @Test + fun `not create the launch and compilation tasks at configuration time`() { + // Registered before the project is evaluated, like the KSP plugin does. + project.tasks.register("kspKotlin") + + evaluate() + + created shouldNotContainAnyOf listOf( + launchMain, + launchTest, + "compileJava", + "compileTestJava", + "kspKotlin", + ) + } + + @Test + fun `make Java compilation depend on the launch task`() { + evaluate() + + dependenciesOf("compileJava") shouldContain launchMain + dependenciesOf("compileTestJava") shouldContain launchTest + } + + @Test + fun `make Kotlin compilation depend on the launch task`() { + evaluate() + // Stubs of the tasks registered by the Kotlin Gradle Plugin. They are registered + // after the evaluation, so that the Compiler plugin does not configure the project + // for Kotlin, which this project does not have. + project.tasks.register("compileKotlin") + project.tasks.register("compileTestKotlin") + + dependenciesOf("compileKotlin") shouldContain launchMain + dependenciesOf("compileTestKotlin") shouldContain launchTest + } + + @Test + fun `make the launch task depend on the 'generateProto' task`() { + evaluate() + + declaredDependenciesOf(launchMain) shouldContain "generateProto" + declaredDependenciesOf(launchTest) shouldContain "generateTestProto" + } + + @Test + fun `pass the proto files compiled by 'generateProto' to the launch task`() { + val protoFile = projectDir.resolve("src/main/proto/given.proto").canonicalFile + protoFile.parentFile.mkdirs() + protoFile.writeText("syntax = \"proto3\";") + + evaluate() + + val launchTask = project.tasks.getByName(launchMain) as LaunchSpineCompiler + launchTask.protoSourceDirs.asFileTree.files shouldContain protoFile + } + + @Test + fun `make 'clean' depend on the clean tasks, which the launch tasks must run after`() { + evaluate() + + val cleanMain = SpineCompilerCleanTask.nameFor(project.sourceSets.getByName("main")) + val cleanTest = SpineCompilerCleanTask.nameFor(project.sourceSets.getByName("test")) + dependenciesOf("clean") shouldContainAll listOf(cleanMain, cleanTest) + val launchTask = project.tasks.getByName(launchMain) + launchTask.mustRunAfter.getDependencies(launchTask).map { it.name } shouldContain cleanMain + } + + @Test + fun `make the KSP tasks depend on the launch task, even if registered later`() { + project.tasks.register("kspKotlin") + evaluate() + // Registered after the project is evaluated, a case that an `afterEvaluate` + // callback could not handle. + project.tasks.register("kspTestKotlin") + + dependenciesOf("kspKotlin") shouldContain launchMain + dependenciesOf("kspTestKotlin") shouldContain launchTest + } + + @Test + fun `serve a source set added after the plugin is applied`() { + val sourceSet = project.sourceSets.create("integrationTest") + val launchTask = CompilerTaskName(SourceSetName(sourceSet.name)).value() + + evaluate() + + created shouldNotContainAnyOf listOf(launchTask, sourceSet.compileJavaTaskName) + project.tasks.names shouldContain launchTask + dependenciesOf("clean") shouldContain SpineCompilerCleanTask.nameFor(sourceSet) + dependenciesOf(sourceSet.compileJavaTaskName) shouldContain launchTask + declaredDependenciesOf(launchTask) shouldContain "generateIntegrationTestProto" + } + + /** + * Verifies that the configuration action the plugin adds for each [GenerateProtoTask] + * runs before the Protobuf Gradle Plugin finalizes the task, and that the code + * generated by the Compiler reaches the `compileJava` task. + * + * The action adds the Compiler `protoc` plugin and the `kotlin` built-in to the task. + * Once the task is finalized, the Protobuf Gradle Plugin rejects such changes, and + * the project fails to evaluate. The action also adds the directory with + * the code generated by the Compiler to the source set. + */ + @Test + fun `configure 'generateProto' before it is finalized`() { + // The temporary directory may be reached via a symbolic link, such as `/var` on macOS. + // The compilation sources are resolved to canonical paths. + val generatedFile = projectDir.resolve("generated/main/java/Generated.java") + .canonicalFile + generatedFile.parentFile.mkdirs() + generatedFile.writeText("class Generated {}") + + evaluate() + + val generateProto = project.tasks.getByName("generateProto") as GenerateProtoTask + generateProto.hasPlugin(SPINE_COMPILER_PROTOC_PLUGIN) shouldBe true + generateProto.builtins() shouldContain "kotlin" + val compileJava = project.tasks.getByName("compileJava") as JavaCompile + compileJava.source.files shouldContain generatedFile + } + + /** + * Verifies that the source set directories are configured before the Protobuf Gradle + * Plugin passes them to the IDEA plugin, which it does right after running the + * `generateProtoTasks { }` blocks. + * + * The configuration of each [GenerateProtoTask] rewrites the source directories of + * its source set. If it ran only when Gradle creates the task, the IDEA plugin reading + * the source directories would make Gradle create the task, and the project would fail + * to evaluate with `ConcurrentModificationException`. + */ + @Test + fun `configure 'generateProto' before the IDEA plugin reads the source set`() { + project.pluginManager.apply("idea") + + evaluate() + + val generateProto = project.tasks.getByName("generateProto") as GenerateProtoTask + generateProto.hasPlugin(SPINE_COMPILER_PROTOC_PLUGIN) shouldBe true + } + + @Test + fun `configure 'generateProto' created before the project is evaluated`() { + // A build script may create the task before the Protobuf Gradle Plugin + // runs the `generateProtoTasks { }` blocks. + project.tasks.getByName("generateProto") + + evaluate() + + val generateProto = project.tasks.getByName("generateProto") as GenerateProtoTask + generateProto.hasPlugin(SPINE_COMPILER_PROTOC_PLUGIN) shouldBe true + dependenciesOf("compileJava") shouldContain launchMain + } + + /** + * Verifies the wiring when Gradle creates each task as soon as it is registered, + * as it does for a build calling `tasks.all { }` before applying the plugins. + * + * The [LaunchSpineCompiler] task is then configured right when the plugin registers + * it, and finds the `generateProto` task registered by the Protobuf Gradle Plugin. + */ + @Test + fun `wire the tasks created as soon as they are registered`() { + val eager = ProjectBuilder.builder() + .withProjectDir(projectDir.resolve("eager")) + .build() + eager.group = "io.spine.stubs" + // Makes Gradle create each task once it is registered. + eager.tasks.all { } + with(eager) { + apply(plugin = "java") + apply() + apply() + repositories.mavenLocal() + } + val protoFile = eager.projectDir.resolve("src/main/proto/given.proto") + protoFile.parentFile.mkdirs() + protoFile.writeText("syntax = \"proto3\";") + + (eager as ProjectInternal).evaluate() + + declaredDependenciesOf(launchMain, eager) shouldContain "generateProto" + val launchTask = eager.tasks.getByName(launchMain) as LaunchSpineCompiler + launchTask.protoSourceDirs.asFileTree.files shouldContain protoFile + dependenciesOf("compileJava", eager) shouldContain launchMain + } +} diff --git a/protoc-plugin/build.gradle.kts b/protoc-plugin/build.gradle.kts index 9e5e992b281..de968153ff7 100644 --- a/protoc-plugin/build.gradle.kts +++ b/protoc-plugin/build.gradle.kts @@ -1,32 +1,21 @@ /* - * Copyright 2026, TeamDev. All rights reserved. + * Copyright 2026 CodeMatters, Lda. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * - * Redistribution and use in source and/or binary forms, with or without - * modification, must retain the above copyright notice and the following - * disclaimer. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * Unless required by applicable law or agreed to in writing, software distributed under + * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific language governing permissions + * and limitations under the License. */ import io.spine.dependency.lib.Protobuf import io.spine.dependency.local.Base import io.spine.dependency.local.ToolBase +import io.spine.gradle.publish.StandardJavaPublicationHandler import org.gradle.api.file.DuplicatesStrategy.INCLUDE plugins { @@ -65,3 +54,15 @@ tasks.jar { archiveClassifier.set("exe") duplicatesStrategy = INCLUDE } + +// With the classifier above, the publication has no main artifact, so its POM keeps +// the `pom` packaging Gradle has always derived for it, rather than the `jar` packaging +// which `spinePublishing` declares for standard publications. +// The publication is created on `afterEvaluate` scheduled by the root project, +// so this action, scheduled later, finds it. +afterEvaluate { + the().publications + .named(StandardJavaPublicationHandler.PUBLICATION_NAME) { + pom.packaging = "pom" + } +} diff --git a/version.gradle.kts b/version.gradle.kts index b1e4fe06325..5a73218bab8 100644 --- a/version.gradle.kts +++ b/version.gradle.kts @@ -18,7 +18,7 @@ * This version is also used by integration test projects. * E.g. see `tests/consumer/build.gradle.kts`. */ -private val compilerVersion = "2.0.0-SNAPSHOT.081" +private val compilerVersion = "2.0.0-SNAPSHOT.082" extra.set("compilerVersion", compilerVersion) /**