diff --git a/base/src/com/google/idea/blaze/base/qsync/ProjectUpdater.java b/base/src/com/google/idea/blaze/base/qsync/ProjectUpdater.java index a7026e015bc..f3918593128 100644 --- a/base/src/com/google/idea/blaze/base/qsync/ProjectUpdater.java +++ b/base/src/com/google/idea/blaze/base/qsync/ProjectUpdater.java @@ -15,10 +15,6 @@ */ package com.google.idea.blaze.base.qsync; -import static com.google.common.collect.ImmutableList.toImmutableList; -import static com.google.common.collect.ImmutableSet.toImmutableSet; -import static java.util.Arrays.stream; - import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -37,7 +33,6 @@ import com.google.idea.blaze.qsync.project.ProjectPath; import com.google.idea.blaze.qsync.project.ProjectProto; import com.google.idea.blaze.qsync.project.ProjectProto.LibrarySource; -import com.google.idea.common.util.Transactions; import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider; import com.intellij.openapi.externalSystem.service.project.ProjectDataManager; import com.intellij.openapi.module.Module; @@ -54,20 +49,30 @@ import com.intellij.openapi.roots.libraries.Library; import com.intellij.openapi.roots.libraries.Library.ModifiableModel; import com.intellij.openapi.vfs.VfsUtil; +import org.jetbrains.jps.model.java.JavaSourceRootProperties; +import org.jetbrains.jps.model.java.JavaSourceRootType; +import org.jetbrains.jps.model.java.JpsJavaExtensionService; + import java.io.File; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.AbstractMap; import java.util.List; import java.util.Set; import java.util.function.Function; -import org.jetbrains.jps.model.java.JavaSourceRootProperties; -import org.jetbrains.jps.model.java.JavaSourceRootType; -import org.jetbrains.jps.model.java.JpsJavaExtensionService; -/** An object that monitors the build graph and applies the changes to the project structure. */ +import static com.google.common.collect.ImmutableList.toImmutableList; +import static com.google.common.collect.ImmutableSet.toImmutableSet; +import static java.util.Arrays.stream; + +/** + * An object that monitors the build graph and applies the changes to the project structure. + */ public class ProjectUpdater implements QuerySyncProjectListener { - /** Entry point for instantiating {@link ProjectUpdater}. */ + /** + * Entry point for instantiating {@link ProjectUpdater}. + */ public static class Provider implements QuerySyncProjectListenerProvider { @Override public QuerySyncProjectListener createListener(QuerySyncProject querySyncProject) { @@ -116,14 +121,16 @@ public void onNewProjectSnapshot(Context context, QuerySyncProjectSnapshot gr private void updateProjectModel(ProjectProto.Project spec, Context context) { File imlDirectory = new File(BlazeDataStorage.getProjectDataDir(importSettings), "modules"); - Transactions.submitWriteActionTransactionAndWait( + ProjectUpdaterThreadingUtils.Companion.performWriteAction(() -> { + for (BlazeQuerySyncPlugin syncPlugin : BlazeQuerySyncPlugin.EP_NAME.getExtensions()) { + syncPlugin.updateProjectSettingsForQuerySync(project, context, projectViewSet); + } + }); + ProjectUpdaterThreadingUtils.Companion.readWriteAction( () -> { IdeModifiableModelsProvider models = ProjectDataManager.getInstance().createModifiableModelsProvider(project); - for (BlazeQuerySyncPlugin syncPlugin : BlazeQuerySyncPlugin.EP_NAME.getExtensions()) { - syncPlugin.updateProjectSettingsForQuerySync(project, context, projectViewSet); - } int removedLibCount = removeUnusedLibraries(models, spec.getLibraryList()); if (removedLibCount > 0) { context.output(PrintOutput.output("Removed " + removedLibCount + " libs")); @@ -135,81 +142,89 @@ private void updateProjectModel(ProjectProto.Project spec, Context context) { } ImmutableMap libMap = libMapBuilder.buildOrThrow(); - for (ProjectProto.Module moduleSpec : spec.getModulesList()) { - Module module = - models.newModule( - imlDirectory.toPath().resolve(moduleSpec.getName() + ".iml").toString(), - mapModuleType(moduleSpec.getType()).getId()); + List> modules = + spec.getModulesList().stream().map(moduleSpec -> { + Module module = + models.newModule( + imlDirectory.toPath().resolve(moduleSpec.getName() + ".iml").toString(), + mapModuleType(moduleSpec.getType()).getId()); - ModifiableRootModel roots = models.getModifiableRootModel(module); - ImmutableList existingLibraryOrderEntries = - stream(roots.getOrderEntries()) - .filter(it -> it instanceof LibraryOrderEntry) - .collect(toImmutableList()); - for (OrderEntry entry : existingLibraryOrderEntries) { - roots.removeOrderEntry(entry); - } - // TODO: should this be encapsulated in ProjectProto.Module? - roots.inheritSdk(); - - // TODO instead of removing all content entries and re-adding, we should calculate the - // diff. - for (ContentEntry entry : roots.getContentEntries()) { - roots.removeContentEntry(entry); - } - for (ProjectProto.ContentEntry ceSpec : moduleSpec.getContentEntriesList()) { - ProjectPath projectPath = ProjectPath.create(ceSpec.getRoot()); + ModifiableRootModel roots = models.getModifiableRootModel(module); + ImmutableList existingLibraryOrderEntries = + stream(roots.getOrderEntries()) + .filter(it -> it instanceof LibraryOrderEntry) + .collect(toImmutableList()); + for (OrderEntry entry : existingLibraryOrderEntries) { + roots.removeOrderEntry(entry); + } + // TODO: should this be encapsulated in ProjectProto.Module? + roots.inheritSdk(); - ContentEntry contentEntry = - roots.addContentEntry( - UrlUtil.pathToUrl(projectPathResolver.resolve(projectPath).toString())); - for (ProjectProto.SourceFolder sfSpec : ceSpec.getSourcesList()) { - ProjectPath sourceFolderProjectPath = ProjectPath.create(sfSpec.getProjectPath()); + // TODO instead of removing all content entries and re-adding, we should calculate the + // diff. + for (ContentEntry entry : roots.getContentEntries()) { + roots.removeContentEntry(entry); + } + for (ProjectProto.ContentEntry ceSpec : moduleSpec.getContentEntriesList()) { + ProjectPath projectPath = ProjectPath.create(ceSpec.getRoot()); - JavaSourceRootProperties properties = - JpsJavaExtensionService.getInstance() - .createSourceRootProperties( - sfSpec.getPackagePrefix(), sfSpec.getIsGenerated()); - JavaSourceRootType rootType = - sfSpec.getIsTest() ? JavaSourceRootType.TEST_SOURCE : JavaSourceRootType.SOURCE; - String url = - UrlUtil.pathToUrl( - projectPathResolver.resolve(sourceFolderProjectPath).toString(), - sourceFolderProjectPath.innerJarPath()); - SourceFolder unused = contentEntry.addSourceFolder(url, rootType, properties); - } - for (String exclude : ceSpec.getExcludesList()) { - contentEntry.addExcludeFolder( - UrlUtil.pathToIdeaDirectoryUrl(workspaceRoot.absolutePathFor(exclude))); - } - } + ContentEntry contentEntry = + roots.addContentEntry( + UrlUtil.pathToUrl(projectPathResolver.resolve(projectPath).toString())); + for (ProjectProto.SourceFolder sfSpec : ceSpec.getSourcesList()) { + ProjectPath sourceFolderProjectPath = ProjectPath.create(sfSpec.getProjectPath()); - for (String lib : moduleSpec.getLibraryNameList()) { - Library library = libMap.get(lib); - if (library == null) { - throw new IllegalStateException( - "Module refers to library " + lib + " not present in the project spec"); - } - LibraryOrderEntry entry = roots.addLibraryEntry(library); - // TODO should this stuff be specified by the Module proto too? - entry.setScope(DependencyScope.COMPILE); - entry.setExported(false); - } + JavaSourceRootProperties properties = + JpsJavaExtensionService.getInstance() + .createSourceRootProperties( + sfSpec.getPackagePrefix(), sfSpec.getIsGenerated()); + JavaSourceRootType rootType = + sfSpec.getIsTest() ? JavaSourceRootType.TEST_SOURCE : JavaSourceRootType.SOURCE; + String url = + UrlUtil.pathToUrl( + projectPathResolver.resolve(sourceFolderProjectPath).toString(), + sourceFolderProjectPath.innerJarPath()); + SourceFolder unused = contentEntry.addSourceFolder(url, rootType, properties); + } + for (String exclude : ceSpec.getExcludesList()) { + contentEntry.addExcludeFolder( + UrlUtil.pathToIdeaDirectoryUrl(workspaceRoot.absolutePathFor(exclude))); + } + } - WorkspaceLanguageSettings workspaceLanguageSettings = - LanguageSupport.createWorkspaceLanguageSettings(projectViewSet); + for (String lib : moduleSpec.getLibraryNameList()) { + Library library = libMap.get(lib); + if (library == null) { + throw new IllegalStateException( + "Module refers to library " + lib + " not present in the project spec"); + } + LibraryOrderEntry entry = roots.addLibraryEntry(library); + // TODO should this stuff be specified by the Module proto too? + entry.setScope(DependencyScope.COMPILE); + entry.setExported(false); + } + return new AbstractMap.SimpleImmutableEntry<>(module, moduleSpec); + }).toList(); + return new AbstractMap.SimpleImmutableEntry<>(models, modules); + }, + readValue -> { + IdeModifiableModelsProvider models = readValue.getKey(); + WorkspaceLanguageSettings workspaceLanguageSettings = + LanguageSupport.createWorkspaceLanguageSettings(projectViewSet); - for (BlazeQuerySyncPlugin syncPlugin : BlazeQuerySyncPlugin.EP_NAME.getExtensions()) { - // TODO update ProjectProto.Module and updateProjectStructure() to allow a more - // suitable - // data type to be passed in here instead of androidResourceDirectories and - // androidSourcePackages + for (BlazeQuerySyncPlugin syncPlugin : BlazeQuerySyncPlugin.EP_NAME.getExtensions()) { + // TODO update ProjectProto.Module and updateProjectStructure() to allow a more + // suitable + // data type to be passed in here instead of androidResourceDirectories and + // androidSourcePackages + for (AbstractMap.SimpleImmutableEntry moduleEntry : readValue.getValue()) { + ProjectProto.Module moduleSpec = moduleEntry.getValue(); syncPlugin.updateProjectStructureForQuerySync( project, context, models, workspaceRoot, - module, + moduleEntry.getKey(), ImmutableSet.copyOf(moduleSpec.getAndroidResourceDirectoriesList()), ImmutableSet.builder() .addAll(moduleSpec.getAndroidSourcePackagesList()) @@ -217,8 +232,8 @@ private void updateProjectModel(ProjectProto.Project spec, Context context) { .build(), workspaceLanguageSettings); } - models.commit(); } + models.commit(); }); } diff --git a/base/src/com/google/idea/blaze/base/qsync/ProjectUpdaterThreadingUtils.kt b/base/src/com/google/idea/blaze/base/qsync/ProjectUpdaterThreadingUtils.kt new file mode 100644 index 00000000000..a7e28d0c50a --- /dev/null +++ b/base/src/com/google/idea/blaze/base/qsync/ProjectUpdaterThreadingUtils.kt @@ -0,0 +1,33 @@ +package com.google.idea.blaze.base.qsync + +import com.intellij.openapi.application.readAndWriteAction +import com.intellij.openapi.application.writeAction +import com.intellij.openapi.diagnostic.Logger +import kotlinx.coroutines.runBlocking +import java.util.concurrent.Callable +import java.util.function.Consumer + +class ProjectUpdaterThreadingUtils { + companion object { + val logger = Logger.getInstance(ProjectUpdaterThreadingUtils::class.java) + fun readWriteAction(readPart: Callable, commit: Consumer) { + runBlocking { + readAndWriteAction { + logger.info("Starting read operation") + val ret = readPart.call(); + writeAction { + commit.accept(ret) + } + } + } + } + + fun performWriteAction(action: Runnable) { + runBlocking { + writeAction { + action.run() + } + } + } + } +} \ No newline at end of file diff --git a/querysync/java/com/google/idea/blaze/qsync/BUILD.bazel b/querysync/java/com/google/idea/blaze/qsync/BUILD.bazel index 5abe7b6d8a9..7a66ba7f5ac 100644 --- a/querysync/java/com/google/idea/blaze/qsync/BUILD.bazel +++ b/querysync/java/com/google/idea/blaze/qsync/BUILD.bazel @@ -2,11 +2,11 @@ java_library( name = "qsync", srcs = glob(["*.java"]), visibility = [ - "//querysync:__subpackages__", "//javatests/com/google/devtools/intellij/blaze/plugin/aswb:__pkg__", + "//querysync:__subpackages__", ], deps = [ - "@jsr305_annotations//jar", + "//base:blaze_executor", "//querysync/java/com/google/idea/blaze/qsync/artifacts", "//querysync/java/com/google/idea/blaze/qsync/cc", "//querysync/java/com/google/idea/blaze/qsync/deps", @@ -21,7 +21,8 @@ java_library( "//shared:vcs", "//third_party/java/auto_value", "@com_google_guava_guava//jar", - "@protobuf//:protobuf_java", "@error_prone_annotations//jar", + "@jsr305_annotations//jar", + "@protobuf//:protobuf_java", ], ) diff --git a/querysync/java/com/google/idea/blaze/qsync/SnapshotHolder.java b/querysync/java/com/google/idea/blaze/qsync/SnapshotHolder.java index 37b1f0bb485..8b2a1aad3f0 100644 --- a/querysync/java/com/google/idea/blaze/qsync/SnapshotHolder.java +++ b/querysync/java/com/google/idea/blaze/qsync/SnapshotHolder.java @@ -19,6 +19,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.io.ByteSource; +import com.google.idea.blaze.base.async.executor.BlazeExecutor; import com.google.idea.blaze.common.Context; import com.google.idea.blaze.exception.BuildException; import java.util.List; @@ -52,7 +53,10 @@ public void setCurrent(Context context, QuerySyncProjectSnapshot newInstance) listeners = ImmutableList.copyOf(this.listeners); } for (QuerySyncProjectListener l : listeners) { - l.onNewProjectSnapshot(context, newInstance); + BlazeExecutor.getInstance().submit(() -> { + l.onNewProjectSnapshot(context, newInstance); + return null; + }); } }