mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
Eliminated use of compiler module
This commit is contained in:
Generated
+6
-1
@@ -19,7 +19,7 @@
|
||||
<remote-repository>
|
||||
<option name="id" value="MavenLocal" />
|
||||
<option name="name" value="MavenLocal" />
|
||||
<option name="url" value="file:/$PROJECT_DIR$/../../../Akash%20Yadav/.m2/repository/" />
|
||||
<option name="url" value="file:/$USER_HOME$/Akash%20Yadav/.m2/repository/" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="maven" />
|
||||
@@ -66,5 +66,10 @@
|
||||
<option name="name" value="MavenLocal" />
|
||||
<option name="url" value="file:$USER_HOME$/.m2/repository" />
|
||||
</remote-repository>
|
||||
<remote-repository>
|
||||
<option name="id" value="maven2" />
|
||||
<option name="name" value="maven2" />
|
||||
<option name="url" value="https://s01.oss.sonatype.org/content/repositories/snapshots/" />
|
||||
</remote-repository>
|
||||
</component>
|
||||
</project>
|
||||
Executable
BIN
Binary file not shown.
@@ -58,16 +58,12 @@ import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.transition.Slide;
|
||||
import androidx.transition.TransitionManager;
|
||||
|
||||
import com.blankj.utilcode.util.ClipboardUtils;
|
||||
import com.blankj.utilcode.util.FileIOUtils;
|
||||
import com.blankj.utilcode.util.FileUtils;
|
||||
import com.blankj.utilcode.util.IntentUtils;
|
||||
import com.blankj.utilcode.util.KeyboardUtils;
|
||||
import com.blankj.utilcode.util.ResourceUtils;
|
||||
import com.blankj.utilcode.util.SizeUtils;
|
||||
import com.blankj.utilcode.util.ThreadUtils;
|
||||
import com.blankj.utilcode.util.ThrowableUtils;
|
||||
import com.blankj.utilcode.util.ZipUtils;
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.android.material.navigation.NavigationView;
|
||||
@@ -102,7 +98,6 @@ import com.itsaky.androidide.lsp.models.DiagnosticItem;
|
||||
import com.itsaky.androidide.lsp.models.InitializeParams;
|
||||
import com.itsaky.androidide.lsp.xml.XMLLanguageServer;
|
||||
import com.itsaky.androidide.managers.PreferenceManager;
|
||||
import com.itsaky.androidide.managers.ToolsManager;
|
||||
import com.itsaky.androidide.models.ApkMetadata;
|
||||
import com.itsaky.androidide.models.DiagnosticGroup;
|
||||
import com.itsaky.androidide.models.LogLine;
|
||||
@@ -148,7 +143,6 @@ import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -1134,7 +1128,6 @@ public class EditorActivity extends StudioActivity
|
||||
EditorActivityActions.register(this);
|
||||
|
||||
try {
|
||||
checkForCompilerModule();
|
||||
dispatchOnResumeToEditors();
|
||||
|
||||
if (mFileTreeFragment != null) {
|
||||
@@ -1166,77 +1159,7 @@ public class EditorActivity extends StudioActivity
|
||||
ProjectManager.INSTANCE.unregister();
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void checkForCompilerModule() {
|
||||
if (!Environment.isCompilerModuleInstalled()) {
|
||||
final var pd =
|
||||
ProgressDialog.show(
|
||||
this,
|
||||
getString(R.string.title_compiler_module_install),
|
||||
getString(R.string.msg_compiler_module_install),
|
||||
true,
|
||||
false);
|
||||
|
||||
final CompletableFuture<Boolean> future =
|
||||
CompletableFuture.supplyAsync(
|
||||
() -> {
|
||||
final var tmpModule = new File(Environment.TMP_DIR, "compiler-module.zip");
|
||||
if (!ResourceUtils.copyFileFromAssets(
|
||||
ToolsManager.getCommonAsset("compiler-module.zip"),
|
||||
tmpModule.getAbsolutePath())) {
|
||||
throw new CompletionException(
|
||||
new RuntimeException("Unable to copy compiler-module.zip"));
|
||||
}
|
||||
|
||||
try {
|
||||
ZipUtils.unzipFile(tmpModule, Environment.COMPILER_MODULE);
|
||||
} catch (Throwable e) {
|
||||
throw new CompletionException(e);
|
||||
}
|
||||
|
||||
if (!Environment.isCompilerModuleInstalled()) {
|
||||
throw new CompletionException(new RuntimeException("Unknown error"));
|
||||
}
|
||||
|
||||
try {
|
||||
FileUtils.delete(tmpModule);
|
||||
} catch (Exception e) {
|
||||
// ignored
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
future.whenComplete(
|
||||
(result, error) -> {
|
||||
pd.dismiss();
|
||||
|
||||
if (error != null) {
|
||||
showCompilerModuleInstallError(error);
|
||||
return;
|
||||
}
|
||||
|
||||
getApp().toast(getString(R.string.msg_compiler_module_installed), Toaster.Type.SUCCESS);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void showCompilerModuleInstallError(Throwable error) {
|
||||
final var stacktrace = ThrowableUtils.getFullStackTrace(error);
|
||||
final var builder = DialogUtils.newMaterialDialogBuilder(this);
|
||||
builder.setTitle(R.string.title_installation_failed);
|
||||
builder.setMessage(getString(R.string.msg_compiler_module_install_failed, stacktrace));
|
||||
builder.setCancelable(false);
|
||||
builder.setPositiveButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
|
||||
builder.setNegativeButton(
|
||||
R.string.copy,
|
||||
(dialog, which) -> {
|
||||
ClipboardUtils.copyText(stacktrace);
|
||||
dialog.dismiss();
|
||||
});
|
||||
builder.show();
|
||||
}
|
||||
|
||||
// TODO Replace with events
|
||||
private void dispatchOnResumeToEditors() {
|
||||
CompletableFuture.runAsync(
|
||||
() -> {
|
||||
|
||||
+1
-1
@@ -179,6 +179,7 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> {
|
||||
* items with length less than or equal to this threshold.
|
||||
*/
|
||||
private static final int MAX_ITEM_LENGTH_FOR_FILLING = 10;
|
||||
|
||||
private static final Pattern FORMAT_SPECIFIER = Pattern.compile("%|\\{[0-9]\\}");
|
||||
static final ImmutableSet<String> LOG_METHODS =
|
||||
ImmutableSet.of(
|
||||
@@ -2777,7 +2778,6 @@ public class JavaInputAstVisitor extends TreePathScanner<Void, Void> {
|
||||
}
|
||||
|
||||
/** Collapse chains of {@code .} operators, across multiple {@link ASTNode} types. */
|
||||
|
||||
private void formatAnnotationOrModifier(Deque<AnnotationOrModifier> modifiers) {
|
||||
AnnotationOrModifier modifier = modifiers.removeFirst();
|
||||
switch (modifier.getKind()) {
|
||||
|
||||
+12
-6
@@ -28,7 +28,8 @@ import com.itsaky.androidide.projects.builder.BuildService
|
||||
import com.itsaky.androidide.projects.models.ActiveDocument
|
||||
import com.itsaky.androidide.projects.util.ProjectTransformer
|
||||
import com.itsaky.androidide.tooling.api.IProject
|
||||
import com.itsaky.androidide.utils.BootstrapClassesProvider
|
||||
import com.itsaky.androidide.utils.BootClasspathProvider
|
||||
import com.itsaky.androidide.utils.Environment
|
||||
import com.itsaky.androidide.utils.ILogger
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.BufferedReader
|
||||
@@ -38,6 +39,7 @@ import java.io.StringReader
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.time.Instant
|
||||
import java.util.*
|
||||
import java.util.concurrent.*
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
@@ -226,12 +228,12 @@ object ProjectManager : EventReceiver {
|
||||
if (!checkInit()) {
|
||||
return createFileReader(file)
|
||||
}
|
||||
|
||||
|
||||
val document = getActiveDocument(file)
|
||||
if (document != null) {
|
||||
return BufferedReader(StringReader(document.content))
|
||||
}
|
||||
|
||||
|
||||
return createFileReader(file)
|
||||
}
|
||||
|
||||
@@ -239,7 +241,7 @@ object ProjectManager : EventReceiver {
|
||||
if (!checkInit()) {
|
||||
return createFileInputStream(file)
|
||||
}
|
||||
|
||||
|
||||
val document = getActiveDocument(file)
|
||||
if (document != null) {
|
||||
BufferedInputStream(document.content.byteInputStream())
|
||||
@@ -256,7 +258,11 @@ object ProjectManager : EventReceiver {
|
||||
super.register()
|
||||
|
||||
// Make sure we list and store the bootstrap classes
|
||||
CompletableFuture.runAsync { BootstrapClassesProvider.bootstrapClasses() }
|
||||
CompletableFuture.runAsync {
|
||||
BootClasspathProvider.update(
|
||||
Collections.singleton(Environment.ANDROID_JAR.absolutePath)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = BACKGROUND)
|
||||
@@ -306,7 +312,7 @@ object ProjectManager : EventReceiver {
|
||||
if (subModule !is ModuleProject) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
if (subModule.onDocumentChanged(event)) {
|
||||
break
|
||||
}
|
||||
|
||||
+6
-32
@@ -18,7 +18,6 @@
|
||||
package com.itsaky.androidide.projects.api
|
||||
|
||||
import android.text.TextUtils
|
||||
import com.google.common.reflect.ClassPath
|
||||
import com.itsaky.androidide.eventbus.events.editor.DocumentChangeEvent
|
||||
import com.itsaky.androidide.eventbus.events.editor.DocumentCloseEvent
|
||||
import com.itsaky.androidide.eventbus.events.editor.DocumentOpenEvent
|
||||
@@ -29,12 +28,9 @@ import com.itsaky.androidide.projects.util.DocumentUtils
|
||||
import com.itsaky.androidide.projects.util.SourceClassTrie
|
||||
import com.itsaky.androidide.projects.util.SourceClassTrie.SourceNode
|
||||
import com.itsaky.androidide.tooling.api.model.GradleTask
|
||||
import com.itsaky.androidide.utils.ClasspathReader
|
||||
import com.itsaky.androidide.utils.ILogger
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.net.MalformedURLException
|
||||
import java.net.URL
|
||||
import java.net.URLClassLoader
|
||||
import java.nio.file.Path
|
||||
import java.time.Instant
|
||||
import java.util.concurrent.*
|
||||
@@ -163,6 +159,7 @@ abstract class ModuleProject(
|
||||
}
|
||||
|
||||
/** Finds the source files and classes from source directories and classpaths and indexes them. */
|
||||
@Suppress("UnstableApiUsage")
|
||||
fun indexSourcesAndClasspaths() {
|
||||
log.info("Indexing sources and classpaths for project:", path)
|
||||
|
||||
@@ -180,26 +177,11 @@ abstract class ModuleProject(
|
||||
}
|
||||
|
||||
log.debug("Sources indexed for project: '$path'. Found $count source files.")
|
||||
count = 0
|
||||
|
||||
val urls =
|
||||
getCompileClasspaths().filter { it.exists() }.map { toUrl(it.toPath()) }.toTypedArray()
|
||||
val loader = URLClassLoader(urls, null)
|
||||
val scanner: ClassPath
|
||||
try {
|
||||
scanner = ClassPath.from(loader)
|
||||
} catch (e: IOException) {
|
||||
log.warn("Unable to read classpaths for project:", path)
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
|
||||
log.debug("Classpaths indexed for project:", path)
|
||||
scanner.topLevelClasses.forEach {
|
||||
this.compileClasspathClasses.append(it.name)
|
||||
count++
|
||||
}
|
||||
|
||||
log.debug("Classpaths indexed for project: '$path'. Found $count classpaths.")
|
||||
val paths = getCompileClasspaths().filter { it.exists() }.map { it.toPath() }
|
||||
val topLevelClasses = ClasspathReader.listClasses(paths).filter { it.isTopLevel }
|
||||
topLevelClasses.forEach { this.compileClasspathClasses.append(it.name) }
|
||||
log.debug("Classpaths indexed for project: '$path'. Found ${topLevelClasses.size} classpaths.")
|
||||
}
|
||||
|
||||
fun getSourceFilesInDir(dir: Path): List<SourceNode> =
|
||||
@@ -278,12 +260,4 @@ abstract class ModuleProject(
|
||||
modified = Instant.now()
|
||||
)
|
||||
}
|
||||
|
||||
private fun toUrl(path: Path): URL {
|
||||
try {
|
||||
return path.toUri().toURL()
|
||||
} catch (e: MalformedURLException) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
+4
-1
@@ -28,7 +28,7 @@ buildscript {
|
||||
mavenLocal ()
|
||||
mavenCentral ()
|
||||
maven {
|
||||
url 'https://oss.sonatype.org/content/repositories/snapshots/'
|
||||
url 'https://s01.oss.sonatype.org/content/repositories/snapshots/'
|
||||
}
|
||||
maven {
|
||||
url 'https://jitpack.io'
|
||||
@@ -116,6 +116,9 @@ allprojects {
|
||||
maven {
|
||||
url 'https://jitpack.io'
|
||||
}
|
||||
maven {
|
||||
url 'https://s01.oss.sonatype.org/content/repositories/snapshots/'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ dependencies {
|
||||
implementation fileTree (dir: 'libs', include: ['*.jar'])
|
||||
implementation common.utilcode
|
||||
implementation common.lang3
|
||||
implementation google.guava
|
||||
implementation google.material
|
||||
|
||||
implementation androidx.appcompat
|
||||
|
||||
@@ -56,9 +56,9 @@ public class ToolsManager {
|
||||
copyBusyboxIfNeeded();
|
||||
extractLogsenderIfNeeded();
|
||||
extractAapt2();
|
||||
// extractLibHooks();
|
||||
extractGradlePlugin();
|
||||
extractToolingApi();
|
||||
extractAndroidJar();
|
||||
extractIdeEnv();
|
||||
writeInitScript();
|
||||
})
|
||||
@@ -74,6 +74,13 @@ public class ToolsManager {
|
||||
});
|
||||
}
|
||||
|
||||
private static void extractAndroidJar() {
|
||||
if (!Environment.ANDROID_JAR.exists()) {
|
||||
ResourceUtils.copyFileFromAssets(
|
||||
getCommonAsset("android.jar"), Environment.ANDROID_JAR.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
private static void extractIdeEnv() {
|
||||
final var file = new File(Environment.BIN_DIR, "ideenv");
|
||||
if (file.exists()) {
|
||||
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.utils;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.reflect.ClassPath;
|
||||
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Provides class names from the boot classpath (i.e. android.jar).
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
public class BootClasspathProvider {
|
||||
|
||||
private static final Map<String, ImmutableSet<ClassPath.ClassInfo>> bootClasspathClasses =
|
||||
new ConcurrentHashMap<>();
|
||||
private static final ILogger LOG = ILogger.newInstance("BootClassProvider");
|
||||
|
||||
/**
|
||||
* Updates the boot classpath cache. If a classpath is already indexed, it is skipped.
|
||||
*
|
||||
* @param classpaths The full file paths of JAR files to index.
|
||||
* @return <code>true</code> if the the classpath cache was updated. <code>false</code> otherwise.
|
||||
* <p>This method <code>true</code> if and only if any new classpath classes are added into
|
||||
* the cache.
|
||||
*/
|
||||
public static synchronized boolean update(Collection<String> classpaths) {
|
||||
final var start = System.currentTimeMillis();
|
||||
var count = 0;
|
||||
for (final var classpath : classpaths) {
|
||||
if (bootClasspathClasses.containsKey(classpath)) {
|
||||
LOG.info("Skipping indexing for boot classpath as it is already indexed:", classpath);
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG.debug("Indexing boot classpath:", classpath);
|
||||
final var path = Paths.get(classpath);
|
||||
final var classes = ClasspathReader.listClasses(Collections.singleton(path));
|
||||
bootClasspathClasses.put(classpath, classes);
|
||||
count += classes.size();
|
||||
}
|
||||
|
||||
LOG.info(
|
||||
"Indexed", count, "boot classpath classes in", System.currentTimeMillis() - start, "ms");
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops the entry for the given classpath from the cache.
|
||||
*
|
||||
* @param classpath The classpath to drop entry for.
|
||||
*/
|
||||
public static synchronized void drop(String classpath) {
|
||||
bootClasspathClasses.remove(classpath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops entries for all the given classpaths from the cache.
|
||||
*
|
||||
* @param classpaths The classpaths to drop entry for.
|
||||
*/
|
||||
public static synchronized void dropAll(Collection<String> classpaths) {
|
||||
classpaths.forEach(BootClasspathProvider::drop);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all cached <strong>top-level</strong> classes from all the given classpath locations.
|
||||
*
|
||||
* @param classpaths The classpaths to get class list from.
|
||||
* @return The cached <strong>top-level</strong> classes from the given classpaths.
|
||||
*/
|
||||
public static synchronized Set<String> getTopLevelClasses(Collection<String> classpaths) {
|
||||
final var result = new TreeSet<String>();
|
||||
if (classpaths == null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
for (final var classpath : classpaths) {
|
||||
final var entry = bootClasspathClasses.get(classpath);
|
||||
if (entry == null || entry.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
entry.stream()
|
||||
.filter(ClassPath.ClassInfo::isTopLevel)
|
||||
.map(ClassPath.ClassInfo::getName)
|
||||
.forEach(result::add);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,107 +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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.utils;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URI;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Provides class names from the bootstrap classpath (i.e. android.jar).
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public class BootstrapClassesProvider {
|
||||
|
||||
private static Set<String> sCachedClasses;
|
||||
private static final ILogger LOG = ILogger.newInstance("BootstrapClassesProvider");
|
||||
|
||||
public static Set<String> bootstrapClasses() {
|
||||
if (sCachedClasses != null && !sCachedClasses.isEmpty()) {
|
||||
return sCachedClasses;
|
||||
}
|
||||
|
||||
final var start = System.currentTimeMillis();
|
||||
sCachedClasses = new HashSet<>();
|
||||
|
||||
try {
|
||||
final var fs = getJRTFileSystem();
|
||||
if (fs == null) {
|
||||
LOG.error("Unable to create JRT FileSystem instance");
|
||||
return sCachedClasses;
|
||||
}
|
||||
|
||||
final var javaBase = fs.getPath("/modules/java.base/");
|
||||
try (final var stream = Files.walk(javaBase)) {
|
||||
final var it = stream.iterator();
|
||||
while (it.hasNext()) {
|
||||
final var classFile = it.next();
|
||||
final String relative = javaBase.relativize(classFile).toString();
|
||||
if (relative.endsWith(".class") && !relative.contains("$")) {
|
||||
final String trim = relative.substring(0, relative.length() - ".class".length());
|
||||
final String qualifiedName = trim.replace(File.separatorChar, '.');
|
||||
sCachedClasses.add(qualifiedName);
|
||||
}
|
||||
}
|
||||
LOG.info(
|
||||
"Found",
|
||||
sCachedClasses.size(),
|
||||
"classes in bootstrap classpath in",
|
||||
System.currentTimeMillis() - start,
|
||||
"ms");
|
||||
} catch (IOException io) {
|
||||
LOG.error("An error occurred while listing classes from bootstrap classpath", io);
|
||||
}
|
||||
} catch (Throwable err) {
|
||||
LOG.error("Unable to find classes from bootstrap classpath", err);
|
||||
}
|
||||
|
||||
return sCachedClasses;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Contract(" -> new")
|
||||
private static FileSystem getJRTFileSystem() throws Exception {
|
||||
final URI uri = URI.create("jrt:/");
|
||||
try {
|
||||
// Tests run on the JVM, so we cannot directly access JRTFileSystemProvider
|
||||
// But this method will work
|
||||
return FileSystems.getFileSystem(uri);
|
||||
} catch (Throwable th) {
|
||||
|
||||
// When on ART, JRT file system is not available
|
||||
// So we create an instance of the FS manually
|
||||
final Class<?> klass = Class.forName("jdk.internal.jrtfs.JrtFileSystemProvider");
|
||||
final Method newFs = klass.getMethod("newFileSystem", URI.class, Map.class);
|
||||
newFs.setAccessible(true);
|
||||
return (FileSystem) newFs.invoke(klass.newInstance(), uri, Collections.emptyMap());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.utils
|
||||
|
||||
import com.google.common.collect.ImmutableSet
|
||||
import com.google.common.reflect.ClassPath
|
||||
import java.io.IOException
|
||||
import java.net.MalformedURLException
|
||||
import java.net.URL
|
||||
import java.net.URLClassLoader
|
||||
import java.nio.file.Path
|
||||
|
||||
/**
|
||||
* Lists all classes from classpath(s).
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
class ClasspathReader {
|
||||
companion object {
|
||||
|
||||
private val log = ILogger.newInstance(ClasspathReader::class.java.simpleName)
|
||||
|
||||
@Suppress("UnstableApiUsage")
|
||||
@JvmStatic
|
||||
fun listClasses(paths: Collection<Path>): ImmutableSet<ClassPath.ClassInfo> {
|
||||
val urls = paths.map { toUrl(it) }.toTypedArray()
|
||||
val classLoader = URLClassLoader(urls, null)
|
||||
|
||||
val scanner: ClassPath
|
||||
try {
|
||||
scanner = ClassPath.from(classLoader)
|
||||
} catch (e: IOException) {
|
||||
log.warn("Unable to read classpaths for project:", paths)
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
|
||||
return scanner.allClasses
|
||||
}
|
||||
|
||||
private fun toUrl(path: Path): URL {
|
||||
try {
|
||||
return path.toUri().toURL()
|
||||
} catch (e: MalformedURLException) {
|
||||
throw RuntimeException(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -58,11 +58,9 @@ public final class Environment {
|
||||
public static File PROJECTS_DIR;
|
||||
public static File IDE_PROPS_FILE;
|
||||
public static File LIB_HOOK;
|
||||
/**
|
||||
* JDK modules used by the java language server for completions. This version of JDK modules
|
||||
* contains only the classes included in android.jar
|
||||
*/
|
||||
public static File COMPILER_MODULE;
|
||||
|
||||
/** Used by Java LSP until the project is initialized. */
|
||||
public static File ANDROID_JAR;
|
||||
|
||||
public static File TOOLING_API_JAR;
|
||||
|
||||
@@ -87,7 +85,7 @@ public final class Environment {
|
||||
BIN_DIR = mkdirIfNotExits(new File(PREFIX, "bin"));
|
||||
LIB_DIR = mkdirIfNotExits(new File(PREFIX, "lib"));
|
||||
PROJECTS_DIR = mkdirIfNotExits(new File(FileUtil.getExternalStorageDir(), PROJECTS_FOLDER));
|
||||
COMPILER_MODULE = mkdirIfNotExits(new File(ANDROIDIDE_HOME, "compiler-module"));
|
||||
ANDROID_JAR = mkdirIfNotExits(new File(ANDROIDIDE_HOME, "android.jar"));
|
||||
TOOLING_API_JAR =
|
||||
new File(mkdirIfNotExits(new File(ANDROIDIDE_HOME, "tooling-api")), "tooling-api-all.jar");
|
||||
AAPT2 = new File(ANDROIDIDE_HOME, "aapt2");
|
||||
@@ -268,15 +266,7 @@ public final class Environment {
|
||||
if (blacklist.isEmpty()) {
|
||||
blacklist.add("HOME");
|
||||
blacklist.add("SYSROOT");
|
||||
blacklist.add("JLS_HOME");
|
||||
}
|
||||
return blacklist;
|
||||
}
|
||||
|
||||
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
|
||||
public static boolean isCompilerModuleInstalled() {
|
||||
final var modules = new File(COMPILER_MODULE, "lib/modules");
|
||||
final var release = new File(COMPILER_MODULE, "release");
|
||||
return modules.exists() && modules.isFile() && release.exists() && release.isFile();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
ext {
|
||||
androidide = [
|
||||
javac: 'io.github.itsaky:nb-javac-android:17.0.0.1'
|
||||
javac: 'io.github.itsaky:nb-javac-android:17.0.0.2'
|
||||
]
|
||||
|
||||
common = [
|
||||
|
||||
@@ -17,17 +17,23 @@
|
||||
|
||||
package com.itsaky.androidide.lsp.java.compiler;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.util.Pair;
|
||||
|
||||
import com.itsaky.androidide.config.JavacConfigProvider;
|
||||
import com.itsaky.androidide.javac.services.compiler.ReusableCompiler;
|
||||
import com.itsaky.androidide.javac.services.partial.DiagnosticListenerImpl;
|
||||
import com.itsaky.androidide.lsp.java.parser.Parser;
|
||||
import com.itsaky.androidide.lsp.java.visitors.MethodRangeScanner;
|
||||
import com.itsaky.androidide.models.Range;
|
||||
import com.itsaky.androidide.projects.ProjectManager;
|
||||
import com.itsaky.androidide.projects.api.AndroidModule;
|
||||
import com.itsaky.androidide.projects.api.ModuleProject;
|
||||
import com.itsaky.androidide.projects.util.SourceClassTrie;
|
||||
import com.itsaky.androidide.tooling.api.IProject;
|
||||
import com.itsaky.androidide.utils.BootClasspathProvider;
|
||||
import com.itsaky.androidide.utils.Environment;
|
||||
import com.itsaky.androidide.utils.ILogger;
|
||||
import com.itsaky.androidide.utils.StopWatch;
|
||||
@@ -50,6 +56,7 @@ import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.lang.model.SourceVersion;
|
||||
import javax.lang.model.element.Name;
|
||||
import javax.tools.Diagnostic;
|
||||
import javax.tools.JavaFileObject;
|
||||
@@ -117,13 +124,25 @@ public class CompileBatch implements AutoCloseable {
|
||||
parent.fileManager, diagnosticListener, options, Collections.emptyList(), sources);
|
||||
}
|
||||
|
||||
@SuppressWarnings("Since15")
|
||||
@NonNull
|
||||
private static List<String> options(Set<Path> classPath) {
|
||||
private List<String> options(Set<Path> classPath) {
|
||||
List<String> list = new ArrayList<>();
|
||||
|
||||
// TODO Boot classpath must be different in java projects
|
||||
// Also, java modules must be enabled in java projects
|
||||
System.setProperty(
|
||||
JavacConfigProvider.PROP_ANDROIDIDE_JAVA_HOME, Environment.JAVA_HOME.getAbsolutePath());
|
||||
JavacConfigProvider.setLatestSourceVersion(SourceVersion.RELEASE_8);
|
||||
JavacConfigProvider.setLatestSupportedSourceVersion(SourceVersion.RELEASE_11);
|
||||
JavacConfigProvider.disableModules();
|
||||
|
||||
final List<String> bootClasspaths = getAndUpdateBootclasspaths();
|
||||
|
||||
Collections.addAll(list, "-bootclasspath", TextUtils.join(File.pathSeparator, bootClasspaths));
|
||||
Collections.addAll(list, "-classpath", joinPath(classPath));
|
||||
Collections.addAll(list, "-source", "11", "-target", "11");
|
||||
Collections.addAll(list, "--system", Environment.COMPILER_MODULE.getAbsolutePath());
|
||||
// Collections.addAll(list, "--system", Environment.COMPILER_MODULE.getAbsolutePath());
|
||||
Collections.addAll(list, "-proc:none");
|
||||
Collections.addAll(list, "-g");
|
||||
|
||||
@@ -139,9 +158,31 @@ public class CompileBatch implements AutoCloseable {
|
||||
"-Xlint:varargs",
|
||||
"-Xlint:static");
|
||||
|
||||
LOG.debug("Compiler options:", TextUtils.join(" ", list));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
private List<String> getAndUpdateBootclasspaths() {
|
||||
final List<String> bootClasspaths = new ArrayList<>(5);
|
||||
if (parent.module == null) {
|
||||
// Use default boot classpath if no module is available
|
||||
bootClasspaths.add(Environment.ANDROID_JAR.getAbsolutePath());
|
||||
} else if (parent.module.getType() == IProject.Type.Android) {
|
||||
bootClasspaths.addAll(
|
||||
((AndroidModule) parent.module)
|
||||
.getBootClassPaths().stream().map(File::getAbsolutePath).collect(Collectors.toSet()));
|
||||
}
|
||||
|
||||
if (BootClasspathProvider.update(bootClasspaths)) {
|
||||
this.parent.bootClasspathClasses.clear();
|
||||
this.parent.bootClasspathClasses.addAll(
|
||||
BootClasspathProvider.getTopLevelClasses(bootClasspaths));
|
||||
}
|
||||
|
||||
return bootClasspaths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Combine source path or class path entries using the system separator, for example ':' in unix
|
||||
*/
|
||||
|
||||
+7
-5
@@ -38,7 +38,8 @@ import com.itsaky.androidide.projects.api.ModuleProject;
|
||||
import com.itsaky.androidide.projects.util.Cache;
|
||||
import com.itsaky.androidide.projects.util.SourceClassTrie;
|
||||
import com.itsaky.androidide.projects.util.StringSearch;
|
||||
import com.itsaky.androidide.utils.BootstrapClassesProvider;
|
||||
import com.itsaky.androidide.utils.BootClasspathProvider;
|
||||
import com.itsaky.androidide.utils.Environment;
|
||||
import com.itsaky.androidide.utils.ILogger;
|
||||
import com.itsaky.androidide.utils.StopWatch;
|
||||
import com.sun.source.tree.CompilationUnitTree;
|
||||
@@ -79,7 +80,9 @@ public class JavaCompilerService implements CompilerProvider {
|
||||
|
||||
protected final Set<Path> classPath;
|
||||
protected final Set<String> classPathClasses;
|
||||
protected final Set<String> jdkClasses = BootstrapClassesProvider.bootstrapClasses();
|
||||
protected final Set<String> bootClasspathClasses =
|
||||
BootClasspathProvider.getTopLevelClasses(
|
||||
Collections.singleton(Environment.ANDROID_JAR.getAbsolutePath()));
|
||||
protected final List<Diagnostic<? extends JavaFileObject>> diagnostics = new ArrayList<>();
|
||||
protected final Map<JavaFileObject, Long> cachedModified = new HashMap<>();
|
||||
protected final Cache<Void, List<String>> cacheFileImports = new Cache<>();
|
||||
@@ -160,7 +163,7 @@ public class JavaCompilerService implements CompilerProvider {
|
||||
all.add(node.getQualifiedName());
|
||||
}
|
||||
all.addAll(classPathClasses);
|
||||
all.addAll(jdkClasses);
|
||||
all.addAll(bootClasspathClasses);
|
||||
return all;
|
||||
}
|
||||
|
||||
@@ -399,8 +402,7 @@ public class JavaCompilerService implements CompilerProvider {
|
||||
|
||||
final String newBody = partialRequest.contents.substring(start, end);
|
||||
final boolean reparsed =
|
||||
reparser.reparseMethod(
|
||||
info, currentMethod.second, newBody, partialRequest.contents);
|
||||
reparser.reparseMethod(info, currentMethod.second, newBody, partialRequest.contents);
|
||||
if (!reparsed) {
|
||||
LOG.error("Failed to reparse");
|
||||
recompile(request);
|
||||
|
||||
@@ -32,11 +32,12 @@ abstract class LanguageServerProvider {
|
||||
|
||||
fun server(): ILanguageServer {
|
||||
|
||||
if (Environment.COMPILER_MODULE == null) {
|
||||
val javaHome = System.getProperty("java.home")
|
||||
assertThat(javaHome).isNotEmpty()
|
||||
Environment.COMPILER_MODULE = File(javaHome)
|
||||
}
|
||||
// TODO replace with android.jar
|
||||
// if (Environment.COMPILER_MODULE == null) {
|
||||
// val javaHome = System.getProperty("java.home")
|
||||
// assertThat(javaHome).isNotEmpty()
|
||||
// Environment.COMPILER_MODULE = File(javaHome)
|
||||
// }
|
||||
|
||||
initIfNecessary()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user