diff options
Diffstat (limited to 'src/main/java')
4 files changed, 524 insertions, 319 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProtoLibrary.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProtoLibrary.java index 344d5d3f14..dbf4d4b353 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProtoLibrary.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProtoLibrary.java @@ -32,20 +32,28 @@ public class ObjcProtoLibrary implements RuleConfiguredTargetFactory { @Override public ConfiguredTarget create(final RuleContext ruleContext) throws InterruptedException, RuleErrorException { - XcodeProvider.Builder xcodeProviderBuilder = new XcodeProvider.Builder(); + + ProtoAttributes attributes = new ProtoAttributes(ruleContext); + attributes.validate(); + + if (attributes.hasPortableProtoFilters()) { + return createProtobufTarget(ruleContext); + } else { + return createProtocolBuffers2Target(ruleContext); + } + } + + private ConfiguredTarget createProtobufTarget(RuleContext ruleContext) + throws InterruptedException, RuleErrorException { NestedSetBuilder<Artifact> filesToBuild = NestedSetBuilder.stableOrder(); + XcodeProvider.Builder xcodeProviderBuilder = new XcodeProvider.Builder(); ProtoSupport protoSupport = new ProtoSupport(ruleContext, TargetType.PROTO_TARGET) - .validate() .addXcodeProviderOptions(xcodeProviderBuilder) .addFilesToBuild(filesToBuild) .registerActions(); - if (ruleContext.hasErrors()) { - return null; - } - ObjcCommon common = protoSupport.getCommon(); filesToBuild.addAll(common.getCompiledArchive().asSet()); @@ -57,8 +65,6 @@ public class ObjcProtoLibrary implements RuleConfiguredTargetFactory { xcodeProviderBuilder, new Attribute(ObjcRuleClasses.PROTO_LIB_ATTR, Mode.TARGET)) .registerActions(xcodeProviderBuilder.build()); - boolean usesProtobufLibrary = protoSupport.usesProtobufLibrary(); - boolean experimentalAutoUnion = ObjcRuleClasses.objcConfiguration(ruleContext).experimentalAutoTopLevelUnionObjCProtos(); @@ -66,7 +72,7 @@ public class ObjcProtoLibrary implements RuleConfiguredTargetFactory { // If the experimental flag is not set, or if it's set and doesn't use the protobuf library, // register the compilation actions, as the output needs to be linked in the final binary. - if (!experimentalAutoUnion || !usesProtobufLibrary) { + if (!experimentalAutoUnion) { compilationSupport.registerCompileAndArchiveActions(common); } else { // Even though there is nothing to compile, still generate a module map based on this target @@ -79,4 +85,25 @@ public class ObjcProtoLibrary implements RuleConfiguredTargetFactory { .addProvider(ObjcProvider.class, common.getObjcProvider()) .build(); } + + private ConfiguredTarget createProtocolBuffers2Target(RuleContext ruleContext) + throws InterruptedException, RuleErrorException { + NestedSetBuilder<Artifact> filesToBuild = NestedSetBuilder.stableOrder(); + + ProtocolBuffers2Support protoSupport = + new ProtocolBuffers2Support(ruleContext) + .registerGenerationActions() + .registerCompilationActions() + .addFilesToBuild(filesToBuild); + + XcodeProvider xcodeProvider = protoSupport.getXcodeProvider(); + + new XcodeSupport(ruleContext).registerActions(xcodeProvider).addFilesToBuild(filesToBuild); + + return ObjcRuleClasses.ruleConfiguredTarget(ruleContext, filesToBuild.build()) + .addProvider(ObjcProvider.class, protoSupport.getObjcProvider()) + .addProvider(XcodeProvider.class, xcodeProvider) + .build(); + } + } diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ProtoAttributes.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ProtoAttributes.java new file mode 100644 index 0000000000..bc7e8bdc7a --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ProtoAttributes.java @@ -0,0 +1,220 @@ +// 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.objc; + +import static com.google.devtools.build.lib.packages.BuildType.LABEL; +import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.analysis.PrerequisiteArtifacts; +import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode; +import com.google.devtools.build.lib.analysis.RuleContext; +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.RuleClass.ConfiguredTargetFactory.RuleErrorException; +import com.google.devtools.build.lib.rules.proto.ProtoSourcesProvider; +import com.google.devtools.build.lib.syntax.Type; +import com.google.devtools.build.lib.util.FileType; + +/** Common rule attributes used by an objc_proto_library. */ +final class ProtoAttributes { + @VisibleForTesting + static final String FILES_DEPRECATED_WARNING = + "Using files and filegroups in objc_proto_library is deprecated"; + + @VisibleForTesting + static final String NO_PROTOS_ERROR = + "no protos to compile - a non-empty deps attribute is required"; + + @VisibleForTesting + static final String PORTABLE_PROTO_FILTERS_NOT_EXCLUSIVE_ERROR = + "The portable_proto_filters attribute is incompatible with the options_file, output_cpp, " + + "per_proto_includes and use_objc_header_names attributes."; + + @VisibleForTesting + static final String PORTABLE_PROTO_FILTERS_EMPTY_ERROR = + "The portable_proto_filters attribute can't be empty"; + + private final RuleContext ruleContext; + + /** + * Creates a new ProtoAttributes object that wraps over objc_proto_library's attributes. + * + * @param ruleContext context of the objc_proto_library to wrap + */ + ProtoAttributes(RuleContext ruleContext) { + this.ruleContext = ruleContext; + } + + /** + * Validates the proto attributes for this target. + * + * <ul> + * <li>Validates that there are protos specified to be compiled. + * <li>Validates that, when enabling the open source protobuf library, the options for the PB2 are + * not specified also. + * <li>Validates that, when enabling the open source protobuf library, the rule specifies at least + * one portable proto filter file. + * </ul> + */ + public void validate() throws RuleErrorException { + + if (getProtoFiles().isEmpty()) { + ruleContext.throwWithRuleError(NO_PROTOS_ERROR); + } + + PrerequisiteArtifacts prerequisiteArtifacts = + ruleContext.getPrerequisiteArtifacts("deps", Mode.TARGET); + ImmutableList<Artifact> protos = prerequisiteArtifacts.filter(FileType.of(".proto")).list(); + if (!protos.isEmpty()) { + ruleContext.attributeWarning("deps", FILES_DEPRECATED_WARNING); + } + + if (ruleContext + .attributes() + .isAttributeValueExplicitlySpecified(ObjcProtoLibraryRule.PORTABLE_PROTO_FILTERS_ATTR)) { + if (getPortableProtoFilters().isEmpty()) { + ruleContext.throwWithRuleError(PORTABLE_PROTO_FILTERS_EMPTY_ERROR); + } + + if (outputsCpp() + || usesObjcHeaderNames() + || needsPerProtoIncludes() + || getOptionsFile().isPresent()) { + ruleContext.throwWithRuleError(PORTABLE_PROTO_FILTERS_NOT_EXCLUSIVE_ERROR); + } + } else { + if (outputsCpp()) { + ruleContext.ruleWarning( + "The output_cpp attribute has been deprecated. Please " + + "refer to b/29342376 for information on possible alternatives."); + } + if (!usesObjcHeaderNames()) { + ruleContext.ruleWarning( + "As part of the migration process, it is recommended to enable " + + "use_objc_header_names. Please refer to b/29368416 for more information."); + } + } + } + + /** Returns whether the generated files should be C++ or Objective C. */ + boolean outputsCpp() { + if (ruleContext.attributes().has(ObjcProtoLibraryRule.OUTPUT_CPP_ATTR, Type.BOOLEAN)) { + return ruleContext.attributes().get(ObjcProtoLibraryRule.OUTPUT_CPP_ATTR, Type.BOOLEAN); + } + return false; + } + + /** Returns whether the generated header files should have be of type pb.h or pbobjc.h. */ + boolean usesObjcHeaderNames() { + if (ruleContext + .attributes() + .has(ObjcProtoLibraryRule.USE_OBJC_HEADER_NAMES_ATTR, Type.BOOLEAN)) { + return ruleContext + .attributes() + .get(ObjcProtoLibraryRule.USE_OBJC_HEADER_NAMES_ATTR, Type.BOOLEAN); + } + return false; + } + + /** Returns whether the includes should include each of the proto generated headers. */ + boolean needsPerProtoIncludes() { + if (ruleContext.attributes().has(ObjcProtoLibraryRule.PER_PROTO_INCLUDES_ATTR, Type.BOOLEAN)) { + return ruleContext + .attributes() + .get(ObjcProtoLibraryRule.PER_PROTO_INCLUDES_ATTR, Type.BOOLEAN); + } + return false; + } + + /** Returns whether to use the protobuf library instead of the PB2 library. */ + boolean hasPortableProtoFilters() { + return ruleContext + .attributes() + .isAttributeValueExplicitlySpecified(ObjcProtoLibraryRule.PORTABLE_PROTO_FILTERS_ATTR); + } + + /** Returns the list of portable proto filters. */ + ImmutableList<Artifact> getPortableProtoFilters() { + if (ruleContext + .attributes() + .has(ObjcProtoLibraryRule.PORTABLE_PROTO_FILTERS_ATTR, LABEL_LIST)) { + return ruleContext + .getPrerequisiteArtifacts(ObjcProtoLibraryRule.PORTABLE_PROTO_FILTERS_ATTR, Mode.HOST) + .list(); + } + return ImmutableList.of(); + } + + /** Returns the list of well known type protos. */ + ImmutableList<Artifact> getWellKnownTypeProtos() { + return ruleContext + .getPrerequisiteArtifacts(ObjcRuleClasses.PROTOBUF_WELL_KNOWN_TYPES, Mode.HOST) + .list(); + } + + /** Returns the options file, or {@link Optional#absent} if it was not specified. */ + Optional<Artifact> getOptionsFile() { + if (ruleContext.attributes().has(ObjcProtoLibraryRule.OPTIONS_FILE_ATTR, LABEL)) { + return Optional.fromNullable( + ruleContext.getPrerequisiteArtifact(ObjcProtoLibraryRule.OPTIONS_FILE_ATTR, Mode.HOST)); + } + return Optional.absent(); + } + + /** Returns the list of proto files to compile. */ + NestedSet<Artifact> getProtoFiles() { + return NestedSetBuilder.<Artifact>stableOrder() + .addAll(getProtoDepsFiles()) + .addTransitive(getProtoDepsSources()) + .build(); + } + + /** Returns the proto compiler to be used. */ + Artifact getProtoCompiler() { + return ruleContext.getPrerequisiteArtifact(ObjcRuleClasses.PROTO_COMPILER_ATTR, Mode.HOST); + } + + /** Returns the list of files needed by the proto compiler. */ + Iterable<Artifact> getProtoCompilerSupport() { + return ruleContext + .getPrerequisiteArtifacts(ObjcRuleClasses.PROTO_COMPILER_SUPPORT_ATTR, Mode.HOST) + .list(); + } + + /** Returns the sets of proto files that were added using proto_library dependencies. */ + private NestedSet<Artifact> getProtoDepsSources() { + NestedSetBuilder<Artifact> artifacts = NestedSetBuilder.stableOrder(); + Iterable<ProtoSourcesProvider> providers = + ruleContext.getPrerequisites("deps", Mode.TARGET, ProtoSourcesProvider.class); + for (ProtoSourcesProvider provider : providers) { + artifacts.addTransitive(provider.getTransitiveProtoSources()); + } + return artifacts.build(); + } + + /** + * Returns the list of proto files that were added directly into the deps attributes. This way of + * specifying the protos is deprecated, and displays a warning when the target does so. + */ + private ImmutableList<Artifact> getProtoDepsFiles() { + PrerequisiteArtifacts prerequisiteArtifacts = + ruleContext.getPrerequisiteArtifacts("deps", Mode.TARGET); + return prerequisiteArtifacts.filter(FileType.of(".proto")).list(); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ProtoSupport.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ProtoSupport.java index 7863b1c4ca..bcfc5e31ca 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ProtoSupport.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ProtoSupport.java @@ -16,19 +16,14 @@ package com.google.devtools.build.lib.rules.objc; import static com.google.common.base.CaseFormat.LOWER_UNDERSCORE; import static com.google.common.base.CaseFormat.UPPER_CAMEL; -import static com.google.devtools.build.lib.packages.BuildType.LABEL; -import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST; -import com.google.common.annotations.VisibleForTesting; import com.google.common.base.CharMatcher; -import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Ordering; import com.google.devtools.build.lib.actions.Artifact; -import com.google.devtools.build.lib.analysis.PrerequisiteArtifacts; 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.CustomCommandLine; @@ -38,15 +33,11 @@ import com.google.devtools.build.lib.cmdline.LabelSyntaxException; 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.RuleClass.ConfiguredTargetFactory.RuleErrorException; -import com.google.devtools.build.lib.rules.proto.ProtoSourcesProvider; -import com.google.devtools.build.lib.syntax.Type; import com.google.devtools.build.lib.util.FileType; import com.google.devtools.build.lib.vfs.FileSystemUtils; import com.google.devtools.build.lib.vfs.PathFragment; - import java.util.ArrayList; -import javax.annotation.Nullable; /** * Support for generating Objective C proto static libraries that registers actions that generate @@ -66,14 +57,6 @@ import javax.annotation.Nullable; final class ProtoSupport { private static final PathFragment BAZEL_TOOLS_PREFIX = new PathFragment("external/bazel_tools/"); - private static final Function<Artifact, PathFragment> PARENT_PATHFRAGMENT = - new Function<Artifact, PathFragment>() { - @Override - public PathFragment apply(Artifact input) { - return input.getExecPath().getParentDirectory(); - } - }; - /** * Type of target that is generating the protos. There is a distinction because when generating * the protos from an objc_proto_library, the attributes of the rule need to be considered, but @@ -93,23 +76,6 @@ final class ProtoSupport { LINKING_TARGET, } - @VisibleForTesting - static final String NO_PROTOS_ERROR = - "no protos to compile - a non-empty deps attribute is required"; - - @VisibleForTesting - static final String FILES_DEPRECATED_WARNING = - "Using files and filegroups in objc_proto_library is deprecated"; - - @VisibleForTesting - static final String PORTABLE_PROTO_FILTERS_NOT_EXCLUSIVE_ERROR = - "The portable_proto_filters attribute is incompatible with the options_file, output_cpp, " - + "per_proto_includes and use_objc_header_names attributes."; - - @VisibleForTesting - static final String PORTABLE_PROTO_FILTERS_EMPTY_ERROR = - "The portable_proto_filters attribute can't be empty"; - private static final String UNIQUE_DIRECTORY_NAME = "_generated_protos"; /** @@ -120,7 +86,7 @@ final class ProtoSupport { ImmutableSet.of("url", "http", "https"); private final RuleContext ruleContext; - private final Attributes attributes; + private final ProtoAttributes attributes; private final TargetType targetType; private final IntermediateArtifacts intermediateArtifacts; @@ -132,7 +98,7 @@ final class ProtoSupport { */ public ProtoSupport(RuleContext ruleContext, TargetType targetType) { this.ruleContext = ruleContext; - this.attributes = new Attributes(ruleContext); + this.attributes = new ProtoAttributes(ruleContext); this.targetType = targetType; if (targetType != TargetType.PROTO_TARGET) { // Use a a prefixed version of the intermediate artifacts to avoid naming collisions, as @@ -146,47 +112,6 @@ final class ProtoSupport { } /** - * Validates proto support. - * <ul> - * <li>Validates that there are protos specified to be compiled. - * <li>Validates that, when enabling the open source protobuf library, the options for the PB2 - * are not specified also. - * <li>Validates that, when enabling the open source protobuf library, the rule specifies at least - * one portable proto filter file. - * </ul> - * - * @return this proto support - */ - public ProtoSupport validate() { - if (attributes.getProtoFiles().isEmpty()) { - ruleContext.ruleError(NO_PROTOS_ERROR); - } - - if (usesProtobufLibrary()) { - if (attributes.getPortableProtoFilters().isEmpty()) { - ruleContext.ruleError(PORTABLE_PROTO_FILTERS_EMPTY_ERROR); - } - - if (attributes.outputsCpp() - || attributes.usesObjcHeaderNames() - || attributes.needsPerProtoIncludes() - || attributes.getOptionsFile() != null) { - ruleContext.ruleError(PORTABLE_PROTO_FILTERS_NOT_EXCLUSIVE_ERROR); - } - } else { - if (attributes.outputsCpp()) { - ruleContext.ruleWarning("The output_cpp attribute has been deprecated. Please " - + "refer to b/29342376 for information on possible alternatives."); - } - if (!attributes.usesObjcHeaderNames()) { - ruleContext.ruleWarning("As part of the migration process, it is recommended to enable " - + "use_objc_header_names. Please refer to b/29368416 for more information."); - } - } - return this; - } - - /** * Returns the intermediate artifacts associated with generated proto compilation. */ public IntermediateArtifacts getIntermediateArtifacts() { @@ -224,7 +149,7 @@ final class ProtoSupport { ruleContext.getPrerequisites( ObjcRuleClasses.PROTO_LIB_ATTR, Mode.TARGET, ObjcProvider.class)); - if (usesProtobufLibrary() && experimentalAutoUnion()) { + if (experimentalAutoUnion()) { commonBuilder.addDirectDependencyHeaderSearchPaths(getUserHeaderSearchPaths()); } else { commonBuilder.addUserHeaderSearchPaths(getUserHeaderSearchPaths()); @@ -288,13 +213,8 @@ final class ProtoSupport { .setPchFile(Optional.<Artifact>absent()) .addAdditionalHdrs(getGeneratedHeaders()); - if (targetType == TargetType.PROTO_TARGET && !usesProtobufLibrary()) { - builder.addAdditionalHdrs(generatedSources); - } - if (experimentalAutoUnion()) { - if ((targetType == TargetType.PROTO_TARGET && !usesProtobufLibrary()) - || targetType == TargetType.LINKING_TARGET) { + if (targetType == TargetType.LINKING_TARGET) { builder.addNonArcSrcs(generatedSources); } } else { @@ -310,28 +230,7 @@ final class ProtoSupport { * Returns the include paths for the generated protos. */ public ImmutableSet<PathFragment> getUserHeaderSearchPaths() { - PathFragment workspaceRelativeOutputDir = getWorkspaceRelativeOutputDir(); - - ImmutableSet.Builder<PathFragment> searchPathEntriesBuilder = - new ImmutableSet.Builder<PathFragment>().add(workspaceRelativeOutputDir); - - if (attributes.needsPerProtoIncludes()) { - PathFragment generatedProtoDir = - new PathFragment(workspaceRelativeOutputDir, ruleContext.getLabel().getPackageFragment()); - - searchPathEntriesBuilder - .add(generatedProtoDir) - .addAll(Iterables.transform(getGeneratedHeaders(), PARENT_PATHFRAGMENT)); - } - - return searchPathEntriesBuilder.build(); - } - - /** - * Returns whether the rule is configured to use the protobuf library. - */ - public boolean usesProtobufLibrary() { - return attributes.hasPortableProtoFilters() || targetType == TargetType.LINKING_TARGET; + return ImmutableSet.of(getWorkspaceRelativeOutputDir()); } private Iterable<Artifact> getAllProtoSources() { @@ -440,16 +339,11 @@ final class ProtoSupport { } private ImmutableList<Artifact> getGeneratedHeaders() { - boolean useObjcName = attributes.usesObjcHeaderNames() || usesProtobufLibrary(); - return generatedOutputArtifacts(FileType.of(".pb" + (useObjcName ? "objc.h" : ".h"))); + return generatedOutputArtifacts(FileType.of(".pbobjc.h")); } private ImmutableList<Artifact> getGeneratedSources() { - return generatedOutputArtifacts( - FileType.of( - ".pb" - + (usesProtobufLibrary() ? "objc" : "") - + (attributes.outputsCpp() ? ".cc" : ".m"))); + return generatedOutputArtifacts(FileType.of(".pbobjc.m")); } private NestedSet<Artifact> getGenerateActionInputs() { @@ -461,11 +355,6 @@ final class ProtoSupport { .addAll(attributes.getProtoCompilerSupport()) .addTransitive(getPortableProtoFilters()); - Artifact optionsFile = attributes.getOptionsFile(); - if (optionsFile != null) { - inputsBuilder.add(optionsFile); - } - return inputsBuilder.build(); } @@ -474,41 +363,6 @@ final class ProtoSupport { } private CustomCommandLine getGenerateCommandLine() { - if (usesProtobufLibrary()) { - return getProtobufCommandLine(); - } else { - return getPb2CommandLine(); - } - } - - private CustomCommandLine getPb2CommandLine() { - CustomCommandLine.Builder commandLineBuilder = - new CustomCommandLine.Builder() - .add(attributes.getProtoCompiler().getExecPathString()) - .add("--input-file-list") - .add(getProtoInputListFile().getExecPathString()) - .add("--output-dir") - .add(getWorkspaceRelativeOutputDir().getSafePathString()) - .add("--working-dir") - .add("."); - - if (attributes.getOptionsFile() != null) { - commandLineBuilder - .add("--compiler-options-path") - .add(attributes.getOptionsFile().getExecPathString()); - } - - if (attributes.outputsCpp()) { - commandLineBuilder.add("--generate-cpp"); - } - - if (attributes.usesObjcHeaderNames()) { - commandLineBuilder.add("--use-objc-header-names"); - } - return commandLineBuilder.build(); - } - - private CustomCommandLine getProtobufCommandLine() { return new CustomCommandLine.Builder() .add(attributes.getProtoCompiler().getExecPathString()) .add("--input-file-list") @@ -526,17 +380,7 @@ final class ProtoSupport { ImmutableList.Builder<Artifact> builder = new ImmutableList.Builder<>(); for (Artifact protoFile : getFilteredProtoSources()) { String protoFileName = FileSystemUtils.removeExtension(protoFile.getFilename()); - String generatedOutputName; - if (attributes.outputsCpp()) { - generatedOutputName = protoFileName; - } else if (usesProtobufLibrary()) { - // The protobuf library generates filenames with some slight modifications. - generatedOutputName = generateProtobufFilename(protoFileName); - } else { - String lowerUnderscoreBaseName = protoFileName.replace('-', '_').toLowerCase(); - generatedOutputName = LOWER_UNDERSCORE.to(UPPER_CAMEL, lowerUnderscoreBaseName); - } - + String generatedOutputName = generateProtobufFilename(protoFileName); PathFragment generatedFilePath = new PathFragment( protoFile.getRootRelativePath().getParentDirectory(), @@ -618,150 +462,4 @@ final class ProtoSupport { } return casedSegments.toString(); } - - /** - * Common rule attributes used by an Objective C proto library. - */ - private static class Attributes { - private final RuleContext ruleContext; - - private Attributes(RuleContext ruleContext) { - this.ruleContext = ruleContext; - } - - /** - * Returns whether the generated files should be C++ or Objective C. - */ - boolean outputsCpp() { - if (ruleContext.attributes().has(ObjcProtoLibraryRule.OUTPUT_CPP_ATTR, Type.BOOLEAN)) { - return ruleContext.attributes().get(ObjcProtoLibraryRule.OUTPUT_CPP_ATTR, Type.BOOLEAN); - } - return false; - } - - /** - * Returns whether the generated header files should have be of type pb.h or pbobjc.h. - */ - boolean usesObjcHeaderNames() { - if (ruleContext - .attributes() - .has(ObjcProtoLibraryRule.USE_OBJC_HEADER_NAMES_ATTR, Type.BOOLEAN)) { - return ruleContext - .attributes() - .get(ObjcProtoLibraryRule.USE_OBJC_HEADER_NAMES_ATTR, Type.BOOLEAN); - } - return false; - } - - /** - * Returns whether the includes should include each of the proto generated headers. - */ - boolean needsPerProtoIncludes() { - if (ruleContext - .attributes() - .has(ObjcProtoLibraryRule.PER_PROTO_INCLUDES_ATTR, Type.BOOLEAN)) { - return ruleContext - .attributes() - .get(ObjcProtoLibraryRule.PER_PROTO_INCLUDES_ATTR, Type.BOOLEAN); - } - return false; - } - - /** - * Returns whether to use the protobuf library instead of the PB2 library. - */ - boolean hasPortableProtoFilters() { - return ruleContext - .attributes() - .isAttributeValueExplicitlySpecified(ObjcProtoLibraryRule.PORTABLE_PROTO_FILTERS_ATTR); - } - - /** - * Returns the list of portable proto filters. - */ - ImmutableList<Artifact> getPortableProtoFilters() { - if (ruleContext - .attributes() - .has(ObjcProtoLibraryRule.PORTABLE_PROTO_FILTERS_ATTR, LABEL_LIST)) { - return ruleContext - .getPrerequisiteArtifacts(ObjcProtoLibraryRule.PORTABLE_PROTO_FILTERS_ATTR, Mode.HOST) - .list(); - } - return ImmutableList.of(); - } - - /** - * Returns the list of well known type protos. - */ - ImmutableList<Artifact> getWellKnownTypeProtos() { - return ruleContext - .getPrerequisiteArtifacts(ObjcRuleClasses.PROTOBUF_WELL_KNOWN_TYPES, Mode.HOST) - .list(); - } - - /** - * Returns the options file, or null if it was not specified. - */ - @Nullable - Artifact getOptionsFile() { - if (ruleContext.attributes().has(ObjcProtoLibraryRule.OPTIONS_FILE_ATTR, LABEL)) { - return ruleContext.getPrerequisiteArtifact( - ObjcProtoLibraryRule.OPTIONS_FILE_ATTR, Mode.HOST); - } - return null; - } - - /** - * Returns the proto compiler to be used. - */ - Artifact getProtoCompiler() { - return ruleContext.getPrerequisiteArtifact(ObjcRuleClasses.PROTO_COMPILER_ATTR, Mode.HOST); - } - - /** - * Returns the list of files needed by the proto compiler. - */ - ImmutableList<Artifact> getProtoCompilerSupport() { - return ruleContext - .getPrerequisiteArtifacts(ObjcRuleClasses.PROTO_COMPILER_SUPPORT_ATTR, Mode.HOST) - .list(); - } - - /** - * Returns the list of proto files to compile. - */ - NestedSet<Artifact> getProtoFiles() { - return NestedSetBuilder.<Artifact>stableOrder() - .addAll(getProtoDepsFiles()) - .addTransitive(getProtoDepsSources()) - .build(); - } - - /** - * Returns the list of proto files that were added directly into the deps attributes. This way - * of specifying the protos is deprecated, and displays a warning when the target does so. - */ - private ImmutableList<Artifact> getProtoDepsFiles() { - PrerequisiteArtifacts prerequisiteArtifacts = - ruleContext.getPrerequisiteArtifacts("deps", Mode.TARGET); - ImmutableList<Artifact> protos = prerequisiteArtifacts.filter(FileType.of(".proto")).list(); - if (!protos.isEmpty()) { - ruleContext.attributeWarning("deps", FILES_DEPRECATED_WARNING); - } - return protos; - } - - /** - * Returns the list of proto files that were added using proto_library dependencies. - */ - private NestedSet<Artifact> getProtoDepsSources() { - NestedSetBuilder<Artifact> artifacts = NestedSetBuilder.stableOrder(); - Iterable<ProtoSourcesProvider> providers = - ruleContext.getPrerequisites("deps", Mode.TARGET, ProtoSourcesProvider.class); - for (ProtoSourcesProvider provider : providers) { - artifacts.addTransitive(provider.getTransitiveProtoSources()); - } - return artifacts.build(); - } - } } diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ProtocolBuffers2Support.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ProtocolBuffers2Support.java new file mode 100644 index 0000000000..49aa2697be --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ProtocolBuffers2Support.java @@ -0,0 +1,260 @@ +// 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.objc; + +import static com.google.common.base.CaseFormat.LOWER_UNDERSCORE; +import static com.google.common.base.CaseFormat.UPPER_CAMEL; +import static com.google.devtools.build.lib.rules.objc.XcodeProductType.LIBRARY_STATIC; + +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Iterables; +import com.google.common.collect.Ordering; +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.analysis.actions.CustomCommandLine; +import com.google.devtools.build.lib.analysis.actions.FileWriteAction; +import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; +import com.google.devtools.build.lib.vfs.FileSystemUtils; +import com.google.devtools.build.lib.vfs.PathFragment; + +/** + * Support for generating Objective C proto static libraries that registers actions which generate + * and compile the Objective C protos by using the deprecated ProtocolBuffers2 library and compiler. + * + * <p>Methods on this class can be called in any order without impacting the result. + */ +final class ProtocolBuffers2Support { + + private static final String UNIQUE_DIRECTORY_NAME = "_generated_protos"; + + private static final Function<Artifact, PathFragment> PARENT_PATHFRAGMENT = + new Function<Artifact, PathFragment>() { + @Override + public PathFragment apply(Artifact input) { + return input.getExecPath().getParentDirectory(); + } + }; + + private final RuleContext ruleContext; + private final ProtoAttributes attributes; + + /** + * Creates a new proto support for the ProtocolBuffers2 library. + * + * @param ruleContext context this proto library is constructed in + */ + public ProtocolBuffers2Support(RuleContext ruleContext) { + this.ruleContext = ruleContext; + this.attributes = new ProtoAttributes(ruleContext); + } + + /** + * Register the proto generation actions. These actions generate the ObjC/CPP code to be compiled + * by this rule, and also generates the Xcode project call the protoc compiler to generate + * ObjC/CPP code. + */ + public ProtocolBuffers2Support registerGenerationActions() throws InterruptedException { + ruleContext.registerAction( + new FileWriteAction( + ruleContext.getActionOwner(), + getProtoInputsFile(), + getProtoInputsFileContents(attributes.getProtoFiles()), + false)); + + ruleContext.registerAction( + ObjcRuleClasses.spawnOnDarwinActionBuilder() + .setMnemonic("GenObjcPB2Protos") + .addInput(attributes.getProtoCompiler()) + .addInputs(attributes.getProtoCompilerSupport()) + .addInput(getProtoInputsFile()) + .addInputs(attributes.getProtoFiles()) + .addInputs(attributes.getOptionsFile().asSet()) + .addOutputs(getGeneratedProtoOutputs(getHeaderExtension())) + .addOutputs(getGeneratedProtoOutputs(getSourceExtension())) + .setExecutable(new PathFragment("/usr/bin/python")) + .setCommandLine(getGenerationCommandLine()) + .build(ruleContext)); + return this; + } + + /** Registers the actions that will compile the generated code. */ + public ProtocolBuffers2Support registerCompilationActions() { + new CompilationSupport(ruleContext) + .registerCompileAndArchiveActions(getCommon()) + .registerGenerateModuleMapAction(Optional.of(getCompilationArtifacts())); + return this; + } + + /** Adds the generated files to the set of files to be output when this rule is built. */ + public ProtocolBuffers2Support addFilesToBuild(NestedSetBuilder<Artifact> filesToBuild) + throws InterruptedException { + filesToBuild + .addAll(getGeneratedProtoOutputs(getHeaderExtension())) + .addAll(getGeneratedProtoOutputs(getSourceExtension())) + .addAll(getCompilationArtifacts().getArchive().asSet()); + return this; + } + + /** Returns the ObjcProvider for this target. */ + public ObjcProvider getObjcProvider() { + return getCommon().getObjcProvider(); + } + + /** Returns the XcodeProvider for this target. */ + public XcodeProvider getXcodeProvider() { + XcodeProvider.Builder xcodeProviderBuilder = + new XcodeProvider.Builder() + .addUserHeaderSearchPaths(getUserHeaderSearchPaths()) + .setCompilationArtifacts(getCompilationArtifacts()); + + new XcodeSupport(ruleContext) + .addXcodeSettings(xcodeProviderBuilder, getCommon().getObjcProvider(), LIBRARY_STATIC) + .addDependencies( + xcodeProviderBuilder, new Attribute(ObjcRuleClasses.PROTO_LIB_ATTR, Mode.TARGET)); + + return xcodeProviderBuilder.build(); + } + + private String getHeaderExtension() { + return ".pb" + (attributes.usesObjcHeaderNames() ? "objc.h" : ".h"); + } + + private String getSourceExtension() { + return ".pb" + (attributes.outputsCpp() ? ".cc" : ".m"); + } + + private ObjcCommon getCommon() { + return new ObjcCommon.Builder(ruleContext) + .setIntermediateArtifacts(new IntermediateArtifacts(ruleContext, "")) + .setHasModuleMap() + .setCompilationArtifacts(getCompilationArtifacts()) + .addUserHeaderSearchPaths(getUserHeaderSearchPaths()) + .addDepObjcProviders( + ruleContext.getPrerequisites( + ObjcRuleClasses.PROTO_LIB_ATTR, Mode.TARGET, ObjcProvider.class)) + .build(); + } + + private CompilationArtifacts getCompilationArtifacts() { + Iterable<Artifact> generatedSources = getGeneratedProtoOutputs(getSourceExtension()); + return new CompilationArtifacts.Builder() + .setIntermediateArtifacts(new IntermediateArtifacts(ruleContext, "")) + .setPchFile(Optional.<Artifact>absent()) + .addAdditionalHdrs(getGeneratedProtoOutputs(getHeaderExtension())) + .addAdditionalHdrs(generatedSources) + .addNonArcSrcs(generatedSources) + .build(); + } + + private Artifact getProtoInputsFile() { + return ruleContext.getUniqueDirectoryArtifact( + "_protos", "_proto_input_files", ruleContext.getConfiguration().getGenfilesDirectory()); + } + + private String getProtoInputsFileContents(Iterable<Artifact> outputProtos) { + // Sort the file names to make the remote action key independent of the precise deps structure. + // compile_protos.py will sort the input list anyway. + Iterable<Artifact> sorted = Ordering.natural().immutableSortedCopy(outputProtos); + return Artifact.joinExecPaths("\n", sorted); + } + + private CustomCommandLine getGenerationCommandLine() { + CustomCommandLine.Builder commandLineBuilder = + new CustomCommandLine.Builder() + .add(attributes.getProtoCompiler().getExecPathString()) + .add("--input-file-list") + .add(getProtoInputsFile().getExecPathString()) + .add("--output-dir") + .add(getWorkspaceRelativeOutputDir().getSafePathString()) + .add("--working-dir") + .add("."); + + if (attributes.getOptionsFile().isPresent()) { + commandLineBuilder + .add("--compiler-options-path") + .add(attributes.getOptionsFile().get().getExecPathString()); + } + + if (attributes.outputsCpp()) { + commandLineBuilder.add("--generate-cpp"); + } + + if (attributes.usesObjcHeaderNames()) { + commandLineBuilder.add("--use-objc-header-names"); + } + return commandLineBuilder.build(); + } + + public ImmutableSet<PathFragment> getUserHeaderSearchPaths() { + ImmutableSet.Builder<PathFragment> searchPathEntriesBuilder = + new ImmutableSet.Builder<PathFragment>().add(getWorkspaceRelativeOutputDir()); + + if (attributes.needsPerProtoIncludes()) { + PathFragment generatedProtoDir = + new PathFragment( + getWorkspaceRelativeOutputDir(), ruleContext.getLabel().getPackageFragment()); + + searchPathEntriesBuilder + .add(generatedProtoDir) + .addAll( + Iterables.transform( + getGeneratedProtoOutputs(getHeaderExtension()), PARENT_PATHFRAGMENT)); + } + + return searchPathEntriesBuilder.build(); + } + + private PathFragment getWorkspaceRelativeOutputDir() { + // Generate sources in a package-and-rule-scoped directory; adds both the + // package-and-rule-scoped directory and the header-containing-directory to the include path + // of dependers. + PathFragment rootRelativeOutputDir = ruleContext.getUniqueDirectory(UNIQUE_DIRECTORY_NAME); + + return new PathFragment( + ruleContext.getBinOrGenfilesDirectory().getExecPath(), rootRelativeOutputDir); + } + + private Iterable<Artifact> getGeneratedProtoOutputs(String extension) { + ImmutableList.Builder<Artifact> builder = new ImmutableList.Builder<>(); + for (Artifact protoFile : attributes.getProtoFiles()) { + String protoFileName = FileSystemUtils.removeExtension(protoFile.getFilename()); + String generatedOutputName; + if (attributes.outputsCpp()) { + generatedOutputName = protoFileName; + } else { + String lowerUnderscoreBaseName = protoFileName.replace('-', '_').toLowerCase(); + generatedOutputName = LOWER_UNDERSCORE.to(UPPER_CAMEL, lowerUnderscoreBaseName); + } + + PathFragment generatedFilePath = + new PathFragment( + protoFile.getRootRelativePath().getParentDirectory(), + new PathFragment(generatedOutputName)); + + PathFragment outputFile = FileSystemUtils.appendExtension(generatedFilePath, extension); + + if (outputFile != null) { + builder.add( + ruleContext.getUniqueDirectoryArtifact( + UNIQUE_DIRECTORY_NAME, outputFile, ruleContext.getBinOrGenfilesDirectory())); + } + } + return builder.build(); + } +} |