| // Copyright 2015 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.collect.nestedset.Order.STABLE_ORDER; |
| import static com.google.devtools.build.lib.packages.Type.STRING; |
| |
| import com.google.common.annotations.VisibleForTesting; |
| import com.google.common.collect.ImmutableList; |
| import com.google.devtools.build.lib.actions.Artifact; |
| import com.google.devtools.build.lib.actions.ArtifactRoot; |
| import com.google.devtools.build.lib.analysis.RuleContext; |
| import com.google.devtools.build.lib.analysis.Runfiles; |
| import com.google.devtools.build.lib.analysis.TransitionMode; |
| import com.google.devtools.build.lib.analysis.actions.SymlinkAction; |
| import com.google.devtools.build.lib.analysis.config.CoreOptionConverters.StrictDepsMode; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.collect.nestedset.NestedSet; |
| import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; |
| import com.google.devtools.build.lib.packages.BuildType; |
| import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException; |
| import com.google.devtools.build.lib.packages.Type; |
| import com.google.devtools.build.lib.syntax.Location; |
| import com.google.devtools.build.lib.syntax.StarlarkSemantics; |
| import com.google.devtools.build.lib.util.Pair; |
| import com.google.devtools.build.lib.vfs.FileSystemUtils; |
| import com.google.devtools.build.lib.vfs.PathFragment; |
| import javax.annotation.Nullable; |
| |
| /** |
| * Utility functions for proto_library and proto aspect implementations. |
| */ |
| public class ProtoCommon { |
| private ProtoCommon() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| // Keep in sync with the migration label in |
| // https://github.com/bazelbuild/rules_proto/blob/master/proto/defs.bzl. |
| @VisibleForTesting |
| public static final String PROTO_RULES_MIGRATION_LABEL = |
| "__PROTO_RULES_MIGRATION_DO_NOT_USE_WILL_BREAK__"; |
| |
| /** |
| * Gets the direct sources of a proto library. If protoSources is not empty, the value is just |
| * protoSources. Otherwise, it's the combined sources of all direct dependencies of the given |
| * RuleContext. |
| * |
| * @param protoDeps the proto dependencies. |
| * @param protoSources the direct proto sources. |
| * @return the direct sources of a proto library. |
| */ |
| private static NestedSet<Artifact> computeStrictImportableProtosForDependents( |
| ImmutableList<ProtoInfo> protoDeps, ImmutableList<Artifact> protoSources) { |
| |
| if (protoSources.isEmpty()) { |
| /* a proxy/alias library, return the sources of the direct deps */ |
| NestedSetBuilder<Artifact> builder = NestedSetBuilder.stableOrder(); |
| for (ProtoInfo provider : protoDeps) { |
| builder.addTransitive(provider.getStrictImportableProtoSourcesForDependents()); |
| } |
| return builder.build(); |
| } else { |
| return NestedSetBuilder.wrap(STABLE_ORDER, protoSources); |
| } |
| } |
| |
| /** |
| * Gets the direct sources and import paths of a proto library. If protoSourcesImportPaths is not |
| * empty, the value is just protoSourcesImportPaths. Otherwise, it's the combined sources of all |
| * direct dependencies of the given RuleContext. |
| * |
| * @param protoDeps the proto dependencies. |
| * @param sourceImportPathPairs List of proto sources to import paths. |
| * @return the direct sources and import paths of a proto library. |
| */ |
| private static NestedSet<Pair<Artifact, String>> |
| computeStrictImportableProtosImportPathsForDependents( |
| ImmutableList<ProtoInfo> protoDeps, |
| ImmutableList<Pair<Artifact, String>> sourceImportPathPairs) { |
| |
| if (sourceImportPathPairs.isEmpty()) { |
| /* a proxy/alias library, return the sources of the direct deps */ |
| NestedSetBuilder<Pair<Artifact, String>> builder = NestedSetBuilder.stableOrder(); |
| for (ProtoInfo provider : protoDeps) { |
| builder.addTransitive(provider.getStrictImportableProtoSourcesImportPathsForDependents()); |
| } |
| return builder.build(); |
| } else { |
| return NestedSetBuilder.wrap(STABLE_ORDER, sourceImportPathPairs); |
| } |
| } |
| |
| /** |
| * Collects all .proto files in this lib and its transitive dependencies. |
| * |
| * <p>Each import is a Artifact/Label pair. |
| */ |
| private static NestedSet<Artifact> computeTransitiveProtoSources( |
| ImmutableList<ProtoInfo> protoDeps, ImmutableList<Artifact> protoSources) { |
| NestedSetBuilder<Artifact> result = NestedSetBuilder.naiveLinkOrder(); |
| |
| result.addAll(protoSources); |
| |
| for (ProtoInfo dep : protoDeps) { |
| result.addTransitive(dep.getTransitiveProtoSources()); |
| } |
| |
| return result.build(); |
| } |
| |
| private static NestedSet<Artifact> computeTransitiveOriginalProtoSources( |
| ImmutableList<ProtoInfo> protoDeps, ImmutableList<Artifact> originalProtoSources) { |
| NestedSetBuilder<Artifact> result = NestedSetBuilder.naiveLinkOrder(); |
| |
| result.addAll(originalProtoSources); |
| |
| for (ProtoInfo dep : protoDeps) { |
| result.addTransitive(dep.getOriginalTransitiveProtoSources()); |
| } |
| |
| return result.build(); |
| } |
| |
| static NestedSet<Artifact> computeDependenciesDescriptorSets(ImmutableList<ProtoInfo> protoDeps) { |
| NestedSetBuilder<Artifact> result = NestedSetBuilder.stableOrder(); |
| |
| for (ProtoInfo provider : protoDeps) { |
| result.addTransitive(provider.getTransitiveDescriptorSets()); |
| } |
| return result.build(); |
| } |
| |
| /** |
| * Returns all proto source roots in this lib ({@code currentProtoSourceRoot}) and in its |
| * transitive dependencies. |
| * |
| * <p>Assumes {@code currentProtoSourceRoot} is the same as the package name. |
| */ |
| private static NestedSet<String> computeTransitiveProtoSourceRoots( |
| ImmutableList<ProtoInfo> protoDeps, String currentProtoSourceRoot) { |
| NestedSetBuilder<String> protoPath = NestedSetBuilder.stableOrder(); |
| |
| protoPath.add(currentProtoSourceRoot); |
| for (ProtoInfo provider : protoDeps) { |
| protoPath.addTransitive(provider.getTransitiveProtoSourceRoots()); |
| } |
| |
| return protoPath.build(); |
| } |
| |
| /** |
| * The set of .proto files in a single <code>proto_library</code> rule. |
| * |
| * <p>In addition to the artifacts of the .proto files, this also includes the proto source root |
| * so that rules depending on this know how to include them. |
| */ |
| // TODO(lberki): Would be nice if had these in ProtoInfo instead of that haphazard set of fields |
| // Unfortunately, ProtoInfo has a Starlark interface so that requires a migration. |
| static final class Library { |
| private final ImmutableList<Artifact> sources; |
| private final ImmutableList<Pair<Artifact, String>> sourceImportPathPair; |
| private final String sourceRoot; |
| |
| Library( |
| ImmutableList<Artifact> sources, |
| String sourceRoot, |
| ImmutableList<Pair<Artifact, String>> sourceImportPathPair) { |
| this.sources = sources; |
| this.sourceRoot = sourceRoot; |
| this.sourceImportPathPair = sourceImportPathPair; |
| } |
| |
| public ImmutableList<Artifact> getSources() { |
| return sources; |
| } |
| |
| public ImmutableList<Pair<Artifact, String>> getSourceImportPathPair() { |
| return sourceImportPathPair; |
| } |
| |
| public String getSourceRoot() { |
| return sourceRoot; |
| } |
| } |
| |
| /** |
| * Returns the {@link Library} representing this <code>proto_library</code> rule. |
| * |
| * <p>Assumes that <code>strip_import_prefix</code> and <code>import_prefix</code> are unset and |
| * that there are no generated .proto files that need to be compiled. |
| */ |
| // TODO(lberki): This should really be a PathFragment. Unfortunately, it's on the Starlark API of |
| // ProtoInfo so it's not an easy change :( |
| @Nullable |
| public static Library createLibraryWithoutVirtualSourceRoot( |
| String protoSourceRoot, ImmutableList<Artifact> directSources) throws InterruptedException { |
| ImmutableList.Builder<Pair<Artifact, String>> builder = ImmutableList.builder(); |
| for (Artifact protoSource : directSources) { |
| builder.add(new Pair<Artifact, String>(protoSource, null)); |
| } |
| return new Library( |
| directSources, protoSourceRoot.isEmpty() ? "." : protoSourceRoot, builder.build()); |
| } |
| |
| private static PathFragment getPathFragmentAttribute( |
| RuleContext ruleContext, String attributeName) { |
| if (!ruleContext.attributes().has(attributeName)) { |
| return null; |
| } |
| |
| if (!ruleContext.attributes().isAttributeValueExplicitlySpecified(attributeName)) { |
| return null; |
| } |
| |
| String asString = ruleContext.attributes().get(attributeName, STRING); |
| if (!PathFragment.isNormalized(asString)) { |
| ruleContext.attributeError( |
| attributeName, "should be normalized (without uplevel references or '.' path segments)"); |
| return null; |
| } |
| |
| return PathFragment.create(asString); |
| } |
| |
| /** |
| * Returns the {@link Library} representing this <code>proto_library</code> rule if import prefix |
| * munging is done. Otherwise, returns null. |
| */ |
| private static Library createLibraryWithVirtualSourceRootMaybe( |
| RuleContext ruleContext, |
| ImmutableList<Artifact> protoSources, |
| boolean generatedProtosInVirtualImports) |
| throws InterruptedException { |
| PathFragment importPrefixAttribute = getPathFragmentAttribute(ruleContext, "import_prefix"); |
| PathFragment stripImportPrefixAttribute = |
| getPathFragmentAttribute(ruleContext, "strip_import_prefix"); |
| boolean hasGeneratedSources = false; |
| |
| if (generatedProtosInVirtualImports) { |
| for (Artifact protoSource : protoSources) { |
| if (!protoSource.isSourceArtifact()) { |
| hasGeneratedSources = true; |
| break; |
| } |
| } |
| } |
| |
| if (importPrefixAttribute == null |
| && stripImportPrefixAttribute == null |
| && !hasGeneratedSources) { |
| // Simple case, no magic required. |
| return null; |
| } |
| |
| PathFragment stripImportPrefix; |
| PathFragment importPrefix; |
| |
| StarlarkSemantics starlarkSemantics = |
| ruleContext.getAnalysisEnvironment().getStarlarkSemantics(); |
| boolean siblingRepositoryLayout = starlarkSemantics.experimentalSiblingRepositoryLayout(); |
| if (stripImportPrefixAttribute != null || importPrefixAttribute != null) { |
| if (stripImportPrefixAttribute == null) { |
| stripImportPrefix = |
| PathFragment.create(ruleContext.getLabel().getWorkspaceRoot(starlarkSemantics)); |
| } else if (stripImportPrefixAttribute.isAbsolute()) { |
| stripImportPrefix = |
| ruleContext |
| .getLabel() |
| .getPackageIdentifier() |
| .getRepository() |
| .getExecPath(siblingRepositoryLayout) |
| .getRelative(stripImportPrefixAttribute.toRelative()); |
| } else { |
| stripImportPrefix = |
| ruleContext |
| .getLabel() |
| .getPackageIdentifier() |
| .getExecPath(siblingRepositoryLayout) |
| .getRelative(stripImportPrefixAttribute); |
| } |
| |
| if (importPrefixAttribute != null) { |
| importPrefix = importPrefixAttribute; |
| } else { |
| importPrefix = PathFragment.EMPTY_FRAGMENT; |
| } |
| |
| if (importPrefix.isAbsolute()) { |
| ruleContext.attributeError("import_prefix", "should be a relative path"); |
| return null; |
| } |
| } else { |
| // Has generated sources, but neither strip_import_prefix nor import_prefix |
| stripImportPrefix = |
| ruleContext |
| .getLabel() |
| .getPackageIdentifier() |
| .getRepository() |
| .getDerivedArtifactSourceRoot(); |
| |
| importPrefix = PathFragment.EMPTY_FRAGMENT; |
| } |
| |
| ImmutableList.Builder<Artifact> symlinks = ImmutableList.builder(); |
| ImmutableList.Builder<Pair<Artifact, String>> protoSourceImportPair = ImmutableList.builder(); |
| |
| PathFragment sourceRootPath = ruleContext.getUniqueDirectory("_virtual_imports"); |
| |
| for (Artifact realProtoSource : protoSources) { |
| if (siblingRepositoryLayout && realProtoSource.isSourceArtifact() |
| ? !realProtoSource.getExecPath().startsWith(stripImportPrefix) |
| : !realProtoSource.getRootRelativePath().startsWith(stripImportPrefix)) { |
| ruleContext.ruleError( |
| String.format( |
| ".proto file '%s' is not under the specified strip prefix '%s'", |
| realProtoSource.getExecPathString(), stripImportPrefix.getPathString())); |
| continue; |
| } |
| Pair<PathFragment, Artifact> importsPair = |
| computeImports( |
| ruleContext, |
| realProtoSource, |
| sourceRootPath, |
| importPrefix, |
| stripImportPrefix, |
| starlarkSemantics.experimentalSiblingRepositoryLayout()); |
| protoSourceImportPair.add(new Pair<>(realProtoSource, importsPair.first.toString())); |
| symlinks.add(importsPair.second); |
| } |
| |
| String sourceRoot = |
| ruleContext |
| .getBinOrGenfilesDirectory() |
| .getExecPath() |
| .getRelative(sourceRootPath) |
| .getPathString(); |
| return new Library(symlinks.build(), sourceRoot, protoSourceImportPair.build()); |
| } |
| |
| private static Pair<PathFragment, Artifact> computeImports( |
| RuleContext ruleContext, |
| Artifact realProtoSource, |
| PathFragment sourceRootPath, |
| PathFragment importPrefix, |
| PathFragment stripImportPrefix, |
| boolean siblingRepositoryLayout) { |
| PathFragment importPath; |
| |
| if (siblingRepositoryLayout && realProtoSource.isSourceArtifact()) { |
| importPath = |
| importPrefix.getRelative(realProtoSource.getExecPath().relativeTo(stripImportPrefix)); |
| } else { |
| importPath = |
| importPrefix.getRelative( |
| realProtoSource.getRootRelativePath().relativeTo(stripImportPrefix)); |
| } |
| |
| Artifact virtualProtoSource = |
| ruleContext.getDerivedArtifact( |
| sourceRootPath.getRelative(importPath), ruleContext.getBinOrGenfilesDirectory()); |
| |
| ruleContext.registerAction( |
| SymlinkAction.toArtifact( |
| ruleContext.getActionOwner(), |
| realProtoSource, |
| virtualProtoSource, |
| "Symlinking virtual .proto sources for " + ruleContext.getLabel())); |
| |
| return Pair.of(importPath, virtualProtoSource); |
| } |
| |
| /** |
| * Returns a set of the {@code proto_source_root} collected from the current library and the |
| * specified attribute. |
| * |
| * <p>Assumes {@code currentProtoSourceRoot} is the same as the package name. |
| */ |
| private static NestedSet<String> getProtoSourceRootsOfAttribute( |
| ImmutableList<ProtoInfo> protoInfos, String currentProtoSourceRoot) { |
| NestedSetBuilder<String> protoSourceRoots = NestedSetBuilder.stableOrder(); |
| protoSourceRoots.add(currentProtoSourceRoot); |
| |
| for (ProtoInfo provider : protoInfos) { |
| protoSourceRoots.add(provider.getDirectProtoSourceRoot()); |
| } |
| |
| return protoSourceRoots.build(); |
| } |
| |
| /** |
| * Returns a set of the {@code proto_source_root} collected from the current library and the |
| * direct dependencies. |
| * |
| * <p>Assumes {@code currentProtoSourceRoot} is the same as the package name. |
| */ |
| private static NestedSet<String> computeStrictImportableProtoSourceRoots( |
| ImmutableList<ProtoInfo> protoDeps, String currentProtoSourceRoot) { |
| return getProtoSourceRootsOfAttribute(protoDeps, currentProtoSourceRoot); |
| } |
| |
| /** |
| * Returns a set of the {@code proto_source_root} collected from the current library and the |
| * exported dependencies. |
| * |
| * <p>Assumes {@code currentProtoSourceRoot} is the same as the package name. |
| */ |
| private static NestedSet<String> computeExportedProtoSourceRoots( |
| ImmutableList<ProtoInfo> exports, String currentProtoSourceRoot) { |
| return getProtoSourceRootsOfAttribute(exports, currentProtoSourceRoot); |
| } |
| |
| /** |
| * Check that .proto files in sources are from the same package. This is done to avoid clashes |
| * with the generated sources. |
| */ |
| public static void checkSourceFilesAreInSamePackage(RuleContext ruleContext) { |
| // TODO(bazel-team): this does not work with filegroups that contain files |
| // that are not in the package |
| for (Label source : ruleContext.attributes().get("srcs", BuildType.LABEL_LIST)) { |
| if (!isConfiguredTargetInSamePackage(ruleContext, source)) { |
| ruleContext.attributeError( |
| "srcs", |
| "Proto source with label '" + source + "' must be in same package as consuming rule."); |
| } |
| } |
| } |
| |
| private static boolean isConfiguredTargetInSamePackage(RuleContext ruleContext, Label source) { |
| return ruleContext.getLabel().getPackageIdentifier().equals(source.getPackageIdentifier()); |
| } |
| |
| public static void checkRuleHasValidMigrationTag(RuleContext ruleContext) |
| throws RuleErrorException { |
| if (!ruleContext.getFragment(ProtoConfiguration.class).loadProtoRulesFromBzl()) { |
| return; |
| } |
| |
| if (!hasValidMigrationTag(ruleContext)) { |
| ruleContext.ruleError( |
| "The native Protobuf rules are deprecated. Please load " |
| + ruleContext.getRule().getRuleClass() |
| + " from the rules_proto repository." |
| + " See http://github.com/bazelbuild/rules_proto."); |
| } |
| } |
| |
| private static boolean hasValidMigrationTag(RuleContext ruleContext) { |
| return ruleContext |
| .attributes() |
| .get("tags", Type.STRING_LIST) |
| .contains(PROTO_RULES_MIGRATION_LABEL); |
| } |
| |
| /** |
| * Creates the {@link ProtoInfo} for the {@code proto_library} rule associated with {@code |
| * ruleContext}. |
| */ |
| public static ProtoInfo createProtoInfo( |
| RuleContext ruleContext, boolean generatedProtosInVirtualImports) |
| throws InterruptedException { |
| ImmutableList<Artifact> originalDirectProtoSources = |
| ruleContext.getPrerequisiteArtifacts("srcs", TransitionMode.TARGET).list(); |
| ImmutableList<ProtoInfo> deps = |
| ImmutableList.copyOf( |
| ruleContext.getPrerequisites("deps", TransitionMode.TARGET, ProtoInfo.PROVIDER)); |
| ImmutableList<ProtoInfo> exports = |
| ImmutableList.copyOf( |
| ruleContext.getPrerequisites("exports", TransitionMode.TARGET, ProtoInfo.PROVIDER)); |
| |
| Artifact directDescriptorSet = |
| ruleContext.getGenfilesArtifact( |
| ruleContext.getLabel().getName() + "-descriptor-set.proto.bin"); |
| |
| Library library = |
| createLibraryWithVirtualSourceRootMaybe( |
| ruleContext, originalDirectProtoSources, generatedProtosInVirtualImports); |
| if (ruleContext.hasErrors()) { |
| return null; |
| } |
| |
| if (library == null) { |
| String contextProtoSourceRoot = |
| ruleContext |
| .getLabel() |
| .getPackageIdentifier() |
| .getRepository() |
| .getExecPath( |
| ruleContext |
| .getAnalysisEnvironment() |
| .getStarlarkSemantics() |
| .experimentalSiblingRepositoryLayout()) |
| .getPathString(); |
| library = |
| createLibraryWithoutVirtualSourceRoot(contextProtoSourceRoot, originalDirectProtoSources); |
| } |
| |
| ImmutableList<Artifact> directProtoSources = library.getSources(); |
| String protoSourceRoot = library.getSourceRoot(); |
| ImmutableList<Pair<Artifact, String>> sourceImportPathPairs = library.getSourceImportPathPair(); |
| |
| NestedSet<Artifact> transitiveProtoSources = |
| computeTransitiveProtoSources(deps, directProtoSources); |
| NestedSet<Artifact> transitiveOriginalProtoSources = |
| computeTransitiveOriginalProtoSources(deps, originalDirectProtoSources); |
| NestedSet<String> transitiveProtoSourceRoots = |
| computeTransitiveProtoSourceRoots(deps, protoSourceRoot); |
| NestedSet<Artifact> strictImportableProtosForDependents = |
| computeStrictImportableProtosForDependents(deps, directProtoSources); |
| NestedSet<Pair<Artifact, String>> strictImportableProtosImportPathsForDependents = |
| computeStrictImportableProtosImportPathsForDependents(deps, sourceImportPathPairs); |
| NestedSet<Pair<Artifact, String>> strictImportableProtos = |
| computeStrictImportableProtos(deps, sourceImportPathPairs); |
| NestedSet<String> strictImportableProtoSourceRoots = |
| computeStrictImportableProtoSourceRoots(deps, protoSourceRoot); |
| |
| NestedSet<Pair<Artifact, String>> exportedProtos = computeExportedProtos(exports); |
| NestedSet<String> exportedProtoSourceRoots = |
| computeExportedProtoSourceRoots(exports, protoSourceRoot); |
| |
| NestedSet<Artifact> dependenciesDescriptorSets = computeDependenciesDescriptorSets(deps); |
| NestedSet<Artifact> transitiveDescriptorSets = |
| NestedSetBuilder.fromNestedSet(dependenciesDescriptorSets).add(directDescriptorSet).build(); |
| |
| ProtoInfo protoInfo = |
| new ProtoInfo( |
| directProtoSources, |
| originalDirectProtoSources, |
| protoSourceRoot, |
| transitiveProtoSources, |
| transitiveOriginalProtoSources, |
| transitiveProtoSourceRoots, |
| strictImportableProtosForDependents, |
| strictImportableProtos, |
| strictImportableProtosImportPathsForDependents, |
| strictImportableProtoSourceRoots, |
| exportedProtos, |
| exportedProtoSourceRoots, |
| directDescriptorSet, |
| transitiveDescriptorSets, |
| Location.BUILTIN); |
| |
| return protoInfo; |
| } |
| |
| public static Runfiles.Builder createDataRunfilesProvider( |
| final NestedSet<Artifact> transitiveProtoSources, RuleContext ruleContext) { |
| // We assume that the proto sources will not have conflicting artifacts |
| // with the same root relative path |
| return new Runfiles.Builder( |
| ruleContext.getWorkspaceName(), ruleContext.getConfiguration().legacyExternalRunfiles()) |
| .addTransitiveArtifactsWrappedInStableOrder(transitiveProtoSources); |
| } |
| |
| // ================================================================= |
| // Protocol compiler invocation stuff. |
| |
| /** |
| * Each language-specific initialization method will call this to construct |
| * Artifacts representing its protocol compiler outputs. |
| * |
| * @param extension Remove ".proto" and replace it with this to produce |
| * the output file name, e.g. ".pb.cc". |
| * @param pythonNames If true, replace hyphens in the file name |
| * with underscores, as required for Python modules. |
| */ |
| public static ImmutableList<Artifact> getGeneratedOutputs(RuleContext ruleContext, |
| ImmutableList<Artifact> protoSources, String extension, boolean pythonNames) { |
| ImmutableList.Builder<Artifact> outputsBuilder = new ImmutableList.Builder<>(); |
| ArtifactRoot genfiles = |
| ruleContext.getConfiguration().getGenfilesDirectory(ruleContext.getRule().getRepository()); |
| for (Artifact src : protoSources) { |
| PathFragment srcPath = src.getRootRelativePath(); |
| if (pythonNames) { |
| srcPath = srcPath.replaceName(srcPath.getBaseName().replace('-', '_')); |
| } |
| // Note that two proto_library rules can have the same source file, so this is actually a |
| // shared action. NB: This can probably result in action conflicts if the proto_library rules |
| // are not the same. |
| outputsBuilder.add( |
| ruleContext.getShareableArtifact(FileSystemUtils.replaceExtension(srcPath, extension), |
| genfiles)); |
| } |
| return outputsBuilder.build(); |
| } |
| |
| /** |
| * Each language-specific initialization method will call this to construct |
| * Artifacts representing its protocol compiler outputs. |
| * |
| * @param extension Remove ".proto" and replace it with this to produce |
| * the output file name, e.g. ".pb.cc". |
| */ |
| public static ImmutableList<Artifact> getGeneratedOutputs(RuleContext ruleContext, |
| ImmutableList<Artifact> protoSources, String extension) { |
| return getGeneratedOutputs(ruleContext, protoSources, extension, false); |
| } |
| |
| public static ImmutableList<Artifact> getGeneratedTreeArtifactOutputs( |
| RuleContext ruleContext, ImmutableList<Artifact> protoSources, PathFragment directory) { |
| ImmutableList.Builder<Artifact> outputsBuilder = new ImmutableList.Builder<>(); |
| if (!protoSources.isEmpty()) { |
| ArtifactRoot genfiles = |
| ruleContext |
| .getConfiguration() |
| .getGenfilesDirectory(ruleContext.getRule().getRepository()); |
| outputsBuilder.add(ruleContext.getTreeArtifact(directory, genfiles)); |
| } |
| return outputsBuilder.build(); |
| } |
| |
| /** |
| * Returns the .proto files that are the direct srcs of the direct-dependencies of this rule. If |
| * the current rule is an alias proto_library (=no srcs), we use the direct srcs of the |
| * direct-dependencies of our direct-dependencies. |
| */ |
| @Nullable |
| private static NestedSet<Pair<Artifact, String>> computeStrictImportableProtos( |
| ImmutableList<ProtoInfo> protoDeps, |
| ImmutableList<Pair<Artifact, String>> sourceImportPathPairs) { |
| NestedSetBuilder<Pair<Artifact, String>> result = NestedSetBuilder.stableOrder(); |
| if (sourceImportPathPairs.isEmpty()) { |
| for (ProtoInfo provider : protoDeps) { |
| result.addTransitive(provider.getStrictImportableProtoSourcesImportPaths()); |
| } |
| } else { |
| for (ProtoInfo provider : protoDeps) { |
| result.addTransitive(provider.getStrictImportableProtoSourcesImportPathsForDependents()); |
| } |
| result.addAll(sourceImportPathPairs); |
| } |
| return result.build(); |
| } |
| |
| /** |
| * Returns the .proto files that are the direct srcs of the exported dependencies of this rule. |
| */ |
| private static NestedSet<Pair<Artifact, String>> computeExportedProtos( |
| ImmutableList<ProtoInfo> exports) { |
| NestedSetBuilder<Pair<Artifact, String>> result = NestedSetBuilder.stableOrder(); |
| for (ProtoInfo provider : exports) { |
| result.addTransitive(provider.getStrictImportableProtoSourcesImportPaths()); |
| } |
| return result.build(); |
| } |
| |
| /** |
| * Decides whether this proto_library should check for strict proto deps. |
| * |
| * <p>Only takes into account the command-line flag --strict_proto_deps. |
| */ |
| @VisibleForTesting |
| public static boolean areDepsStrict(RuleContext ruleContext) { |
| StrictDepsMode flagValue = ruleContext.getFragment(ProtoConfiguration.class).strictProtoDeps(); |
| return flagValue != StrictDepsMode.OFF && flagValue != StrictDepsMode.DEFAULT; |
| } |
| } |