diff --git a/.gitignore b/.gitignore index e26bbdc..b6afc57 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ local.properties .DS_Store kotlin-js-store +.kotlin diff --git a/README.md b/README.md index 5fb664c..a44532d 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,8 @@ plugins { ... metalava { + format.set(Format.V4) + filename.set("api.txt") ... } ``` @@ -85,7 +87,7 @@ Check out the [samples](https://github.com/tylerbwong/metalava-gradle/tree/main/ ### License - Copyright 2023 Tyler Wong + Copyright 2024 Tyler Wong Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 640a719..57f8851 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,8 +1,8 @@ [versions] -androidGradle = "8.1.3" +androidGradle = "8.4.2" junit = "5.9.3" -kotlin = "1.9.20" -ktlintGradle = "11.4.0" +kotlin = "2.0.0" +ktlintGradle = "12.1.1" pluginPublish = "1.2.0" metalavaGradle = "0.3.5" diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index c1962a7..7f93135 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 744c64d..a441313 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index aeb74cb..1aa94a4 100755 --- a/gradlew +++ b/gradlew @@ -83,7 +83,8 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -130,10 +131,13 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. @@ -141,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -149,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -198,11 +202,11 @@ fi # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ diff --git a/plugin/api/0.3.6-SNAPSHOT.txt b/plugin/api/0.3.6-SNAPSHOT.txt index 9cc4fde..577f695 100644 --- a/plugin/api/0.3.6-SNAPSHOT.txt +++ b/plugin/api/0.3.6-SNAPSHOT.txt @@ -1,17 +1,6 @@ // Signature format: 4.0 package me.tylerbwong.gradle.metalava { - public enum Documentation { - method public String toString(); - method public static me.tylerbwong.gradle.metalava.Documentation valueOf(String value); - method public static me.tylerbwong.gradle.metalava.Documentation[] values(); - enum_constant public static final me.tylerbwong.gradle.metalava.Documentation HIDDEN; - enum_constant public static final me.tylerbwong.gradle.metalava.Documentation PACKAGE; - enum_constant public static final me.tylerbwong.gradle.metalava.Documentation PRIVATE; - enum_constant public static final me.tylerbwong.gradle.metalava.Documentation PROTECTED; - enum_constant public static final me.tylerbwong.gradle.metalava.Documentation PUBLIC; - } - public enum Format { method public String toString(); method public static me.tylerbwong.gradle.metalava.Format valueOf(String value); @@ -41,36 +30,28 @@ package me.tylerbwong.gradle.metalava.extension { public class MetalavaExtension { ctor @javax.inject.Inject public MetalavaExtension(org.gradle.api.model.ObjectFactory objectFactory); method public final org.gradle.api.provider.Property getApiType(); - method public final org.gradle.api.provider.Property getDocumentation(); method public final org.gradle.api.provider.Property getEnforceCheck(); method public final org.gradle.api.provider.Property getFilename(); method public final org.gradle.api.provider.Property getFormat(); method public final org.gradle.api.provider.SetProperty getHiddenAnnotations(); - method public final org.gradle.api.provider.Property getIncludeSignatureVersion(); method public final org.gradle.api.provider.Property getInputKotlinNulls(); method public final org.gradle.api.provider.Property getJavaSourceLevel(); method public final org.gradle.api.provider.Property getKeepFilename(); method public final org.gradle.api.provider.Property getMetalavaJarPath(); - method public final org.gradle.api.provider.Property getOutputDefaultValues(); - method public final org.gradle.api.provider.Property getOutputKotlinNulls(); method public final org.gradle.api.provider.Property getReportLintsAsErrors(); method public final org.gradle.api.provider.Property getReportWarningsAsErrors(); method public final org.gradle.api.provider.Property getSignature(); method public final org.gradle.api.file.ConfigurableFileCollection getSourcePaths(); method public final org.gradle.api.provider.Property getVersion(); property public final org.gradle.api.provider.Property apiType; - property public final org.gradle.api.provider.Property documentation; property public final org.gradle.api.provider.Property enforceCheck; property public final org.gradle.api.provider.Property filename; property public final org.gradle.api.provider.Property format; property public final org.gradle.api.provider.SetProperty hiddenAnnotations; - property public final org.gradle.api.provider.Property includeSignatureVersion; property public final org.gradle.api.provider.Property inputKotlinNulls; property public final org.gradle.api.provider.Property javaSourceLevel; property public final org.gradle.api.provider.Property keepFilename; property public final org.gradle.api.provider.Property metalavaJarPath; - property public final org.gradle.api.provider.Property outputDefaultValues; - property public final org.gradle.api.provider.Property outputKotlinNulls; property public final org.gradle.api.provider.Property reportLintsAsErrors; property public final org.gradle.api.provider.Property reportWarningsAsErrors; property public final org.gradle.api.provider.Property signature; diff --git a/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/Documentation.kt b/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/Documentation.kt deleted file mode 100644 index 6bab1bd..0000000 --- a/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/Documentation.kt +++ /dev/null @@ -1,34 +0,0 @@ -package me.tylerbwong.gradle.metalava - -/** - * Flags to determine documented API by visibility modifier. - */ -@Deprecated("This has been removed and is not currently used.") -enum class Documentation(private val flagValue: String) { - /** - * Only include elements that are public. - */ - PUBLIC("--public"), - - /** - * Only include elements that are public or protected. - */ - PROTECTED("--protected"), - - /** - * Only include elements that are public, protected or package protected. - */ - PACKAGE("--package"), - - /** - * Include all elements except those that are marked hidden. - */ - PRIVATE("--private"), - - /** - * Include all elements, including hidden. - */ - HIDDEN("--hidden"); - - override fun toString(): String = flagValue -} diff --git a/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/Module.kt b/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/Module.kt index 8612b25..9fd2683 100644 --- a/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/Module.kt +++ b/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/Module.kt @@ -55,7 +55,7 @@ internal sealed class Module { override fun compileClasspath(variant: String?): FileCollection { return extension.targets .flatMap { it.compilations } - .filter { it.defaultSourceSetName.contains("main", ignoreCase = true) } + .filter { it.defaultSourceSet.name.contains("main", ignoreCase = true) } .map { it.compileDependencyFiles } .reduce(FileCollection::plus) .filter { it.exists() && it.checkDirectory(listOf(".jar", ".class")) } diff --git a/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/extension/MetalavaExtension.kt b/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/extension/MetalavaExtension.kt index 0b468bf..50f284e 100644 --- a/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/extension/MetalavaExtension.kt +++ b/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/extension/MetalavaExtension.kt @@ -1,6 +1,5 @@ package me.tylerbwong.gradle.metalava.extension -import me.tylerbwong.gradle.metalava.Documentation import me.tylerbwong.gradle.metalava.Format import me.tylerbwong.gradle.metalava.Signature import org.gradle.api.JavaVersion @@ -16,7 +15,7 @@ open class MetalavaExtension @Inject constructor( /** * The version of Metalava to use. */ - val version: Property = objectFactory.property { set("1.0.0-alpha10") } + val version: Property = objectFactory.property().also { it.set("1.0.0-alpha10") } /** * A custom Metalava JAR location path to use instead of the embedded dependency. @@ -45,39 +44,11 @@ open class MetalavaExtension @Inject constructor( */ val filename: Property = objectFactory.property { set("api.txt") } - /** - * @see Documentation - */ - @Deprecated("This has been removed and is not currently used.") - val documentation: Property = objectFactory.property { - set(Documentation.PROTECTED) - } - /** * Type is one of 'api' and 'removed', which checks either the public api or the removed api. */ val apiType: Property = objectFactory.property { set("api") } - /** - * Controls whether nullness annotations should be formatted as in Kotlin (with "?" for nullable - * types, "" for non-nullable types, and "!" for unknown. The default is true. - */ - @Deprecated("This has been removed and is not currently used.") - val outputKotlinNulls: Property = objectFactory.property { set(true) } - - /** - * Controls whether default values should be included in signature files. The default is true. - */ - @Deprecated("This has been removed and is not currently used.") - val outputDefaultValues: Property = objectFactory.property { set(true) } - - /** - * Whether the signature files should include a comment listing the format version of the - * signature file. The default is true. - */ - @Deprecated("This has been removed and is not currently used.") - val includeSignatureVersion: Property = objectFactory.property { set(true) } - /** * Remove the given packages from the API even if they have not been marked with @hide. */ @@ -126,9 +97,9 @@ open class MetalavaExtension @Inject constructor( private inline fun ObjectFactory.property( configuration: Property.() -> Unit = {} - ) = property(T::class.java).apply { configuration() } + ): Property = property(T::class.java).apply { configuration() } private inline fun ObjectFactory.setProperty( configuration: SetProperty.() -> Unit = {} - ) = setProperty(T::class.java).apply { configuration() } + ): SetProperty = setProperty(T::class.java).apply { configuration() } } diff --git a/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/plugin/MetalavaPlugin.kt b/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/plugin/MetalavaPlugin.kt index 8fbaa1e..f780738 100644 --- a/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/plugin/MetalavaPlugin.kt +++ b/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/plugin/MetalavaPlugin.kt @@ -5,6 +5,7 @@ import me.tylerbwong.gradle.metalava.Module.Companion.module import me.tylerbwong.gradle.metalava.extension.MetalavaExtension import me.tylerbwong.gradle.metalava.task.MetalavaCheckCompatibilityTask import me.tylerbwong.gradle.metalava.task.MetalavaGenerateSignatureTask +import me.tylerbwong.gradle.metalava.task.MetalavaHelpTask import org.gradle.api.Plugin import org.gradle.api.Project import org.gradle.api.model.ObjectFactory @@ -40,7 +41,13 @@ internal class MetalavaPlugin @Inject constructor( module: Module, variantName: String? = null, ) { - MetalavaGenerateSignatureTask.create( + MetalavaHelpTask.register( + project = project.rootProject, + objectFactory = objectFactory, + extension = metalavaExtension, + ) + + MetalavaGenerateSignatureTask.register( project = project, objectFactory = objectFactory, extension = metalavaExtension, @@ -48,7 +55,7 @@ internal class MetalavaPlugin @Inject constructor( variantName = variantName, ) - val checkCompatibilityTask = MetalavaCheckCompatibilityTask.create( + val checkCompatibilityTask = MetalavaCheckCompatibilityTask.register( project = project, objectFactory = objectFactory, extension = metalavaExtension, diff --git a/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/task/BaseMetalavaTask.kt b/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/task/BaseMetalavaTask.kt index ed49ee4..99b41aa 100644 --- a/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/task/BaseMetalavaTask.kt +++ b/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/task/BaseMetalavaTask.kt @@ -9,6 +9,7 @@ import org.gradle.api.provider.Property import org.gradle.api.provider.SetProperty import org.gradle.api.tasks.Classpath import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Optional import org.gradle.api.tasks.OutputFile import org.gradle.kotlin.dsl.setProperty import org.gradle.workers.WorkerExecutor @@ -21,15 +22,19 @@ internal abstract class BaseMetalavaTask( @get:Classpath abstract val metalavaClasspath: ConfigurableFileCollection + @get:Optional @get:OutputFile abstract val filename: Property + @get:Optional @get:Input abstract val format: Property + @get:Optional @get:Input val hiddenPackages: SetProperty = objectFactory.setProperty() + @get:Optional @get:Input val hiddenAnnotations: SetProperty = objectFactory.setProperty() diff --git a/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/task/MetalavaCheckCompatibilityTask.kt b/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/task/MetalavaCheckCompatibilityTask.kt index 8224ecf..4f2f067 100644 --- a/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/task/MetalavaCheckCompatibilityTask.kt +++ b/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/task/MetalavaCheckCompatibilityTask.kt @@ -61,7 +61,7 @@ internal abstract class MetalavaCheckCompatibilityTask @Inject constructor( "Checks API compatibility between the code base and the released API." private const val METALAVA_CURRENT_PATH = "metalava/current.txt" - fun create( + fun register( project: Project, objectFactory: ObjectFactory, extension: MetalavaExtension, diff --git a/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/task/MetalavaGenerateSignatureTask.kt b/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/task/MetalavaGenerateSignatureTask.kt index dee03d6..e6c9571 100644 --- a/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/task/MetalavaGenerateSignatureTask.kt +++ b/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/task/MetalavaGenerateSignatureTask.kt @@ -92,7 +92,7 @@ internal abstract class MetalavaGenerateSignatureTask @Inject constructor( private const val TASK_NAME = "metalavaGenerateSignature" private const val TASK_DESCRIPTION = "Generates a Metalava signature descriptor file." - fun create( + fun register( project: Project, objectFactory: ObjectFactory, extension: MetalavaExtension, diff --git a/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/task/MetalavaHelpTask.kt b/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/task/MetalavaHelpTask.kt new file mode 100644 index 0000000..b86664b --- /dev/null +++ b/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/task/MetalavaHelpTask.kt @@ -0,0 +1,49 @@ +package me.tylerbwong.gradle.metalava.task + +import me.tylerbwong.gradle.metalava.extension.MetalavaExtension +import org.gradle.api.Project +import org.gradle.api.model.ObjectFactory +import org.gradle.api.tasks.CacheableTask +import org.gradle.api.tasks.TaskAction +import org.gradle.kotlin.dsl.register +import org.gradle.workers.WorkerExecutor +import javax.inject.Inject + +@CacheableTask +internal abstract class MetalavaHelpTask @Inject constructor( + objectFactory: ObjectFactory, + workerExecutor: WorkerExecutor, +) : BaseMetalavaTask(objectFactory, workerExecutor) { + + init { + group = "other" + description = TASK_DESCRIPTION + } + + @TaskAction + fun metalavaHelp() { + executeMetalavaWork(listOf("--help")) + } + + companion object : MetalavaTaskContainer() { + private const val TASK_NAME = "metalavaHelp" + private const val TASK_DESCRIPTION = "Displays the metalava help message." + + fun register( + project: Project, + objectFactory: ObjectFactory, + extension: MetalavaExtension, + ) { + val metalavaClasspath = project.getMetalavaClasspath( + objectFactory, + jarPath = extension.metalavaJarPath.get().ifEmpty { null }, + version = extension.version.get(), + ) + if (project.tasks.findByName(TASK_NAME) == null) { + project.tasks.register(TASK_NAME) { + this.metalavaClasspath.from(metalavaClasspath) + } + } + } + } +} \ No newline at end of file diff --git a/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/task/MetalavaTaskContainer.kt b/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/task/MetalavaTaskContainer.kt index 5f5f39f..b7431b6 100644 --- a/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/task/MetalavaTaskContainer.kt +++ b/plugin/src/main/kotlin/me/tylerbwong/gradle/metalava/task/MetalavaTaskContainer.kt @@ -7,8 +7,6 @@ import org.gradle.api.model.ObjectFactory import java.util.Locale internal abstract class MetalavaTaskContainer { - protected val Boolean.flagValue: String get() = if (this) "yes" else "no" - protected fun Boolean.flag(flagValue: String): List = if (this) { listOf(flagValue) } else { @@ -40,7 +38,9 @@ internal abstract class MetalavaTaskContainer { protected fun getFullTaskName(taskName: String, variantName: String?): String { return if (variantName != null) { - taskName + variantName.capitalize(Locale.getDefault()) + taskName + variantName.replaceFirstChar { + if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() + } } else { taskName } diff --git a/samples/groovy-java/api/groovy-java-api.txt b/samples/groovy-java/api/groovy-java-api.txt index 6146f51..d1bdb0f 100644 --- a/samples/groovy-java/api/groovy-java-api.txt +++ b/samples/groovy-java/api/groovy-java-api.txt @@ -1,3 +1,4 @@ +// Signature format: 4.0 package me.tylerbwong.gradle.metalava.sample { public abstract class SampleJavaPublicApi { @@ -8,11 +9,11 @@ package me.tylerbwong.gradle.metalava.sample { } public interface SamplePublicApi { - method @Nullable public String getPublicApiNullableProperty(); - method @NonNull public String getPublicApiProperty(); + method public String? getPublicApiNullableProperty(); + method public String getPublicApiProperty(); method public void publicApiFunction(); - property @Nullable public abstract String publicApiNullableProperty; - property @NonNull public abstract String publicApiProperty; + property public abstract String? publicApiNullableProperty; + property public abstract String publicApiProperty; } }