diff --git a/app/src/main/java/com/itsaky/androidide/activities/editor/ProjectHandlerActivity.kt b/app/src/main/java/com/itsaky/androidide/activities/editor/ProjectHandlerActivity.kt index 7a9f48cd6..ae2f9bf9d 100644 --- a/app/src/main/java/com/itsaky/androidide/activities/editor/ProjectHandlerActivity.kt +++ b/app/src/main/java/com/itsaky/androidide/activities/editor/ProjectHandlerActivity.kt @@ -27,7 +27,6 @@ import androidx.annotation.GravityInt import androidx.appcompat.app.AlertDialog import com.blankj.utilcode.util.SizeUtils import com.blankj.utilcode.util.ThreadUtils -import com.google.common.collect.ImmutableList import com.itsaky.androidide.R import com.itsaky.androidide.R.string import com.itsaky.androidide.databinding.LayoutSearchProjectBinding @@ -39,17 +38,15 @@ import com.itsaky.androidide.handlers.LspHandler.destroyLanguageServers import com.itsaky.androidide.lookup.Lookup import com.itsaky.androidide.lsp.IDELanguageClientImpl import com.itsaky.androidide.preferences.internal.NO_OPENED_PROJECT -import com.itsaky.androidide.preferences.internal.gradleInstallationDir import com.itsaky.androidide.preferences.internal.lastOpenedProject import com.itsaky.androidide.projects.ProjectManagerImpl -import com.itsaky.androidide.projects.api.AndroidModule import com.itsaky.androidide.projects.api.GradleProject import com.itsaky.androidide.projects.builder.BuildService import com.itsaky.androidide.services.builder.GradleBuildService import com.itsaky.androidide.services.builder.GradleBuildServiceConnnection +import com.itsaky.androidide.services.builder.gradleDistributionParams import com.itsaky.androidide.tasks.executeAsyncProvideError import com.itsaky.androidide.tasks.executeWithProgress -import com.itsaky.androidide.tooling.api.IAndroidProject import com.itsaky.androidide.tooling.api.messages.AndroidInitializationParams import com.itsaky.androidide.tooling.api.messages.InitializeProjectParams import com.itsaky.androidide.tooling.api.messages.result.InitializeResult @@ -405,11 +402,13 @@ abstract class ProjectHandlerActivity : BaseEditorActivity() { } } - private fun createProjectInitParams(projectDir: File, - buildVariants: Map): InitializeProjectParams { + private fun createProjectInitParams( + projectDir: File, + buildVariants: Map + ): InitializeProjectParams { return InitializeProjectParams( projectDir.absolutePath, - gradleInstallationDir, + gradleDistributionParams, createAndroidParams(buildVariants) ) } diff --git a/app/src/main/java/com/itsaky/androidide/services/builder/GradleBuildService.java b/app/src/main/java/com/itsaky/androidide/services/builder/GradleBuildService.java index 2caa6d8f2..ce0b68804 100644 --- a/app/src/main/java/com/itsaky/androidide/services/builder/GradleBuildService.java +++ b/app/src/main/java/com/itsaky/androidide/services/builder/GradleBuildService.java @@ -18,7 +18,6 @@ package com.itsaky.androidide.services.builder; import static com.itsaky.androidide.managers.ToolsManager.getCommonAsset; -import static com.itsaky.androidide.preferences.internal.BuildPreferencesKt.getGradleInstallationDir; import static com.itsaky.androidide.preferences.internal.BuildPreferencesKt.isBuildCacheEnabled; import static com.itsaky.androidide.preferences.internal.BuildPreferencesKt.isDebugEnabled; import static com.itsaky.androidide.preferences.internal.BuildPreferencesKt.isInfoEnabled; @@ -36,10 +35,8 @@ import android.content.Context; import android.content.Intent; import android.os.IBinder; import android.text.TextUtils; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import com.blankj.utilcode.util.ResourceUtils; import com.blankj.utilcode.util.ThreadUtils; import com.blankj.utilcode.util.ZipUtils; @@ -68,9 +65,6 @@ import com.itsaky.androidide.tooling.api.messages.result.TaskExecutionResult; import com.itsaky.androidide.tooling.events.ProgressEvent; import com.itsaky.androidide.utils.Environment; import com.itsaky.androidide.utils.ILogger; - -import org.jetbrains.annotations.NotNull; - import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -80,13 +74,15 @@ import java.util.List; import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; +import org.jetbrains.annotations.NotNull; /** * A foreground service that handles interaction with the Gradle Tooling API. * * @author Akash Yadav */ -public class GradleBuildService extends Service implements BuildService, IToolingApiClient, ToolingServerRunner.Observer { +public class GradleBuildService extends Service implements BuildService, IToolingApiClient, + ToolingServerRunner.Observer { private static final ILogger LOG = newInstance("GradleBuildService"); private static final int NOTIFICATION_ID = R.string.app_name; @@ -95,11 +91,12 @@ public class GradleBuildService extends Service implements BuildService, IToolin private GradleServiceBinder mBinder = null; private boolean isToolingServerStarted = false; private boolean isBuildInProgress = false; - + /** * We do not provide direct access to GradleBuildService instance to the Tooling API launcher as - * it may cause memory leaks. Instead, we create another client object which forwards all calls to us. - * So, when the service is destroyed, we release the reference to the service from this client. + * it may cause memory leaks. Instead, we create another client object which forwards all calls to + * us. So, when the service is destroyed, we release the reference to the service from this + * client. */ private ForwardingToolingApiClient _toolingApiClient; private ToolingServerRunner toolingServerThread; @@ -175,7 +172,7 @@ public class GradleBuildService extends Service implements BuildService, IToolin if (server != null) { server.cancelCurrentBuild(); final var shutdown = server.shutdown(); - + //noinspection ConstantConditions if (shutdown != null) { try { @@ -192,12 +189,12 @@ public class GradleBuildService extends Service implements BuildService, IToolin toolingServerThread.interrupt(); toolingServerThread = null; } - + if (_toolingApiClient != null) { _toolingApiClient.setClient(null); _toolingApiClient = null; } - + if (outputReader != null) { outputReader.interrupt(); outputReader = null; @@ -214,21 +211,22 @@ public class GradleBuildService extends Service implements BuildService, IToolin } return mBinder; } - + @Override - public void onListenerStarted(@NotNull final IToolingApiServer server, @NotNull IProject projectProxy, @NotNull final ProcessStreamsHolder streams) { + public void onListenerStarted(@NotNull final IToolingApiServer server, + @NotNull IProject projectProxy, @NotNull final ProcessStreamsHolder streams) { startServerOutputReader(streams.err); this.server = server; Lookup.getDefault().update(BuildService.KEY_PROJECT_PROXY, projectProxy); this.isToolingServerStarted = true; } - + @Override public void onServerExited(int exitCode) { LOG.warn("Tooling API process terminated with exit code:", exitCode); stopForeground(true); } - + @NonNull @Override public IToolingApiClient getClient() { @@ -237,7 +235,7 @@ public class GradleBuildService extends Service implements BuildService, IToolin } return _toolingApiClient; } - + @Override public void logMessage(@NonNull LogLine line) { SERVER_LOGGER.log(line.priority, line.formattedTagAndMessage()); @@ -333,7 +331,8 @@ public class GradleBuildService extends Service implements BuildService, IToolin return false; } - final var projectRoot = Objects.requireNonNull(ProjectManagerImpl.getInstance().getProjectDir()); + final var projectRoot = Objects.requireNonNull( + ProjectManagerImpl.getInstance().getProjectDir()); if (!projectRoot.exists()) { return false; } @@ -346,7 +345,7 @@ public class GradleBuildService extends Service implements BuildService, IToolin return gradlew.exists() && gradleWrapperJar.exists() && gradleWrapperProps.exists(); } - public void setServerListener (@Nullable ToolingServerRunner.OnServerStartListener listener) { + public void setServerListener(@Nullable ToolingServerRunner.OnServerStartListener listener) { if (this.toolingServerThread != null) { this.toolingServerThread.setListener(listener); } @@ -401,7 +400,8 @@ public class GradleBuildService extends Service implements BuildService, IToolin @NonNull @Override - public CompletableFuture initializeProject(@NonNull InitializeProjectParams params) { + public CompletableFuture initializeProject( + @NonNull InitializeProjectParams params) { checkServerStarted(); Objects.requireNonNull(params); return performBuildTasks(server.initialize(params)); @@ -412,7 +412,8 @@ public class GradleBuildService extends Service implements BuildService, IToolin public CompletableFuture executeTasks(@NonNull String... tasks) { checkServerStarted(); final var message = - new TaskExecutionMessage(IProject.ROOT_PROJECT_PATH, Arrays.asList(tasks), getGradleInstallationDir()); + new TaskExecutionMessage(IProject.ROOT_PROJECT_PATH, Arrays.asList(tasks), + UtilKt.getGradleDistributionParams()); return performBuildTasks(server.executeTasks(message)); } @@ -422,7 +423,8 @@ public class GradleBuildService extends Service implements BuildService, IToolin @NonNull String projectPath, @NonNull String... tasks) { checkServerStarted(); final var message = - new TaskExecutionMessage(projectPath, Arrays.asList(tasks), getGradleInstallationDir()); + new TaskExecutionMessage(projectPath, Arrays.asList(tasks), + UtilKt.getGradleDistributionParams()); return performBuildTasks(server.executeTasks(message)); } @@ -496,7 +498,7 @@ public class GradleBuildService extends Service implements BuildService, IToolin this.eventListener = null; return this; } - + this.eventListener = wrap(eventListener); return this; } @@ -544,13 +546,15 @@ public class GradleBuildService extends Service implements BuildService, IToolin if (outputReader == null) { outputReader = new OutputReaderThread(err, SERVER_System_err::error); } - + if (!outputReader.isAlive()) { outputReader.start(); } } - - /** Handles events received from a Gradle build. */ + + /** + * Handles events received from a Gradle build. + */ public interface EventListener { /** diff --git a/app/src/main/java/com/itsaky/androidide/services/builder/util.kt b/app/src/main/java/com/itsaky/androidide/services/builder/util.kt new file mode 100644 index 000000000..4305ae2b2 --- /dev/null +++ b/app/src/main/java/com/itsaky/androidide/services/builder/util.kt @@ -0,0 +1,33 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.services.builder + +import com.itsaky.androidide.preferences.internal.gradleInstallationDir +import com.itsaky.androidide.tooling.api.messages.GradleDistributionParams + +/** + * The distribution params. This considers [gradleInstallationDir] preference as well. + */ +val gradleDistributionParams: GradleDistributionParams + get() { + if (gradleInstallationDir.isBlank()) { + return GradleDistributionParams.WRAPPER + } + + return GradleDistributionParams.forInstallationDir(gradleInstallationDir) + } \ No newline at end of file diff --git a/lsp/testing/src/main/java/com/itsaky/androidide/lsp/api/LSPTest.kt b/lsp/testing/src/main/java/com/itsaky/androidide/lsp/api/LSPTest.kt index 002ba6c8d..6c65613e8 100644 --- a/lsp/testing/src/main/java/com/itsaky/androidide/lsp/api/LSPTest.kt +++ b/lsp/testing/src/main/java/com/itsaky/androidide/lsp/api/LSPTest.kt @@ -85,18 +85,17 @@ abstract class LSPTest { mockkStatic(::tabSize) every { tabSize } returns 4 - val (server, project) = + val (server, project, result) = ToolingApiTestLauncher() .launchServer() + + assertThat(result?.isSuccessful).isTrue() + this.toolingProject = project this.toolingServer = server Lookup.getDefault().update(BuildService.KEY_PROJECT_PROXY, project) - server - .initialize(InitializeProjectParams(FileProvider.testProjectRoot().toFile().absolutePath)) - .get() - Environment.ANDROID_JAR = FileProvider.resources().resolve("android.jar").toFile() Environment.JAVA_HOME = File(System.getProperty("java.home")!!) registerServer() diff --git a/subprojects/projects/src/test/java/com/itsaky/androidide/projects/api/ModuleProjectTest.kt b/subprojects/projects/src/test/java/com/itsaky/androidide/projects/api/ModuleProjectTest.kt index 9280cdd21..126d36de2 100644 --- a/subprojects/projects/src/test/java/com/itsaky/androidide/projects/api/ModuleProjectTest.kt +++ b/subprojects/projects/src/test/java/com/itsaky/androidide/projects/api/ModuleProjectTest.kt @@ -46,9 +46,7 @@ class ModuleProjectTest { @Test fun test() { - val (server, project) = ToolingApiTestLauncher().launchServer() - val result = server.initialize(InitializeProjectParams(FileProvider.testProjectRoot().pathString)) - .get() + val (_, project, result) = ToolingApiTestLauncher().launchServer() assertThat(result?.isSuccessful).isTrue() Lookup.getDefault().register(BuildService.KEY_PROJECT_PROXY, project) diff --git a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/ToolingApiServerImpl.kt b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/ToolingApiServerImpl.kt index 3d2f7949b..a2eddbe56 100644 --- a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/ToolingApiServerImpl.kt +++ b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/ToolingApiServerImpl.kt @@ -20,6 +20,8 @@ package com.itsaky.androidide.tooling.impl import com.itsaky.androidide.tooling.api.IProject import com.itsaky.androidide.tooling.api.IToolingApiClient import com.itsaky.androidide.tooling.api.IToolingApiServer +import com.itsaky.androidide.tooling.api.messages.GradleDistributionParams +import com.itsaky.androidide.tooling.api.messages.GradleDistributionType import com.itsaky.androidide.tooling.api.messages.InitializeProjectParams import com.itsaky.androidide.tooling.api.messages.TaskExecutionMessage import com.itsaky.androidide.tooling.api.messages.result.BuildCancellationRequestResult @@ -86,7 +88,7 @@ internal class ToolingApiServerImpl(private val project: ProjectImpl) : log.debug("Got initialize request", params) val stopWatch = StopWatch("Connection to project") this.connector = GradleConnector.newConnector().forProjectDirectory(File(params.directory)) - setupConnectorForGradleInstallation(this.connector!!, params.gradleInstallation) + setupConnectorForGradleInstallation(this.connector!!, params.gradleDistribution) stopWatch.lap("Connector created") if (this.connector == null) { @@ -155,7 +157,7 @@ internal class ToolingApiServerImpl(private val project: ProjectImpl) : } this.connector!!.forProjectDirectory(project.getMetadata().get().projectDir) - setupConnectorForGradleInstallation(this.connector!!, message.gradleInstallation) + setupConnectorForGradleInstallation(this.connector!!, message.gradleDistribution) val connection = this.connector!!.connect() val builder = connection.newBuild() @@ -188,20 +190,29 @@ internal class ToolingApiServerImpl(private val project: ProjectImpl) : private fun setupConnectorForGradleInstallation( connector: GradleConnector, - gradleDistribution: String? + params: GradleDistributionParams ) { - if (gradleDistribution.isNullOrBlank()) { - log.info("Using Gradle wrapper for build...") - return - } - val file = File(gradleDistribution) - if (!file.exists() || !file.isDirectory) { - log.error("Specified Gradle installation does not exist:", gradleDistribution) - return - } + when (params.type) { + GradleDistributionType.GRADLE_WRAPPER -> { + log.info("Using Gradle wrapper for build...") + } - log.info("Using Gradle installation:", file.canonicalPath) - connector.useInstallation(file) + GradleDistributionType.GRADLE_INSTALLATION -> { + val file = File(params.value) + if (!file.exists() || !file.isDirectory) { + log.error("Specified Gradle installation does not exist:", params) + return + } + + log.info("Using Gradle installation:", file.canonicalPath) + connector.useInstallation(file) + } + + GradleDistributionType.GRADLE_VERSION -> { + log.info("Using Gradle version '${params.value}'") + connector.useGradleVersion(params.value) + } + } } private fun notifyBuildFailure(tasks: List) { diff --git a/subprojects/tooling-api-impl/src/test/java/com/itsaky/androidide/tooling/impl/MultiModuleAndroidProjectTest.kt b/subprojects/tooling-api-impl/src/test/java/com/itsaky/androidide/tooling/impl/MultiModuleAndroidProjectTest.kt index be4b67475..6283dd150 100644 --- a/subprojects/tooling-api-impl/src/test/java/com/itsaky/androidide/tooling/impl/MultiModuleAndroidProjectTest.kt +++ b/subprojects/tooling-api-impl/src/test/java/com/itsaky/androidide/tooling/impl/MultiModuleAndroidProjectTest.kt @@ -49,8 +49,8 @@ class MultiModuleAndroidProjectTest { @Test fun `test simple multi module project initialization`() { - val (server, project) = ToolingApiTestLauncher().launchServer() - server.initializeTestProject() + val (server, project, result) = ToolingApiTestLauncher().launchServer() + assertThat(result?.isSuccessful).isTrue() doAssertions(project, server) } @@ -200,10 +200,9 @@ class MultiModuleAndroidProjectTest { for ((agpVersion, gradleVersion) in versions) { client.agpVersion = agpVersion client.gradleVersion = gradleVersion - val (server, project) = ToolingApiTestLauncher().launchServer(client = client) - server.initializeTestProject() + val (server, project, result) = ToolingApiTestLauncher().launchServer(client = client) + assertThat(result?.isSuccessful).isTrue() doAssertions(project = project, server = server) - FileProvider.testProjectRoot().resolve(MultiVersionTestClient.buildFile).deleteExisting() } } } @@ -211,36 +210,38 @@ class MultiModuleAndroidProjectTest { @Test @Throws(CIOnlyException::class) fun `test CI-only latest tested AGP version warning`() { - val log = CollectingLogger() - val agpVersion = "8.1.1" - val client = MultiVersionTestClient(agpVersion = agpVersion, gradleVersion = "8.2", log = log) - val (server, _) = ToolingApiTestLauncher().launchServer(client = client, log = log) - val result = server.initializeTestProject() - val output = log.toString() + ciOnlyTest { + val log = CollectingLogger() + val agpVersion = "8.1.1" + val client = MultiVersionTestClient(agpVersion = agpVersion, gradleVersion = "8.1", log = log) + val (_, _, result) = ToolingApiTestLauncher().launchServer(client = client, log = log) + val output = log.toString() - if (result?.isSuccessful != true) { - // print the output if the initialization fails - println(output) + if (result?.isSuccessful != true) { + // print the output if the initialization fails + println(output) + } + + assertThat(result?.isSuccessful).isTrue() + assertThat(output).contains( + "You are using Android Gradle Plugin version that has not been tested with AndroidIDE.") } - - assertThat(result?.isSuccessful).isTrue() - assertThat(output).contains( - "You are using Android Gradle Plugin version that has not been tested with AndroidIDE.") } @Test @Throws(CIOnlyException::class) fun `test CI-only minimum AGP version failure`() { - val log = CollectingLogger() - val agpVersion = "7.1.0" - val client = MultiVersionTestClient(agpVersion = agpVersion, log = log) - val (server, _) = ToolingApiTestLauncher().launchServer(client = client, log = log) - val result = server.initializeTestProject() - assertThat(result?.isSuccessful).isFalse() + ciOnlyTest { + val log = CollectingLogger() + val agpVersion = "7.1.0" + val client = MultiVersionTestClient(agpVersion = agpVersion, gradleVersion = "7.2", log = log) + val (_, _, result) = ToolingApiTestLauncher().launchServer(client = client, log = log) + assertThat(result?.isSuccessful).isFalse() - val output = log.toString() - assertThat(output).contains( - "Android Gradle Plugin version $agpVersion is not supported by AndroidIDE.") + val output = log.toString() + assertThat(output).contains( + "Android Gradle Plugin version $agpVersion is not supported by AndroidIDE.") + } } private fun ciOnlyTest(test: () -> Unit) { @@ -264,11 +265,7 @@ class MultiModuleAndroidProjectTest { return System.getenv("TEST_TOOLING_API_IMPL").let { it == "true" } } - private fun IToolingApiServer.initializeTestProject(): InitializeResult? { - return initialize(InitializeProjectParams(FileProvider.testProjectRoot().pathString)).get() - } - - internal class CollectingLogger() : ILogger(CollectingLogger::class.simpleName) { + internal class CollectingLogger : ILogger(CollectingLogger::class.simpleName) { private val string = StringBuilder() diff --git a/subprojects/tooling-api-testing/src/main/java/com/itsaky/androidide/tooling/testing/ToolingApiTestLauncher.kt b/subprojects/tooling-api-testing/src/main/java/com/itsaky/androidide/tooling/testing/ToolingApiTestLauncher.kt index 746f83035..6bc6e0880 100644 --- a/subprojects/tooling-api-testing/src/main/java/com/itsaky/androidide/tooling/testing/ToolingApiTestLauncher.kt +++ b/subprojects/tooling-api-testing/src/main/java/com/itsaky/androidide/tooling/testing/ToolingApiTestLauncher.kt @@ -21,9 +21,12 @@ import com.itsaky.androidide.models.LogLine import com.itsaky.androidide.tooling.api.IProject import com.itsaky.androidide.tooling.api.IToolingApiClient import com.itsaky.androidide.tooling.api.IToolingApiServer +import com.itsaky.androidide.tooling.api.messages.GradleDistributionParams +import com.itsaky.androidide.tooling.api.messages.InitializeProjectParams import com.itsaky.androidide.tooling.api.messages.result.BuildInfo import com.itsaky.androidide.tooling.api.messages.result.BuildResult import com.itsaky.androidide.tooling.api.messages.result.GradleWrapperCheckResult +import com.itsaky.androidide.tooling.api.messages.result.InitializeResult import com.itsaky.androidide.tooling.api.util.ToolingApiLauncher import com.itsaky.androidide.tooling.events.ProgressEvent import com.itsaky.androidide.utils.FileProvider @@ -60,10 +63,16 @@ class ToolingApiTestLauncher { "jdk.compiler" to "com.sun.tools.javac.tree", "jdk.compiler" to "com.sun.tools.javac.util") + @JvmOverloads fun launchServer( - client: IToolingApiClient = MultiVersionTestClient(), + projectDir: Path = FileProvider.testProjectRoot(), + client: MultiVersionTestClient = MultiVersionTestClient(), + initParams: InitializeProjectParams = InitializeProjectParams( + projectDir.pathString, + GradleDistributionParams.forVersion(client.gradleVersion) + ), log: ILogger = ILogger.newInstance("BuildOutputLogger") - ): Pair { + ): Triple { val builder = ProcessBuilder(createProcessCmd(FileProvider.implModule() .resolve("build/libs/tooling-api-all.jar").pathString)) val androidHome = findAndroidHome() @@ -79,7 +88,10 @@ class ToolingApiTestLauncher { launcher.startListening() - return launcher.remoteProxy as IToolingApiServer to launcher.remoteProxy as IProject + val server = launcher.remoteProxy as IToolingApiServer + val project = launcher.remoteProxy as IProject + val result = server.initialize(initParams).get() + return Triple(server, project, result) } private fun createProcessCmd(jar: String): List { @@ -94,7 +106,7 @@ class ToolingApiTestLauncher { cmd[0] = java.absolutePath } } -// cmd.add("-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000") + for (open in opens) { cmd.add("--add-opens=${open.key}/${open.value}=ALL-UNNAMED") } @@ -123,11 +135,8 @@ class ToolingApiTestLauncher { const val buildFile = "build.gradle" const val buildFileIn = "$buildFile.in" - const val gradlewProps = "gradle/wrapper/gradle-wrapper.properties" - const val gradlewPropsIn = "$gradlewProps.in" - const val DEFAULT_AGP_VERSION = "7.2.0" - const val DEFAULT_GRADLE_VERSION = "7.5.1" + const val DEFAULT_GRADLE_VERSION = "7.3.3" const val GENERATED_FILE_WARNING = "DO NOT EDIT - Automatically generated file" @@ -150,22 +159,18 @@ class ToolingApiTestLauncher { projectDir.resolve(buildFileIn) .replaceContents(dest = projectDir.resolve(buildFile), candidate = "@@TOOLING_API_TEST_AGP_VERSION@@" to this.agpVersion) - - projectDir.resolve(gradlewPropsIn) - .replaceContents(comment = "#", dest = projectDir.resolve(gradlewProps), - candidate = "@@GRADLE_VERSION@@" to this.gradleVersion) } override fun onBuildSuccessful(result: BuildResult) { onBuildResult(result) } + override fun onBuildFailed(result: BuildResult) { onBuildResult(result) } private fun onBuildResult(result: BuildResult) { projectDir.resolve(buildFile).deleteIfExists() - projectDir.resolve(gradlewProps).deleteIfExists() } override fun onProgressEvent(event: ProgressEvent) {} diff --git a/subprojects/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/AndroidInitializationParams.kt b/subprojects/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/AndroidInitializationParams.kt new file mode 100644 index 000000000..dc0c0acba --- /dev/null +++ b/subprojects/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/AndroidInitializationParams.kt @@ -0,0 +1,38 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.tooling.api.messages + +import java.io.Serializable + +/** + * Initialization params for Android project/modules. + * + * @property variantSelections The map of module paths to the name of the variants which should + * be fetched/initialized. + */ +data class AndroidInitializationParams(val variantSelections: Map) : Serializable { + + companion object { + + /** + * Default initialization params. This initializes the Android modules with default values. + */ + @JvmStatic + val DEFAULT = AndroidInitializationParams(emptyMap()) + } +} \ No newline at end of file diff --git a/subprojects/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/GradleDistributionParams.kt b/subprojects/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/GradleDistributionParams.kt new file mode 100644 index 000000000..7b5777fe3 --- /dev/null +++ b/subprojects/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/GradleDistributionParams.kt @@ -0,0 +1,77 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.tooling.api.messages + +import java.io.Serializable + +/** + * Parameters for specifying the Gradle distribution which should be used during project initialization. + * + * @property type The type of Gradle distribution. + * @property value The value for the distribution type. + * @author Akash Yadav + */ +data class GradleDistributionParams(val type: GradleDistributionType, val value: String) : + Serializable { + + companion object { + + /** + * [GradleDistributionParams] with type [GRADLE_WRAPPER][GradleDistributionType.GRADLE_WRAPPER]. + */ + @JvmStatic + val WRAPPER = GradleDistributionParams(GradleDistributionType.GRADLE_WRAPPER, "") + + /** + * Creates [GradleDistributionParams] for the given [distribution path][path]. + */ + @JvmStatic + fun forInstallationDir(path: String): GradleDistributionParams { + return GradleDistributionParams(GradleDistributionType.GRADLE_INSTALLATION, path) + } + + /** + * Creates [GradleDistributionParams] for the given [Gradle version][version]. + */ + @JvmStatic + fun forVersion(version: String): GradleDistributionParams { + return GradleDistributionParams(GradleDistributionType.GRADLE_VERSION, version) + } + } +} + +/** + * Type of Gradle distributions for project initialization. + */ +enum class GradleDistributionType { + + /** + * Initialize the project using the distribution specified in `gradle-wrapper.properties`. + */ + GRADLE_WRAPPER, + + /** + * Initialize the project using a specific Gradle version. + */ + GRADLE_VERSION, + + /** + * Initialize the project using a specific Gradle distribution path. + */ + GRADLE_INSTALLATION +} \ No newline at end of file diff --git a/subprojects/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/InitializeProjectParams.kt b/subprojects/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/InitializeProjectParams.kt index 3cda97ac8..0a65c8508 100644 --- a/subprojects/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/InitializeProjectParams.kt +++ b/subprojects/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/InitializeProjectParams.kt @@ -23,30 +23,13 @@ import java.io.Serializable * Message sent from client to server to initialize the tooling API client in the given directory. * * @property directory The absolute path to the root directory of the project to initialize. - * @property gradleInstallation The installation path of the Gradle distribution to use. + * @property gradleDistribution The parameters for the Gradle distribution to use. * @property androidParams The [AndroidInitializationParams] for initializing the Android module projects. * @author Akash Yadav */ data class InitializeProjectParams @JvmOverloads constructor( val directory: String, - val gradleInstallation: String = "", + val gradleDistribution: GradleDistributionParams = GradleDistributionParams.WRAPPER, val androidParams: AndroidInitializationParams = AndroidInitializationParams.DEFAULT ) : Serializable -/** - * Initialization params for Android project/modules. - * - * @property variantSelections The map of module paths to the name of the variants which should - * be fetched/initialized. - */ -data class AndroidInitializationParams(val variantSelections: Map) : Serializable { - - companion object { - - /** - * Default initialization params. This initializes the Android modules with default values. - */ - @JvmStatic - val DEFAULT = AndroidInitializationParams(emptyMap()) - } -} diff --git a/subprojects/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/TaskExecutionMessage.kt b/subprojects/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/TaskExecutionMessage.kt index 99e4f3a16..d12c42839 100644 --- a/subprojects/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/TaskExecutionMessage.kt +++ b/subprojects/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/TaskExecutionMessage.kt @@ -24,5 +24,5 @@ package com.itsaky.androidide.tooling.api.messages data class TaskExecutionMessage( val projectPath: String?, val tasks: List, - val gradleInstallation: String = "" + val gradleDistribution: GradleDistributionParams = GradleDistributionParams.WRAPPER ) diff --git a/xml-inflater/src/test/java/com/itsaky/androidide/inflater/XmlInflaterTest.kt b/xml-inflater/src/test/java/com/itsaky/androidide/inflater/XmlInflaterTest.kt index 12b120e4b..ff2afc0f1 100644 --- a/xml-inflater/src/test/java/com/itsaky/androidide/inflater/XmlInflaterTest.kt +++ b/xml-inflater/src/test/java/com/itsaky/androidide/inflater/XmlInflaterTest.kt @@ -18,6 +18,7 @@ package com.itsaky.androidide.inflater import androidx.appcompat.app.AppCompatActivity +import com.google.common.truth.Truth.assertThat import com.itsaky.androidide.inflater.utils.endParse import com.itsaky.androidide.inflater.utils.startParse import com.itsaky.androidide.lookup.Lookup @@ -25,13 +26,10 @@ import com.itsaky.androidide.projects.IProjectManager import com.itsaky.androidide.projects.api.AndroidModule import com.itsaky.androidide.projects.builder.BuildService import com.itsaky.androidide.projects.util.findAppModule -import com.itsaky.androidide.tooling.api.messages.InitializeProjectParams import com.itsaky.androidide.tooling.testing.ToolingApiTestLauncher -import com.itsaky.androidide.utils.FileProvider import org.junit.Ignore import org.robolectric.Robolectric import java.util.concurrent.atomic.AtomicBoolean -import kotlin.io.path.pathString @Ignore("Test utility provider") object XmlInflaterTest { @@ -44,12 +42,12 @@ object XmlInflaterTest { return } - val (server, project) = + val (_, project, result) = ToolingApiTestLauncher().launchServer() - server.initialize(InitializeProjectParams(FileProvider.testProjectRoot().pathString)).get() + assertThat(result?.isSuccessful).isTrue() Lookup.getDefault().register(BuildService.KEY_PROJECT_PROXY, project) - IProjectManager.getInstance().setupProject() + IProjectManager.getInstance().setupProject(project) init.set(true) } }