Android import: initial implementation
This commit is contained in:
+6
-6
@@ -276,7 +276,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() {
|
||||
|
||||
val sourceSetToCompilationData = LinkedHashMap<KotlinSourceSet, MutableSet<GradleSourceSetData>>()
|
||||
for (target in mppModel.targets) {
|
||||
if (target.platform == KotlinPlatform.ANDROID) continue
|
||||
// if (target.platform == KotlinPlatform.ANDROID) continue
|
||||
if (target.name == KotlinTarget.METADATA_TARGET_NAME) continue
|
||||
val targetData = KotlinTargetData(target.name).also {
|
||||
it.archiveFile = target.jar?.archiveFile
|
||||
@@ -350,7 +350,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() {
|
||||
|
||||
val ignoreCommonSourceSets by lazy { externalProject.notImportedCommonSourceSets() }
|
||||
for (sourceSet in mppModel.sourceSets.values) {
|
||||
if (sourceSet.actualPlatforms.platforms.singleOrNull() == KotlinPlatform.ANDROID) continue
|
||||
// if (sourceSet.actualPlatforms.platforms.singleOrNull() == KotlinPlatform.ANDROID) continue
|
||||
if (sourceSet.actualPlatforms.supports(KotlinPlatform.COMMON) && ignoreCommonSourceSets) continue
|
||||
val moduleId = getKotlinModuleId(gradleModule, sourceSet, resolverCtx)
|
||||
val existingSourceSetDataNode = sourceSetMap[moduleId]?.first
|
||||
@@ -448,7 +448,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() {
|
||||
.toMap()
|
||||
if (resolverCtx.getExtraProject(gradleModule, ExternalProject::class.java) == null) return
|
||||
processSourceSets(gradleModule, mppModel, ideModule, resolverCtx) { dataNode, sourceSet ->
|
||||
if (dataNode == null || sourceSet.actualPlatforms.platforms.singleOrNull() == KotlinPlatform.ANDROID) return@processSourceSets
|
||||
if (dataNode == null /*|| sourceSet.actualPlatforms.platforms.singleOrNull() == KotlinPlatform.ANDROID*/) return@processSourceSets
|
||||
createContentRootData(
|
||||
sourceSet.sourceDirs,
|
||||
sourceSet.sourceType,
|
||||
@@ -558,7 +558,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() {
|
||||
}
|
||||
val closedSourceSetGraph = Graphs.transitiveClosure(sourceSetGraph)
|
||||
for (sourceSet in closedSourceSetGraph.nodes()) {
|
||||
val isAndroid = sourceSet.actualPlatforms.platforms.singleOrNull() == KotlinPlatform.ANDROID
|
||||
val isAndroid = false //sourceSet.actualPlatforms.platforms.singleOrNull() == KotlinPlatform.ANDROID
|
||||
val fromDataNode = if (isAndroid) {
|
||||
ideModule
|
||||
} else {
|
||||
@@ -578,7 +578,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() {
|
||||
sourceSetInfo.addSourceSets(dependeeSourceSets, selfName, gradleModule, resolverCtx)
|
||||
}
|
||||
}
|
||||
if (sourceSet.actualPlatforms.platforms.singleOrNull() == KotlinPlatform.ANDROID) continue
|
||||
// if (sourceSet.actualPlatforms.platforms.singleOrNull() == KotlinPlatform.ANDROID) continue
|
||||
for (dependeeSourceSet in dependeeSourceSets) {
|
||||
val toDataNode = getSiblingKotlinModuleData(dependeeSourceSet, gradleModule, ideModule, resolverCtx) ?: continue
|
||||
addDependency(fromDataNode, toDataNode, dependeeSourceSet.isTestModule)
|
||||
@@ -747,7 +747,7 @@ open class KotlinMPPGradleProjectResolver : AbstractProjectResolverExtension() {
|
||||
}
|
||||
}
|
||||
for (target in mppModel.targets) {
|
||||
if (target.platform == KotlinPlatform.ANDROID) continue
|
||||
// if (target.platform == KotlinPlatform.ANDROID) continue
|
||||
for (compilation in target.compilations) {
|
||||
val moduleId = getKotlinModuleId(gradleModule, compilation, resolverCtx)
|
||||
val moduleDataNode = sourceSetsMap[moduleId] ?: continue
|
||||
|
||||
@@ -141,11 +141,15 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
|
||||
val sourceSets =
|
||||
(getSourceSets(kotlinExt) as? NamedDomainObjectContainer<Named>)?.asMap?.values ?: emptyList<Named>()
|
||||
|
||||
val getAndroidSourceSetDependencies = kotlinExt.javaClass.getMethodOrNull("getAndroidSourceSetDependencies", Project::class.java)
|
||||
@Suppress("UNCHECKED_CAST") val androidDeps =
|
||||
getAndroidSourceSetDependencies?.let { it(kotlinExt, project) } as Map<String, List<File>>?
|
||||
|
||||
// Some performance optimisation: do not build metadata dependencies if source set is not common
|
||||
val doBuildMetadataDependencies =
|
||||
project.properties["build_metadata_dependencies_for_actualised_source_sets"]?.toString()?.toBoolean()
|
||||
?: DEFAULT_BUILD_METADATA_DEPENDENCIES_FOR_ACTUALISED_SOURCE_SETS
|
||||
val allSourceSetsProtos = sourceSets.mapNotNull { buildSourceSet(it, dependencyResolver, project, dependencyMapper) }
|
||||
val allSourceSetsProtos = sourceSets.mapNotNull { buildSourceSet(it, dependencyResolver, project, dependencyMapper, androidDeps) }
|
||||
val allSourceSets = if (doBuildMetadataDependencies) {
|
||||
allSourceSetsProtos.map { proto -> proto.buildKotlinSourceSetImpl(true) }
|
||||
} else {
|
||||
@@ -173,7 +177,8 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
|
||||
gradleSourceSet: Named,
|
||||
dependencyResolver: DependencyResolver,
|
||||
project: Project,
|
||||
dependencyMapper: KotlinDependencyMapper
|
||||
dependencyMapper: KotlinDependencyMapper,
|
||||
androidDeps: Map<String, List<File>>?
|
||||
): KotlinSourceSetProto? {
|
||||
val sourceSetClass = gradleSourceSet.javaClass
|
||||
val getLanguageSettings = sourceSetClass.getMethodOrNull("getLanguageSettings") ?: return null
|
||||
@@ -188,7 +193,7 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
|
||||
val dependsOnSourceSets = (getDependsOn(gradleSourceSet) as? Set<Named>)?.mapTo(LinkedHashSet()) { it.name } ?: emptySet<String>()
|
||||
|
||||
val sourceSetDependenciesBuilder: () -> Array<KotlinDependencyId> = {
|
||||
buildSourceSetDependencies(gradleSourceSet, dependencyResolver, project).map { dependencyMapper.getId(it) }.distinct()
|
||||
buildSourceSetDependencies(gradleSourceSet, dependencyResolver, project, androidDeps).map { dependencyMapper.getId(it) }.distinct()
|
||||
.toTypedArray()
|
||||
}
|
||||
return KotlinSourceSetProto(
|
||||
@@ -592,7 +597,8 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
|
||||
private fun buildSourceSetDependencies(
|
||||
gradleSourceSet: Named,
|
||||
dependencyResolver: DependencyResolver,
|
||||
project: Project
|
||||
project: Project,
|
||||
androidDeps: Map<String, List<File>>?
|
||||
): List<KotlinDependency> {
|
||||
return ArrayList<KotlinDependency>().apply {
|
||||
val transformationBuilder = MetadataDependencyTransformationBuilder(gradleSourceSet)
|
||||
@@ -608,6 +614,8 @@ class KotlinMPPGradleModelBuilder : ModelBuilderService {
|
||||
this += buildDependencies(
|
||||
gradleSourceSet, dependencyResolver, "getRuntimeOnlyMetadataConfigurationName", "RUNTIME", project, transformationBuilder
|
||||
)
|
||||
|
||||
androidDeps?.get(gradleSourceSet.name)?.let { this += DefaultFileCollectionDependency(it) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,10 +48,6 @@ dependencies {
|
||||
compile("com.google.code.gson:gson:${rootProject.extra["versions.jar.gson"]}")
|
||||
compile("de.undercouch:gradle-download-task:4.0.2")
|
||||
|
||||
compileOnly("com.android.tools.build:gradle:2.0.0")
|
||||
compileOnly("com.android.tools.build:gradle-core:2.0.0")
|
||||
compileOnly("com.android.tools.build:builder:2.0.0")
|
||||
compileOnly("com.android.tools.build:builder-model:2.0.0")
|
||||
compileOnly("org.codehaus.groovy:groovy-all:2.4.12")
|
||||
compileOnly(gradleApi())
|
||||
|
||||
@@ -70,9 +66,21 @@ dependencies {
|
||||
})
|
||||
|
||||
// com.android.tools.build:gradle has ~50 unneeded transitive dependencies
|
||||
compileOnly("com.android.tools.build:gradle:3.0.0") { isTransitive = false }
|
||||
compileOnly("com.android.tools.build:gradle-core:3.0.0") { isTransitive = false }
|
||||
compileOnly("com.android.tools.build:builder-model:3.0.0") { isTransitive = false }
|
||||
compileOnly("com.android.tools:common:26.6.0-alpha12") { isTransitive = false }
|
||||
compileOnly("com.android.tools:repository:26.6.0-alpha12") { isTransitive = false }
|
||||
compileOnly("com.android.tools:sdklib:26.6.0-alpha12") { isTransitive = false }
|
||||
compileOnly("com.android.tools.lint:lint-gradle-api:26.6.0-alpha12") { isTransitive = false }
|
||||
compileOnly("com.android.tools.build:gradle:3.6.0-alpha12") //{ isTransitive = false }
|
||||
compileOnly("com.android.tools.build:gradle-api:3.6.0-alpha12") { isTransitive = false }
|
||||
compileOnly("com.android.tools.build:builder-model:3.6.0-alpha12") { isTransitive = false }
|
||||
compileOnly("com.android.tools.build:builder:3.6.0-alpha12") { isTransitive = false }
|
||||
// compileOnly("com.android.tools.build:builder:3.5.0") { isTransitive = false }
|
||||
// compileOnly("com.android.tools:common:26.6.0-alpha12") { isTransitive = false }
|
||||
// compileOnly("com.android.tools:repository:26.6.0-alpha12") { isTransitive = false }
|
||||
// compileOnly("com.android.tools.build:gradle:3.6.0-alpha12") { isTransitive = false }
|
||||
// compileOnly("com.android.tools.build:gradle-api:3.6.0-alpha12") { isTransitive = false }
|
||||
// compileOnly("com.android.tools.build:builder-model:3.6.0-alpha12") { isTransitive = false }
|
||||
// compileOnly("com.android.tools.build:builder:3.6.0-alpha12") { isTransitive = false }
|
||||
|
||||
testCompile(intellijDep()) { includeJars( "junit", "serviceMessages", rootProject = rootProject) }
|
||||
|
||||
|
||||
+127
@@ -5,13 +5,30 @@
|
||||
|
||||
package org.jetbrains.kotlin.gradle.dsl
|
||||
|
||||
import com.android.build.gradle.BaseExtension
|
||||
import com.android.build.gradle.internal.LoggerWrapper
|
||||
import com.android.build.gradle.internal.SdkLocationSourceSet
|
||||
import com.android.build.gradle.internal.SdkLocator
|
||||
import com.android.build.gradle.internal.publishing.AndroidArtifacts
|
||||
import com.android.build.gradle.internal.publishing.AndroidArtifacts.ARTIFACT_TYPE
|
||||
import com.android.sdklib.IAndroidTarget
|
||||
import com.android.sdklib.repository.AndroidSdkHandler
|
||||
import com.android.sdklib.repository.LoggerProgressIndicatorWrapper
|
||||
import groovy.lang.Closure
|
||||
import org.gradle.api.InvalidUserCodeException
|
||||
import org.gradle.api.NamedDomainObjectCollection
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.*
|
||||
import org.gradle.api.artifacts.component.ModuleComponentSelector
|
||||
import org.gradle.api.artifacts.result.ResolvedComponentResult
|
||||
import org.gradle.api.internal.artifacts.dependencies.DefaultExternalModuleDependency
|
||||
import org.gradle.internal.component.external.model.DefaultModuleComponentIdentifier
|
||||
import org.gradle.util.ConfigureUtil
|
||||
import org.jetbrains.kotlin.gradle.plugin.*
|
||||
import org.jetbrains.kotlin.gradle.plugin.mpp.*
|
||||
import org.jetbrains.kotlin.gradle.utils.isGradleVersionAtLeast
|
||||
import org.jetbrains.kotlin.gradle.utils.lowerCamelCaseName
|
||||
import java.io.File
|
||||
|
||||
open class KotlinMultiplatformExtension :
|
||||
KotlinProjectExtension(),
|
||||
@@ -53,6 +70,116 @@ open class KotlinMultiplatformExtension :
|
||||
internal val rootSoftwareComponent: KotlinSoftwareComponent by lazy {
|
||||
KotlinSoftwareComponentWithCoordinatesAndPublication("kotlin", targets)
|
||||
}
|
||||
|
||||
private fun getAndroidSdkJar(project: Project): File? {
|
||||
val androidExtension = project.extensions.findByName("android") as BaseExtension? ?: return null
|
||||
val sdkLocation = SdkLocator.getSdkLocation(SdkLocationSourceSet(project.rootDir)).directory ?: return null
|
||||
val sdkHandler = AndroidSdkHandler.getInstance(sdkLocation)
|
||||
val logger = LoggerProgressIndicatorWrapper(LoggerWrapper(project.logger))
|
||||
val androidTarget = sdkHandler.getAndroidTargetManager(logger).getTargetFromHashString(androidExtension.compileSdkVersion, logger)
|
||||
return File(androidTarget.getPath(IAndroidTarget.ANDROID_JAR))
|
||||
}
|
||||
|
||||
fun getAndroidSourceSetDependencies(project: Project): Map<String, List<File>?> {
|
||||
val ext = project.extensions.getByName("android") as BaseExtension
|
||||
val androidSdkJar = getAndroidSdkJar(project) ?: return emptyMap()
|
||||
|
||||
val sourceSet2Impl = ext.sourceSets.map {
|
||||
lowerCamelCaseName("android", it.name) to project.configurations.getByName(it.implementationConfigurationName)
|
||||
}.toMap()
|
||||
|
||||
val allImplConfigs = sourceSet2Impl.values.toSet()
|
||||
val impl2CompileClasspath = mapImplementationToCompileClasspathConfiguration(project, allImplConfigs)
|
||||
|
||||
return sourceSet2Impl.mapValues { entry ->
|
||||
val compileClasspathConf = impl2CompileClasspath[entry.value] ?: return@mapValues null
|
||||
|
||||
val dependencies = findDependencies(allImplConfigs, entry.value)
|
||||
|
||||
val selfResolved = dependencies.filterIsInstance<SelfResolvingDependency>().flatMap { it.resolve() }
|
||||
val resolvedExternal = dependencies.filterIsInstance<DefaultExternalModuleDependency>()
|
||||
.flatMap { collectDependencies(it.module, compileClasspathConf) }
|
||||
|
||||
selfResolved + resolvedExternal + androidSdkJar
|
||||
}.toMap()
|
||||
}
|
||||
|
||||
@Suppress("UnstableApiUsage")
|
||||
private fun collectDependencies(
|
||||
module: ModuleIdentifier,
|
||||
compileClasspathConf: Configuration
|
||||
): List<File> {
|
||||
val viewConfig: (ArtifactView.ViewConfiguration) -> Unit = { config ->
|
||||
config.attributes { it.attribute(ARTIFACT_TYPE, AndroidArtifacts.ArtifactType.JAR.type) }
|
||||
config.isLenient = true
|
||||
}
|
||||
|
||||
val resolvedArtifacts = compileClasspathConf.incoming.artifactView(viewConfig).artifacts.mapNotNull {
|
||||
val id = (it.id.componentIdentifier as? DefaultModuleComponentIdentifier)?.moduleIdentifier ?: return@mapNotNull null
|
||||
id to it
|
||||
}.toMap()
|
||||
|
||||
val resolutionResults = compileClasspathConf.incoming.resolutionResult.allComponents.mapNotNull {
|
||||
val id = it.moduleVersion?.module ?: return@mapNotNull null
|
||||
id to it
|
||||
}.toMap()
|
||||
|
||||
val deps = HashSet<ModuleIdentifier>().also { doCollectDependencies(listOf(module), resolutionResults, it) }
|
||||
return deps.mapNotNull { resolvedArtifacts[it] }.map { it.file }
|
||||
}
|
||||
|
||||
@Suppress("UnstableApiUsage")
|
||||
private tailrec fun doCollectDependencies(
|
||||
modules: List<ModuleIdentifier>,
|
||||
resolutionResults: Map<ModuleIdentifier, ResolvedComponentResult>,
|
||||
result: MutableSet<ModuleIdentifier>
|
||||
) {
|
||||
if (modules.isEmpty()) return
|
||||
val newModules = modules.filter { result.add(it) }.mapNotNull { resolutionResults[it] }.flatMap { it.dependencies }
|
||||
.mapNotNull { (it.requested as? ModuleComponentSelector)?.moduleIdentifier }
|
||||
doCollectDependencies(newModules, resolutionResults, result)
|
||||
}
|
||||
|
||||
private fun findDependencies(implConfigs: Set<Configuration>, conf: Configuration): Set<Dependency> {
|
||||
return HashSet<Dependency>().also { doFindDependencies(implConfigs, listOf(conf), it) }
|
||||
}
|
||||
|
||||
private tailrec fun doFindDependencies(
|
||||
implConfigs: Set<Configuration>, configs: List<Configuration>, result: MutableSet<Dependency>,
|
||||
visited: MutableSet<Configuration> = HashSet()
|
||||
) {
|
||||
if (configs.isEmpty()) return
|
||||
result.addAll(configs.flatMap { it.dependencies })
|
||||
doFindDependencies(implConfigs, configs.flatMap { it.extendsFrom }.filter { it !in implConfigs && visited.add(it) }, result)
|
||||
}
|
||||
|
||||
private fun mapImplementationToCompileClasspathConfiguration(
|
||||
project: Project,
|
||||
allImplConfigs: Set<Configuration>
|
||||
): Map<Configuration, Configuration> {
|
||||
val compileClasspathConfigurations = project.configurations.filter { it.name.endsWith("CompileClasspath", true) }
|
||||
return compileClasspathConfigurations.flatMap { ccConf ->
|
||||
findImplementationConfigurations(allImplConfigs, ccConf).map { it to ccConf }
|
||||
}.toMap()
|
||||
}
|
||||
|
||||
private fun findImplementationConfigurations(
|
||||
allImplConfigs: Set<Configuration>,
|
||||
compileClasspathConf: Configuration
|
||||
): Set<Configuration> {
|
||||
return HashSet<Configuration>().also { doFindImplementationConfigurations(allImplConfigs, listOf(compileClasspathConf), it) }
|
||||
}
|
||||
|
||||
private tailrec fun doFindImplementationConfigurations(
|
||||
allImplConfigs: Set<Configuration>,
|
||||
conf: List<Configuration>,
|
||||
result: MutableSet<Configuration>,
|
||||
visited: MutableSet<Configuration> = HashSet()
|
||||
) {
|
||||
if (conf.isEmpty()) return
|
||||
result.addAll(conf.flatMap { it.extendsFrom.filter { newConf -> allImplConfigs.contains(newConf) } })
|
||||
doFindImplementationConfigurations(allImplConfigs, conf.flatMap { it.extendsFrom }.filter { visited.add(it) }, result)
|
||||
}
|
||||
}
|
||||
|
||||
internal fun KotlinTarget.isProducedFromPreset(kotlinTargetPreset: KotlinTargetPreset<*>): Boolean =
|
||||
|
||||
Reference in New Issue
Block a user