func updateSourcePaths()

in src/Tulsi/TulsiGeneratorConfigDocument.swift [438:554]


  func updateSourcePaths(_ callback: @escaping ([UISourcePath]) -> Void) {
    var sourcePathMap = [String: UISourcePath]()
    selectedSourcePaths.forEach() { sourcePathMap[$0.path] = $0 }
    processingTaskStarted()

    let selectedLabels = self.selectedRuleInfos.map() { $0.label }
    let optionSet = self.optionSet!
    Thread.doOnQOSUserInitiatedThread() {
      defer {
        Thread.doOnMainQueue() {
          self.sourcePaths = [UISourcePath](sourcePathMap.values)
          callback(self.sourcePaths)
          self.processingTaskFinished()
        }
      }
      let ruleEntryMap: RuleEntryMap
      do {
        let startupOptions = optionSet[.BazelBuildStartupOptionsDebug]
        let extraStartupOptions = optionSet[.ProjectGenerationBazelStartupOptions]
        let buildOptions = optionSet[.BazelBuildOptionsDebug]
        let compilationModeOption = optionSet[.ProjectGenerationCompilationMode]
        let platformConfigOption = optionSet[.ProjectGenerationPlatformConfiguration]
        let prioritizeSwiftOption = optionSet[.ProjectPrioritizesSwift]
        let use64BitWatchSimulatorOption = optionSet[.Use64BitWatchSimulator]
        ruleEntryMap = try self.infoExtractor.ruleEntriesForLabels(selectedLabels,
                                                                   startupOptions: startupOptions,
                                                                   extraStartupOptions: extraStartupOptions,
                                                                   buildOptions: buildOptions,
                                                                   compilationModeOption: compilationModeOption,
                                                                   platformConfigOption: platformConfigOption,
                                                                   prioritizeSwiftOption: prioritizeSwiftOption,
                                                                   use64BitWatchSimulatorOption: use64BitWatchSimulatorOption,
                                                                   features: self.enabledFeatures(options: optionSet))
      } catch TulsiProjectInfoExtractor.ExtractorError.ruleEntriesFailed(let info) {
        LogMessage.postError("Label resolution failed: \(info)")
        return
      } catch let e {
        LogMessage.postError("Label resolution failed. \(e)")
        return
      }

      var unresolvedLabels = Set<BuildLabel>()
      var sourceRuleEntries = [RuleEntry]()
      for label in selectedLabels {
        let ruleEntries = ruleEntryMap.ruleEntries(buildLabel: label)
        if ruleEntries.isEmpty {
          unresolvedLabels.insert(label)
        } else {
          sourceRuleEntries.append(contentsOf: ruleEntries)
        }
      }

      if !unresolvedLabels.isEmpty {
        let fmt = NSLocalizedString("Warning_LabelResolutionFailed",
                                    comment: "A non-critical failure to restore some Bazel labels when loading a document. Details are provided as %1$@.")
        LogMessage.postWarning(String(format: fmt,
                                      "Missing labels: \(unresolvedLabels.map({$0.description}))"))
      }

      var selectedRuleEntries = [RuleEntry]()
      for selectedRuleInfo in self.selectedRuleInfos {
        selectedRuleEntries.append(contentsOf: ruleEntryMap.ruleEntries(buildLabel: selectedRuleInfo.label))
      }

      var processedEntries = Set<RuleEntry>()

      let componentDelimiters = CharacterSet(charactersIn: "/:")
      func addPath(_ path: String) {
        let path = (path as NSString).deletingLastPathComponent
        if path.isEmpty { return }

        let pathComponents = path.components(separatedBy: componentDelimiters)
        var cumulativePathComponents = [String]()
        for component in pathComponents {
          cumulativePathComponents.append(component)
          let componentPath = cumulativePathComponents.joined(separator: "/")
          cumulativePathComponents = [componentPath]
          if sourcePathMap[componentPath] == nil {
            sourcePathMap[componentPath] = UISourcePath(path: componentPath)
          }
        }
      }

      func extractSourcePaths(_ ruleEntry: RuleEntry) {
        if processedEntries.contains(ruleEntry) {
          // Rules that have already been processed will already have all of their transitive
          // sources captured.
          return
        }
        processedEntries.insert(ruleEntry)
        for dep in ruleEntry.dependencies {
          guard let depRuleEntry = ruleEntryMap.ruleEntry(buildLabel: dep, depender: ruleEntry) else {
            // Some dependencies are expected to be unresolved, e.g., those that rely on implicit
            // outputs of other rules.
            continue
          }
          extractSourcePaths(depRuleEntry)
        }

        for fileInfo in ruleEntry.projectArtifacts {
          addPath(fileInfo.fullPath)
        }
      }

      var sourceTargets = [BuildLabel]()
      for entry in sourceRuleEntries {
        extractSourcePaths(entry)
        sourceTargets.append(entry.label)
      }

      let buildfiles = self.infoExtractor.extractBuildfiles(sourceTargets)
      for buildfileLabel in buildfiles {
        guard let path = buildfileLabel.asFileName else { continue }
        addPath(path)
      }
    }
  }