Skip to content

Commit

Permalink
Add 0.15.0 migration guide (#242)
Browse files Browse the repository at this point in the history
* Add a document describing how to migrate a project to BCV 0.15.0

* Update a migration guide with information about module exclusion

* Add an old property to provide users with a more insightful error message
  • Loading branch information
fzhinkin authored Jun 26, 2024
1 parent 3590611 commit 3b1bfe8
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 0 deletions.
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

0 comments on commit 3b1bfe8

Please sign in to comment.