Files
encore/app/build.gradle.kts
T
2026-08-08 17:21:17 -04:00

154 lines
6.2 KiB
Kotlin

import java.util.Properties
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
}
// Release signing is loaded from keystore.properties, which exists only on a machine
// that owns the signing key (never committed — see .gitignore). F-Droid and CI have no
// such file and build unsigned; that unsigned output is exactly what F-Droid's
// reproducible-build check compares against before re-applying our signature.
val keystorePropertiesFile = rootProject.file("keystore.properties")
val keystoreProperties =
Properties().apply {
if (keystorePropertiesFile.exists()) keystorePropertiesFile.inputStream().use { load(it) }
}
android {
namespace = "ca.ksamad.encore"
compileSdk = 35
// Pin the build-tools to exactly what Nix supplies. Without this, AGP picks
// its own default version and tries to auto-install it into the read-only
// Nix SDK, which fails. Keep this in sync with flake.nix's buildToolsVersion.
buildToolsVersion = "35.0.0"
defaultConfig {
applicationId = "ca.ksamad.encore"
minSdk = 24
targetSdk = 35
versionCode = 1
versionName = "0.1.0"
}
signingConfigs {
// Present only when the developer keystore is configured; absent on F-Droid/CI.
if (keystorePropertiesFile.exists()) {
create("release") {
storeFile = file(keystoreProperties.getProperty("storeFile"))
storePassword = keystoreProperties.getProperty("storePassword")
keyAlias = keystoreProperties.getProperty("keyAlias")
keyPassword = keystoreProperties.getProperty("keyPassword")
}
}
}
buildTypes {
release {
// Sign with the release key when configured; otherwise stay unsigned so
// F-Droid can build/verify reproducibly and re-apply our signature.
signingConfig = signingConfigs.findByName("release")
// R8 code shrinking + resource shrinking. Critical with
// material-icons-extended: it bundles thousands of vector icons, and
// R8 strips everything we don't reference (debug builds can't shrink,
// so the debug APK stays large — that's expected).
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro",
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
buildFeatures {
compose = true
}
testOptions {
unitTests.all {
// Surface `println`/stdout from unit tests in the Gradle console.
// Handy for the gated MPD integration test (see MpdServerIntegrationTest).
it.testLogging {
showStandardStreams = true
}
}
}
}
// --- Nix reproducible-build support -----------------------------------------
// The Nix dependency-capture step (`nixDownloadDeps`) resolves *every* resolvable
// configuration at once to record what Gradle downloads. Several AGP/Kotlin
// configurations aren't meant to be resolved that way and break the sweep:
// * `*DependenciesMetadata` (Kotlin) resolve with no version under a BOM.
// * the unit-/android-test classpaths carry a `:app -> :app` self-dependency
// that is ambiguous without AGP's own artifact-view based resolution.
// These tweaks apply ONLY during that capture step, detected via the
// IN_GRADLE_UPDATE_DEPS env var the update script sets. Ordinary builds — in the
// dev shell or the real `nix build` (assembleDebug) — see none of this and use
// 100% standard Android configuration, including their own test resolution.
if (System.getenv("IN_GRADLE_UPDATE_DEPS") == "1") {
// Drop the test components entirely for the sweep, so their compile/runtime
// classpaths (which carry the ambiguous `:app -> :app` self-dependency) are
// never created. Removing the component is clean; merely marking those
// classpaths non-resolvable instead makes the Kotlin plugin fail.
androidComponents {
beforeVariants(selector().all()) { variantBuilder ->
variantBuilder.enableAndroidTest = false
variantBuilder.enableUnitTest = false
}
}
// Kotlin's `*DependenciesMetadata` configs resolve with no version under a
// BOM; they pull nothing the real classpaths don't, so skip them.
configurations.configureEach {
if (name.endsWith("DependenciesMetadata")) {
isCanBeResolved = false
}
}
// With the unit-test component gone, still record the test-only artifacts
// (JUnit) via a clean configuration that has no project self-dependency, so
// the captured lockfile is complete enough for `testDebugUnitTest` to run
// offline during `nix build`.
val nixCaptureTestDeps = configurations.create("nixCaptureTestDeps")
dependencies.addProvider(nixCaptureTestDeps.name, libs.junit)
}
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.androidx.lifecycle.viewmodel.compose)
implementation(libs.androidx.activity.compose)
implementation(libs.kotlinx.coroutines.android)
implementation(libs.androidx.datastore.preferences)
implementation(libs.androidx.media)
implementation(libs.coil.compose)
implementation(libs.androidx.palette)
// The Compose BOM aligns every Compose artifact to one tested version set,
// so the individual Compose deps below are declared without versions.
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.ui)
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)
implementation(libs.androidx.material.icons.extended)
debugImplementation(libs.androidx.ui.tooling)
testImplementation(libs.junit)
}