Skip to main content

Configuration

Configuration options for library-api-watchdog.

Apply the Gradle plugin

Add the Space EAP repository to the plugin and dependency repositories:

settings.gradle.kts
pluginManagement {
repositories {
maven("https://packages.jetbrains.team/maven/p/kt-lib/eap")
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositories {
maven("https://packages.jetbrains.team/maven/p/kt-lib/eap")
mavenCentral()
}
}

Then apply the plugin:

build.gradle.kts
plugins {
kotlin("library.api-watchdog") version "0.1.0-SNAPSHOT"
}

library-api-watchdog is a Kotlin compiler plugin. It needs a Gradle project that applies the Kotlin plugin and turns on explicit API mode:

build.gradle.kts
kotlin {
explicitApi()
}

The -Xexplicit-api compiler flag, and the warning variant of either form, also count. Without explicit API mode enabled, library-api-watchdog registers no checks at all: there is no public API contract to watch. The Gradle plugin prints a build warning when explicit API mode is not enabled.

What applying does

Applying the plugin:

  • Registers the compiler plugin for every compilation in the project except test compilations: test sources are not published, so they carry no API contract to watch.
  • Adds a dependency on org.jetbrains.kotlin:kotlin-library-api-watchdog-plugin-annotations, a runtime library with the @Intentionally* exemption annotations.
  • Warns when explicit API mode is not enabled.
  • Checks whether binary compatibility validation is enabled alongside it, printing a build warning with a setup snippet for either one that is missing. See below.

Errors by default

library-api-watchdog is intentionally restrictive by default: every check reports a compilation error until configured otherwise. See The apiWatchdog extension for demoting individual checks to warnings or disabling them, and Exemptions and internal API for exempting a single declaration in place instead of changing severity project-wide.

Adding the plugin to existing libraries

When added to an exiting library, chances are that the whole codebase will turn red, and most of the declarations reported can't be changes easily without breaking users.

Use updateBackwardsCompatibilityExempts task to mark existing APIs with @Intentionally* annotations and ExemptionReason.FOR_BACKWARDS_COMPATIBILITY:

./gradlew updateBackwardsCompatibilityExempts

See Adding the plugin to existing libraries for more details.

Without Gradle

When invoking the compiler directly, configure severities with the repeatable plugin option:

-P plugin:org.jetbrains.kotlin.library.api.watchdog:diagnosticSeverity=<NAME>:<severity>

Parameters:

  • <NAME> is a diagnostic name. Any value from the Property reference is valid.
  • <severity> is error, warning, or none.

The PUBLIC_TYPE_FROM_NON_TRANSITIVE_DEPENDENCY check is unavailable without Gradle because the compiler alone cannot distinguish dependencies declared with api from those declared with implementation.

For example, to demote undocumented public API to a warning use the following argument form:

-P plugin:org.jetbrains.kotlin.library.api.watchdog:diagnosticSeverity=UNDOCUMENTED_PUBLIC_API:warning

The apiWatchdog extension

Every property has a default value. For configurable checks it is WatchdogSeverity.ERROR, for enabled/disabled switches is it true by default.

See the list of configurable properties:

build.gradle.kts
apiWatchdog {
suggestAbiValidation = true
publicTypesMustBeTransitiveDependencies = true
publicTypeWithInternalApi = true
openApiWithoutSubclassOptIn = WatchdogSeverity.ERROR
subclassOptInWithoutMarkers = WatchdogSeverity.ERROR
exhaustivePublicApi = WatchdogSeverity.ERROR
undocumentedPublicApi = WatchdogSeverity.ERROR
functionTypeAliasPublicApi = WatchdogSeverity.ERROR
dataClassPublicApi = WatchdogSeverity.ERROR
statefulClassWithoutEquals = WatchdogSeverity.ERROR
statefulClassWithoutHashCode = WatchdogSeverity.ERROR
statefulClassWithoutToString = WatchdogSeverity.ERROR
mutableCollectionPublicApi = WatchdogSeverity.ERROR
pairOrTriplePublicApi = WatchdogSeverity.ERROR
booleanParameterPublicApi = WatchdogSeverity.ERROR
nullableBooleanPublicApi = WatchdogSeverity.ERROR
requiredParameterAfterOptional = WatchdogSeverity.ERROR
inconsistentParameterOrderInOverloads = WatchdogSeverity.ERROR
inlineFunctionWithLogic = WatchdogSeverity.ERROR
dslMarkerNoopTarget = WatchdogSeverity.ERROR
dslMarkerWithoutExplicitTargets = WatchdogSeverity.ERROR
dslMarkerNoopTypePosition = WatchdogSeverity.ERROR
javaInterop {
// One switch for the whole Java interop group,
// it overrides the severities below.
enabled = true
mangledJvmNamePublicApi = WatchdogSeverity.ERROR
kotlinOnlyApiWithoutJvmSynthetic = WatchdogSeverity.ERROR
companionApiWithoutJvmStatic = WatchdogSeverity.ERROR
companionConstantWithoutJvmField = WatchdogSeverity.ERROR
topLevelApiWithoutJvmName = WatchdogSeverity.ERROR
defaultParametersWithoutJvmOverloads = WatchdogSeverity.ERROR
}
}

Severity semantics

Each check's severity is a WatchdogSeverity:

ValueEffect
ERRORFails the compilation. This is the default for every configurable diagnostic.
WARNINGReported as a compiler warning, the build still succeeds.
NONEThe check is disabled entirely.

EXEMPTION_WITHOUT_EXPLANATION has no matching property and is always an error. PUBLIC_TYPE_WITH_INTERNAL_API and PUBLIC_TYPE_FROM_NON_TRANSITIVE_DEPENDENCY are always errors when enabled. Their Gradle properties are Boolean whole-check switches rather than severities. See Exemptions and internal API.

Property reference

PropertyCheckDiagnostic
publicTypesMustBeTransitiveDependenciesPublic types from non-transitive dependenciesPUBLIC_TYPE_FROM_NON_TRANSITIVE_DEPENDENCY
publicTypeWithInternalApiPublic types marked as internal APIPUBLIC_TYPE_WITH_INTERNAL_API
openApiWithoutSubclassOptInOpen API without subclass opt-inOPEN_API_WITHOUT_SUBCLASS_OPT_IN
subclassOptInWithoutMarkersSubclass opt-in without markersSUBCLASS_OPT_IN_WITHOUT_MARKERS
exhaustivePublicApiExhaustive public APIEXHAUSTIVE_PUBLIC_API
undocumentedPublicApiUndocumented public APIUNDOCUMENTED_PUBLIC_API
functionTypeAliasPublicApiFunction type aliases in public APIFUNCTION_TYPE_ALIAS_PUBLIC_API
dataClassPublicApiData classes in public APIDATA_CLASS_PUBLIC_API
statefulClassWithoutEqualsStateful classes without equals, hashCode, and toStringSTATEFUL_CLASS_WITHOUT_EQUALS
statefulClassWithoutHashCodeStateful classes without equals, hashCode, and toStringSTATEFUL_CLASS_WITHOUT_HASH_CODE
statefulClassWithoutToStringStateful classes without equals, hashCode, and toStringSTATEFUL_CLASS_WITHOUT_TO_STRING
mutableCollectionPublicApiMutable collections in public APIMUTABLE_COLLECTION_PUBLIC_API
pairOrTriplePublicApiPair and Triple in public APIPAIR_OR_TRIPLE_PUBLIC_API
booleanParameterPublicApiBoolean parameters in public APIBOOLEAN_PARAMETER_PUBLIC_API
nullableBooleanPublicApiNullable Booleans in public APINULLABLE_BOOLEAN_PUBLIC_API
requiredParameterAfterOptionalRequired parameters after optional onesREQUIRED_PARAMETER_AFTER_OPTIONAL
inconsistentParameterOrderInOverloadsInconsistent parameter order in overloadsINCONSISTENT_PARAMETER_ORDER_IN_OVERLOADS
inlineFunctionWithLogicInline functions with logicINLINE_FUNCTION_WITH_LOGIC
dslMarkerNoopTargetDSL markers with no-op targetsDSL_MARKER_NOOP_TARGET
dslMarkerWithoutExplicitTargetsDSL markers without explicit targetsDSL_MARKER_WITHOUT_EXPLICIT_TARGETS
dslMarkerNoopTypePositionDSL markers on no-op type positionsDSL_MARKER_NOOP_TYPE_POSITION
javaInterop.mangledJvmNamePublicApiMangled JVM names in public APIMANGLED_JVM_NAME_PUBLIC_API
javaInterop.kotlinOnlyApiWithoutJvmSyntheticKotlin-only API without JvmSyntheticKOTLIN_ONLY_API_WITHOUT_JVM_SYNTHETIC
javaInterop.companionApiWithoutJvmStaticCompanion API without JvmStaticCOMPANION_API_WITHOUT_JVM_STATIC
javaInterop.companionConstantWithoutJvmFieldCompanion constants without JvmFieldCOMPANION_CONSTANT_WITHOUT_JVM_FIELD
javaInterop.topLevelApiWithoutJvmNameTop-level API without JvmNameTOP_LEVEL_API_WITHOUT_JVM_NAME
javaInterop.defaultParametersWithoutJvmOverloadsDefault parameters without JvmOverloadsDEFAULT_PARAMETERS_WITHOUT_JVM_OVERLOADS

The last six properties live inside the javaInterop { } block. They only run in JVM compilations, and javaInterop.enabled (default true) is a single switch for all of them: set it to false and every one of the six resolves to NONE, no matter what its own property says. See Java interop checks for more details.

Binary compatibility validation suggestion

suggestAbiValidation (default is true) controls a build warning unrelated to any diagnostic. If neither the Kotlin Gradle plugin's built-in ABI validation nor the standalone Binary Compatibility Validator plugin is enabled alongside the watchdog, the plugin warns that incompatible changes to already-shipped API would go unnoticed. Set it to false to silence the warning:

build.gradle.kts
apiWatchdog {
suggestAbiValidation = false
}

See Binary compatibility validation suggestion for more details.

Next steps