public static void updateProjectStructure()

in aswb/src/com/google/idea/blaze/android/sync/projectstructure/BlazeAndroidProjectStructureSyncer.java [102:224]


  public static void updateProjectStructure(
      Project project,
      BlazeContext context,
      ProjectViewSet projectViewSet,
      BlazeProjectData blazeProjectData,
      @Nullable BlazeProjectData projectDataFromPreviousSync,
      BlazeSyncPlugin.ModuleEditor moduleEditor,
      Module workspaceModule,
      ModifiableRootModel workspaceModifiableModel,
      boolean isAndroidWorkspace) {
    if (!isAndroidWorkspace) {
      AndroidFacetModuleCustomizer.removeAndroidFacet(workspaceModule);
      // Workspace type should always be ANDROID as long as the blaze android plugin is present.
      log.error(
          "No android workspace found for project \""
              + project.getName()
              + "\". Removing AndroidFacet from workspace module.");
      return;
    }
    AndroidFacetModuleCustomizer.createAndroidFacet(workspaceModule, false);

    BlazeAndroidSyncData syncData = blazeProjectData.getSyncState().get(BlazeAndroidSyncData.class);
    if (syncData == null) {
      if (projectDataFromPreviousSync != null) {
        // If the prior sync had android sync data, but the current one doesn't, then something
        // really bad happened. Nothing's gonna work until this is fixed.
        context.output(
            PrintOutput.error(
                "The IDE was not able to retrieve the necessary information from Blaze. Many"
                    + " android specific features may not work. Please try [Blaze > Sync > Sync"
                    + " project with BUILD files] again."));
      }
      return;
    }
    AndroidSdkPlatform androidSdkPlatform = syncData.androidSdkPlatform;
    if (androidSdkPlatform == null) {
      return;
    }

    // We need to create android resource modules for all targets excluding those that end up
    // getting associated with the workspace module.
    List<AndroidResourceModule> nonWorkspaceResourceModules =
        syncData.importResult.androidResourceModules.stream()
            .filter(m -> !WORKSPACE_RESOURCES_TARGET_KEY.equals(m.targetKey))
            .collect(Collectors.toList());

    int totalOrderEntries = 0;
    Set<File> existingRoots = Sets.newHashSet();
    ArtifactLocationDecoder artifactLocationDecoder = blazeProjectData.getArtifactLocationDecoder();
    LibraryTable libraryTable = LibraryTablesRegistrar.getInstance().getLibraryTable(project);

    for (AndroidResourceModule androidResourceModule : nonWorkspaceResourceModules) {
      String moduleName = moduleNameForAndroidModule(androidResourceModule.targetKey);
      Module module = moduleEditor.createModule(moduleName, StdModuleTypes.JAVA);

      TargetIdeInfo target = blazeProjectData.getTargetMap().get(androidResourceModule.targetKey);
      Verify.verifyNotNull(target);
      boolean isApp =
          target.kindIsOneOf(RuleTypes.ANDROID_BINARY.getKind(), RuleTypes.ANDROID_TEST.getKind());
      AndroidFacetModuleCustomizer.createAndroidFacet(module, isApp);

      AndroidIdeInfo androidIdeInfo = Verify.verifyNotNull(target.getAndroidIdeInfo());
      ArrayList<File> newRoots =
          new ArrayList<>(
              OutputArtifactResolver.resolveAll(
                  project, artifactLocationDecoder, androidResourceModule.resources));

      File moduleDirectory =
          moduleDirectoryForAndroidTarget(WorkspaceRoot.fromProject(project), target);
      File manifest =
          manifestFileForAndroidTarget(
              project, artifactLocationDecoder, androidIdeInfo, moduleDirectory);
      if (manifest != null) {
        newRoots.add(manifest);
      }

      // Multiple libraries may end up pointing to files from the same res folder. If we've already
      // added something as a root, then we skip registering it as a root in another res module.
      // This works since our dependency graph is cyclic via the workspace module.
      newRoots.removeAll(existingRoots);
      existingRoots.addAll(newRoots);

      ModifiableRootModel modifiableRootModel = moduleEditor.editModule(module);
      ResourceModuleContentRootCustomizer.setupContentRoots(modifiableRootModel, newRoots);
      modifiableRootModel.addModuleOrderEntry(workspaceModule);
      ++totalOrderEntries;

      // Add a dependency from the workspace to the resource module
      ModuleOrderEntry orderEntry = workspaceModifiableModel.addModuleOrderEntry(module);
      orderEntry.setExported(true);
      ++totalOrderEntries;

      // The workspace module depends on all libraries (including aars). All resource modules
      // depend on the workspace module, and hence transitively depend on all the libraries. As a
      // result, there is no need to explicitly attach libraries to each resource module. Doing
      // so only increases the number of order entries, which exacerbates issues like b/187413558
      // where the Kotlin plugin does calculations proportional to the # of order entries.
      if (attachAarForResourceModule.getValue()) {
        for (String libraryName : androidResourceModule.resourceLibraryKeys) {
          Library lib = libraryTable.getLibraryByName(libraryName);
          if (lib == null) {
            String message =
                String.format(
                    "Could not find library '%s' for module '%s'. Re-syncing might fix this issue.",
                    libraryName, moduleName);
            log.warn(message);
            context.output(PrintOutput.log(message));
          } else {
            modifiableRootModel.addLibraryEntry(lib);
          }
        }
      }
    }

    int allowedGenResources = projectViewSet.listItems(GeneratedAndroidResourcesSection.KEY).size();
    context.output(
        PrintOutput.log(
            String.format(
                "Android resource module count: %d, order entries: %d, generated resources: %d",
                syncData.importResult.androidResourceModules.size(),
                totalOrderEntries,
                allowedGenResources)));
  }