| // 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 com.google.common.annotations.VisibleForTesting; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.Interner; |
| import com.google.devtools.build.lib.actions.Artifact; |
| import com.google.devtools.build.lib.analysis.ConfiguredTarget; |
| import com.google.devtools.build.lib.analysis.FilesToRunProvider; |
| import com.google.devtools.build.lib.analysis.RuleContext; |
| import com.google.devtools.build.lib.analysis.config.CoreOptionConverters.StrictDepsMode; |
| import com.google.devtools.build.lib.analysis.starlark.Args; |
| import com.google.devtools.build.lib.cmdline.BazelModuleContext; |
| import com.google.devtools.build.lib.cmdline.Label; |
| import com.google.devtools.build.lib.collect.nestedset.Depset; |
| import com.google.devtools.build.lib.collect.nestedset.Depset.ElementType; |
| import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; |
| import com.google.devtools.build.lib.collect.nestedset.Order; |
| import com.google.devtools.build.lib.concurrent.BlazeInterners; |
| import com.google.devtools.build.lib.packages.RuleClass.ConfiguredTargetFactory.RuleErrorException; |
| import com.google.devtools.build.lib.packages.StarlarkInfo; |
| import com.google.devtools.build.lib.vfs.PathFragment; |
| import javax.annotation.Nullable; |
| import net.starlark.java.eval.Dict; |
| import net.starlark.java.eval.EvalException; |
| import net.starlark.java.eval.Module; |
| import net.starlark.java.eval.Sequence; |
| import net.starlark.java.eval.Starlark; |
| import net.starlark.java.eval.StarlarkCallable; |
| import net.starlark.java.eval.StarlarkFunction; |
| import net.starlark.java.eval.StarlarkList; |
| import net.starlark.java.eval.StarlarkThread; |
| import net.starlark.java.eval.Tuple; |
| |
| /** 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__"; |
| |
| private static final Interner<PathFragment> PROTO_SOURCE_ROOT_INTERNER = |
| BlazeInterners.newWeakInterner(); |
| |
| /** |
| * Returns a memory efficient version of the passed protoSourceRoot. |
| * |
| * <p>Any sizable proto graph will contain many {@code .proto} sources with the same source root. |
| * We can't afford to have all of them represented as individual objects in memory. |
| * |
| * @param protoSourceRoot |
| * @return |
| */ |
| static PathFragment memoryEfficientProtoSourceRoot(PathFragment protoSourceRoot) { |
| return PROTO_SOURCE_ROOT_INTERNER.intern(protoSourceRoot); |
| } |
| |
| // ================================================================= |
| // Protocol compiler invocation stuff. |
| |
| /** |
| * 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 getBool = ruleContext.getFragment(ProtoConfiguration.class).strictProtoDeps(); |
| return getBool != StrictDepsMode.OFF && getBool != StrictDepsMode.DEFAULT; |
| } |
| |
| public static void checkPrivateStarlarkificationAllowlist(StarlarkThread thread) |
| throws EvalException { |
| Label label = |
| ((BazelModuleContext) Module.ofInnermostEnclosingStarlarkFunction(thread).getClientData()) |
| .label(); |
| if (!label.getPackageIdentifier().getRepository().toString().equals("@_builtins")) { |
| throw Starlark.errorf("Rule in '%s' cannot use private API", label.getPackageName()); |
| } |
| } |
| |
| public static ImmutableList<Artifact> declareGeneratedFiles( |
| RuleContext ruleContext, ConfiguredTarget protoTarget, String extension) |
| throws RuleErrorException, InterruptedException { |
| StarlarkFunction declareGeneratedFiles = |
| (StarlarkFunction) |
| ruleContext.getStarlarkDefinedBuiltin("proto_common_declare_generated_files"); |
| ruleContext.initStarlarkRuleContext(); |
| Sequence<?> outputs = |
| (Sequence<?>) |
| ruleContext.callStarlarkOrThrowRuleError( |
| declareGeneratedFiles, |
| ImmutableList.of( |
| /* actions */ ruleContext.getStarlarkRuleContext().actions(), |
| /* proto_info */ protoTarget.get(ProtoInfo.PROVIDER), |
| /* extension */ extension), |
| ImmutableMap.of()); |
| try { |
| return Sequence.cast(outputs, Artifact.class, "declare_generated_files").getImmutableList(); |
| } catch (EvalException e) { |
| throw new RuleErrorException(e.getMessageWithStack()); |
| } |
| } |
| |
| private static final StarlarkCallable pythonMapper = |
| new StarlarkCallable() { |
| @Override |
| public Object call(StarlarkThread thread, Tuple args, Dict<String, Object> kwargs) { |
| return args.get(0).toString().replace('-', '_').replace('.', '/'); |
| } |
| |
| @Override |
| public String getName() { |
| return "python_mapper"; |
| } |
| }; |
| |
| /** |
| * Each language-specific initialization method will call this to construct Artifacts representing |
| * its protocol compiler outputs. The cals replaces hyphens in the file name with underscores, and |
| * dots in the file name with forward slashes, as required for Python modules. |
| * |
| * @param extension Remove ".proto" and replace it with this to produce the output file name, e.g. |
| * ".pb.cc". |
| */ |
| public static ImmutableList<Artifact> declareGeneratedFilesPython( |
| RuleContext ruleContext, ConfiguredTarget protoTarget, String extension) |
| throws RuleErrorException, InterruptedException { |
| StarlarkFunction declareGeneratedFiles = |
| (StarlarkFunction) |
| ruleContext.getStarlarkDefinedBuiltin("proto_common_declare_generated_files"); |
| ruleContext.initStarlarkRuleContext(); |
| Sequence<?> outputs = |
| (Sequence<?>) |
| ruleContext.callStarlarkOrThrowRuleError( |
| declareGeneratedFiles, |
| ImmutableList.of( |
| /* actions */ ruleContext.getStarlarkRuleContext().actions(), |
| /* proto_info */ protoTarget.get(ProtoInfo.PROVIDER), |
| /* extension */ extension, |
| /* experimental_python_names */ pythonMapper), |
| ImmutableMap.of()); |
| try { |
| return Sequence.cast(outputs, Artifact.class, "declare_generated_files").getImmutableList(); |
| } catch (EvalException e) { |
| throw new RuleErrorException(e.getMessageWithStack()); |
| } |
| } |
| |
| public static void compile( |
| RuleContext ruleContext, |
| ConfiguredTarget protoTarget, |
| StarlarkInfo protoLangToolchainInfo, |
| Iterable<Artifact> generatedFiles, |
| @Nullable Object pluginOutput, |
| @Nullable Args additionalArgs, |
| Iterable<FilesToRunProvider> additionalTools, |
| Iterable<Artifact> additionalInputs, |
| @Nullable StarlarkCallable resourceSet, |
| String progressMessage) |
| throws RuleErrorException, InterruptedException { |
| StarlarkFunction compile = |
| (StarlarkFunction) ruleContext.getStarlarkDefinedBuiltin("proto_common_compile"); |
| ruleContext.initStarlarkRuleContext(); |
| ruleContext.callStarlarkOrThrowRuleError( |
| compile, |
| ImmutableList.of( |
| /* actions */ ruleContext.getStarlarkRuleContext().actions(), |
| /* proto_info */ protoTarget.get(ProtoInfo.PROVIDER), |
| /* proto_lang_toolchain_info */ protoLangToolchainInfo, |
| /* generated_files */ StarlarkList.immutableCopyOf(generatedFiles), |
| /* plugin_output */ pluginOutput == null ? Starlark.NONE : pluginOutput, |
| /* additional_args */ additionalArgs == null ? Starlark.NONE : additionalArgs, |
| /* additional_tools */ StarlarkList.immutableCopyOf(additionalTools), |
| /* additional_inputs */ additionalInputs == null |
| ? Depset.of(ElementType.EMPTY, NestedSetBuilder.emptySet(Order.STABLE_ORDER)) |
| : Depset.of( |
| Artifact.TYPE, NestedSetBuilder.wrap(Order.STABLE_ORDER, additionalInputs)), |
| /* resource_set */ resourceSet == null ? Starlark.NONE : resourceSet, |
| /* experimental_progress_message */ progressMessage), |
| ImmutableMap.of()); |
| } |
| |
| public static void compile( |
| RuleContext ruleContext, |
| ConfiguredTarget protoTarget, |
| StarlarkInfo protoLangToolchainInfo, |
| Iterable<Artifact> generatedFiles, |
| @Nullable Object pluginOutput, |
| String progressMessage) |
| throws RuleErrorException, InterruptedException { |
| StarlarkFunction compile = |
| (StarlarkFunction) ruleContext.getStarlarkDefinedBuiltin("proto_common_compile"); |
| ruleContext.initStarlarkRuleContext(); |
| ruleContext.callStarlarkOrThrowRuleError( |
| compile, |
| ImmutableList.of( |
| /* actions */ ruleContext.getStarlarkRuleContext().actions(), |
| /* proto_info */ protoTarget.get(ProtoInfo.PROVIDER), |
| /* proto_lang_toolchain_info */ protoLangToolchainInfo, |
| /* generated_files */ StarlarkList.immutableCopyOf(generatedFiles), |
| /* plugin_output */ pluginOutput == null ? Starlark.NONE : pluginOutput), |
| ImmutableMap.of("experimental_progress_message", progressMessage)); |
| } |
| |
| public static boolean shouldGenerateCode( |
| RuleContext ruleContext, |
| ConfiguredTarget protoTarget, |
| StarlarkInfo protoLangToolchainInfo, |
| String ruleName) |
| throws RuleErrorException, InterruptedException { |
| StarlarkFunction shouldGenerateCode = |
| (StarlarkFunction) |
| ruleContext.getStarlarkDefinedBuiltin("proto_common_experimental_should_generate_code"); |
| ruleContext.initStarlarkRuleContext(); |
| return (Boolean) |
| ruleContext.callStarlarkOrThrowRuleError( |
| shouldGenerateCode, |
| ImmutableList.of( |
| /* proto_info */ protoTarget.get(ProtoInfo.PROVIDER), |
| /* proto_lang_toolchain_info */ protoLangToolchainInfo, |
| /* rule_name */ ruleName, |
| /* target_label */ protoTarget.getLabel()), |
| ImmutableMap.of()); |
| } |
| |
| public static Sequence<Artifact> filterSources( |
| RuleContext ruleContext, ConfiguredTarget protoTarget, StarlarkInfo protoLangToolchainInfo) |
| throws RuleErrorException, InterruptedException { |
| StarlarkFunction filterSources = |
| (StarlarkFunction) |
| ruleContext.getStarlarkDefinedBuiltin("proto_common_experimental_filter_sources"); |
| ruleContext.initStarlarkRuleContext(); |
| try { |
| return Sequence.cast( |
| ((Tuple) |
| ruleContext.callStarlarkOrThrowRuleError( |
| filterSources, |
| ImmutableList.of( |
| /* proto_info */ protoTarget.get(ProtoInfo.PROVIDER), |
| /* proto_lang_toolchain_info */ protoLangToolchainInfo), |
| ImmutableMap.of())) |
| .get(0), |
| Artifact.class, |
| "included"); |
| } catch (EvalException e) { |
| |
| throw new RuleErrorException(e.getMessageWithStack()); |
| } |
| } |
| } |