com.facebook.buck.features.python.PythonUtil.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.buck.features.python.PythonUtil.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.features.python;

import com.facebook.buck.core.cell.CellPathResolver;
import com.facebook.buck.core.exceptions.HumanReadableException;
import com.facebook.buck.core.model.BuildTarget;
import com.facebook.buck.core.rules.ActionGraphBuilder;
import com.facebook.buck.core.rules.BuildRule;
import com.facebook.buck.core.rules.BuildRuleParams;
import com.facebook.buck.core.rules.SourcePathRuleFinder;
import com.facebook.buck.core.sourcepath.SourcePath;
import com.facebook.buck.core.sourcepath.resolver.SourcePathResolver;
import com.facebook.buck.core.util.graph.AbstractBreadthFirstTraversal;
import com.facebook.buck.cxx.CxxGenruleDescription;
import com.facebook.buck.cxx.Omnibus;
import com.facebook.buck.cxx.OmnibusLibraries;
import com.facebook.buck.cxx.OmnibusLibrary;
import com.facebook.buck.cxx.OmnibusRoot;
import com.facebook.buck.cxx.OmnibusRoots;
import com.facebook.buck.cxx.toolchain.CxxBuckConfig;
import com.facebook.buck.cxx.toolchain.CxxPlatform;
import com.facebook.buck.cxx.toolchain.nativelink.NativeLinkStrategy;
import com.facebook.buck.cxx.toolchain.nativelink.NativeLinkTarget;
import com.facebook.buck.cxx.toolchain.nativelink.NativeLinkTargetMode;
import com.facebook.buck.cxx.toolchain.nativelink.NativeLinkable;
import com.facebook.buck.cxx.toolchain.nativelink.NativeLinkables;
import com.facebook.buck.features.python.toolchain.PythonPlatform;
import com.facebook.buck.io.file.MorePaths;
import com.facebook.buck.io.filesystem.ProjectFilesystem;
import com.facebook.buck.rules.args.Arg;
import com.facebook.buck.rules.coercer.PatternMatchedCollection;
import com.facebook.buck.rules.coercer.SourceSortedSet;
import com.facebook.buck.rules.coercer.VersionMatchedCollection;
import com.facebook.buck.rules.macros.LocationMacroExpander;
import com.facebook.buck.rules.macros.Macro;
import com.facebook.buck.rules.macros.MacroExpander;
import com.facebook.buck.util.RichStream;
import com.facebook.buck.versions.Version;
import com.google.common.base.CaseFormat;
import com.google.common.base.Preconditions;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

public class PythonUtil {

    static final ImmutableList<MacroExpander<? extends Macro, ?>> MACRO_EXPANDERS = ImmutableList
            .of(new LocationMacroExpander());

    private PythonUtil() {
    }

    public static ImmutableList<BuildTarget> getDeps(PythonPlatform pythonPlatform, CxxPlatform cxxPlatform,
            ImmutableSortedSet<BuildTarget> deps,
            PatternMatchedCollection<ImmutableSortedSet<BuildTarget>> platformDeps) {
        return RichStream.<BuildTarget>empty().concat(deps.stream())
                .concat(platformDeps.getMatchingValues(pythonPlatform.getFlavor().toString()).stream()
                        .flatMap(Collection::stream))
                .concat(platformDeps.getMatchingValues(cxxPlatform.getFlavor().toString()).stream()
                        .flatMap(Collection::stream))
                .toImmutableList();
    }

