Files
kotlin/libraries/tools/binary-compatibility-validator
Alexander Udalov 329fbd8fa8 Extract kotlin-reflect-api module out of kotlin-reflect
This is needed only for faster compilation of the Kotlin project itself
and has no effect on the public artifact
org.jetbrains.kotlin:kotlin-reflect.

The problem this is solving is the rebuild of the project once anything
has been changed in modules in 'core' (even inside function bodies, i.e.
a non-API change). Previously, changes in 'core' led to the compilation
of kotlin-reflect, which led to the rebuild of all modules depending on
kotlin-reflect directly or indirectly (which is almost all modules in
the project) because kotlin-reflect's artifacts are custom-built and the
changes can not be picked up incrementally. But 99.9% of the time the
initial changes in 'core' could not have any effect on the usages of
kotlin-reflect, because classes from those modules are moved to an
internal package in kotlin-reflect and thus are an internal
implementation detail.

Now, changes in 'core' still lead to the compilation of kotlin-reflect
and to the process of building the custom jar. But if a module depends
on kotlin-reflect-api, not kotlin-reflect, then the incremental
difference checker will detect that the module does not have to be
recompiled if there hasn't been any changes to the API of
kotlin-reflect-api. Which means that the module will not be rebuilt on
every change in 'core'.

This commit only introduces the new module. The dependencies
(kotlin-reflect -> kotlin-reflect-api) are replaced in the next commit.
2017-11-28 12:35:48 +01:00
..

Kotlin Public API binary compatibility validation tool

This tool allows to dump binary API of a Kotlin library that is public in sense of Kotlin visibilities and ensure that the public binary API wasn't changed in a way that make this change binary incompatible.

How to run

Compile and run tests. CasesPublicAPITest verifies the tool itself, and RuntimePublicAPITest dumps the public API of kotlin-stdlib, kotlin-stdlib-jdk7/8, kotlin-stdlib-jre7/8 and kotlin-reflect jars, which must be built beforehand with gradle. Use clean assemble tasks, since the incremental compilation currently doesn't produce all the required output.

When substantial changes are made to the public API, it may be convenient to overwrite the entire dump and compare changes later before committing: pass -Doverwrite.output=true property to the test to do so.

Also you can use shared run configuration "Binary compatibility tests", which also overwrites the results when they differ.

What constitutes the public API

Classes

A class is considered to be effectively public if all of the following conditions are met:

  • it has public or protected JVM access (ACC_PUBLIC or ACC_PROTECTED)
  • it has one of the following visibilities in Kotlin:
    • no visibility (means no Kotlin declaration corresponds to this compiled class)
    • public
    • protected
    • internal, only in case if the class is annotated with InlineExposed
  • it isn't a local class
  • it isn't a synthetic class with mappings for when tableswitches ($WhenMappings)
  • it contains at least one effectively public member, in case if the class corresponds to a kotlin file with top-level members or a multifile facade
  • in case if the class is a member in another class, it is contained in the effectively public class
  • in case if the class is a protected member in another class, it is contained in the non-final class

Members

A member of the class (i.e. a field or a method) is considered to be effectively public if all of the following conditions are met:

  • it has public or protected JVM access (ACC_PUBLIC or ACC_PROTECTED)

  • it has one of the following visibilities in Kotlin:

    • no visibility (means no Kotlin declaration corresponds to this class member)
    • public
    • protected
    • internal, only in case if the class is annotated with InlineExposed

    Note that Kotlin visibility of a field exposed by lateinit property is the visibility of it's setter.

  • in case if the member is protected, it is contained in non-final class

  • it isn't a synthetic access method for a private field

What makes an incompatible change to the public binary API

Class changes

For a class a binary incompatible change is:

  • changing the full class name (including package and containing classes)
  • changing the superclass, so that the class no longer has the previous superclass in the inheritance chain
  • changing the set of implemented interfaces so that the class no longer implements interfaces it had implemented before
  • changing one of the following access flags:
    • ACC_PUBLIC, ACC_PROTECTED, ACC_PRIVATE — lessening the class visibility
    • ACC_FINAL — making non-final class final
    • ACC_ABSTRACT — making non-abstract class abstract
    • ACC_INTERFACE — changing class to interface and vice versa
    • ACC_ANNOTATION — changing annotation to interface and vice versa

Class member changes

For a class member a binary incompatible change is:

  • changing its name
  • changing its descriptor (erased return type and parameter types for methods); this includes changing field to method and vice versa
  • changing one of the following access flags:
    • ACC_PUBLIC, ACC_PROTECTED, ACC_PRIVATE — lessening the member visibility
    • ACC_FINAL — making non-final field or method final
    • ACC_ABSTRACT — making non-abstract method abstract
    • ACC_STATIC — changing instance member to static and vice versa