fix(tooling-api): avoid recreating ProjectConnection multiple times

Also, the TaskExecutionMessage now only accepts the tasks to run, not the project path and the Gradle distribution params
This commit is contained in:
Akash Yadav
2023-09-30 22:02:58 +05:30
parent 4c95232854
commit 1874b911e7
4 changed files with 26 additions and 64 deletions
@@ -348,19 +348,7 @@ class GradleBuildService : Service(), BuildService, IToolingApiClient,
override fun executeTasks(vararg tasks: String): CompletableFuture<TaskExecutionResult> {
checkServerStarted()
val message = TaskExecutionMessage(null, listOf(*tasks),
gradleDistributionParams)
return performBuildTasks(
server!!.executeTasks(message))
}
override fun executeProjectTasks(
projectPath: String,
vararg tasks: String
): CompletableFuture<TaskExecutionResult> {
checkServerStarted()
val message = TaskExecutionMessage(projectPath, listOf(*tasks),
gradleDistributionParams)
val message = TaskExecutionMessage(listOf(*tasks))
return performBuildTasks(
server!!.executeTasks(message))
}
@@ -68,26 +68,9 @@ interface BuildService {
* then it will be executed in the root project directory.
* @return A [CompletableFuture] which returns a list of [TaskExecutionResult]. The result
* contains a list of tasks that were executed and the result of the whole execution.
* @see BuildService.executeProjectTasks
*/
fun executeTasks(vararg tasks: String): CompletableFuture<TaskExecutionResult>
/**
* Execute the given tasks of the given project.
*
* @param projectPath The path of the project. All the tasks will be executed in this project.
* @param tasks The tasks to execute. These may or may not be fully qualified names of the
* project. If the task name is not fully qualified, then it will be executed in the given
* project path. For example, if the project path is ':app' and the task is 'assembleDebug',
* then, ':app:assembleDebug' task will be executed.
* @return A [CompletableFuture] which returns a list of [TaskExecutionResult]. The result
* contains a list of tasks that were executed and the result of the whole execution.
*/
fun executeProjectTasks(
projectPath: String,
vararg tasks: String,
): CompletableFuture<TaskExecutionResult>
/** Cancel any running build. */
fun cancelCurrentBuild(): CompletableFuture<BuildCancellationRequestResult>
}
@@ -39,7 +39,6 @@ import com.itsaky.androidide.tooling.api.messages.result.TaskExecutionResult.Fai
import com.itsaky.androidide.tooling.api.messages.result.TaskExecutionResult.Failure.UNSUPPORTED_BUILD_ARGUMENT
import com.itsaky.androidide.tooling.api.messages.result.TaskExecutionResult.Failure.UNSUPPORTED_CONFIGURATION
import com.itsaky.androidide.tooling.api.messages.result.TaskExecutionResult.Failure.UNSUPPORTED_GRADLE_VERSION
import com.itsaky.androidide.tooling.api.models.params.StringParameter
import com.itsaky.androidide.tooling.impl.internal.ProjectImpl
import com.itsaky.androidide.tooling.impl.sync.ModelBuilderException
import com.itsaky.androidide.tooling.impl.sync.RootModelBuilder
@@ -69,6 +68,7 @@ internal class ToolingApiServerImpl(private val project: ProjectImpl) :
private var initialized = false
private var client: IToolingApiClient? = null
private var connector: GradleConnector? = null
private var connection: ProjectConnection? = null
private var lastInitParams: InitializeProjectParams? = null
private var buildCancellationToken: CancellationTokenSource? = null
private val log = ILogger.newInstance(javaClass.simpleName)
@@ -85,9 +85,11 @@ internal class ToolingApiServerImpl(private val project: ProjectImpl) :
}
val stopWatch = StopWatch("Connection to project")
val isReinitializing = connector != null && connection != null && params == lastInitParams
if (connector != null && params == lastInitParams) {
log.info("Project is being reinitialized. Reusing connector instance...")
if (isReinitializing) {
log.info("Project is being reinitialized")
log.info("Reusing connector instance...")
} else {
// a new project is being initialized
// or the project is being initialized with different parameters
@@ -100,13 +102,22 @@ internal class ToolingApiServerImpl(private val project: ProjectImpl) :
lastInitParams = params
checkNotNull(connector) {
val connector = checkNotNull(connector) {
"Unable to create gradle connector for project directory: ${params.directory}"
}
notifyBeforeBuild(BuildInfo(emptyList()))
val connection = this.connector!!.connect()
if (isReinitializing) {
log.info("Reusing project connection...")
} else {
connection = connector.connect()
}
val connection = checkNotNull(this.connection) {
"Unable to create project connection for project directory: ${params.directory}"
}
stopWatch.lapFromLast("Project connection established")
val project = try {
@@ -115,8 +126,6 @@ internal class ToolingApiServerImpl(private val project: ProjectImpl) :
impl
} catch (err: Throwable) {
throw err
} finally {
connection.close()
}
stopWatch.lapFromLast("Project read successful")
@@ -152,26 +161,13 @@ internal class ToolingApiServerImpl(private val project: ProjectImpl) :
assertProjectInitialized()
log.debug("Received request to run tasks.", message)
Main.checkGradleWrapper()
var projectPath = message.projectPath
if (projectPath == null) {
projectPath = project.rootProjectPath
val connection = checkNotNull(this.connection) {
"ProjectConnection has not been initialized. Cannot execute tasks."
}
checkNotNull(projectPath) {
"Unable to determine root project path"
}
val project = this.project.run {
selectProject(StringParameter(projectPath)).get()
asGradleProject()
}
this.connector!!.forProjectDirectory(project.getMetadata().get().projectDir)
setupConnectorForGradleInstallation(this.connector!!, message.gradleDistribution)
val connection = this.connector!!.connect()
val builder = connection.newBuild()
// System.in and System.out are used for communication between this server and the
@@ -228,21 +224,15 @@ internal class ToolingApiServerImpl(private val project: ProjectImpl) :
}
private fun notifyBuildFailure(tasks: List<String>) {
if (client != null) {
client!!.onBuildFailed(BuildResult((tasks)))
}
client?.onBuildFailed(BuildResult((tasks)))
}
private fun notifyBuildSuccess(tasks: List<String>) {
if (client != null) {
client!!.onBuildSuccessful(BuildResult(tasks))
}
client?.onBuildSuccessful(BuildResult(tasks))
}
private fun notifyBeforeBuild(buildInfo: BuildInfo) {
if (client != null) {
client!!.prepareBuild(buildInfo)
}
client?.prepareBuild(buildInfo)
}
override fun cancelCurrentBuild(): CompletableFuture<BuildCancellationRequestResult> {
@@ -268,12 +258,15 @@ internal class ToolingApiServerImpl(private val project: ProjectImpl) :
override fun shutdown(): CompletableFuture<Void> {
return CompletableFuture.runAsync {
connection?.close()
connector?.disconnect()
Main.future?.cancel(true)
this.connection = null
this.connector = null
this.client = null
this.buildCancellationToken = null
this.lastInitParams = null
Main.future = null
Main.client = null
@@ -22,7 +22,5 @@ package com.itsaky.androidide.tooling.api.messages
* @author Akash Yadav
*/
data class TaskExecutionMessage(
val projectPath: String?,
val tasks: List<String>,
val gradleDistribution: GradleDistributionParams = GradleDistributionParams.WRAPPER
)