blob: 37f0f718dc1d670371c1c1bae1b877c536b6f764 [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.event.BuckEventBus;
import com.facebook.buck.graph.AbstractBottomUpTraversal;
import com.facebook.buck.graph.AbstractBreadthFirstTraversal;
import com.facebook.buck.graph.MutableDirectedGraph;
import com.facebook.buck.log.Logger;
import com.facebook.buck.parser.NoSuchBuildTargetException;
import com.facebook.buck.util.HumanReadableException;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import javax.annotation.Nullable;
public class TargetGraphToActionGraph implements TargetGraphTransformer<ActionGraph> {
private static final Logger LOG = Logger.get(TargetGraphToActionGraph.class);
private final BuckEventBus eventBus;
private final TargetNodeToBuildRuleTransformer buildRuleGenerator;
@Nullable
private volatile ActionGraph actionGraph;
private volatile int hashOfTargetGraph;
public TargetGraphToActionGraph(
BuckEventBus eventBus,
TargetNodeToBuildRuleTransformer buildRuleGenerator) {
this.eventBus = eventBus;
this.buildRuleGenerator = buildRuleGenerator;
}
@Override
public synchronized ActionGraph apply(TargetGraph targetGraph) {
if (actionGraph != null) {
if (targetGraph.hashCode() == hashOfTargetGraph) {
return actionGraph;
}
LOG.info("Flushing cached action graph. May be a performance hit.");
}
actionGraph = createActionGraph(targetGraph);
hashOfTargetGraph = targetGraph.hashCode();
return actionGraph;
}
private ActionGraph createActionGraph(final TargetGraph targetGraph) {
eventBus.post(ActionGraphEvent.started());
final BuildRuleResolver ruleResolver = new BuildRuleResolver();
final MutableDirectedGraph<BuildRule> actionGraph = new MutableDirectedGraph<>();
AbstractBottomUpTraversal<TargetNode<?>, ActionGraph> bottomUpTraversal =
new AbstractBottomUpTraversal<TargetNode<?>, ActionGraph>(targetGraph) {
@Override
public void visit(TargetNode<?> node) {
BuildRule rule;
try {
rule = buildRuleGenerator.transform(targetGraph, ruleResolver, node);
} catch (NoSuchBuildTargetException e) {
throw new HumanReadableException(e);
}
// Check whether a rule with this build target already exists. This is possible
// if we create a new build rule during graph enhancement, and the user asks to
// build the same build rule. The returned rule may have a different name from the
// target node.
Optional<BuildRule> existingRule =
ruleResolver.getRuleOptional(rule.getBuildTarget());
Preconditions.checkState(
!existingRule.isPresent() || existingRule.get().equals(rule));
if (!existingRule.isPresent()) {
ruleResolver.addToIndex(rule);
}
actionGraph.addNode(rule);
for (BuildRule buildRule : rule.getDeps()) {
if (buildRule.getBuildTarget().isFlavored()) {
addGraphEnhancedDeps(rule);
}
}
for (BuildRule dep : rule.getDeps()) {
actionGraph.addEdge(rule, dep);
}
}
@Override
public ActionGraph getResult() {
return new ActionGraph(actionGraph);
}
private void addGraphEnhancedDeps(BuildRule rule) {
new AbstractBreadthFirstTraversal<BuildRule>(rule) {
@Override
public ImmutableSet<BuildRule> visit(BuildRule rule) {
ImmutableSet.Builder<BuildRule> depsToVisit = null;
boolean isRuleFlavored = rule.getBuildTarget().isFlavored();
for (BuildRule dep : rule.getDeps()) {
boolean isDepFlavored = dep.getBuildTarget().isFlavored();
if (isRuleFlavored || isDepFlavored) {
actionGraph.addEdge(rule, dep);
}
if (isDepFlavored) {
if (depsToVisit == null) {
depsToVisit = ImmutableSet.builder();
}
depsToVisit.add(dep);
}
}
return depsToVisit == null ?
ImmutableSet.<BuildRule>of() :
depsToVisit.build();
}
}.start();
}
};
bottomUpTraversal.traverse();
ActionGraph result = bottomUpTraversal.getResult();
eventBus.post(ActionGraphEvent.finished());
return result;
}
}