Include java source and bytecode version info in Tooling API ModuleProjects

This commit is contained in:
Akash Yadav
2022-07-16 22:38:46 +05:30
parent af93c16711
commit 345a44445c
13 changed files with 205 additions and 39 deletions
Binary file not shown.
@@ -21,10 +21,23 @@ import com.android.builder.model.v2.ide.JavaCompileOptions
import java.io.Serializable
/** @author Akash Yadav */
class DefaultJavaCompileOptions : JavaCompileOptions, Serializable {
class DefaultJavaCompileOptions : IJavaCompilerSettings(), JavaCompileOptions, Serializable {
private val serialVersionUID = 1L
override var encoding: String = ""
override var isCoreLibraryDesugaringEnabled: Boolean = false
override var sourceCompatibility: String = ""
set(value) {
field = value
javaSourceVersion = value
}
override var targetCompatibility: String = ""
set(value) {
field = value
javaBytecodeVersion = value
}
override var javaSourceVersion: String = sourceCompatibility
override var javaBytecodeVersion: String = targetCompatibility
}
@@ -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 <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.builder.model
/**
* Java compilation settings for both, Android and Java modules.
*
* @author Akash Yadav
*/
abstract class IJavaCompilerSettings {
private val gsonType: String = javaClass.name
/** Source java version. */
abstract val javaSourceVersion: String
/** Target java version. */
abstract val javaBytecodeVersion: String
}
@@ -30,12 +30,14 @@ 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;
@@ -46,6 +48,7 @@ import com.itsaky.androidide.tooling.impl.Main;
import com.itsaky.androidide.utils.ILogger;
import com.itsaky.androidide.utils.LogUtils;
import org.gradle.api.JavaVersion;
import org.gradle.tooling.BuildController;
import org.gradle.tooling.ConfigurableLauncher;
import org.gradle.tooling.ProjectConnection;
@@ -177,13 +180,15 @@ public class ProjectReader {
private static IdeGradleProject buildModuleProject(
BuildController controller,
final IdeaProject ideaProject,
IdeaModule ideaModule,
Map<String, DefaultProjectSyncIssues> outIssues) {
return buildModuleProject(controller, ideaModule, outIssues, false);
return buildModuleProject(controller, ideaProject, ideaModule, outIssues, false);
}
private static IdeGradleProject buildModuleProject(
BuildController controller,
final IdeaProject ideaProject,
IdeaModule ideaModule,
Map<String, DefaultProjectSyncIssues> outIssues,
boolean androidOnly) {
@@ -213,7 +218,7 @@ public class ProjectReader {
error.printStackTrace();
}
return buildJavaModuleProject(gradle, ideaModule);
return buildJavaModuleProject(gradle, ideaProject, ideaModule);
} catch (Throwable e) {
log("Unable to create model for project");
e.printStackTrace();
@@ -280,7 +285,8 @@ public class ProjectReader {
return new ModelInfoContainer(module, syncIssues);
}
private static JavaModule buildJavaModuleProject(GradleProject gradle, IdeaModule idea) {
private static JavaModule buildJavaModuleProject(
GradleProject gradle, final IdeaProject ideaProject, IdeaModule idea) {
final var builder = new ProjectBuilder();
builder.setName(gradle.getName());
builder.setDescription(gradle.getDescription());
@@ -289,13 +295,66 @@ public class ProjectReader {
builder.setBuildDir(gradle.getBuildDirectory());
builder.setBuildScript(gradle.getBuildScript().getSourceFile());
builder.setContentRoots(collectContentRoots(idea));
// builder.setJavaDependencies(collectJavaDependencies(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);
sb.append(msg instanceof Throwable ? "\n" : MSG_SEPARATOR);
}
return sb.toString();
}
private static List<? extends JavaModuleDependency> collectJavaDependencies(IdeaModule idea) {
final var list = new ArrayList<JavaModuleDependency>();
for (IdeaDependency dependency : idea.getDependencies()) {
@@ -392,7 +451,8 @@ public class ProjectReader {
"No GradleProject model is associated with project path: " + "':'");
}
IdeGradleProject project = buildModuleProject(controller, rootModule.get(), outIssues, true);
IdeGradleProject project =
buildModuleProject(controller, ideaProject, rootModule.get(), outIssues, true);
final var gradle = rootModule.get().getGradleProject();
if (project == null) {
final var builder = new ProjectBuilder();
@@ -430,29 +490,10 @@ public class ProjectReader {
continue;
}
final var sub = buildModuleProject(controller, module, outIssues);
final var sub = buildModuleProject(controller, ideaProject, module, outIssues);
if (sub != null) {
project.moduleProjects.add(sub);
}
}
}
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);
sb.append(msg instanceof Throwable ? "\n" : MSG_SEPARATOR);
}
return sb.toString();
}
}
@@ -28,10 +28,10 @@ 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.testing.ToolingApiTestLauncher
import java.io.File
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import java.io.File
/** @author Akash Yadav */
@RunWith(JUnit4::class)
@@ -60,6 +60,8 @@ class ToolingApiImplTest {
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)
@@ -102,12 +104,17 @@ class ToolingApiImplTest {
)
.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 as JavaModule).javaDependencies.firstOrNull {
javaLibrary.javaDependencies.firstOrNull {
it is JavaModuleExternalDependency &&
it.gradleArtifact != null &&
it.run {
@@ -120,7 +127,7 @@ class ToolingApiImplTest {
.isNotNull()
assertThat(
javaLibrary.javaDependencies.first {
javaLibrary.javaDependencies.firstOrNull {
it is JavaModuleProjectDependency && it.moduleName == "another-java-library"
}
)
@@ -135,5 +142,11 @@ class ToolingApiImplTest {
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")
}
}
@@ -26,6 +26,7 @@ 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.tooling.api.IProject.Type
import com.itsaky.androidide.tooling.api.IProject.Type.Android
import com.itsaky.androidide.tooling.api.messages.result.SimpleVariantData
@@ -50,13 +51,13 @@ open class AndroidModule(
parent: IdeGradleProject?,
tasks: List<GradleTask>,
val projectType: ProjectType?,
override var dynamicFeatures: Collection<String>?,
override var flags: DefaultAndroidGradlePluginProjectFlags,
override var javaCompileOptions: DefaultJavaCompileOptions,
override var resourcePrefix: String?,
override val dynamicFeatures: Collection<String>?,
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<DefaultVariant> = emptyList(),
override var viewBindingOptions: DefaultViewBindingOptions?,
override val viewBindingOptions: DefaultViewBindingOptions?,
override val lintChecksJars: List<File>,
val modelSyncFiles: List<DefaultModelSyncFile>,
val simpleVariants: MutableList<SimpleVariantData> = mutableListOf()
@@ -77,8 +78,9 @@ open class AndroidModule(
}
var bootClassPaths: Collection<File> = emptyList()
var mainSourceSet: DefaultSourceSetContainer? = null
var libraries: List<DefaultLibrary> = emptyList()
override var compilerSettings: IJavaCompilerSettings = javaCompileOptions
@Suppress("unused")
companion object {
@@ -17,6 +17,7 @@
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
@@ -39,6 +40,9 @@ open class JavaModule(
parent: IdeGradleProject?,
tasks: List<GradleTask>,
/** Compiler settings for this module. */
override val compilerSettings: IJavaCompilerSettings,
/** * Source directories of this project. */
val contentRoots: List<JavaContentRoot>,
@@ -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 <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.tooling.api.model
import com.itsaky.androidide.builder.model.IJavaCompilerSettings
import javax.lang.model.SourceVersion
/**
* Compiler settings for [JavaModule].
*
* @author Akash Yadav
*/
class JavaModuleCompilerSettings(
override val javaSourceVersion: String,
override val javaBytecodeVersion: String
) : IJavaCompilerSettings(), java.io.Serializable {
private val serialVersionUID = 1L
internal constructor() : this(SourceVersion.RELEASE_11.name, SourceVersion.RELEASE_11.name)
}
@@ -17,6 +17,7 @@
package com.itsaky.androidide.tooling.api.model
import com.itsaky.androidide.builder.model.IJavaCompilerSettings
import java.io.File
/**
@@ -26,6 +27,9 @@ import java.io.File
*/
interface ModuleProject {
/** Compiler settings for this module. */
val compilerSettings: IJavaCompilerSettings
/**
* Get the generated JAR file for this module.
*
@@ -21,12 +21,14 @@ import com.itsaky.androidide.builder.model.DefaultAndroidGradlePluginProjectFlag
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
@@ -48,7 +50,13 @@ class ProjectBuilder {
var dynamicFeatures: Collection<String>? = 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<DefaultModelSyncFile> = emptyList()
@@ -81,6 +89,7 @@ class ProjectBuilder {
buildScript,
parent,
tasks,
javaCompilerSettings,
contentRoots,
javaDependencies
)
@@ -18,14 +18,17 @@
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.IdeGradleProject;
import com.itsaky.androidide.tooling.api.model.GradleTask;
import com.itsaky.androidide.tooling.api.model.JavaModule;
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;
@@ -115,6 +118,13 @@ public class ToolingApiLauncher {
.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)
Binary file not shown.
@@ -3,8 +3,8 @@ plugins {
}
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
dependencies {