build scripts: unify search for ultimate/community root at BuildPaths class

rewrite search of root in IdeaProjectLoaderUtil including current dir and bazel workspace location as well

GitOrigin-RevId: 384be637002ed4a21b5e097f84e098824a6d3592
This commit is contained in:
Leonid Shalupov
2025-05-07 21:18:36 +02:00
committed by intellij-monorepo-bot
parent eeff0201ce
commit 679440e5ef
4 changed files with 96 additions and 87 deletions

View File

@@ -38,12 +38,12 @@ class BuildPaths(
companion object {
@JvmStatic
val ULTIMATE_HOME: Path by lazy {
IdeaProjectLoaderUtil.guessUltimateHome(this::class.java)
IdeaProjectLoaderUtil.guessUltimateHome()
}
@JvmStatic
val COMMUNITY_ROOT: BuildDependenciesCommunityRoot by lazy {
IdeaProjectLoaderUtil.guessCommunityHome(this::class.java)
IdeaProjectLoaderUtil.guessCommunityHome()
}
}

View File

@@ -1,106 +1,121 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.intellij.build
import com.intellij.openapi.application.PathManager
import com.intellij.util.lang.UrlClassLoader
import com.jetbrains.plugin.structure.base.utils.exists
import org.jetbrains.annotations.ApiStatus
import org.jetbrains.intellij.build.dependencies.BuildDependenciesCommunityRoot
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import kotlin.io.path.absolute
object IdeaProjectLoaderUtil {
internal object IdeaProjectLoaderUtil {
private const val JPS_BOOTSTRAP_COMMUNITY_HOME_ENV_NAME = "JPS_BOOTSTRAP_COMMUNITY_HOME"
private const val ULTIMATE_REPO_MARKER_FILE = ".ultimate.root.marker"
private const val COMMUNITY_REPO_MARKER_FILE = "intellij.idea.community.main.iml"
private val ULTIMATE_REPO_MARKER_FILE = Path.of(".ultimate.root.marker")
private val COMMUNITY_REPO_MARKER_FILE = Path.of("intellij.idea.community.main.iml")
private const val INTELLIJ_BUILD_COMMUNITY_HOME_PATH = "intellij.build.community.home.path"
private const val INTELLIJ_BUILD_ULTIMATE_HOME_PATH = "intellij.build.ultimate.home.path"
private data class HomeSource(val moniker: String, val path: String?)
// collect a list of locations from most relevant to least relevant
private fun collectHomeSources(): List<HomeSource> = listOf(
HomeSource(
moniker = "property '$INTELLIJ_BUILD_ULTIMATE_HOME_PATH'",
path = System.getProperty(INTELLIJ_BUILD_ULTIMATE_HOME_PATH),
),
HomeSource(
moniker = "property '$INTELLIJ_BUILD_COMMUNITY_HOME_PATH'",
path = System.getProperty(INTELLIJ_BUILD_COMMUNITY_HOME_PATH),
),
HomeSource(
moniker = "env '$JPS_BOOTSTRAP_COMMUNITY_HOME_ENV_NAME'",
path = System.getenv(JPS_BOOTSTRAP_COMMUNITY_HOME_ENV_NAME),
),
HomeSource(
moniker = "property '${PathManager.PROPERTY_HOME_PATH}'",
path = System.getProperty(PathManager.PROPERTY_HOME_PATH),
),
// Bazel workspace location
HomeSource(
moniker = "bazel env 'BUILD_WORKSPACE_DIRECTORY'",
path = System.getenv("BUILD_WORKSPACE_DIRECTORY"),
),
// current class jar or .class location
HomeSource(
moniker = "jar location",
path = getPathFromClass(javaClass),
),
HomeSource(
moniker = "current directory",
path = System.getProperty("user.dir"),
),
)
/**
* This method only for internal usage. Use [BuildPaths.ULTIMATE_HOME] instead.
* @param klass must be a class inside idea home directory, the one with `file` protocol. Jar files in the maven directory aren't accepted.
*/
@ApiStatus.Internal
fun guessUltimateHome(klass: Class<*>): Path {
val ultimateHomePathOverride = System.getProperty(INTELLIJ_BUILD_ULTIMATE_HOME_PATH)
if (ultimateHomePathOverride != null) {
val path = Paths.get(ultimateHomePathOverride)
require(path.toFile().exists()) {
("Ultimate home path: '" + path
+ "' passed via system property: '" + INTELLIJ_BUILD_ULTIMATE_HOME_PATH + " not exists")
}
return path
}
val start = getSomeRoot(klass)
var home: Path? = start
while (home != null) {
if (Files.exists(home.resolve(ULTIMATE_REPO_MARKER_FILE))) {
return home
}
home = home.parent
}
throw IllegalArgumentException("Cannot guess ultimate project home from root '" + start + "'" +
", marker file '" + ULTIMATE_REPO_MARKER_FILE + "'")
fun guessUltimateHome(): Path {
return searchForAnyMarkerFile(listOf(ULTIMATE_REPO_MARKER_FILE))
}
/**
* This method only for internal usage. Use [BuildPaths.COMMUNITY_ROOT] instead.
* @param klass must be a class inside idea home directory, the one with `file` protocol. Jar files in the maven directory aren't accepted.
*/
@ApiStatus.Internal
fun guessCommunityHome(klass: Class<*>): BuildDependenciesCommunityRoot {
val communityHomePathOverride = System.getProperty(INTELLIJ_BUILD_COMMUNITY_HOME_PATH)
if (communityHomePathOverride != null) {
val path = Paths.get(communityHomePathOverride)
require(path.toFile().exists()) {
("Community home path: '" + path
+ "' passed via system property: '" + INTELLIJ_BUILD_COMMUNITY_HOME_PATH + " not exists")
}
return BuildDependenciesCommunityRoot(path)
}
val start = getSomeRoot(klass)
var home: Path? = start
fun guessCommunityHome(): BuildDependenciesCommunityRoot {
val directMarker = COMMUNITY_REPO_MARKER_FILE
val inSubdirMarker = Path.of("community").resolve(COMMUNITY_REPO_MARKER_FILE)
while (home != null) {
if (Files.exists(home.resolve(COMMUNITY_REPO_MARKER_FILE))) {
return BuildDependenciesCommunityRoot(home)
}
val root = searchForAnyMarkerFile(listOf(directMarker, inSubdirMarker))
if (Files.exists(home.resolve("community").resolve(COMMUNITY_REPO_MARKER_FILE))) {
return BuildDependenciesCommunityRoot(home.resolve("community"))
}
home = home.parent
}
throw IllegalArgumentException("Cannot guess community project home from root '" + start + "'" +
", marker file '" + COMMUNITY_REPO_MARKER_FILE + "'")
return when {
root.resolve(directMarker).exists() -> root
root.resolve(inSubdirMarker).exists() -> root.resolve("community")
else -> error("should not happen")
}.let { BuildDependenciesCommunityRoot(it) }
}
private fun getSomeRoot(klass: Class<*>): Path {
// Under jps-bootstrap home is already known, reuse it
val communityHome = System.getenv(JPS_BOOTSTRAP_COMMUNITY_HOME_ENV_NAME)
if (communityHome != null) {
return Path.of(communityHome).normalize()
fun searchForAnyMarkerFile(markerFiles: Collection<Path>): Path {
val homeSources = collectHomeSources()
for (source in homeSources) {
if (source.path == null) {
continue
}
for (markerFile in markerFiles) {
val home = searchForMarkerFileUpwards(Paths.get(source.path), markerFile)
if (home != null) {
if (System.getProperty("intellij.build.search.for.repo.marker.debug") != null) {
println("Root found by ${source.moniker}: $home (resolved from ${source.path})")
}
return home.absolute()
}
}
}
val path = getPathFromClass(klass)
if (!path.toString().endsWith("class")) {
val ideaHomePath = System.getProperty("idea.home.path")
if (ideaHomePath != null) {
return Path.of(ideaHomePath)
}
throw IllegalArgumentException(
String.format("To guess idea home, you must provide class that resides in .class file inside of idea home dir. " +
"But provided %s resides in %s", klass, path))
}
return path
error(
"Cannot find marker file $markerFiles across sources:\n" +
homeSources.joinToString("\n") { " ${it.moniker}: ${it.path}" })
}
private fun getPathFromClass(klass: Class<*>): Path {
private fun searchForMarkerFileUpwards(start: Path, markerFile: Path): Path? {
var current = start
while (true) {
if (current.resolve(markerFile).exists()) {
return current
}
current = current.parent ?: return null
}
}
private fun getPathFromClass(klass: Class<*>): String {
val klassFileName = klass.getName().replace(klass.getPackageName() + ".", "")
val classFileURL = klass.getResource("$klassFileName.class")
checkNotNull(classFileURL) { "Could not get .class file location from class " + klass.getName() }
return Path.of(UrlClassLoader.urlToFilePath(classFileURL.path))
return UrlClassLoader.urlToFilePath(classFileURL.path)
}
}

View File

@@ -3,7 +3,7 @@ package org.jetbrains.intellij.build.dependencies
import kotlinx.coroutines.runBlocking
import org.jetbrains.intellij.build.BuildDependenciesJps
import org.jetbrains.intellij.build.IdeaProjectLoaderUtil
import org.jetbrains.intellij.build.BuildPaths
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
@@ -26,7 +26,7 @@ class BuildDependenciesJpsTest {
iml,
"debugger-agent",
BuildDependenciesConstants.INTELLIJ_DEPENDENCIES_URL,
communityRoot,
BuildPaths.COMMUNITY_ROOT,
null
)
assertTrue(root.pathString, root.pathString.endsWith("debugger-agent-1.9.jar"))
@@ -46,7 +46,7 @@ class BuildDependenciesJpsTest {
iml,
"debugger-agent",
BuildDependenciesConstants.INTELLIJ_DEPENDENCIES_URL,
communityRoot,
BuildPaths.COMMUNITY_ROOT,
null
)
assertEquals("must resolve to a local file from .m2/repository", localFile.pathString, resolved.pathString)
@@ -64,7 +64,7 @@ class BuildDependenciesJpsTest {
iml,
"debugger-agent",
BuildDependenciesConstants.INTELLIJ_DEPENDENCIES_URL,
communityRoot,
BuildPaths.COMMUNITY_ROOT,
null
)
}
@@ -82,7 +82,7 @@ class BuildDependenciesJpsTest {
iml,
"debugger-agent",
BuildDependenciesConstants.INTELLIJ_DEPENDENCIES_URL,
communityRoot,
BuildPaths.COMMUNITY_ROOT,
null
)
}
@@ -102,7 +102,7 @@ class BuildDependenciesJpsTest {
iml,
"debugger-agent",
BuildDependenciesConstants.INTELLIJ_DEPENDENCIES_URL,
communityRoot,
BuildPaths.COMMUNITY_ROOT,
null
)
assertEquals("must resolve to a local file from .m2/repository", localFile.pathString, resolved.pathString)
@@ -112,12 +112,8 @@ class BuildDependenciesJpsTest {
}
}
private val communityRoot by lazy {
IdeaProjectLoaderUtil.guessCommunityHome(javaClass)
}
private fun getTestDataRoot(): Path {
val testData = communityRoot.communityRoot.resolve("platform/build-scripts/tests/testData")
val testData = BuildPaths.COMMUNITY_ROOT.communityRoot.resolve("platform/build-scripts/tests/testData")
check(testData.isDirectory()) {
"not a directory: $testData"
}

View File

@@ -2,8 +2,7 @@
package org.jetbrains.intellij.build.pycharm.pythons
import com.intellij.util.system.OS
import org.jetbrains.intellij.build.IdeaProjectLoaderUtil
import org.jetbrains.intellij.build.dependencies.BuildDependenciesCommunityRoot
import org.jetbrains.intellij.build.BuildPaths
import org.jetbrains.intellij.build.dependencies.BuildDependenciesDownloader
import org.jetbrains.intellij.build.downloadFileToCacheLocation
import java.nio.file.Path
@@ -35,8 +34,7 @@ val Python.directoryName: String
get() = "$pyenvDefinition-$name-$revision-$os-$arch"
private val buildRoot =
BuildDependenciesCommunityRoot(IdeaProjectLoaderUtil.guessCommunityHome(BuildDependenciesCommunityRoot::class.java).communityRoot)
private val buildRoot = BuildPaths.COMMUNITY_ROOT
private val pyEnvHome = buildRoot.communityRoot / "out" / "pyenv"
private val pythonsHome = buildRoot.communityRoot / "out" / "pythons"