Gradle, js, npm: resolver between projects
#KT-30530
This commit is contained in:
+9
-3
@@ -10,9 +10,7 @@ import org.gradle.api.artifacts.Configuration
|
|||||||
import org.gradle.api.artifacts.ResolvedArtifact
|
import org.gradle.api.artifacts.ResolvedArtifact
|
||||||
import org.gradle.api.artifacts.ResolvedDependency
|
import org.gradle.api.artifacts.ResolvedDependency
|
||||||
import org.gradle.api.artifacts.component.ProjectComponentIdentifier
|
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.internal.ProcessedFilesCache
|
||||||
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
|
|
||||||
|
|
||||||
internal class GradleNodeModulesBuilder(val project: Project) : AutoCloseable {
|
internal class GradleNodeModulesBuilder(val project: Project) : AutoCloseable {
|
||||||
companion object {
|
companion object {
|
||||||
@@ -28,6 +26,8 @@ internal class GradleNodeModulesBuilder(val project: Project) : AutoCloseable {
|
|||||||
GradleNodeModule(it, dir.resolve(it))
|
GradleNodeModule(it, dir.resolve(it))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val projects = mutableSetOf<Project>()
|
||||||
|
|
||||||
fun visitConfiguration(configuration: Configuration) {
|
fun visitConfiguration(configuration: Configuration) {
|
||||||
if (configuration.isCanBeResolved) {
|
if (configuration.isCanBeResolved) {
|
||||||
configuration.resolvedConfiguration.firstLevelModuleDependencies.forEach {
|
configuration.resolvedConfiguration.firstLevelModuleDependencies.forEach {
|
||||||
@@ -57,7 +57,13 @@ internal class GradleNodeModulesBuilder(val project: Project) : AutoCloseable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
artifacts.forEach { artifact ->
|
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) {
|
cache.getOrCompute(artifact.file) {
|
||||||
lazyDirName
|
lazyDirName
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -56,14 +56,14 @@ data class NpmDependency(
|
|||||||
return when (result) {
|
return when (result) {
|
||||||
is AlreadyInProgress -> null
|
is AlreadyInProgress -> null
|
||||||
is AlreadyResolved -> {
|
is AlreadyResolved -> {
|
||||||
check(this in result.resolution.dependencies) {
|
check(this in result.resolution.npmPackage!!.npmDependencies) {
|
||||||
"Project hierarchy is already resolved in NPM without $this"
|
"Project hierarchy is already resolved in NPM without $this"
|
||||||
}
|
}
|
||||||
|
|
||||||
result.resolution
|
result.resolution
|
||||||
}
|
}
|
||||||
is ResolvedNow -> {
|
is ResolvedNow -> {
|
||||||
check(this in result.resolution.dependencies) {
|
check(this in result.resolution.npmPackage!!.npmDependencies) {
|
||||||
"$this was not visited during resolution"
|
"$this was not visited during resolution"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+27
-30
@@ -28,7 +28,7 @@ import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates `package.json` file for projects with npm or js dependencies and
|
* 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`.
|
* 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`
|
* 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
|
val resolved = process?.resolved
|
||||||
|
|
||||||
return if (resolved != null) AlreadyResolved(resolved)
|
return if (resolved != null) AlreadyResolved(ProjectData[project]!!.resolved!!)
|
||||||
else {
|
else {
|
||||||
val resolver = NpmResolver(rootProject)
|
NpmResolver(rootProject).resolve(rootProject, null)!!
|
||||||
check(resolver.resolve(rootProject, null))
|
ResolvedNow(ProjectData[project]!!.resolved!!)
|
||||||
val resolution = ResolvedProject(resolver.npmPackages, resolver.requiredByTasks)
|
|
||||||
ProjectData[rootProject]!!.resolved = resolution
|
|
||||||
|
|
||||||
ResolvedNow(resolution)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,7 +57,7 @@ internal class NpmResolver private constructor(val rootProject: Project) {
|
|||||||
?: error("NPM dependencies should be resolved$reason")
|
?: error("NPM dependencies should be resolved$reason")
|
||||||
|
|
||||||
fun checkRequiredDependencies(project: Project, target: RequiresNpmDependencies) {
|
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()
|
val targetRequired = required[target]?.toSet() ?: setOf()
|
||||||
|
|
||||||
target.requiredNpmDependencies.forEach {
|
target.requiredNpmDependencies.forEach {
|
||||||
@@ -98,13 +94,9 @@ internal class NpmResolver private constructor(val rootProject: Project) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class ResolvedProject(
|
class ResolvedProject(
|
||||||
val npmPackages: Collection<NpmPackage>,
|
val npmPackage: NpmPackage?,
|
||||||
val requiredByTasks: Map<RequiresNpmDependencies, Collection<RequiredKotlinJsDependency>>
|
val taskDependencies: Map<RequiresNpmDependencies, Collection<RequiredKotlinJsDependency>>
|
||||||
) {
|
)
|
||||||
val dependencies by lazy {
|
|
||||||
npmPackages.flatMapTo(mutableSetOf()) { it.npmDependencies }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NpmPackage(
|
class NpmPackage(
|
||||||
val project: Project,
|
val project: Project,
|
||||||
@@ -128,19 +120,15 @@ internal class NpmResolver private constructor(val rootProject: Project) {
|
|||||||
packageManagerInstalled = true
|
packageManagerInstalled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getOrResolve(project: Project, parentGradleModules: GradleNodeModulesBuilder?): ResolvedProject {
|
||||||
|
return (ProjectData[project] ?: resolve(project, parentGradleModules)!!).resolved!!
|
||||||
|
}
|
||||||
|
|
||||||
private fun resolve(
|
private fun resolve(
|
||||||
project: Project,
|
project: Project,
|
||||||
parentGradleModules: GradleNodeModulesBuilder?
|
parentGradleModules: GradleNodeModulesBuilder?
|
||||||
): Boolean {
|
): ProjectData? {
|
||||||
val existedProcess = ProjectData[project]
|
val result = ProjectData().also {
|
||||||
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[project] = it
|
ProjectData[project] = it
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,10 +137,10 @@ internal class NpmResolver private constructor(val rootProject: Project) {
|
|||||||
else GradleNodeModulesBuilder(project)
|
else GradleNodeModulesBuilder(project)
|
||||||
|
|
||||||
project.subprojects.forEach {
|
project.subprojects.forEach {
|
||||||
resolve(it, gradleModules)
|
getOrResolve(it, gradleModules)
|
||||||
}
|
}
|
||||||
|
|
||||||
val npmPackage = extractNpmPackage(project, gradleModules)
|
val npmPackage = extractNpmPackage(project, gradleModules, parentGradleModules)
|
||||||
if (npmPackage != null) {
|
if (npmPackage != null) {
|
||||||
npmPackage.savePackageJson(gson)
|
npmPackage.savePackageJson(gson)
|
||||||
|
|
||||||
@@ -162,6 +150,8 @@ internal class NpmResolver private constructor(val rootProject: Project) {
|
|||||||
npmPackages.add(npmPackage)
|
npmPackages.add(npmPackage)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result.resolved = ResolvedProject(npmPackage, requiredByTasks)
|
||||||
|
|
||||||
if (project == rootProject) {
|
if (project == rootProject) {
|
||||||
if (npmPackages.isNotEmpty()) {
|
if (npmPackages.isNotEmpty()) {
|
||||||
requirePackageManagerSetup()
|
requirePackageManagerSetup()
|
||||||
@@ -173,12 +163,13 @@ internal class NpmResolver private constructor(val rootProject: Project) {
|
|||||||
gradleModules.close()
|
gradleModules.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun extractNpmPackage(
|
private fun extractNpmPackage(
|
||||||
project: Project,
|
project: Project,
|
||||||
gradleComponents: GradleNodeModulesBuilder
|
gradleComponents: GradleNodeModulesBuilder,
|
||||||
|
parentGradleModules: GradleNodeModulesBuilder?
|
||||||
): NpmPackage? {
|
): NpmPackage? {
|
||||||
val packageJson = PackageJson(project.name, project.version.toString())
|
val packageJson = PackageJson(project.name, project.version.toString())
|
||||||
val npmDependencies = mutableSetOf<NpmDependency>()
|
val npmDependencies = mutableSetOf<NpmDependency>()
|
||||||
@@ -192,6 +183,12 @@ internal class NpmResolver private constructor(val rootProject: Project) {
|
|||||||
val relativePath = it.path.relativeTo(NpmProject[project].nodeWorkDir)
|
val relativePath = it.path.relativeTo(NpmProject[project].nodeWorkDir)
|
||||||
packageJson.dependencies[it.name] = "file:$relativePath"
|
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 {
|
project.nodeJs.packageJsonHandlers.forEach {
|
||||||
|
|||||||
+2
-2
@@ -111,8 +111,8 @@ object Yarn : NpmApi {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun shouldHoistGradleNodeModules(project: Project): Boolean =
|
// override fun shouldHoistGradleNodeModules(project: Project): Boolean =
|
||||||
project.rootProject.yarn.useWorkspaces
|
// project.rootProject.yarn.useWorkspaces
|
||||||
|
|
||||||
@Suppress("EXPOSED_PARAMETER_TYPE")
|
@Suppress("EXPOSED_PARAMETER_TYPE")
|
||||||
override fun resolveRootProject(
|
override fun resolveRootProject(
|
||||||
|
|||||||
Reference in New Issue
Block a user