com.facebook.buck.parser.ParserNg.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.buck.parser.ParserNg.java

Source

/*
 * Copyright 2015-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.parser;

import com.facebook.buck.event.BuckEvent;
import com.facebook.buck.event.BuckEventBus;
import com.facebook.buck.event.PerfEventId;
import com.facebook.buck.event.SimplePerfEvent;
import com.facebook.buck.graph.AbstractAcyclicDepthFirstPostOrderTraversal;
import com.facebook.buck.graph.MutableDirectedGraph;
import com.facebook.buck.io.WatchEvents;
import com.facebook.buck.json.BuildFileParseException;
import com.facebook.buck.log.Logger;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.BuildTargetException;
import com.facebook.buck.model.Pair;
import com.facebook.buck.rules.Cell;
import com.facebook.buck.rules.TargetGraph;
import com.facebook.buck.rules.TargetNode;
import com.facebook.buck.util.HumanReadableException;
import com.google.common.base.Functions;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import com.google.common.eventbus.Subscribe;

import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.annotation.Nullable;

/**
 * High-level build file parsing machinery. Primarily responsible for producing a
 * {@link TargetGraph} based on a set of targets. Caches build rules to minimise the number of calls
 * to python and processes filesystem WatchEvents to invalidate the cache as files change.
 */
public class ParserNg {

    private static final Logger LOG = Logger.get(ParserNg.class);

    private final DaemonicParserState permState;

    public ParserNg(boolean useWatchmanGlob) {
        this.permState = new DaemonicParserState(useWatchmanGlob);
    }

    public ImmutableList<Map<String, Object>> getRawTargetNodes(BuckEventBus eventBus, Cell cell,
            boolean enableProfiling, Path buildFile) throws InterruptedException, BuildFileParseException {
        Preconditions.checkState(buildFile.isAbsolute());
        Preconditions.checkState(buildFile.startsWith(cell.getRoot()));

        try (PerBuildState state = new PerBuildState(permState, eventBus, cell, enableProfiling)) {
            return state.getAllRawNodes(cell, buildFile);
        }
    }

    public ImmutableSet<TargetNode<?>> getAllTargetNodes(BuckEventBus eventBus, Cell cell, boolean enableProfiling,
            Path buildFile) throws InterruptedException, IOException, BuildFileParseException {
        Preconditions.checkState(buildFile.isAbsolute(), "Build should be absolute: %s", buildFile);
        Preconditions.checkState(buildFile.startsWith(cell.getRoot()), "Roots do not match %s -> %s",
                cell.getRoot(), buildFile);

        try (PerBuildState state = new PerBuildState(permState, eventBus, cell, enableProfiling)) {
            return state.getAllTargetNodes(cell, buildFile);
        }
    }

    @Nullable
    public TargetNode<?> getTargetNode(BuckEventBus eventBus, Cell cell, boolean enableProfiling,
            BuildTarget target)
            throws IOException, InterruptedException, BuildFileParseException, BuildTargetException {
        try (PerBuildState state = new PerBuildState(permState, eventBus, cell, enableProfiling)) {
            return state.getTargetNode(target);
        } catch (RuntimeException e) {
            throw e;
        }
    }

    @Nullable
    public SortedMap<String, Object> getRawTargetNode(BuckEventBus eventBus, Cell cell, boolean enableProfiling,
            TargetNode<?> targetNode) throws InterruptedException, BuildFileParseException {

        try {
            Cell owningCell = cell.getCell(targetNode.getBuildTarget());
            ImmutableList<Map<String, Object>> allRawNodes = getRawTargetNodes(eventBus, owningCell,
                    enableProfiling, cell.getAbsolutePathToBuildFile(targetNode.getBuildTarget()));

            String shortName = targetNode.getBuildTarget().getShortName();
            for (Map<String, Object> rawNode : allRawNodes) {
                if (shortName.equals(rawNode.get("name"))) {
                    SortedMap<String, Object> toReturn = new TreeMap<>();
                    toReturn.putAll(rawNode);
                    toReturn.put("buck.direct_dependencies", FluentIterable.from(targetNode.getDeps())
                            .transform(Functions.toStringFunction()).toList());
                    return toReturn;
                }
            }
        } catch (Cell.MissingBuildFileException e) {
            throw new RuntimeException("Deeply unlikely to be true: the cell is missing: " + targetNode);
        }
        return null;
    }

