Skip to content

Add 0.15.0 migration guide #242

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions api/binary-compatibility-validator.api
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,9 @@ public abstract class kotlinx/validation/KotlinApiBuildTask : kotlinx/validation
public abstract fun getInputClassesDirs ()Lorg/gradle/api/file/ConfigurableFileCollection;
public abstract fun getInputDependencies ()Lorg/gradle/api/file/ConfigurableFileCollection;
public abstract fun getInputJar ()Lorg/gradle/api/file/RegularFileProperty;
public final fun getOutputApiDir ()Ljava/io/File;
public abstract fun getOutputApiFile ()Lorg/gradle/api/file/RegularFileProperty;
public final fun setOutputApiDir (Ljava/io/File;)V
}

public class kotlinx/validation/KotlinApiCompareTask : org/gradle/api/DefaultTask {
Expand Down
109 changes: 109 additions & 0 deletions docs/design/0.15.0-migration-guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
The major feature shipped with the release 0.15.0 of the Binary Compatibility Validator is Kotlin libraries (or KLibs)
ABI validation support.

This support, however, comes with a few changes that break the plugin's compatibility or changes its behavior:
- In multiplatform projects, empty modules are no longer ignored during ABI validation;
- ABI file names (`api/<project name>.api`) are no longer treated in a case-insensitive way;
- Gradle tasks provided by the plugin changed their API.

If you have a multiplatform project with modules having no sources, configure BCV Gradle tasks manually,
or have an ABI file with a name in a case different from how the project was named in the settings,
please check the following steps for smooth migration.

### In multiplatform projects, empty modules are no longer ignored during ABI validation

Previously, if there was an empty Gradle module/project (for example, test-only module), BCV ignored validation of
such a module if the multiplatform platform plugin was applied there. However, BCV behaved differently for similar
projects with the JVM-plugin applied.

Starting from `0.15.0` behavior was aligned and now BCV no longer ignores validation of empty modules/projects.
If you have such a module, please either generate and commit (empty) ABI dump file for it by running
`./gradlew apiDump`, or consider excluding the module from validation using `ignoredProjects` setting:
```kotlin
apiValidation {
ignoredProjects += "empty-module"
}
```

### ABI file names are no longer treated in a case-insensitive way

Starting from the version `0.15.0`, name of an ABI dump file stored in the project's directory
has to match project's name. Previously, a project named `gradle-project` might use an ABI file
named `Gradle-Project.api`, but now it has to be `gradle-project.api`.

On case-insensitive filesystems (Windows and macOS filesystems are case-insensitive) the change won't manifest itself,
but on case-sensitive filesystems (Linux filesystems) the validation will start failing due to a missing ABI dump file
error.

You can either rename the existing file or delete it and run `apiDump` task.

If you're using git SCV, it's highly recommended to use `git mv` command to rename a file, or a pair of `git rm` and
`git add` commands, if you're deleting the old file and then generating a new one. When done differently, git may
not reflect changes in a repository index (it's specific to case-insensitive filesystems).

If you're using another SCV, please consult its documentation for details on how to deal with file renaming on
case-insensitive filesystems.

### Gradle tasks provided by the plugin changed their API

All Gradle tasks that existed before are still presented and in use, but some of them changed
their API to use Gradle Properties-based configuration. There are several advantages of using Gradle Property
instead of regular Kotlin properties, but the main one is that it improves dependency tracking between tasks.
You can [Gradle docs](https://docs.gradle.org/current/userguide/lazy_configuration.html) for more details
on the subject.

The following task-classes changed their API:
- KotlinApiBuildTask
- `ignoredPackages`, `nonPublicMarkers`, `ignoredClasses`, `publicPackages`, `publicMarkers`, `publicClasses` are
all `SetProperty<String>` instances now;
- `outputApiDir` was renamed to `outputApiFile` and became `RegularFileProperty` instance; now it should point to
the generated file instead of a directory holding it;
- `inputClassesDirs` and `inputDependencies` are both `ConfigurableFileCollection` instances now.
- KotlinApiCompareTask
- `projectApiFile` and `generatedApiFile` are both `RegularFileProperty` instances now.

If you were configuring `KotlinApiBuildTask.outputApiDir`, `KotlinApiCompareTask.projectApiFile` or
`KotlinApiCompareTask.generatedApiFile` by assigning a file instance to it, now these properties could only
be configured by using [RegularFileProperty setters](https://docs.gradle.org/current/javadoc/org/gradle/api/file/RegularFileProperty.html).

Value assignments to `KotlinApiBuildTask.inputClassesDirs` and `KotlinApiBuildTask.inputDependencies` properties have
to be replaced with [ConfigurableFileCollection setters](https://docs.gradle.org/current/javadoc/org/gradle/api/file/ConfigurableFileCollection.html).

All `KotlinApiBuildTask`'s filters could be configured by using [SetProperty setters](https://docs.gradle.org/current/javadoc/org/gradle/api/provider/SetProperty.html)
now.

Below is an example of how task configuration needs to be updated:
```kotlin
// Old configuration
val buildTask = tasks.register("buildApi", KotlinApiBuildTask::class.java) {
val classes = compilation.output.classesDirs

it.inputClassesDirs = files(classes)
it.inputDependencies = files(classes)
it.outputApiDir = project.layout.buildDirectory.get().asFile
it.ignoredPackages = setOf("org.example")
}

val checkTask = tasks.register("checkApi", KotlinApiCompareTask::class.java) {
it.projectApiFile = project.layout.projectDirectory.dir("api").file("gradle-project.api").asFile
it.generatedApiFile = project.layout.buildDirectory.get().asFile.resolve("dump.api")

it.dependsOn(buildTask)
}
```

```kotlin
// New configuration
val buildTask = tasks.register("buildApi", KotlinApiBuildTask::class.java) {
val classes = compilation.output.classesDirs

it.inputClassesDirs.from(classes)
it.inputDependencies.from(classes)
it.outputApiFile.set(project.layout.buildDirectory.map { it.file("gradle-project.api") })
it.ignoredPackages.set(setOf("org.example"))
}
val checkTask = tasks.register("checkApi", KotlinApiCompareTask::class.java) {
it.projectApiFile.set(project.layout.projectDirectory.dir("api").file("gradle-project.api"))
it.generatedApiFile.set(buildTask.flatMap { it.outputApiFile })
}
```
10 changes: 10 additions & 0 deletions src/main/kotlin/KotlinApiBuildTask.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,24 @@ import kotlinx.validation.api.*
import org.gradle.api.*
import org.gradle.api.file.*
import org.gradle.api.tasks.*
import java.io.File
import java.util.jar.JarFile
import javax.inject.Inject

private const val MIGRATION_GUIDE_LINK = "https://github.com/Kotlin/binary-compatibility-validator/blob/master/docs/design/0.15.0-migration-guide.md"
private const val OUTPUT_API_DIR_ERROR = "Property outputApiDir was replaced with outputApiFile. Please refer to the migration guide for migration details: $MIGRATION_GUIDE_LINK"

public abstract class KotlinApiBuildTask @Inject constructor(
) : BuildTaskBase() {
@get:OutputFile
public abstract val outputApiFile: RegularFileProperty

@get:Internal
@Deprecated(level = DeprecationLevel.ERROR, message = OUTPUT_API_DIR_ERROR)
public var outputApiDir: File
get() = throw UnsupportedOperationException(OUTPUT_API_DIR_ERROR)
set(_) = throw UnsupportedOperationException(OUTPUT_API_DIR_ERROR)

@get:InputFiles
@get:Optional
@get:PathSensitive(PathSensitivity.RELATIVE)
Expand Down