Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add input type setting to support running analysis with apk files #15

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
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