Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,4 @@ jobs:
- name: Build sample app
run: |
cd ./sample
./gradlew app:appSizeAnalysisProRelease --stacktrace -Dorg.gradle.debug=false --no-daemon
./gradlew app:aabSizeAnalysisProRelease --stacktrace -Dorg.gradle.debug=false --no-daemon
12 changes: 8 additions & 4 deletions docs/plugin.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ appSizer {

### Run the analysis

```bash
./gradlew app:apkSizeAnalysis[Release|Debug] --no-configure-on-demand --no-configuration-cache
```
Or if you need to analyze apk files generated from the aab file according to device specs:
```bash
./gradlew app:appSizeAnalysis[Release|Debug] --no-configure-on-demand --no-configuration-cache
```
Expand Down Expand Up @@ -87,8 +91,8 @@ appSizer {
variantFilter { variant ->
variant.setIgnore(variant.flavors.contains("your-ignore-flavor"))
}
apk {
// APK Generation
aab {
// APK Generation from aab
}
Copy link
Contributor

@MinhNguyen-nvm MinhNguyen-nvm Feb 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On the other comment, I mean, let's keep the "apk" extension as well

}
...
Expand Down Expand Up @@ -122,7 +126,7 @@ Configure APK generation settings:
appSizer {
projectInput {
...
apk {
aab {
deviceSpecs = [
file("path/to/device-1.json"),
file("path/to/device-2.json"),
Expand Down Expand Up @@ -186,7 +190,7 @@ appSizer {
appSizer {
enabled = true
projectInput {
apk {
aab {
bundleToolFile = file("${rootProject.rootDir}/binary/bundletool-all-1.15.4.jar")
deviceSpecs = [
file("${rootProject.rootDir}/app-size-config/device-1.json"),
Expand Down
2 changes: 2 additions & 0 deletions docs/task_graph.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ flowchart TD
A(generateApkDebug)
B(generateArchiveDepDebug)
C(appSizeAnalysisDebug)
D(apkSizeAnalysisDebug)


C --> A
C --> B
D --> B
```
6 changes: 5 additions & 1 deletion sample/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ appSizer {
To run the App Sizer analysis using the Gradle plugin:

1. Open a terminal in the sample project directory.
2. Execute the following command:
2. Execute one of the following commands:
```
./gradlew app:apkSizeAnalysisProRelease --no-configure-on-demand
```
If you need to analyze apk files generated from the aab file according to device specs:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let keeps the appSizeAnalysis* as the default. I would propose to have

2. Execute the following command to analyze the apks generated from the aab file according to device specs:

./gradlew app:appSizeAnalysisProRelease --no-configure-on-demand

Or if you need to analyze apk file only, you can execute the following command:

./gradlew app:apkSizeAnalysisProRelease --no-configure-on-demand

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

```
./gradlew app:appSizeAnalysisProRelease --no-configure-on-demand
```
Expand Down
2 changes: 1 addition & 1 deletion sample/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ apply plugin: "com.grab.sizer"
appSizer {
enabled = true
projectInput {
apk {
aab {
bundleToolFile = file("${rootProject.rootDir}/binary/bundletool-all-1.15.4.jar")
deviceSpecs = [
file("${rootProject.rootDir}/app-size-config/device-1.json"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
package com.grab.plugin.sizer

import com.android.build.gradle.AppExtension
import com.android.build.gradle.api.ApplicationVariant
import com.android.build.gradle.api.BaseVariant
import com.android.build.gradle.internal.dsl.BuildType
import com.android.build.gradle.internal.dsl.ProductFlavor
Expand All @@ -44,7 +45,9 @@ import com.grab.plugin.sizer.utils.isKotlinJvm
import org.gradle.api.Project
import org.gradle.api.Task
import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.file.Directory
import org.gradle.api.tasks.TaskProvider
import org.gradle.kotlin.dsl.listProperty
import org.gradle.kotlin.dsl.the

/*
Expand All @@ -71,38 +74,67 @@ internal class TaskManager(
val variantFilter = DefaultVariantFilter(variant)
pluginExtension.input.variantFilter?.execute(variantFilter)
if (!variantFilter.ignored) {
val generateApkTask = GenerateApkTask.registerTask(
project,
pluginExtension,
variant
)

val generateArchivesListTask = GenerateArchivesListTask.registerTask(
project,
project = project,
variant = variant,
flavorMatchingFallbacks = getProductFlavor(variant)?.matchingFallbacks ?: emptyList(),
buildTypeMatchingFallbacks = getOriginalBuildType(variant).matchingFallbacks,
enableMatchDebugVariant = pluginExtension.input.enableMatchDebugVariant
)

val appSizeAnalysisTask = AppSizeAnalysisTask.registerTask(
project,
variant,
pluginExtension,
generateApkTask,
generateArchivesListTask,
)
registerAppSizeTaskDep(project, variant, this, appSizeAnalysisTask)
val aabSizeAnalysisTask = createAabAnalysisTask(project, variant, generateArchivesListTask)
val apkSizeAnalysisTask = createApkAnylysisTask(project, variant, generateArchivesListTask)

registerAppSizeTaskDep(project, variant, this, listOf(aabSizeAnalysisTask, apkSizeAnalysisTask))
}
}
}
}

private fun createApkAnylysisTask(
project: Project,
variant: ApplicationVariant,
generateArchivesListTask: TaskProvider<GenerateArchivesListTask>
) = AppSizeAnalysisTask.registerTask(
name = "apk",
project = project,
variant = variant,
pluginExtension = pluginExtension,
apkDirectories = variant.packageApplicationProvider.map {
project.objects.listProperty<Directory>().value(listOf(it.outputDirectory.get()))
},
generateArchivesListTask = generateArchivesListTask,
)

private fun createAabAnalysisTask(
project: Project,
variant: ApplicationVariant,
generateArchivesListTask: TaskProvider<GenerateArchivesListTask>
): TaskProvider<AppSizeAnalysisTask> {
val generateApkFromAabTask = GenerateApkTask.registerTask(
project,
pluginExtension,
variant
)

val aabSizeAnalysisTask = AppSizeAnalysisTask.registerTask(
name = "aab",
project = project,
variant = variant,
pluginExtension = pluginExtension,
apkDirectories = generateApkFromAabTask.map { it.outputDirectories },
generateArchivesListTask = generateArchivesListTask,
)

return aabSizeAnalysisTask
}

private fun registerAppSizeTaskDep(
project: Project,
variant: BaseVariant,
appExtension: AppExtension,
depTask: TaskProvider<out Task>
depTasks: List<TaskProvider<out Task>>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could reduce the task dependencies graph by adding a dummy task that depends on other jar/aar generation tasks. And let the App size analysis & APK analysis tasks depend on that single task.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know about the improvement before. Could you please explain how it works?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that generateArchivesListTask should be run after the assemble task, because it takes a lot of memory to run two tasks in parallel

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My idea is something like:

val compileDependenciesTask = project.tasks.register("compileDepe${variant.name}")
 aabSizeAnalysisTask.dependsOn(compileDependenciesTask)
 apkSizeAnalysisTask.dependsOn(compileDependenciesTask)

Then let compileDependenciesTask depend on the module's compilation tasks.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that generateArchivesListTask should be run after the assemble task, because it takes a lot of memory to run two tasks in parallel

Do you face OOM error? Let treat it as a separated issue

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah. ok, i will create another PR after this one

) {
val dependenciesComponent = DaggerDependenciesComponent.factory().create(
project = project,
Expand All @@ -112,46 +144,46 @@ internal class TaskManager(
enableMatchDebugVariant = pluginExtension.input.enableMatchDebugVariant
)
val markAsChecked = mutableSetOf<String>()
dfs(project, markAsChecked, dependenciesComponent, depTask)
dfs(project, markAsChecked, dependenciesComponent, depTasks)
}

private fun dfs(
project: Project,
markAsChecked: MutableSet<String>,
dependenciesComponent: DependenciesComponent,
depTask: TaskProvider<out Task>
depTasks: List<TaskProvider<out Task>>
) {
if (markAsChecked.contains(project.path)) return
markAsChecked.add(project.path)
handleSubProject(project, depTask, dependenciesComponent.variantExtractor())
handleSubProject(project, depTasks, dependenciesComponent.variantExtractor())
dependenciesComponent.configurationExtractor()
.runtimeConfigurations(project)
.flatMap { configuration ->
configuration.dependencies.withType(ProjectDependency::class.java)
}.forEach {
dfs(it.dependencyProject, markAsChecked, dependenciesComponent, depTask)
dfs(it.dependencyProject, markAsChecked, dependenciesComponent, depTasks)
}
}

private fun handleSubProject(
project: Project,
task: TaskProvider<out Task>,
tasks: List<TaskProvider<out Task>>,
variantExtractor: VariantExtractor
) {
when {
project.isAndroidLibrary -> {
val variant = variantExtractor.findMatchVariant(project)
if (variant is AndroidAppSizeVariant) {
task.dependsOn(variant.baseVariant.assembleProvider)
tasks.forEach { it.dependsOn(variant.baseVariant.assembleProvider) }
}
}

project.isKotlinJvm -> {
task.dependsOn(project.tasks.named("jar"))
tasks.forEach { it.dependsOn(project.tasks.named("jar")) }
}

project.isJava -> {
task.dependsOn(project.tasks.named("jar"))
tasks.forEach { it.dependsOn(project.tasks.named("jar")) }
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,13 @@

package com.grab.plugin.sizer.configuration

import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.ListProperty
import java.io.File
import javax.inject.Inject

open class ApkGeneratorExtension @Inject constructor(objects: ObjectFactory) {
open class AabGeneratorExtension @Inject constructor(objects: ObjectFactory) {
val bundleToolFile: RegularFileProperty = objects.fileProperty()
val deviceSpecs: ListProperty<File> = objects.listProperty(File::class.java)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,26 +38,30 @@ import javax.inject.Inject
private const val DEFAULT_LARGE_FILE = 10240L // 10kb

open class InputExtension @Inject constructor(objects: ObjectFactory) {
val apk: ApkGeneratorExtension = objects.newInstance(ApkGeneratorExtension::class.java, objects)
val aab: AabGeneratorExtension = objects.newInstance(AabGeneratorExtension::class.java, objects)
val teamMappingFile: RegularFileProperty = objects.fileProperty()
var variantFilter: Action<VariantFilter>? = null
var largeFileThreshold: Long = DEFAULT_LARGE_FILE
var enableMatchDebugVariant = false


fun variantFilter(action: Action<VariantFilter>) {
variantFilter = action
}

fun apk(action: Action<in ApkGeneratorExtension>) {
action.execute(apk)
fun aab(action: Action<in AabGeneratorExtension>) {
action.execute(aab)
}

fun apk(block: ApkGeneratorExtension.() -> Unit) {
block(apk)
fun aab(block: AabGeneratorExtension.() -> Unit) {
block(aab)
}
}

enum class InputType {
AAB,
APK
}

interface VariantFilter {
fun setIgnore(ignore: Boolean)
val buildType: BuildType
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,13 @@ import com.grab.sizer.report.db.InfluxDBConfig
import org.gradle.api.DefaultTask
import org.gradle.api.Project
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.Directory
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.RegularFileProperty
import org.gradle.api.provider.ListProperty
import org.gradle.api.provider.MapProperty
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.*
import java.io.File

Expand Down Expand Up @@ -157,17 +160,18 @@ internal abstract class AppSizeAnalysisTask : DefaultTask() {

companion object {
fun registerTask(
name: String = "app",
project: Project,
variant: BaseVariant,
pluginExtension: AppSizePluginExtension,
generateApkTask: TaskProvider<GenerateApkTask>,
apkDirectories: Provider<ListProperty<Directory>>,
generateArchivesListTask: TaskProvider<GenerateArchivesListTask>,
): TaskProvider<AppSizeAnalysisTask> {
return project.tasks.register(
"appSizeAnalysis${variant.name.capitalize()}", AppSizeAnalysisTask::class.java
"${name}SizeAnalysis${variant.name.capitalize()}", AppSizeAnalysisTask::class.java
) {
this.variantInput.set(variant.toVariantInput())
this.apkDirectories.setFrom(generateApkTask.map { it.outputDirectories })
this.apkDirectories.setFrom(apkDirectories)
this.archiveDepJsonFile.set(generateArchivesListTask.map { it.archiveDepFile.get() })
this.libName.set(project.params().libraryName())
this.option.set(project.params().option())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,8 @@ internal abstract class GenerateApkTask : DefaultTask() {
): TaskProvider<GenerateApkTask> {
val bundleTask = project.tasks.named("sign${variant.name.capitalize()}Bundle")
val task = project.tasks.register("generateApk${variant.name.capitalize()}", GenerateApkTask::class.java) {
deviceSpecFiles.setFrom(extension.input.apk.deviceSpecs)
bundleToolFile.set(extension.input.apk.bundleToolFile)
deviceSpecFiles.setFrom(extension.input.aab.deviceSpecs)
bundleToolFile.set(extension.input.aab.bundleToolFile)
appBundleFile.set(
bundleTask.map {
(it as FinalizeBundleTask).finalBundleFile.get()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import java.io.File

private const val EXT_AAR = "aar"
private const val EXT_JAR = "jar"
private const val EXT_APK = "apk"

class PluginInputProvider(
private val archiveDependencyStore: ArchiveDependencyStore,
Expand Down Expand Up @@ -79,7 +80,8 @@ class PluginInputProvider(
.filter { it.file.extension.equals(EXT_AAR, true) }

override fun provideApkFiles(): Sequence<File> {
return apksDirectory.listFiles()?.asSequence() ?: emptySequence()
return apksDirectory.listFiles()?.asSequence()?.filter { it.extension.equals(EXT_APK, true) }
?: emptySequence()
}

override fun provideR8MappingFile(): File? = r8MappingFile
Expand Down
Loading