| // Copyright 2025 The Bazel Authors. All rights reserved. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| package com.google.devtools.build.lib.packages; | |
| import static com.google.common.base.Preconditions.checkNotNull; | |
| import static com.google.common.base.Preconditions.checkState; | |
| import com.google.common.collect.ImmutableList; | |
| import com.google.devtools.build.lib.cmdline.Label; | |
| import com.google.devtools.build.lib.cmdline.PackageIdentifier; | |
| import com.google.devtools.build.lib.packages.TargetRecorder.MacroNamespaceViolationException; | |
| import com.google.devtools.build.lib.server.FailureDetails.FailureDetail; | |
| import java.util.OptionalLong; | |
| import javax.annotation.Nullable; | |
| /** | |
| * A package-like entity (either a full {@link Package} or a package piece) which serves as a | |
| * container of {@link Target} and {@link MacroInstance} objects and links them to the package's | |
| * metadata and declarations. | |
| * | |
| * <p>Each {@link Target} or {@link MacroInstance} object is uniquely owned one packageoid (for | |
| * targets, that's the packageoid returned by {@link Target#getPackageoid()}). In some cases, the | |
| * target or macro instance may also be referenced from other packageoids, provided that this | |
| * doesn't create a skyframe cycle. | |
| * | |
| * <p>To obtain a {@link Package} from a {@link Packageoid}, use a PackageProvider or skyframe | |
| * machinery. | |
| */ | |
| public abstract class Packageoid { | |
| /** Sentinel value for package overhead being empty. */ | |
| protected static final long PACKAGE_OVERHEAD_UNSET = -1; | |
| // ==== General package metadata fields ==== | |
| protected final Package.Metadata metadata; | |
| protected Package.Declarations declarations; | |
| // ==== Common metadata fields ==== | |
| /** | |
| * True iff this packageoid's Starlark files contained lexical or grammatical errors, or | |
| * experienced errors during evaluation, or semantic errors during the construction of any rule. | |
| * | |
| * <p>Note: A packageoid containing errors does not necessarily prevent a build; if all the rules | |
| * needed for a given build were constructed prior to the first error, the build may proceed. | |
| */ | |
| protected boolean containsErrors = false; | |
| /** | |
| * The first detailed error encountered during this packageoid's construction and evaluation, or | |
| * {@code null} if there were no such errors or all its errors lacked details. | |
| */ | |
| @Nullable protected FailureDetail failureDetail; | |
| protected long computationSteps = 0; | |
| /** | |
| * A rough approximation of the memory and general accounting costs associated with a loaded | |
| * packageoid. A value of -1 means it is unset. Stored as a long to take up less memory per pkg. | |
| */ | |
| protected long packageOverhead = PACKAGE_OVERHEAD_UNSET; | |
| // ==== Common target and macro fields ==== | |
| /** | |
| * The collection of all targets defined in this packageoid, ordered by {@link Target#getName}. | |
| * Null until the packageoid is fully initialized by its builder's {@code finishBuild()}. | |
| */ | |
| // TODO(bazel-team): Clarify what this contains when a rule and its output share the same name. | |
| @Nullable protected ImmutableList<Target> targets; | |
| /** | |
| * Returns the metadata of the package; in other words, information which is known about a package | |
| * before BUILD file evaluation has started. | |
| */ | |
| public Package.Metadata getMetadata() { | |
| return metadata; | |
| } | |
| /** | |
| * Returns the package's identifier. This is a convenience wrapper for {@link | |
| * Package.Metadata#packageIdentifier()}. | |
| */ | |
| public PackageIdentifier getPackageIdentifier() { | |
| return getMetadata().packageIdentifier(); | |
| } | |
| /** | |
| * Returns data about the package which is known after BUILD file evaluation without expanding | |
| * symbolic macros. | |
| */ | |
| public Package.Declarations getDeclarations() { | |
| return declarations; | |
| } | |
| /** | |
| * Returns the label for the package's BUILD file. | |
| * | |
| * <p>Typically, <code>getBuildFileLabel().getName().equals("BUILD")</code> -- though not | |
| * necessarily: data in a subdirectory of a test package may use a different filename to avoid | |
| * inadvertently creating a new package. | |
| */ | |
| public Label getBuildFileLabel() { | |
| return getMetadata().buildFileLabel(); | |
| } | |
| /** | |
| * Returns a short, lower-case description of this packageoid, e.g. for use in logging and error | |
| * messages. | |
| */ | |
| public abstract String getShortDescription(); | |
| /** | |
| * Returns an (immutable, ordered) list of all the targets belonging to this packageoid. Note that | |
| * if this packageoid is a package piece, this method does not search for targets in any other | |
| * package pieces. | |
| */ | |
| public ImmutableList<Target> getTargets() { | |
| return targets; | |
| } | |
| /** | |
| * Returns the target in this packageoid whose name is {@code targetName}, or {@code null} if no | |
| * such target exists. Binary searches the sorted list of targets by name. | |
| */ | |
| @Nullable | |
| public Target getTargetOrNull(String targetName) { | |
| int low = 0; | |
| int high = targets.size() - 1; | |
| while (low <= high) { | |
| int mid = (low + high) >>> 1; | |
| Target midTarget = targets.get(mid); | |
| int cmp = midTarget.getName().compareTo(targetName); | |
| if (cmp < 0) { | |
| low = mid + 1; | |
| } else if (cmp > 0) { | |
| high = mid - 1; | |
| } else { | |
| return midTarget; | |
| } | |
| } | |
| return null; | |
| } | |
| /** | |
| * Returns true if errors were encountered during evaluation of this packageoid. | |
| * | |
| * <p>If a packageoid contains errors, it may be incomplete and its contents should not be relied | |
| * upon for critical operations. All rules in such a packageoid will have their {@link | |
| * Rule#containsErrors()} flag set to true. | |
| */ | |
| public boolean containsErrors() { | |
| return containsErrors; | |
| } | |
| /** | |
| * Marks this packageoid as in error. | |
| * | |
| * <p>This method may only be called while the packageoid is being constructed. Intended only for | |
| * use by {@link Rule#reportError}, since its callers might not have access to the packageoid's | |
| * builder instance. | |
| * | |
| * @throws IllegalStateException if this packageoid has completed construction. | |
| */ | |
| void setContainsErrors() { | |
| checkState( | |
| targets == null, | |
| "setContainsErrors() can only be called while the packageoid is being constructed"); | |
| containsErrors = true; | |
| } | |
| /** | |
| * Returns the first {@link FailureDetail} describing one of the packageoid's errors, or {@code | |
| * null} if it has no errors or all its errors lack details. | |
| */ | |
| @Nullable | |
| public FailureDetail getFailureDetail() { | |
| return failureDetail; | |
| } | |
| /** | |
| * Returns the number of Starlark computation steps executed during the evaluation of this | |
| * packageoid. | |
| */ | |
| public long getComputationSteps() { | |
| return computationSteps; | |
| } | |
| /** Returns package overhead as configured by the configured {@link PackageOverheadEstimator}. */ | |
| public OptionalLong getPackageOverhead() { | |
| return packageOverhead == PACKAGE_OVERHEAD_UNSET | |
| ? OptionalLong.empty() | |
| : OptionalLong.of(packageOverhead); | |
| } | |
| /** | |
| * Throws {@link MacroNamespaceViolationException} if the given target (which must be a member of | |
| * this packageoid) violates macro naming rules. | |
| */ | |
| public abstract void checkMacroNamespaceCompliance(Target target) | |
| throws MacroNamespaceViolationException; | |
| /** | |
| * Returns the target (a member of this packagoid) whose name is "targetName". First rules are | |
| * searched, then output files, then input files. The target name must be valid, as defined by | |
| * {@code LabelValidator#validateTargetName}. | |
| * | |
| * <p>Use with care. In particular, note that {@code target.getPackageoid().getTarget("sibling")} | |
| * will succeed for all package-wide sibling targets if the packageoid is a package, but will | |
| * throw for targets belonging to a different package piece if the packageoid is a package piece. | |
| * | |
| * @throws NoSuchTargetException if the specified target was not found in this packageoid. | |
| */ | |
| public abstract Target getTarget(String targetName) throws NoSuchTargetException; | |
| protected Packageoid(Package.Metadata metadata, Package.Declarations declarations) { | |
| this.metadata = checkNotNull(metadata); | |
| this.declarations = checkNotNull(declarations); | |
| } | |
| } |