com.facebook.buck.model.BuildTargets.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.buck.model.BuildTargets.java

Source

/*
 * 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.model;

import com.facebook.buck.io.BuckPaths;
import com.facebook.buck.io.ProjectFilesystem;
import com.facebook.buck.util.HumanReadableException;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Sets;

import java.nio.file.Path;
import java.util.Set;

/**
 * Static helpers for working with build targets.
 */
public class BuildTargets {

    /** Utility class: do not instantiate. */
    private BuildTargets() {
    }

    /**
     * Return a path to a file in the buck-out/bin/ directory. {@code format} will be prepended with
     * the {@link BuckPaths#getScratchDir()} and the target base path, then formatted with the target
     * short name.
     *
     * @param target The {@link BuildTarget} to scope this path to.
     * @param format {@link String#format} string for the path name.  It should contain one "%s",
     *     which will be filled in with the rule's short name.  It should not start with a slash.
     * @return A {@link java.nio.file.Path} under buck-out/bin, scoped to the base path of
     * {@code target}.
     */
    public static Path getScratchPath(ProjectFilesystem filesystem, BuildTarget target, String format) {
        Preconditions.checkArgument(!format.startsWith("/"), "format string should not start with a slash");
        return filesystem.getBuckPaths().getScratchDir().resolve(target.getBasePath())
                .resolve(String.format(format, target.getShortNameAndFlavorPostfix()));
    }

    /**
     * Return a path to a file in the buck-out/annotation/ directory. {@code format} will be prepended
     * with the {@link BuckPaths#getAnnotationDir()} and the target base path, then formatted with the
     * target short name.
     *
     * @param target The {@link BuildTarget} to scope this path to.
     * @param format {@link String#format} string for the path name.  It should contain one "%s",
     *     which will be filled in with the rule's short name.  It should not start with a slash.
     * @return A {@link java.nio.file.Path} under buck-out/annotation, scoped to the base path of
     * {@code target}.
     */
    public static Path getAnnotationPath(ProjectFilesystem filesystem, BuildTarget target, String format) {
        Preconditions.checkArgument(!format.startsWith("/"), "format string should not start with a slash");
        return filesystem.getBuckPaths().getAnnotationDir().resolve(target.getBasePath())
                .resolve(String.format(format, target.getShortNameAndFlavorPostfix()));
    }

    /**
     * Return a path to a file in the buck-out/gen/ directory. {@code format} will be prepended with
     * the {@link BuckPaths#getGenDir()} and the target base path, then formatted with the target
     * short name.
     *
     * @param target The {@link BuildTarget} to scope this path to.
     * @param format {@link String#format} string for the path name.  It should contain one "%s",
     *     which will be filled in with the rule's short name.  It should not start with a slash.
     * @return A {@link java.nio.file.Path} under buck-out/gen, scoped to the base path of
     * {@code target}.
     */
    public static Path getGenPath(ProjectFilesystem filesystem, BuildTarget target, String format) {
        Preconditions.checkArgument(!format.startsWith("/"), "format string should not start with a slash");
        return filesystem.getBuckPaths().getGenDir().resolve(target.getBasePath())
                .resolve(String.format(format, target.getShortNameAndFlavorPostfix()));
    }

    /**
     * Takes the {@link BuildTarget} for {@code hasBuildTarget} and derives a new {@link BuildTarget}
     * from it with the specified flavor.
     * @throws IllegalArgumentException if the original {@link BuildTarget} already has a flavor.
     */
    public static BuildTarget createFlavoredBuildTarget(UnflavoredBuildTarget buildTarget, Flavor flavor) {
        return BuildTarget.builder(buildTarget).addFlavors(flavor).build();
    }

    public static Predicate<BuildTarget> containsFlavors(final FlavorDomain<?> domain) {
        return input -> {
            ImmutableSet<Flavor> flavorSet = Sets.intersection(domain.getFlavors(), input.getFlavors())
                    .immutableCopy();
            return !flavorSet.isEmpty();
        };
    }

    public static Predicate<BuildTarget> containsFlavor(final Flavor flavor) {
        return input -> input.getFlavors().contains(flavor);
    }

    /**
     * Propagate flavors represented by the given {@link FlavorDomain} objects from a parent
     * target to its dependencies.
     */
    public static ImmutableSortedSet<BuildTarget> propagateFlavorDomains(BuildTarget target,
            Iterable<FlavorDomain<?>> domains, Iterable<BuildTarget> deps) {

        Set<Flavor> flavors = Sets.newHashSet();

        // For each flavor domain, extract the corresponding flavor from the parent target and
        // verify that each dependency hasn't already set this flavor.
        for (FlavorDomain<?> domain : domains) {

            // Now extract all relevant domain flavors from our parent target.
            ImmutableSet<Flavor> flavorSet = Sets.intersection(domain.getFlavors(), target.getFlavors())
                    .immutableCopy();

            if (flavorSet.isEmpty()) {
                throw new HumanReadableException("%s: no flavor for \"%s\"", target, domain.getName());
            }
            flavors.addAll(flavorSet);

            // First verify that our deps are not already flavored for our given domains.
            for (BuildTarget dep : deps) {
                if (domain.getFlavor(dep).isPresent()) {
                    throw new HumanReadableException("%s: dep %s already has flavor for \"%s\" : %s", target, dep,
                            domain.getName(), flavorSet.toString());
                }
            }
        }

        ImmutableSortedSet.Builder<BuildTarget> flavoredDeps = ImmutableSortedSet.naturalOrder();

        // Now flavor each dependency with the relevant flavors.
        for (BuildTarget dep : deps) {
            flavoredDeps.add(BuildTarget.builder(dep).addAllFlavors(flavors).build());
        }

        return flavoredDeps.build();
    }

    /**
     * Propagate a build target's flavors in a certain domain to a list of other build targets.
     *
     * @param domain the flavor domain to be propagated.
     * @param buildTarget the build target containing the flavors to be propagated
     * @param deps list of BuildTargets to propagate the flavors to.  If a target already contains
     *             one or more flavors in domain, it is left unchanged.
     * @return the list of BuildTargets with any flavors propagated.
     */
    public static FluentIterable<BuildTarget> propagateFlavorsInDomainIfNotPresent(FlavorDomain<?> domain,
            BuildTarget buildTarget, FluentIterable<BuildTarget> deps) {
        if (domain.containsAnyOf(buildTarget.getFlavors())) {
            FluentIterable<BuildTarget> targetsWithFlavorsAlready = deps
                    .filter(BuildTargets.containsFlavors(domain));

            FluentIterable<BuildTarget> targetsWithoutFlavors = deps
                    .filter(Predicates.not(BuildTargets.containsFlavors(domain)));

            deps = targetsWithFlavorsAlready.append(BuildTargets.propagateFlavorDomains(buildTarget,
                    ImmutableSet.of(domain), targetsWithoutFlavors));
        }

        return deps;
    }
}