/* * Copyright 2014-present Facebook, Inc. * * 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.facebook.buck.rules; import com.facebook.buck.io.ProjectFilesystem; import com.facebook.buck.model.BuildTarget; import com.facebook.buck.model.Flavor; import com.facebook.buck.util.HumanReadableException; import com.facebook.buck.versions.Version; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; import com.google.common.hash.HashCode; import java.nio.file.Path; import java.util.Optional; import java.util.Set; import java.util.stream.Stream; /** * A {@link TargetNode} represents a node in the target graph which is created by the {@link * com.facebook.buck.parser.Parser} as a result of parsing BUCK files in a project. It is * responsible for processing the raw (python) inputs of a build rule, and gathering any build * targets and paths referenced from those inputs. */ public class TargetNode<T, U extends Description<T>> implements Comparable<TargetNode<?, ?>>, ObeysVisibility { private final TargetNodeFactory factory; private final HashCode rawInputsHashCode; private final ProjectFilesystem filesystem; private final BuildTarget buildTarget; private final CellPathResolver cellNames; private final U description; private final T constructorArg; private final ImmutableSet<Path> inputs; private final ImmutableSet<BuildTarget> declaredDeps; private final ImmutableSet<BuildTarget> extraDeps; private final ImmutableSet<BuildTarget> targetGraphOnlyDeps; private final VisibilityChecker visibilityChecker; private final Optional<ImmutableMap<BuildTarget, Version>> selectedVersions; TargetNode( TargetNodeFactory factory, HashCode rawInputsHashCode, U description, T constructorArg, ProjectFilesystem filesystem, BuildTarget buildTarget, ImmutableSet<BuildTarget> declaredDeps, ImmutableSet<BuildTarget> extraDeps, ImmutableSet<BuildTarget> targetGraphOnlyDeps, ImmutableSet<VisibilityPattern> visibilityPatterns, ImmutableSet<VisibilityPattern> withinViewPatterns, ImmutableSet<Path> paths, CellPathResolver cellNames, Optional<ImmutableMap<BuildTarget, Version>> selectedVersions) { this.factory = factory; this.rawInputsHashCode = rawInputsHashCode; this.description = description; this.constructorArg = constructorArg; this.filesystem = filesystem; this.buildTarget = buildTarget; this.cellNames = cellNames; this.declaredDeps = declaredDeps; this.extraDeps = extraDeps; this.targetGraphOnlyDeps = targetGraphOnlyDeps; this.inputs = paths; this.selectedVersions = selectedVersions; this.visibilityChecker = new VisibilityChecker(this, visibilityPatterns, withinViewPatterns); } /** @return A hash of the raw input from the build file used to construct the node. */ public HashCode getRawInputsHashCode() { return rawInputsHashCode; } public U getDescription() { return description; } public T getConstructorArg() { return constructorArg; } public ProjectFilesystem getFilesystem() { return filesystem; } @Override public BuildTarget getBuildTarget() { return buildTarget; } public ImmutableSet<Path> getInputs() { return inputs; } public ImmutableSet<BuildTarget> getDeclaredDeps() { return declaredDeps; } public ImmutableSet<BuildTarget> getExtraDeps() { return extraDeps; } /** @return all targets which must be built before this one can be. */ public Set<BuildTarget> getBuildDeps() { return Sets.union(declaredDeps, extraDeps); } /** * @return all targets which must be present in the TargetGraph before this one can be transformed * into a BuildRule. */ public Set<BuildTarget> getParseDeps() { return Sets.union(getBuildDeps(), targetGraphOnlyDeps); } /** * Stream-style API for getting dependencies. This may return duplicates if certain dependencies * are in both declared deps and exported deps. * * <p>This method can be faster than {@link #getBuildDeps()} in cases where repeated traversals * and set operations are not necessary, as it avoids creating the intermediate set. */ public Stream<BuildTarget> getBuildDepsStream() { return Stream.concat(getDeclaredDeps().stream(), getExtraDeps().stream()); } /** * BuildTargets which, when changed, may change the BuildRules produced by this TargetNode, but * whose steps don't need executing in order to build this TargetNode's BuildRules. * * <p>A TargetNode may require metadata from other targets in order to be constructed, but may not * actually require those targets' build output. For example, some targets may execute queries * against the TargetGraph (e.g. detecting the names of rules of a certain type) but don't use the * output of those detected rules. */ public ImmutableSet<BuildTarget> getTargetGraphOnlyDeps() { return targetGraphOnlyDeps; } @Override public VisibilityChecker getVisibilityChecker() { return visibilityChecker; } public boolean isVisibleTo(TargetNode<?, ?> viewer) { return visibilityChecker.isVisibleTo(viewer); } public void isVisibleToOrThrow(TargetNode<?, ?> viewer) { if (!isVisibleTo(viewer)) { throw new HumanReadableException( "%s depends on %s, which is not visible", viewer, getBuildTarget()); } } /** Type safe checked cast of the constructor arg. */ @SuppressWarnings("unchecked") public <V> Optional<TargetNode<V, ?>> castArg(Class<V> cls) { if (cls.isInstance(constructorArg)) { return Optional.of((TargetNode<V, ?>) this); } else { return Optional.empty(); } } @Override public int compareTo(TargetNode<?, ?> o) { return getBuildTarget().compareTo(o.getBuildTarget()); } @Override public final String toString() { return getBuildTarget().getFullyQualifiedName(); } public <V, W extends Description<V>> TargetNode<V, W> withDescription(W description) { return factory.copyNodeWithDescription(this, description); } public TargetNode<T, U> copyWithFlavors(ImmutableSet<Flavor> flavors) { return factory.copyNodeWithFlavors(this, flavors); } public TargetNode<T, U> withFlavors(ImmutableSet<Flavor> flavors) { return new TargetNode<>( factory, getRawInputsHashCode(), getDescription(), constructorArg, filesystem, getBuildTarget().withFlavors(flavors), declaredDeps, extraDeps, targetGraphOnlyDeps, getVisibilityPatterns(), getWithinViewPatterns(), getInputs(), getCellNames(), getSelectedVersions()); } public TargetNode<T, U> withTargetConstructorArgDepsAndSelectedVerisons( BuildTarget target, T constructorArg, ImmutableSet<BuildTarget> declaredDeps, ImmutableSet<BuildTarget> extraDeps, ImmutableSet<BuildTarget> targetGraphOnlyDeps, Optional<ImmutableMap<BuildTarget, Version>> selectedVerisons) { return new TargetNode<>( factory, getRawInputsHashCode(), getDescription(), constructorArg, filesystem, target, declaredDeps, extraDeps, targetGraphOnlyDeps, getVisibilityPatterns(), getWithinViewPatterns(), getInputs(), getCellNames(), selectedVerisons); } public CellPathResolver getCellNames() { return cellNames; } public ImmutableSet<VisibilityPattern> getVisibilityPatterns() { return visibilityChecker.getVisibilityPatterns(); } public ImmutableSet<VisibilityPattern> getWithinViewPatterns() { return visibilityChecker.getWithinViewPatterns(); } public Optional<ImmutableMap<BuildTarget, Version>> getSelectedVersions() { return selectedVersions; } }