diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 0c719c9df..a4d6bfde3 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -122,9 +122,9 @@ - - - + + + 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 6319691d3..6f8ad7a75 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 @@ -403,7 +403,7 @@ public class GradleBuildService extends Service implements BuildService, IToolin @Override public CompletableFuture initializeProject(@NonNull String rootDir) { checkServerStarted(); - final var message = new InitializeProjectMessage(rootDir, getGradleInstallationDir()); + final var message = new InitializeProjectMessage(rootDir, "debug", getGradleInstallationDir()); return performBuildTasks(server.initialize(message)); } 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 01319620a..e43b95f6b 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 @@ -37,7 +37,6 @@ import com.itsaky.androidide.tooling.api.IProject import com.itsaky.androidide.tooling.api.IToolingApiServer import com.itsaky.androidide.tooling.api.messages.InitializeProjectMessage import com.itsaky.androidide.tooling.testing.ToolingApiTestLauncher -import com.itsaky.androidide.tooling.testing.ToolingApiTestLauncher.MultiVersionTestClient import com.itsaky.androidide.utils.Environment import com.itsaky.androidide.utils.FileProvider import com.itsaky.androidide.utils.ILogger @@ -52,7 +51,6 @@ import org.robolectric.RuntimeEnvironment import org.robolectric.annotation.Config import java.io.File import java.nio.file.Path -import kotlin.io.path.pathString /** * Runs tests for a language server. @@ -94,7 +92,8 @@ abstract class LSPTest { Lookup.getDefault().update(BuildService.KEY_PROJECT_PROXY, project) server - .initialize(InitializeProjectMessage(FileProvider.testProjectRoot().toFile().absolutePath)) + .initialize(InitializeProjectMessage(FileProvider.testProjectRoot().toFile().absolutePath, + "debug")) .get() Environment.ANDROID_JAR = FileProvider.resources().resolve("android.jar").toFile() diff --git a/settings.gradle.kts b/settings.gradle.kts index 9f13c4414..8d45d11c4 100755 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -96,4 +96,4 @@ include( ":subprojects:tooling-api-testing", ":subprojects:xml-dom", ":subprojects:xml-utils" -) \ No newline at end of file +) 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 74c79daf0..655a3d6de 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 @@ -45,7 +45,7 @@ class ModuleProjectTest { @Test fun test() { val (server, project) = ToolingApiTestLauncher().launchServer() - server.initialize(InitializeProjectMessage(FileProvider.testProjectRoot().pathString)).get() + server.initialize(InitializeProjectMessage(FileProvider.testProjectRoot().pathString, "debug")).get() Lookup.getDefault().register(BuildService.KEY_PROJECT_PROXY, project) diff --git a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/Main.java b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/Main.java index 99ef6757d..3c8995273 100644 --- a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/Main.java +++ b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/Main.java @@ -20,10 +20,10 @@ package com.itsaky.androidide.tooling.impl; import static com.itsaky.androidide.utils.ILogger.newInstance; import com.itsaky.androidide.models.LogLine; -import com.itsaky.androidide.tooling.api.IProject; +import com.itsaky.androidide.tooling.api.IGradleProject; import com.itsaky.androidide.tooling.api.IToolingApiClient; import com.itsaky.androidide.tooling.api.util.ToolingApiLauncher; -import com.itsaky.androidide.tooling.impl.model.InternalForwardingProject; +import com.itsaky.androidide.tooling.impl.internal.ProjectImpl; import com.itsaky.androidide.tooling.impl.progress.ForwardingProgressListener; import com.itsaky.androidide.utils.ILogger; import com.itsaky.androidide.utils.JvmLogger; @@ -51,7 +51,7 @@ public class Main { public static void main(String[] args) { LOG.debug("Starting Tooling API server..."); - final var project = new InternalForwardingProject(null, IProject.FILE_PATH_NOT_AVAILABLE); + final var project = new ProjectImpl(); final var server = new ToolingApiServerImpl(project); final var launcher = ToolingApiLauncher.newServerLauncher(server, project, System.in, System.out); 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 922741ecd..91614bf49 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 @@ -18,6 +18,7 @@ package com.itsaky.androidide.tooling.impl import com.itsaky.androidide.builder.model.DefaultProjectSyncIssues +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.InitializeProjectMessage @@ -38,13 +39,12 @@ 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.model.IdeGradleProject -import com.itsaky.androidide.tooling.impl.model.InternalForwardingProject -import com.itsaky.androidide.tooling.impl.util.ProjectReader +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 import com.itsaky.androidide.tooling.impl.util.StopWatch import com.itsaky.androidide.utils.ILogger -import java.io.File -import java.util.concurrent.* import org.eclipse.lsp4j.jsonrpc.CompletableFutures import org.gradle.tooling.BuildCancelledException import org.gradle.tooling.BuildException @@ -54,30 +54,29 @@ import org.gradle.tooling.GradleConnector import org.gradle.tooling.UnsupportedVersionException import org.gradle.tooling.exceptions.UnsupportedBuildArgumentException import org.gradle.tooling.exceptions.UnsupportedOperationConfigurationException +import java.io.File +import java.util.concurrent.* /** * Implementation for the Gradle Tooling API server. * * @author Akash Yadav */ -internal class ToolingApiServerImpl(private val forwardingProject: InternalForwardingProject) : +internal class ToolingApiServerImpl(private val project: ProjectImpl) : IToolingApiServer { private var initialized = false private var client: IToolingApiClient? = null private var connector: GradleConnector? = null - private var project: IdeGradleProject? = null private var buildCancellationToken: CancellationTokenSource? = null private val log = ILogger.newInstance(javaClass.simpleName) override fun initialize(params: InitializeProjectMessage): CompletableFuture { - forwardingProject.projectPath = params.directory return CompletableFutures.computeAsync { try { if (initialized && connector != null) { connector?.disconnect() connector = null - project = null } Main.checkGradleWrapper() @@ -106,14 +105,16 @@ internal class ToolingApiServerImpl(private val forwardingProject: InternalForwa stopWatch.lapFromLast("Project connection established") val issues: MutableMap = mutableMapOf() - this.project = ProjectReader.read(connection, issues) - stopWatch.lapFromLast("Project read ${if(this.project == null) "failed" else "successful"}") + val project = RootModelBuilder.build(connection to "debug") as? ProjectImpl? + project ?: throw ModelBuilderException("Failed to build project model") + + this.project.rootProject = project.rootProject + this.project.projects = project.projects + stopWatch.lapFromLast("Project read successful") connection.close() stopWatch.log() - this.forwardingProject.project = this.project - initialized = true notifyBuildSuccess(emptyList()) @@ -131,7 +132,7 @@ internal class ToolingApiServerImpl(private val forwardingProject: InternalForwa return CompletableFuture.supplyAsync { initialized } } - override fun getRootProject(): CompletableFuture { + override fun getRootProject(): CompletableFuture { return CompletableFutures.computeAsync { assertProjectInitialized() return@computeAsync this.project @@ -150,11 +151,12 @@ internal class ToolingApiServerImpl(private val forwardingProject: InternalForwa projectPath = ":" } - val project = - this.project!!.findByPath(projectPath).get() - ?: return@computeAsync TaskExecutionResult(false, PROJECT_NOT_FOUND) + val project = this.project!!.run { + selectProject(StringParameter(projectPath)).get() + asGradleProject() + } ?: return@computeAsync TaskExecutionResult(false, PROJECT_NOT_FOUND) - this.connector!!.forProjectDirectory(project.projectDir) + this.connector!!.forProjectDirectory(project.getMetadata().get().projectDir) setupConnectorForGradleInstallation(this.connector!!, message.gradleInstallation) val connection = this.connector!!.connect() @@ -250,9 +252,7 @@ internal class ToolingApiServerImpl(private val forwardingProject: InternalForwa this.connector = null this.client = null - this.project = null this.buildCancellationToken = null - this.forwardingProject.project = null Main.future = null Main.client = null diff --git a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/internal/AndroidProjectImpl.kt b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/internal/AndroidProjectImpl.kt new file mode 100644 index 000000000..9615b7000 --- /dev/null +++ b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/internal/AndroidProjectImpl.kt @@ -0,0 +1,226 @@ +/* + * 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.impl.internal + +import com.android.builder.model.v2.ide.AndroidArtifact +import com.android.builder.model.v2.ide.GraphItem +import com.android.builder.model.v2.ide.Library +import com.android.builder.model.v2.ide.Variant +import com.android.builder.model.v2.models.AndroidProject +import com.android.builder.model.v2.models.BasicAndroidProject +import com.android.builder.model.v2.models.VariantDependencies +import com.itsaky.androidide.builder.model.DefaultLibrary +import com.itsaky.androidide.builder.model.DefaultModelSyncFile +import com.itsaky.androidide.builder.model.DefaultSourceProvider +import com.itsaky.androidide.builder.model.DefaultSourceSetContainer +import com.itsaky.androidide.builder.model.DefaultViewBindingOptions +import com.itsaky.androidide.builder.model.UNKNOWN_PACKAGE +import com.itsaky.androidide.tooling.api.IAndroidProject +import com.itsaky.androidide.tooling.api.models.AndroidArtifactMetadata +import com.itsaky.androidide.tooling.api.models.AndroidProjectMetadata +import com.itsaky.androidide.tooling.api.models.AndroidVariantMetadata +import com.itsaky.androidide.tooling.api.models.BasicAndroidVariantMetadata +import com.itsaky.androidide.tooling.api.models.BasicProjectMetadata +import com.itsaky.androidide.tooling.api.models.ProjectMetadata +import com.itsaky.androidide.tooling.api.models.params.StringParameter +import com.itsaky.androidide.tooling.api.util.AndroidModulePropertyCopier +import com.itsaky.androidide.tooling.api.util.AndroidModulePropertyCopier.copy +import com.itsaky.androidide.tooling.api.util.extractPackageName +import org.gradle.tooling.model.GradleProject +import java.io.File +import java.io.Serializable +import java.util.concurrent.CompletableFuture + +/** + * @author Akash Yadav + */ +internal class AndroidProjectImpl( + gradleProject: GradleProject, + private val variant: String, + private val basicAndroidProject: BasicAndroidProject, + private val androidProject: AndroidProject, + private val variantDependencies: VariantDependencies +) : GradleProjectImpl(gradleProject), IAndroidProject, Serializable { + + private val serialVersionUID = 1L + + private var shouldLookupPackage = true + var packageName: String = UNKNOWN_PACKAGE + get() { + if (field == UNKNOWN_PACKAGE && shouldLookupPackage) { + findPackageName() + } + + return field + } + + + + override fun getVariants(): CompletableFuture> { + return CompletableFuture.supplyAsync { + androidProject.variants.map { + BasicAndroidVariantMetadata(it.name, it.mainArtifact.toMetadata(it.name)) + } + } + } + + private fun AndroidArtifact.toMetadata(name: String): AndroidArtifactMetadata { + return AndroidArtifactMetadata( + name = name, + resGenTaskName = resGenTaskName, + assembleTaskOutputListingFile = assembleTaskOutputListingFile, + generatedResourceFolders = generatedResourceFolders, + generatedSourceFolders = generatedSourceFolders, maxSdkVersion = maxSdkVersion, + minSdkVersion = minSdkVersion.apiLevel, signingConfigName = signingConfigName, + sourceGenTaskName = sourceGenTaskName, assembleTaskName = assembleTaskName, + classJars = classesFolders.filter { it.name.endsWith(".jar") }, + compileTaskName = compileTaskName, + targetSdkVersionOverride = targetSdkVersionOverride?.apiLevel ?: -1 + ) + } + + override fun getVariant(param: StringParameter): CompletableFuture { + return CompletableFuture.supplyAsync { + androidProject.variants.find { it.name == param.value }?.toMetadata() + } + } + + private fun Variant.toMetadata(): AndroidVariantMetadata { + return AndroidVariantMetadata( + name = name, + mainArtifact = mainArtifact.toMetadata(name), + otherArtifacts = mutableMapOf() + ) + } + + override fun getBootClasspaths(): CompletableFuture> { + return CompletableFuture.supplyAsync { + basicAndroidProject.bootClasspath + } + } + + override fun getLibraryMap(): CompletableFuture> { + return CompletableFuture.supplyAsync { + val seen = HashMap() + val compileDependencies = variantDependencies.mainArtifact.compileDependencies + val libraries = variantDependencies.libraries + for (dependency in compileDependencies) { + seen[dependency.key] ?: fillLibrary(dependency, libraries, seen) + } + seen + } + } + + private fun fillLibrary( + item: GraphItem, + libraries: Map, + seen: HashMap + ): DefaultLibrary? { + + val lib = libraries[item.key] ?: return null + val library = copy(lib) + + for (dependency in item.dependencies) { + val dep = fillLibrary(dependency, libraries, seen)!! + library.dependencies.add(dep.key) + } + + seen[item.key] = library + + return library + } + + override fun getMainSourceSet(): CompletableFuture { + return CompletableFuture.supplyAsync { + basicAndroidProject.mainSourceSet?.let(AndroidModulePropertyCopier::copy) + } + } + + override fun getLintCheckJars(): CompletableFuture> { + return CompletableFuture.supplyAsync { androidProject.lintChecksJars } + } + + override fun getModelSyncFiles(): CompletableFuture> { + return CompletableFuture.supplyAsync { copy(androidProject.modelSyncFiles) } + } + + override fun getClassesJar(): CompletableFuture { + // TODO(itsaky): this should handle product flavors as well + return CompletableFuture.supplyAsync { + File(gradleProject.buildDirectory, + "${IAndroidProject.FD_INTERMEDIATES}/compile_library_classes_jar/$variant/classes.jar") + } + } + + override fun getClasspaths(): CompletableFuture> { + return CompletableFuture.supplyAsync { + mutableListOf().apply { + add(getClassesJar().get()) + getVariant(StringParameter(variant)).get()?.mainArtifact?.classJars?.let(this::addAll) + } + } + } + + override fun getMetadata(): CompletableFuture { + return CompletableFuture.supplyAsync { + val gradleMetadata = super.getMetadata().get() + + val viewBindingOptions = androidProject.viewBindingOptions?.let( + AndroidModulePropertyCopier::copy) + ?: DefaultViewBindingOptions() + + return@supplyAsync AndroidProjectMetadata( + gradleMetadata as ProjectMetadata, + packageName, + basicAndroidProject.projectType, + copy(androidProject.flags), + copy(androidProject.javaCompileOptions), + viewBindingOptions, + androidProject.resourcePrefix, + androidProject.namespace, + androidProject.androidTestNamespace, + androidProject.testFixturesNamespace + ) + } + } + + private fun findPackageName() { + + val namespace = androidProject.namespace + if (namespace.isNotBlank()) { + this.packageName = namespace + this.shouldLookupPackage = false + return + } + + val mainSourceSet = basicAndroidProject.mainSourceSet + if (mainSourceSet == null) { + shouldLookupPackage = false + return + } + + val manifestFile = mainSourceSet.sourceProvider.manifestFile + if (manifestFile == DefaultSourceProvider.NoFile) { + shouldLookupPackage = false + return + } + + this.packageName = extractPackageName(manifestFile) ?: UNKNOWN_PACKAGE + this.shouldLookupPackage = false + } +} \ No newline at end of file diff --git a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/internal/GradleProjectImpl.kt b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/internal/GradleProjectImpl.kt new file mode 100644 index 000000000..5f4a29b8e --- /dev/null +++ b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/internal/GradleProjectImpl.kt @@ -0,0 +1,69 @@ +/* + * 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.impl.internal + +import com.itsaky.androidide.tooling.api.IGradleProject +import com.itsaky.androidide.tooling.api.ProjectType +import com.itsaky.androidide.tooling.api.models.BasicProjectMetadata +import com.itsaky.androidide.tooling.api.models.GradleTask +import com.itsaky.androidide.tooling.api.models.ProjectMetadata +import org.gradle.tooling.model.GradleProject +import java.io.Serializable +import java.util.concurrent.CompletableFuture + +/** + * @author Akash Yadav + */ +internal open class GradleProjectImpl( + protected val gradleProject: GradleProject +) : IGradleProject, Serializable { + + private val serialVersionUID = 1L + + override fun getMetadata(): CompletableFuture { + return CompletableFuture.supplyAsync { + ProjectMetadata( + gradleProject.name, + gradleProject.path, + gradleProject.projectDirectory, + gradleProject.buildDirectory, + gradleProject.description, + gradleProject.buildScript.sourceFile, + ProjectType.Gradle + ) + } + } + + override fun getTasks(): CompletableFuture> { + return CompletableFuture.supplyAsync { + mutableListOf().apply { + for (task in gradleProject.tasks) { + add(GradleTask( + task.name, + task.description, + task.group, + task.path, + task.displayName, + task.isPublic, + task.project.path + )) + } + } + } + } +} \ No newline at end of file diff --git a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/internal/JavaProjectImpl.kt b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/internal/JavaProjectImpl.kt new file mode 100644 index 000000000..ccc0ad157 --- /dev/null +++ b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/internal/JavaProjectImpl.kt @@ -0,0 +1,53 @@ +/* + * 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.impl.internal + +import com.itsaky.androidide.tooling.api.IJavaProject +import com.itsaky.androidide.tooling.api.models.GradleTask +import com.itsaky.androidide.tooling.api.models.JavaContentRoot +import com.itsaky.androidide.tooling.api.models.JavaModuleDependency +import com.itsaky.androidide.tooling.api.models.ProjectMetadata +import org.gradle.tooling.model.GradleProject +import java.io.File +import java.io.Serializable +import java.util.concurrent.CompletableFuture + +/** + * @author Akash Yadav + */ +internal class JavaProjectImpl(gradleProject: GradleProject) : GradleProjectImpl(gradleProject), + IJavaProject, Serializable { + + private val serialVersionUID = 1L + + override fun getContentRoots(): CompletableFuture> { + TODO("Not yet implemented") + } + + override fun getDependencies(): CompletableFuture> { + TODO("Not yet implemented") + } + + override fun getClassesJar(): CompletableFuture { + TODO("Not yet implemented") + } + + override fun getClasspaths(): CompletableFuture> { + TODO("Not yet implemented") + } +} \ No newline at end of file diff --git a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/internal/ProjectImpl.kt b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/internal/ProjectImpl.kt new file mode 100644 index 000000000..155531e1b --- /dev/null +++ b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/internal/ProjectImpl.kt @@ -0,0 +1,103 @@ +/* + * 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.impl.internal + +import com.itsaky.androidide.builder.model.DefaultProjectSyncIssues +import com.itsaky.androidide.tooling.api.IAndroidProject +import com.itsaky.androidide.tooling.api.IGradleProject +import com.itsaky.androidide.tooling.api.IJavaProject +import com.itsaky.androidide.tooling.api.IProject +import com.itsaky.androidide.tooling.api.ProjectType +import com.itsaky.androidide.tooling.api.models.BasicProjectMetadata +import com.itsaky.androidide.tooling.api.models.params.StringParameter +import com.itsaky.androidide.tooling.api.models.result.SelectProjectResult +import com.itsaky.androidide.tooling.impl.internal.forwarding.ForwardingProject +import java.io.Serializable +import java.util.concurrent.CompletableFuture + +/** + * @author Akash Yadav + */ +@Suppress("JoinDeclarationAndAssignment") +internal class ProjectImpl(var rootProject: IGradleProject? = null, + var projects: List = emptyList()) : IProject, Serializable { + + private val serialVersionUID = 1L + + @Transient + private var _lock: Any? = null + private val lock: Any + get() = _lock ?: Any().also { _lock = it } + + @Transient + private val selectedProject: ForwardingProject + + init { + this.selectedProject = ForwardingProject() + } + + private fun getProject(path: String): IGradleProject? { + return if (":" == path) rootProject else projects.find { + it.getMetadata().get().projectPath == path + } + } + + override fun getProjects(): CompletableFuture> { + return CompletableFuture.supplyAsync { + projects.map { it.getMetadata().get() } + } + } + + override fun getProjectSyncIssues(): CompletableFuture> { + return CompletableFuture.completedFuture(emptyMap()) + } + + override fun selectProject(param: StringParameter): CompletableFuture { + return CompletableFuture.supplyAsync { + synchronized(lock) { + this.selectedProject.project = getProject(param.value) + SelectProjectResult(this.selectedProject.project != null) + } + } + } + + override fun getType(): CompletableFuture { + return CompletableFuture.supplyAsync { + synchronized(lock) { + return@supplyAsync when (this.selectedProject.project) { + is IAndroidProject -> ProjectType.Android + is IJavaProject -> ProjectType.Java + is IGradleProject -> ProjectType.Gradle + else -> ProjectType.Unknown + } + } + } + } + + override fun asGradleProject(): IGradleProject { + return this.selectedProject + } + + override fun asAndroidProject(): IAndroidProject { + return this.selectedProject + } + + override fun asJavaProject(): IJavaProject { + return this.selectedProject + } +} \ No newline at end of file diff --git a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/internal/forwarding/ForwardingProject.kt b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/internal/forwarding/ForwardingProject.kt new file mode 100644 index 000000000..05aa073cb --- /dev/null +++ b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/internal/forwarding/ForwardingProject.kt @@ -0,0 +1,115 @@ +/* + * 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.impl.internal.forwarding + +import com.itsaky.androidide.builder.model.DefaultLibrary +import com.itsaky.androidide.builder.model.DefaultModelSyncFile +import com.itsaky.androidide.builder.model.DefaultSourceSetContainer +import com.itsaky.androidide.tooling.api.IAndroidProject +import com.itsaky.androidide.tooling.api.IGradleProject +import com.itsaky.androidide.tooling.api.IJavaProject +import com.itsaky.androidide.tooling.api.models.AndroidVariantMetadata +import com.itsaky.androidide.tooling.api.models.BasicAndroidVariantMetadata +import com.itsaky.androidide.tooling.api.models.BasicProjectMetadata +import com.itsaky.androidide.tooling.api.models.GradleTask +import com.itsaky.androidide.tooling.api.models.JavaContentRoot +import com.itsaky.androidide.tooling.api.models.JavaModuleDependency +import com.itsaky.androidide.tooling.api.models.ProjectMetadata +import com.itsaky.androidide.tooling.api.models.params.StringParameter +import java.io.File +import java.util.concurrent.CompletableFuture + +/** + * @author Akash Yadav + */ +@Suppress("NewApi") +internal class ForwardingProject(var project: IGradleProject? = null) : IGradleProject, + IAndroidProject, IJavaProject { + + private val androidProject: IAndroidProject? + get() = this.project as? IAndroidProject? + + private val javaProject: IJavaProject? + get() = this.project as? IJavaProject? + + + override fun getContentRoots(): CompletableFuture> { + return this.javaProject?.getContentRoots() ?: CompletableFuture.failedFuture( + UnsupportedOperationException()) + } + + override fun getDependencies(): CompletableFuture> { + return this.javaProject?.getDependencies() ?: CompletableFuture.failedFuture( + UnsupportedOperationException()) + } + + override fun getVariants(): CompletableFuture> { + return this.androidProject?.getVariants() ?: CompletableFuture.failedFuture( + UnsupportedOperationException()) + } + + override fun getVariant(param: StringParameter): CompletableFuture { + return this.androidProject?.getVariant(param) ?: CompletableFuture.failedFuture( + UnsupportedOperationException()) + } + + override fun getBootClasspaths(): CompletableFuture> { + return this.androidProject?.getBootClasspaths() ?: CompletableFuture.failedFuture( + UnsupportedOperationException()) + } + + override fun getLibraryMap(): CompletableFuture> { + return this.androidProject?.getLibraryMap() ?: CompletableFuture.failedFuture( + UnsupportedOperationException()) + } + + override fun getMainSourceSet(): CompletableFuture { + return this.androidProject?.getMainSourceSet() ?: CompletableFuture.failedFuture( + UnsupportedOperationException()) + } + + override fun getLintCheckJars(): CompletableFuture> { + return this.androidProject?.getLintCheckJars() ?: CompletableFuture.failedFuture( + UnsupportedOperationException()) + } + + override fun getModelSyncFiles(): CompletableFuture> { + return this.androidProject?.getModelSyncFiles() ?: CompletableFuture.failedFuture( + UnsupportedOperationException()) + } + + override fun getClassesJar(): CompletableFuture { + return this.javaProject?.getClassesJar() ?: CompletableFuture.failedFuture( + UnsupportedOperationException()) + } + + override fun getClasspaths(): CompletableFuture> { + return this.javaProject?.getClasspaths() ?: CompletableFuture.failedFuture( + UnsupportedOperationException()) + } + + override fun getMetadata(): CompletableFuture { + return this.project?.getMetadata() ?: CompletableFuture.failedFuture( + UnsupportedOperationException()) + } + + override fun getTasks(): CompletableFuture> { + return this.project?.getTasks() ?: CompletableFuture.failedFuture( + UnsupportedOperationException()) + } +} \ No newline at end of file diff --git a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/model/InternalForwardingProject.kt b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/model/InternalForwardingProject.kt deleted file mode 100644 index ef184d436..000000000 --- a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/model/InternalForwardingProject.kt +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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.impl.model - -import com.itsaky.androidide.tooling.api.IProject -import com.itsaky.androidide.tooling.api.IProject.Type -import com.itsaky.androidide.tooling.api.IProject.Type.Unknown -import com.itsaky.androidide.tooling.api.IToolingApiClient -import com.itsaky.androidide.tooling.api.messages.VariantDataRequest -import com.itsaky.androidide.tooling.api.messages.result.SimpleModuleData -import com.itsaky.androidide.tooling.api.messages.result.SimpleVariantData -import com.itsaky.androidide.tooling.api.model.AndroidModule -import com.itsaky.androidide.tooling.api.model.IdeGradleProject -import com.itsaky.androidide.tooling.api.model.GradleTask -import com.itsaky.androidide.utils.ILogger -import java.io.File -import java.util.concurrent.* - -/** - * An [IProject] implementation which forwards all calls to another [IProject]. - * - * We provide [IProject] as a service to the [IToolingApiClient]. As there is no real project - * instance available when the server starts, we use this implementation. When the project is - * initialized, the Tooling API server updates the project instance here. Once the project instance - * is available, the client can fetch real information about the project. - * - * @author Akash Yadav - */ -class InternalForwardingProject( - var project: IProject?, - var projectPath: String = IProject.FILE_PATH_NOT_AVAILABLE -) : IProject { - - private val log = ILogger.newInstance(javaClass.simpleName) - - override fun isProjectInitialized(): CompletableFuture { - return CompletableFuture.completedFuture(this.project != null) - } - - override fun getName(): CompletableFuture = - if (this.project != null) this.project!!.name else CompletableFuture.completedFuture("") - - override fun getDescription(): CompletableFuture = - if (this.project != null) this.project!!.description else CompletableFuture.completedFuture("") - - override fun getProjectPath(): CompletableFuture = - if (this.project != null) this.project!!.projectPath else CompletableFuture.completedFuture("") - - override fun getProjectDir(): CompletableFuture = - if (this.project != null) this.project!!.projectDir - else CompletableFuture.completedFuture(File(projectPath)) - - override fun getType(): CompletableFuture = - if (this.project != null) this.project!!.type else CompletableFuture.completedFuture(Unknown) - - override fun getBuildDir(): CompletableFuture = - if (this.project != null) this.project!!.buildDir - else CompletableFuture.completedFuture(File(projectPath)) - - override fun getBuildScript(): CompletableFuture = - if (this.project != null) this.project!!.buildScript - else CompletableFuture.completedFuture(File(projectPath)) - - override fun getTasks(): CompletableFuture> = - if (this.project != null) this.project!!.tasks - else CompletableFuture.completedFuture(mutableListOf()) - - override fun listModules(): CompletableFuture> = - if (this.project != null) project!!.listModules() - else CompletableFuture.completedFuture(mutableListOf()) - - override fun findByPath(path: String): CompletableFuture = - if (this.project != null) this.project!!.findByPath(path) - else CompletableFuture.completedFuture(null) - - override fun findAndroidModules(): CompletableFuture> = - if (this.project != null) this.project!!.findAndroidModules() - else CompletableFuture.completedFuture(mutableListOf()) - - override fun findFirstAndroidModule(): CompletableFuture = - if (this.project != null) this.project!!.findFirstAndroidModule() - else CompletableFuture.completedFuture(null) - - override fun findFirstAndroidAppModule(): CompletableFuture = - if (this.project != null) this.project!!.findFirstAndroidAppModule() - else CompletableFuture.completedFuture(null) - - override fun getVariantData(request: VariantDataRequest): CompletableFuture = - if (this.project != null) this.project!!.getVariantData(request) - else CompletableFuture.completedFuture(null) -} diff --git a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/AbstractModelBuilder.kt b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/AbstractModelBuilder.kt new file mode 100644 index 000000000..7e7af8edc --- /dev/null +++ b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/AbstractModelBuilder.kt @@ -0,0 +1,106 @@ +/* + * 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.impl.sync + +import com.android.builder.model.v2.models.Versions +import com.itsaky.androidide.tooling.impl.Main +import com.itsaky.androidide.utils.ILogger +import com.itsaky.androidide.utils.LogUtils +import org.gradle.tooling.BuildController +import org.gradle.tooling.model.Model + +/** + * Abstract class for [IModelBuilder] implementations. + * + * @property androidVariant The name of the variant for which the Android models will be built. + * @author Akash Yadav + */ +abstract class AbstractModelBuilder(protected val androidVariant: String = VARIANT_DEBUG) : + IModelBuilder { + + companion object { + + /** + * Default variant name used for [AbstractModelBuilder.androidVariant]. + */ + const val VARIANT_DEBUG = "debug" + + /** + * Checks the Android Gradle Plugin version from the given [Versions] model and compares + * it with [Main.MIN_SUPPORTED_AGP_VERSION]. If the version is less than the [Main.MIN_SUPPORTED_AGP_VERSION], + * throws an [UnsupportedOperationException]. + * + * @param versions The [Versions] model. + */ + @JvmStatic + protected fun assertMinimumAgp(versions: Versions) { + if (versions.agp < Main.MIN_SUPPORTED_AGP_VERSION) { + throw ModelBuilderException( + "Android Gradle Plugin version " + + versions.agp + + " is not supported by AndroidIDE. " + + "Please update your project to use at least v" + + Main.MIN_SUPPORTED_AGP_VERSION + + " of Android Gradle Plugin to build this project.") + } + } + + /** + * Get the [Versions] information about Android projects. This returns `null` if + * the project is not an Android project. + * + * @param model The model element, usually a project. + * @param controller The build controller that is used for finding the model. + * @return The [Versions] model if available, `null` otherwise. + */ + @JvmStatic + protected fun getAndroidVersions(model: Model, controller: BuildController): Versions? { + return controller.findModel(model, Versions::class.java) + } + + @JvmStatic + protected fun log(vararg objects: Any?) { + buildLog(*objects) + } + + /** + * Logs the given objects to the error stream. + * + * @param objects The objects to log. + */ + protected fun buildLog(vararg objects: Any?) { + System.err.println(generateMessage(*objects)) + } + + /** + * Generates the log message for the given objects. This works similar to + * [ generateMessage(Object...)][com.itsaky.androidide.utils.ILogger.generateMessage] in [ILogger][com.itsaky.androidide.utils.ILogger]. + * + * @param objects The objects to print in the message. + * @return The generated message. + */ + protected fun generateMessage(vararg objects: Any?): String { + val sb = StringBuilder() + for (msg in objects) { + sb.append(if (msg is Throwable) "\n" else ILogger.MSG_SEPARATOR) + sb.append(if (msg is Throwable) LogUtils.getFullStackTrace( + msg as Throwable?) else msg) + } + return sb.toString() + } + } +} \ No newline at end of file diff --git a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/AndroidProjectModelBuilder.kt b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/AndroidProjectModelBuilder.kt new file mode 100644 index 000000000..9a5a8c037 --- /dev/null +++ b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/AndroidProjectModelBuilder.kt @@ -0,0 +1,49 @@ +/* + * 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.impl.sync + +import com.android.builder.model.v2.models.AndroidProject +import com.android.builder.model.v2.models.BasicAndroidProject +import com.android.builder.model.v2.models.ModelBuilderParameter +import com.android.builder.model.v2.models.VariantDependencies +import com.itsaky.androidide.tooling.api.IAndroidProject +import com.itsaky.androidide.tooling.impl.internal.AndroidProjectImpl + +/** + * Builds model for Android application and library projects. + * + * @author Akash Yadav + */ +class AndroidProjectModelBuilder(androidVariant: String) : + AbstractModelBuilder(androidVariant) { + + override fun build(param: BuildControllderAndIdeaModule): IAndroidProject { + val (controller, module) = param + val basicModel = controller.findModel(module, BasicAndroidProject::class.java) + val androidModel = controller.findModel(module, AndroidProject::class.java) + val variantDependencies = controller.findModel(module, VariantDependencies::class.java, + ModelBuilderParameter::class.java) { it.variantName = androidVariant } + + return AndroidProjectImpl( + module.gradleProject, + androidVariant, + basicModel, + androidModel, + variantDependencies + ) + } +} \ No newline at end of file diff --git a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/GradleProjectModelBuilder.kt b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/GradleProjectModelBuilder.kt new file mode 100644 index 000000000..58dcfacd2 --- /dev/null +++ b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/GradleProjectModelBuilder.kt @@ -0,0 +1,34 @@ +/* + * 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.impl.sync + +import com.itsaky.androidide.tooling.api.IGradleProject +import com.itsaky.androidide.tooling.impl.internal.GradleProjectImpl +import org.gradle.tooling.model.GradleProject + +/** + * Builds model for root Gradle project (represented with [IGradleProject]. + * + * @author Akash Yadav + */ +class GradleProjectModelBuilder : AbstractModelBuilder() { + + @Throws(ModelBuilderException::class) + override fun build(param: GradleProject): IGradleProject { + return GradleProjectImpl(param) + } +} \ No newline at end of file diff --git a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/IModelBuilder.kt b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/IModelBuilder.kt new file mode 100644 index 000000000..59a400f38 --- /dev/null +++ b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/IModelBuilder.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.impl.sync + +/** + * A model builder builds project models when the project is initialized/synced. + * + * @param

The parameter for building the model. + * @param The type of model that is built. + * @author Akash Yadav + */ +interface IModelBuilder { + + /** + * Builds the model. + * + * @param param Parameter for building the model. + * @return The built model. Implementations should throw [ModelBuilderException] instead of + * returning `null`. + * @throws ModelBuilderException If the model could not be built. + */ + @Throws(ModelBuilderException::class) + fun build(param: P): R +} \ No newline at end of file diff --git a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/JavaProjectModelBuilder.kt b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/JavaProjectModelBuilder.kt new file mode 100644 index 000000000..278d4ba2b --- /dev/null +++ b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/JavaProjectModelBuilder.kt @@ -0,0 +1,34 @@ +/* + * 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.impl.sync + +import com.itsaky.androidide.tooling.api.IJavaProject +import com.itsaky.androidide.tooling.impl.internal.JavaProjectImpl +import org.gradle.tooling.model.idea.IdeaModule + +/** + * Builds model for Java library projects. + * + * @author Akash Yadav + */ +class JavaProjectModelBuilder : AbstractModelBuilder() { + + override fun build(param: IdeaModule): IJavaProject { + return JavaProjectImpl(param.gradleProject) + } +} \ No newline at end of file diff --git a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/ModelBuilderException.java b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/ModelBuilderException.java new file mode 100644 index 000000000..a1c2dc7eb --- /dev/null +++ b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/ModelBuilderException.java @@ -0,0 +1,41 @@ +/* + * 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.impl.sync; + +/** + * Exception thrown while building project models. + * + * @author Akash Yadav + */ +public class ModelBuilderException extends RuntimeException { + + public ModelBuilderException() { + } + + public ModelBuilderException(String message) { + super(message); + } + + public ModelBuilderException(String message, Throwable cause) { + super(message, cause); + } + + public ModelBuilderException(Throwable cause) { + super(cause); + } +} diff --git a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/ModuleProjectModelBuilder.kt b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/ModuleProjectModelBuilder.kt new file mode 100644 index 000000000..2cb88a23e --- /dev/null +++ b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/ModuleProjectModelBuilder.kt @@ -0,0 +1,36 @@ +/* + * 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.impl.sync + +import com.itsaky.androidide.tooling.api.IModuleProject + +/** + * Builds models for module projects (either Android app/library or Java library projects). + * + * @author Akash Yadav + */ +class ModuleProjectModelBuilder(androidVariant: String = VARIANT_DEBUG) : + AbstractModelBuilder(androidVariant) { + + override fun build(param: BuildControllderAndIdeaModule): IModuleProject { + val (controller, module) = param + val isAndroidProject = getAndroidVersions(module, controller) != null + return if (isAndroidProject) AndroidProjectModelBuilder(androidVariant).build( + controller to module) else JavaProjectModelBuilder().build(module) + } +} \ No newline at end of file diff --git a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/NotAnAndroidProjectException.java b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/NotAnAndroidProjectException.java new file mode 100644 index 000000000..31c143dea --- /dev/null +++ b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/NotAnAndroidProjectException.java @@ -0,0 +1,35 @@ +/* + * 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.impl.sync; + +/** + * Exception thrown to indicate that a project is not an Android project. + * + * @author Akash Yadav + */ +public class NotAnAndroidProjectException extends ModelBuilderException { + + /** + * Creates a new {@link NotAnAndroidProjectException} for the given project name. + * + * @param projectName The name of the project which is not an Android project. + */ + public NotAnAndroidProjectException(String projectName) { + super("Project " + projectName + " is not an Android project"); + } +} diff --git a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/RootModelBuilder.kt b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/RootModelBuilder.kt new file mode 100644 index 000000000..dd16f4a9c --- /dev/null +++ b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/RootModelBuilder.kt @@ -0,0 +1,96 @@ +/* + * 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.impl.sync + +import com.itsaky.androidide.tooling.api.IAndroidProject +import com.itsaky.androidide.tooling.api.IProject +import com.itsaky.androidide.tooling.impl.Main +import com.itsaky.androidide.tooling.impl.Main.finalizeLauncher +import com.itsaky.androidide.tooling.impl.internal.ProjectImpl +import com.itsaky.androidide.utils.ILogger +import org.gradle.tooling.BuildController +import org.gradle.tooling.ConfigurableLauncher +import org.gradle.tooling.model.idea.IdeaProject + +/** + * Utility class to build the project models. + * + * @author Akash Yadav + */ +object RootModelBuilder : AbstractModelBuilder("") { + + override fun build(param: ProjectConnectionAndAndroidVariant): IProject { + val (connection, androidVariant) = param + val executor = connection.action { controller -> + val ideaProject = findIdeaProject(controller) ?: throw ModelBuilderException( + "IdeaProject model could not be created") + + val ideaModules = ideaProject.modules + val rootModule = ideaModules.find { it.gradleProject.path == ":" } + ?: throw ModelBuilderException( + "No GradleProject model is associated with project path: ':'") + + val rootProjectVersions = getAndroidVersions(rootModule, controller) + + val rootProject = if (rootProjectVersions != null) { + // Root project is an Android project + assertMinimumAgp(rootProjectVersions) + AndroidProjectModelBuilder(androidVariant).build(controller to rootModule) + } else { + GradleProjectModelBuilder().build(rootModule.gradleProject) + } + + val projects = ideaModules.map { ideaModule -> + ModuleProjectModelBuilder(androidVariant).build(controller to ideaModule) + } + + ProjectImpl(rootProject, projects) + } + + finalizeLauncher(executor) + applyAndroidModelBuilderProps(executor) + + val logger = ILogger.newInstance("ProjectReader") + logger.warn("Starting build. See build output for more details...") + + if (Main.client != null) { + Main.client.logOutput("Starting build...") + } + + return executor.run().also { + logger.debug("Build action executed. Result:", it) + } + } + + private fun findIdeaProject(controller: BuildController): IdeaProject? { + log("Creating Idea project model...") + return controller.findModel(IdeaProject::class.java)?.also { + log("IdeaProject model created...") + } + } + + private fun applyAndroidModelBuilderProps( + launcher: ConfigurableLauncher<*>) { + launcher.addProperty(IAndroidProject.PROPERTY_BUILD_MODEL_ONLY, true) + launcher.addProperty(IAndroidProject.PROPERTY_INVOKED_FROM_IDE, true) + } + + private fun ConfigurableLauncher<*>.addProperty(property: String, value: Any) { + addArguments(String.format("-P%s=%s", property, value)) + } +} \ No newline at end of file diff --git a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/types.kt b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/types.kt new file mode 100644 index 000000000..8459b87a1 --- /dev/null +++ b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/sync/types.kt @@ -0,0 +1,26 @@ +/* + * 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.impl.sync + +import org.gradle.tooling.BuildController +import org.gradle.tooling.ProjectConnection +import org.gradle.tooling.model.idea.IdeaModule + + +typealias BuildControllderAndIdeaModule = Pair +typealias ProjectConnectionAndAndroidVariant = Pair \ No newline at end of file diff --git a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/util/AnnotationUtil.java b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/util/AnnotationUtil.java new file mode 100644 index 000000000..e3221ba35 --- /dev/null +++ b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/util/AnnotationUtil.java @@ -0,0 +1,160 @@ +/* + * 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.impl.util; + +/** + * @author Akash Yadav + */ + +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.Arrays; +import java.util.Set; +import java.util.function.Consumer; +import org.eclipse.lsp4j.jsonrpc.services.JsonDelegate; +import org.eclipse.lsp4j.jsonrpc.services.JsonNotification; +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; + +public final class AnnotationUtil { + + private AnnotationUtil() { + } + + public static void findDelegateSegments(Class clazz, Set> visited, + Consumer acceptor) { + if (clazz == null || !visited.add(clazz)) { + return; + } + findDelegateSegments(clazz.getSuperclass(), visited, acceptor); + for (Class interf : clazz.getInterfaces()) { + findDelegateSegments(interf, visited, acceptor); + } + for (Method method : clazz.getDeclaredMethods()) { + if (isDelegateMethod(method)) { + acceptor.accept(method); + } + } + } + + public static boolean isDelegateMethod(Method method) { + if (!method.isSynthetic()) { + JsonDelegate jsonDelegate = method.getAnnotation(JsonDelegate.class); + if (jsonDelegate != null) { + if (!(method.getParameterCount() == 0 && method.getReturnType().isInterface())) { + throw new IllegalStateException( + "The method " + method.toString() + " is not a proper @JsonDelegate method."); + } + return true; + } + } + return false; + } + + + /** + * Depth first search for annotated methods in hierarchy. + */ + public static void findRpcMethods(Class clazz, Set> visited, + Consumer acceptor) { + if (clazz == null || !visited.add(clazz)) { + return; + } + findRpcMethods(clazz.getSuperclass(), visited, acceptor); + for (Class interf : clazz.getInterfaces()) { + findRpcMethods(interf, visited, acceptor); + } + String segment = getSegment(clazz); + for (Method method : clazz.getDeclaredMethods()) { + MethodInfo methodInfo = createMethodInfo(method, segment); + if (methodInfo != null) { + acceptor.accept(methodInfo); + } + } + } + + protected static String getSegment(Class clazz) { + JsonSegment jsonSegment = clazz.getAnnotation(JsonSegment.class); + return jsonSegment == null ? "" : jsonSegment.value() + "/"; + } + + protected static MethodInfo createMethodInfo(Method method, String segment) { + if (!method.isSynthetic()) { + JsonRequest jsonRequest = method.getAnnotation(JsonRequest.class); + if (jsonRequest != null) { + return createRequestInfo(method, segment, jsonRequest); + } + JsonNotification jsonNotification = method.getAnnotation(JsonNotification.class); + if (jsonNotification != null) { + return createNotificationInfo(method, segment, jsonNotification); + } + } + return null; + } + + protected static MethodInfo createNotificationInfo(Method method, String segment, + JsonNotification jsonNotification) { + MethodInfo methodInfo = createMethodInfo(method, jsonNotification.useSegment(), segment, + jsonNotification.value()); + methodInfo.isNotification = true; + return methodInfo; + } + + protected static MethodInfo createRequestInfo(Method method, String segment, + JsonRequest jsonRequest) { + return createMethodInfo(method, jsonRequest.useSegment(), segment, jsonRequest.value()); + } + + protected static MethodInfo createMethodInfo(Method method, boolean useSegment, String segment, + String value) { + method.setAccessible(true); + + MethodInfo methodInfo = new MethodInfo(); + methodInfo.method = method; + methodInfo.parameterTypes = getParameterTypes(method); + methodInfo.name = getMethodName(method, useSegment, segment, value); + return methodInfo; + } + + protected static String getMethodName(Method method, boolean useSegment, String segment, + String value) { + String name = value != null && value.length() > 0 ? value : method.getName(); + return useSegment ? segment + name : name; + } + + protected static Type[] getParameterTypes(Method method) { + return Arrays.stream(method.getParameters()).map(t -> t.getParameterizedType()) + .toArray(Type[]::new); + } + + public static class MethodInfo { + + private static Type[] EMPTY_TYPE_ARRAY = {}; + public String name; + public Method method; + public Type[] parameterTypes = EMPTY_TYPE_ARRAY; + public boolean isNotification = false; + } + + public static class DelegateInfo { + + public Method method; + public Object delegate; + } +} + diff --git a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/util/ProjectReader.java b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/util/ProjectReader.java index 1278f4da8..e8d05dcc4 100644 --- a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/util/ProjectReader.java +++ b/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/util/ProjectReader.java @@ -17,42 +17,25 @@ package com.itsaky.androidide.tooling.impl.util; -import static com.itsaky.androidide.tooling.impl.Main.finalizeLauncher; import static com.itsaky.androidide.utils.ILogger.MSG_SEPARATOR; import static java.util.Collections.emptyList; import com.android.builder.model.ModelBuilderParameter; -import com.android.builder.model.v2.ide.GraphItem; -import com.android.builder.model.v2.ide.Library; import com.android.builder.model.v2.ide.ProjectType; import com.android.builder.model.v2.models.AndroidProject; import com.android.builder.model.v2.models.BasicAndroidProject; import com.android.builder.model.v2.models.ProjectSyncIssues; import com.android.builder.model.v2.models.VariantDependencies; import com.android.builder.model.v2.models.Versions; -import com.itsaky.androidide.builder.model.DefaultLibrary; import com.itsaky.androidide.builder.model.DefaultProjectSyncIssues; import com.itsaky.androidide.builder.model.IJavaCompilerSettings; -import com.itsaky.androidide.tooling.api.model.AndroidModule; -import com.itsaky.androidide.tooling.api.model.GradleArtifact; -import com.itsaky.androidide.tooling.api.model.GradleTask; -import com.itsaky.androidide.tooling.api.model.IdeGradleProject; -import com.itsaky.androidide.tooling.api.model.JavaContentRoot; -import com.itsaky.androidide.tooling.api.model.JavaModule; -import com.itsaky.androidide.tooling.api.model.JavaModuleCompilerSettings; -import com.itsaky.androidide.tooling.api.model.JavaModuleDependency; -import com.itsaky.androidide.tooling.api.model.JavaModuleExternalDependency; -import com.itsaky.androidide.tooling.api.model.JavaModuleProjectDependency; -import com.itsaky.androidide.tooling.api.model.JavaSourceDirectory; -import com.itsaky.androidide.tooling.api.model.util.AndroidModulePropertyCopier; -import com.itsaky.androidide.tooling.api.model.util.ProjectBuilder; import com.itsaky.androidide.tooling.impl.Main; -import com.itsaky.androidide.utils.ILogger; import com.itsaky.androidide.utils.LogUtils; - +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; import org.gradle.api.JavaVersion; import org.gradle.tooling.BuildController; -import org.gradle.tooling.ConfigurableLauncher; import org.gradle.tooling.ProjectConnection; import org.gradle.tooling.UnknownModelException; import org.gradle.tooling.model.GradleProject; @@ -63,492 +46,416 @@ import org.gradle.tooling.model.idea.IdeaProject; import org.gradle.tooling.model.idea.IdeaSingleEntryLibraryDependency; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutionException; - -import kotlin.Pair; - /** * @author Akash Yadav */ public class ProjectReader { - /** All modules mapped by their names (not paths). */ - private static final Map allModules = new ConcurrentHashMap<>(); - - public static IdeGradleProject read( - ProjectConnection connection, Map outIssues) - throws ExecutionException, InterruptedException { - final var buildActionExecutor = - connection.action( - controller -> { - log("Creating Idea project model..."); - final var ideaProject = controller.findModel(IdeaProject.class); - log("IdeaProject model created..."); - return buildGradleProjectModel(ideaProject, controller, outIssues); - }); - finalizeLauncher(buildActionExecutor); - applyAndroidModelBuilderProps(buildActionExecutor); - - final var logger = ILogger.newInstance("ProjectReader"); - logger.warn("Starting build. See build output for more details..."); - - if (Main.client != null) { - Main.client.logOutput("Starting build..."); - } - - final var project = buildActionExecutor.run(); - - // Fetch java module dependencies - final var ideaProject = connection.model(IdeaProject.class).get(); - final var modules = ideaProject.getModules(); - - for (var module : modules) { - allModules.put(module.getName(), module); - } - - for (final var module : modules) { - final var moduleProject = project.findByPath(module.getGradleProject().getPath()).get(); - if (!(moduleProject instanceof JavaModule)) { - continue; - } - - final var dependencies = ((JavaModule) moduleProject).getJavaDependencies(); - dependencies.clear(); - dependencies.addAll(collectJavaDependencies(module)); - } - - allModules.clear(); - - return project; - } - - public static AndroidModule buildAndroidModuleProject( - GradleProject gradle, AndroidProject android, ProjectType type) { - log("Building AndroidModule for project: " + gradle.getName()); - final var builder = new ProjectBuilder(); - final var copier = AndroidModulePropertyCopier.INSTANCE; - builder.setName(gradle.getName()); - builder.setDescription(gradle.getDescription()); - builder.setPath(gradle.getPath()); - builder.setProjectDir(gradle.getProjectDirectory()); - builder.setBuildDir(gradle.getBuildDirectory()); - builder.setBuildScript(gradle.getBuildScript().getSourceFile()); - builder.setSimpleVariants(copier.asSimpleVariants(android.getVariants())); - builder.setJavaCompileOptions(copier.copy(android.getJavaCompileOptions())); - builder.setResourcePrefix(android.getResourcePrefix()); - builder.setDynamicFeatures(android.getDynamicFeatures()); - builder.setViewBindingOptions(copier.copy(android.getViewBindingOptions())); - builder.setFlags(copier.copy(android.getFlags())); - builder.setModelSyncFiles(emptyList()); - builder.setLintChecksJars(android.getLintChecksJars()); - builder.setProjectType(type); - - final var module = builder.buildAndroidModule(); - tryFillNamespaces(android, module); - addTasks(gradle, module); - - return module; - } - - private static void tryFillNamespaces(final AndroidProject android, final AndroidModule module) { - try { - module.setNamespace(android.getNamespace()); - module.setAndroidTestNamespace(android.getAndroidTestNamespace()); - module.setTestFixturesNamespace(android.getTestFixturesNamespace()); - } catch (Throwable err) { - log("Unable to get namespaces of module: '" + module.projectPath + "'.", err.getMessage()); - } - } - - private static void addTasks(GradleProject gradle, IdeGradleProject project) { - for (final var task : gradle.getTasks()) { - project.tasks.add(buildGradleTaskModel(project, task)); - } - } - - private static GradleTask buildGradleTaskModel( - IdeGradleProject project, org.gradle.tooling.model.GradleTask task) { - return new GradleTask( - task.getName(), - task.getDescription(), - task.getGroup(), - task.getPath(), - task.getDisplayName(), - task.isPublic(), - project.projectPath); - } - - private static IdeGradleProject buildModuleProject( - BuildController controller, - final IdeaProject ideaProject, - IdeaModule ideaModule, - Map outIssues) { - return buildModuleProject(controller, ideaProject, ideaModule, outIssues, false); - } - - private static IdeGradleProject buildModuleProject( - BuildController controller, - final IdeaProject ideaProject, - IdeaModule ideaModule, - Map outIssues, - boolean androidOnly) { - - final var gradle = ideaModule.getGradleProject(); - - try { - final var info = createAndroidModelInfo(gradle, controller); - if (info == null) { - throw new NotAnAndroidProjectException(gradle.getName()); - } - - outIssues.put(gradle.getPath(), info.getSyncIssues()); - return info.getProject(); - } catch (Throwable error) { - - if (androidOnly) { - if (error instanceof NotAnAndroidProjectException && gradle.getParent() == null) { - // A root project is not an Android project in most cases - // do not emit misleading logs - return null; - } - - log(error.getMessage()); - return null; - } - - try { - log("Building IdeGradleProject model for project: " + gradle.getPath()); - if (!(error instanceof UnknownModelException)) { - // If the error is something else than UnknownModelException, we should log it - error.printStackTrace(); - } - - return buildJavaModuleProject(gradle, ideaProject, ideaModule); - } catch (Throwable e) { - log("Unable to create model for project"); - e.printStackTrace(); - return null; - } - } - } - - private static ModelInfoContainer createAndroidModelInfo( - GradleProject gradle, BuildController controller) { - final var start = System.currentTimeMillis(); - final var versions = controller.findModel(gradle, Versions.class); - if (versions == null) { - return null; - } - - if (versions.getAgp().compareTo(Main.MIN_SUPPORTED_AGP_VERSION) < 0) { - throw new UnsupportedOperationException( - "Android Gradle Plugin version " - + versions.getAgp() - + " is not supported by AndroidIDE. " - + "Please update your project to use at least v" - + Main.MIN_SUPPORTED_AGP_VERSION - + " of Android Gradle Plugin to build this project."); - } - - final var basicAndroid = controller.findModel(gradle, BasicAndroidProject.class); - - log("Fetching Android project model..."); - final var android = controller.findModel(gradle, AndroidProject.class); - final var module = buildAndroidModuleProject(gradle, android, basicAndroid.getProjectType()); - module.setBootClassPaths(basicAndroid.getBootClasspath()); - module.setMainSourceSet( - basicAndroid.getMainSourceSet() == null - ? null - : AndroidModulePropertyCopier.INSTANCE.copy(basicAndroid.getMainSourceSet())); - - log("Fetching 'debug' dependencies"); - final var variantDependencies = - controller.findModel( - gradle, - VariantDependencies.class, - ModelBuilderParameter.class, - it -> it.setVariantName("debug")); - - final var dependencies = getAllCompileDependencies(variantDependencies); - module.setLibraryMap(dependencies.getFirst()); - module.setLibraries(dependencies.getSecond()); - - log("Fetching project sync issues..."); - final var issues = controller.findModel(gradle, ProjectSyncIssues.class); - final var syncIssues = - issues == null - ? new DefaultProjectSyncIssues(emptyList()) - : AndroidModulePropertyCopier.INSTANCE.copy(issues); - - log( - "Android project model for project '" + gradle.getName() + "' created in", - System.currentTimeMillis() - start, - "ms"); - return new ModelInfoContainer(module, syncIssues); - } - - private static Pair, Set> getAllCompileDependencies( - final VariantDependencies variantDependencies) { - final var seen = new HashMap(); - final var libs = new HashSet(); - final var compileDependencies = variantDependencies.getMainArtifact().getCompileDependencies(); - final var libraries = variantDependencies.getLibraries(); - for (final var dependency : compileDependencies) { - var library = seen.get(dependency.getKey()); - if (library == null) { - library = getLibrary(dependency, libraries, seen); - } - if (library == null) { - continue; - } - - libs.add(library.getKey()); - } - return new Pair<>(seen, libs); - } - - private static DefaultLibrary getLibrary( - final GraphItem item, - final Map libraries, - final HashMap seen) { - final var lib = libraries.get(item.getKey()); - if (lib == null) { - return null; - } - - final var library = AndroidModulePropertyCopier.INSTANCE.copy(lib); - for (final var dependency : item.getDependencies()) { - final DefaultLibrary dep = Objects.requireNonNull(getLibrary(dependency, libraries, seen)); - library.getDependencies().add(dep.getKey()); - } - - seen.put(item.getKey(), library); - return library; - } - - private static JavaModule buildJavaModuleProject( - GradleProject gradle, final IdeaProject ideaProject, IdeaModule idea) { - final var builder = new ProjectBuilder(); - builder.setName(gradle.getName()); - builder.setDescription(gradle.getDescription()); - builder.setPath(gradle.getPath()); - builder.setProjectDir(gradle.getProjectDirectory()); - builder.setBuildDir(gradle.getBuildDirectory()); - builder.setBuildScript(gradle.getBuildScript().getSourceFile()); - builder.setContentRoots(collectContentRoots(idea)); - builder.setJavaCompilerSettings(createCompilerSettings(ideaProject, idea)); - - final var project = builder.buildJavaModule(); - addTasks(gradle, project); - return project; - } - - private static IJavaCompilerSettings createCompilerSettings( - final IdeaProject ideaProject, final IdeaModule module) { - final var javaLanguageSettings = module.getJavaLanguageSettings(); - if (javaLanguageSettings == null) { - return createCompilerSettings(ideaProject); - } - - JavaVersion languageLevel = javaLanguageSettings.getLanguageLevel(); - JavaVersion targetBytecodeVersion = javaLanguageSettings.getTargetBytecodeVersion(); - - if (languageLevel == null || targetBytecodeVersion == null) { - return createCompilerSettings(ideaProject); - } - - final var source = languageLevel.toString(); - final var target = targetBytecodeVersion.toString(); - return new JavaModuleCompilerSettings(source, target); - } - - private static IJavaCompilerSettings createCompilerSettings(final IdeaProject ideaProject) { - final var settings = ideaProject.getJavaLanguageSettings(); - if (settings == null) { - return new JavaModuleCompilerSettings(); - } - - final var source = settings.getLanguageLevel(); - final var target = settings.getTargetBytecodeVersion(); - if (source == null || target == null) { - return new JavaModuleCompilerSettings(); - } - - return new JavaModuleCompilerSettings(source.toString(), target.toString()); - } - - private static void log(Object... messages) { - System.err.println(generateMessage(messages)); - } - - private static String generateMessage(Object... messages) { - StringBuilder sb = new StringBuilder(); - if (messages == null) { - return "null"; - } - - for (Object msg : messages) { - sb.append(msg instanceof Throwable ? "\n" : MSG_SEPARATOR); - sb.append(msg instanceof Throwable ? LogUtils.getFullStackTrace(((Throwable) msg)) : msg); - } - - return sb.toString(); - } - - private static List collectJavaDependencies(IdeaModule idea) { - final var list = new ArrayList(); - for (IdeaDependency dependency : idea.getDependencies()) { - // TODO There might be unresolved dependencies here. We need to handle them too. - if (dependency instanceof IdeaSingleEntryLibraryDependency) { - final var external = (IdeaSingleEntryLibraryDependency) dependency; - final var file = external.getFile(); - final var source = external.getSource(); - final var javadoc = external.getJavadoc(); - final GradleArtifact artifact = getGradleArtifact(external); - list.add( - new JavaModuleExternalDependency( - file, - source, - javadoc, - artifact, - dependency.getScope().getScope(), - dependency.getExported())); - } else if (dependency instanceof IdeaModuleDependency) { - final var project = ((IdeaModuleDependency) dependency); - final var moduleName = project.getTargetModuleName(); - list.add( - new JavaModuleProjectDependency( - moduleName, - findModulePathByName(moduleName), - project.getScope().getScope(), - project.getExported())); - } - } - return list; - } - - @Nullable - private static GradleArtifact getGradleArtifact(final IdeaSingleEntryLibraryDependency external) { - final var moduleVersion = external.getGradleModuleVersion(); - if (moduleVersion == null) { - return null; - } - - return new GradleArtifact( - moduleVersion.getGroup(), moduleVersion.getName(), moduleVersion.getVersion()); - } - - private static String findModulePathByName(final String moduleName) { - final var module = allModules.get(moduleName); - if (module != null) { - return module.getGradleProject().getPath(); - } - return ""; - } - - private static List collectContentRoots(IdeaModule module) { - final var list = new ArrayList(); - for (var contentRoot : module.getContentRoots()) { - final var thisRoot = new JavaContentRoot(); - for (var sourceDir : contentRoot.getSourceDirectories()) { - thisRoot - .getSourceDirectories() - .add(new JavaSourceDirectory(sourceDir.getDirectory(), sourceDir.isGenerated())); - } - - for (var testDir : contentRoot.getTestDirectories()) { - thisRoot - .getTestDirectories() - .add(new JavaSourceDirectory(testDir.getDirectory(), testDir.isGenerated())); - } - - list.add(thisRoot); - } - return list; - } - - private static > void applyAndroidModelBuilderProps( - ConfigurableLauncher launcher) { - addProperty(launcher, AndroidModule.PROPERTY_BUILD_MODEL_ONLY, true); - addProperty(launcher, AndroidModule.PROPERTY_INVOKED_FROM_IDE, true); - } - - private static void addProperty(ConfigurableLauncher launcher, String property, Object value) { - launcher.addArguments(String.format("-P%s=%s", property, value)); - } - - private static IdeGradleProject buildGradleProjectModel( - IdeaProject ideaProject, - BuildController controller, - Map outIssues) { - final var rootModule = - ideaProject.getModules().stream() - .filter(it -> it.getGradleProject().getPath().equals(":")) - .findAny(); - - if (rootModule.isEmpty()) { - throw new IllegalArgumentException( - "No GradleProject model is associated with project path: " + "':'"); - } - - IdeGradleProject project = - buildModuleProject(controller, ideaProject, rootModule.get(), outIssues, true); - final var gradle = rootModule.get().getGradleProject(); - if (project == null) { - final var builder = new ProjectBuilder(); - builder.setName(gradle.getName()); - builder.setDescription(gradle.getDescription()); - builder.setPath(gradle.getPath()); - builder.setProjectDir(gradle.getProjectDirectory()); - builder.setBuildDir(gradle.getBuildDirectory()); - builder.setBuildScript(gradle.getBuildScript().getSourceFile()); - - project = builder.buildGradleProject(); - - // If the root project is an Android project - // Then the tasks have already been added into the project - // So we only add them here if we create a Gradle project model - addTasks(gradle, project); - } else { - log("Root project is an Android project..."); - } - - addModules(ideaProject, project, controller, outIssues); - - return project; - } - - private static void addModules( - IdeaProject ideaProject, - IdeGradleProject project, - BuildController controller, - Map outIssues) { - for (final var module : ideaProject.getModules()) { - final var gradle = module.getGradleProject(); - if (gradle.getPath().equals(":")) { - // Do not try to add the root project again - continue; - } - - final var sub = buildModuleProject(controller, ideaProject, module, outIssues); - if (sub != null) { - project.moduleProjects.add(sub); - } - } - } - - private static class NotAnAndroidProjectException extends UnknownModelException { - - public NotAnAndroidProjectException(String projectName) { - super("Project " + projectName + " is not an Android project"); - } - } +// /** +// * All modules mapped by their names (not paths). +// */ +// private static final Map allModules = new ConcurrentHashMap<>(); +// +// public static IdeGradleProject read( +// ProjectConnection connection, Map outIssues) +// throws ExecutionException, InterruptedException { +// +// final var project = buildActionExecutor.run(); +// +// // Fetch java module dependencies +// final var ideaProject = connection.model(IdeaProject.class).get(); +// final var modules = ideaProject.getModules(); +// +// for (var module : modules) { +// allModules.put(module.getName(), module); +// } +// +// for (final var module : modules) { +// final var moduleProject = project.findByPath(module.getGradleProject().getPath()).get(); +// if (!(moduleProject instanceof JavaModule)) { +// continue; +// } +// +// final var dependencies = ((JavaModule) moduleProject).getJavaDependencies(); +// dependencies.clear(); +// dependencies.addAll(collectJavaDependencies(module)); +// } +// +// allModules.clear(); +// +// return project; +// } +// +// public static AndroidModule buildAndroidModuleProject( +// GradleProject gradle, AndroidProject android, ProjectType type) { +// log("Building AndroidModule for project: " + gradle.getName()); +// final var builder = new ProjectBuilder(); +// final var copier = AndroidModulePropertyCopier.INSTANCE; +// builder.setName(gradle.getName()); +// builder.setDescription(gradle.getDescription()); +// builder.setPath(gradle.getPath()); +// builder.setProjectDir(gradle.getProjectDirectory()); +// builder.setBuildDir(gradle.getBuildDirectory()); +// builder.setBuildScript(gradle.getBuildScript().getSourceFile()); +// builder.setSimpleVariants(copier.asSimpleVariants(android.getVariants())); +// builder.setJavaCompileOptions(copier.copy(android.getJavaCompileOptions())); +// builder.setResourcePrefix(android.getResourcePrefix()); +// builder.setDynamicFeatures(android.getDynamicFeatures()); +// builder.setViewBindingOptions(copier.copy(android.getViewBindingOptions())); +// builder.setFlags(copier.copy(android.getFlags())); +// builder.setModelSyncFiles(emptyList()); +// builder.setLintChecksJars(android.getLintChecksJars()); +// builder.setProjectType(type); +// +// final var module = builder.buildAndroidModule(); +// tryFillNamespaces(android, module); +// addTasks(gradle, module); +// +// return module; +// } +// +// private static void tryFillNamespaces(final AndroidProject android, final AndroidModule module) { +// try { +// module.setNamespace(android.getNamespace()); +// module.setAndroidTestNamespace(android.getAndroidTestNamespace()); +// module.setTestFixturesNamespace(android.getTestFixturesNamespace()); +// } catch (Throwable err) { +// log("Unable to get namespaces of module: '" + module.projectPath + "'.", err.getMessage()); +// } +// } +// +// private static void addTasks(GradleProject gradle, IdeGradleProject project) { +// for (final var task : gradle.getTasks()) { +// project.tasks.add(buildGradleTaskModel(project, task)); +// } +// } +// +// private static GradleTask buildGradleTaskModel( +// IdeGradleProject project, org.gradle.tooling.model.GradleTask task) { +// return new GradleTask( +// task.getName(), +// task.getDescription(), +// task.getGroup(), +// task.getPath(), +// task.getDisplayName(), +// task.isPublic(), +// project.projectPath); +// } +// +// private static IdeGradleProject buildModuleProject( +// BuildController controller, +// final IdeaProject ideaProject, +// IdeaModule ideaModule, +// Map outIssues) { +// return buildModuleProject(controller, ideaProject, ideaModule, outIssues, false); +// } +// +// private static IdeGradleProject buildModuleProject( +// BuildController controller, +// final IdeaProject ideaProject, +// IdeaModule ideaModule, +// Map outIssues, +// boolean androidOnly) { +// +// final var gradle = ideaModule.getGradleProject(); +// +// try { +// final var info = createAndroidModelInfo(gradle, controller); +// if (info == null) { +// throw new NotAnAndroidProjectException(gradle.getName()); +// } +// +// outIssues.put(gradle.getPath(), info.getSyncIssues()); +// return info.getProject(); +// } catch (Throwable error) { +// +// if (androidOnly) { +// if (error instanceof NotAnAndroidProjectException && gradle.getParent() == null) { +// // A root project is not an Android project in most cases +// // do not emit misleading logs +// return null; +// } +// +// log(error.getMessage()); +// return null; +// } +// +// try { +// log("Building IdeGradleProject model for project: " + gradle.getPath()); +// if (!(error instanceof UnknownModelException)) { +// // If the error is something else than UnknownModelException, we should log it +// error.printStackTrace(); +// } +// +// return buildJavaModuleProject(gradle, ideaProject, ideaModule); +// } catch (Throwable e) { +// log("Unable to create model for project"); +// e.printStackTrace(); +// return null; +// } +// } +// } +// +// private static ModelInfoContainer createAndroidModelInfo( +// GradleProject gradle, BuildController controller) { +// final var start = System.currentTimeMillis(); +// final var versions = controller.findModel(gradle, Versions.class); +// if (versions == null) { +// return null; +// } +// +// if (versions.getAgp().compareTo(Main.MIN_SUPPORTED_AGP_VERSION) < 0) { +// throw new UnsupportedOperationException( +// "Android Gradle Plugin version " +// + versions.getAgp() +// + " is not supported by AndroidIDE. " +// + "Please update your project to use at least v" +// + Main.MIN_SUPPORTED_AGP_VERSION +// + " of Android Gradle Plugin to build this project."); +// } +// +// final var basicAndroid = controller.findModel(gradle, BasicAndroidProject.class); +// +// log("Fetching Android project model..."); +// final var android = controller.findModel(gradle, AndroidProject.class); +// final var module = buildAndroidModuleProject(gradle, android, basicAndroid.getProjectType()); +// module.setBootClassPaths(basicAndroid.getBootClasspath()); +// module.setMainSourceSet( +// basicAndroid.getMainSourceSet() == null +// ? null +// : AndroidModulePropertyCopier.INSTANCE.copy(basicAndroid.getMainSourceSet())); +// +// log("Fetching 'debug' dependencies"); +// final var variantDependencies = +// controller.findModel( +// gradle, +// VariantDependencies.class, +// ModelBuilderParameter.class, +// it -> it.setVariantName("debug")); +// +// final var dependencies = getAllCompileDependencies(variantDependencies); +// module.setLibraryMap(dependencies.getFirst()); +// module.setLibraries(dependencies.getSecond()); +// +// log("Fetching project sync issues..."); +// final var issues = controller.findModel(gradle, ProjectSyncIssues.class); +// final var syncIssues = +// issues == null +// ? new DefaultProjectSyncIssues(emptyList()) +// : AndroidModulePropertyCopier.INSTANCE.copy(issues); +// +// log( +// "Android project model for project '" + gradle.getName() + "' created in", +// System.currentTimeMillis() - start, +// "ms"); +// return new ModelInfoContainer(module, syncIssues); +// } +// +// private static JavaModule buildJavaModuleProject( +// GradleProject gradle, final IdeaProject ideaProject, IdeaModule idea) { +// final var builder = new ProjectBuilder(); +// builder.setName(gradle.getName()); +// builder.setDescription(gradle.getDescription()); +// builder.setPath(gradle.getPath()); +// builder.setProjectDir(gradle.getProjectDirectory()); +// builder.setBuildDir(gradle.getBuildDirectory()); +// builder.setBuildScript(gradle.getBuildScript().getSourceFile()); +// builder.setContentRoots(collectContentRoots(idea)); +// builder.setJavaCompilerSettings(createCompilerSettings(ideaProject, idea)); +// +// final var project = builder.buildJavaModule(); +// addTasks(gradle, project); +// return project; +// } +// +// private static IJavaCompilerSettings createCompilerSettings( +// final IdeaProject ideaProject, final IdeaModule module) { +// final var javaLanguageSettings = module.getJavaLanguageSettings(); +// if (javaLanguageSettings == null) { +// return createCompilerSettings(ideaProject); +// } +// +// JavaVersion languageLevel = javaLanguageSettings.getLanguageLevel(); +// JavaVersion targetBytecodeVersion = javaLanguageSettings.getTargetBytecodeVersion(); +// +// if (languageLevel == null || targetBytecodeVersion == null) { +// return createCompilerSettings(ideaProject); +// } +// +// final var source = languageLevel.toString(); +// final var target = targetBytecodeVersion.toString(); +// return new JavaModuleCompilerSettings(source, target); +// } +// +// private static IJavaCompilerSettings createCompilerSettings(final IdeaProject ideaProject) { +// final var settings = ideaProject.getJavaLanguageSettings(); +// if (settings == null) { +// return new JavaModuleCompilerSettings(); +// } +// +// final var source = settings.getLanguageLevel(); +// final var target = settings.getTargetBytecodeVersion(); +// if (source == null || target == null) { +// return new JavaModuleCompilerSettings(); +// } +// +// return new JavaModuleCompilerSettings(source.toString(), target.toString()); +// } +// +// private static void log(Object... messages) { +// System.err.println(generateMessage(messages)); +// } +// +// private static String generateMessage(Object... messages) { +// StringBuilder sb = new StringBuilder(); +// if (messages == null) { +// return "null"; +// } +// +// for (Object msg : messages) { +// sb.append(msg instanceof Throwable ? "\n" : MSG_SEPARATOR); +// sb.append(msg instanceof Throwable ? LogUtils.getFullStackTrace(((Throwable) msg)) : msg); +// } +// +// return sb.toString(); +// } +// +// private static List collectJavaDependencies(IdeaModule idea) { +// final var list = new ArrayList(); +// for (IdeaDependency dependency : idea.getDependencies()) { +// // TODO There might be unresolved dependencies here. We need to handle them too. +// if (dependency instanceof IdeaSingleEntryLibraryDependency) { +// final var external = (IdeaSingleEntryLibraryDependency) dependency; +// final var file = external.getFile(); +// final var source = external.getSource(); +// final var javadoc = external.getJavadoc(); +// final GradleArtifact artifact = getGradleArtifact(external); +// list.add( +// new JavaModuleExternalDependency( +// file, +// source, +// javadoc, +// artifact, +// dependency.getScope().getScope(), +// dependency.getExported())); +// } else if (dependency instanceof IdeaModuleDependency) { +// final var project = ((IdeaModuleDependency) dependency); +// final var moduleName = project.getTargetModuleName(); +// list.add( +// new JavaModuleProjectDependency( +// moduleName, +// findModulePathByName(moduleName), +// project.getScope().getScope(), +// project.getExported())); +// } +// } +// return list; +// } +// +// @Nullable +// private static GradleArtifact getGradleArtifact(final IdeaSingleEntryLibraryDependency external) { +// final var moduleVersion = external.getGradleModuleVersion(); +// if (moduleVersion == null) { +// return null; +// } +// +// return new GradleArtifact( +// moduleVersion.getGroup(), moduleVersion.getName(), moduleVersion.getVersion()); +// } +// +// private static String findModulePathByName(final String moduleName) { +// final var module = allModules.get(moduleName); +// if (module != null) { +// return module.getGradleProject().getPath(); +// } +// return ""; +// } +// +// private static List collectContentRoots(IdeaModule module) { +// final var list = new ArrayList(); +// for (var contentRoot : module.getContentRoots()) { +// final var thisRoot = new JavaContentRoot(); +// for (var sourceDir : contentRoot.getSourceDirectories()) { +// thisRoot +// .getSourceDirectories() +// .add(new JavaSourceDirectory(sourceDir.getDirectory(), sourceDir.isGenerated())); +// } +// +// for (var testDir : contentRoot.getTestDirectories()) { +// thisRoot +// .getTestDirectories() +// .add(new JavaSourceDirectory(testDir.getDirectory(), testDir.isGenerated())); +// } +// +// list.add(thisRoot); +// } +// return list; +// } +// +// private static IdeGradleProject buildGradleProjectModel( +// IdeaProject ideaProject, +// BuildController controller, +// Map outIssues) { +// final var rootModule = +// ideaProject.getModules().stream() +// .filter(it -> it.getGradleProject().getPath().equals(":")) +// .findAny(); +// +// if (rootModule.isEmpty()) { +// throw new IllegalArgumentException( +// "No GradleProject model is associated with project path: " + "':'"); +// } +// +// IdeGradleProject project = +// buildModuleProject(controller, ideaProject, rootModule.get(), outIssues, true); +// final var gradle = rootModule.get().getGradleProject(); +// if (project == null) { +// final var builder = new ProjectBuilder(); +// builder.setName(gradle.getName()); +// builder.setDescription(gradle.getDescription()); +// builder.setPath(gradle.getPath()); +// builder.setProjectDir(gradle.getProjectDirectory()); +// builder.setBuildDir(gradle.getBuildDirectory()); +// builder.setBuildScript(gradle.getBuildScript().getSourceFile()); +// +// project = builder.buildGradleProject(); +// +// // If the root project is an Android project +// // Then the tasks have already been added into the project +// // So we only add them here if we create a Gradle project model +// addTasks(gradle, project); +// } else { +// log("Root project is an Android project..."); +// } +// +// addModules(ideaProject, project, controller, outIssues); +// +// return project; +// } +// +// private static void addModules( +// IdeaProject ideaProject, +// IdeGradleProject project, +// BuildController controller, +// Map outIssues) { +// for (final var module : ideaProject.getModules()) { +// final var gradle = module.getGradleProject(); +// if (gradle.getPath().equals(":")) { +// // Do not try to add the root project again +// continue; +// } +// +// final var sub = buildModuleProject(controller, ideaProject, module, outIssues); +// if (sub != null) { +// project.moduleProjects.add(sub); +// } +// } +// } +// +// private static class NotAnAndroidProjectException extends UnknownModelException { +// +// public NotAnAndroidProjectException(String projectName) { +// super("Project " + projectName + " is not an Android project"); +// } +// } } 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 705004db2..76c79217d 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 @@ -17,17 +17,16 @@ package com.itsaky.androidide.tooling.impl -import com.android.builder.model.v2.ide.LibraryType.PROJECT +import com.android.builder.model.v2.ide.LibraryType import com.android.builder.model.v2.ide.ProjectType.APPLICATION import com.google.common.truth.Truth.assertThat +import com.itsaky.androidide.tooling.api.IAndroidProject import com.itsaky.androidide.tooling.api.IProject -import com.itsaky.androidide.tooling.api.IProject.Type.Gradle import com.itsaky.androidide.tooling.api.IToolingApiServer +import com.itsaky.androidide.tooling.api.ProjectType import com.itsaky.androidide.tooling.api.messages.InitializeProjectMessage -import com.itsaky.androidide.tooling.api.model.AndroidModule -import com.itsaky.androidide.tooling.api.model.JavaModule -import com.itsaky.androidide.tooling.api.model.JavaModuleExternalDependency -import com.itsaky.androidide.tooling.api.model.JavaModuleProjectDependency +import com.itsaky.androidide.tooling.api.models.AndroidProjectMetadata +import com.itsaky.androidide.tooling.api.models.params.StringParameter import com.itsaky.androidide.tooling.testing.ToolingApiTestLauncher import com.itsaky.androidide.tooling.testing.ToolingApiTestLauncher.MultiVersionTestClient import com.itsaky.androidide.utils.FileProvider @@ -44,98 +43,121 @@ class MultiModuleAndroidProjectTest { @Test fun `test simple multi module project initialization`() { val (server, project) = ToolingApiTestLauncher().launchServer() - server.initialize(InitializeProjectMessage(FileProvider.testProjectRoot().pathString)).get() + server.initialize(InitializeProjectMessage(FileProvider.testProjectRoot().pathString, "debug")).get() doAssertions(project, server) } private fun doAssertions(project: IProject, server: IToolingApiServer) { assertThat(project).isNotNull() - assertThat(project.type.get()).isEqualTo(Gradle) + // As the returned project is just a proxy, // project instanceOf IdeGradleProject will always return false val isInitialized = server.isServerInitialized().get() assertThat(isInitialized).isTrue() - val app = project.findByPath(":app").get() + + var selectionResult = project.selectProject(StringParameter(":")).get() + assertThat(selectionResult.isSuccessful).isTrue() + assertThat(project.getType().get()).isEqualTo(ProjectType.Gradle) + + selectionResult = project.selectProject(StringParameter(":app")).get() + assertThat(selectionResult).isNotNull() + assertThat(selectionResult.isSuccessful).isTrue() + + val app = project.asAndroidProject() assertThat(app).isNotNull() - assertThat(app).isInstanceOf(AndroidModule::class.java) - assertThat((app as AndroidModule).javaCompileOptions).isNotNull() - assertThat(app.javaCompileOptions.sourceCompatibility).isEqualTo("11") - assertThat(app.javaCompileOptions.targetCompatibility).isEqualTo("11") - assertThat(app.javaCompileOptions.javaSourceVersion).isEqualTo("11") - assertThat(app.javaCompileOptions.javaBytecodeVersion).isEqualTo("11") - assertThat(app.javaCompileOptions.isCoreLibraryDesugaringEnabled).isFalse() - assertThat(app.projectType).isEqualTo(APPLICATION) - assertThat(app.packageName).isEqualTo("com.itsaky.test.app") - assertThat(app.viewBindingOptions).isNotNull() - assertThat(app.viewBindingOptions!!.isEnabled).isTrue() + assertThat(app!!).isInstanceOf(IAndroidProject::class.java) + + var metadata = app.getMetadata().get() as AndroidProjectMetadata + assertThat(metadata).isNotNull() + assertThat(metadata.javaCompileOptions.sourceCompatibility).isEqualTo("11") + assertThat(metadata.javaCompileOptions.targetCompatibility).isEqualTo("11") + assertThat(metadata.javaCompileOptions.javaSourceVersion).isEqualTo("11") + assertThat(metadata.javaCompileOptions.javaBytecodeVersion).isEqualTo("11") + assertThat(metadata.javaCompileOptions.isCoreLibraryDesugaringEnabled).isFalse() + assertThat(metadata.androidType).isEqualTo(APPLICATION) + assertThat(metadata.packageName).isEqualTo("com.itsaky.test.app") + assertThat(metadata.viewBindingOptions).isNotNull() + assertThat(metadata.viewBindingOptions.isEnabled).isTrue() + // There are always more than 100 tasks in an android module // Also, the tasks must contain the user defined tasks - assertThat(app.tasks.size).isAtLeast(100) - assertThat(app.tasks.first { it.path == "${app.projectPath}:thisIsATestTask" }).isNotNull() - assertThat(app.libraries).isNotEmpty() + val tasks = app.getTasks().get() + assertThat(tasks.size).isAtLeast(100) + assertThat(tasks.first { it.path == "${metadata.projectPath}:thisIsATestTask" }).isNotNull() + + var libraries = app.getLibraryMap().get() // At least one project library - assertThat(app.libraryMap.values.filter { it.type == PROJECT }).isNotEmpty() + assertThat(libraries.values.filter { it.type == LibraryType.PROJECT }).isNotEmpty() // :app module includes :java-library as a dependency. But it is not transitive assertThat( - app.libraryMap.values.firstOrNull { - it.type == PROJECT && + libraries.values.firstOrNull { + it.type == LibraryType.PROJECT && it.projectInfo!!.projectPath == ":another-java-library" && it.projectInfo!!.attributes["org.gradle.usage"] == "java-api" } ) .isNull() - val androidLib = project.findByPath(":android-library").get() + + selectionResult = project.selectProject(StringParameter(":android-library")).get() + assertThat(selectionResult.isSuccessful).isTrue() + + val androidLib = project.asAndroidProject() assertThat(androidLib).isNotNull() - assertThat(androidLib).isInstanceOf(AndroidModule::class.java) + assertThat(androidLib!!).isInstanceOf(IAndroidProject::class.java) // Make sure that transitive dependencies are included here because :android-library includes // :java-library project which further includes :another-java-libraries project with 'api' // configuration + libraries = androidLib.getLibraryMap().get()!! assertThat( - (androidLib as AndroidModule).libraryMap.values.firstOrNull { - it.type == PROJECT && + libraries.values.firstOrNull { + it.type == LibraryType.PROJECT && it.projectInfo!!.projectPath == ":another-java-library" && it.projectInfo!!.attributes["org.gradle.usage"] == "java-api" } ) .isNotNull() - assertThat(androidLib.javaCompileOptions.javaSourceVersion).isEqualTo("11") - assertThat(androidLib.javaCompileOptions.javaBytecodeVersion).isEqualTo("11") - val javaLibrary = project.findByPath(":java-library").get() - assertThat(javaLibrary).isNotNull() - assertThat(javaLibrary).isInstanceOf(JavaModule::class.java) - assertThat((javaLibrary as JavaModule).compilerSettings.javaSourceVersion).isEqualTo("11") - assertThat(javaLibrary.compilerSettings.javaBytecodeVersion).isEqualTo("11") - assertThat( - javaLibrary.javaDependencies.firstOrNull { - it is JavaModuleExternalDependency && - it.gradleArtifact != null && - it.run { - gradleArtifact!!.group == "io.github.itsaky" && - gradleArtifact!!.name == "nb-javac-android" && - gradleArtifact!!.version == "17.0.0.0" - } - } - ) - .isNotNull() - assertThat( - javaLibrary.javaDependencies.firstOrNull { - it is JavaModuleProjectDependency && it.moduleName == "another-java-library" - } - ) - .isNotNull() - // In case we have multiple dependencies with same name but different path - val nested = - javaLibrary.javaDependencies - .filterIsInstance(JavaModuleProjectDependency::class.java) - .filter { it.moduleName.endsWith("nested-java-library") } - assertThat(nested).hasSize(2) - assertThat(nested[0].projectPath).isNotEqualTo(nested[1].projectPath) - assertThat(project.findByPath(":does-not-exist").get()).isNull() - val anotherJavaLib = project.findByPath(":another-java-library").get() - assertThat(anotherJavaLib).isNotNull() - assertThat(anotherJavaLib).isInstanceOf(JavaModule::class.java) - assertThat((anotherJavaLib as JavaModule).compilerSettings.javaSourceVersion).isEqualTo("1.8") - assertThat(anotherJavaLib.compilerSettings.javaBytecodeVersion).isEqualTo("1.8") + + metadata = androidLib.getMetadata().get() as AndroidProjectMetadata + assertThat(metadata.javaCompileOptions.javaSourceVersion).isEqualTo("11") + assertThat(metadata.javaCompileOptions.javaBytecodeVersion).isEqualTo("11") + + selectionResult = project.selectProject(StringParameter(":java-library")).get() + val javaLibrary = project.asJavaProject() +// assertThat(javaLibrary).isNotNull() +// assertThat(javaLibrary).isInstanceOf(JavaModule::class.java) +// assertThat((javaLibrary as JavaModule).compilerSettings.javaSourceVersion).isEqualTo("11") +// assertThat(javaLibrary.compilerSettings.javaBytecodeVersion).isEqualTo("11") +// assertThat( +// javaLibrary.javaDependencies.firstOrNull { +// it is JavaModuleExternalDependency && +// it.gradleArtifact != null && +// it.run { +// gradleArtifact!!.group == "io.github.itsaky" && +// gradleArtifact!!.name == "nb-javac-android" && +// gradleArtifact!!.version == "17.0.0.0" +// } +// } +// ) +// .isNotNull() +// assertThat( +// javaLibrary.javaDependencies.firstOrNull { +// it is JavaModuleProjectDependency && it.moduleName == "another-java-library" +// } +// ) +// .isNotNull() +// // In case we have multiple dependencies with same name but different path +// val nested = +// javaLibrary.javaDependencies +// .filterIsInstance(JavaModuleProjectDependency::class.java) +// .filter { it.moduleName.endsWith("nested-java-library") } +// assertThat(nested).hasSize(2) +// assertThat(nested[0].projectPath).isNotEqualTo(nested[1].projectPath) +// assertThat(project.findByPath(":does-not-exist").get()).isNull() +// val anotherJavaLib = project.findByPath(":another-java-library").get() +// assertThat(anotherJavaLib).isNotNull() +// assertThat(anotherJavaLib).isInstanceOf(JavaModule::class.java) +// assertThat((anotherJavaLib as JavaModule).compilerSettings.javaSourceVersion).isEqualTo("1.8") +// assertThat(anotherJavaLib.compilerSettings.javaBytecodeVersion).isEqualTo("1.8") } /** @@ -159,7 +181,8 @@ class MultiModuleAndroidProjectTest { client.agpVersion = agpVersion client.gradleVersion = gradleVersion val (server, project) = ToolingApiTestLauncher().launchServer(client = client) - server.initialize(InitializeProjectMessage(FileProvider.testProjectRoot().pathString)).get() + server.initialize(InitializeProjectMessage(FileProvider.testProjectRoot().pathString, + "debug")).get() doAssertions(project = project, server = server) FileProvider.testProjectRoot().resolve(MultiVersionTestClient.buildFile).deleteExisting() } diff --git a/subprojects/tooling-api-model/build.gradle.kts b/subprojects/tooling-api-model/build.gradle.kts index 1f9687e96..82a6f29ea 100644 --- a/subprojects/tooling-api-model/build.gradle.kts +++ b/subprojects/tooling-api-model/build.gradle.kts @@ -1,45 +1,45 @@ -/* - * 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 . - */ - -@Suppress("JavaPluginLanguageLevel") -plugins { - id ("java-library") - id ("org.jetbrains.kotlin.jvm") -} - -dependencies { - implementation(projects.logger) - - api(projects.subprojects.xmlDom) - api(projects.subprojects.builderModelImpl) - api(libs.common.jsonrpc) - - implementation(libs.common.jkotlin) -} - -tasks.register < Copy > ("copyToTestDir") { - from ("${project.buildDir.absolutePath}/libs/tooling-api-model.jar") - into ("${project.rootProject.file ("tests/test-home/.androidide/init").absolutePath}/") - rename { "model.jar" } - - outputs.upToDateWhen { false } -} - -project.tasks.jar { - finalizedBy ("copyToTestDir") - outputs.upToDateWhen { false } -} +/* + * 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 . + */ + +@Suppress("JavaPluginLanguageLevel") +plugins { + id ("java-library") + id ("org.jetbrains.kotlin.jvm") +} + +dependencies { + implementation(projects.logger) + + api(projects.subprojects.xmlDom) + api(projects.subprojects.builderModelImpl) + api(libs.common.jsonrpc) + + implementation(libs.common.jkotlin) +} + +tasks.register < Copy > ("copyToTestDir") { + from ("${project.buildDir.absolutePath}/libs/tooling-api-model.jar") + into ("${project.rootProject.file ("tests/test-home/.androidide/init").absolutePath}/") + rename { "model.jar" } + + outputs.upToDateWhen { false } +} + +project.tasks.jar { + finalizedBy ("copyToTestDir") + outputs.upToDateWhen { false } +} diff --git a/subprojects/tooling-api-model/libs/builder-model-7.2.0-rc01.jar b/subprojects/tooling-api-model/libs/builder-model-7.2.0-rc01.jar deleted file mode 100644 index fa2966e98..000000000 Binary files a/subprojects/tooling-api-model/libs/builder-model-7.2.0-rc01.jar and /dev/null differ diff --git a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/AndroidModule.kt b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/IAndroidProject.kt similarity index 56% rename from subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/AndroidModule.kt rename to subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/IAndroidProject.kt index 4641bd8d3..b8e6ed46d 100644 --- a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/AndroidModule.kt +++ b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/IAndroidProject.kt @@ -1,209 +1,170 @@ -/* - * 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.model - -import com.android.builder.model.v2.ide.ProjectType -import com.android.builder.model.v2.models.AndroidProject -import com.itsaky.androidide.builder.model.DefaultAndroidGradlePluginProjectFlags -import com.itsaky.androidide.builder.model.DefaultJavaCompileOptions -import com.itsaky.androidide.builder.model.DefaultLibrary -import com.itsaky.androidide.builder.model.DefaultModelSyncFile -import com.itsaky.androidide.builder.model.DefaultSourceProvider -import com.itsaky.androidide.builder.model.DefaultSourceSetContainer -import com.itsaky.androidide.builder.model.DefaultVariant -import com.itsaky.androidide.builder.model.DefaultViewBindingOptions -import com.itsaky.androidide.builder.model.IJavaCompilerSettings -import com.itsaky.androidide.builder.model.UNKNOWN_PACKAGE -import com.itsaky.androidide.tooling.api.IProject.Type -import com.itsaky.androidide.tooling.api.IProject.Type.Android -import com.itsaky.androidide.tooling.api.messages.result.SimpleVariantData -import com.itsaky.androidide.tooling.api.model.util.extractPackageName -import java.io.File -import java.io.Serializable -import java.util.concurrent.CompletableFuture - -/** - * Default implementation of [AndroidProject]. - * - * @author Akash Yadav - */ -open class AndroidModule( - name: String, - path: String, - description: String?, - projectDir: File, - buildDir: File, - buildScript: File, - parent: IdeGradleProject?, - tasks: List, - val projectType: ProjectType?, - override val dynamicFeatures: Collection?, - override val flags: DefaultAndroidGradlePluginProjectFlags, - final override val javaCompileOptions: DefaultJavaCompileOptions, - override val resourcePrefix: String?, - @Deprecated("This is resource intensive. Use AndroidModule.simpleVariants instead.") - override val variants: Collection = emptyList(), - override val viewBindingOptions: DefaultViewBindingOptions?, - override val lintChecksJars: List, - override val modelSyncFiles: List, - val simpleVariants: MutableList = mutableListOf() -) : - IdeGradleProject(name, description, path, projectDir, buildDir, buildScript, parent, tasks), - ModuleProject, - AndroidProject, - Serializable { - - private var shouldLookupPackage = true - open var packageName: String = UNKNOWN_PACKAGE - get() { - if (field == UNKNOWN_PACKAGE && shouldLookupPackage) { - findPackageName() - } - - return field - } - var bootClassPaths: Collection = emptyList() - var mainSourceSet: DefaultSourceSetContainer? = null - var libraries = mutableSetOf() - var libraryMap = mutableMapOf() - - override var compilerSettings: IJavaCompilerSettings = javaCompileOptions - - @Suppress("unused") - companion object { - const val ANDROID_NAMESPACE = "http://schemas.android.com/res/android" - - // Injectable properties to use with -P - // Sent by Studio 4.2+ - const val PROPERTY_BUILD_MODEL_ONLY = "android.injected.build.model.v2" - - // Sent by Studio 2.2+ and Android Support plugin running with IDEA from 4.1+ - // This property will enable compatibility checks between Android Support plugin and the - // Android - // Gradle plugin. - // A use case for this property is that by restricting which versions are compatible - // with the plugin, we could safely remove deprecated methods in the builder-model - // interfaces. - const val PROPERTY_ANDROID_SUPPORT_VERSION = "android.injected.studio.version" - - // Sent in when external native projects models requires a refresh. - const val PROPERTY_REFRESH_EXTERNAL_NATIVE_MODEL = - "android.injected.refresh.external.native.model" - - // Sent by Studio 2.2+ - // This property is sent when a run or debug is invoked. APK built with this property - // should - // be marked with android:testOnly="true" in the AndroidManifest.xml such that it will be - // rejected by the Play store. - const val PROPERTY_TEST_ONLY = "android.injected.testOnly" - - // Sent by Studio 1.5+ - // The version api level of the target device. - const val PROPERTY_BUILD_API = "android.injected.build.api" - - // The version codename of the target device. Null for released versions, - const val PROPERTY_BUILD_API_CODENAME = "android.injected.build.codename" - const val PROPERTY_BUILD_ABI = "android.injected.build.abi" - const val PROPERTY_BUILD_DENSITY = "android.injected.build.density" - - // Has the effect of telling the Gradle plugin to - // 1) Generate machine-readable errors - // 2) Generate build metadata JSON files - const val PROPERTY_INVOKED_FROM_IDE = "android.injected.invoked.from.ide" - const val PROPERTY_SIGNING_STORE_FILE = "android.injected.signing.store.file" - const val PROPERTY_SIGNING_STORE_PASSWORD = "android.injected.signing.store.password" - const val PROPERTY_SIGNING_KEY_ALIAS = "android.injected.signing.key.alias" - const val PROPERTY_SIGNING_KEY_PASSWORD = "android.injected.signing.key.password" - const val PROPERTY_SIGNING_STORE_TYPE = "android.injected.signing.store.type" - const val PROPERTY_SIGNING_V1_ENABLED = "android.injected.signing.v1-enabled" - const val PROPERTY_SIGNING_V2_ENABLED = "android.injected.signing.v2-enabled" - const val PROPERTY_DEPLOY_AS_INSTANT_APP = "android.injected.deploy.instant-app" - const val PROPERTY_SIGNING_COLDSWAP_MODE = "android.injected.coldswap.mode" - const val PROPERTY_APK_SELECT_CONFIG = "android.inject.apkselect.config" - const val PROPERTY_EXTRACT_INSTANT_APK = "android.inject.bundle.extractinstant" - - /** Version code to be used in the built APK. */ - const val PROPERTY_VERSION_CODE = "android.injected.version.code" - - /** Version name to be used in the built APK. */ - const val PROPERTY_VERSION_NAME = "android.injected.version.name" - - /** - * Location for APKs. If defined as a relative path, then it is resolved against the project's - * path. - */ - const val PROPERTY_APK_LOCATION = "android.injected.apk.location" - - /** - * Location of the build attribution file produced by the gradle plugin to be deserialized and - * used in the IDE build attribution. - */ - const val PROPERTY_ATTRIBUTION_FILE_LOCATION = "android.injected.attribution.file.location" - - /** - * Comma separated list of on-demand dynamic modules or instant app modules names that are - * selected by the user for installation on the device during deployment. - */ - const val PROPERTY_INJECTED_DYNAMIC_MODULES_LIST = "android.injected.modules.install.list" - - const val FD_INTERMEDIATES = "intermediates" - const val FD_LOGS = "logs" - const val FD_OUTPUTS = "outputs" - const val FD_GENERATED = "generated" - } - - override fun getType(): CompletableFuture { - return CompletableFuture.completedFuture(Android) - } - - override fun getGeneratedJar(variant: String): File { - return File(buildDir, "$FD_INTERMEDIATES/compile_library_classes_jar/$variant/classes.jar") - } - - override fun getClassPaths(): Set = - mutableSetOf().apply { - add(getGeneratedJar("debug")) - addAll(simpleVariants.first { it.name == "debug" }.mainArtifact.classJars) - } - - private fun findPackageName() { - - if (this.namespace.isNotBlank()) { - this.packageName = namespace - this.shouldLookupPackage = false - return - } - - if (mainSourceSet == null) { - shouldLookupPackage = false - return - } - - val manifestFile = mainSourceSet!!.sourceProvider.manifestFile - if (manifestFile == DefaultSourceProvider.NoFile) { - shouldLookupPackage = false - return - } - - this.packageName = extractPackageName(manifestFile) ?: UNKNOWN_PACKAGE - this.shouldLookupPackage = false - } - - override var androidTestNamespace: String? = null - override var namespace: String = "" - override var testFixturesNamespace: String? = null -} +/* + * 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 + +import com.itsaky.androidide.builder.model.DefaultLibrary +import com.itsaky.androidide.builder.model.DefaultModelSyncFile +import com.itsaky.androidide.builder.model.DefaultSourceSetContainer +import com.itsaky.androidide.tooling.api.models.AndroidVariantMetadata +import com.itsaky.androidide.tooling.api.models.BasicAndroidVariantMetadata +import com.itsaky.androidide.tooling.api.models.params.StringParameter +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest +import org.eclipse.lsp4j.jsonrpc.services.JsonSegment +import java.io.File +import java.util.concurrent.CompletableFuture + +/** + * Model for an Android project/module. + * + * @author Akash Yadav + */ +@JsonSegment("android") +interface IAndroidProject : IModuleProject { + + /** + * Get the metadata about all variants of this Android project. + */ + @JsonRequest + fun getVariants(): CompletableFuture> + + /** + * Get the metadata about the variant with the given name. + */ + @JsonRequest + fun getVariant(param: StringParameter): CompletableFuture + + /** + * Get the boot classpaths for this Android project. + */ + @JsonRequest + fun getBootClasspaths(): CompletableFuture> + + /** + * Get the map of libraries. Each entry is a unique key representing the library, and allowing + * to match it with GraphItem instances. + * + * @param variant The name of the variant for which the libraries will be fetched. + */ + @JsonRequest + fun getLibraryMap(): CompletableFuture> + + /** + * Get the main source set container for this project. + * + * @return The main source set container or `null` if it is unavailable. + */ + @JsonRequest + fun getMainSourceSet(): CompletableFuture + + /** + * Get the lint check jars for this project. + */ + @JsonRequest + fun getLintCheckJars(): CompletableFuture> + + /** + * Get the list of [DefaultModelSyncFile]s for this project. + * See [ModelSyncFile][com.android.builder.model.v2.ModelSyncFile] for more details. + */ + @JsonRequest + fun getModelSyncFiles(): CompletableFuture> + + @Suppress("unused") + companion object { + + const val ANDROID_NAMESPACE = "http://schemas.android.com/res/android" + + // Injectable properties to use with -P + // Sent by Studio 4.2+ + const val PROPERTY_BUILD_MODEL_ONLY = "android.injected.build.model.v2" + + // Sent by Studio 2.2+ and Android Support plugin running with IDEA from 4.1+ + // This property will enable compatibility checks between Android Support plugin and the + // Android + // Gradle plugin. + // A use case for this property is that by restricting which versions are compatible + // with the plugin, we could safely remove deprecated methods in the builder-model + // interfaces. + const val PROPERTY_ANDROID_SUPPORT_VERSION = "android.injected.studio.version" + + // Sent in when external native projects models requires a refresh. + const val PROPERTY_REFRESH_EXTERNAL_NATIVE_MODEL = + "android.injected.refresh.external.native.model" + + // Sent by Studio 2.2+ + // This property is sent when a run or debug is invoked. APK built with this property + // should + // be marked with android:testOnly="true" in the AndroidManifest.xml such that it will be + // rejected by the Play store. + const val PROPERTY_TEST_ONLY = "android.injected.testOnly" + + // Sent by Studio 1.5+ + // The version api level of the target device. + const val PROPERTY_BUILD_API = "android.injected.build.api" + + // The version codename of the target device. Null for released versions, + const val PROPERTY_BUILD_API_CODENAME = "android.injected.build.codename" + const val PROPERTY_BUILD_ABI = "android.injected.build.abi" + const val PROPERTY_BUILD_DENSITY = "android.injected.build.density" + + // Has the effect of telling the Gradle plugin to + // 1) Generate machine-readable errors + // 2) Generate build metadata JSON files + const val PROPERTY_INVOKED_FROM_IDE = "android.injected.invoked.from.ide" + const val PROPERTY_SIGNING_STORE_FILE = "android.injected.signing.store.file" + const val PROPERTY_SIGNING_STORE_PASSWORD = "android.injected.signing.store.password" + const val PROPERTY_SIGNING_KEY_ALIAS = "android.injected.signing.key.alias" + const val PROPERTY_SIGNING_KEY_PASSWORD = "android.injected.signing.key.password" + const val PROPERTY_SIGNING_STORE_TYPE = "android.injected.signing.store.type" + const val PROPERTY_SIGNING_V1_ENABLED = "android.injected.signing.v1-enabled" + const val PROPERTY_SIGNING_V2_ENABLED = "android.injected.signing.v2-enabled" + const val PROPERTY_DEPLOY_AS_INSTANT_APP = "android.injected.deploy.instant-app" + const val PROPERTY_SIGNING_COLDSWAP_MODE = "android.injected.coldswap.mode" + const val PROPERTY_APK_SELECT_CONFIG = "android.inject.apkselect.config" + const val PROPERTY_EXTRACT_INSTANT_APK = "android.inject.bundle.extractinstant" + + /** Version code to be used in the built APK. */ + const val PROPERTY_VERSION_CODE = "android.injected.version.code" + + /** Version name to be used in the built APK. */ + const val PROPERTY_VERSION_NAME = "android.injected.version.name" + + /** + * Location for APKs. If defined as a relative path, then it is resolved against the project's + * path. + */ + const val PROPERTY_APK_LOCATION = "android.injected.apk.location" + + /** + * Location of the build attribution file produced by the gradle plugin to be deserialized and + * used in the IDE build attribution. + */ + const val PROPERTY_ATTRIBUTION_FILE_LOCATION = "android.injected.attribution.file.location" + + /** + * Comma separated list of on-demand dynamic modules or instant app modules names that are + * selected by the user for installation on the device during deployment. + */ + const val PROPERTY_INJECTED_DYNAMIC_MODULES_LIST = "android.injected.modules.install.list" + + const val FD_INTERMEDIATES = "intermediates" + const val FD_LOGS = "logs" + const val FD_OUTPUTS = "outputs" + const val FD_GENERATED = "generated" + } +} \ No newline at end of file diff --git a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/IGradleProject.kt b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/IGradleProject.kt new file mode 100644 index 000000000..293527bed --- /dev/null +++ b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/IGradleProject.kt @@ -0,0 +1,53 @@ +/* + * 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 + +import com.itsaky.androidide.tooling.api.models.BasicProjectMetadata +import com.itsaky.androidide.tooling.api.models.GradleTask +import com.itsaky.androidide.tooling.api.models.ProjectMetadata +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest +import org.eclipse.lsp4j.jsonrpc.services.JsonSegment +import java.util.concurrent.CompletableFuture + +/** + * A model for representing a project which is not an Android or Java project. + * + * @author Akash Yadav + */ +@JsonSegment("gradle") +interface IGradleProject { + + /** + * Get the metadata about the project. This includes basic information about the project. + * + * @see [ProjectMetadata]. + */ + @JsonRequest + fun getMetadata(): CompletableFuture + + /** + * Get this project's tasks. + */ + @JsonRequest + fun getTasks(): CompletableFuture> + + companion object { + + const val FILE_PATH_NOT_AVAILABLE = "" + } +} \ No newline at end of file diff --git a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/ModuleProject.kt b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/IJavaProject.kt similarity index 52% rename from subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/ModuleProject.kt rename to subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/IJavaProject.kt index f5eebd944..631a048f7 100644 --- a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/ModuleProject.kt +++ b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/IJavaProject.kt @@ -1,46 +1,45 @@ -/* - * 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.model - -import com.itsaky.androidide.builder.model.IJavaCompilerSettings -import java.io.File - -/** - * A module in a Gradle project. - * - * @author Akash Yadav - */ -interface ModuleProject { - - /** Compiler settings for this module. */ - val compilerSettings: IJavaCompilerSettings - - /** - * Get the generated JAR file for this module. - * - * @param variant The build variant name. NOT applicable for [JavaModule]. - */ - fun getGeneratedJar(variant: String): File - - /** - * Get the classpath of this module. For example, the generated JAR file. - * - * @return The class paths. - */ - fun getClassPaths(): Set -} +/* + * 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 + +import com.itsaky.androidide.tooling.api.models.JavaContentRoot +import com.itsaky.androidide.tooling.api.models.JavaModuleDependency +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest +import org.eclipse.lsp4j.jsonrpc.services.JsonSegment +import java.util.concurrent.CompletableFuture + +/** + * Model for a Java library project. + * + * @author Akash Yadav + */ +@JsonSegment("java") +interface IJavaProject : IModuleProject { + + /** + * Get the content roots for this Java project. + */ + @JsonRequest + fun getContentRoots(): CompletableFuture> + + /** + * Get the dependencies of this Java project. + */ + @JsonRequest + fun getDependencies() : CompletableFuture> +} \ No newline at end of file diff --git a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/IModuleProject.kt b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/IModuleProject.kt new file mode 100644 index 000000000..ae485611d --- /dev/null +++ b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/IModuleProject.kt @@ -0,0 +1,40 @@ +/* + * 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 + +import java.io.File +import java.util.concurrent.CompletableFuture + +/** + * Common APIs for module projects. + * + * @author Akash Yadav + */ +interface IModuleProject : IGradleProject { + + /** + * Get the `classes.jar` file for this module project. + */ + fun getClassesJar(): CompletableFuture + + /** + * Get the classpaths for this module project. The returned list always included the + * `classes.jar` file from [getClassesJar]. + */ + fun getClasspaths(): CompletableFuture> +} \ No newline at end of file diff --git a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/IProject.java b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/IProject.java deleted file mode 100644 index 5a7555b6e..000000000 --- a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/IProject.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * 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; - -import com.itsaky.androidide.tooling.api.messages.VariantDataRequest; -import com.itsaky.androidide.tooling.api.messages.result.SimpleModuleData; -import com.itsaky.androidide.tooling.api.messages.result.SimpleVariantData; -import com.itsaky.androidide.tooling.api.model.AndroidModule; -import com.itsaky.androidide.tooling.api.model.GradleTask; -import com.itsaky.androidide.tooling.api.model.IdeGradleProject; - -import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; -import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; -import org.jetbrains.annotations.NotNull; - -import java.io.File; -import java.util.List; -import java.util.concurrent.CompletableFuture; - -/** - * Root Gradle project. The getter methods return completed {@link CompletableFuture}s. - * - * @author Akash Yadav - */ -@JsonSegment("project") -public interface IProject { - - String FILE_PATH_NOT_AVAILABLE = ""; - - @NotNull - @JsonRequest - CompletableFuture isProjectInitialized(); - - @NotNull - @JsonRequest - CompletableFuture getName(); - - @NotNull - @JsonRequest - CompletableFuture getDescription(); - - @NotNull - @JsonRequest - CompletableFuture getProjectPath(); - - @NotNull - @JsonRequest - CompletableFuture getProjectDir(); - - @NotNull - @JsonRequest - CompletableFuture getType(); - - @NotNull - @JsonRequest - CompletableFuture getBuildDir(); - - @NotNull - @JsonRequest - CompletableFuture getBuildScript(); - - @NotNull - @JsonRequest - CompletableFuture> getTasks(); - - @NotNull - @JsonRequest - CompletableFuture> listModules(); - - @NotNull - @JsonRequest - CompletableFuture getVariantData(@NotNull VariantDataRequest request); - - @NotNull - @JsonRequest - CompletableFuture findByPath(@NotNull String path); - - @NotNull - @JsonRequest - CompletableFuture findFirstAndroidModule(); - - @NotNull - @JsonRequest - CompletableFuture findFirstAndroidAppModule(); - - @NotNull - @JsonRequest - CompletableFuture> findAndroidModules(); - - /** Type of the {@link IProject}. */ - enum Type { - - /** A simple Gradle project. Only root projects are represented by this type. */ - Gradle, - - /** - * An Android project. Mostly module projects are of this type. But in some cases, this type can - * also be applied to a root Gradle project. - */ - Android, - - /** - * A Java project. Usually, module projects which are not {@link Type#Android} type are of this - * type. - */ - Java, - - /** An unknown project type. */ - Unknown - } -} diff --git a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/IProject.kt b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/IProject.kt new file mode 100644 index 000000000..507e4f47c --- /dev/null +++ b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/IProject.kt @@ -0,0 +1,47 @@ +/* + * 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 + +import com.itsaky.androidide.builder.model.DefaultProjectSyncIssues +import com.itsaky.androidide.tooling.api.models.BasicProjectMetadata +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest +import org.eclipse.lsp4j.jsonrpc.services.JsonSegment +import java.util.concurrent.CompletableFuture + +/** + * A service providing access to the whole Gradle project and its structure. + * + * This class + * + * @author Akash Yadav + */ +@JsonSegment("root") +interface IProject : IProjectQueries { + + /** + * Get all the projects included in this root project. + */ + @JsonRequest + fun getProjects(): CompletableFuture> + + /** + * Get the project sync issues. + */ + @JsonRequest + fun getProjectSyncIssues(): CompletableFuture> +} \ No newline at end of file diff --git a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/IProjectQueries.kt b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/IProjectQueries.kt new file mode 100644 index 000000000..3d6c32090 --- /dev/null +++ b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/IProjectQueries.kt @@ -0,0 +1,81 @@ +/* + * 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 + +import com.itsaky.androidide.tooling.api.models.params.StringParameter +import com.itsaky.androidide.tooling.api.models.result.SelectProjectResult +import org.eclipse.lsp4j.jsonrpc.services.JsonDelegate +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest +import java.util.concurrent.CompletableFuture + +/** + * Transmiting the whole project models over the communication streams results in a bad performance. + * If the project is huge, the IDE may even run out of allocated memory. + * + * In the previous tooling API implementation, we used to fetch project models from Gradle, copy their + * data to our own models, then send it to the client. This resulted in a bad performance and + * increased memory usage. To overcome this, we keep a reference to the project models we get from + * from the Android Gradle Plugin and compute only the requested information from those models. + * + * With LSP4J's JsonRpc implementation, it is not possible to create a delegate method in a + * service which can accept parameters. What this means is that if a method is annotated with + * [JsonDelegate][org.eclipse.lsp4j.jsonrpc.services.JsonDelegate], it cannot accept any parameters. + * + * As we cannot send the whole project model to the client for the reasons mentioned above, we use + * another approach in this implementation. The client first selects the project it wants to fetch + * information about. After this, when the delegate methods are called, it fetches the information + * about the selected project. + * + * TODO(itsaky): Find a better approach to this issue. + * + * @author Akash Yadav + */ +interface IProjectQueries { + + /** + * Select the project to work with. This overwrites the existing selection. + * + * @param param A [StringParameter] with the project's path as the value. + */ + @JsonRequest + fun selectProject(param: StringParameter): CompletableFuture + + /** + * Get the type of selected project. + */ + @JsonRequest + fun getType(): CompletableFuture + + /** + * Get the selected project as Gradle project. + */ + @JsonDelegate + fun asGradleProject(): IGradleProject? + + /** + * Get the selected project as Android project. + */ + @JsonDelegate + fun asAndroidProject(): IAndroidProject? + + /** + * Get the selected project as Java project. + */ + @JsonDelegate + fun asJavaProject(): IJavaProject? +} \ No newline at end of file diff --git a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/ProjectType.kt b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/ProjectType.kt new file mode 100644 index 000000000..98832d7be --- /dev/null +++ b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/ProjectType.kt @@ -0,0 +1,44 @@ +/* + * 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 + +/** + * The type of [IGradleProject]. + * + * @author Akash Yadav + */ +enum class ProjectType { + + /** A simple Gradle project. Only root projects are represented by this type. */ + Gradle, + + /** + * An Android project. Mostly module projects are of this type. But in some cases, this type can + * also be applied to a root Gradle project. + */ + Android, + + /** + * A Java project. Usually, module projects which are not {@link Type#Android} type are of this + * type. + */ + Java, + + /** An unknown project type. */ + Unknown +} \ No newline at end of file diff --git a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/IdeGradleProject.kt b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/IdeGradleProject.kt deleted file mode 100644 index 8c91be7c4..000000000 --- a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/IdeGradleProject.kt +++ /dev/null @@ -1,141 +0,0 @@ -/* - * 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.model - -import com.android.builder.model.v2.ide.ProjectType.APPLICATION -import com.itsaky.androidide.tooling.api.IProject.Type -import com.itsaky.androidide.tooling.api.IProject.Type.Gradle -import com.itsaky.androidide.tooling.api.messages.VariantDataRequest -import com.itsaky.androidide.tooling.api.messages.result.SimpleModuleData -import com.itsaky.androidide.tooling.api.messages.result.SimpleVariantData -import org.eclipse.lsp4j.jsonrpc.CompletableFutures -import java.io.File -import java.io.Serializable -import java.util.concurrent.* - -/** - * A root Gradle project. - * @author Akash Yadav - */ -open class IdeGradleProject( - @JvmField val name: String, - @JvmField val description: String?, - @JvmField val projectPath: String, - @JvmField val projectDir: File, - @JvmField val buildDir: File, - @JvmField val buildScript: File, - @JvmField val parent: IdeGradleProject?, - @JvmField val tasks: List, -) : com.itsaky.androidide.tooling.api.IProject, Serializable { - private val serialVersionUID = 1L - - private val gsonType: String = javaClass.name - @JvmField val moduleProjects: MutableList = mutableListOf() - - override fun isProjectInitialized(): CompletableFuture { - return CompletableFuture.completedFuture(true) - } - - override fun getName(): CompletableFuture { - return CompletableFutures.computeAsync { this.name } - } - - override fun getDescription(): CompletableFuture { - return CompletableFutures.computeAsync { this.description } - } - - override fun getProjectPath(): CompletableFuture { - return CompletableFutures.computeAsync { this.projectPath } - } - - override fun getProjectDir(): CompletableFuture { - return CompletableFutures.computeAsync { this.projectDir } - } - - override fun getType(): CompletableFuture { - return CompletableFuture.completedFuture(Gradle) - } - - override fun getBuildDir(): CompletableFuture { - return CompletableFutures.computeAsync { this.buildDir } - } - - override fun getBuildScript(): CompletableFuture { - return CompletableFutures.computeAsync { this.buildScript } - } - - override fun getTasks(): CompletableFuture> { - return CompletableFutures.computeAsync { this.tasks.toMutableList() } - } - - override fun listModules(): CompletableFuture> { - return CompletableFutures.computeAsync { - return@computeAsync this.moduleProjects - .map { - SimpleModuleData( - name = it.name, - path = it.projectPath, - projectDir = it.projectDir, - classPaths = if (it is ModuleProject) it.getClassPaths() else emptySet() - ) - } - .toMutableList() - } - } - - override fun getVariantData(request: VariantDataRequest): CompletableFuture { - return findByPath(request.projectPath).thenApply { project -> - if (project !is AndroidModule) { - return@thenApply null - } - - return@thenApply project.simpleVariants.firstOrNull { it.name == request.variantName } - } - } - - override fun findByPath(path: String): CompletableFuture { - return CompletableFutures.computeAsync { - if (path == this.projectPath) { - return@computeAsync this - } - - return@computeAsync moduleProjects.firstOrNull { it.projectPath == path } - } - } - - override fun findAndroidModules(): CompletableFuture> { - return CompletableFutures.computeAsync { - moduleProjects.filterIsInstance(AndroidModule::class.java) - } - } - - override fun findFirstAndroidModule(): CompletableFuture { - return findAndroidModules().thenApply { it.firstOrNull() } - } - - override fun findFirstAndroidAppModule(): CompletableFuture { - if (this is AndroidModule) { - return CompletableFuture.completedFuture(this) - } - - return findAndroidModules().thenApply { modules -> - val application = - modules.stream().filter { it != null && it.projectType == APPLICATION }.findFirst() - application.orElse(null) - } - } -} diff --git a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/JavaModule.kt b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/JavaModule.kt deleted file mode 100644 index 7acbb5848..000000000 --- a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/JavaModule.kt +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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.model - -import com.itsaky.androidide.builder.model.IJavaCompilerSettings -import com.itsaky.androidide.tooling.api.IProject.Type -import com.itsaky.androidide.tooling.api.IProject.Type.Java -import java.io.File -import java.io.Serializable -import java.util.concurrent.* - -/** - * A java library model. Modules represented by this model does not apply any of the Android - * plugins. - * - * @author Akash Yadav - */ -open class JavaModule( - name: String, - path: String, - description: String?, - projectDir: File, - buildDir: File, - buildScript: File, - parent: IdeGradleProject?, - tasks: List, - - /** Compiler settings for this module. */ - override val compilerSettings: IJavaCompilerSettings, - - /** * Source directories of this project. */ - val contentRoots: List, - - /** Dependencies of this project. */ - val javaDependencies: MutableList -) : - IdeGradleProject(name, description, path, projectDir, buildDir, buildScript, parent, tasks), - ModuleProject, - Serializable { - - override fun getType(): CompletableFuture { - return CompletableFuture.completedFuture(Java) - } - - override fun getGeneratedJar(variant: String): File { - var jar = File(buildDir, "libs/$name.jar") - if (jar.exists()) { - return jar - } - - jar = - File(buildDir, "libs").listFiles()?.first { it.name.startsWith(this.name) } - ?: File("module-jar-does-not-exist.jar") - - return jar - } - - override fun getClassPaths() = - javaDependencies.mapNotNull { it.jarFile }.toMutableSet().apply { add(getGeneratedJar("")) } -} diff --git a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/util/ProjectBuilder.kt b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/util/ProjectBuilder.kt deleted file mode 100644 index d0d33490b..000000000 --- a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/util/ProjectBuilder.kt +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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.model.util - -import com.android.builder.model.v2.ide.ProjectType -import com.itsaky.androidide.builder.model.DefaultAndroidGradlePluginProjectFlags -import com.itsaky.androidide.builder.model.DefaultJavaCompileOptions -import com.itsaky.androidide.builder.model.DefaultModelSyncFile -import com.itsaky.androidide.builder.model.DefaultViewBindingOptions -import com.itsaky.androidide.builder.model.IJavaCompilerSettings -import com.itsaky.androidide.tooling.api.messages.result.SimpleVariantData -import com.itsaky.androidide.tooling.api.model.AndroidModule -import com.itsaky.androidide.tooling.api.model.GradleTask -import com.itsaky.androidide.tooling.api.model.IdeGradleProject -import com.itsaky.androidide.tooling.api.model.JavaContentRoot -import com.itsaky.androidide.tooling.api.model.JavaModule -import com.itsaky.androidide.tooling.api.model.JavaModuleCompilerSettings -import com.itsaky.androidide.tooling.api.model.JavaModuleDependency -import java.io.File - -/** - * Builds instances of [IdeGradleProject]. - * - * @author Akash Yadav - */ -class ProjectBuilder { - var name: String = "" - var description: String? = null - var path: String = ":" - var projectDir: File = File(".") - var buildDir: File = File(".") - var buildScript: File = File(".") - var parent: IdeGradleProject? = null - var modules: List = mutableListOf() - var tasks: List = mutableListOf() - var dynamicFeatures: Collection? = mutableListOf() - var flags: DefaultAndroidGradlePluginProjectFlags = - DefaultAndroidGradlePluginProjectFlags(emptyMap()) - - // For Android modules only - var javaCompileOptions: DefaultJavaCompileOptions = DefaultJavaCompileOptions() - - // For Java modules - var javaCompilerSettings: IJavaCompilerSettings = JavaModuleCompilerSettings() - - var resourcePrefix: String? = "" - var viewBindingOptions: DefaultViewBindingOptions? = null - var modelSyncFiles: List = emptyList() - var lintChecksJars: List = mutableListOf() - var contentRoots: List = mutableListOf() - var javaDependencies: MutableList = mutableListOf() - var projectType: ProjectType? = null - var simpleVariants: MutableList = mutableListOf() - - fun buildGradleProject(): IdeGradleProject { - return IdeGradleProject( - name, - description, - path, - projectDir, - buildDir, - buildScript, - parent, - tasks - ) - } - - fun buildJavaModule(): JavaModule { - return JavaModule( - name, - path, - description, - projectDir, - buildDir, - buildScript, - parent, - tasks, - javaCompilerSettings, - contentRoots, - javaDependencies - ) - } - - fun buildAndroidModule(): AndroidModule = - AndroidModule( - name = name, - path = path, - description = description, - projectDir = projectDir, - buildDir = buildDir, - buildScript = buildScript, - parent = parent, - tasks = tasks, - projectType = projectType, - dynamicFeatures = dynamicFeatures, - flags = flags, - javaCompileOptions = javaCompileOptions, - resourcePrefix = resourcePrefix, - viewBindingOptions = viewBindingOptions, - lintChecksJars = lintChecksJars, - modelSyncFiles = modelSyncFiles, - simpleVariants = simpleVariants - ) -} diff --git a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/messages/result/SimpleArtifact.kt b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/AndroidArtifactMetadata.kt similarity index 88% rename from subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/messages/result/SimpleArtifact.kt rename to subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/AndroidArtifactMetadata.kt index 2a8ff8379..a7c236d26 100644 --- a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/messages/result/SimpleArtifact.kt +++ b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/AndroidArtifactMetadata.kt @@ -1,44 +1,44 @@ -/* - * 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.result - -import java.io.File -import java.io.Serializable - -/** - * Artifact in a [SimpleVariantData]. - * - * @author Akash Yadav - */ -data class SimpleArtifact( - val name: String, - val resGenTaskName: String?, - val assembleTaskOutputListingFile: File?, - val generatedResourceFolders: Collection, - val generatedSourceFolders: Collection, - val maxSdkVersion: Int?, - val minSdkVersion: Int, - val signingConfigName: String?, - val sourceGenTaskName: String, - val assembleTaskName: String, - val classJars: List, - val compileTaskName: String, - val targetSdkVersionOverride: Int -) : Serializable { - private val serialVersionUID = 1L -} +/* + * 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.models + +import java.io.File +import java.io.Serializable + +/** + * Metadata about variant artifacts in an Android project. + * + * @author Akash Yadav + */ +data class AndroidArtifactMetadata( + val name: String, + val resGenTaskName: String?, + val assembleTaskOutputListingFile: File?, + val generatedResourceFolders: Collection, + val generatedSourceFolders: Collection, + val maxSdkVersion: Int?, + val minSdkVersion: Int, + val signingConfigName: String?, + val sourceGenTaskName: String, + val assembleTaskName: String, + val classJars: List, + val compileTaskName: String, + val targetSdkVersionOverride: Int +) : Serializable { + private val serialVersionUID = 1L +} \ No newline at end of file diff --git a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/AndroidProjectMetadata.kt b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/AndroidProjectMetadata.kt new file mode 100644 index 000000000..20c576eae --- /dev/null +++ b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/AndroidProjectMetadata.kt @@ -0,0 +1,82 @@ +/* + * 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.models + +import com.itsaky.androidide.builder.model.DefaultAndroidGradlePluginProjectFlags +import com.itsaky.androidide.builder.model.DefaultJavaCompileOptions +import com.itsaky.androidide.builder.model.DefaultViewBindingOptions +import com.itsaky.androidide.tooling.api.ProjectType +import java.io.File + +/** + * Metadata about an Android project. + * + * @author Akash Yadav + */ +class AndroidProjectMetadata( + name: String?, + path: String, + projectDir: File, + buildDir: File, + description: String?, + buildScript: File, + type: ProjectType, + val packageName: String, + val androidType: AndroidProjectType, + val flags: DefaultAndroidGradlePluginProjectFlags, + val javaCompileOptions: DefaultJavaCompileOptions, + val viewBindingOptions: DefaultViewBindingOptions, + val resourcePrefix: String?, + val namespace: String?, + val androidTestNamespace: String?, + val testFixtureNamespace: String? +) : ProjectMetadata(name, path, projectDir, buildDir, description, buildScript, type) { + + constructor( + base: ProjectMetadata, + packageName: String, + androidType: AndroidProjectType, + flags: DefaultAndroidGradlePluginProjectFlags, + javaCompileOptiosn: DefaultJavaCompileOptions, + viewBindingOptions: DefaultViewBindingOptions, + resourcePrefix: String?, + namespace: String?, + androidTestNamespace: String?, + testFixtureNamespace: String?) : + + this( + base.name, + base.projectPath, + base.projectDir, + base.buildDir, + base.description, + base.buildScript, + base.type, + packageName, + androidType, + flags, + javaCompileOptiosn, + viewBindingOptions, + resourcePrefix, + namespace, + androidTestNamespace, + testFixtureNamespace + ) +} + +typealias AndroidProjectType = com.android.builder.model.v2.ide.ProjectType \ No newline at end of file diff --git a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/messages/result/SimpleVariantData.kt b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/AndroidVariantMetadata.kt similarity index 66% rename from subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/messages/result/SimpleVariantData.kt rename to subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/AndroidVariantMetadata.kt index 0e84bd97d..b652d7150 100644 --- a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/messages/result/SimpleVariantData.kt +++ b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/AndroidVariantMetadata.kt @@ -1,33 +1,35 @@ -/* - * 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.result - -import java.io.Serializable - -/** - * Contains simple data about a variant in an Android module project. - * - * @author Akash Yadav - */ -data class SimpleVariantData( - val name: String, - val mainArtifact: SimpleArtifact, - val otherArtifacts: Map -) : Serializable { - private val serialVersionUID = 1L -} +/* + * 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.models + +import java.io.Serializable + +/** + * Metadata about a variant in an Android project. + * + * @property otherArtifacts Artifacts of this variant other than the [mainArtifact]. + * @author Akash Yadav + */ + +open class AndroidVariantMetadata( + name: String, + mainArtifact: AndroidArtifactMetadata, + val otherArtifacts: Map +) : BasicAndroidVariantMetadata(name, mainArtifact), Serializable { + private val serialVersionUID = 1L +} \ No newline at end of file diff --git a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/IdeLaunchable.kt b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/BasicAndroidVariantMetadata.kt similarity index 71% rename from subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/IdeLaunchable.kt rename to subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/BasicAndroidVariantMetadata.kt index c585e8a8b..2c1402da7 100644 --- a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/IdeLaunchable.kt +++ b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/BasicAndroidVariantMetadata.kt @@ -1,29 +1,32 @@ -/* - * 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.model - -import java.io.Serializable - -/** - * Default implementation for [IdeLaunchable]. - * - * @author Akash Yadav - */ -open class IdeLaunchable(val displayName: String?, val isPublic: Boolean?) : Serializable { - protected val gsonType: String = javaClass.name - private val serialVersionUID = 1L -} +/* + * 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.models + +import java.io.Serializable + +/** + * Basic metadata about a variant in an Android project. + * + * @property name The name of the variant. + * @property mainArtifact Metadata about the main artifact of this variant. + * @author Akash Yadav + */ +open class BasicAndroidVariantMetadata(val name: String, val mainArtifact: AndroidArtifactMetadata) : Serializable { + protected val gsonType: String = javaClass.name + private val serialVersionUID = 1L +} \ No newline at end of file diff --git a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/messages/result/SimpleModuleData.kt b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/BasicProjectMetadata.kt similarity index 66% rename from subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/messages/result/SimpleModuleData.kt rename to subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/BasicProjectMetadata.kt index af7bb2a00..9e37995f3 100644 --- a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/messages/result/SimpleModuleData.kt +++ b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/BasicProjectMetadata.kt @@ -1,35 +1,41 @@ -/* - * 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.result - -import java.io.File -import java.io.Serializable - -/** - * Container for basic data about module projects. - * - * @author Akash Yadav - */ -data class SimpleModuleData( - val name: String, - val path: String, - val projectDir: File, - val classPaths: Set = emptySet() -) : Serializable { - private val serialVersionUID = 1L -} +/* + * 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.models + +import java.io.File +import java.io.Serializable + +/** + * Basic (minimal) metadata about a project. + * + * @property name The name of the module. + * @property projectPath The project's path. + * @property projectDir The project directory. + * @property buildDir The project's build directory. + * @author Akash Yadav + */ +open class BasicProjectMetadata( + val name: String?, + val projectPath: String, + val projectDir: File, + val buildDir: File +) : Serializable { + + protected val gsonType: String = javaClass.name + private val serialVersionUID = 1L +} \ No newline at end of file diff --git a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/GradleArtifact.kt b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/GradleArtifact.kt similarity index 92% rename from subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/GradleArtifact.kt rename to subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/GradleArtifact.kt index 78458519e..1d8c5ea16 100644 --- a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/GradleArtifact.kt +++ b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/GradleArtifact.kt @@ -15,7 +15,7 @@ * along with AndroidIDE. If not, see . */ -package com.itsaky.androidide.tooling.api.model +package com.itsaky.androidide.tooling.api.models /** * Represents a Gradle dependency artifact. @@ -23,4 +23,4 @@ package com.itsaky.androidide.tooling.api.model * @author Akash Yadav */ data class GradleArtifact(val group: String, val name: String, val version: String) : - java.io.Serializable + java.io.Serializable \ No newline at end of file diff --git a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/GradleTask.kt b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/GradleTask.kt similarity index 71% rename from subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/GradleTask.kt rename to subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/GradleTask.kt index d81fb5a0e..e94460196 100644 --- a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/GradleTask.kt +++ b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/GradleTask.kt @@ -1,34 +1,40 @@ -/* - * 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.model - -import java.io.Serializable - -/** - * Default implementation for [GradleTask]. - * - * @author Akash Yadav - */ -open class GradleTask( - val name: String, - val description: String?, - val group: String?, - val path: String, - displayName: String?, - isPublic: Boolean?, - val projectPath: String? -) : IdeLaunchable(displayName, isPublic), Serializable +/* + * 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.models + +import java.io.Serializable + +/** + * A Gradle task. + * + * @property name The name of the task. + * @property description The task description. + * @property group The task group. + * @property path The task path (such as `:app:build`). + * @property projectPath The path of the project in which this task is included. + * @author Akash Yadav + */ +open class GradleTask( + val name: String, + val description: String?, + val group: String?, + val path: String, + displayName: String?, + isPublic: Boolean?, + val projectPath: String? +) : Launchable(displayName, isPublic ?: false), Serializable \ No newline at end of file diff --git a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/JavaContentRoot.kt b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/JavaContentRoot.kt similarity index 92% rename from subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/JavaContentRoot.kt rename to subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/JavaContentRoot.kt index 717b90725..eefeba3cb 100644 --- a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/JavaContentRoot.kt +++ b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/JavaContentRoot.kt @@ -15,7 +15,7 @@ * along with AndroidIDE. If not, see . */ -package com.itsaky.androidide.tooling.api.model +package com.itsaky.androidide.tooling.api.models import java.io.Serializable diff --git a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/JavaModuleCompilerSettings.kt b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/JavaModuleCompilerSettings.kt similarity index 96% rename from subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/JavaModuleCompilerSettings.kt rename to subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/JavaModuleCompilerSettings.kt index dd9f92e90..99c035fa9 100644 --- a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/JavaModuleCompilerSettings.kt +++ b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/JavaModuleCompilerSettings.kt @@ -15,7 +15,7 @@ * along with AndroidIDE. If not, see . */ -package com.itsaky.androidide.tooling.api.model +package com.itsaky.androidide.tooling.api.models import com.itsaky.androidide.builder.model.IJavaCompilerSettings @@ -37,4 +37,4 @@ class JavaModuleCompilerSettings( // Using the 'javax' reference will result a 'ClassNotFoundException' while deserializing the // JSONRpc data received from the Tooling API server internal constructor() : this("RELEASE_11", "RELEASE_11") -} +} \ No newline at end of file diff --git a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/JavaModuleDependency.kt b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/JavaModuleDependency.kt similarity index 92% rename from subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/JavaModuleDependency.kt rename to subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/JavaModuleDependency.kt index ed81a54b1..ad578cee1 100644 --- a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/JavaModuleDependency.kt +++ b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/JavaModuleDependency.kt @@ -15,7 +15,7 @@ * along with AndroidIDE. If not, see . */ -package com.itsaky.androidide.tooling.api.model +package com.itsaky.androidide.tooling.api.models import java.io.File import java.io.Serializable diff --git a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/JavaModuleExternalDependency.kt b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/JavaModuleExternalDependency.kt similarity index 93% rename from subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/JavaModuleExternalDependency.kt rename to subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/JavaModuleExternalDependency.kt index 6c27d250b..40d2ee6cb 100644 --- a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/JavaModuleExternalDependency.kt +++ b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/JavaModuleExternalDependency.kt @@ -15,7 +15,7 @@ * along with AndroidIDE. If not, see . */ -package com.itsaky.androidide.tooling.api.model +package com.itsaky.androidide.tooling.api.models import java.io.File import java.io.Serializable diff --git a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/JavaModuleProjectDependency.kt b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/JavaModuleProjectDependency.kt similarity index 93% rename from subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/JavaModuleProjectDependency.kt rename to subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/JavaModuleProjectDependency.kt index 2a2f6f03b..1583af49a 100644 --- a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/JavaModuleProjectDependency.kt +++ b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/JavaModuleProjectDependency.kt @@ -15,7 +15,7 @@ * along with AndroidIDE. If not, see . */ -package com.itsaky.androidide.tooling.api.model +package com.itsaky.androidide.tooling.api.models import java.io.Serializable diff --git a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/JavaSourceDirectory.kt b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/JavaSourceDirectory.kt similarity index 92% rename from subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/JavaSourceDirectory.kt rename to subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/JavaSourceDirectory.kt index 907ef41ea..d2cd92b66 100644 --- a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/JavaSourceDirectory.kt +++ b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/JavaSourceDirectory.kt @@ -15,7 +15,7 @@ * along with AndroidIDE. If not, see . */ -package com.itsaky.androidide.tooling.api.model +package com.itsaky.androidide.tooling.api.models import java.io.File import java.io.Serializable diff --git a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/util/ModelInfoContainer.kt b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/Launchable.kt similarity index 62% rename from subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/util/ModelInfoContainer.kt rename to subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/Launchable.kt index 0c93b1eab..22c4ebd33 100644 --- a/subprojects/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/util/ModelInfoContainer.kt +++ b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/Launchable.kt @@ -1,30 +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.tooling.impl.util - -import com.itsaky.androidide.builder.model.DefaultProjectSyncIssues -import com.itsaky.androidide.tooling.api.model.AndroidModule -import java.io.Serializable - -/** @author Akash Yadav */ -data class ModelInfoContainer( - val project: AndroidModule, - val syncIssues: DefaultProjectSyncIssues, -) : Serializable { - private val serialVersionUID = 1L -} +/* + * 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.models + +import java.io.Serializable + +/** + * Model containing information about anything which can be launched by the users (e.g. Gradle tasks). + * + * @property displayName The display name of the launchable. + * @property isPublic Whether the launchable is publicly accessible. + * @author Akash Yadav + */ +open class Launchable(val displayName: String?, val isPublic: Boolean) : Serializable { + + protected val gsonType: String = javaClass.name + private val serialVersionUID = 1L +} \ No newline at end of file diff --git a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/ProjectMetadata.kt b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/ProjectMetadata.kt new file mode 100644 index 000000000..fa8b414c4 --- /dev/null +++ b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/ProjectMetadata.kt @@ -0,0 +1,39 @@ +/* + * 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.models + +import com.itsaky.androidide.tooling.api.ProjectType +import java.io.File + +/** + * Metadata about a project. + * + * @property description The project description. + * @property buildScript The build script file (`build.gradle[.kts]`). + * @property type The type of the project. See constants in [ProjectType] for more details. + * @author Akash Yadav + */ +open class ProjectMetadata( + name: String?, + path: String, + projectDir: File, + buildDir: File, + val description: String?, + val buildScript: File, + val type: ProjectType +) : BasicProjectMetadata(name, path, projectDir, buildDir) \ No newline at end of file diff --git a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/messages/VariantDataRequest.kt b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/params/StringParameter.kt similarity index 77% rename from subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/messages/VariantDataRequest.kt rename to subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/params/StringParameter.kt index 26ca64dd1..3d22a2271 100644 --- a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/messages/VariantDataRequest.kt +++ b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/params/StringParameter.kt @@ -1,21 +1,25 @@ -/* - * 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 - -/** @author Akash Yadav */ -data class VariantDataRequest(val variantName: String, val projectPath: String) +/* + * 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.models.params + +/** + * Use for requests/notification which accept one string parameter. + * + * @author Akash Yadav + */ +data class StringParameter(val value: String) diff --git a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/result/SelectProjectResult.kt b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/result/SelectProjectResult.kt new file mode 100644 index 000000000..adb550b54 --- /dev/null +++ b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/models/result/SelectProjectResult.kt @@ -0,0 +1,27 @@ +/* + * 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.models.result + +/** + * Result sent to the client when selecting a project in [IProjectQueries][com.itsaky.androidide.tooling.api.IProjectQueries]. + * + * @param isSuccessful Whether the project selection was successful. If the value is `false`, it is + * because the project with the provided path was not found. + * @author Akash Yadav + */ +data class SelectProjectResult(val isSuccessful: Boolean) \ No newline at end of file diff --git a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/util/AndroidModulePropertyCopier.kt b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/util/AndroidModulePropertyCopier.kt similarity index 62% rename from subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/util/AndroidModulePropertyCopier.kt rename to subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/util/AndroidModulePropertyCopier.kt index 1c6f7b7df..ee115d759 100644 --- a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/util/AndroidModulePropertyCopier.kt +++ b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/util/AndroidModulePropertyCopier.kt @@ -1,481 +1,307 @@ -/* - * 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.model.util - -import com.android.builder.model.v2.CustomSourceDirectory -import com.android.builder.model.v2.ModelSyncFile -import com.android.builder.model.v2.ide.AndroidArtifact -import com.android.builder.model.v2.ide.AndroidGradlePluginProjectFlags -import com.android.builder.model.v2.ide.AndroidGradlePluginProjectFlags.BooleanFlag -import com.android.builder.model.v2.ide.AndroidLibraryData -import com.android.builder.model.v2.ide.ApiVersion -import com.android.builder.model.v2.ide.ArtifactDependencies -import com.android.builder.model.v2.ide.BundleInfo -import com.android.builder.model.v2.ide.GraphItem -import com.android.builder.model.v2.ide.JavaArtifact -import com.android.builder.model.v2.ide.JavaCompileOptions -import com.android.builder.model.v2.ide.Library -import com.android.builder.model.v2.ide.LibraryInfo -import com.android.builder.model.v2.ide.ProjectInfo -import com.android.builder.model.v2.ide.SourceProvider -import com.android.builder.model.v2.ide.SourceSetContainer -import com.android.builder.model.v2.ide.SyncIssue -import com.android.builder.model.v2.ide.TestInfo -import com.android.builder.model.v2.ide.TestedTargetVariant -import com.android.builder.model.v2.ide.UnresolvedDependency -import com.android.builder.model.v2.ide.Variant -import com.android.builder.model.v2.ide.ViewBindingOptions -import com.android.builder.model.v2.models.ProjectSyncIssues -import com.android.builder.model.v2.models.VariantDependencies -import com.itsaky.androidide.builder.model.DefaultAndroidArtifact -import com.itsaky.androidide.builder.model.DefaultAndroidGradlePluginProjectFlags -import com.itsaky.androidide.builder.model.DefaultAndroidLibraryData -import com.itsaky.androidide.builder.model.DefaultApiVersion -import com.itsaky.androidide.builder.model.DefaultArtifactDependencies -import com.itsaky.androidide.builder.model.DefaultBundleInfo -import com.itsaky.androidide.builder.model.DefaultCustomSourceDirectory -import com.itsaky.androidide.builder.model.DefaultGraphItem -import com.itsaky.androidide.builder.model.DefaultJavaArtifact -import com.itsaky.androidide.builder.model.DefaultJavaCompileOptions -import com.itsaky.androidide.builder.model.DefaultLibrary -import com.itsaky.androidide.builder.model.DefaultLibraryInfo -import com.itsaky.androidide.builder.model.DefaultModelSyncFile -import com.itsaky.androidide.builder.model.DefaultProjectInfo -import com.itsaky.androidide.builder.model.DefaultProjectSyncIssues -import com.itsaky.androidide.builder.model.DefaultSourceProvider -import com.itsaky.androidide.builder.model.DefaultSourceSetContainer -import com.itsaky.androidide.builder.model.DefaultSyncIssue -import com.itsaky.androidide.builder.model.DefaultTestInfo -import com.itsaky.androidide.builder.model.DefaultTestedTargetVariant -import com.itsaky.androidide.builder.model.DefaultUnresolvedDependency -import com.itsaky.androidide.builder.model.DefaultVariant -import com.itsaky.androidide.builder.model.DefaultVariantDependencies -import com.itsaky.androidide.builder.model.DefaultViewBindingOptions -import com.itsaky.androidide.tooling.api.messages.result.SimpleArtifact -import com.itsaky.androidide.tooling.api.messages.result.SimpleVariantData -import com.itsaky.androidide.tooling.api.model.AndroidModule -import com.itsaky.androidide.utils.ILogger - -/** - * As the data is sent over streams, and the instances of properties specified in [AndroidModule] - * are just proxy classes, we need to make copy of those properties so that they can ben serialized - * by Gson. - * - * This class handles the work of making copy of those properties. - * - * @author Akash Yadav - */ -object AndroidModulePropertyCopier { - - private val log = ILogger.newInstance(javaClass.simpleName) - - fun copy(module: AndroidModule): AndroidModule { - @Suppress("DEPRECATION") - return AndroidModule( - module.name, - module.projectPath, - module.description, - module.projectDir, - module.buildDir, - module.buildScript, - module.parent, - module.tasks, - module.projectType, - module.dynamicFeatures, - copy(module.flags), - copy(module.javaCompileOptions), - module.resourcePrefix, - copy(module.variants), - copy(module.viewBindingOptions), - module.lintChecksJars, - copy(module.modelSyncFiles) - ) - } - - fun copy(viewBindingOptions: ViewBindingOptions?): DefaultViewBindingOptions? { - return when (viewBindingOptions) { - null -> null - else -> DefaultViewBindingOptions().apply { isEnabled = viewBindingOptions.isEnabled } - } - } - - @JvmName("copyVariants") - fun copy(variants: Collection): Collection { - val new = mutableListOf() - for (variant in variants) { - new.add(copy(variant)) - } - return new - } - - fun copy(variant: Variant): DefaultVariant = - DefaultVariant().apply { - androidTestArtifact = copy(variant.androidTestArtifact) - displayName = variant.displayName - isInstantAppCompatible = variant.isInstantAppCompatible - mainArtifact = copy(variant.mainArtifact)!! - name = variant.name - testFixturesArtifact = copy(variant.testFixturesArtifact) - testedTargetVariant = copy(variant.testedTargetVariant) - unitTestArtifact = copy(variant.unitTestArtifact) - desugaredMethods = emptyList() - } - fun copy(artifact: JavaArtifact?): DefaultJavaArtifact? { - if (artifact == null) { - return null - } - - return DefaultJavaArtifact().apply { - modelSyncFiles = emptyList() - assembleTaskName = artifact.assembleTaskName - classesFolders = artifact.classesFolders - compileTaskName = artifact.compileTaskName - generatedSourceFolders = artifact.generatedSourceFolders - ideSetupTaskNames = artifact.ideSetupTaskNames - mockablePlatformJar = artifact.mockablePlatformJar - runtimeResourceFolder = artifact.runtimeResourceFolder - } - } - - fun copy(variant: TestedTargetVariant?): DefaultTestedTargetVariant? { - if (variant == null) { - return null - } - - return DefaultTestedTargetVariant().apply { - targetProjectPath = variant.targetProjectPath - targetVariant = variant.targetVariant - } - } - - fun copy(artifact: AndroidArtifact?): DefaultAndroidArtifact? { - if (artifact == null) { - return null - } - - return DefaultAndroidArtifact().apply { - applicationId = artifact.applicationId - resGenTaskName = artifact.resGenTaskName - abiFilters = artifact.abiFilters - assembleTaskOutputListingFile = artifact.assembleTaskOutputListingFile - bundleInfo = copy(artifact.bundleInfo) - codeShrinker = artifact.codeShrinker - generatedResourceFolders = artifact.generatedResourceFolders - isSigned = artifact.isSigned - maxSdkVersion = artifact.maxSdkVersion - minSdkVersion = copy(artifact.minSdkVersion)!! - signingConfigName = artifact.signingConfigName - sourceGenTaskName = artifact.sourceGenTaskName - testInfo = copy(artifact.testInfo) - assembleTaskName = artifact.assembleTaskName - classesFolders = artifact.classesFolders - compileTaskName = artifact.compileTaskName - generatedSourceFolders = artifact.generatedSourceFolders - ideSetupTaskNames = artifact.ideSetupTaskNames - targetSdkVersionOverride = copy(artifact.targetSdkVersionOverride) - modelSyncFiles = copy(artifact.modelSyncFiles) - } - } - - @JvmName("copyModelSyncFiles") - fun copy(modelSyncFiles: Collection): List { - val new = mutableListOf() - for (file in modelSyncFiles) { - new.add(copy(file)) - } - - return new - } - - fun copy(file: ModelSyncFile): DefaultModelSyncFile = - DefaultModelSyncFile().apply { - modelSyncType = file.modelSyncType - syncFile = file.syncFile - taskName = file.taskName - } - - fun copy(info: TestInfo?): DefaultTestInfo? { - if (info == null) { - return null - } - - return DefaultTestInfo().apply { - additionalRuntimeApks = info.additionalRuntimeApks - animationsDisabled = info.animationsDisabled - execution = info.execution - instrumentedTestTaskName = info.instrumentedTestTaskName - } - } - - fun copy(version: ApiVersion?): DefaultApiVersion? = - if (version == null) null - else - DefaultApiVersion().apply { - apiLevel = version.apiLevel - codename = version.codename - } - - fun copy(bundleInfo: BundleInfo?): DefaultBundleInfo? { - if (bundleInfo == null) { - return null - } - - return DefaultBundleInfo().apply { - apkFromBundleTaskName = bundleInfo.apkFromBundleTaskName - apkFromBundleTaskOutputListingFile = bundleInfo.apkFromBundleTaskOutputListingFile - bundleTaskName = bundleInfo.bundleTaskName - bundleTaskOutputListingFile = bundleInfo.bundleTaskOutputListingFile - } - } - - fun copy(javaCompileOptions: JavaCompileOptions): DefaultJavaCompileOptions { - return DefaultJavaCompileOptions().apply { - encoding = javaCompileOptions.encoding - isCoreLibraryDesugaringEnabled = javaCompileOptions.isCoreLibraryDesugaringEnabled - sourceCompatibility = javaCompileOptions.sourceCompatibility - targetCompatibility = javaCompileOptions.targetCompatibility - } - } - - fun copy( - @Suppress("UNUSED_PARAMETER") flags: AndroidGradlePluginProjectFlags - ): DefaultAndroidGradlePluginProjectFlags { - val flagMap: MutableMap = mutableMapOf() - // log.debug("Flags:", flags) - // - // flagMap[APPLICATION_R_CLASS_CONSTANT_IDS] = - // flags.getFlagValue(APPLICATION_R_CLASS_CONSTANT_IDS.name) - // flagMap[JETPACK_COMPOSE] = flags.getFlagValue(JETPACK_COMPOSE.name) - // flagMap[ML_MODEL_BINDING] = flags.getFlagValue(ML_MODEL_BINDING.name) - // flagMap[TEST_R_CLASS_CONSTANT_IDS] = - // flags.getFlagValue(TEST_R_CLASS_CONSTANT_IDS.name) - // flagMap[TRANSITIVE_R_CLASS] = flags.getFlagValue(TRANSITIVE_R_CLASS.name) - // flagMap[UNIFIED_TEST_PLATFORM] = flags.getFlagValue(UNIFIED_TEST_PLATFORM.name) - // - // log.debug("Flag map:", flagMap) - return DefaultAndroidGradlePluginProjectFlags(flagMap) - } - - fun copy(containers: Collection): Collection { - val new = mutableListOf() - - for (container in containers) { - new.add(copy(container)) - } - - return new - } - - fun copy(container: SourceSetContainer): DefaultSourceSetContainer { - return DefaultSourceSetContainer().apply { - androidTestSourceProvider = copy(container.androidTestSourceProvider) - sourceProvider = copy(container.sourceProvider)!! - testFixturesSourceProvider = copy(container.testFixturesSourceProvider) - unitTestSourceProvider = copy(container.unitTestSourceProvider) - } - } - - fun copy(provider: SourceProvider?): DefaultSourceProvider? { - if (provider == null) { - return null - } - - return DefaultSourceProvider().apply { - aidlDirectories = provider.aidlDirectories - assetsDirectories = provider.assetsDirectories - customDirectories = copy(provider.customDirectories) - javaDirectories = provider.javaDirectories - jniLibsDirectories = provider.jniLibsDirectories - kotlinDirectories = provider.kotlinDirectories - manifestFile = provider.manifestFile - mlModelsDirectories = provider.mlModelsDirectories - name = provider.name - renderscriptDirectories = provider.renderscriptDirectories - resDirectories = provider.resDirectories - resourcesDirectories = provider.resourcesDirectories - shadersDirectories = provider.shadersDirectories - } - } - - @JvmName("copyCustomSourceDirectories") - private fun copy( - directories: Collection? - ): Collection? { - return directories?.map { copy(it) } - } - - private fun copy(it: CustomSourceDirectory): DefaultCustomSourceDirectory { - return DefaultCustomSourceDirectory(it.directory, it.sourceTypeName) - } - - fun copy(variantDependencies: VariantDependencies): DefaultVariantDependencies { - return DefaultVariantDependencies().apply { - this.name = variantDependencies.name - this.libraries = copy(variantDependencies.libraries) - this.mainArtifact = copy(variantDependencies.mainArtifact)!! - this.androidTestArtifact = copy(variantDependencies.androidTestArtifact) - this.testFixturesArtifact = copy(variantDependencies.testFixturesArtifact) - this.unitTestArtifact = copy(variantDependencies.unitTestArtifact) - } - } - - private fun copy(libraries: Map): Map { - val new = mutableMapOf() - for ((key, value) in libraries) { - new[key] = copy(value) - } - - return new - } - - fun copy(value: Library): DefaultLibrary { - return DefaultLibrary().apply { - this.androidLibraryData = copy(value.androidLibraryData) - this.artifact = value.artifact - this.key = value.key - this.libraryInfo = copy(value.libraryInfo) - this.lintJar = value.lintJar - this.projectInfo = copy(value.projectInfo) - this.type = value.type - } - } - - private fun copy(info: ProjectInfo?): DefaultProjectInfo? { - return if (info == null) null - else - DefaultProjectInfo( - info.attributes, - info.buildType, - info.capabilities, - info.isTestFixtures, - info.productFlavors, - info.buildId, - info.projectPath - ) - } - - private fun copy(info: LibraryInfo?): DefaultLibraryInfo? { - return if (info == null) null - else - DefaultLibraryInfo( - info.attributes, - info.buildType, - info.capabilities, - info.isTestFixtures, - info.productFlavors, - info.group, - info.name, - info.version - ) - } - - private fun copy(data: AndroidLibraryData?): DefaultAndroidLibraryData? { - return if (data == null) null - else - DefaultAndroidLibraryData( - data.aidlFolder, - data.assetsFolder, - data.compileJarFiles, - data.externalAnnotations, - data.jniFolder, - data.manifest, - data.proguardRules, - data.publicResources, - data.renderscriptFolder, - data.resFolder, - data.resStaticLibrary, - data.runtimeJarFiles, - data.symbolFile - ) - } - - fun copy(artifact: ArtifactDependencies?): DefaultArtifactDependencies? = - DefaultArtifactDependencies().apply { - if (artifact == null) { - return null - } - - this.compileDependencies = copy(artifact.compileDependencies) - this.runtimeDependencies = copy(artifact.runtimeDependencies) - this.unresolvedDependencies = copy(artifact.unresolvedDependencies) - } - - private fun copy(dependencies: List): List { - return dependencies.map { copy(it) } - } - - private fun copy(it: UnresolvedDependency): DefaultUnresolvedDependency { - return DefaultUnresolvedDependency(it.cause, it.name) - } - - @JvmName("copyGraphItems") - fun copy(graphs: List): List { - return graphs.map { copy(it) } - } - - fun copy(graph: GraphItem): DefaultGraphItem = - DefaultGraphItem().apply { - this.key = graph.key - this.dependencies = copy(graph.dependencies) - this.requestedCoordinates = graph.requestedCoordinates - } - - fun copy(issues: ProjectSyncIssues): DefaultProjectSyncIssues = - DefaultProjectSyncIssues(copy(issues.syncIssues)) - - @JvmName("copySyncIssue") - fun copy(syncIssues: Collection): Collection { - return syncIssues.map { copy(it) } - } - - fun copy(issue: SyncIssue) = - DefaultSyncIssue(issue.data, issue.message, issue.multiLineMessage, issue.severity, issue.type) - - fun asSimpleVariants(variants: MutableCollection): MutableList { - return mutableListOf().apply { - for (variant in variants) { - add( - SimpleVariantData( - variant.name, - asSimpleArtifact("main", variant.mainArtifact), - mutableMapOf() - ) - ) - } - } - } - - @Suppress("SameParameterValue") // We might need details about other artifacts in future - private fun asSimpleArtifact(name: String, artifact: AndroidArtifact): SimpleArtifact { - return SimpleArtifact( - name = name, - assembleTaskName = artifact.assembleTaskName, - assembleTaskOutputListingFile = artifact.assembleTaskOutputListingFile, - classJars = artifact.classesFolders.filter { it.name.endsWith(".jar") }, - compileTaskName = artifact.compileTaskName, - generatedResourceFolders = artifact.generatedResourceFolders, - generatedSourceFolders = artifact.generatedSourceFolders, - maxSdkVersion = artifact.maxSdkVersion, - minSdkVersion = artifact.minSdkVersion.apiLevel, - resGenTaskName = artifact.resGenTaskName, - signingConfigName = artifact.signingConfigName, - sourceGenTaskName = artifact.sourceGenTaskName, - targetSdkVersionOverride = artifact.targetSdkVersionOverride?.apiLevel ?: -1 - ) - } -} +/* + * 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.util + +import com.android.builder.model.v2.CustomSourceDirectory +import com.android.builder.model.v2.ModelSyncFile +import com.android.builder.model.v2.ide.AndroidArtifact +import com.android.builder.model.v2.ide.AndroidGradlePluginProjectFlags +import com.android.builder.model.v2.ide.AndroidGradlePluginProjectFlags.BooleanFlag +import com.android.builder.model.v2.ide.AndroidLibraryData +import com.android.builder.model.v2.ide.ApiVersion +import com.android.builder.model.v2.ide.ArtifactDependencies +import com.android.builder.model.v2.ide.BundleInfo +import com.android.builder.model.v2.ide.GraphItem +import com.android.builder.model.v2.ide.JavaArtifact +import com.android.builder.model.v2.ide.JavaCompileOptions +import com.android.builder.model.v2.ide.Library +import com.android.builder.model.v2.ide.LibraryInfo +import com.android.builder.model.v2.ide.ProjectInfo +import com.android.builder.model.v2.ide.SourceProvider +import com.android.builder.model.v2.ide.SourceSetContainer +import com.android.builder.model.v2.ide.SyncIssue +import com.android.builder.model.v2.ide.TestInfo +import com.android.builder.model.v2.ide.TestedTargetVariant +import com.android.builder.model.v2.ide.UnresolvedDependency +import com.android.builder.model.v2.ide.Variant +import com.android.builder.model.v2.ide.ViewBindingOptions +import com.android.builder.model.v2.models.ProjectSyncIssues +import com.android.builder.model.v2.models.VariantDependencies +import com.itsaky.androidide.builder.model.DefaultAndroidArtifact +import com.itsaky.androidide.builder.model.DefaultAndroidGradlePluginProjectFlags +import com.itsaky.androidide.builder.model.DefaultAndroidLibraryData +import com.itsaky.androidide.builder.model.DefaultApiVersion +import com.itsaky.androidide.builder.model.DefaultArtifactDependencies +import com.itsaky.androidide.builder.model.DefaultBundleInfo +import com.itsaky.androidide.builder.model.DefaultCustomSourceDirectory +import com.itsaky.androidide.builder.model.DefaultGraphItem +import com.itsaky.androidide.builder.model.DefaultJavaArtifact +import com.itsaky.androidide.builder.model.DefaultJavaCompileOptions +import com.itsaky.androidide.builder.model.DefaultLibrary +import com.itsaky.androidide.builder.model.DefaultLibraryInfo +import com.itsaky.androidide.builder.model.DefaultModelSyncFile +import com.itsaky.androidide.builder.model.DefaultProjectInfo +import com.itsaky.androidide.builder.model.DefaultProjectSyncIssues +import com.itsaky.androidide.builder.model.DefaultSourceProvider +import com.itsaky.androidide.builder.model.DefaultSourceSetContainer +import com.itsaky.androidide.builder.model.DefaultSyncIssue +import com.itsaky.androidide.builder.model.DefaultTestInfo +import com.itsaky.androidide.builder.model.DefaultTestedTargetVariant +import com.itsaky.androidide.builder.model.DefaultUnresolvedDependency +import com.itsaky.androidide.builder.model.DefaultVariant +import com.itsaky.androidide.builder.model.DefaultVariantDependencies +import com.itsaky.androidide.builder.model.DefaultViewBindingOptions +import com.itsaky.androidide.utils.ILogger + +/** + * As the data is sent over streams, and the instances of properties specified in [AndroidModule] + * are just proxy classes, we need to make copy of those properties so that they can ben serialized + * by Gson. + * + * This class handles the work of making copy of those properties. + * + * @author Akash Yadav + */ +object AndroidModulePropertyCopier { + + private val log = ILogger.newInstance(javaClass.simpleName) + + fun copy(viewBindingOptions: ViewBindingOptions?): DefaultViewBindingOptions? { + return when (viewBindingOptions) { + null -> null + else -> DefaultViewBindingOptions().apply { isEnabled = viewBindingOptions.isEnabled } + } + } + + @JvmName("copyModelSyncFiles") + fun copy(modelSyncFiles: Collection): List { + val new = mutableListOf() + for (file in modelSyncFiles) { + new.add(copy(file)) + } + + return new + } + + fun copy(file: ModelSyncFile): DefaultModelSyncFile = + DefaultModelSyncFile().apply { + modelSyncType = file.modelSyncType + syncFile = file.syncFile + taskName = file.taskName + } + + fun copy(version: ApiVersion?): DefaultApiVersion? = + if (version == null) null + else + DefaultApiVersion().apply { + apiLevel = version.apiLevel + codename = version.codename + } + + fun copy(javaCompileOptions: JavaCompileOptions): DefaultJavaCompileOptions { + return DefaultJavaCompileOptions().apply { + encoding = javaCompileOptions.encoding + isCoreLibraryDesugaringEnabled = javaCompileOptions.isCoreLibraryDesugaringEnabled + sourceCompatibility = javaCompileOptions.sourceCompatibility + targetCompatibility = javaCompileOptions.targetCompatibility + } + } + + fun copy( + @Suppress("UNUSED_PARAMETER") flags: AndroidGradlePluginProjectFlags + ): DefaultAndroidGradlePluginProjectFlags { + val flagMap: MutableMap = mutableMapOf() + // log.debug("Flags:", flags) + // + // flagMap[APPLICATION_R_CLASS_CONSTANT_IDS] = + // flags.getFlagValue(APPLICATION_R_CLASS_CONSTANT_IDS.name) + // flagMap[JETPACK_COMPOSE] = flags.getFlagValue(JETPACK_COMPOSE.name) + // flagMap[ML_MODEL_BINDING] = flags.getFlagValue(ML_MODEL_BINDING.name) + // flagMap[TEST_R_CLASS_CONSTANT_IDS] = + // flags.getFlagValue(TEST_R_CLASS_CONSTANT_IDS.name) + // flagMap[TRANSITIVE_R_CLASS] = flags.getFlagValue(TRANSITIVE_R_CLASS.name) + // flagMap[UNIFIED_TEST_PLATFORM] = flags.getFlagValue(UNIFIED_TEST_PLATFORM.name) + // + // log.debug("Flag map:", flagMap) + return DefaultAndroidGradlePluginProjectFlags(flagMap) + } + + fun copy(containers: Collection): Collection { + val new = mutableListOf() + + for (container in containers) { + new.add(copy(container)) + } + + return new + } + + fun copy(container: SourceSetContainer): DefaultSourceSetContainer { + return DefaultSourceSetContainer().apply { + androidTestSourceProvider = copy(container.androidTestSourceProvider) + sourceProvider = copy(container.sourceProvider)!! + testFixturesSourceProvider = copy(container.testFixturesSourceProvider) + unitTestSourceProvider = copy(container.unitTestSourceProvider) + } + } + + fun copy(provider: SourceProvider?): DefaultSourceProvider? { + if (provider == null) { + return null + } + + return DefaultSourceProvider().apply { + aidlDirectories = provider.aidlDirectories + assetsDirectories = provider.assetsDirectories + customDirectories = copy(provider.customDirectories) + javaDirectories = provider.javaDirectories + jniLibsDirectories = provider.jniLibsDirectories + kotlinDirectories = provider.kotlinDirectories + manifestFile = provider.manifestFile + mlModelsDirectories = provider.mlModelsDirectories + name = provider.name + renderscriptDirectories = provider.renderscriptDirectories + resDirectories = provider.resDirectories + resourcesDirectories = provider.resourcesDirectories + shadersDirectories = provider.shadersDirectories + } + } + + @JvmName("copyCustomSourceDirectories") + private fun copy( + directories: Collection? + ): Collection? { + return directories?.map { copy(it) } + } + + private fun copy(it: CustomSourceDirectory): DefaultCustomSourceDirectory { + return DefaultCustomSourceDirectory(it.directory, it.sourceTypeName) + } + + private fun copy(libraries: Map): Map { + val new = mutableMapOf() + for ((key, value) in libraries) { + new[key] = copy(value) + } + + return new + } + + fun copy(value: Library): DefaultLibrary { + return DefaultLibrary().apply { + this.androidLibraryData = copy(value.androidLibraryData) + this.artifact = value.artifact + this.key = value.key + this.libraryInfo = copy(value.libraryInfo) + this.lintJar = value.lintJar + this.projectInfo = copy(value.projectInfo) + this.type = value.type + } + } + + private fun copy(info: ProjectInfo?): DefaultProjectInfo? { + return if (info == null) null + else + DefaultProjectInfo( + info.attributes, + info.buildType, + info.capabilities, + info.isTestFixtures, + info.productFlavors, + info.buildId, + info.projectPath + ) + } + + private fun copy(info: LibraryInfo?): DefaultLibraryInfo? { + return if (info == null) null + else + DefaultLibraryInfo( + info.attributes, + info.buildType, + info.capabilities, + info.isTestFixtures, + info.productFlavors, + info.group, + info.name, + info.version + ) + } + + private fun copy(data: AndroidLibraryData?): DefaultAndroidLibraryData? { + return if (data == null) null + else + DefaultAndroidLibraryData( + data.aidlFolder, + data.assetsFolder, + data.compileJarFiles, + data.externalAnnotations, + data.jniFolder, + data.manifest, + data.proguardRules, + data.publicResources, + data.renderscriptFolder, + data.resFolder, + data.resStaticLibrary, + data.runtimeJarFiles, + data.symbolFile + ) + } + + fun copy(artifact: ArtifactDependencies?): DefaultArtifactDependencies? = + DefaultArtifactDependencies().apply { + if (artifact == null) { + return null + } + + this.compileDependencies = copy(artifact.compileDependencies) + this.runtimeDependencies = copy(artifact.runtimeDependencies) + this.unresolvedDependencies = copy(artifact.unresolvedDependencies) + } + + private fun copy(dependencies: List): List { + return dependencies.map { copy(it) } + } + + private fun copy(it: UnresolvedDependency): DefaultUnresolvedDependency { + return DefaultUnresolvedDependency(it.cause, it.name) + } + + @JvmName("copyGraphItems") + fun copy(graphs: List): List { + return graphs.map { copy(it) } + } + + fun copy(graph: GraphItem): DefaultGraphItem = + DefaultGraphItem().apply { + this.key = graph.key + this.dependencies = copy(graph.dependencies) + this.requestedCoordinates = graph.requestedCoordinates + } + + fun copy(issues: ProjectSyncIssues): DefaultProjectSyncIssues = + DefaultProjectSyncIssues(copy(issues.syncIssues)) + + @JvmName("copySyncIssue") + fun copy(syncIssues: Collection): Collection { + return syncIssues.map { copy(it) } + } + + fun copy(issue: SyncIssue) = + DefaultSyncIssue(issue.data, issue.message, issue.multiLineMessage, issue.severity, issue.type) +} \ No newline at end of file diff --git a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/util/utils.kt b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/util/utils.kt similarity index 88% rename from subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/util/utils.kt rename to subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/util/utils.kt index ba535fb1e..59011fd75 100644 --- a/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/util/utils.kt +++ b/subprojects/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/util/utils.kt @@ -15,15 +15,15 @@ * along with AndroidIDE. If not, see . */ -package com.itsaky.androidide.tooling.api.model.util +package com.itsaky.androidide.tooling.api.util import com.android.builder.model.v2.ide.LibraryType.ANDROID_LIBRARY import com.itsaky.androidide.builder.model.DefaultLibrary import com.itsaky.androidide.builder.model.UNKNOWN_PACKAGE -import com.itsaky.androidide.tooling.api.model.AndroidModule -import java.io.File +import com.itsaky.androidide.tooling.api.IAndroidProject import org.eclipse.lemminx.dom.DOMParser import org.eclipse.lemminx.uriresolver.URIResolverExtensionManager +import java.io.File /** * Find the package name for this library. If this library is not an [ANDROID_LIBRARY] or if error @@ -50,16 +50,16 @@ fun DefaultLibrary.findPackageName(): String { return this.packageName } -/** Extractes package name from the given `AndroidManifest.xml` file. */ +/** Extracts package name from the given `AndroidManifest.xml` file. */ fun extractPackageName(manifestFile: File): String? { val content = manifestFile.readText() val document = DOMParser.getInstance() - .parse(content, AndroidModule.ANDROID_NAMESPACE, URIResolverExtensionManager()) + .parse(content, IAndroidProject.ANDROID_NAMESPACE, URIResolverExtensionManager()) val manifest = document.children.first { it.nodeName == "manifest" } ?: return null val packageAttr = manifest.attributes.getNamedItem("package") if (packageAttr != null) { return packageAttr.nodeValue } return null -} +} \ No newline at end of file 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 0acdd46b5..a91a9ad03 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 @@ -93,6 +93,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") } diff --git a/subprojects/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/IToolingApiServer.kt b/subprojects/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/IToolingApiServer.kt index 9b123d47c..76b13dc5b 100644 --- a/subprojects/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/IToolingApiServer.kt +++ b/subprojects/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/IToolingApiServer.kt @@ -22,7 +22,6 @@ import com.itsaky.androidide.tooling.api.messages.TaskExecutionMessage import com.itsaky.androidide.tooling.api.messages.result.BuildCancellationRequestResult import com.itsaky.androidide.tooling.api.messages.result.InitializeResult import com.itsaky.androidide.tooling.api.messages.result.TaskExecutionResult -import com.itsaky.androidide.tooling.api.model.IdeGradleProject import org.eclipse.lsp4j.jsonrpc.services.JsonNotification import org.eclipse.lsp4j.jsonrpc.services.JsonRequest import org.eclipse.lsp4j.jsonrpc.services.JsonSegment @@ -43,7 +42,7 @@ interface IToolingApiServer { @JsonRequest fun isServerInitialized(): CompletableFuture /** Get the root project. */ - @JsonRequest fun getRootProject(): CompletableFuture + @JsonRequest fun getRootProject(): CompletableFuture /** Execute the tasks specified in the message. */ @JsonRequest diff --git a/subprojects/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/InitializeProjectMessage.kt b/subprojects/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/InitializeProjectMessage.kt index 79cc93b86..6ff696cdf 100644 --- a/subprojects/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/InitializeProjectMessage.kt +++ b/subprojects/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/InitializeProjectMessage.kt @@ -22,4 +22,4 @@ package com.itsaky.androidide.tooling.api.messages * * @author Akash Yadav */ -data class InitializeProjectMessage(val directory: String, val gradleInstallation: String = "") +data class InitializeProjectMessage(val directory: String, val androidVariant: String, val gradleInstallation: String = "") diff --git a/subprojects/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/util/ToolingApiLauncher.java b/subprojects/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/util/ToolingApiLauncher.java deleted file mode 100644 index 1e93659d7..000000000 --- a/subprojects/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/util/ToolingApiLauncher.java +++ /dev/null @@ -1,240 +0,0 @@ -/* - * 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.util; - -import com.google.gson.GsonBuilder; -import com.itsaky.androidide.builder.model.DefaultJavaCompileOptions; -import com.itsaky.androidide.builder.model.IJavaCompilerSettings; -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.model.AndroidModule; -import com.itsaky.androidide.tooling.api.model.GradleTask; -import com.itsaky.androidide.tooling.api.model.IdeGradleProject; -import com.itsaky.androidide.tooling.api.model.IdeLaunchable; -import com.itsaky.androidide.tooling.api.model.JavaModule; -import com.itsaky.androidide.tooling.api.model.JavaModuleCompilerSettings; -import com.itsaky.androidide.tooling.api.model.JavaModuleDependency; -import com.itsaky.androidide.tooling.api.model.JavaModuleExternalDependency; -import com.itsaky.androidide.tooling.api.model.JavaModuleProjectDependency; -import com.itsaky.androidide.tooling.events.OperationDescriptor; -import com.itsaky.androidide.tooling.events.OperationResult; -import com.itsaky.androidide.tooling.events.ProgressEvent; -import com.itsaky.androidide.tooling.events.StatusEvent; -import com.itsaky.androidide.tooling.events.configuration.ProjectConfigurationFinishEvent; -import com.itsaky.androidide.tooling.events.configuration.ProjectConfigurationOperationDescriptor; -import com.itsaky.androidide.tooling.events.configuration.ProjectConfigurationOperationResult; -import com.itsaky.androidide.tooling.events.configuration.ProjectConfigurationProgressEvent; -import com.itsaky.androidide.tooling.events.configuration.ProjectConfigurationStartEvent; -import com.itsaky.androidide.tooling.events.download.FileDownloadFinishEvent; -import com.itsaky.androidide.tooling.events.download.FileDownloadOperationDescriptor; -import com.itsaky.androidide.tooling.events.download.FileDownloadProgressEvent; -import com.itsaky.androidide.tooling.events.download.FileDownloadResult; -import com.itsaky.androidide.tooling.events.download.FileDownloadStartEvent; -import com.itsaky.androidide.tooling.events.internal.DefaultFinishEvent; -import com.itsaky.androidide.tooling.events.internal.DefaultOperationDescriptor; -import com.itsaky.androidide.tooling.events.internal.DefaultOperationResult; -import com.itsaky.androidide.tooling.events.internal.DefaultProgressEvent; -import com.itsaky.androidide.tooling.events.internal.DefaultStartEvent; -import com.itsaky.androidide.tooling.events.task.TaskExecutionResult; -import com.itsaky.androidide.tooling.events.task.TaskFailureResult; -import com.itsaky.androidide.tooling.events.task.TaskFinishEvent; -import com.itsaky.androidide.tooling.events.task.TaskOperationDescriptor; -import com.itsaky.androidide.tooling.events.task.TaskOperationResult; -import com.itsaky.androidide.tooling.events.task.TaskProgressEvent; -import com.itsaky.androidide.tooling.events.task.TaskSkippedResult; -import com.itsaky.androidide.tooling.events.task.TaskStartEvent; -import com.itsaky.androidide.tooling.events.task.TaskSuccessResult; -import com.itsaky.androidide.tooling.events.test.TestFinishEvent; -import com.itsaky.androidide.tooling.events.test.TestOperationDescriptor; -import com.itsaky.androidide.tooling.events.test.TestOperationResult; -import com.itsaky.androidide.tooling.events.test.TestProgressEvent; -import com.itsaky.androidide.tooling.events.test.TestStartEvent; -import com.itsaky.androidide.tooling.events.transform.TransformFinishEvent; -import com.itsaky.androidide.tooling.events.transform.TransformOperationDescriptor; -import com.itsaky.androidide.tooling.events.transform.TransformOperationResult; -import com.itsaky.androidide.tooling.events.transform.TransformProgressEvent; -import com.itsaky.androidide.tooling.events.transform.TransformStartEvent; -import com.itsaky.androidide.tooling.events.work.WorkItemFinishEvent; -import com.itsaky.androidide.tooling.events.work.WorkItemOperationDescriptor; -import com.itsaky.androidide.tooling.events.work.WorkItemOperationResult; -import com.itsaky.androidide.tooling.events.work.WorkItemProgressEvent; -import com.itsaky.androidide.tooling.events.work.WorkItemStartEvent; - -import org.eclipse.lsp4j.jsonrpc.Launcher; - -import java.io.File; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Arrays; -import java.util.concurrent.Executors; - -/** - * Utility class for launching {@link IToolingApiClient} and {@link IToolingApiServer}. - * - * @author Akash Yadav - */ -public class ToolingApiLauncher { - - public static Launcher createIOLauncher( - Object local, Class remote, InputStream in, OutputStream out) { - return new Launcher.Builder() - .setInput(in) - .setOutput(out) - .setLocalService(local) - .setRemoteInterface(remote) - .configureGson(ToolingApiLauncher::configureGson) - .create(); - } - - public static void configureGson(GsonBuilder builder) { - builder.registerTypeAdapter(File.class, new FileTypeAdapter()); - builder.registerTypeAdapterFactory( - RuntimeTypeAdapterFactory.of(IdeGradleProject.class, "gsonType", true) - .registerSubtype(AndroidModule.class, AndroidModule.class.getName()) - .registerSubtype(JavaModule.class, JavaModule.class.getName()) - .registerSubtype(IdeGradleProject.class, IdeGradleProject.class.getName())); - builder.registerTypeAdapterFactory( - RuntimeTypeAdapterFactory.of(IdeLaunchable.class, "gsonType", true) - .registerSubtype(GradleTask.class, GradleTask.class.getName()) - .registerSubtype(IdeLaunchable.class, IdeLaunchable.class.getName())); - builder.registerTypeAdapterFactory( - RuntimeTypeAdapterFactory.of(JavaModuleDependency.class, "gsonType", true) - .registerSubtype( - JavaModuleExternalDependency.class, JavaModuleExternalDependency.class.getName()) - .registerSubtype( - JavaModuleProjectDependency.class, JavaModuleProjectDependency.class.getName())); - - builder.registerTypeAdapterFactory( - RuntimeTypeAdapterFactory.of(IJavaCompilerSettings.class) - .registerSubtype( - DefaultJavaCompileOptions.class, DefaultJavaCompileOptions.class.getName()) - .registerSubtype( - JavaModuleCompilerSettings.class, JavaModuleCompilerSettings.class.getName())); - - builder.registerTypeAdapterFactory( - RuntimeTypeAdapterFactory.of(ProgressEvent.class, "gsonType", true) - - // Project configuration - .registerSubtype( - ProjectConfigurationProgressEvent.class, - ProjectConfigurationProgressEvent.class.getName()) - .registerSubtype( - ProjectConfigurationStartEvent.class, - ProjectConfigurationStartEvent.class.getName()) - .registerSubtype( - ProjectConfigurationFinishEvent.class, - ProjectConfigurationFinishEvent.class.getName()) - - // File download - .registerSubtype( - FileDownloadProgressEvent.class, FileDownloadProgressEvent.class.getName()) - .registerSubtype(FileDownloadStartEvent.class, FileDownloadStartEvent.class.getName()) - .registerSubtype(FileDownloadFinishEvent.class, FileDownloadFinishEvent.class.getName()) - - // Task execution - .registerSubtype(TaskProgressEvent.class, TaskProgressEvent.class.getName()) - .registerSubtype(TaskStartEvent.class, TaskStartEvent.class.getName()) - .registerSubtype(TaskFinishEvent.class, TaskFinishEvent.class.getName()) - - // Test execution - .registerSubtype(TestProgressEvent.class, TestProgressEvent.class.getName()) - .registerSubtype(TestStartEvent.class, TestStartEvent.class.getName()) - .registerSubtype(TestFinishEvent.class, TestFinishEvent.class.getName()) - - // Transform - .registerSubtype(TransformProgressEvent.class, TransformProgressEvent.class.getName()) - .registerSubtype(TransformStartEvent.class, TransformStartEvent.class.getName()) - .registerSubtype(TransformFinishEvent.class, TransformFinishEvent.class.getName()) - - // Work item - .registerSubtype(WorkItemProgressEvent.class, WorkItemProgressEvent.class.getName()) - .registerSubtype(WorkItemStartEvent.class, WorkItemStartEvent.class.getName()) - .registerSubtype(WorkItemFinishEvent.class, WorkItemFinishEvent.class.getName()) - - // Default implementations - .registerSubtype(DefaultProgressEvent.class, DefaultProgressEvent.class.getName()) - .registerSubtype(DefaultStartEvent.class, DefaultStartEvent.class.getName()) - .registerSubtype(DefaultFinishEvent.class, DefaultFinishEvent.class.getName()) - - // Status event - .registerSubtype(StatusEvent.class, StatusEvent.class.getName())); - - builder.registerTypeAdapterFactory( - RuntimeTypeAdapterFactory.of(OperationDescriptor.class, "gsonType", true) - .registerSubtype( - ProjectConfigurationOperationDescriptor.class, - ProjectConfigurationOperationDescriptor.class.getName()) - .registerSubtype( - FileDownloadOperationDescriptor.class, - FileDownloadOperationDescriptor.class.getName()) - .registerSubtype(TaskOperationDescriptor.class, TaskOperationDescriptor.class.getName()) - .registerSubtype(TestOperationDescriptor.class, TestOperationDescriptor.class.getName()) - .registerSubtype( - TransformOperationDescriptor.class, TransformOperationDescriptor.class.getName()) - .registerSubtype( - WorkItemOperationDescriptor.class, WorkItemOperationDescriptor.class.getName()) - .registerSubtype( - DefaultOperationDescriptor.class, DefaultOperationDescriptor.class.getName())); - - builder.registerTypeAdapterFactory( - RuntimeTypeAdapterFactory.of(OperationResult.class, "gsonType", true) - .registerSubtype( - ProjectConfigurationOperationResult.class, - ProjectConfigurationOperationResult.class.getName()) - .registerSubtype(FileDownloadResult.class, FileDownloadResult.class.getName()) - .registerSubtype(TaskOperationResult.class, TaskOperationResult.class.getName()) - .registerSubtype(TestOperationResult.class, TestOperationResult.class.getName()) - .registerSubtype( - TransformOperationResult.class, TransformOperationResult.class.getName()) - .registerSubtype(WorkItemOperationResult.class, WorkItemOperationResult.class.getName()) - .registerSubtype(DefaultOperationResult.class, DefaultOperationResult.class.getName())); - - builder.registerTypeAdapterFactory( - RuntimeTypeAdapterFactory.of(TaskOperationResult.class, "gsonType", true) - .registerSubtype(TaskFailureResult.class, TaskFailureResult.class.getName()) - .registerSubtype(TaskSkippedResult.class, TaskSkippedResult.class.getName()) - .registerSubtype(TaskExecutionResult.class, TaskExecutionResult.class.getName()) - .registerSubtype(TaskSuccessResult.class, TaskSuccessResult.class.getName())); - } - - public static Launcher newClientLauncher( - IToolingApiClient client, InputStream in, OutputStream out) { - return newIoLauncher( - new Object[] {client}, new Class[] {IToolingApiServer.class, IProject.class}, in, out); - } - - public static Launcher newIoLauncher( - Object[] locals, Class[] remotes, InputStream in, OutputStream out) { - return new Launcher.Builder<>() - .setInput(in) - .setOutput(out) - .setExecutorService(Executors.newCachedThreadPool()) - .setLocalServices(Arrays.asList(locals)) - .setRemoteInterfaces(Arrays.asList(remotes)) - .configureGson(ToolingApiLauncher::configureGson) - .setClassLoader(locals[0].getClass().getClassLoader()) - .create(); - } - - public static Launcher newServerLauncher( - IToolingApiServer server, IProject project, InputStream in, OutputStream out) { - return newIoLauncher( - new Object[] {server, project}, new Class[] {IToolingApiClient.class}, in, out); - } -} diff --git a/subprojects/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/util/ToolingApiLauncher.kt b/subprojects/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/util/ToolingApiLauncher.kt new file mode 100644 index 000000000..94e08109f --- /dev/null +++ b/subprojects/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/util/ToolingApiLauncher.kt @@ -0,0 +1,227 @@ +/* + * 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.util + +import com.google.gson.GsonBuilder +import com.itsaky.androidide.builder.model.DefaultJavaCompileOptions +import com.itsaky.androidide.builder.model.IJavaCompilerSettings +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.models.AndroidProjectMetadata +import com.itsaky.androidide.tooling.api.models.AndroidVariantMetadata +import com.itsaky.androidide.tooling.api.models.BasicAndroidVariantMetadata +import com.itsaky.androidide.tooling.api.models.BasicProjectMetadata +import com.itsaky.androidide.tooling.api.models.GradleTask +import com.itsaky.androidide.tooling.api.models.JavaModuleCompilerSettings +import com.itsaky.androidide.tooling.api.models.JavaModuleDependency +import com.itsaky.androidide.tooling.api.models.JavaModuleExternalDependency +import com.itsaky.androidide.tooling.api.models.JavaModuleProjectDependency +import com.itsaky.androidide.tooling.api.models.Launchable +import com.itsaky.androidide.tooling.api.models.ProjectMetadata +import com.itsaky.androidide.tooling.events.OperationDescriptor +import com.itsaky.androidide.tooling.events.OperationResult +import com.itsaky.androidide.tooling.events.ProgressEvent +import com.itsaky.androidide.tooling.events.StatusEvent +import com.itsaky.androidide.tooling.events.configuration.ProjectConfigurationFinishEvent +import com.itsaky.androidide.tooling.events.configuration.ProjectConfigurationOperationDescriptor +import com.itsaky.androidide.tooling.events.configuration.ProjectConfigurationOperationResult +import com.itsaky.androidide.tooling.events.configuration.ProjectConfigurationProgressEvent +import com.itsaky.androidide.tooling.events.configuration.ProjectConfigurationStartEvent +import com.itsaky.androidide.tooling.events.download.FileDownloadFinishEvent +import com.itsaky.androidide.tooling.events.download.FileDownloadOperationDescriptor +import com.itsaky.androidide.tooling.events.download.FileDownloadProgressEvent +import com.itsaky.androidide.tooling.events.download.FileDownloadResult +import com.itsaky.androidide.tooling.events.download.FileDownloadStartEvent +import com.itsaky.androidide.tooling.events.internal.DefaultFinishEvent +import com.itsaky.androidide.tooling.events.internal.DefaultOperationDescriptor +import com.itsaky.androidide.tooling.events.internal.DefaultOperationResult +import com.itsaky.androidide.tooling.events.internal.DefaultProgressEvent +import com.itsaky.androidide.tooling.events.internal.DefaultStartEvent +import com.itsaky.androidide.tooling.events.task.TaskExecutionResult +import com.itsaky.androidide.tooling.events.task.TaskFailureResult +import com.itsaky.androidide.tooling.events.task.TaskFinishEvent +import com.itsaky.androidide.tooling.events.task.TaskOperationDescriptor +import com.itsaky.androidide.tooling.events.task.TaskOperationResult +import com.itsaky.androidide.tooling.events.task.TaskProgressEvent +import com.itsaky.androidide.tooling.events.task.TaskSkippedResult +import com.itsaky.androidide.tooling.events.task.TaskStartEvent +import com.itsaky.androidide.tooling.events.task.TaskSuccessResult +import com.itsaky.androidide.tooling.events.test.TestFinishEvent +import com.itsaky.androidide.tooling.events.test.TestOperationDescriptor +import com.itsaky.androidide.tooling.events.test.TestOperationResult +import com.itsaky.androidide.tooling.events.test.TestProgressEvent +import com.itsaky.androidide.tooling.events.test.TestStartEvent +import com.itsaky.androidide.tooling.events.transform.TransformFinishEvent +import com.itsaky.androidide.tooling.events.transform.TransformOperationDescriptor +import com.itsaky.androidide.tooling.events.transform.TransformProgressEvent +import com.itsaky.androidide.tooling.events.transform.TransformStartEvent +import com.itsaky.androidide.tooling.events.work.WorkItemFinishEvent +import com.itsaky.androidide.tooling.events.work.WorkItemOperationDescriptor +import com.itsaky.androidide.tooling.events.work.WorkItemOperationResult +import com.itsaky.androidide.tooling.events.work.WorkItemProgressEvent +import com.itsaky.androidide.tooling.events.work.WorkItemStartEvent +import org.eclipse.lsp4j.jsonrpc.Launcher +import java.io.File +import java.io.InputStream +import java.io.OutputStream +import java.util.concurrent.Executors + +/** + * Utility class for launching [IToolingApiClient] and [IToolingApiServer]. + * + * @author Akash Yadav + */ +object ToolingApiLauncher { + + fun createIOLauncher( + local: Any?, remote: Class?, `in`: InputStream?, out: OutputStream?): Launcher { + return Launcher.Builder() + .setInput(`in`) + .setOutput(out) + .setLocalService(local) + .setRemoteInterface(remote) + .configureGson { configureGson(it) } + .create() + } + + @JvmStatic + fun configureGson(builder: GsonBuilder) { + builder.registerTypeAdapter(File::class.java, FileTypeAdapter()) + builder.runtimeTypeAdapter( + BasicProjectMetadata::class.java, + ProjectMetadata::class.java, + AndroidProjectMetadata::class.java + ) + builder.runtimeTypeAdapter( + BasicAndroidVariantMetadata::class.java, + AndroidVariantMetadata::class.java + ) + builder.runtimeTypeAdapter( + JavaModuleDependency::class.java, + JavaModuleExternalDependency::class.java, + JavaModuleProjectDependency::class.java + ) + builder.runtimeTypeAdapter( + IJavaCompilerSettings::class.java, + DefaultJavaCompileOptions::class.java, + JavaModuleCompilerSettings::class.java + ) + builder.runtimeTypeAdapter( + Launchable::class.java, + GradleTask::class.java + ) + builder.runtimeTypeAdapter( + ProgressEvent::class.java, + ProjectConfigurationProgressEvent::class.java, + ProjectConfigurationStartEvent::class.java, + ProjectConfigurationFinishEvent::class.java, + + FileDownloadProgressEvent::class.java, + FileDownloadStartEvent::class.java, + FileDownloadFinishEvent::class.java, + + TaskProgressEvent::class.java, + TaskStartEvent::class.java, + TaskFinishEvent::class.java, + + TestProgressEvent::class.java, + TestStartEvent::class.java, + TestFinishEvent::class.java, + + TransformProgressEvent::class.java, + TransformStartEvent::class.java, + TransformFinishEvent::class.java, + + WorkItemProgressEvent::class.java, + WorkItemStartEvent::class.java, + WorkItemFinishEvent::class.java, + + DefaultProgressEvent::class.java, + DefaultStartEvent::class.java, + DefaultFinishEvent::class.java, + + StatusEvent::class.java + ) + builder.runtimeTypeAdapter( + OperationDescriptor::class.java, + ProjectConfigurationOperationDescriptor::class.java, + FileDownloadOperationDescriptor::class.java, + TaskOperationDescriptor::class.java, + TestOperationDescriptor::class.java, + TransformOperationDescriptor::class.java, + WorkItemOperationDescriptor::class.java, + DefaultOperationDescriptor::class.java + ) + builder.runtimeTypeAdapter( + OperationResult::class.java, + ProjectConfigurationOperationResult::class.java, + FileDownloadResult::class.java, + TaskOperationResult::class.java, + TestOperationResult::class.java, + WorkItemOperationResult::class.java, + DefaultOperationResult::class.java + ) + builder.runtimeTypeAdapter( + TaskOperationResult::class.java, + TaskFailureResult::class.java, + TaskSkippedResult::class.java, + TaskExecutionResult::class.java, + TaskSuccessResult::class.java + ) + } + + private fun GsonBuilder.runtimeTypeAdapter(baseClass: Class, + vararg subtype: Class) { + registerTypeAdapterFactory( + RuntimeTypeAdapterFactory.of(baseClass, "gsonType", true) + .registerSubtype(baseClass).also { factory -> + subtype.forEach { subtype -> + factory.registerSubtype(subtype, subtype.name) + } + } + ) + } + + fun newClientLauncher( + client: IToolingApiClient, `in`: InputStream?, out: OutputStream?): Launcher { + return newIoLauncher(arrayOf(client), arrayOf( + IToolingApiServer::class.java, IProject::class.java), `in`, out) + } + + fun newIoLauncher( + locals: Array, remotes: Array?>, `in`: InputStream?, + out: OutputStream?): Launcher { + return Launcher.Builder() + .setInput(`in`) + .setOutput(out) + .setExecutorService(Executors.newCachedThreadPool()) + .setLocalServices(listOf(*locals)) + .setRemoteInterfaces(listOf(*remotes)) + .configureGson { configureGson(it) } + .setClassLoader(locals[0].javaClass.classLoader) + .create() + } + + @JvmStatic + fun newServerLauncher( + server: IToolingApiServer, project: IProject, `in`: InputStream?, + out: OutputStream?): Launcher { + return newIoLauncher(arrayOf(server, project), arrayOf( + IToolingApiClient::class.java), `in`, out) + } +} \ No newline at end of file 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 6ebd5fb5d..6cd6cd14a 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 @@ -45,7 +45,7 @@ object XmlInflaterTest { val (server, project) = ToolingApiTestLauncher().launchServer() - server.initialize(InitializeProjectMessage(FileProvider.testProjectRoot().pathString)).get() + server.initialize(InitializeProjectMessage(FileProvider.testProjectRoot().pathString, "debug")).get() Lookup.getDefault().register(BuildService.KEY_PROJECT_PROXY, project) ProjectManager.setupProject()