diff options
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/rules/proto/ProtoSourceFileBlacklist.java')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/rules/proto/ProtoSourceFileBlacklist.java | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoSourceFileBlacklist.java b/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoSourceFileBlacklist.java new file mode 100644 index 0000000000..f324ce5fb3 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoSourceFileBlacklist.java @@ -0,0 +1,159 @@ +// 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.rules.proto; + +import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.HOST; +import static com.google.devtools.build.lib.packages.Attribute.attr; +import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Joiner; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode; +import com.google.devtools.build.lib.analysis.RuleContext; +import com.google.devtools.build.lib.cmdline.Label; +import com.google.devtools.build.lib.packages.Attribute; +import com.google.devtools.build.lib.vfs.PathFragment; +import java.util.ArrayList; +import java.util.List; + +/** + * A blacklist of proto source files. + * + * <p>There are cases where we need to identify proto files that we should not create generated + * files for. For example, we should not create generated code for google/protobuf/any.proto, which + * is already baked into the language support libraries. This class provides us with the ability + * to identify these proto files and avoid linking in their associated generated files. + */ +public class ProtoSourceFileBlacklist { + private static final PathFragment BAZEL_TOOLS_PREFIX = new PathFragment("external/bazel_tools/"); + private final RuleContext ruleContext; + private final ImmutableSet<PathFragment> blacklistProtoFilePaths; + + private final Predicate<Artifact> isBlacklistProto = + new Predicate<Artifact>() { + @Override + public boolean apply(Artifact protoFile) { + return isBlacklisted(protoFile); + } + }; + + /** + * Creates a proto source file blacklist. + * @param ruleContext the proto rule context. + * @param blacklistAttribute the attribute that points to a filegroup containing the blacklisted + * protos. + */ + public ProtoSourceFileBlacklist(RuleContext ruleContext, String blacklistAttribute) { + this.ruleContext = ruleContext; + Iterable<Artifact> blacklistProtoFiles = + ruleContext.getPrerequisiteArtifacts(blacklistAttribute, Mode.HOST).list(); + ImmutableSet.Builder<PathFragment> blacklistProtoFilePathsBuilder = + new ImmutableSet.Builder<>(); + for (Artifact blacklistProtoFile : blacklistProtoFiles) { + PathFragment execPath = blacklistProtoFile.getExecPath(); + // For blacklisted protos bundled with the Bazel tools repository, their exec paths start + // with external/bazel_tools/. This prefix needs to be removed first, because the protos in + // user repositories will not have that prefix. + if (execPath.startsWith(BAZEL_TOOLS_PREFIX)) { + blacklistProtoFilePathsBuilder.add(execPath.relativeTo(BAZEL_TOOLS_PREFIX)); + } else { + blacklistProtoFilePathsBuilder.add(execPath); + } + } + blacklistProtoFilePaths = blacklistProtoFilePathsBuilder.build(); + } + + /** + * Filters the blacklisted protos from the given protos. + */ + public Iterable<Artifact> filter(Iterable<Artifact> protoFiles) { + return ImmutableSet.copyOf(Iterables.filter(protoFiles, Predicates.not(isBlacklistProto))); + } + + /** + * Checks the proto sources for mixing blacklisted and non-blacklisted protos in one single + * proto_library rule. Registers an attribute error if proto mixing is detected. + * + * @param protoFiles the protos to filter. + * @param topLevelProtoRuleName the name of the top-level rule that generates the protos. + * @return whether the proto sources are clean without mixing. + */ + public boolean checkSrcs(Iterable<Artifact> protoFiles, String topLevelProtoRuleName) { + List<Artifact> blacklisted = new ArrayList<>(); + List<Artifact> nonBlacklisted = new ArrayList<>(); + for (Artifact protoFile : protoFiles) { + if (isBlacklisted(protoFile)) { + blacklisted.add(protoFile); + } else { + nonBlacklisted.add(protoFile); + } + } + if (!nonBlacklisted.isEmpty() && !blacklisted.isEmpty()) { + ruleContext.attributeError( + "srcs", + createBlacklistedProtosMixError( + Artifact.toRootRelativePaths(blacklisted), + Artifact.toRootRelativePaths(nonBlacklisted), + ruleContext.getLabel().toString(), + topLevelProtoRuleName)); + } + + return blacklisted.isEmpty(); + } + + /** + * Returns blacklisted protos from the given protos. + */ + private Iterable<Artifact> blacklisted(Iterable<Artifact> protoFiles) { + return ImmutableSet.copyOf(Iterables.filter(protoFiles, isBlacklistProto)); + } + + /** + * Returns whether the given proto file is blacklisted. + */ + public boolean isBlacklisted(Artifact protoFile) { + return blacklistProtoFilePaths.contains(protoFile.getExecPath()); + } + + /** + * Returns an attribute for the implicit dependency on blacklist proto filegroups. + * @param attributeName the name of the attribute. + * @param blacklistFileGroups a list of labels pointin to the filegroups containing blacklisted + * protos. + */ + public static Attribute.Builder<List<Label>> blacklistFilegroupAttribute( + String attributeName, List<Label> blacklistFileGroups) { + return attr(attributeName, LABEL_LIST).cfg(HOST).value(blacklistFileGroups); + } + + @VisibleForTesting + public static String createBlacklistedProtosMixError( + Iterable<String> blacklisted, Iterable<String> nonBlacklisted, String protoLibraryRuleLabel, + String topLevelProtoRuleName) { + return String.format( + "The 'srcs' attribute of '%s' contains protos for which '%s' " + + "shouldn't generate code (%s), in addition to protos for which it should (%s).\n" + + "Separate '%1$s' into 2 proto_library rules.", + protoLibraryRuleLabel, + topLevelProtoRuleName, + Joiner.on(", ").join(blacklisted), + Joiner.on(", ").join(nonBlacklisted)); + } +} |