/* * Copyright 2016-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.parser.NoSuchBuildTargetException; import com.facebook.buck.rules.coercer.CoercedTypeCache; import com.facebook.buck.rules.coercer.ParamInfo; import com.facebook.buck.rules.coercer.TypeCoercerFactory; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; import com.google.common.collect.Sets; import com.google.common.hash.HashCode; import java.nio.file.Path; import java.util.Optional; public class TargetNodeFactory { private final TypeCoercerFactory typeCoercerFactory; public TargetNodeFactory(TypeCoercerFactory typeCoercerFactory) { this.typeCoercerFactory = typeCoercerFactory; } /** * This factory method lets the wildcard be bound, so the constructor can be casted to it. * * <p>This does no checking that the type of {@code constructorArg} is correct, since {@code * Description} does not hold the Class of the constructor arg. * * <p>See <a href="https://docs.oracle.com/javase/tutorial/java/generics/capture.html">Wildcard * Capture and Helper Methods</a>. */ @SuppressWarnings("unchecked") public <T, U extends Description<T>> TargetNode<T, U> createFromObject( HashCode rawInputsHashCode, U description, Object constructorArg, ProjectFilesystem filesystem, BuildTarget buildTarget, ImmutableSet<BuildTarget> declaredDeps, ImmutableSet<VisibilityPattern> visibilityPatterns, ImmutableSet<VisibilityPattern> withinViewPatterns, CellPathResolver cellRoots) throws NoSuchBuildTargetException { return create( rawInputsHashCode, description, (T) constructorArg, filesystem, buildTarget, declaredDeps, visibilityPatterns, withinViewPatterns, cellRoots); } @SuppressWarnings("unchecked") public <T, U extends Description<T>> TargetNode<T, U> create( HashCode rawInputsHashCode, U description, T constructorArg, ProjectFilesystem filesystem, BuildTarget buildTarget, ImmutableSet<BuildTarget> declaredDeps, ImmutableSet<VisibilityPattern> visibilityPatterns, ImmutableSet<VisibilityPattern> withinViewPatterns, CellPathResolver cellRoots) throws NoSuchBuildTargetException { ImmutableSortedSet.Builder<BuildTarget> extraDepsBuilder = ImmutableSortedSet.naturalOrder(); ImmutableSortedSet.Builder<BuildTarget> targetGraphOnlyDepsBuilder = ImmutableSortedSet.naturalOrder(); ImmutableSet.Builder<Path> pathsBuilder = ImmutableSet.builder(); // Scan the input to find possible BuildTargets, necessary for loading dependent rules. for (ParamInfo info : CoercedTypeCache.INSTANCE .getAllParamInfo(typeCoercerFactory, description.getConstructorArgType()) .values()) { if (info.isDep() && info.isInput() && info.hasElementTypes(BuildTarget.class, SourcePath.class, Path.class)) { detectBuildTargetsAndPathsForConstructorArg( extraDepsBuilder, pathsBuilder, info, constructorArg); } } if (description instanceof ImplicitDepsInferringDescription) { ((ImplicitDepsInferringDescription<T>) description) .findDepsForTargetFromConstructorArgs( buildTarget, cellRoots, constructorArg, extraDepsBuilder, targetGraphOnlyDepsBuilder); } if (description instanceof ImplicitInputsInferringDescription) { pathsBuilder.addAll( ((ImplicitInputsInferringDescription<T>) description) .inferInputsFromConstructorArgs( buildTarget.getUnflavoredBuildTarget(), constructorArg)); } return new TargetNode<>( this, rawInputsHashCode, description, constructorArg, filesystem, buildTarget, declaredDeps, ImmutableSortedSet.copyOf(Sets.difference(extraDepsBuilder.build(), declaredDeps)), targetGraphOnlyDepsBuilder.build(), visibilityPatterns, withinViewPatterns, pathsBuilder.build(), cellRoots, Optional.empty()); } private static void detectBuildTargetsAndPathsForConstructorArg( final ImmutableSet.Builder<BuildTarget> depsBuilder, final ImmutableSet.Builder<Path> pathsBuilder, ParamInfo info, Object constructorArg) throws NoSuchBuildTargetException { // We'll make no test for optionality here. Let's assume it's done elsewhere. try { info.traverse( object -> { if (object instanceof PathSourcePath) { pathsBuilder.add(((PathSourcePath) object).getRelativePath()); } else if (object instanceof BuildTargetSourcePath) { depsBuilder.add(((BuildTargetSourcePath) object).getTarget()); } else if (object instanceof Path) { pathsBuilder.add((Path) object); } else if (object instanceof BuildTarget) { depsBuilder.add((BuildTarget) object); } }, constructorArg); } catch (RuntimeException e) { if (e.getCause() instanceof NoSuchBuildTargetException) { throw (NoSuchBuildTargetException) e.getCause(); } } } @SuppressWarnings("unchecked") public <T, U extends Description<T>> TargetNode<T, U> copyNodeWithDescription( TargetNode<?, ?> originalNode, U description) { try { return create( originalNode.getRawInputsHashCode(), description, (T) originalNode.getConstructorArg(), originalNode.getFilesystem(), originalNode.getBuildTarget(), originalNode.getDeclaredDeps(), originalNode.getVisibilityPatterns(), originalNode.getWithinViewPatterns(), originalNode.getCellNames()); } catch (NoSuchBuildTargetException e) { throw new IllegalStateException( String.format( "Caught exception when transforming TargetNode to use a different description: %s", originalNode.getBuildTarget()), e); } } public <T, U extends Description<T>> TargetNode<T, U> copyNodeWithFlavors( TargetNode<T, U> originalNode, ImmutableSet<Flavor> flavors) { try { return create( originalNode.getRawInputsHashCode(), originalNode.getDescription(), originalNode.getConstructorArg(), originalNode.getFilesystem(), originalNode.getBuildTarget().withFlavors(flavors), originalNode.getDeclaredDeps(), originalNode.getVisibilityPatterns(), originalNode.getWithinViewPatterns(), originalNode.getCellNames()); } catch (NoSuchBuildTargetException e) { throw new IllegalStateException( String.format( "Caught exception when transforming TargetNode to use different flavors: %s", originalNode.getBuildTarget()), e); } } }