Skip to content

Commit

Permalink
Merge pull request #17 from usefulness/updates
Browse files Browse the repository at this point in the history
  • Loading branch information
mateuszkwiecinski authored Nov 13, 2023
2 parents ffb4fef + 5c3b1be commit 1eda298
Show file tree
Hide file tree
Showing 36 changed files with 325 additions and 342 deletions.
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ licenseeForAndroid {
enableResourceGeneration = false
resourceFileName = "licensee_artifacts.json"
singularVariantName = null
automaticCoreDependencyManagement = true
}
```

Expand All @@ -49,6 +50,7 @@ licenseeForAndroid {
- `enableResourceGeneration` - Enables copying _licensee_ report to asset(Android)/resource(JVM) directory, making it available under 'resourceFileName' name.
- `resourceFileName` - The name of the asset/resource file the licensee report gets copied to.
- `singularVariantName` - The name of the build variant that all variants will use to have always the same licensed, regardless of app variant. (i.e. `"paidRelease"`)
- `automaticCoreDependencyManagement` - Automatically add `licensee-for-android-core` core artifact as an implementation dependency for the generated code. The idea is to use the core artifact in a consumer project, and wire generated implementation via DI mechanism

### Common recipes

Expand All @@ -72,6 +74,7 @@ licenseeForAndroid {
```
#### Generate Kotlin code in Kotlin-only module using licensee output from a different module

###### Gradle based approach
```groovy
plugins {
id("org.jetbrains.kotlin.jvm") // or any other module type
Expand Down Expand Up @@ -102,5 +105,32 @@ tasks.named("compileKotlin") {
}
```

###### DI based approach

`app/build.gradle`:

```groovy
plugins {
id("com.android.application")
id("app.cash.licensee")
id("io.github.usefulness.licensee-for-android")
}
licenseeForAndroid {
enableKotlinCodeGeneration = true
}
```
\+ provide `LicenseeForAndroid` object as `Licensee` interface

`consumer/build.gradle`:
```groovy
plugins {
id("org.jetbrains.kotlin.jvm") // or any other module type
}
dependencies
```
\+ inject `Licensee` interface


