blob: 4005fc8550ce03ad7d7b1ab4108b59d4f75c9b44 [file] [log] [blame]
/*
* 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.BuildTargetPattern;
import com.facebook.buck.parser.BuildTargetParser;
import com.facebook.buck.parser.NoSuchBuildTargetException;
import com.facebook.buck.testutil.FakeProjectFilesystem;
import com.google.common.base.Optional;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import java.lang.reflect.Field;
/**
* 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<A> {
protected final Description<A> description;
protected final BuildRuleFactoryParams factoryParams;
protected final BuildTarget target;
protected final A arg;
protected AbstractNodeBuilder(
Description<A> description,
BuildTarget target) {
this.description = description;
this.factoryParams = NonCheckingBuildRuleFactoryParams.createNonCheckingBuildRuleFactoryParams(
new BuildTargetParser(),
target);
this.target = target;
this.arg = description.createUnpopulatedConstructorArg();
populateWithDefaultValues(this.arg);
}
public final BuildRule build(BuildRuleResolver resolver) {
return build(resolver, new FakeProjectFilesystem(), TargetGraph.EMPTY);
}
public final BuildRule build(BuildRuleResolver resolver, ProjectFilesystem filesystem) {
return build(resolver, filesystem, TargetGraph.EMPTY);
}
public final BuildRule build(
BuildRuleResolver resolver,
ProjectFilesystem filesystem,
TargetGraph targetGraph) {
// The BuildRule determines its deps by extracting them from the rule parameters.
BuildRuleParams params = createBuildRuleParams(resolver, filesystem, targetGraph);
BuildRule rule = description.createBuildRule(params, resolver, arg);
resolver.addToIndex(rule);
return rule;
}
public TargetNode<A> build() {
try {
return new TargetNode<>(
description,
arg,
factoryParams,
getDepsFromArg(),
ImmutableSet.<BuildTargetPattern>of());
} catch (NoSuchBuildTargetException | TargetNode.InvalidSourcePathInputException e) {
throw Throwables.propagate(e);
}
}
@SuppressWarnings("unchecked")
private BuildRuleParams createBuildRuleParams(
BuildRuleResolver resolver,
ProjectFilesystem filesystem,
TargetGraph targetGraph) {
// Not all rules have deps, but all rules call them deps. When they do, they're always optional.
// Grab them in the unsafest way I know.
FakeBuildRuleParamsBuilder builder = new FakeBuildRuleParamsBuilder(target)
.setType(description.getBuildRuleType())
.setProjectFilesystem(filesystem)
.setTargetGraph(targetGraph);
try {
Field depsField = arg.getClass().getField("deps");
Object optional = depsField.get(arg);
if (optional == null) {
return builder.build();
}
// Here's a whole series of assumptions in one lump of a Bad Idea.
ImmutableSortedSet<BuildTarget> deps =
(ImmutableSortedSet<BuildTarget>) ((Optional<?>) optional).get();
return builder.setDeps(resolver.getAllRules(deps)).build();
} catch (ReflectiveOperationException ignored) {
// Field doesn't exist: no deps.
return builder.build();
}
}
@SuppressWarnings("unchecked")
private ImmutableSortedSet<BuildTarget> getDepsFromArg() {
// Not all rules have deps, but all rules call them deps. When they do, they're always optional.
// Grab them in the unsafest way I know.
try {
Field depsField = arg.getClass().getField("deps");
Object optional = depsField.get(arg);
if (optional == null) {
return ImmutableSortedSet.of();
}
// Here's a whole series of assumptions in one lump of a Bad Idea.
return (ImmutableSortedSet<BuildTarget>) ((Optional<?>) optional).get();
} catch (ReflectiveOperationException ignored) {
// Field doesn't exist: no deps.
return ImmutableSortedSet.of();
}
}
protected <C extends Comparable<?>> Optional<ImmutableSortedSet<C>> amend(
Optional<ImmutableSortedSet<C>> existing,
C instance) {
ImmutableSortedSet.Builder<C> toReturn = ImmutableSortedSet.naturalOrder();
if (existing != null && existing.isPresent()) {
toReturn.addAll(existing.get());
}
toReturn.add(instance);
return Optional.of(toReturn.build());
}
// Thanks to type erasure, this needs a unique name.
protected <C extends Comparable<?>> Optional<ImmutableSet<C>> amendSet(
Optional<ImmutableSet<C>> existing,
C instance) {
ImmutableSet.Builder<C> toReturn = ImmutableSet.builder();
if (existing.isPresent()) {
toReturn.addAll(existing.get());
}
toReturn.add(instance);
return Optional.of(toReturn.build());
}
private void populateWithDefaultValues(A arg) {
try {
new ConstructorArgMarshaller().populate(
new FakeProjectFilesystem(),
factoryParams,
arg,
ImmutableSet.<BuildTarget>builder(),
ImmutableMap.<String, Object>of(),
true);
} catch (ConstructorArgMarshalException error) {
throw Throwables.propagate(error);
}
}
}