diff options
author | 2017-04-26 16:45:06 +0200 | |
---|---|---|
committer | 2017-04-26 18:05:08 +0200 | |
commit | d5ee3b5397135eebd4b5d5b6bd4a4444093c4df8 (patch) | |
tree | 3ba8bfe196ac72790ffb8946031c1403fa3e9fc3 /src/main/java/com/google/devtools/build/lib | |
parent | 4db102cb2662c48b5c72f67a5da7d7632a0580a3 (diff) |
Repositories can only be accessed in projects that define them in their WORKSPACE file
This is prep for #1943 - hierarchical workspace loading.
RELNOTES[INC]: Remote repositories must define any remote repositories they
themselves use (e.g., if @x//:foo depends on @y//:bar, @y must be defined
in @x's WORKSPACE file).
PiperOrigin-RevId: 154295762
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib')
4 files changed, 165 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/repository/RepositoryVisibilityFunction.java b/src/main/java/com/google/devtools/build/lib/rules/repository/RepositoryVisibilityFunction.java new file mode 100644 index 0000000000..4daed03952 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/repository/RepositoryVisibilityFunction.java @@ -0,0 +1,127 @@ +// Copyright 2017 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.rules.repository; + +import com.google.auto.value.AutoValue; +import com.google.devtools.build.lib.cmdline.Label; +import com.google.devtools.build.lib.cmdline.RepositoryName; +import com.google.devtools.build.lib.packages.Rule; +import com.google.devtools.build.lib.skyframe.PackageLookupValue; +import com.google.devtools.build.lib.skyframe.SkyFunctions; +import com.google.devtools.build.lib.skyframe.WorkspaceFileValue; +import com.google.devtools.build.lib.util.Preconditions; +import com.google.devtools.build.lib.vfs.RootedPath; +import com.google.devtools.build.skyframe.SkyFunction; +import com.google.devtools.build.skyframe.SkyKey; +import com.google.devtools.build.skyframe.SkyValue; +import javax.annotation.Nullable; + +/** + * Used for repositories that are declared in other repositories' WORKSPACE files. + */ +public class RepositoryVisibilityFunction implements SkyFunction { + public static SkyKey key(RepositoryName parent, RepositoryName child) { + return SkyKey.create(SkyFunctions.REPOSITORY_DEPENDENCY, Key.create(parent, child)); + } + + @Nullable + @Override + public SkyValue compute(SkyKey skyKey, Environment env) throws InterruptedException { + Key key = (Key) skyKey.argument(); + RepositoryDirectoryValue parentDir = (RepositoryDirectoryValue) env.getValue( + RepositoryDirectoryValue.key(key.parent())); + if (env.valuesMissing()) { + return null; + } + + SkyKey parentWorkspaceKey; + if (parentDir.repositoryExists()) { + parentWorkspaceKey = WorkspaceFileValue.key( + RootedPath.toRootedPath(parentDir.getPath(), Label.EXTERNAL_PACKAGE_FILE_NAME)); + } else { + // This is kind of hacky: the main repository won't exist under output_base/external, so RDF + // won't find it above. However, we know that the parent repository has to exist, so we'll + // just assume it's the main repository if RDF couldn't find it. + PackageLookupValue packageLookupValue = (PackageLookupValue) env.getValue( + PackageLookupValue.key(Label.EXTERNAL_PACKAGE_IDENTIFIER)); + if (env.valuesMissing()) { + return null; + } + Preconditions.checkState(packageLookupValue.packageExists()); + parentWorkspaceKey = WorkspaceFileValue.key( + RootedPath.toRootedPath(packageLookupValue.getRoot(), Label.EXTERNAL_PACKAGE_FILE_NAME)); + } + + // This just looks for the child repo name. It doesn't care if the name is used for a different + // repository (either a different type or a different path/url/commit/whichever) than is + // actually being used. For example, if someone has a copy of Boost on their system that + // they're using but a library they're depending downloads Boost, we just want "everyone" to + // use the local copy of Boost, not error out. This is the check that the library actually + // declares a repository "dependency" on @boost. + while (true) { + WorkspaceFileValue parentWorkspace = (WorkspaceFileValue) env.getValue(parentWorkspaceKey); + if (env.valuesMissing()) { + return null; + } + + Rule child = parentWorkspace.getPackage().getRule(key.child().strippedName()); + if (child == null) { + if (parentWorkspace.hasNext()) { + parentWorkspaceKey = parentWorkspace.next(); + } else { + break; + } + } else { + return RepositoryVisibilityValue.OK; + } + } + return RepositoryVisibilityValue.NOT_FOUND; + } + + @Nullable + @Override + public String extractTag(SkyKey skyKey) { + return null; + } + + /** + * Represents a parent repository and a child repository that we expect to be defined in the + * parent's WORKSPACE file. + */ + @AutoValue + public abstract static class Key { + public static Key create(RepositoryName parent, RepositoryName child) { + return new AutoValue_RepositoryVisibilityFunction_Key(parent, child); + } + + public abstract RepositoryName parent(); + public abstract RepositoryName child(); + } + + /** + * Returns if the repository definition was found or not. + */ + public static class RepositoryVisibilityValue implements SkyValue { + private static RepositoryVisibilityValue OK = new RepositoryVisibilityValue(); + private static RepositoryVisibilityValue NOT_FOUND = new RepositoryVisibilityValue(); + + private RepositoryVisibilityValue() { + } + + public boolean ok() { + return this == OK; + } + } +} diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java b/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java index d79f0dbf4c..0dc838ce6a 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/ConfiguredTargetFunction.java @@ -22,6 +22,7 @@ import com.google.common.base.Verify; import com.google.common.base.VerifyException; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.LinkedListMultimap; @@ -50,6 +51,7 @@ import com.google.devtools.build.lib.analysis.config.HostTransition; import com.google.devtools.build.lib.analysis.config.InvalidConfigurationException; import com.google.devtools.build.lib.analysis.config.PatchTransition; import com.google.devtools.build.lib.cmdline.Label; +import com.google.devtools.build.lib.cmdline.RepositoryName; import com.google.devtools.build.lib.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.collect.nestedset.Order; @@ -69,6 +71,8 @@ import com.google.devtools.build.lib.packages.RuleClassProvider; import com.google.devtools.build.lib.packages.SkylarkProviderIdentifier; import com.google.devtools.build.lib.packages.Target; import com.google.devtools.build.lib.packages.TargetUtils; +import com.google.devtools.build.lib.rules.repository.RepositoryVisibilityFunction; +import com.google.devtools.build.lib.rules.repository.RepositoryVisibilityFunction.RepositoryVisibilityValue; import com.google.devtools.build.lib.skyframe.AspectFunction.AspectCreationException; import com.google.devtools.build.lib.skyframe.AspectValue.AspectKey; import com.google.devtools.build.lib.skyframe.SkyframeExecutor.BuildViewProvider; @@ -91,6 +95,7 @@ import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Objects; import java.util.Set; import java.util.concurrent.Semaphore; @@ -336,6 +341,35 @@ final class ConfiguredTargetFunction implements SkyFunction { throw new DependencyEvaluationException(e); } + RepositoryName ctgRepository = ctgValue.getLabel().getPackageIdentifier().getRepository(); + ImmutableSet.Builder<SkyKey> pairsToCheck = ImmutableSet.builder(); + for (Dependency dep : depValueNames.values()) { + RepositoryName depRepository = dep.getLabel().getPackageIdentifier().getRepository(); + if (ctgRepository.equals(depRepository) || depRepository.isMain()) { + continue; + } + pairsToCheck.add(RepositoryVisibilityFunction.key(ctgRepository, depRepository)); + } + Map<SkyKey, SkyValue> pairs = env.getValues(pairsToCheck.build()); + if (env.valuesMissing()) { + return null; + } + boolean hadError = false; + for (Entry<SkyKey, SkyValue> entry : pairs.entrySet()) { + if (!((RepositoryVisibilityValue) entry.getValue()).ok()) { + RepositoryVisibilityFunction.Key key = + (RepositoryVisibilityFunction.Key) entry.getKey().argument(); + String message = ctgValue.getLabel() + " has a dependency on " + + key.child() + " but does not define " + key.child() + " in its WORKSPACE"; + env.getListener().handle(Event.error(message)); + hadError = true; + } + } + if (hadError) { + throw new DependencyEvaluationException( + new ConfiguredValueCreationException("Missing external repository definitions")); + } + // Trim each dep's configuration so it only includes the fragments needed by its transitive // closure (only dynamic configurations support this). if (useDynamicConfigurations(ctgValue.getConfiguration())) { diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java index 05c78873b6..474b3ea21c 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyFunctions.java @@ -109,6 +109,8 @@ public final class SkyFunctions { SkyFunctionName.create("ACTION_TEMPLATE_EXPANSION"); public static final SkyFunctionName LOCAL_REPOSITORY_LOOKUP = SkyFunctionName.create("LOCAL_REPOSITORY_LOOKUP"); + public static final SkyFunctionName REPOSITORY_DEPENDENCY = + SkyFunctionName.create("REPOSITORY_DEPENDENCY"); public static Predicate<SkyKey> isSkyFunction(final SkyFunctionName functionName) { return new Predicate<SkyKey>() { diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java index b4114029ea..b4264989ba 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java @@ -105,6 +105,7 @@ import com.google.devtools.build.lib.pkgcache.TargetParsingCompleteEvent; import com.google.devtools.build.lib.pkgcache.TestFilter; import com.google.devtools.build.lib.pkgcache.TransitivePackageLoader; import com.google.devtools.build.lib.profiler.AutoProfiler; +import com.google.devtools.build.lib.rules.repository.RepositoryVisibilityFunction; import com.google.devtools.build.lib.skyframe.AspectValue.AspectValueKey; import com.google.devtools.build.lib.skyframe.DirtinessCheckerUtils.FileDirtinessChecker; import com.google.devtools.build.lib.skyframe.ExternalFilesHelper.ExternalFileAction; @@ -446,6 +447,7 @@ public abstract class SkyframeExecutor implements WalkableGraphFactory { SkyFunctions.ACTION_TEMPLATE_EXPANSION, new ActionTemplateExpansionFunction(removeActionsAfterEvaluation)); map.put(SkyFunctions.LOCAL_REPOSITORY_LOOKUP, new LocalRepositoryLookupFunction()); + map.put(SkyFunctions.REPOSITORY_DEPENDENCY, new RepositoryVisibilityFunction()); map.putAll(extraSkyFunctions); return map.build(); } |