### Credits
Huge thanks to [premex-ab/gross](https://github.com/premex-ab/gross) which this plugin forked from.
4 changes: 3 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[versions]
gradle-starter = "0.65.0"
gradle-starter = "0.66.0"
gradle-pluginpublish = "1.2.1"
gradle-doctor = "0.9.0"
google-agp = "8.1.3"
Expand All @@ -9,6 +9,7 @@ maven-junit = "5.10.1"
maven-assertj = "3.24.2"
maven-binarycompatiblity = "0.13.2"
maven-dokka = "1.9.10"
maven-poko = "0.15.0"

[libraries]
junit-jupiter-api = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "maven-junit" }
Expand All @@ -31,4 +32,5 @@ starter-library-android = { id = "com.starter.library.android", version.ref = "g
gradle-pluginpublish = { id = "com.gradle.plugin-publish", version.ref = "gradle-pluginpublish" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "maven-kotlin" }
kotlinx-binarycompatibility = { id = "org.jetbrains.kotlinx.binary-compatibility-validator", version.ref = "maven-binarycompatiblity" }
drewhamilton-poko = { id = "dev.drewhamilton.poko", version.ref = "maven-poko" }
osacky-doctor = { id = "com.osacky.doctor", version.ref = "gradle-doctor" }
5 changes: 5 additions & 0 deletions gradle/plugins/src/main/kotlin/PublishingPlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ class PublishingPlugin : Plugin<Project> {
}

extensions.configure<PublishingExtension> {
if(!pluginManager.hasPlugin("com.gradle.plugin-publish")) {
publications.register("mavenJava", MavenPublication::class.java) { publication ->
publication.from(components.getByName("java"))
}
}
publications.configureEach { publication ->
(publication as? MavenPublication)?.pom { pom ->
pom.name.set("${project.group}:${project.name}")
Expand Down
45 changes: 45 additions & 0 deletions licensee-for-android-core/api/licensee-for-android-core.api
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
public final class io/github/usefulness/licensee/Artifact {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/List;Lio/github/usefulness/licensee/Scm;Ljava/util/List;)V
public fun equals (Ljava/lang/Object;)Z
public final fun getArtifactId ()Ljava/lang/String;
public final fun getGroupId ()Ljava/lang/String;
public final fun getName ()Ljava/lang/String;
public final fun getScm ()Lio/github/usefulness/licensee/Scm;
public final fun getSpdxLicenses ()Ljava/util/List;
public final fun getUnknownLicenses ()Ljava/util/List;
public final fun getVersion ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public abstract interface class io/github/usefulness/licensee/Licensee {
public abstract fun getArtifacts ()Ljava/util/List;
}

public final class io/github/usefulness/licensee/Scm {
public fun <init> (Ljava/lang/String;)V
public fun equals (Ljava/lang/Object;)Z
public final fun getUrl ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class io/github/usefulness/licensee/SpdxLicense {
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public fun equals (Ljava/lang/Object;)Z
public final fun getIdentifier ()Ljava/lang/String;
public final fun getName ()Ljava/lang/String;
public final fun getUrl ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class io/github/usefulness/licensee/UnknownLicense {
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
public fun equals (Ljava/lang/Object;)Z
public final fun getName ()Ljava/lang/String;
public final fun getUrl ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

30 changes: 30 additions & 0 deletions licensee-for-android-core/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
alias(libs.plugins.starter.library.kotlin)
alias(libs.plugins.kotlinx.binarycompatibility)
alias(libs.plugins.drewhamilton.poko)
id("com.starter.publishing")
}

kotlin {
explicitApi()
}

def targetVersion = JavaVersion.VERSION_1_8
tasks.withType(KotlinCompile).configureEach {
it.kotlinOptions.jvmTarget = targetVersion.toString()
}
tasks.withType(JavaCompile).configureEach {
it.options.release.set(targetVersion.majorVersion.toInteger())
}

tasks.withType(KotlinCompile).configureEach {
kotlinOptions {
apiVersion = "1.8"
languageVersion = "1.8"
}
}
tasks.withType(Test).configureEach {
useJUnitPlatform()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package io.github.usefulness.licensee

import dev.drewhamilton.poko.Poko

@Poko
public class Artifact(
public val groupId: String,
public val artifactId: String,
public val version: String,
public val name: String?,
public val spdxLicenses: List<SpdxLicense>,
public val scm: Scm?,
public val unknownLicenses: List<UnknownLicense>,
)

@Poko
public class SpdxLicense(
public val identifier: String,
public val name: String,
public val url: String,
)

@Poko
public class Scm(public val url: String)

@Poko
public class UnknownLicense(
public val name: String,
public val url: String,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package io.github.usefulness.licensee

public interface Licensee {

public val artifacts: List<Artifact>
}
1 change: 1 addition & 0 deletions licensee-for-android/api/licensee-for-android.api
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public abstract class io/github/usefulness/licensee/LicenseeFileCopyTask : org/g

public class io/github/usefulness/licensee/LicenseeForAndroidExtension {
public fun <init> (Lorg/gradle/api/model/ObjectFactory;)V
public final fun getAutomaticCoreDependencyManagement ()Lorg/gradle/api/provider/Property;
public final fun getEnableKotlinCodeGeneration ()Lorg/gradle/api/provider/Property;
public final fun getEnableResourceGeneration ()Lorg/gradle/api/provider/Property;
public final fun getGeneratedPackageName ()Lorg/gradle/api/provider/Property;
Expand Down
35 changes: 34 additions & 1 deletion licensee-for-android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ description = "A plugin that generates a list of open source licenses you depend

gradlePlugin {
plugins {
create("gross") {
register("licenseeForAndroid") {
id = "io.github.usefulness.licensee-for-android"
displayName = "`cashapp/licensee` helper for Android"
description = project.description
Expand All @@ -55,4 +55,37 @@ gradlePlugin {
}
}

final GENERATED_PACKAGE_NAME = "io.github.usefulness.licensee.generated"
final GENERATED_BUILD_DIR = layout.buildDirectory.map { it.dir("generated/config/${GENERATED_PACKAGE_NAME.replace('.', '/')}")}

tasks.register("generateBuildConfig") {
final version = project.version
doLast {
def generatedDir = GENERATED_BUILD_DIR.get().asFile
generatedDir.deleteDir()
generatedDir.mkdirs()

final className = "LicenseeForAndroidBuildConfig"
final configClass = new File(generatedDir, "${className}.kt")
BufferedWriter writer = configClass.newWriter()
try {
writer.writeLine("package $GENERATED_PACKAGE_NAME")
writer.writeLine("")
writer.writeLine("internal object $className { internal const val VERSION: String = \"${version.toString()}\" }")
writer.flush()
} finally {
writer.close()
}
}
}

tasks.named("compileKotlin") {
dependsOn("generateBuildConfig")
sourceSets.named("main") {
kotlin.srcDir(GENERATED_BUILD_DIR)
}
}

tasks.named('clean') {
delete GENERATED_BUILD_DIR
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,20 @@ package io.github.usefulness.licensee

import com.squareup.kotlinpoet.ClassName
import com.squareup.kotlinpoet.CodeBlock
import com.squareup.kotlinpoet.LIST
import com.squareup.kotlinpoet.MemberName
import com.squareup.kotlinpoet.TypeSpec
import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy
import com.squareup.kotlinpoet.withIndent
import io.github.usefulness.licensee.core.Artifact
import io.github.usefulness.licensee.serialization.Artifact

internal class ArtifactCodeGenerator(
private val packageName: String,
private val spdxLicensesTypeSpec: TypeSpec,
private val scmTypeSpec: TypeSpec,
private val unknownLicensesTypeSpec: TypeSpec,
) {
internal object ArtifactCodeGenerator {

val entrypointType = coreClassName("Licensee")
val artifactListType = LIST.parameterizedBy(coreClassName("Artifact"))

fun artifactCodeBlock(artifact: Artifact) = CodeBlock.builder()
.withIndent {
addStatement("Artifact(")
addStatement("%T(", ARTIFACT_KCLASS_NAME)
withIndent {
addStatement("groupId = %S,", artifact.groupId)
addStatement("artifactId = %S,", artifact.artifactId)
Expand All @@ -29,7 +28,7 @@ internal class ArtifactCodeGenerator(
addStatement("spdxLicenses = %M(", MemberName("kotlin.collections", "listOf"))
withIndent {
artifact.spdxLicenses.forEach { license ->
addStatement("%T(", ClassName(packageName, spdxLicensesTypeSpec.name!!))
addStatement("%T(", SPDX_LICENSE_KCLASS_NAME)
withIndent {
addStatement("identifier = %S,", license.identifier)
addStatement("name = %S,", license.name)
Expand All @@ -44,7 +43,7 @@ internal class ArtifactCodeGenerator(
if (artifact.scm == null) {
addStatement("scm = null,")
} else {
addStatement("scm = %T(url = %S),", ClassName(packageName, scmTypeSpec.name!!), artifact.scm.url)
addStatement("scm = %T(url = %S),", SCM_KCLASS_NAME, artifact.scm.url)
}

if (artifact.unknownLicenses.isNullOrEmpty()) {
Expand All @@ -53,7 +52,7 @@ internal class ArtifactCodeGenerator(
addStatement("unknownLicenses = %M(", MemberName("kotlin.collections", "listOf"))
withIndent {
artifact.unknownLicenses.forEach { license ->
addStatement("%T(", ClassName(packageName, unknownLicensesTypeSpec.name!!))
addStatement("%T(", UNKNOWN_LICENSE_KCLASS_NAME)
withIndent {
addStatement("name = %S,", license.name)
addStatement("url = %S,", license.url)
Expand All @@ -67,4 +66,11 @@ internal class ArtifactCodeGenerator(
addStatement("),")
}
.build()

private fun coreClassName(name: String) = ClassName("io.github.usefulness.licensee", name)

private val SPDX_LICENSE_KCLASS_NAME = coreClassName("SpdxLicense")
private val SCM_KCLASS_NAME = coreClassName("Scm")
private val UNKNOWN_LICENSE_KCLASS_NAME = coreClassName("UnknownLicense")
private val ARTIFACT_KCLASS_NAME = coreClassName("Artifact")
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ package io.github.usefulness.licensee

import com.squareup.kotlinpoet.CodeBlock
import com.squareup.kotlinpoet.FileSpec
import com.squareup.kotlinpoet.KModifier
import com.squareup.kotlinpoet.MemberName
import com.squareup.kotlinpoet.PropertySpec
import com.squareup.kotlinpoet.TypeSpec
import io.github.usefulness.licensee.core.Artifact
import io.github.usefulness.licensee.serialization.Artifact
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.okio.decodeFromBufferedSource
Expand Down Expand Up @@ -39,42 +40,27 @@ public abstract class CodeGenerationTask : DefaultTask() {
@TaskAction
@ExperimentalSerializationApi
public fun action() {
val packageName = packageName.get()
val licenseeTypesGenerator = LicenseeTypesGenerator(packageName)

FileSpec.builder(packageName, "Artifact")
.addType(licenseeTypesGenerator.spdxLicensesTypeSpec)
.addType(licenseeTypesGenerator.scmTypeSpec)
.addType(licenseeTypesGenerator.unknownLicensesTypeSpec)
.addType(licenseeTypesGenerator.artifactTypeSpec)
.build().writeTo(outputDirectory.asFile.get())

val artifacts = Json.decodeFromBufferedSource<List<Artifact>>(inputFile.asFile.get().source().buffer())

val artifactCodeGenerator = ArtifactCodeGenerator(
packageName = packageName,
spdxLicensesTypeSpec = licenseeTypesGenerator.spdxLicensesTypeSpec,
scmTypeSpec = licenseeTypesGenerator.scmTypeSpec,
unknownLicensesTypeSpec = licenseeTypesGenerator.unknownLicensesTypeSpec,
)

val artifactList = CodeBlock.builder().apply {
addStatement("%M(", MemberName("kotlin.collections", "listOf"))
artifacts.forEach { artifact ->
add(artifactCodeGenerator.artifactCodeBlock(artifact))
add(ArtifactCodeGenerator.artifactCodeBlock(artifact))
}
addStatement(")")
}.build()

val grossType = TypeSpec.objectBuilder("Licensee")
val licenseeType = TypeSpec.objectBuilder("LicenseeForAndroid")
.addSuperinterface(ArtifactCodeGenerator.entrypointType)
.addProperty(
PropertySpec.builder("artifacts", licenseeTypesGenerator.artifactListType)
PropertySpec.builder("artifacts", ArtifactCodeGenerator.artifactListType)
.addModifiers(KModifier.OVERRIDE)
.initializer(artifactList).build(),
)
.build()

FileSpec.builder(packageName, "Licensee")
.addType(grossType)
FileSpec.builder(packageName.get(), "LicenseeForAndroid")
.addType(licenseeType)
.build().writeTo(outputDirectory.asFile.get())
}
}
Loading

0 comments on commit 1eda298

Please sign in to comment.