blob: dcc82979c87d693be2943787279b74e45b9752f5 [file] [log] [blame]
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.build.gradle.internal.ide.kmp
import com.android.SdkConstants
import com.android.Version
import com.android.build.api.component.impl.KmpAndroidTestImpl
import com.android.build.api.component.impl.KmpHostTestImpl
import com.android.build.api.dsl.KotlinMultiplatformAndroidTarget
import com.android.build.gradle.internal.component.DeviceTestCreationConfig
import com.android.build.gradle.internal.component.KmpComponentCreationConfig
import com.android.build.gradle.internal.component.KmpCreationConfig
import com.android.build.gradle.internal.component.HostTestCreationConfig
import com.android.build.gradle.internal.ide.proto.convert
import com.android.build.gradle.internal.ide.proto.setIfNotNull
import com.android.build.gradle.internal.ide.v2.ModelBuilder.Companion.getAgpFlags
import com.android.build.gradle.internal.ide.v2.TestInfoImpl
import com.android.build.gradle.internal.ide.v2.convertToExecution
import com.android.build.gradle.internal.lint.getLocalCustomLintChecksForModel
import com.android.build.gradle.internal.scope.InternalArtifactType
import com.android.build.gradle.internal.utils.getDesugarLibConfigFile
import com.android.build.gradle.internal.utils.getDesugaredMethods
import com.android.build.gradle.options.ProjectOptions
import com.android.builder.core.ComponentType
import com.android.builder.errors.IssueReporter
import com.android.kotlin.multiplatform.ide.models.serialization.androidCompilationKey
import com.android.kotlin.multiplatform.ide.models.serialization.androidSourceSetKey
import com.android.kotlin.multiplatform.ide.models.serialization.androidTargetKey
import com.android.kotlin.multiplatform.models.AndroidCompilation
import com.android.kotlin.multiplatform.models.AndroidSourceSet
import com.android.kotlin.multiplatform.models.AndroidTarget
import com.android.kotlin.multiplatform.models.InstrumentedTestInfo
import com.android.kotlin.multiplatform.models.MainVariantInfo
import com.android.kotlin.multiplatform.models.SourceProvider
import com.android.kotlin.multiplatform.models.UnitTestInfo
import org.gradle.api.Project
/**
* A singleton that is responsible for populating the android models sent along with the android
* target, compilation and sourceSets. Data is added through [org.jetbrains.kotlin.tooling.core.HasMutableExtras.extras]
* which the target, compilation and sourceSet implements.
*
* Checkout [androidTargetKey], [androidCompilationKey], [androidSourceSetKey] to see what data is
* held in each model.
*/
object KotlinModelBuildingConfigurator {
private fun KmpComponentCreationConfig.toType() = when (this) {
is KmpCreationConfig -> AndroidCompilation.CompilationType.MAIN
is KmpHostTestImpl -> AndroidCompilation.CompilationType.UNIT_TEST
is KmpAndroidTestImpl -> AndroidCompilation.CompilationType.INSTRUMENTED_TEST
else -> throw IllegalArgumentException("Unknown type ${this::class.java}")
}
/**
* As each [KmpComponentCreationConfig] corresponds to a kotlin compilation, this method
* adds extra android-specific information to the android kotlin compilations. That includes,
* the main compilation and the unitTest and instrumentedTest compilations if enabled.
*
* This method also adds android-specific data about the default sourceSet in each compilation,
* mainly the android manifest location.
*/
fun setupAndroidCompilations(
components: List<KmpComponentCreationConfig>,
testInstrumentationRunner: String?,
testInstrumentationRunnerArguments: Map<String, String>
) {
components.forEach { component ->
val compilation = component.androidKotlinCompilation
compilation.extras[androidCompilationKey] =
AndroidCompilation.newBuilder()
.setType(component.toType())
.setDefaultSourceSetName(
compilation.defaultSourceSet.name
)
.setAssembleTaskName(
component.taskContainer.assembleTask.name
)
.setKotlinCompileTaskName(
component.androidKotlinCompilation.compileKotlinTaskName
)
.setIfNotNull(
(component as? KmpCreationConfig)?.toInfo(),
AndroidCompilation.Builder::setMainInfo
)
.setIfNotNull(
(component as? HostTestCreationConfig)?.toInfo(),
AndroidCompilation.Builder::setUnitTestInfo
)
.setIfNotNull(
(component as? DeviceTestCreationConfig)?.toInfo(
testInstrumentationRunner, testInstrumentationRunnerArguments
),
AndroidCompilation.Builder::setInstrumentedTestInfo
)
.build()
compilation.defaultSourceSet.extras[androidSourceSetKey] =
AndroidSourceSet.newBuilder()
.setSourceProvider(
SourceProvider.newBuilder()
.setManifestFile(component.sources.manifestFile.get().convert())
)
.build()
}
}
/**
* This method attaches global information about the android target to the kotlin android target
* object. The data sent here is global to the different android compilations created within the
* target.
*/
fun setupAndroidTargetModels(
project: Project,
mainVariant: KmpCreationConfig,
androidTarget: KotlinMultiplatformAndroidTarget,
projectOptions: ProjectOptions,
issueReporter: IssueReporter
) {
androidTarget.extras[androidTargetKey] =
AndroidTarget.newBuilder()
.setAgpVersion(Version.ANDROID_GRADLE_PLUGIN_VERSION)
.setProjectPath(project.path)
.setRootBuildId(
(project.gradle.parent ?: project.gradle).rootProject.projectDir.convert()
)
.setBuildId(project.gradle.rootProject.projectDir.convert())
.setBuildDir(project.layout.buildDirectory.get().asFile.convert())
.setBuildToolsVersion(mainVariant.global.buildToolsRevision.toString())
.setGroupId(
project.group.toString()
)
.addAllBootClasspath(
mainVariant.global.filteredBootClasspath.get().map { it.asFile.convert() }
)
.setTestInfo(
TestInfoImpl(
animationsDisabled = mainVariant.global.androidTestOptions.takeIf {
mainVariant.androidDeviceTest != null
}?.animationsDisabled ?: false,
execution = mainVariant.global.androidTestOptions.takeIf {
mainVariant.androidDeviceTest != null
}?.execution?.convertToExecution(),
additionalRuntimeApks = project
.configurations
.findByName(
SdkConstants.GRADLE_ANDROID_TEST_UTIL_CONFIGURATION
)?.files ?: listOf(),
instrumentedTestTaskName = mainVariant.androidDeviceTest?.taskContainer?.connectedTestTask?.name
?: ""
).convert()
)
.setFlags(
getAgpFlags(
variants = listOf(mainVariant),
projectOptions = projectOptions
).convert()
)
.addAllLintChecksJars(
getLocalCustomLintChecksForModel(
project,
issueReporter
).map { it.convert() }
)
.setIsCoreLibraryDesugaringEnabled(
mainVariant.isCoreLibraryDesugaringEnabledLintCheck
)
.addAllDesugarLibConfig(
if (mainVariant.isCoreLibraryDesugaringEnabledLintCheck) {
getDesugarLibConfigFile(project)
} else {
emptyList()
}.map { it.convert() }
)
.addAllDesugaredMethodsFiles(
getDesugaredMethods(
mainVariant.services,
mainVariant.isCoreLibraryDesugaringEnabledLintCheck,
mainVariant.minSdk,
mainVariant.global
).files.map { it.convert() }
)
.setWithJava(mainVariant.withJava)
.build()
}
private fun KmpCreationConfig.toInfo() =
MainVariantInfo.newBuilder()
.setNamespace(namespace.get())
.setCompileSdkTarget(global.compileSdkHashString)
.setMinSdkVersion(minSdk.convert())
.setIfNotNull(
maxSdk,
MainVariantInfo.Builder::setMaxSdkVersion
)
.addAllProguardFiles(
optimizationCreationConfig.proguardFiles.get().map { it.asFile.convert() }
)
.addAllConsumerProguardFiles(
optimizationCreationConfig.consumerProguardFilePaths.map { it.convert() }
)
.setMinificationEnabled(
optimizationCreationConfig.minifiedEnabled
)
.build()
private fun HostTestCreationConfig.toInfo() =
UnitTestInfo.newBuilder()
.setNamespace(namespace.get())
.setIfNotNull(
global.mockableJarArtifact.files.singleOrNull()?.convert(),
UnitTestInfo.Builder::setMockablePlatformJar
)
.setUnitTestTaskName(
computeTaskNameInternal(ComponentType.UNIT_TEST_PREFIX)
)
.build()
private fun DeviceTestCreationConfig.toInfo(
testInstrumentationRunner: String?,
testInstrumentationRunnerArguments: Map<String, String>
) =
InstrumentedTestInfo.newBuilder()
.setNamespace(namespace.get())
.setIfNotNull(
testInstrumentationRunner,
InstrumentedTestInfo.Builder::setTestInstrumentationRunner
)
.setIfNotNull(
signingConfig?.convert(),
InstrumentedTestInfo.Builder::setSigningConfig
)
.putAllTestInstrumentationRunnerArguments(testInstrumentationRunnerArguments)
.setAssembleTaskOutputListingFile(
artifacts.get(InternalArtifactType.APK_IDE_REDIRECT_FILE).get().asFile.convert()
)
.build()
}