skyfocus: ensure re-run if analysis cache drops (e.g. due to build setting change).

This adds a subscriber for `AnalysisPhaseCompleteEvent`, which contains information when the analysis cache was dropped (and therefore rebuilt), and needs a Skyfocus re-run. With Skymeld, `AnalysisPhaseCompleteEvent` is fired from a callback (when `AnalysisOperationWatcher#handleTopLevelEntityAnalysisConcluded` empties out the set of expected top level keys), so it's when analysis is _fully_ finished.

RELNOTES:
PiperOrigin-RevId: 613529397
Change-Id: I738c0e390cd2338dbbed909272dabe2aeb3cbe91
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/BUILD b/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
index c39eab3..e149a63 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/BUILD
@@ -2483,6 +2483,7 @@
         ":precomputed_value",
         "//src/main/java/com/google/devtools/build/lib:runtime",
         "//src/main/java/com/google/devtools/build/lib/actions:artifacts",
+        "//src/main/java/com/google/devtools/build/lib/analysis:analysis_phase_complete_event",
         "//src/main/java/com/google/devtools/build/lib/collect/nestedset:artifact_nested_set_key",
         "//src/main/java/com/google/devtools/build/lib/concurrent",
         "//src/main/java/com/google/devtools/build/lib/events",
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyfocusModule.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyfocusModule.java
index 5355a86..f602348 100644
--- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyfocusModule.java
+++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyfocusModule.java
@@ -20,6 +20,7 @@
 import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Sets;
 import com.google.common.eventbus.Subscribe;
+import com.google.devtools.build.lib.analysis.AnalysisPhaseCompleteEvent;
 import com.google.devtools.build.lib.buildtool.BuildPrecompleteEvent;
 import com.google.devtools.build.lib.buildtool.BuildToolFinalizingEvent;
 import com.google.devtools.build.lib.events.Event;
@@ -231,6 +232,21 @@
     }
   }
 
+  /** Subscriber trigger for Skyfocus using information from {@link AnalysisPhaseCompleteEvent}. */
+  @SuppressWarnings("unused")
+  @Subscribe
+  public void onAnalysisPhaseComplete(AnalysisPhaseCompleteEvent event) {
+    if (!skyfocusEnabled()) {
+      return;
+    }
+
+    // If there's an active working set and the analysis cache was dropped for any reason (e.g.
+    // configuration change), we need to re-run Skyfocus.
+    if (event.wasAnalysisCacheDropped() && !env.getSkyframeExecutor().getWorkingSet().isEmpty()) {
+      pendingSkyfocusState = PendingSkyfocusState.RUN_FOCUS;
+    }
+  }
+
   /**
    * Subscriber trigger for Skyfocus using {@link BuildToolFinalizingEvent}.
    *
diff --git a/src/test/shell/integration/focus_test.sh b/src/test/shell/integration/focus_test.sh
index 4eb083d..f00007f 100755
--- a/src/test/shell/integration/focus_test.sh
+++ b/src/test/shell/integration/focus_test.sh
@@ -656,4 +656,78 @@
   expect_log "Skyfocus did not run due to an unsuccessful build."
 }
 
+function test_reanalysis_with_label_flag_change() {
+  local -r pkg=${FUNCNAME[0]}
+  mkdir -p ${pkg}
+  touch ${pkg}/in.txt
+
+  cat > ${pkg}/BUILD <<EOF
+load("//${pkg}:rules.bzl", "my_rule", "simple_rule")
+
+my_rule(name = "my_rule", src = "in.txt")
+
+simple_rule(name = "default", value = "default_val")
+
+simple_rule(name = "command_line", value = "command_line_val")
+
+label_flag(
+    name = "my_label_build_setting",
+    build_setting_default = ":default"
+)
+EOF
+
+  cat > ${pkg}/rules.bzl <<EOF
+def _impl(ctx):
+    _setting = "value=" + ctx.attr._label_flag[SimpleRuleInfo].value
+
+    out = ctx.actions.declare_file(ctx.attr.name + ".txt")
+    ctx.actions.run_shell(
+        inputs = [ctx.file.src],
+        outputs = [out],
+        command = " ".join(["cat", ctx.file.src.path, ">", out.path, "&&", "echo", _setting, ">>", out.path]),
+    )
+
+    return [DefaultInfo(files = depset([out]))]
+
+my_rule = rule(
+    implementation = _impl,
+    attrs = {
+        "src": attr.label(allow_single_file = True),
+        "_label_flag": attr.label(default = Label("//${pkg}:my_label_build_setting")),
+    },
+)
+
+SimpleRuleInfo = provider(fields = ['value'])
+
+def _simple_rule_impl(ctx):
+    return [SimpleRuleInfo(value = ctx.attr.value)]
+
+simple_rule = rule(
+    implementation = _simple_rule_impl,
+    attrs = {
+        "value": attr.string(),
+    },
+)
+EOF
+
+  out=$(bazel info "${PRODUCT_NAME}-bin")/${pkg}/my_rule.txt
+  bazel build //${pkg}:my_rule --experimental_working_set=${pkg}/in.txt \
+    || fail "expected build to succeed"
+
+  assert_contains "value=default_val" ${out}
+
+  # Change the configuration dep.
+  bazel build //${pkg}:my_rule --//${pkg}:my_label_build_setting=//${pkg}:command_line &> "$TEST_log" \
+    || fail "expected build to succeed"
+
+  # Analysis cache should be dropped due to the changed configuration.
+  expect_log "WARNING: Build option --//${pkg}:my_label_build_setting has changed, discarding analysis cache"
+
+  # Skyfocus should rerun due to the dropped analysis cache.
+  expect_log "Focusing on .\+ roots, .\+ leafs"
+
+  # New result.
+  assert_contains "value=command_line_val" ${out}
+}
+
 run_suite "Tests for Skyfocus"