diff --git a/aswb/src/com/google/idea/blaze/android/projectsystem/BlazeModuleSystem.java b/aswb/src/com/google/idea/blaze/android/projectsystem/BlazeModuleSystem.java index 4c29bc47c92..e8325a68572 100644 --- a/aswb/src/com/google/idea/blaze/android/projectsystem/BlazeModuleSystem.java +++ b/aswb/src/com/google/idea/blaze/android/projectsystem/BlazeModuleSystem.java @@ -15,8 +15,6 @@ */ package com.google.idea.blaze.android.projectsystem; -import static com.google.common.collect.ImmutableList.toImmutableList; - import com.android.ide.common.util.PathString; import com.android.projectmodel.ExternalAndroidLibrary; import com.android.projectmodel.ExternalLibraryImpl; @@ -26,6 +24,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.idea.blaze.android.libraries.UnpackedAars; +import com.google.idea.blaze.android.manifest.ManifestParser; import com.google.idea.blaze.android.sync.model.AarLibrary; import com.google.idea.blaze.android.sync.model.AndroidResourceModuleRegistry; import com.google.idea.blaze.android.sync.model.BlazeAndroidSyncData; @@ -43,15 +42,20 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.Nullable; + import java.io.File; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Collection; import java.util.Collections; import java.util.stream.Stream; -import org.jetbrains.annotations.Nullable; + +import static com.google.common.collect.ImmutableList.toImmutableList; /** Blaze implementation of {@link AndroidModuleSystem}. */ public class BlazeModuleSystem extends BlazeModuleSystemBase { @@ -162,14 +166,26 @@ static ExternalAndroidLibrary toExternalLibrary( library.aarArtifact)); return null; } + File resFolder = unpackedAars.getResourceDirectory(decoder, library); PathString resFolderPathString = resFolder == null ? null : new PathString(resFolder); + PathString manifest = resFolderPathString == null + ? null + : resFolderPathString.getParentOrRoot().resolve("AndroidManifest.xml"); + String resourcePackage = library.resourcePackage; + if ((resourcePackage == null || resourcePackage == "") && manifest != null) { + try (InputStream is = new FileInputStream(manifest.toFile())) { + ManifestParser.ParsedManifest parsedManifest = ManifestParser.parseManifestFromInputStream(is); + resourcePackage = parsedManifest.packageName; + } catch (IOException ioe) { + logger.warn( + String.format("Could not parse package from manifest in library %s", aarFile.getName())); + return null; + } + } return new ExternalLibraryImpl(library.key.toString()) .withLocation(new PathString(aarFile)) - .withManifestFile( - resFolderPathString == null - ? null - : resFolderPathString.getParentOrRoot().resolve("AndroidManifest.xml")) + .withManifestFile(manifest) .withResFolder( resFolderPathString == null ? null @@ -178,7 +194,7 @@ static ExternalAndroidLibrary toExternalLibrary( resFolderPathString == null ? null : resFolderPathString.getParentOrRoot().resolve("R.txt")) - .withPackageName(library.resourcePackage); + .withPackageName(resourcePackage); } @Override diff --git a/aswb/src/com/google/idea/blaze/android/projectsystem/BlazeModuleSystemBase.java b/aswb/src/com/google/idea/blaze/android/projectsystem/BlazeModuleSystemBase.java index c90ede3f88a..8938c3d9305 100644 --- a/aswb/src/com/google/idea/blaze/android/projectsystem/BlazeModuleSystemBase.java +++ b/aswb/src/com/google/idea/blaze/android/projectsystem/BlazeModuleSystemBase.java @@ -205,6 +205,10 @@ public void registerDependency(GradleCoordinate coordinate, DependencyType type) } } + public void clearCache() { + classFileFinder.clearCache(); + } + @Nullable @Override public GradleCoordinate getRegisteredDependency(GradleCoordinate coordinate) { diff --git a/aswb/src/com/google/idea/blaze/android/projectsystem/RenderJarClassFileFinder.java b/aswb/src/com/google/idea/blaze/android/projectsystem/RenderJarClassFileFinder.java index 617265883a6..7d337b6d978 100644 --- a/aswb/src/com/google/idea/blaze/android/projectsystem/RenderJarClassFileFinder.java +++ b/aswb/src/com/google/idea/blaze/android/projectsystem/RenderJarClassFileFinder.java @@ -15,8 +15,6 @@ */ package com.google.idea.blaze.android.projectsystem; -import static java.util.stream.Collectors.joining; - import com.android.tools.idea.projectsystem.ClassFileFinder; import com.android.tools.idea.projectsystem.ClassFileFinderUtil; import com.google.common.annotations.VisibleForTesting; @@ -25,6 +23,7 @@ import com.google.idea.blaze.android.libraries.RenderJarCache; import com.google.idea.blaze.android.sync.model.AndroidResourceModule; import com.google.idea.blaze.android.sync.model.AndroidResourceModuleRegistry; +import com.google.idea.blaze.android.sync.model.idea.BlazeClassJarProvider; import com.google.idea.blaze.android.targetmaps.TargetToBinaryMap; import com.google.idea.blaze.base.ideinfo.TargetIdeInfo; import com.google.idea.blaze.base.ideinfo.TargetKey; @@ -34,6 +33,7 @@ import com.google.idea.blaze.base.sync.BlazeSyncModificationTracker; import com.google.idea.blaze.base.sync.data.BlazeDataStorage; import com.google.idea.blaze.base.sync.data.BlazeProjectDataManager; +import com.google.idea.blaze.base.sync.projectstructure.ModuleFinder; import com.google.idea.common.experiments.BoolExperiment; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.diagnostic.Logger; @@ -42,9 +42,15 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.JarFileSystem; import com.intellij.openapi.vfs.VirtualFile; +import org.jetbrains.annotations.Nullable; + import java.io.File; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.regex.Pattern; -import org.jetbrains.annotations.Nullable; + +import static java.util.stream.Collectors.joining; /** * A {@link ClassFileFinder} that uses deploy JAR like artifacts (called render jar henceforth) for @@ -62,6 +68,8 @@ * *

NOTE: Blaze targets that constitutes the resource module will be called "resource target(s)" * in comments below. + * + * TODO: The role of this class has expanded beyond just render jar resolution. Should rename it. */ public class RenderJarClassFileFinder implements ClassFileFinder { /** Experiment to control whether class file finding from render jars should be enabled. */ @@ -74,7 +82,7 @@ public class RenderJarClassFileFinder implements ClassFileFinder { */ @VisibleForTesting static final BoolExperiment resolveResourceClasses = - new BoolExperiment("aswb.resolve.resources.render.jar", false); + new BoolExperiment("aswb.resolve.resources.render.jar", true); // needed for previews private static final Logger log = Logger.getInstance(RenderJarClassFileFinder.class); @@ -86,6 +94,8 @@ public class RenderJarClassFileFinder implements ClassFileFinder { private final Module module; private final Project project; + private final BlazeClassJarProvider blazeClassJarProvider; + // tracks the binary targets that depend resource targets // will be recalculated after every sync private ImmutableSet binaryTargets = ImmutableSet.of(); @@ -98,9 +108,12 @@ public class RenderJarClassFileFinder implements ClassFileFinder { // true if the current module is the .workspace Module private final boolean isWorkspaceModule; + private Map moduleCache = new HashMap(); + public RenderJarClassFileFinder(Module module) { this.module = module; this.project = module.getProject(); + this.blazeClassJarProvider = new BlazeClassJarProvider(this.project); this.isWorkspaceModule = BlazeDataStorage.WORKSPACE_MODULE_NAME.equals(module.getName()); } @@ -169,14 +182,43 @@ public VirtualFile findClass(String fqcn) { for (TargetKey binaryTarget : binaryTargets) { VirtualFile classFile = getClassFromRenderResolveJar(projectData, fqcn, binaryTarget); if (classFile != null) { + log.warn(String.format("Found class %s in target %s", fqcn, binaryTarget.toString())); return classFile; } } + // TODO: look into BlazeLightResourceClassService? + + VirtualFile moduleClass = findFQCNInModule(fqcn, this.module); + if (moduleClass == null) { + moduleClass = findFQCNInModule(fqcn, null); + } + if (moduleClass != null) { + return moduleClass; + } + log.warn(String.format("Could not find class `%1$s` (module: `%2$s`)", fqcn, module.getName())); return null; } + public synchronized void clearCache() { + log.debug("clearing cache"); + moduleCache.clear(); + } + + private synchronized VirtualFile findFQCNInModule(String fqcn, @Nullable Module module) { + if (module == null) { + module = ModuleFinder.getInstance(this.project) + .findModuleByName(BlazeDataStorage.WORKSPACE_MODULE_NAME); + } + ModuleCache cache = moduleCache.get(module.getName()); + if (cache == null) { + cache = new ModuleCache(module); + moduleCache.put(module.getName(), cache); + } + return cache.searchForFQCNInModule(fqcn); + } + @VisibleForTesting static boolean isResourceClass(String fqcn) { return RESOURCE_CLASS_NAME.matcher(fqcn).matches(); @@ -268,4 +310,43 @@ private static VirtualFile getJarRootForLocalFile(VirtualFile file) { public static boolean isEnabled() { return enabled.getValue(); } + + private class ModuleCache { + private final List moduleLibraries; + private Map packageJarHint = new HashMap(); + + private ModuleCache(Module module) { + moduleLibraries = blazeClassJarProvider.getModuleExternalLibraries(module); + } + + @Nullable + private VirtualFile searchForFQCNInModule(String fqcn) { + String pkg = null; + int pkgIdx = fqcn.lastIndexOf('.'); + if (pkgIdx != -1) { + pkg = fqcn.substring(0, pkgIdx); + } + VirtualFile hintJarVF = pkg == null ? null : packageJarHint.get(pkg); + if (hintJarVF != null) { + VirtualFile foundClass = findClassInJar(hintJarVF, fqcn); + if (foundClass != null) { + return foundClass; + } + } + for (File jar : moduleLibraries) { + VirtualFile jarVF = VirtualFileSystemProvider.getInstance().getSystem().findFileByIoFile(jar); + if (jarVF == null) { + continue; + } + VirtualFile foundClass = findClassInJar(jarVF, fqcn); + if (foundClass != null) { + if (pkg != null) { + packageJarHint.put(pkg, jarVF); + } + return foundClass; + } + } + return null; + } + } } diff --git a/aswb/src/com/google/idea/blaze/android/resources/BlazeLightResourceClassService.java b/aswb/src/com/google/idea/blaze/android/resources/BlazeLightResourceClassService.java index 90697501184..c01b98a0d0c 100644 --- a/aswb/src/com/google/idea/blaze/android/resources/BlazeLightResourceClassService.java +++ b/aswb/src/com/google/idea/blaze/android/resources/BlazeLightResourceClassService.java @@ -15,8 +15,6 @@ */ package com.google.idea.blaze.android.resources; -import static com.google.common.collect.ImmutableSet.toImmutableSet; - import com.android.tools.idea.projectsystem.LightResourceClassService; import com.android.tools.idea.res.AndroidLightPackage; import com.google.common.annotations.VisibleForTesting; @@ -35,12 +33,15 @@ import com.intellij.psi.PsiManager; import com.intellij.psi.PsiPackage; import com.intellij.psi.search.GlobalSearchScope; +import org.jetbrains.android.facet.AndroidFacet; +import org.jetbrains.annotations.Nullable; + import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Set; -import org.jetbrains.android.facet.AndroidFacet; -import org.jetbrains.annotations.Nullable; + +import static com.google.common.collect.ImmutableSet.toImmutableSet; /** Implementation of {@link LightResourceClassService} set up at Blaze sync time. */ public class BlazeLightResourceClassService implements LightResourceClassService { diff --git a/aswb/src/com/google/idea/blaze/android/sync/importer/BlazeAndroidWorkspaceImporter.java b/aswb/src/com/google/idea/blaze/android/sync/importer/BlazeAndroidWorkspaceImporter.java index bc63f3e003d..62a41bf8ba5 100644 --- a/aswb/src/com/google/idea/blaze/android/sync/importer/BlazeAndroidWorkspaceImporter.java +++ b/aswb/src/com/google/idea/blaze/android/sync/importer/BlazeAndroidWorkspaceImporter.java @@ -469,30 +469,6 @@ private String createAarLibrary(@NotNull TargetIdeInfo target) { String libraryKey = LibraryKey.libraryNameFromArtifactLocation(target.getAndroidAarIdeInfo().getAar()); - - //TODO: Should not be hardcoded, this is temp workaround, the package should be inferred properly, possibly from the manifest.xml? - if(target.toString().equals("@maven//:androidx_lifecycle_lifecycle_runtime")) { - resourcePackage = "androidx.lifecycle.runtime"; - } - if(target.toString().equals("@maven//:androidx_savedstate_savedstate")) { - resourcePackage = "androidx.savedstate"; - } - if(target.toString().equals("@maven//:androidx_lifecycle_lifecycle_viewmodel")) { - resourcePackage = "androidx.lifecycle.viewmodel"; - } - if(target.toString().equals("@maven//:androidx_compose_ui_ui")) { - resourcePackage = "androidx.compose.ui"; - } - if(target.toString().equals("@maven//:androidx_core_core")) { - resourcePackage = "androidx.core"; - } - if(target.toString().equals("@maven//:androidx_constraintlayout_constraintlayout")) { - resourcePackage = "androidx.constraintlayout"; - } - if(target.toString().equals("@maven//:androidx_appcompat_appcompat")) { - resourcePackage = "androidx.appcompat"; - } - if (!aarLibraries.containsKey(libraryKey)) { // aar_import should only have one jar (a merged jar from the AAR's jars). LibraryArtifact firstJar = target.getJavaIdeInfo().getJars().iterator().next(); diff --git a/aswb/src/com/google/idea/blaze/android/sync/model/idea/BlazeClassJarProvider.java b/aswb/src/com/google/idea/blaze/android/sync/model/idea/BlazeClassJarProvider.java index 191629e2137..51ae1109e39 100644 --- a/aswb/src/com/google/idea/blaze/android/sync/model/idea/BlazeClassJarProvider.java +++ b/aswb/src/com/google/idea/blaze/android/sync/model/idea/BlazeClassJarProvider.java @@ -20,6 +20,7 @@ import com.android.tools.idea.model.ClassJarProvider; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; import com.google.idea.blaze.android.libraries.RenderJarCache; import com.google.idea.blaze.android.sync.model.AndroidResourceModuleRegistry; import com.google.idea.blaze.android.targetmaps.TargetToBinaryMap; @@ -194,7 +195,7 @@ public static boolean testIsClassFileOutOfDate( } List getAllExternalLibraires(TargetMap targetMap, ArtifactLocationDecoder decoder) { - ImmutableList.Builder results = ImmutableList.builder(); + ImmutableSet.Builder infos = ImmutableSet.builder(); for (TargetIdeInfo target : targetMap.targets()) { for (TargetKey dependencyTargetKey : TransitiveDependencyMap.getInstance(project).getTransitiveDependencies(target.getKey())) { @@ -206,19 +207,28 @@ List getAllExternalLibraires(TargetMap targetMap, ArtifactLocationDecoder // Add all import jars as external libraries. JavaIdeInfo javaIdeInfo = dependencyTarget.getJavaIdeInfo(); if (javaIdeInfo != null) { - for (LibraryArtifact jar : javaIdeInfo.getJars()) { - ArtifactLocation classJar = jar.getClassJar(); - if (classJar != null) { - results.add( - Preconditions.checkNotNull( - OutputArtifactResolver.resolve(project, decoder, classJar), - "Fail to find file %s", - classJar.getRelativePath())); - } - } + infos.add(javaIdeInfo); } } + JavaIdeInfo javaIdeInfo = target.getJavaIdeInfo(); + if (javaIdeInfo != null) { + infos.add(javaIdeInfo); + } } + ImmutableList.Builder results = ImmutableList.builder(); + for (JavaIdeInfo javaIdeInfo : infos.build()) { + for (LibraryArtifact jar : javaIdeInfo.getJars()) { + ArtifactLocation classJar = jar.getClassJar(); + if (classJar != null) { + results.add( + Preconditions.checkNotNull( + OutputArtifactResolver.resolve(project, decoder, classJar), + "Fail to find file %s", + classJar.getRelativePath())); + } + } + } + return results.build(); } } diff --git a/aswb/src/com/google/idea/blaze/android/sync/projectstructure/BlazeAndroidProjectStructureSyncer.java b/aswb/src/com/google/idea/blaze/android/sync/projectstructure/BlazeAndroidProjectStructureSyncer.java index 2382d6b168e..a73e6d346e6 100644 --- a/aswb/src/com/google/idea/blaze/android/sync/projectstructure/BlazeAndroidProjectStructureSyncer.java +++ b/aswb/src/com/google/idea/blaze/android/sync/projectstructure/BlazeAndroidProjectStructureSyncer.java @@ -33,6 +33,7 @@ import com.google.common.util.concurrent.MoreExecutors; import com.google.idea.blaze.android.manifest.ManifestParser; import com.google.idea.blaze.android.manifest.ParsedManifestService; +import com.google.idea.blaze.android.projectsystem.BlazeModuleSystem; import com.google.idea.blaze.android.projectview.GeneratedAndroidResourcesSection; import com.google.idea.blaze.android.resources.BlazeLightResourceClassService; import com.google.idea.blaze.android.sync.importer.BlazeAndroidWorkspaceImporter; @@ -358,6 +359,7 @@ private static void updateInMemoryState( configAndroidJava8Libs); rClassBuilder.addRClass(modulePackage, module); sourcePackages.remove(modulePackage); + BlazeModuleSystem.getInstance(module).clearCache(); } rClassBuilder.addWorkspacePackages(sourcePackages);