Skip to content

Commit 040e8ff

Browse files
Merge remote-tracking branch 'github/prevent-extractvariant-exception'
2 parents 08f7429 + f5f84fd commit 040e8ff

16 files changed

Lines changed: 228 additions & 77 deletions

File tree

.gitignore

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,3 @@ build/
1616
*.iws
1717
.java-version
1818

19-
# Documentation folder - contains drafts, AI-generated content, and other private content not intended for public repo
20-
docs/

app-sizer/src/main/kotlin/com/grab/sizer/parser/ProguardFileParser.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,9 @@ class DefaultProguardFileParser @Inject constructor(
5858
readFromReader(InputStreamReader(it, Charsets.UTF_8))
5959
}
6060
} catch (e: IOException) {
61-
logger.log(e)
61+
logger.log("Parse mapping file failure", e)
6262
} catch (e: ParseException) {
63-
logger.log(e)
63+
logger.log("Parse mapping file failure", e)
6464
}
6565
}
6666
}

app-sizer/src/main/kotlin/com/grab/sizer/utils/Logger.kt

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,13 @@ const val DEFAULT_TAG = "AppSize"
3232

3333
interface Logger {
3434
fun log(tag: String, message: String)
35-
fun log(tag: String, e: Exception)
36-
fun logDebug(tag: String, message: String)
35+
fun log(tag: String, message: String, e: Exception)
3736
}
3837

39-
fun Logger.logDebug(message: String) {
40-
logDebug(DEFAULT_TAG, message)
41-
}
4238
fun Logger.log(message: String) {
4339
log(DEFAULT_TAG, message)
4440
}
4541