    public static ImmutableMap<Path, SourcePath> getModules(BuildTarget target, ActionGraphBuilder graphBuilder,
            SourcePathRuleFinder ruleFinder, SourcePathResolver pathResolver, PythonPlatform pythonPlatform,
            CxxPlatform cxxPlatform, String parameter, Path baseModule, SourceSortedSet items,
            PatternMatchedCollection<SourceSortedSet> platformItems,
            Optional<VersionMatchedCollection<SourceSortedSet>> versionItems,
            Optional<ImmutableMap<BuildTarget, Version>> versions) {
        return CxxGenruleDescription.fixupSourcePaths(graphBuilder, ruleFinder, cxxPlatform, ImmutableMap
                .<Path, SourcePath>builder()
                .putAll(PythonUtil.toModuleMap(target, pathResolver, parameter, baseModule,
                        ImmutableList.of(items)))
                .putAll(PythonUtil.toModuleMap(target, pathResolver,
                        "platform" + CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, parameter), baseModule,
                        Iterables.concat(platformItems.getMatchingValues(pythonPlatform.getFlavor().toString()),
                                platformItems.getMatchingValues(cxxPlatform.getFlavor().toString()))))
                .putAll(PythonUtil.toModuleMap(target, pathResolver,
                        "versioned" + CaseFormat.LOWER_HYPHEN.to(CaseFormat.UPPER_CAMEL, parameter), baseModule,
                        versions.isPresent() && versionItems.isPresent()
                                ? versionItems.get().getMatchingValues(versions.get())
                                : ImmutableList.of()))
                .build());
    }

    static ImmutableMap<Path, SourcePath> toModuleMap(BuildTarget target, SourcePathResolver resolver,
            String parameter, Path baseModule, Iterable<SourceSortedSet> inputs) {

        ImmutableMap.Builder<Path, SourcePath> moduleNamesAndSourcePaths = ImmutableMap.builder();

        for (SourceSortedSet input : inputs) {
            ImmutableMap<String, SourcePath> namesAndSourcePaths;
            if (input.getUnnamedSources().isPresent()) {
                namesAndSourcePaths = resolver.getSourcePathNames(target, parameter,
                        input.getUnnamedSources().get());
            } else {
                namesAndSourcePaths = input.getNamedSources().get();
            }
            for (ImmutableMap.Entry<String, SourcePath> entry : namesAndSourcePaths.entrySet()) {
                moduleNamesAndSourcePaths.put(baseModule.resolve(entry.getKey()), entry.getValue());
            }
        }

        return moduleNamesAndSourcePaths.build();
    }

    /** Convert a path to a module to it's module name as referenced in import statements. */
    static String toModuleName(BuildTarget target, String name) {
        int ext = name.lastIndexOf('.');
        if (ext == -1) {
            throw new HumanReadableException("%s: missing extension for module path: %s", target, name);
        }
        name = name.substring(0, ext);
        return MorePaths.pathWithUnixSeparators(name).replace('/', '.');
    }

    static PythonPackageComponents getAllComponents(CellPathResolver cellPathResolver, BuildTarget buildTarget,
            ProjectFilesystem projectFilesystem, BuildRuleParams params, ActionGraphBuilder graphBuilder,
            SourcePathRuleFinder ruleFinder, Iterable<BuildRule> deps, PythonPackageComponents packageComponents,
            PythonPlatform pythonPlatform, CxxBuckConfig cxxBuckConfig, CxxPlatform cxxPlatform,
            ImmutableList<? extends Arg> extraLdflags, NativeLinkStrategy nativeLinkStrategy,
            ImmutableSet<BuildTarget> preloadDeps) {

        PythonPackageComponents.Builder allComponents = new PythonPackageComponents.Builder(buildTarget);

        Map<BuildTarget, CxxPythonExtension> extensions = new LinkedHashMap<>();
        Map<BuildTarget, NativeLinkable> nativeLinkableRoots = new LinkedHashMap<>();

        OmnibusRoots.Builder omnibusRoots = OmnibusRoots.builder(cxxPlatform, preloadDeps, graphBuilder);

        // Add the top-level components.
        allComponents.addComponent(packageComponents, buildTarget);

        // Walk all our transitive deps to build our complete package that we'll
        // turn into an executable.
        new AbstractBreadthFirstTraversal<BuildRule>(
                Iterables.concat(deps, graphBuilder.getAllRules(preloadDeps))) {
            private final ImmutableList<BuildRule> empty = ImmutableList.of();

            @Override
            public Iterable<BuildRule> visit(BuildRule rule) {
                Iterable<BuildRule> deps = empty;
                if (rule instanceof CxxPythonExtension) {
                    CxxPythonExtension extension = (CxxPythonExtension) rule;
                    NativeLinkTarget target = ((CxxPythonExtension) rule).getNativeLinkTarget(pythonPlatform);
                    extensions.put(target.getBuildTarget(), extension);
                    omnibusRoots.addIncludedRoot(target);
                    List<BuildRule> cxxpydeps = new ArrayList<>();
                    for (BuildRule dep : extension.getPythonPackageDeps(pythonPlatform, cxxPlatform,
                            graphBuilder)) {
                        if (dep instanceof PythonPackagable) {
                            cxxpydeps.add(dep);
                        }
                    }
                    deps = cxxpydeps;
                } else if (rule instanceof PythonPackagable) {
                    PythonPackagable packagable = (PythonPackagable) rule;
                    PythonPackageComponents comps = packagable.getPythonPackageComponents(pythonPlatform,
                            cxxPlatform, graphBuilder);
                    allComponents.addComponent(comps, rule.getBuildTarget());
                    if (packagable.doesPythonPackageDisallowOmnibus() || comps.hasNativeCode(cxxPlatform)) {
                        for (BuildRule dep : packagable.getPythonPackageDeps(pythonPlatform, cxxPlatform,
                                graphBuilder)) {
                            if (dep instanceof NativeLinkable) {
                                NativeLinkable linkable = (NativeLinkable) dep;
                                nativeLinkableRoots.put(linkable.getBuildTarget(), linkable);
                                omnibusRoots.addExcludedRoot(linkable);
                            }
                        }
                    }
                    deps = packagable.getPythonPackageDeps(pythonPlatform, cxxPlatform, graphBuilder);
                } else if (rule instanceof NativeLinkable) {
                    NativeLinkable linkable = (NativeLinkable) rule;
                    nativeLinkableRoots.put(linkable.getBuildTarget(), linkable);
                    omnibusRoots.addPotentialRoot(linkable);
                }
                return deps;
            }
        }.start();

        // For the merged strategy, build up the lists of included native linkable roots, and the
        // excluded native linkable roots.
        if (nativeLinkStrategy == NativeLinkStrategy.MERGED) {
            OmnibusRoots roots = omnibusRoots.build();
            OmnibusLibraries libraries = Omnibus.getSharedLibraries(buildTarget, projectFilesystem, params,
                    cellPathResolver, graphBuilder, ruleFinder, cxxBuckConfig, cxxPlatform, extraLdflags,
                    roots.getIncludedRoots().values(), roots.getExcludedRoots().values());

            // Add all the roots from the omnibus link.  If it's an extension, add it as a module.
            // Otherwise, add it as a native library.
            for (Map.Entry<BuildTarget, OmnibusRoot> root : libraries.getRoots().entrySet()) {
                CxxPythonExtension extension = extensions.get(root.getKey());
                if (extension != null) {
                    allComponents.addModule(extension.getModule(), root.getValue().getPath(), root.getKey());
                } else {
                    NativeLinkTarget target = Preconditions.checkNotNull(
                            roots.getIncludedRoots().get(root.getKey()), "%s: linked unexpected omnibus root: %s",
                            buildTarget, root.getKey());
                    NativeLinkTargetMode mode = target.getNativeLinkTargetMode(cxxPlatform);
                    String soname = Preconditions.checkNotNull(mode.getLibraryName().orElse(null),
                            "%s: omnibus library for %s was built without soname", buildTarget, root.getKey());
                    allComponents.addNativeLibraries(Paths.get(soname), root.getValue().getPath(), root.getKey());
                }
            }

            // Add all remaining libraries as native libraries.
            for (OmnibusLibrary library : libraries.getLibraries()) {
                allComponents.addNativeLibraries(Paths.get(library.getSoname()), library.getPath(), buildTarget);
            }
        } else {

            // For regular linking, add all extensions via the package components interface.
            Map<BuildTarget, NativeLinkable> extensionNativeDeps = new LinkedHashMap<>();
            for (Map.Entry<BuildTarget, CxxPythonExtension> entry : extensions.entrySet()) {
                allComponents.addComponent(
                        entry.getValue().getPythonPackageComponents(pythonPlatform, cxxPlatform, graphBuilder),
                        entry.getValue().getBuildTarget());
                extensionNativeDeps.putAll(Maps.uniqueIndex(entry.getValue().getNativeLinkTarget(pythonPlatform)
                        .getNativeLinkTargetDeps(cxxPlatform, graphBuilder), NativeLinkable::getBuildTarget));
            }

            // Add all the native libraries.
            ImmutableMap<BuildTarget, NativeLinkable> nativeLinkables = NativeLinkables
                    .getTransitiveNativeLinkables(cxxPlatform, graphBuilder,
                            Iterables.concat(nativeLinkableRoots.values(), extensionNativeDeps.values()));
            for (NativeLinkable nativeLinkable : nativeLinkables.values()) {
                NativeLinkable.Linkage linkage = nativeLinkable.getPreferredLinkage(cxxPlatform, graphBuilder);
                if (nativeLinkableRoots.containsKey(nativeLinkable.getBuildTarget())
                        || linkage != NativeLinkable.Linkage.STATIC) {
                    ImmutableMap<String, SourcePath> libs = nativeLinkable.getSharedLibraries(cxxPlatform,
                            graphBuilder);
                    for (Map.Entry<String, SourcePath> ent : libs.entrySet()) {
                        allComponents.addNativeLibraries(Paths.get(ent.getKey()), ent.getValue(),
                                nativeLinkable.getBuildTarget());
                    }
                }
            }
        }

        return allComponents.build();
    }

    public static Path getBasePath(BuildTarget target, Optional<String> override) {
        return override.isPresent() ? Paths.get(override.get().replace('.', '/')) : target.getBasePath();
    }

    static ImmutableSet<String> getPreloadNames(ActionGraphBuilder graphBuilder, CxxPlatform cxxPlatform,
            Iterable<BuildTarget> preloadDeps) {
        ImmutableSet.Builder<String> builder = ImmutableSortedSet.naturalOrder();
        for (NativeLinkable nativeLinkable : FluentIterable.from(preloadDeps).transform(graphBuilder::getRule)
                .filter(NativeLinkable.class)) {
            builder.addAll(nativeLinkable.getSharedLibraries(cxxPlatform, graphBuilder).keySet());
        }
        return builder.build();
    }
}