    public TargetGraph buildTargetGraph(final BuckEventBus eventBus, final Cell rootCell, boolean enableProfiling,
            final Iterable<BuildTarget> toExplore)
            throws IOException, InterruptedException, BuildFileParseException {
        final MutableDirectedGraph<TargetNode<?>> graph = new MutableDirectedGraph<>();

        ParseEvent.Started parseStart = ParseEvent.started(toExplore);
        eventBus.post(parseStart);

        TargetGraph targetGraph = null;
        try (PerBuildState state = new PerBuildState(permState, eventBus, rootCell, enableProfiling)) {
            final AbstractAcyclicDepthFirstPostOrderTraversal<BuildTarget> traversal = new AbstractAcyclicDepthFirstPostOrderTraversal<BuildTarget>() {

                @Override
                protected Iterator<BuildTarget> findChildren(BuildTarget target)
                        throws IOException, InterruptedException {
                    try (SimplePerfEvent.Scope getTargetDepsEventScope = SimplePerfEvent.scope(eventBus,
                            PerfEventId.of("GetTargetDeps"), "target", target)) {

                        TargetNode<?> node;
                        try (SimplePerfEvent.Scope scope = getTargetNodeEventScope(eventBus, target)) {
                            try {
                                node = state.getTargetNode(target);
                            } catch (BuildFileParseException | BuildTargetException e) {
                                throw new RuntimeException(e);
                            }
                        }

                        Set<BuildTarget> deps = Sets.newHashSet();
                        for (BuildTarget dep : node.getDeps()) {
                            TargetNode<?> depTargetNode;
                            try (SimplePerfEvent.Scope scope = getTargetNodeEventScope(eventBus, dep)) {
                                try {
                                    depTargetNode = state.getTargetNode(dep);
                                } catch (BuildFileParseException | BuildTargetException
                                        | HumanReadableException e) {
                                    throw new HumanReadableException(e,
                                            "Couldn't get dependency '%s' of target '%s':\n%s", dep, target,
                                            e.getMessage());
                                }
                            }
                            depTargetNode.checkVisibility(target);
                            deps.add(dep);
                        }
                        return deps.iterator();
                    }
                }

                @Override
                protected void onNodeExplored(BuildTarget target) throws IOException, InterruptedException {
                    try {
                        TargetNode<?> targetNode = null;

                        targetNode = state.getTargetNode(target);

                        Preconditions.checkNotNull(targetNode, "No target node found for %s", target);
                        graph.addNode(targetNode);
                        for (BuildTarget dep : targetNode.getDeps()) {
                            graph.addEdge(targetNode, state.getTargetNode(dep));
                        }
                    } catch (BuildFileParseException | BuildTargetException e) {
                        throw new RuntimeException(e);
                    }
                }

                @Override
                protected void onTraversalComplete(Iterable<BuildTarget> nodesInExplorationOrder) {

                }
            };

            traversal.traverse(toExplore);
            targetGraph = new TargetGraph(graph);
            return targetGraph;
        } catch (AbstractAcyclicDepthFirstPostOrderTraversal.CycleException e) {
            throw new HumanReadableException(e.getMessage());
        } finally {
            eventBus.post(ParseEvent.finished(parseStart, Optional.fromNullable(targetGraph)));
        }
    }

