/* * 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 static java.nio.charset.StandardCharsets.UTF_8; import com.facebook.buck.io.ProjectFilesystem; import com.facebook.buck.model.BuildTarget; import com.facebook.buck.parser.NoSuchBuildTargetException; import com.facebook.buck.rules.coercer.DefaultTypeCoercerFactory; import com.facebook.buck.rules.coercer.TypeCoercerFactory; import com.facebook.buck.testutil.FakeProjectFilesystem; import com.facebook.buck.versions.Version; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSortedSet; import com.google.common.hash.HashCode; import com.google.common.hash.Hashing; import java.util.Optional; import javax.annotation.Nullable; /** * Support class for writing builders for nodes of a {@link TargetGraph} and {@link ActionGraph} * ({@link TargetNode} and {@link BuildRule} respectively) mirroring the behavior seen when running * the actual parser as closely as possible. */ public abstract class AbstractNodeBuilder< TArg, TDescription extends Description<TArg>, TBuildRule extends BuildRule> { protected static final TypeCoercerFactory TYPE_COERCER_FACTORY = new DefaultTypeCoercerFactory(); private static final VisibilityPatternParser VISIBILITY_PATTERN_PARSER = new VisibilityPatternParser(); protected final TDescription description; protected final ProjectFilesystem filesystem; protected final BuildTarget target; protected final CellPathResolver cellRoots; @Nullable private final HashCode rawHashCode; private Optional<ImmutableMap<BuildTarget, Version>> selectedVersions = Optional.empty(); protected AbstractNodeBuilder(TDescription description, BuildTarget target) { this(description, target, new FakeProjectFilesystem(), null); } protected AbstractNodeBuilder( TDescription description, BuildTarget target, ProjectFilesystem projectFilesystem) { this(description, target, projectFilesystem, null); } protected AbstractNodeBuilder( TDescription description, BuildTarget target, ProjectFilesystem projectFilesystem, HashCode hashCode) { this.description = description; this.filesystem = projectFilesystem; this.target = target; this.rawHashCode = hashCode; this.cellRoots = new FakeCellPathResolver(projectFilesystem); } public final TBuildRule build(BuildRuleResolver resolver) throws NoSuchBuildTargetException { return build(resolver, filesystem, TargetGraph.EMPTY); } public final TBuildRule build(BuildRuleResolver resolver, TargetGraph targetGraph) throws NoSuchBuildTargetException { return build(resolver, filesystem, targetGraph); } public final TBuildRule build(BuildRuleResolver resolver, ProjectFilesystem filesystem) throws NoSuchBuildTargetException { return build(resolver, filesystem, TargetGraph.EMPTY); } public final TBuildRule build( BuildRuleResolver resolver, ProjectFilesystem filesystem, TargetGraph targetGraph) throws NoSuchBuildTargetException { // The BuildRule determines its deps by extracting them from the rule parameters. BuildRuleParams params = createBuildRuleParams(resolver, filesystem); TArg builtArg = getPopulatedArg(); @SuppressWarnings("unchecked") TBuildRule rule = (TBuildRule) description.createBuildRule(targetGraph, params, resolver, cellRoots, builtArg); resolver.addToIndex(rule); return rule; } public TargetNode<TArg, TDescription> build() { try { HashCode hash = rawHashCode == null ? Hashing.sha1().hashString(target.getFullyQualifiedName(), UTF_8) : rawHashCode; TargetNodeFactory factory = new TargetNodeFactory(TYPE_COERCER_FACTORY); TArg populatedArg = getPopulatedArg(); TargetNode<TArg, TDescription> node = factory.create( // This hash will do in a pinch. hash, description, populatedArg, filesystem, target, getDepsFromArg(populatedArg), ImmutableSet.of( VISIBILITY_PATTERN_PARSER.parse(null, VisibilityPatternParser.VISIBILITY_PUBLIC)), ImmutableSet.of(), cellRoots); if (selectedVersions.isPresent()) { node = node.withTargetConstructorArgDepsAndSelectedVerisons( node.getBuildTarget(), node.getConstructorArg(), node.getDeclaredDeps(), node.getExtraDeps(), node.getTargetGraphOnlyDeps(), selectedVersions); } return node; } catch (NoSuchBuildTargetException e) { throw new RuntimeException(e); } } public BuildRuleParams createBuildRuleParams( BuildRuleResolver resolver, ProjectFilesystem filesystem) { TargetNode<?, ?> node = build(); return new FakeBuildRuleParamsBuilder(target) .setProjectFilesystem(filesystem) .setDeclaredDeps(resolver.getAllRules(node.getDeclaredDeps())) .setExtraDeps(resolver.getAllRules(node.getExtraDeps())) .build(); } @SuppressWarnings("unchecked") public ImmutableSortedSet<BuildTarget> findImplicitDeps() { ImplicitDepsInferringDescription<TArg> desc = (ImplicitDepsInferringDescription<TArg>) description; ImmutableSortedSet.Builder<BuildTarget> builder = ImmutableSortedSet.naturalOrder(); desc.findDepsForTargetFromConstructorArgs( target, cellRoots, getPopulatedArg(), builder, ImmutableSortedSet.naturalOrder()); return builder.build(); } public BuildTarget getTarget() { return target; } public AbstractNodeBuilder<TArg, TDescription, TBuildRule> setSelectedVersions( ImmutableMap<BuildTarget, Version> selectedVersions) { this.selectedVersions = Optional.of(selectedVersions); return this; } protected abstract Object getArgForPopulating(); protected abstract TArg getPopulatedArg(); protected abstract ImmutableSortedSet<BuildTarget> getDepsFromArg(TArg arg); }