diff --git a/src/main/java/com/google/devtools/build/lib/packages/Package.java b/src/main/java/com/google/devtools/build/lib/packages/Package.java
index 63bb4de6f73b27..864f9432b4b61f 100644
--- a/src/main/java/com/google/devtools/build/lib/packages/Package.java
+++ b/src/main/java/com/google/devtools/build/lib/packages/Package.java
@@ -1525,6 +1525,103 @@ public void expandAllRemainingMacros(StarlarkSemantics semantics) throws Interru
}
}
+ /**
+ * Creates and returns input files for targets that have been referenced but not explicitly
+ * declared in this package.
+ *
+ *
Precisely: If L is a label that points within the current package, and L appears in a
+ * label-typed attribute of some declaration (target or symbolic macro) D in this package, then
+ * we create an {@code InputFile} corresponding to L and return it in this map (keyed by its
+ * name), provided that all of the following are true:
+ *
+ *
+ *
The package does not otherwise declare a target for L.
+ *
D is not itself declared inside a symbolic macro.
+ *
L is not within the namespace of any symbolic macro in the package.
+ *
+ *
+ * The second condition ensures that we can know all implicitly created input files without
+ * having to evaluate any symbolic macros. The third condition ensures that we don't need to
+ * expand a symbolic macro to decide whether it defines a target that conflicts with an
+ * implicitly created input file (except for the case where the target doesn't satisfy the
+ * macro's naming requirements, in which case it would be unusable anyway).
+ */
+ private static Map createAssumedInputFiles(
+ Package pkg, TargetRecorder recorder, boolean noImplicitFileExport) {
+ Map implicitlyCreatedInputFiles = new HashMap<>();
+
+ for (Rule rule : recorder.getRules()) {
+ if (!recorder.isRuleCreatedInMacro(rule)) {
+ for (Label label : recorder.getRuleLabels(rule)) {
+ maybeCreateAssumedInputFile(
+ implicitlyCreatedInputFiles,
+ pkg,
+ recorder,
+ noImplicitFileExport,
+ label,
+ rule.getLocation());
+ }
+ }
+ }
+
+ for (MacroInstance macro : recorder.getMacroMap().values()) {
+ if (macro.getParent() == null) {
+ macro.visitExplicitAttributeLabels(
+ label ->
+ maybeCreateAssumedInputFile(
+ implicitlyCreatedInputFiles,
+ pkg,
+ recorder,
+ noImplicitFileExport,
+ label,
+ // TODO(bazel-team): We don't save a MacroInstance's location information yet,
+ // but when we do, use that here.
+ Location.BUILTIN));
+ }
+ }
+
+ return implicitlyCreatedInputFiles;
+ }
+
+ /**
+ * Adds an implicitly created input file to the given map if the label points within the current
+ * package, there is no existing target for that label, and the label does not lie within any
+ * macro's namespace.
+ */
+ private static void maybeCreateAssumedInputFile(
+ Map implicitlyCreatedInputFiles,
+ Package pkg,
+ TargetRecorder recorder,
+ boolean noImplicitFileExport,
+ Label label,
+ Location loc) {
+ String name = label.getName();
+ if (!label.getPackageIdentifier().equals(pkg.getPackageIdentifier())) {
+ return;
+ }
+ if (recorder.getTargetMap().containsKey(name)
+ || implicitlyCreatedInputFiles.containsKey(name)) {
+ return;
+ }
+ // TODO(#19922): This conflict check is quadratic complexity -- the number of candidate inputs
+ // to create times the number of macros in the package (top-level or nested). We can optimize
+ // by only checking against top-level macros, since child macro namespaces are contained
+ // within their parents' namespace. We can also use a trie data structure to zoom in on the
+ // relevant conflicting macro if it exists, since you can't be in a macro's namespace unless
+ // you suffix its name (at least, under current namespacing rules).
+ for (MacroInstance macro : recorder.getMacroMap().values()) {
+ if (TargetRecorder.nameIsWithinMacroNamespace(name, macro.getName())) {
+ return;
+ }
+ }
+
+ implicitlyCreatedInputFiles.put(
+ name,
+ noImplicitFileExport
+ ? new PrivateVisibilityInputFile(pkg, label, loc)
+ : new InputFile(pkg, label, loc));
+ }
+
@CanIgnoreReturnValue
private Builder beforeBuild(boolean discoverAssumedInputFiles) throws NoSuchPackageException {
// For correct semantics, we refuse to build a package that has declared symbolic macros that
@@ -1573,58 +1670,18 @@ private Builder beforeBuild(boolean discoverAssumedInputFiles) throws NoSuchPack
// Clear tests before discovering them again in order to keep this method idempotent -
// otherwise we may double-count tests if we're called twice due to a skyframe restart, etc.
testSuiteImplicitTestsAccumulator.clearAccumulatedTests();
-
- Map newInputFiles = new HashMap<>();
for (Rule rule : recorder.getRules()) {
- if (discoverAssumedInputFiles) {
- // Labels mentioned by a rule that refer to an unknown target in the current package are
- // assumed to be InputFiles, unless they overlap a namespace owned by a macro. Create
- // these InputFiles now. But don't do this for rules created within a symbolic macro,
- // since we don't want the evaluation of the macro to affect the semantics of whether or
- // not this target was created (i.e. all implicitly created files are knowable without
- // necessarily evaluating symbolic macros).
- if (recorder.isRuleCreatedInMacro(rule)) {
- continue;
- }
- // We use a temporary map, newInputFiles, to avoid concurrent modification to this.targets
- // while iterating (via getRules() above).
- List