Files
mosaic/mosaic-tty/build.gradle
2025-06-20 20:13:49 +00:00

292 lines
9.0 KiB
Groovy

import com.jakewharton.mosaic.buildsupport.ZigDownloadTask
import java.nio.file.FileSystems
import java.nio.file.Files
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
import static co.touchlab.cklib.gradle.CompileToBitcode.Language.C
import static org.gradle.language.base.plugins.LifecycleBasePlugin.BUILD_GROUP
import static org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.MAIN_COMPILATION_NAME
import static org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.TEST_COMPILATION_NAME
apply plugin: 'org.jetbrains.kotlin.multiplatform'
apply from: "$rootDir/addAllTargets.gradle"
apply from: "$rootDir/publish.gradle"
apply plugin: 'co.touchlab.cklib'
apply plugin: 'com.jakewharton.mosaic.build'
apply plugin: 'de.infolektuell.jextract'
mosaicBuild {
jvmTestDistribution()
patchJavaModuleWithKotlinClasses('mosaic.tty')
}
configurations {
proGuard
r8
}
repositories {
maven {
url 'https://storage.googleapis.com/r8-releases/raw/'
}
}
dependencies {
proGuard 'com.guardsquare:proguard-base:7.7.0'
r8 'com.android.tools:r8:8.9.35'
}
def zigDownload = tasks.register('zigDownload', ZigDownloadTask) {
version = '0.14.1'
outputDir = layout.buildDirectory.dir('zig')
}
jextract.libraries.create('mosaic') {
header = file('src/commonMain/c/mosaic.h')
headerClassName = 'Libmosaic'
targetPackage = 'com.jakewharton.mosaic.tty'
}
kotlin {
explicitApi()
sourceSets {
configureEach {
languageSettings {
optIn('kotlinx.cinterop.ExperimentalForeignApi')
}
}
commonTest {
dependencies {
implementation libs.androidx.annotation
implementation libs.assertk
implementation libs.kotlin.test
implementation libs.kotlinx.coroutines.test
}
}
}
targets.withType(KotlinJvmTarget).configureEach { target ->
def mainCompilation = target.compilations.named(MAIN_COMPILATION_NAME)
def testCompilation = target.compilations.named(TEST_COMPILATION_NAME)
mainCompilation.configure { main ->
def headers = main.compileJavaTaskProvider.flatMap { it.options.headerOutputDirectory }
def jniTask = tasks.register("${target.name}JniZigBuild", Exec) {
inputs.file('build.zig')
inputs.dir('src/commonMain/c')
inputs.dir("src/${target.name}Main/c")
inputs.dir("src/${target.name}Main/include")
def jniDir = layout.buildDirectory.dir("zigJni/${target.name}")
outputs.dir(jniDir)
dependsOn(headers)
args('build', '-p', jniDir.get().asFile.absolutePath, "-Djavah=${projectDir.relativePath(headers.get().asFile)}")
dependsOn(zigDownload)
executable(zigDownload.get().zigExecutable.path)
}
main.defaultSourceSet.resources.srcDir(jniTask)
}
def jdk22Compilation = target.compilations.create('jdk22') {
// I can't find a way to add a Java srcDir, so we have to add it both to Kotlin task so its
// sources can link and to Java task so the files are actually compiled to classes.
def mosaicFfmJava = tasks.named('jextract')
defaultSourceSet.kotlin.srcDir(mosaicFfmJava)
compileJavaTaskProvider.configure { task ->
task.source(mosaicFfmJava)
}
compileTaskProvider.configure { task ->
task.compilerOptions { options ->
options.jvmTarget = JvmTarget.JVM_22
options.freeCompilerArgs.add('-Xexplicit-api=strict')
}
}
compileJavaTaskProvider.configure { task ->
task.sourceCompatibility = '22'
task.targetCompatibility = '22'
}
associateWith(mainCompilation.get())
}
def mainJarTask = tasks.named(target.artifactsTaskName, Jar) {
from(jdk22Compilation.output) {
into('META-INF/versions/22')
// Don't add a nested .kotlin_module file.
exclude('META-INF')
// Empty class noise produced by jextract.
exclude('**/_*.class')
}
manifest {
attributes(['Multi-Release': 'true'])
}
}
def mainJar = mainJarTask.flatMap { it.archiveFile }
def testJar = tasks.register("${target.name}TestJar", Jar) {
from(testCompilation.map { it.output.allOutputs })
archiveBaseName = base.archivesName.map { it + '-' + target.name }
archiveClassifier = 'tests'
}.flatMap { it.archiveFile }
def testDependencyFiles = testCompilation.map { it.runtimeDependencyFiles }
def r8Rules = layout.buildDirectory.file('shrinker/r8.txt')
def r8RulesExtract = tasks.register("${target.name}ExtractR8Rules", JavaExec) {
inputs.files(mainJar)
inputs.files(testJar)
inputs.files(configurations.r8)
inputs.files(testDependencyFiles)
outputs.file(r8Rules.get())
classpath(configurations.r8)
mainClass = 'com.android.tools.r8.ExtractR8Rules'
args = [
'--rules-output', r8Rules.get().asFile.absolutePath,
'--include-origin-comments',
]
doFirst {
// Defer resolving this until execution time since JavaExec lacks provider-based args.
testDependencyFiles.get().files.findAll {it.isFile() }.forEach {
args += it.absolutePath
}
args += mainJar.get().asFile.absolutePath
args += testJar.get().asFile.absolutePath
}
}
def r8Jar = base.archivesName.flatMap { archivesName ->
layout.buildDirectory.file("libs/$archivesName-${target.name}-$version-testsR8.jar")
}
def r8Task = tasks.register("${target.name}TestJarR8", JavaExec) {
group = BUILD_GROUP
description = 'Assembles an archive containing the test classes run through ProGuard.'
inputs.files(configurations.r8)
inputs.files(testDependencyFiles)
inputs.files(r8RulesExtract)
outputs.file(r8Jar.get())
classpath(configurations.r8)
mainClass = 'com.android.tools.r8.R8'
args = [
'--classfile',
'--output', r8Jar.get().asFile.absolutePath,
'--pg-conf', r8Rules.get().asFile.absolutePath,
'--lib', System.properties['java.home'].toString(),
]
doFirst {
// Defer resolving this until execution time since JavaExec lacks provider-based args.
testDependencyFiles.get().files.findAll {it.isFile() }.forEach {
args += it.absolutePath
}
args += mainJar.get().asFile.absolutePath
args += testJar.get().asFile.absolutePath
}
doLast {
// Quick work around for https://issuetracker.google.com/issues/134372167.
try (def fs = FileSystems.newFileSystem(r8Jar.get().asFile.toPath(), (ClassLoader) null)) {
def root = fs.rootDirectories.first()
Files.delete(root.resolve('module-info.class'))
Files.delete(root.resolve('META-INF/versions/9/module-info.class'))
}
}
}
target.testRuns.create("r8") { run ->
run.executionTask.configure {
dependsOn(r8Task)
}
run.setExecutionSourceFrom(
project.files(r8Jar.get()),
testDependencyFiles.get().filter { it.isDirectory() },
)
}
def proGuardJar = base.archivesName.flatMap { archivesName ->
layout.buildDirectory.file("libs/$archivesName-${target.name}-$version-testsProGuard.jar")
}
def proGuardTask = tasks.register("${target.name}TestJarProGuard", JavaExec) {
group = BUILD_GROUP
description = 'Assembles an archive containing the test classes run through ProGuard.'
inputs.files(configurations.proGuard)
inputs.files(testDependencyFiles)
outputs.file(proGuardJar.get())
classpath(configurations.proGuard)
mainClass = 'proguard.ProGuard'
args = [
'-libraryjars', '<java.home>/jmods/java.base.jmod(!**.jar;!module-info.class)',
// TODO These should be pulled from the jars, but for now this unblocks us.
'@src/jvmMain/resources/META-INF/proguard/com.jakewharton.mosaic-tty.pro',
'@src/jvmTest/resources/META-INF/proguard/com.jakewharton.mosaic-tty-tests.pro',
]
doFirst {
// Defer resolving this until execution time since JavaExec lacks provider-based args.
args += '-injars'
args += testDependencyFiles.get().files.findAll { it.exists() }.join(File.pathSeparator)
// These do not need deferred, but since it must come after -injars it has to go here.
args += '-outjars'
args += proGuardJar.get().asFile.toString()
}
}
target.testRuns.create("proGuard") { run ->
run.executionTask.configure {
dependsOn(proGuardTask)
}
run.setExecutionSourceFrom(
project.files(proGuardJar.get()),
testDependencyFiles.get().filter { it.isDirectory() },
)
}
// Adding additional test runs somehow removes the configuration capabilities which allow
// automatic resolution of the junit test dependency. Add it explicitly instead.
testCompilation.configure {
dependencies {
implementation(libs.kotlin.test.junit)
}
}
}
def linkNativeDebugTests = tasks.register('linkNativeDebugTests')
targets.withType(KotlinNativeTarget).configureEach {
compilations.main.cinterops {
create('mosaic') {
header(file('src/commonMain/c/mosaic.h'))
packageName('com.jakewharton.mosaic.tty')
compilerOpts(
'-std=gnu11',
'-Wall',
'-Wextra',
'-Werror',
)
}
}
linkNativeDebugTests.configure {
it.dependsOn(compilations.test.binariesTaskName)
}
}
compilerOptions.freeCompilerArgs.add('-Xexpect-actual-classes')
}
cklib {
config.kotlinVersion = libs.versions.kotlin.get()
create('mosaic', file('src/commonMain/c'), ['main']) {
it.srcDirs = files('src/commonMain/c')
it.language = C
}
}