Updated on 2026-08-14
This commit is contained in:
parent
b9eb3a6ab4
commit
b63f8be2ce
5 changed files with 384 additions and 22 deletions
289
build.gradle.kts
289
build.gradle.kts
|
|
@ -12,6 +12,8 @@ plugins {
|
|||
alias(deps.plugins.room) apply false
|
||||
alias(deps.plugins.kotlin.compose.compiler) apply false
|
||||
alias(deps.plugins.ksp) apply false
|
||||
// Applied (not `apply false`) so the root project gets the aggregate `buildHealth` task.
|
||||
alias(deps.plugins.dependency.analysis)
|
||||
}
|
||||
|
||||
buildscript {
|
||||
|
|
@ -21,6 +23,288 @@ buildscript {
|
|||
}
|
||||
}
|
||||
|
||||
// Dependency Analysis (DAGP) global configuration.
|
||||
dependencyAnalysis {
|
||||
structure {
|
||||
// Treat co-versioned artifact splits as a single logical dependency, so the plugin doesn't
|
||||
// advise "declare the transitive directly" when you depend on one part and use a sibling.
|
||||
// e.g. `serialization-json` always brings `serialization-core` (@Serializable lives there).
|
||||
bundle("kotlinx-serialization") {
|
||||
includeDependency("org.jetbrains.kotlinx:kotlinx-serialization-core")
|
||||
includeDependency("org.jetbrains.kotlinx:kotlinx-serialization-json")
|
||||
}
|
||||
// Hilt/Dagger is split across many artifacts (hilt-android pulls dagger, hilt-core,
|
||||
// javax.inject; hilt-compiler pulls dagger-compiler). Declaring hilt-android + hilt-compiler
|
||||
// is the single entry point — don't advise expanding each transitive separately.
|
||||
bundle("hilt") {
|
||||
primary("com.google.dagger:hilt-android")
|
||||
includeGroup("com.google.dagger")
|
||||
includeDependency("javax.inject:javax.inject")
|
||||
}
|
||||
// `junit-jupiter` is an aggregator over `-api`/`-params`/`-engine`. Tests import the `-api`
|
||||
// package; declaring the `junit-jupiter` aggregator (deps.test.junit5) is the project standard.
|
||||
bundle("junit5") {
|
||||
primary("org.junit.jupiter:junit-jupiter")
|
||||
includeGroup("org.junit.jupiter")
|
||||
}
|
||||
// MockK's DSL functions (every/coEvery/verify) live in `mockk-dsl`, pulled by `mockk`.
|
||||
// Declaring `mockk` (deps.test.mockk) is enough.
|
||||
bundle("mockk") {
|
||||
primary("io.mockk:mockk")
|
||||
includeGroup("io.mockk")
|
||||
}
|
||||
// DataStore is split into datastore-core / datastore-preferences-core / datastore-preferences,
|
||||
// all co-versioned. Declaring the one you use covers the sibling packages.
|
||||
bundle("androidx-datastore") {
|
||||
includeGroup("androidx.datastore")
|
||||
}
|
||||
// web3j is split across core/abi/crypto/tuples (+ its rxjava reactive layer), all co-versioned.
|
||||
// The generated Solidity contract wrappers expose these in their public API; declaring
|
||||
// web3j-core (the entry point) covers the family. rxjava is web3j-only in this repo.
|
||||
bundle("web3j") {
|
||||
primary("org.web3j:core")
|
||||
includeGroup("org.web3j")
|
||||
includeDependency("io.reactivex.rxjava2:rxjava")
|
||||
}
|
||||
// Decompose's public API (ComponentContext etc.) extends Essenty owner interfaces, so the
|
||||
// essenty artifacts are part of decompose's surface. Declaring `decompose` covers them.
|
||||
bundle("decompose") {
|
||||
primary("com.arkivanov.decompose:decompose")
|
||||
includeGroup("com.arkivanov.decompose")
|
||||
includeGroup("com.arkivanov.essenty")
|
||||
}
|
||||
// Jetpack Compose is split across ~15 co-versioned artifacts (ui, ui-graphics, ui-text,
|
||||
// ui-unit, foundation, foundation-layout, animation, animation-core, runtime, runtime-saveable,
|
||||
// material-ripple, …). Treat the whole `androidx.compose.*` family as one logical dependency.
|
||||
bundle("compose") {
|
||||
include("^androidx\\.compose\\..*")
|
||||
}
|
||||
// Co-versioned families split across many artifacts — declaring one covers the siblings.
|
||||
bundle("androidx-lifecycle") {
|
||||
primary("androidx.lifecycle:lifecycle-runtime-ktx")
|
||||
includeGroup("androidx.lifecycle")
|
||||
}
|
||||
bundle("androidx-appcompat") {
|
||||
primary("androidx.appcompat:appcompat")
|
||||
includeGroup("androidx.appcompat")
|
||||
}
|
||||
bundle("androidx-paging") {
|
||||
includeGroup("androidx.paging")
|
||||
}
|
||||
bundle("coil") {
|
||||
includeGroup("io.coil-kt")
|
||||
}
|
||||
// Room is split into runtime/ktx/common/compiler/paging (+ its androidx.sqlite runtime).
|
||||
// Declaring room-runtime covers the siblings.
|
||||
bundle("room") {
|
||||
primary("androidx.room:room-runtime")
|
||||
includeGroup("androidx.room")
|
||||
includeGroup("androidx.sqlite")
|
||||
}
|
||||
}
|
||||
issues {
|
||||
all {
|
||||
// hilt-android / hilt-core get flagged as `api`: Kotlin `internal` @Inject classes (and
|
||||
// public @Inject impls) compile to public bytecode carrying RUNTIME javax.inject annotations,
|
||||
// so DAGP sees javax.inject in the ABI. The Hilt runtime is never genuinely public API — keep
|
||||
// it `implementation` repo-wide.
|
||||
onIncorrectConfiguration {
|
||||
exclude(
|
||||
"com.google.dagger:hilt-android",
|
||||
"com.google.dagger:hilt-core",
|
||||
)
|
||||
}
|
||||
// dagger-compiler is always pulled by hilt-compiler (declared via kapt(deps.hilt.kapt));
|
||||
// no need to declare the annotation processor separately.
|
||||
// :test:core is the documented single entry point for the unit-test stack — it re-exports
|
||||
// junit5/mockk/truth/turbine/coroutines-test as `api`. Because those re-exports are excluded
|
||||
// from the "declare directly" advice above, DAGP sees :test:core itself as unused. It isn't —
|
||||
// keep declaring testImplementation(projects.test.core) and silence the false positive.
|
||||
onUnusedDependencies {
|
||||
exclude(":test:core")
|
||||
}
|
||||
onUsedTransitiveDependencies {
|
||||
exclude("com.google.dagger:dagger-compiler")
|
||||
// jsr305 is a ubiquitous CLASS-retention nullability-annotation transitive (pulled in
|
||||
// by many libs); consumers tolerate its absence, it's never declared directly.
|
||||
exclude("com.google.code.findbugs:jsr305")
|
||||
// :test:core re-exports the unit-test stack as `api` and is the documented single
|
||||
// entry point (testImplementation(projects.test.core)). Don't advise declaring its
|
||||
// re-exports directly in every test module.
|
||||
exclude(
|
||||
"app.cash.turbine:turbine",
|
||||
"com.google.truth:truth",
|
||||
"io.mockk:mockk",
|
||||
"org.jetbrains.kotlinx:kotlinx-coroutines-test",
|
||||
"org.junit.jupiter:junit-jupiter",
|
||||
)
|
||||
// wire-runtime (protobuf) and jakarta.inject-api are deep transitives of the Visa /
|
||||
// WalletConnect (reown) SDKs. A few generated/SDK-facing types reference them, but they're
|
||||
// never declared directly — they always arrive with their owning SDK. Don't advise
|
||||
// declaring them per-module.
|
||||
exclude(
|
||||
"com.squareup.wire:wire-runtime",
|
||||
"jakarta.inject:jakarta.inject-api",
|
||||
)
|
||||
}
|
||||
}
|
||||
// In :libs:auth these are injected only into `internal` classes (DI modules / Default* impls).
|
||||
// DAGP advises `api` because Kotlin `internal` compiles to public bytecode (and Hilt's
|
||||
// generated `_Factory` classes expose the constructor types publicly) — a false positive, not
|
||||
// a real ABI leak. Keep them `implementation`. Scoped to this module so genuine api advice in
|
||||
// other modules still surfaces.
|
||||
project(":libs:auth") {
|
||||
onIncorrectConfiguration {
|
||||
exclude(
|
||||
"com.squareup.moshi:moshi",
|
||||
":core:config-toggles",
|
||||
":core:datasource",
|
||||
":core:utils",
|
||||
)
|
||||
}
|
||||
}
|
||||
// Same Kotlin-`internal`-compiles-to-public false positive: these are used only inside
|
||||
// `internal` classes / DI modules / method bodies (verified), not in the public ABI.
|
||||
project(":libs:blockchain-sdk") {
|
||||
onIncorrectConfiguration {
|
||||
exclude(
|
||||
"androidx.datastore:datastore-preferences",
|
||||
"com.squareup.moshi:moshi",
|
||||
":core:analytics",
|
||||
":core:utils",
|
||||
)
|
||||
}
|
||||
}
|
||||
// config-toggles is used only inside the `internal` DefaultCardSdkFeatureToggles (the public
|
||||
// CardSdkFeatureToggles interface is empty) — internal→public false positive.
|
||||
project(":libs:tangem-sdk-api") {
|
||||
onIncorrectConfiguration {
|
||||
exclude(":core:config-toggles")
|
||||
}
|
||||
}
|
||||
// web3j leaks into the ABI only via incidentally-public generated contract wrappers
|
||||
// (ERC20/TangemPaymentAccount/…), not the module's intended public API (VisaContractInfoProvider
|
||||
// / VisaContractInfo) — keep it `implementation` (see PR review), so silence the `api` advice.
|
||||
project(":libs:visa") {
|
||||
onIncorrectConfiguration {
|
||||
exclude("org.web3j:core")
|
||||
}
|
||||
}
|
||||
// datasource/utils are used only inside the `internal` DI module + Amplitude impl (the public
|
||||
// ABTestsManager interface doesn't expose them) — internal→public false positive.
|
||||
project(":core:ab-tests") {
|
||||
onIncorrectConfiguration {
|
||||
exclude(":core:datasource", ":core:utils")
|
||||
}
|
||||
}
|
||||
// datasource is used only inside `internal` DI modules + LocalTogglesStorage (the public
|
||||
// FeatureTogglesManager API doesn't expose it) — internal→public false positive.
|
||||
project(":core:config-toggles") {
|
||||
onIncorrectConfiguration {
|
||||
exclude(":core:datasource")
|
||||
}
|
||||
}
|
||||
// moshi-polymorphic-adapter is used only in @Provides bodies / as annotation args, never in a
|
||||
// public signature — internal→public false positive, keep it `implementation`.
|
||||
project(":core:datasource") {
|
||||
onIncorrectConfiguration {
|
||||
exclude("dev.onenowy.moshipolymorphicadapter:moshi-polymorphic-adapter")
|
||||
}
|
||||
}
|
||||
// material is consumed only via resources (styles.xml inherits MaterialComponents themes), which
|
||||
// DAGP can't see — it suggests runtimeOnly, but resource linking needs it on the compile
|
||||
// classpath. Keep it `implementation`.
|
||||
project(":core:ui") {
|
||||
onRuntimeOnly {
|
||||
exclude("com.google.android.material:material")
|
||||
}
|
||||
}
|
||||
// core:error exposes UniversalError, a supertype of VisaActivationError that card consumes via
|
||||
// domain:visa:models. The compiler needs the supertype on the classpath, but DAGP sees no direct
|
||||
// reference and flags it unused — false positive, keep it `implementation`.
|
||||
project(":domain:card") {
|
||||
onUnusedDependencies {
|
||||
exclude(":core:error")
|
||||
}
|
||||
}
|
||||
// core:utils is deliberately re-exported as api from the ubiquitous domain:models module so the
|
||||
// many consumers that use TangemLogger / utils through it keep compiling. Demoting it to
|
||||
// implementation would cascade across the whole repo, so silence the incorrect-config advice.
|
||||
project(":domain:models") {
|
||||
onIncorrectConfiguration {
|
||||
exclude(":core:utils")
|
||||
}
|
||||
}
|
||||
// :domain:models is kept as api because domain:markets:models exposes CryptoCurrency.RawID (a
|
||||
// domain:models type) in its public data classes (TokenMarket/RawMarketToken/TokenMarketParams).
|
||||
// DAGP misses this nested-type ABI leak and advises implementation; that advice is a false negative.
|
||||
project(":domain:markets:models") {
|
||||
onIncorrectConfiguration {
|
||||
exclude(":domain:models")
|
||||
}
|
||||
}
|
||||
// :domain:core is kept as api in :domain:legacy and :domain:express because their public APIs
|
||||
// (RampStateManager, ExpressServiceFetcher#getInitializationStatus) return Flow<Lce<...>> where Lce
|
||||
// is a domain:core type. DAGP doesn't trace the generic type argument into the ABI and advises
|
||||
// implementation; that advice is a false negative.
|
||||
project(":domain:legacy") {
|
||||
onIncorrectConfiguration {
|
||||
exclude(":domain:core")
|
||||
}
|
||||
}
|
||||
project(":domain:express") {
|
||||
onIncorrectConfiguration {
|
||||
exclude(":domain:core")
|
||||
}
|
||||
}
|
||||
// :domain:core kept as api in :domain:onramp — public use cases (GetOnrampCurrenciesUseCase etc.)
|
||||
// return EitherFlow<...> (a domain:core alias). DAGP doesn't trace the alias/generic into the ABI.
|
||||
project(":domain:onramp") {
|
||||
onIncorrectConfiguration {
|
||||
exclude(":domain:core")
|
||||
}
|
||||
}
|
||||
// arrow-core kept as api in :domain:onboarding — WasTwinsOnboardingShownUseCase#invoke returns
|
||||
// Flow<Either<...>> (arrow.core.Either in the public ABI). DAGP advises implementation here (false
|
||||
// negative on the generic type argument).
|
||||
project(":domain:onboarding") {
|
||||
onIncorrectConfiguration {
|
||||
exclude("io.arrow-kt:arrow-core")
|
||||
}
|
||||
}
|
||||
// :domain:core kept as api in :domain:wallets — GetUserWalletUseCase#invokeFlow returns
|
||||
// EitherFlow<...> (a domain:core alias). DAGP false-negative on the alias/generic.
|
||||
project(":domain:wallets") {
|
||||
onIncorrectConfiguration {
|
||||
exclude(":domain:core")
|
||||
}
|
||||
}
|
||||
// :domain:models kept as api in :domain:yield-supply:models — YieldMarketToken (public data
|
||||
// class) exposes SerializedBigDecimal (a domain:models type) in public fields. DAGP misses it.
|
||||
project(":domain:yield-supply:models") {
|
||||
onIncorrectConfiguration {
|
||||
exclude(":domain:models")
|
||||
}
|
||||
}
|
||||
// :common is deliberately re-exported as api from common:ui (a ubiquitous UI dependency) so the
|
||||
// many feature modules that use TangemBlogUrlBuilder / common types through it keep compiling.
|
||||
// Demoting it to implementation would cascade across the feature graph.
|
||||
project(":common:ui") {
|
||||
onIncorrectConfiguration {
|
||||
exclude(":common")
|
||||
}
|
||||
}
|
||||
// libs:crypto is deliberately re-exported as api from the ubiquitous :common module so the many
|
||||
// feature modules that use BlockchainUtils / crypto helpers through it keep compiling. Demoting
|
||||
// it to implementation would cascade across the feature graph.
|
||||
project(":common") {
|
||||
onIncorrectConfiguration {
|
||||
exclude(":libs:crypto")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val clean by tasks.registering {
|
||||
delete(rootProject.buildDir)
|
||||
}
|
||||
|
|
@ -37,6 +321,11 @@ val unitTest by tasks.registering {
|
|||
}
|
||||
|
||||
subprojects {
|
||||
// Dependency Analysis (DAGP) registers `projectHealth`/`reason` on each module. In 3.x the
|
||||
// root application no longer auto-applies to subprojects, so apply it here. Reusing the plugin
|
||||
// already resolved by the root `plugins {}` block keeps it in the same classloader as AGP/Kotlin.
|
||||
apply(plugin = "com.autonomousapps.dependency-analysis")
|
||||
|
||||
// App module
|
||||
plugins.withId("com.android.application") {
|
||||
afterEvaluate {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue