com.google.devtools.build.lib.skyframe.LocalRepositoryLookupFunction.java Source code

Java tutorial

Introduction

Here is the source code for com.google.devtools.build.lib.skyframe.LocalRepositoryLookupFunction.java

Source

// Copyright 2016 The Bazel Authors. All rights reserved.
//
// 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.google.devtools.build.lib.skyframe;

import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.cmdline.LabelSyntaxException;
import com.google.devtools.build.lib.cmdline.RepositoryName;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.packages.AggregatingAttributeMapper;
import com.google.devtools.build.lib.packages.BuildFileNotFoundException;
import com.google.devtools.build.lib.packages.ErrorDeterminingRepositoryException;
import com.google.devtools.build.lib.packages.Package;
import com.google.devtools.build.lib.packages.Package.NameConflictException;
import com.google.devtools.build.lib.packages.Rule;
import com.google.devtools.build.lib.rules.repository.LocalRepositoryRule;
import com.google.devtools.build.lib.skyframe.PackageFunction.PackageFunctionException;
import com.google.devtools.build.lib.syntax.Type;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.build.lib.vfs.RootedPath;
import com.google.devtools.build.skyframe.SkyFunction;
import com.google.devtools.build.skyframe.SkyFunctionException;
import com.google.devtools.build.skyframe.SkyFunctionException.Transience;
import com.google.devtools.build.skyframe.SkyKey;
import com.google.devtools.build.skyframe.SkyValue;
import java.io.IOException;
import javax.annotation.Nullable;

/** SkyFunction for {@link LocalRepositoryLookupValue}s. */
public class LocalRepositoryLookupFunction implements SkyFunction {

    @Override
    @Nullable
    public String extractTag(SkyKey skyKey) {
        return null;
    }

    // Implementation note: Although LocalRepositoryLookupValue.NOT_FOUND exists, it should never be
    // returned from this method.
    @Override
    public SkyValue compute(SkyKey skyKey, Environment env) throws SkyFunctionException, InterruptedException {
        RootedPath directory = (RootedPath) skyKey.argument();

        // Is this the root directory? If so, we're in the MAIN repository. This assumes that the main
        // repository has a WORKSPACE in the root directory, but Bazel will have failed with an error
        // before this can be called if that is incorrect.
        if (directory.getRelativePath().equals(PathFragment.EMPTY_FRAGMENT)) {
            return LocalRepositoryLookupValue.mainRepository();
        }

        // Does this directory contain a WORKSPACE file?
        Optional<Boolean> maybeWorkspaceFileExists = maybeGetWorkspaceFileExistence(env, directory);
        if (!maybeWorkspaceFileExists.isPresent()) {
            return null;
        } else if (maybeWorkspaceFileExists.get()) {
            Optional<LocalRepositoryLookupValue> maybeRepository = maybeCheckWorkspaceForRepository(env, directory);
            if (!maybeRepository.isPresent()) {
                return null;
            }
            LocalRepositoryLookupValue repository = maybeRepository.get();
            // If the repository that was discovered doesn't exist, continue recursing.
            if (repository.exists()) {
                return repository;
            }
        }

        // If we haven't found a repository yet, check the parent directory.
        RootedPath parentDirectory = RootedPath.toRootedPath(directory.getRoot(),
                directory.getRelativePath().getParentDirectory());
        return env.getValue(LocalRepositoryLookupValue.key(parentDirectory));
    }

    private Optional<Boolean> maybeGetWorkspaceFileExistence(Environment env, RootedPath directory)
            throws InterruptedException, LocalRepositoryLookupFunctionException {
        try {
            RootedPath workspaceRootedFile = RootedPath.toRootedPath(directory.getRoot(),
                    directory.getRelativePath().getChild(PackageLookupValue.BuildFileName.WORKSPACE.getFilename()));
            FileValue workspaceFileValue = (FileValue) env.getValueOrThrow(FileValue.key(workspaceRootedFile),
                    IOException.class, FileSymlinkException.class, InconsistentFilesystemException.class);
            if (workspaceFileValue == null) {
                return Optional.absent();
            }
            return Optional.of(workspaceFileValue.exists());
        } catch (IOException e) {
            throw new LocalRepositoryLookupFunctionException(new ErrorDeterminingRepositoryException(
                    "IOException while checking if there is a WORKSPACE file in "
                            + directory.asPath().getPathString(),
                    e), Transience.PERSISTENT);
        } catch (FileSymlinkException e) {
            throw new LocalRepositoryLookupFunctionException(new ErrorDeterminingRepositoryException(
                    "FileSymlinkException while checking if there is a WORKSPACE file in "
                            + directory.asPath().getPathString(),
                    e), Transience.PERSISTENT);
        } catch (InconsistentFilesystemException e) {
            throw new LocalRepositoryLookupFunctionException(new ErrorDeterminingRepositoryException(
                    "InconsistentFilesystemException while checking if there is a WORKSPACE file in "
                            + directory.asPath().getPathString(),
                    e), Transience.PERSISTENT);
        }
    }

