/* * 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.parser; import com.facebook.buck.model.BuildTarget; import com.facebook.buck.model.BuildTargetException; import com.facebook.buck.parser.PipelineNodeCache.Cache; import com.facebook.buck.rules.Cell; import com.facebook.buck.util.HumanReadableException; import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListeningExecutorService; import java.nio.file.Path; import java.util.List; import java.util.Optional; import java.util.concurrent.atomic.AtomicLong; /** * Base class for a parse pipeline that converts data one item at a time. * * @param <F> Type to convert from (raw nodes, for example) * @param <T> Type to convert to (TargetNode, for example) */ public abstract class ConvertingPipeline<F, T> extends ParsePipeline<T> { private final PipelineNodeCache<BuildTarget, T> cache; protected final ListeningExecutorService executorService; public ConvertingPipeline(ListeningExecutorService executorService, Cache<BuildTarget, T> cache) { this.cache = new PipelineNodeCache<>(cache); this.executorService = executorService; } @Override public ListenableFuture<ImmutableSet<T>> getAllNodesJob( final Cell cell, final Path buildFile, AtomicLong processedBytes) throws BuildTargetException { // TODO(csarbora): this hits the chained pipeline before hitting the cache ListenableFuture<List<T>> allNodesListJob = Futures.transformAsync( getItemsToConvert(cell, buildFile, processedBytes), allToConvert -> { if (shuttingDown()) { return Futures.immediateCancelledFuture(); } ImmutableList.Builder<ListenableFuture<T>> allNodeJobs = ImmutableList.builder(); for (final F from : allToConvert) { if (isValid(from)) { BuildTarget target = getBuildTarget(cell.getRoot(), cell.getCanonicalName(), buildFile, from); allNodeJobs.add( cache.getJobWithCacheLookup( cell, target, () -> { if (shuttingDown()) { return Futures.immediateCancelledFuture(); } return dispatchComputeNode(cell, target, processedBytes, from); })); } } return Futures.allAsList(allNodeJobs.build()); }, executorService); return Futures.transform( allNodesListJob, (Function<List<T>, ImmutableSet<T>>) ImmutableSet::copyOf, executorService); } @Override public ListenableFuture<T> getNodeJob( final Cell cell, final BuildTarget buildTarget, AtomicLong processedBytes) throws BuildTargetException { return cache.getJobWithCacheLookup( cell, buildTarget, () -> Futures.transformAsync( getItemToConvert(cell, buildTarget, processedBytes), from -> dispatchComputeNode(cell, buildTarget, processedBytes, from))); } protected boolean isValid(F from) { return from != null; } protected abstract BuildTarget getBuildTarget( Path root, Optional<String> cellName, Path buildFile, F from); protected abstract T computeNode( Cell cell, BuildTarget buildTarget, F rawNode, AtomicLong processedBytes) throws BuildTargetException; protected abstract ListenableFuture<ImmutableSet<F>> getItemsToConvert( Cell cell, Path buildFile, AtomicLong processedBytes) throws BuildTargetException; protected abstract ListenableFuture<F> getItemToConvert( Cell cell, BuildTarget buildTarget, AtomicLong processedBytes) throws BuildTargetException; private ListenableFuture<T> dispatchComputeNode( Cell cell, BuildTarget buildTarget, AtomicLong processedBytes, F from) throws BuildTargetException { // TODO(csarbora): would be nice to have the first half of this function pulled up into base if (shuttingDown()) { return Futures.immediateCancelledFuture(); } if (!isValid(from)) { throw new NoSuchBuildTargetException(buildTarget); } Path pathToCheck = buildTarget.getBasePath(); if (cell.getFilesystem().isIgnored(pathToCheck)) { throw new HumanReadableException( "Content of '%s' cannot be built because" + " it is defined in an ignored directory.", pathToCheck); } return Futures.immediateFuture(computeNode(cell, buildTarget, from, processedBytes)); } }