1 /*
<lambda>null2  * Copyright 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.baselineprofile.gradle.consumer
18 
19 import androidx.baselineprofile.gradle.configuration.ConfigurationManager
20 import androidx.baselineprofile.gradle.consumer.task.GenerateBaselineProfileTask
21 import androidx.baselineprofile.gradle.consumer.task.MainGenerateBaselineProfileTaskForAgp80Only
22 import androidx.baselineprofile.gradle.consumer.task.MergeBaselineProfileTask
23 import androidx.baselineprofile.gradle.consumer.task.PrintConfigurationForVariantTask
24 import androidx.baselineprofile.gradle.consumer.task.PrintMapPropertiesForVariantTask
25 import androidx.baselineprofile.gradle.utils.AgpFeature
26 import androidx.baselineprofile.gradle.utils.AgpPlugin
27 import androidx.baselineprofile.gradle.utils.AgpPluginId
28 import androidx.baselineprofile.gradle.utils.BUILD_TYPE_BASELINE_PROFILE_PREFIX
29 import androidx.baselineprofile.gradle.utils.BUILD_TYPE_BENCHMARK_PREFIX
30 import androidx.baselineprofile.gradle.utils.CONFIGURATION_NAME_BASELINE_PROFILES
31 import androidx.baselineprofile.gradle.utils.INTERMEDIATES_BASE_FOLDER
32 import androidx.baselineprofile.gradle.utils.KOTLIN_MULTIPLATFORM_PLUGIN_ID
33 import androidx.baselineprofile.gradle.utils.KotlinMultiPlatformUtils
34 import androidx.baselineprofile.gradle.utils.MAX_AGP_VERSION_RECOMMENDED_EXCLUSIVE
35 import androidx.baselineprofile.gradle.utils.MIN_AGP_VERSION_REQUIRED_INCLUSIVE
36 import androidx.baselineprofile.gradle.utils.R8Utils
37 import androidx.baselineprofile.gradle.utils.RELEASE
38 import androidx.baselineprofile.gradle.utils.camelCase
39 import androidx.baselineprofile.gradle.utils.namedOrNull
40 import com.android.build.api.dsl.ApplicationExtension
41 import com.android.build.api.dsl.LibraryExtension
42 import com.android.build.api.variant.ApplicationVariant
43 import com.android.build.api.variant.ApplicationVariantBuilder
44 import com.android.build.api.variant.Variant
45 import org.gradle.api.GradleException
46 import org.gradle.api.Plugin
47 import org.gradle.api.Project
48 import org.gradle.api.Task
49 import org.gradle.api.artifacts.Configuration
50 import org.gradle.api.tasks.TaskContainer
51 
52 /**
53  * This is the consumer plugin for baseline profile generation. In order to generate baseline
54  * profiles three plugins are needed: one is applied to the app or the library that should consume
55  * the baseline profile when building (consumer), one is applied to the module that should supply
56  * the under test app (app target) and the last one is applied to a test module containing the ui
57  * test that generate the baseline profile on the device (producer).
58  */
59 class BaselineProfileConsumerPlugin : Plugin<Project> {
60     override fun apply(project: Project) = BaselineProfileConsumerAgpPlugin(project).onApply()
61 }
62 
63 private class BaselineProfileConsumerAgpPlugin(private val project: Project) :
64     AgpPlugin(
65         project = project,
66         supportedAgpPlugins =
67             setOf(AgpPluginId.ID_ANDROID_APPLICATION_PLUGIN, AgpPluginId.ID_ANDROID_LIBRARY_PLUGIN),
68         minAgpVersionInclusive = MIN_AGP_VERSION_REQUIRED_INCLUSIVE,
69         maxAgpVersionExclusive = MAX_AGP_VERSION_RECOMMENDED_EXCLUSIVE
70     ) {
71 
72     // List of the non debuggable build types
73     private val nonDebuggableBuildTypes = mutableListOf<String>()
74 
75     // The baseline profile consumer extension to access non-variant specific configuration options
76     private val baselineProfileExtension = BaselineProfileConsumerExtension.register(project)
77 
78     // Offers quick access to configuration extension, hiding the property override and merge logic
79     private val perVariantBaselineProfileExtensionManager =
80         PerVariantConsumerExtensionManager(baselineProfileExtension)
81 
82     // Manages creation of configurations
83     private val configurationManager = ConfigurationManager(project)
84 
85     // Manages r8 properties
86     private val r8Utils = R8Utils(project)
87 
88     // Global baseline profile configuration. Note that created here it can be directly consumed
89     // in the dependencies block.
90     private val mainBaselineProfileConfiguration =
91         configurationManager.maybeCreate(
92             nameParts = listOf(CONFIGURATION_NAME_BASELINE_PROFILES),
93             canBeConsumed = false,
94             canBeResolved = true,
95             buildType = null,
96             productFlavors = null
97         )
98 
99     private val Variant.benchmarkVariantName: String
100         get() {
101             val parts =
<lambda>null102                 listOfNotNull(flavorName, BUILD_TYPE_BENCHMARK_PREFIX, buildType).filter {
103                     it.isNotBlank()
104                 }
105             return camelCase(*parts.toTypedArray())
106         }
107 
onAgpPluginNotFoundnull108     override fun onAgpPluginNotFound(pluginIds: Set<AgpPluginId>) {
109         throw IllegalStateException(
110             """
111             The module ${project.name} does not have the `com.android.application` or
112             `com.android.library` plugin applied. The `androidx.baselineprofile.consumer`
113             plugin supports only android application and library modules. Please review
114             your build.gradle to ensure this plugin is applied to the correct module.
115             """
116                 .trimIndent()
117         )
118     }
119 
onAgpPluginFoundnull120     override fun onAgpPluginFound(pluginIds: Set<AgpPluginId>) {
121         project.logger.debug(
122             """
123             [BaselineProfileConsumerPlugin] afterEvaluate check: app or library plugin was applied
124             """
125                 .trimIndent()
126         )
127     }
128 
onApplicationFinalizeDslnull129     override fun onApplicationFinalizeDsl(extension: ApplicationExtension) {
130 
131         // Here we select the build types we want to process if this is an application,
132         // i.e. non debuggable build types that have not been created by the app target plugin.
133         // Also exclude the build types starting with baseline profile prefix, in case the app
134         // target plugin is also applied.
135 
136         nonDebuggableBuildTypes.addAll(
137             extension.buildTypes
138                 .filter {
139                     !it.isDebuggable &&
140                         !it.name.startsWith(BUILD_TYPE_BASELINE_PROFILE_PREFIX) &&
141                         !it.name.startsWith(BUILD_TYPE_BENCHMARK_PREFIX)
142                 }
143                 .map { it.name }
144         )
145     }
146 
onLibraryFinalizeDslnull147     override fun onLibraryFinalizeDsl(extension: LibraryExtension) {
148 
149         // Here we select the build types we want to process if this is a library.
150         // Libraries don't have a `debuggable` flag. Also we don't need to exclude build types
151         // prefixed with the baseline profile prefix. Ideally on the `debug` type should be
152         // excluded.
153 
154         nonDebuggableBuildTypes.addAll(
155             extension.buildTypes.filter { it.name != "debug" }.map { it.name }
156         )
157     }
158 
getWarningsnull159     override fun getWarnings() = baselineProfileExtension.warnings
160 
161     override fun onApplicationBeforeVariants(variantBuilder: ApplicationVariantBuilder) {
162 
163         // Note that the lifecycle is for each variant `beforeVariant`, `onVariant`. This means
164         // that the `onVariant` for the base variants of the module (for example `release`) will
165         // run before `beforeVariant` of `benchmarkRelease` and `nonMinifiedRelease`.
166         // Since we schedule some callbacks in for benchmark and nonMinified variants in the
167         // onVariant callback for the base variants, this is the place where we can remove them,
168         // in case the benchmark and nonMinified variants have been disabled.
169 
170         val isBaselineProfilePluginCreatedBuildType =
171             isBaselineProfilePluginCreatedBuildType(variantBuilder.buildType)
172 
173         // Note that the callback should be remove at the end, after all the variants
174         // have been processed. This is because the benchmark and nonMinified variants can be
175         // disabled at any point AFTER the plugin has been applied. So checking immediately here
176         // would tell us that the variant is enabled, while it could be disabled later.
177         afterVariants {
178             if (!variantBuilder.enable && isBaselineProfilePluginCreatedBuildType) {
179                 removeOnVariantCallback(variantBuilder.name)
180                 logger.warn(
181                     property = { disabledVariants },
182                     propertyName = "disabledVariants",
183                     message =
184                         "Variant `${variantBuilder.name}` is disabled. If this " +
185                             "is not intentional, please check your gradle configuration " +
186                             "for beforeVariants blocks. For more information on variant " +
187                             "filters checkout the docs at https://developer.android.com/" +
188                             "build/build-variants#filter-variants."
189                 )
190             }
191         }
192     }
193 
194     @Suppress("UnstableApiUsage")
onVariantsnull195     override fun onVariants(variant: Variant) {
196 
197         // For test only: this registers a print task with the experimental properties of the
198         // variant. This task is hidden from the `tasks` command.
199         PrintMapPropertiesForVariantTask.registerForVariant(project = project, variant = variant)
200 
201         // Controls whether Android Studio should see this variant. Variants created by the
202         // baseline profile gradle plugin are hidden by default.
203         if (
204             baselineProfileExtension.hideSyntheticBuildTypesInAndroidStudio &&
205                 isBaselineProfilePluginCreatedBuildType(variant.buildType)
206         ) {
207             variant.experimentalProperties.put("androidx.baselineProfile.hideInStudio", true)
208         }
209 
210         // From here on, process only the non debuggable build types we previously selected.
211         if (variant.buildType !in nonDebuggableBuildTypes) return
212 
213         // This allows quick access to this variant configuration according to the override
214         // and merge rules implemented in the PerVariantConsumerExtensionManager.
215         val variantConfiguration = perVariantBaselineProfileExtensionManager.variant(variant)
216 
217         // For test only: this registers a print task with the configuration of the variant.
218         // This task is hidden from the `tasks` command.
219         PrintConfigurationForVariantTask.registerForVariant(
220             project = project,
221             variant = variant,
222             variantConfig = variantConfiguration
223         )
224 
225         // Sets the r8 rewrite baseline profile for the non debuggable variant.
226         variantConfiguration.baselineProfileRulesRewrite?.let {
227             r8Utils.setRulesRewriteForVariantEnabled(variant, it)
228         }
229 
230         // Sets the r8 startup dex optimization profile for the non debuggable variant.
231         variantConfiguration.dexLayoutOptimization?.let {
232             r8Utils.setDexLayoutOptimizationEnabled(variant, it)
233         }
234 
235         // Check if this variant has any direct dependency
236         val variantDependencies = variantConfiguration.dependencies
237 
238         // Creates the configuration to carry the specific variant artifact
239         val baselineProfileConfiguration =
240             createConfigurationForVariant(
241                 variant = variant,
242                 mainConfiguration = mainBaselineProfileConfiguration
243             )
244 
245         // Adds the custom dependencies for baseline profiles. Note that dependencies
246         // for global, build type, flavor and variant specific are all merged.
247         variantDependencies.forEach {
248             val targetProjectDependency = project.dependencyFactory.create(it)
249             baselineProfileConfiguration.dependencies.add(targetProjectDependency)
250         }
251 
252         // There are 2 different ways in which the output task can merge the baseline
253         // profile rules, according to [BaselineProfileConsumerExtension#mergeIntoMain].
254         // When mergeIntoMain is `true` the first variant will create a task shared across
255         // all the variants to merge, while the next variants will simply add the additional
256         // baseline profile artifacts, modifying the existing task.
257         // When mergeIntoMain is `false` each variants has its own task with a single
258         // artifact per task, specific for that variant.
259         // When mergeIntoMain is not specified, it's by default true for libraries and false
260         // for apps.
261 
262         // Warning: support for baseline profile source sets in library module was added with
263         // agp 8.3.0 alpha 15 (b/309858620). Therefore, before then, we can only always merge into
264         // main and always output only in src/main/baseline-prof.txt.
265         val forceOutputInSrcMain =
266             isLibraryModule() &&
267                 !supportsFeature(AgpFeature.LIBRARY_MODULE_SUPPORTS_BASELINE_PROFILE_SOURCE_SETS)
268 
269         val mergeIntoMain =
270             if (forceOutputInSrcMain) {
271                 true
272             } else {
273                 variantConfiguration.mergeIntoMain ?: isLibraryModule()
274             }
275 
276         // Determines the target name for the Android target in kotlin multiplatform projects.
277         // Note that KotlinMultiPlatformUtils references the kmp extension that exists only if the
278         // multiplatform plugin has been applied.
279         val androidTargetName =
280             if (project.plugins.hasPlugin(KOTLIN_MULTIPLATFORM_PLUGIN_ID)) {
281                 KotlinMultiPlatformUtils.androidTargetName(project)
282             } else {
283                 ""
284             }
285 
286         // This part changes according to the AGP version of the module. `mergeIntoMain` merges
287         // the profile of the generated profile for this variant into the main one. This can be
288         // applied to the `main` configuration or to single variants. On Agp 8.0, since it's not
289         // possible to run tests on multiple build types in the same run, when `mergeIntoMain` is
290         // true only variants of the specific build type invoked are merged. This means that on
291         // AGP 8.0 the `main` baseline profile is generated by only the build type `release` when
292         // calling `generateReleaseBaselineProfiles`. On Agp 8.1 instead, it works as intended and
293         // we can merge all the variants with `mergeIntoMain` true, independently from the build
294         // type.
295         data class TaskAndFolderName(val taskVariantName: String, val folderVariantName: String)
296         val (mergeAwareTaskName, mergeAwareVariantOutput) =
297             if (mergeIntoMain) {
298                 if (supportsFeature(AgpFeature.TEST_MODULE_SUPPORTS_MULTIPLE_BUILD_TYPES)) {
299                     TaskAndFolderName(
300                         taskVariantName = "",
301                         folderVariantName = camelCase(androidTargetName, "main")
302                     )
303                 } else {
304                     // Note that the exception here cannot happen because all the variants have a
305                     // build
306                     // type in Android.
307                     TaskAndFolderName(
308                         taskVariantName =
309                             variant.buildType
310                                 ?: throw IllegalStateException("Found variant without build type."),
311                         folderVariantName = camelCase(androidTargetName, "main")
312                     )
313                 }
314             } else {
315                 TaskAndFolderName(
316                     taskVariantName = variant.name,
317                     folderVariantName = camelCase(androidTargetName, variant.name)
318                 )
319             }
320 
321         // Creates the task to merge the baseline profile artifacts coming from
322         // different configurations.
323         val mergedTaskOutputDir =
324             project.layout.buildDirectory.dir(
325                 "$INTERMEDIATES_BASE_FOLDER/$mergeAwareVariantOutput/merged"
326             )
327 
328         val mergeTaskProvider =
329             MergeBaselineProfileTask.maybeRegisterForMerge(
330                 project = project,
331                 variantName = variant.name,
332                 mergeAwareTaskName = mergeAwareTaskName,
333                 hasDependencies = baselineProfileConfiguration.allDependencies.isNotEmpty(),
334                 sourceProfilesFileCollection = baselineProfileConfiguration,
335                 outputDir = mergedTaskOutputDir,
336                 filterRules = variantConfiguration.filterRules,
337                 library = isLibraryModule(),
338                 warnings = baselineProfileExtension.warnings,
339 
340                 // Note that the merge task is the last task only if saveInSrc is disabled. When
341                 // saveInSrc is enabled an additional task is created to copy the profile in the
342                 // sources
343                 // folder.
344                 isLastTask = !variantConfiguration.saveInSrc
345             )
346 
347         // If `saveInSrc` is true, we create an additional task to copy the output
348         // of the merge task in the src folder.
349         val lastTaskProvider =
350             if (variantConfiguration.saveInSrc) {
351 
352                 // Here we determine where the final baseline profile file should be placed.
353                 // Before AGP 8.3.0 alpha 15, libraries don't support source sets so we can only
354                 // output in src/main/baseline-prof.txt. Variable `shouldOutputInSrcMain` defined
355                 // above, controls this behavior. Note that `mergeAwareVariantOutput` is always
356                 // `main`
357                 // when `shouldOutputInSrcMain` is true
358                 var srcOutputDir =
359                     project.layout.projectDirectory.dir("src/$mergeAwareVariantOutput/")
360                 if (!forceOutputInSrcMain) {
361                     val baselineProfileOutputDir =
362                         perVariantBaselineProfileExtensionManager
363                             .variant(variant)
364                             .baselineProfileOutputDir
365                     srcOutputDir = srcOutputDir.dir("$baselineProfileOutputDir/")
366                 }
367 
368                 // This task copies the baseline profile generated from the merge task.
369                 // Note that we're reutilizing the [MergeBaselineProfileTask] because
370                 // if the flag `mergeIntoMain` is true tasks will have the same name
371                 // and we just want to add more file to copy to the same output. This is
372                 // already handled in the MergeBaselineProfileTask.
373                 val copyTaskProvider =
374                     MergeBaselineProfileTask.maybeRegisterForCopy(
375                         project = project,
376                         variantName = variant.name,
377                         mergeAwareTaskName = mergeAwareTaskName,
378                         library = isLibraryModule(),
379                         sourceDir = mergeTaskProvider.flatMap { it.baselineProfileDir },
380                         outputDir = project.provider { srcOutputDir },
381                         hasDependencies = baselineProfileConfiguration.allDependencies.isNotEmpty(),
382                         isLastTask = true,
383                         warnings = baselineProfileExtension.warnings
384                     )
385 
386                 // Applies the source path for this variant. Note that this doesn't apply when the
387                 // output is src/main/baseline-prof.txt.
388                 if (!forceOutputInSrcMain) {
389 
390                     val srcOutputDirPath = srcOutputDir.asFile.apply { mkdirs() }.absolutePath
391                     fun applySourceSets(variant: Variant) {
392                         variant.sources.baselineProfiles?.addStaticSourceDirectory(srcOutputDirPath)
393                     }
394                     applySourceSets(variant)
395 
396                     // For apps the source set needs to be applied to both the current variant
397                     // (for example `release`) and its benchmark version.
398                     if (
399                         isApplicationModule() &&
400                             supportsFeature(AgpFeature.TEST_MODULE_SUPPORTS_MULTIPLE_BUILD_TYPES)
401                     ) {
402                         onVariant(variant.benchmarkVariantName) { v: ApplicationVariant ->
403                             applySourceSets(v)
404                         }
405                     }
406                 }
407 
408                 // If this is an application, we need to ensure that:
409                 // If `automaticGenerationDuringBuild` is true, building a release build
410                 // should trigger the generation of the profile. This is done through a
411                 // dependsOn rule.
412                 // If `automaticGenerationDuringBuild` is false and the user calls both
413                 // tasks to generate and assemble, assembling the release should wait of the
414                 // generation to be completed. This is done through a `mustRunAfter` rule.
415                 // Depending on whether the flag `automaticGenerationDuringBuild` is enabled
416                 // Note that we cannot use the variant src set api
417                 // `addGeneratedSourceDirectory` since that overwrites the outputDir,
418                 // that would be re-set in the build dir.
419                 // Also this is specific for applications: doing this for a library would
420                 // trigger a circular task dependency since the library would require
421                 // the profile in order to build the aar for the sample app and generate
422                 // the profile.
423 
424                 val automaticGeneration =
425                     perVariantBaselineProfileExtensionManager
426                         .variant(variant)
427                         .automaticGenerationDuringBuild
428 
429                 if (automaticGeneration && isLibraryModule() && !isGradleSyncRunning()) {
430                     throw IllegalStateException(
431                         "The flag `automaticGenerationDuringBuild` is not compatible with library " +
432                             "modules. Please remove the flag `automaticGenerationDuringBuild` in " +
433                             "your com.android.library module ${project.name}."
434                     )
435                 }
436 
437                 if (isApplicationModule()) {
438                     // Defines a function to apply the baseline profile source sets to a variant.
439                     fun applySourceSets(variantName: String) {
440 
441                         // These dependencies causes a circular task dependency when the producer
442                         // points to a consumer that does not have the appTarget plugin.
443                         // Note that on old versions of AGP these tasks may not exist.
444                         listOfNotNull(
445                                 project.tasks.taskMergeArtProfile(variantName),
446                                 project.tasks.taskMergeStartupProfile(variantName)
447                             )
448                             .forEach {
449                                 it.configure { t ->
450                                     if (automaticGeneration) {
451                                         t.dependsOn(copyTaskProvider)
452                                     } else {
453                                         t.mustRunAfter(copyTaskProvider)
454                                     }
455                                 }
456                             }
457                     }
458 
459                     afterVariants {
460 
461                         // Apply the source sets to the variant.
462                         applySourceSets(variant.name)
463 
464                         // Apply the source sets to the benchmark variant if supported.
465                         if (supportsFeature(AgpFeature.TEST_MODULE_SUPPORTS_MULTIPLE_BUILD_TYPES)) {
466                             applySourceSets(variant.benchmarkVariantName)
467                         }
468                     }
469                 }
470 
471                 // In this case the last task is the copy task.
472                 copyTaskProvider
473             } else {
474 
475                 if (variantConfiguration.automaticGenerationDuringBuild) {
476 
477                     // If the flag `automaticGenerationDuringBuild` is true, we can set the
478                     // merge task to provide generated sources for the variant, using the
479                     // src set variant api. This means that we don't need to manually depend
480                     // on the merge or prepare art profile task.
481 
482                     // Defines a function to apply the baseline profile source sets to a variant.
483                     fun applySourceSets(v: Variant) {
484                         v.sources.baselineProfiles?.addGeneratedSourceDirectory(
485                             taskProvider = mergeTaskProvider,
486                             wiredWith = MergeBaselineProfileTask::baselineProfileDir
487                         )
488                     }
489 
490                     // Apply the source sets to the variant.
491                     applySourceSets(variant)
492 
493                     // Apply the source sets to the benchmark variant if supported and this the
494                     // consumer is an app (libraries don't have benchmark type).
495                     if (
496                         isApplicationModule() &&
497                             supportsFeature(AgpFeature.TEST_MODULE_SUPPORTS_MULTIPLE_BUILD_TYPES)
498                     ) {
499 
500                         // Note that there is no way to access directly a specific variant, so we
501                         // schedule a callback for later, when the variant is processed. Note that
502                         // because the benchmark build type is created after the baseline profile
503                         // build type, its variants will also come after the ones for baseline
504                         // profile.
505                         onVariant(variant.benchmarkVariantName) { v: ApplicationVariant ->
506                             applySourceSets(v)
507                         }
508                     }
509                 } else {
510 
511                     // This is the case of `saveInSrc` and `automaticGenerationDuringBuild`
512                     // both false, that is unsupported. In this case we simply throw an
513                     // error.
514                     if (!isGradleSyncRunning()) {
515                         throw GradleException(
516                             """
517                 The current configuration of flags `saveInSrc` and `automaticGenerationDuringBuild`
518                 is not supported. At least one of these should be set to `true`. Please review your
519                 baseline profile plugin configuration in your build.gradle.
520                     """
521                                 .trimIndent()
522                         )
523                     }
524                 }
525 
526                 // In this case the last task is the merge task.
527                 mergeTaskProvider
528             }
529 
530         // Here we create the final generate task that triggers the whole generation for this
531         // variant and all the parent tasks. For this one the child task is either copy or merge,
532         // depending on the configuration.
533         GenerateBaselineProfileTask.maybeCreate(
534             project = project,
535             variantName = mergeAwareTaskName,
536             lastTaskProvider = lastTaskProvider
537         )
538 
539         // Create the build type task. For example `generateReleaseBaselineProfile`
540         // The variant name is equal to the build type name if there are no flavors.
541         // Note that if `mergeIntoMain` is `true` the build type task already exists.
542         if (
543             !mergeIntoMain &&
544                 !variant.buildType.isNullOrBlank() &&
545                 variant.buildType != variant.name
546         ) {
547             GenerateBaselineProfileTask.maybeCreate(
548                 project = project,
549                 variantName = variant.buildType!!,
550                 lastTaskProvider = lastTaskProvider
551             )
552         }
553 
554         if (supportsFeature(AgpFeature.TEST_MODULE_SUPPORTS_MULTIPLE_BUILD_TYPES)) {
555 
556             // Generate a flavor task for the assembled flavor name and each flavor dimension.
557             // For example for variant `freeRelease` (build type `release`, flavor `free`):
558             // `generateFreeBaselineProfile`.
559             // For example for variant `freeRedRelease` (build type `release`, flavor dimensions
560             // `free` and `red`): `generateFreeBaselineProfile`, `generateRedBaselineProfile` and
561             // `generateFreeRedBaselineProfile`.
562             if (!mergeIntoMain) {
563                 listOfNotNull(
564                         variant.flavorName,
565                         *variant.productFlavors.map { it.second }.toTypedArray()
566                     )
567                     .filter { it != variant.name && it.isNotBlank() }
568                     .toSet()
569                     .forEach {
570                         GenerateBaselineProfileTask.maybeCreate(
571                             project = project,
572                             variantName = it,
573                             lastTaskProvider = lastTaskProvider
574                         )
575                     }
576             }
577 
578             // Generate the main global tasks `generateBaselineProfile
579             GenerateBaselineProfileTask.maybeCreate(
580                 project = project,
581                 variantName = "",
582                 lastTaskProvider = lastTaskProvider
583             )
584         } else {
585             // Due to b/265438201 we cannot have a global task `generateBaselineProfile` that
586             // triggers generation for all the variants when there are multiple build types.
587             // So for version of AGP that don't support that, invoking `generateBaselineProfile`
588             // will run generation for `release` build type only, that is the same behavior of
589             // `generateReleaseBaselineProfile`. For this same reason we cannot have a flavor
590             // task, such as `generateFreeBaselineProfile` because that would run generation for
591             // all the build types with flavor free, that is not as well supported.
592             if (variant.buildType == RELEASE) {
593                 MainGenerateBaselineProfileTaskForAgp80Only.maybeCreate(
594                     project = project,
595                     variantName = "",
596                     lastTaskProvider = lastTaskProvider,
597                     warnings = baselineProfileExtension.warnings
598                 )
599             }
600         }
601     }
602 
TaskContainernull603     fun TaskContainer.taskMergeArtProfile(variantName: String) =
604         project.tasks.namedOrNull<Task>("merge", variantName, "artProfile")
605 
606     fun TaskContainer.taskMergeStartupProfile(variantName: String) =
607         project.tasks.namedOrNull<Task>("merge", variantName, "startupProfile")
608 
609     private fun createConfigurationForVariant(variant: Variant, mainConfiguration: Configuration) =
610         configurationManager.maybeCreate(
611             nameParts = listOf(variant.name, CONFIGURATION_NAME_BASELINE_PROFILES),
612             canBeResolved = true,
613             canBeConsumed = false,
614             extendFromConfigurations = listOf(mainConfiguration),
615             buildType = variant.buildType ?: "",
616             productFlavors = variant.productFlavors
617         )
618 
619     private fun isBaselineProfilePluginCreatedBuildType(buildType: String?) =
620         buildType?.let {
621             it.startsWith(BUILD_TYPE_BASELINE_PROFILE_PREFIX) ||
622                 it.startsWith(BUILD_TYPE_BENCHMARK_PREFIX)
623         } ?: false
624 }
625