46-
fun Logger.log(e: Exception) {
47-
log(DEFAULT_TAG, e)
42+
fun Logger.log(message: String, e: Exception) {
43+
log(DEFAULT_TAG, message, e)
4844
}

cli/src/main/kotlin/com/grab/sizer/utils/CliLogger.kt

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,8 @@ class CliLogger : Logger {
3232
println("$tag : $message")
3333
}
3434

35-
override fun log(tag: String, e: Exception) {
36-
println("$tag :")
37-
e.printStackTrace()
38-
}
39-
40-
override fun logDebug(tag: String, message: String) {
35+
override fun log(tag: String, message: String, e: Exception) {
4136
println("$tag : $message")
37+
e.printStackTrace()
4238
}
4339
}

docs/plugin.md

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,14 +231,76 @@ appSizer {
231231
<img src="../images/task-graph.png" width="80%">
232232
</p>
233233

234+
## Known Limitations
235+
236+
### Variant Matching and Module Skipping
237+
238+
The App Sizer plugin uses sophisticated variant matching to analyze dependencies across different project types. However, some scenarios may result in modules being skipped during analysis, which can impact the accuracy of the final results.
239+
240+
#### When Modules Are Skipped
241+
242+
The plugin may skip modules in the following scenarios:
243+
244+
1. **Unsupported Project Types**: Projects that don't match any supported type (Android app/library, Java/Kotlin JVM, Kotlin Multiplatform)
245+
2. **Missing Build Variants**: Android library modules that lack variants matching the main app's flavor/buildType configuration
246+
4. **Custom Build Logic**: Modules using non-standard build configurations that the plugin cannot interpret
247+
248+
249+
#### Error Handling Behavior
250+
251+
The plugin uses defensive error handling to maintain build stability while logging informative warnings:
252+
253+
```
254+
AppSize: Skipping project module-name - variant extraction failed: Cannot find matching variant for module-name
255+
AppSize: Skipping dependency project library-name - unsupported type: Project type not supported: library-name
256+
AppSize: Could not find matching variant for Android library project module-name: Cannot find matching variant for module-name
257+
AppSize: Unsupported project type for Android library project module-name: Project type not supported: module-name
258+
```
259+
260+
When debug logging is enabled (`--debug` flag), full stack traces are available for detailed troubleshooting:
261+
262+
```
263+
AppSize: Full stack trace for variant extraction failure:
264+
java.lang.IllegalStateException: Cannot find matching variant for module-name
265+
at com.grab.plugin.sizer.dependencies.DefaultVariantExtractor.extractVariant(VariantExtractor.kt:284)
266+
at com.grab.plugin.sizer.dependencies.DefaultVariantExtractor.defaultFindMatchVariant(VariantExtractor.kt:132)
267+
[... full stack trace ...]
268+
```
269+
270+
#### Impact on Analysis Results
271+
272+
All classes and resources belonging to skipped modules will be automatically attributed to the app module during analysis, which may impact the accuracy of module-wise and team-based size breakdowns.
273+
274+
234275
## Troubleshooting
235276

277+
### Common Variant Matching Issues
278+
279+
1. **"Cannot find matching variant for [module-name]"**
280+
- **Cause**: Library module lacks a variant matching the app's configuration
281+
- **Solution**: Add `matchingFallbacks` to the library module or ensure consistent flavor/buildType naming
282+
283+
2. **"Unsupported project type: [project-name]"**
284+
- **Cause**: Project doesn't use a supported plugin type
285+
- **Solution**: Verify the project applies Android, Java, or Kotlin plugin correctly
286+
287+
3. **"Skipping dependency project [library-name]"**
288+
- **Cause**: External or internal dependency has configuration issues
289+
- **Solution**: Check dependency's build configuration and ensure it's compatible
290+
236291
### Resource Verification Failures
237292
If you encounter issues with the `verifyResourceRelease` task, try these solutions:
238293
- Check that your resource files are properly formatted and located
239294
- Verify that resource names follow Android naming conventions
240295
- Enable the `enableMatchDebugVariant` flag in your configuration
241296

297+
### Missing Analysis Data
298+
299+
If modules appear to be missing from your analysis reports:
300+
301+
1. **Check Warning Logs**: Look for "Skipping project" messages in build output
302+
2. **Enable Debug Mode**: Run with `--debug` to get detailed variant matching information
303+
242304
### Dagger NoSuchMethodError
243305
If you encounter this exception:
244306
```java
@@ -252,6 +314,16 @@ This error typically occurs due to a version conflict between the Android build
252314
classpath "com.google.dagger:dagger:2.47"
253315
```
254316

317+
### Debug Mode Analysis
318+
319+
Run with debug logging to get detailed information about module processing, variant matching, and potential issues:
320+
321+
```bash
322+
./gradlew appSizeAnalysisRelease --debug 2>&1 | grep -E "(Skipping|variant|extraction)"
323+
```
324+
325+
This will filter the output to show only variant matching and module processing information.
326+
255327
## Resources
256328

257329
- [Bundletool GitHub Repository](https://github.com/google/bundletool)

sizer-gradle-plugin/src/main/kotlin/com/grab/plugin/sizer/AppSizerPlugin.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
package com.grab.plugin.sizer
2929

30+
import com.grab.plugin.sizer.utils.DefaultPluginLogger
3031
import org.gradle.api.Plugin
3132
import org.gradle.api.Project
3233

@@ -36,6 +37,7 @@ class AppSizerPlugin : Plugin<Project> {
3637
override fun apply(project: Project) =
3738
TaskManager(
3839
project,
39-
project.extensions.create(PLUGIN_EXTENSION, AppSizePluginExtension::class.java)
40+
project.extensions.create(PLUGIN_EXTENSION, AppSizePluginExtension::class.java),
41+
DefaultPluginLogger(project)
4042
).configTasks()
4143
}

sizer-gradle-plugin/src/main/kotlin/com/grab/plugin/sizer/TaskManager.kt

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,7 @@ import com.grab.plugin.sizer.dependencies.*
3737
import com.grab.plugin.sizer.tasks.AppSizeAnalysisTask
3838
import com.grab.plugin.sizer.tasks.GenerateApkTask
3939
import com.grab.plugin.sizer.tasks.GenerateArchivesListTask
40-
import com.grab.plugin.sizer.utils.isAndroidApplication
41-
import com.grab.plugin.sizer.utils.isAndroidLibrary
42-
import com.grab.plugin.sizer.utils.isJava
43-
import com.grab.plugin.sizer.utils.isKotlinJvm
44-
import com.grab.plugin.sizer.utils.isKotlinMultiplatform
40+
import com.grab.plugin.sizer.utils.*
4541
import org.gradle.api.Project
4642
import org.gradle.api.Task
4743
import org.gradle.api.artifacts.ProjectDependency
@@ -57,7 +53,8 @@ import org.gradle.kotlin.dsl.the
5753
*/
5854
internal class TaskManager(
5955
private val project: Project,
60-
private val pluginExtension: AppSizePluginExtension
56+
private val pluginExtension: AppSizePluginExtension,
57+
private val logger: PluginLogger
6158
) {
6259
fun configTasks() {
6360
project.rootProject.gradle.projectsEvaluated {
@@ -147,8 +144,12 @@ internal class TaskManager(
147144
if (variant is AndroidAppSizeVariant) {
148145
task.dependsOn(variant.baseVariant.assembleProvider)
149146
}
150-
} catch (e: RuntimeException) {
151-
project.logger.warn("Could not find matching variant for Android library project ${project.name}: ${e.message}")
147+
} catch (e: UnsupportedOperationException) {
148+
logger.warn("Unsupported project type for Android library project ${project.name}: ${e.message}")
149+
logger.debug("Full stack trace for variant extraction failure:", e)
150+
} catch (e: IllegalStateException) {
151+
logger.warn("Could not find matching variant for Android library project ${project.name}: ${e.message}")
152+
logger.debug("Full stack trace for variant extraction failure:", e)
152153
}
153154
}
154155

@@ -163,10 +164,10 @@ internal class TaskManager(
163164
project.isKotlinMultiplatform -> {
164165
task.dependsOn(project.tasks.named(KMP_JAR_TASK))
165166
}
166-
167+
167168
else -> {
168169
// Skip unsupported project types to avoid variant extraction errors
169-
project.logger.debug("Skipping variant extraction for unsupported project type: ${project.name}")
170+
logger.warn("Skipping variant extraction for unsupported project type: ${project.name}")
170171
}
171172
}
172173

sizer-gradle-plugin/src/main/kotlin/com/grab/plugin/sizer/dependencies/ArchiveExtractor.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,23 @@ import org.gradle.api.Project
3636
import javax.inject.Inject
3737

3838
interface ArchiveExtractor {
39+
/**
40+
* Extracts archive dependency from a project.
41+
*
42+
* @param project The project to extract archive dependency from
43+
* @return ArchiveDependency representing the project's binary output
44+
* @throws UnsupportedOperationException if the project type is unsupported
45+
* @throws IllegalStateException if variant extraction fails
46+
*/
47+
@Throws(UnsupportedOperationException::class, IllegalStateException::class)
3948
fun extract(project: Project): ArchiveDependency
4049
}
4150

4251
@DependenciesScope
4352
internal class DefaultArchiveExtractor @Inject constructor(
4453
private val variantExtractor: VariantExtractor
4554
) : ArchiveExtractor {
55+
@Throws(UnsupportedOperationException::class, IllegalStateException::class)
4656
override fun extract(project: Project): ArchiveDependency {
4757
val matchVariant = variantExtractor.findMatchVariant(project)
4858
return when {
@@ -61,7 +71,7 @@ internal class DefaultArchiveExtractor @Inject constructor(
6171
pathToArtifact = matchVariant.binaryOutPut.path
6272
)
6373

64-
else -> throw IllegalArgumentException("The ${project.name} is not an Android/Kotlin/Java module")
74+
else -> throw UnsupportedOperationException("Unsupported project type: ${project.name}")
6575
}
6676
}
6777
}

sizer-gradle-plugin/src/main/kotlin/com/grab/plugin/sizer/dependencies/DefaultConfigurationExtractor.kt

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@
2727

2828
package com.grab.plugin.sizer.dependencies
2929

30+
import com.grab.plugin.sizer.utils.PluginLogger
31+
import com.grab.plugin.sizer.utils.debug
32+
import com.grab.plugin.sizer.utils.warn
3033
import org.gradle.api.Project
3134
import org.gradle.api.artifacts.Configuration
3235
import javax.inject.Inject
@@ -37,14 +40,22 @@ interface ConfigurationExtractor {
3740

3841
@DependenciesScope
3942
internal class DefaultConfigurationExtractor @Inject constructor(
40-
private val variantExtractor: VariantExtractor
43+
private val variantExtractor: VariantExtractor,
44+
private val logger: PluginLogger
4145
) : ConfigurationExtractor {
4246
override fun runtimeConfigurations(project: Project): Sequence<Configuration> {
43-
val variant = variantExtractor.findMatchVariant(project)
44-
return project.configurations.asSequence()
45-
.filter {
46-
variant.runtimeConfiguration.hierarchy.contains(it)
47-
}
47+
return try {
48+
val variant = variantExtractor.findMatchVariant(project)
49+
project.configurations.asSequence()
50+
.filter {
51+
variant.runtimeConfiguration.hierarchy.contains(it)
52+
}
53+
} catch (e: RuntimeException) {
54+
logger.warn("Could not find matching variant for project ${project.name}: ${e.message}")
55+
logger.debug("Full stack trace for variant extraction failure:", e)
56+
// Return empty sequence if variant extraction fails
57+
emptySequence()
58+
}
4859
}
4960
}
5061

sizer-gradle-plugin/src/main/kotlin/com/grab/plugin/sizer/dependencies/DependenciesComponent.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
package com.grab.plugin.sizer.dependencies
2929

30+
import com.grab.plugin.sizer.utils.DefaultPluginLogger
3031
import com.grab.plugin.sizer.utils.PluginLogger
3132
import com.grab.sizer.utils.Logger
3233
import dagger.Binds
@@ -50,8 +51,6 @@ internal interface DependenciesComponent {
5051
fun configurationExtractor(): ConfigurationExtractor
5152
fun variantExtractor(): VariantExtractor
5253

53-
fun logger(): Logger
54-
5554
@Component.Factory
5655
interface Factory {
5756
fun create(
@@ -79,5 +78,5 @@ internal interface DependenciesModule {
7978
fun bindVariantExtractor(extractor: DefaultVariantExtractor): VariantExtractor
8079

8180
@Binds
82-
fun bindLogger(logger: PluginLogger): Logger
81+
fun bindLogger(logger: DefaultPluginLogger): PluginLogger
8382
}

0 commit comments

Comments
 (0)