    /**
     * Checks whether the directory exists and is a workspace root. Returns {@link Optional#absent()}
     * if Skyframe needs to re-run, {@link Optional#of(LocalRepositoryLookupValue)} otherwise.
     */
    private Optional<LocalRepositoryLookupValue> maybeCheckWorkspaceForRepository(Environment env,
            final RootedPath directory) throws InterruptedException, LocalRepositoryLookupFunctionException {
        // Look up the main WORKSPACE file by the external package, to find all repositories.
        PackageLookupValue externalPackageLookupValue;
        try {
            externalPackageLookupValue = (PackageLookupValue) env.getValueOrThrow(
                    PackageLookupValue.key(Label.EXTERNAL_PACKAGE_IDENTIFIER), BuildFileNotFoundException.class,
                    InconsistentFilesystemException.class);
            if (externalPackageLookupValue == null) {
                return Optional.absent();
            }
        } catch (BuildFileNotFoundException e) {
            throw new LocalRepositoryLookupFunctionException(
                    new ErrorDeterminingRepositoryException(
                            "BuildFileNotFoundException while loading the //external package", e),
                    Transience.PERSISTENT);
        } catch (InconsistentFilesystemException e) {
            throw new LocalRepositoryLookupFunctionException(
                    new ErrorDeterminingRepositoryException(
                            "InconsistentFilesystemException while loading the //external package", e),
                    Transience.PERSISTENT);
        }

        RootedPath workspacePath = externalPackageLookupValue.getRootedPath(Label.EXTERNAL_PACKAGE_IDENTIFIER);

        SkyKey workspaceKey = WorkspaceFileValue.key(workspacePath);
        do {
            WorkspaceFileValue value;
            try {
                value = (WorkspaceFileValue) env.getValueOrThrow(workspaceKey, PackageFunctionException.class,
                        NameConflictException.class);
                if (value == null) {
                    return Optional.absent();
                }
            } catch (PackageFunctionException e) {
                // TODO(jcater): When WFF is rewritten to not throw a PFE, update this.
                throw new LocalRepositoryLookupFunctionException(
                        new ErrorDeterminingRepositoryException(
                                "PackageFunctionException while loading the root WORKSPACE file", e),
                        Transience.PERSISTENT);
            } catch (NameConflictException e) {
                throw new LocalRepositoryLookupFunctionException(
                        new ErrorDeterminingRepositoryException(
                                "NameConflictException while loading the root WORKSPACE file", e),
                        Transience.PERSISTENT);
            }

            Package externalPackage = value.getPackage();
            if (externalPackage.containsErrors()) {
                Event.replayEventsOn(env.getListener(), externalPackage.getEvents());
            }

            // Find all local_repository rules in the WORKSPACE, and check if any have a "path" attribute
            // the same as the requested directory.
            Iterable<Rule> localRepositories = externalPackage.getRulesMatchingRuleClass(LocalRepositoryRule.NAME);
            Rule rule = Iterables.find(localRepositories, new Predicate<Rule>() {
                @Override
                public boolean apply(@Nullable Rule rule) {
                    AggregatingAttributeMapper mapper = AggregatingAttributeMapper.of(rule);
                    PathFragment pathAttr = new PathFragment(mapper.get("path", Type.STRING));
                    return directory.getRelativePath().equals(pathAttr);
                }
            }, null);
            if (rule != null) {
                try {
                    return Optional
                            .of(LocalRepositoryLookupValue.success(RepositoryName.create("@" + rule.getName())));
                } catch (LabelSyntaxException e) {
                    // This shouldn't be possible if the rule name is valid, and it should already have been
                    // validated.
                    throw new LocalRepositoryLookupFunctionException(new ErrorDeterminingRepositoryException(
                            "LabelSyntaxException while creating the repository name from the rule "
                                    + rule.getName(),
                            e), Transience.PERSISTENT);
                }
            }
            workspaceKey = value.next();

            // TODO(bazel-team): This loop can be quadratic in the number of load() statements, consider
            // rewriting or unrolling.
        } while (workspaceKey != null);

        return Optional.of(LocalRepositoryLookupValue.notFound());
    }

    /**
     * Used to declare all the exception types that can be wrapped in the exception thrown by {@link
     * LocalRepositoryLookupFunction#compute}.
     */
    private static final class LocalRepositoryLookupFunctionException extends SkyFunctionException {
        public LocalRepositoryLookupFunctionException(ErrorDeterminingRepositoryException e,
                Transience transience) {
            super(e, transience);
        }
    }
}