Gradle, js, npm: resolver between projects

#KT-30530
This commit is contained in:
Sergey Rostov
2019-05-15 18:14:04 +03:00
parent 5cfe117fd7
commit eb3c701040
4 changed files with 40 additions and 37 deletions
@@ -10,9 +10,7 @@ import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ResolvedArtifact
import org.gradle.api.artifacts.ResolvedDependency
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions
import org.jetbrains.kotlin.gradle.internal.ProcessedFilesCache
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
internal class GradleNodeModulesBuilder(val project: Project) : AutoCloseable {
companion object {
@@ -28,6 +26,8 @@ internal class GradleNodeModulesBuilder(val project: Project) : AutoCloseable {
GradleNodeModule(it, dir.resolve(it))
}
val projects = mutableSetOf<Project>()
fun visitConfiguration(configuration: Configuration) {
if (configuration.isCanBeResolved) {
configuration.resolvedConfiguration.firstLevelModuleDependencies.forEach {
@@ -57,7 +57,13 @@ internal class GradleNodeModulesBuilder(val project: Project) : AutoCloseable {
}
artifacts.forEach { artifact ->
if (artifact.id.componentIdentifier !is ProjectComponentIdentifier) {
val componentIdentifier = artifact.id.componentIdentifier
if (componentIdentifier is ProjectComponentIdentifier) {
val dependentProject = project.findProject(componentIdentifier.projectPath)
?: error("Cannot find project ${componentIdentifier.projectPath}")
projects.add(dependentProject)
} else {
cache.getOrCompute(artifact.file) {
lazyDirName
}
@@ -56,14 +56,14 @@ data class NpmDependency(
return when (result) {
is AlreadyInProgress -> null
is AlreadyResolved -> {
check(this in result.resolution.dependencies) {
check(this in result.resolution.npmPackage!!.npmDependencies) {
"Project hierarchy is already resolved in NPM without $this"
}
result.resolution
}
is ResolvedNow -> {
check(this in result.resolution.dependencies) {
check(this in result.resolution.npmPackage!!.npmDependencies) {
"$this was not visited during resolution"
}
@@ -28,7 +28,7 @@ import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
/**
* Generates `package.json` file for projects with npm or js dependencies and
* runs selected [NodeJsRootExtension.packageManager] to download and install it.
* runs selected [NodeJsRootExtension.packageManager] to download and install all of it's dependencies.
*
* All [NpmDependency] for configurations related to kotlin/js will be added to `package.json`.
* For external gradle modules, fake npm packages will be created and added to `package.json`
@@ -45,14 +45,10 @@ internal class NpmResolver private constructor(val rootProject: Project) {
val resolved = process?.resolved
return if (resolved != null) AlreadyResolved(resolved)
return if (resolved != null) AlreadyResolved(ProjectData[project]!!.resolved!!)
else {
val resolver = NpmResolver(rootProject)
check(resolver.resolve(rootProject, null))
val resolution = ResolvedProject(resolver.npmPackages, resolver.requiredByTasks)
ProjectData[rootProject]!!.resolved = resolution
ResolvedNow(resolution)
NpmResolver(rootProject).resolve(rootProject, null)!!
ResolvedNow(ProjectData[project]!!.resolved!!)
}
}
@@ -61,7 +57,7 @@ internal class NpmResolver private constructor(val rootProject: Project) {
?: error("NPM dependencies should be resolved$reason")
fun checkRequiredDependencies(project: Project, target: RequiresNpmDependencies) {
val required = requireResolved(project, "before $target execution").requiredByTasks
val required = requireResolved(project, "before $target execution").taskDependencies
val targetRequired = required[target]?.toSet() ?: setOf()
target.requiredNpmDependencies.forEach {
@@ -98,13 +94,9 @@ internal class NpmResolver private constructor(val rootProject: Project) {
}
class ResolvedProject(
val npmPackages: Collection<NpmPackage>,
val requiredByTasks: Map<RequiresNpmDependencies, Collection<RequiredKotlinJsDependency>>
) {
val dependencies by lazy {
npmPackages.flatMapTo(mutableSetOf()) { it.npmDependencies }
}
}
val npmPackage: NpmPackage?,
val taskDependencies: Map<RequiresNpmDependencies, Collection<RequiredKotlinJsDependency>>
)
class NpmPackage(
val project: Project,
@@ -128,19 +120,15 @@ internal class NpmResolver private constructor(val rootProject: Project) {
packageManagerInstalled = true
}
private fun getOrResolve(project: Project, parentGradleModules: GradleNodeModulesBuilder?): ResolvedProject {
return (ProjectData[project] ?: resolve(project, parentGradleModules)!!).resolved!!
}
private fun resolve(
project: Project,
parentGradleModules: GradleNodeModulesBuilder?
): Boolean {
val existedProcess = ProjectData[project]
if (existedProcess != null) {
if (existedProcess.resolved != null) error("yarn dependencies for $project already resolved")
else {
// called inside resolution of classpath (from visitTarget)
// we should return as we are already in progress
return false
}
} else ProjectData().also {
): ProjectData? {
val result = ProjectData().also {
ProjectData[project] = it
}
@@ -149,10 +137,10 @@ internal class NpmResolver private constructor(val rootProject: Project) {
else GradleNodeModulesBuilder(project)
project.subprojects.forEach {
resolve(it, gradleModules)
getOrResolve(it, gradleModules)
}
val npmPackage = extractNpmPackage(project, gradleModules)
val npmPackage = extractNpmPackage(project, gradleModules, parentGradleModules)
if (npmPackage != null) {
npmPackage.savePackageJson(gson)
@@ -162,6 +150,8 @@ internal class NpmResolver private constructor(val rootProject: Project) {
npmPackages.add(npmPackage)
}
result.resolved = ResolvedProject(npmPackage, requiredByTasks)
if (project == rootProject) {
if (npmPackages.isNotEmpty()) {
requirePackageManagerSetup()
@@ -173,12 +163,13 @@ internal class NpmResolver private constructor(val rootProject: Project) {
gradleModules.close()
}
return true
return result
}
private fun extractNpmPackage(
project: Project,
gradleComponents: GradleNodeModulesBuilder
gradleComponents: GradleNodeModulesBuilder,
parentGradleModules: GradleNodeModulesBuilder?
): NpmPackage? {
val packageJson = PackageJson(project.name, project.version.toString())
val npmDependencies = mutableSetOf<NpmDependency>()
@@ -192,6 +183,12 @@ internal class NpmResolver private constructor(val rootProject: Project) {
val relativePath = it.path.relativeTo(NpmProject[project].nodeWorkDir)
packageJson.dependencies[it.name] = "file:$relativePath"
}
gradleComponents.projects.forEach {
val npmPackage = getOrResolve(it, parentGradleModules).npmPackage
if (npmPackage != null) {
packageJson.dependencies[npmPackage.packageJson.name] = npmPackage.packageJson.version
}
}
}
project.nodeJs.packageJsonHandlers.forEach {
@@ -111,8 +111,8 @@ object Yarn : NpmApi {
return false
}
override fun shouldHoistGradleNodeModules(project: Project): Boolean =
project.rootProject.yarn.useWorkspaces
// override fun shouldHoistGradleNodeModules(project: Project): Boolean =
// project.rootProject.yarn.useWorkspaces
@Suppress("EXPOSED_PARAMETER_TYPE")
override fun resolveRootProject(