    /**
     * @param targetNodeSpecs the specs representing the build targets to generate a target graph for.
     * @param eventBus used to log events while parsing.
     * @return the target graph containing the build targets and their related targets.
     */
    public synchronized Pair<ImmutableSet<BuildTarget>, TargetGraph> buildTargetGraphForTargetNodeSpecs(
            BuckEventBus eventBus, Cell rootCell, boolean enableProfiling,
            Iterable<? extends TargetNodeSpec> targetNodeSpecs)
            throws BuildFileParseException, BuildTargetException, IOException, InterruptedException {

        TargetGraph graph = null;

        try (PerBuildState state = new PerBuildState(permState, eventBus, rootCell, enableProfiling)) {
            // Resolve the target node specs to the build targets the represent.
            ImmutableSet<BuildTarget> buildTargets = resolveTargetSpecs(state, rootCell, targetNodeSpecs);

            ParseEvent.Started started = ParseEvent.started(buildTargets);
            eventBus.post(started);

            try {
                graph = buildTargetGraph(eventBus, rootCell, enableProfiling, buildTargets);
                return new Pair<>(buildTargets, graph);
            } finally {
                eventBus.post(ParseEvent.finished(started, Optional.fromNullable(graph)));
            }
        }
    }

    @Override
    public String toString() {
        return permState.toString();
    }

    private ImmutableSet<BuildTarget> resolveTargetSpecs(PerBuildState state, Cell cell,
            Iterable<? extends TargetNodeSpec> specs)
            throws BuildFileParseException, BuildTargetException, IOException, InterruptedException {

        ImmutableSet.Builder<BuildTarget> targets = ImmutableSet.builder();

        for (TargetNodeSpec spec : specs) {
            targets.addAll(resolveTargetSpec(state, cell, spec));
        }

        return targets.build();
    }

    public ImmutableSet<BuildTarget> resolveTargetSpec(BuckEventBus eventBus, Cell rootCell,
            boolean enableProfiling, TargetNodeSpec spec)
            throws BuildFileParseException, BuildTargetException, InterruptedException, IOException {
        try (PerBuildState state = new PerBuildState(permState, eventBus, rootCell, enableProfiling)) {
            return resolveTargetSpec(state, rootCell, spec);
        }
    }

    /**
     * @return a set of {@link BuildTarget} objects that this {@link TargetNodeSpec} refers to.
     */
    private ImmutableSet<BuildTarget> resolveTargetSpec(PerBuildState state, Cell cell, TargetNodeSpec spec)
            throws BuildFileParseException, BuildTargetException, IOException, InterruptedException {

        ImmutableSet.Builder<BuildTarget> targets = ImmutableSet.builder();

        // Iterate over the build files the given target node spec returns.
        for (Path buildFile : spec.getBuildFileSpec().findBuildFiles(cell)) {

            // Format a proper error message for non-existent build files.
            if (!cell.getFilesystem().isFile(buildFile)) {
                throw new MissingBuildFileException(spec, cell.getFilesystem().getRootPath().relativize(buildFile));
            }

            // Build up a list of all target nodes from the build file.
            ImmutableSet<TargetNode<?>> nodes = state.getAllTargetNodes(cell, buildFile);
            // Call back into the target node spec to filter the relevant build targets.
            targets.addAll(spec.filter(nodes));
        }

        return targets.build();
    }

    private SimplePerfEvent.Scope getTargetNodeEventScope(BuckEventBus eventBus, BuildTarget buildTarget) {
        return SimplePerfEvent.scope(eventBus, PerfEventId.of("GetTargetNode"), "target", buildTarget);
    }

    @Subscribe
    public void onFileSystemChange(WatchEvent<?> event) throws InterruptedException {
        if (LOG.isVerboseEnabled()) {
            LOG.verbose("Parser watched event %s %s", event.kind(), WatchEvents.createContextString(event));
        }

        permState.invalidateBasedOn(event);
    }

    public void recordParseStartTime(BuckEventBus eventBus) {
        LOG.debug(eventBus.toString());
        // Does nothing
    }

    public Optional<BuckEvent> getParseStartTime() {
        return Optional.absent();
    }
}