diff options
author | Carmi Grushko <carmi@google.com> | 2015-12-11 18:44:57 +0000 |
---|---|---|
committer | David Chen <dzc@google.com> | 2015-12-13 18:27:17 +0000 |
commit | 08a99317b5b70c6671017dd9b7442f83ad0e5f8f (patch) | |
tree | 8103e3f565f844dd25f304b55978d223aa69f97a /src/main/java/com/google/devtools/build/lib/rules/proto/ProtoCompileAction.java | |
parent | 91f3e5ad3b4ca53136a66f8d26111e8a04623f4d (diff) |
Open-source code that creates proto-buffer compile actions.
--
MOS_MIGRATED_REVID=110008191
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/rules/proto/ProtoCompileAction.java')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/rules/proto/ProtoCompileAction.java | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoCompileAction.java b/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoCompileAction.java new file mode 100644 index 0000000000..cb7243bbb4 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/proto/ProtoCompileAction.java @@ -0,0 +1,270 @@ +// 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.common.base.Optional.absent; +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.collect.Iterables.isEmpty; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import com.google.common.collect.ImmutableList; +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.actions.ResourceSet; +import com.google.devtools.build.lib.analysis.FilesToRunProvider; +import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode; +import com.google.devtools.build.lib.analysis.RuleContext; +import com.google.devtools.build.lib.analysis.actions.CommandLine; +import com.google.devtools.build.lib.analysis.actions.CustomCommandLine; +import com.google.devtools.build.lib.analysis.actions.CustomCommandLine.CustomMultiArgv; +import com.google.devtools.build.lib.analysis.actions.SpawnAction; +import com.google.devtools.build.lib.util.LazyString; + +import java.util.List; + +/** + * An action to run the protocol compiler to generate sources from .proto files. + */ +public final class ProtoCompileAction { + + private static final String MNEMONIC = "GenProto"; + private static final ResourceSet GENPROTO_RESOURCE_SET = + ResourceSet.createWithRamCpuIo(100, .1, .0); + + private final RuleContext ruleContext; + private final SupportData supportData; + private final String language; + private final Iterable<Artifact> outputs; + private final List<? extends CharSequence> prefixArguments; + private final FilesToRunProvider langPluginTarget; + private final FilesToRunProvider compilerTarget; + private final List<String> suffixArguments; + + public static class Builder { + private RuleContext ruleContext; + private SupportData supportData; + private String language; + private String langPrefix; + private Iterable<Artifact> outputs; + private String langParameter; + private String langPluginName; + private String langPluginParameter; + private Supplier<String> langPluginParameterSupplier; + private boolean hasServices; + + public Builder setRuleContext(RuleContext ruleContext) { + this.ruleContext = ruleContext; + return this; + } + + public Builder setSupportData(SupportData supportData) { + this.supportData = supportData; + return this; + } + + public Builder setLanguage(String language) { + this.language = language; + return this; + } + + public Builder setLangPrefix(String langPrefix) { + this.langPrefix = langPrefix; + return this; + } + + public Builder setHasServices(boolean hasServices) { + this.hasServices = hasServices; + return this; + } + + public Builder setOutputs(Iterable<Artifact> outputs) { + this.outputs = outputs; + return this; + } + + public Builder setLangParameter(String langParameter) { + this.langParameter = langParameter; + return this; + } + + public Builder setLangPluginName(String langPluginName) { + this.langPluginName = langPluginName; + return this; + } + + public Builder setLangPluginParameter(String langPluginParameter) { + this.langPluginParameter = langPluginParameter; + return this; + } + + public Builder setLangPluginParameterSupplier(Supplier<String> langPluginParameterSupplier) { + this.langPluginParameterSupplier = langPluginParameterSupplier; + return this; + } + + public Builder(RuleContext ruleContext, SupportData supportData, String language, + String langPrefix, Iterable<Artifact> outputs) { + this.ruleContext = ruleContext; + this.supportData = supportData; + this.language = language; + this.langPrefix = langPrefix; + this.outputs = outputs; + } + + public Optional<ProtoCompileAction> build() { + checkState(langPluginParameter == null || langPluginParameterSupplier == null, + "Only one of {langPluginParameter, langPluginParameterSupplier} should be set."); + + final Supplier<String> langPluginParameter1 = + langPluginParameter == null + ? langPluginParameterSupplier + : Suppliers.ofInstance(langPluginParameter); + if (isEmpty(outputs)) { + return absent(); + } + + FilesToRunProvider langPluginTarget = null; + List<? extends CharSequence> prefixArguments; + if (langPluginName != null) { + Preconditions.checkArgument(langParameter == null); + Preconditions.checkArgument(langPluginParameter1 != null); + // We pass a separate langPluginName as there are plugins that cannot be overridden + // and thus we have to deal with "$xx_plugin" and "xx_plugin". + langPluginTarget = ruleContext.getExecutablePrerequisite(langPluginName, Mode.HOST); + if (ruleContext.hasErrors()) { + return absent(); + } + LazyString lazyLangPlugingFlag = + new LazyString() { + @Override + public String toString() { + return String.format("--%s_out=%s", langPrefix, langPluginParameter1.get()); + } + }; + prefixArguments = + ImmutableList.of( + String.format( + "--plugin=protoc-gen-%s=%s", + langPrefix, + langPluginTarget.getExecutable().getExecPathString()), + lazyLangPlugingFlag); + } else { + prefixArguments = + (langParameter != null) ? ImmutableList.of(langParameter) : ImmutableList.<String>of(); + } + + List<String> suffixArguments = + hasServices ? ImmutableList.<String>of() : ImmutableList.of("--disallow_services"); + + FilesToRunProvider compilerTarget = + ruleContext.getExecutablePrerequisite("$compiler", Mode.HOST); + + if (ruleContext.hasErrors()) { + return absent(); + } + + return Optional.of( + new ProtoCompileAction( + ruleContext, + supportData, + language, + suffixArguments, + outputs, + prefixArguments, + langPluginTarget, + compilerTarget)); + } + } + + public ProtoCompileAction( + RuleContext ruleContext, + SupportData supportData, + String language, + List<String> suffixArguments, + Iterable<Artifact> outputs, + List<? extends CharSequence> prefixArguments, + FilesToRunProvider langPluginTarget, + FilesToRunProvider compilerTarget) { + this.ruleContext = ruleContext; + this.supportData = supportData; + this.language = language; + this.suffixArguments = suffixArguments; + this.outputs = outputs; + this.prefixArguments = prefixArguments; + this.langPluginTarget = langPluginTarget; + this.compilerTarget = compilerTarget; + } + + public SpawnAction.Builder createAction(CommandLine commandLine) { + SpawnAction.Builder builder = + new SpawnAction.Builder().addTransitiveInputs(supportData.getTransitiveImports()); + + // We also depend on the strict protodeps result to ensure this is run. + if (supportData.getUsedDirectDeps() != null) { + builder.addInput(supportData.getUsedDirectDeps()); + } + + if (langPluginTarget != null) { + builder.addTool(langPluginTarget); + } + + builder + .addOutputs(outputs) + .setResources(GENPROTO_RESOURCE_SET) + .useDefaultShellEnvironment() + .setExecutable(compilerTarget) + .setCommandLine(commandLine) + .setProgressMessage("Generating " + language + " proto_library " + ruleContext.getLabel()) + .setMnemonic(MNEMONIC); + + return builder; + } + + /* Commandline generator for protoc invocations. */ + public CustomCommandLine.Builder protoCompileCommandLine() { + CustomCommandLine.Builder arguments = CustomCommandLine.builder(); + for (CharSequence charSequence : prefixArguments) { + arguments.add(charSequence); + } + arguments.add(ruleContext.getFragment(ProtoConfiguration.class).protocOpts()); + + // Add include maps + arguments.add( + new CustomMultiArgv() { + @Override + public Iterable<String> argv() { + ImmutableList.Builder<String> builder = ImmutableList.builder(); + for (Artifact artifact : supportData.getTransitiveImports()) { + builder.add( + "-I" + + artifact.getRootRelativePath().getPathString() + + "=" + + artifact.getExecPathString()); + } + return builder.build(); + } + }); + + for (Artifact src : supportData.getDirectProtoSources()) { + arguments.addPath(src.getRootRelativePath()); + } + + arguments.add(suffixArguments); + return arguments; + } +} + |