diff options
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProtoLibrary.java')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProtoLibrary.java | 242 |
1 files changed, 242 insertions, 0 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 new file mode 100644 index 0000000000..647b221258 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcProtoLibrary.java @@ -0,0 +1,242 @@ +// Copyright 2014 Google Inc. 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.annotations.VisibleForTesting; +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +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.AnalysisUtils; +import com.google.devtools.build.lib.analysis.ConfiguredTarget; +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.analysis.actions.SpawnAction; +import com.google.devtools.build.lib.collect.nestedset.NestedSet; +import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; +import com.google.devtools.build.lib.collect.nestedset.Order; +import com.google.devtools.build.lib.packages.Type; +import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory; +import com.google.devtools.build.lib.rules.proto.ProtoSourcesProvider; +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 javax.annotation.Nullable; + +/** + * Implementation for the "objc_proto_library" rule. + */ +public class ObjcProtoLibrary implements RuleConfiguredTargetFactory { + private static final Function<Artifact, PathFragment> PARENT_PATHFRAGMENT = + new Function<Artifact, PathFragment>() { + @Override + public PathFragment apply(Artifact input) { + return input.getExecPath().getParentDirectory(); + } + }; + + @VisibleForTesting + static final String NO_PROTOS_ERROR = + "no protos to compile - a non-empty deps attribute is required"; + + @Override + public ConfiguredTarget create(final RuleContext ruleContext) throws InterruptedException { + Artifact compileProtos = ruleContext.getPrerequisiteArtifact( + ObjcRuleClasses.ObjcProtoRule.COMPILE_PROTOS_ATTR, Mode.HOST); + Optional<Artifact> optionsFile = Optional.fromNullable( + ruleContext.getPrerequisiteArtifact(ObjcProtoLibraryRule.OPTIONS_FILE_ATTR, Mode.HOST)); + NestedSet<Artifact> protos = NestedSetBuilder.<Artifact>stableOrder() + .addAll(ruleContext.getPrerequisiteArtifacts("deps", Mode.TARGET) + .filter(FileType.of(".proto")) + .list()) + .addTransitive(maybeGetProtoSources(ruleContext)) + .build(); + + if (Iterables.isEmpty(protos)) { + ruleContext.ruleError(NO_PROTOS_ERROR); + } + + ImmutableList<Artifact> libProtobuf = ruleContext + .getPrerequisiteArtifacts(ObjcProtoLibraryRule.LIBPROTOBUF_ATTR, Mode.TARGET) + .list(); + ImmutableList<Artifact> protoSupport = ruleContext + .getPrerequisiteArtifacts(ObjcRuleClasses.ObjcProtoRule.PROTO_SUPPORT_ATTR, Mode.HOST) + .list(); + + // 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 = new PathFragment( + ruleContext.getLabel().getPackageFragment(), + new PathFragment("_generated_protos_" + ruleContext.getLabel().getName())); + PathFragment workspaceRelativeOutputDir = new PathFragment( + ruleContext.getBinOrGenfilesDirectory().getExecPath(), rootRelativeOutputDir); + PathFragment generatedProtoDir = + new PathFragment(workspaceRelativeOutputDir, ruleContext.getLabel().getPackageFragment()); + + boolean outputCpp = + ruleContext.attributes().get(ObjcProtoLibraryRule.OUTPUT_CPP_ATTR, Type.BOOLEAN); + + ImmutableList<Artifact> protoGeneratedSources = outputArtifacts( + ruleContext, rootRelativeOutputDir, protos, FileType.of(".pb." + (outputCpp ? "cc" : "m")), + outputCpp); + ImmutableList<Artifact> protoGeneratedHeaders = outputArtifacts( + ruleContext, rootRelativeOutputDir, protos, FileType.of(".pb.h"), outputCpp); + + Artifact inputFileList = ruleContext.getAnalysisEnvironment().getDerivedArtifact( + AnalysisUtils.getUniqueDirectory(ruleContext.getLabel(), new PathFragment("_protos")) + .getRelative("_proto_input_files"), + ruleContext.getConfiguration().getGenfilesDirectory()); + + ruleContext.registerAction(new FileWriteAction( + ruleContext.getActionOwner(), + inputFileList, + ObjcActionsBuilder.joinExecPaths(protos), + false)); + + CustomCommandLine.Builder commandLineBuilder = new CustomCommandLine.Builder() + .add(compileProtos.getExecPathString()) + .add("--input-file-list").add(inputFileList.getExecPathString()) + .add("--output-dir").add(workspaceRelativeOutputDir.getSafePathString()); + if (optionsFile.isPresent()) { + commandLineBuilder + .add("--compiler-options-path") + .add(optionsFile.get().getExecPathString()); + } + if (outputCpp) { + commandLineBuilder.add("--generate-cpp"); + } + + if (!Iterables.isEmpty(protos)) { + ruleContext.registerAction(new SpawnAction.Builder() + .setMnemonic("GenObjcProtos") + .addInput(compileProtos) + .addInputs(optionsFile.asSet()) + .addInputs(protos) + .addInput(inputFileList) + .addInputs(libProtobuf) + .addInputs(protoSupport) + .addOutputs(Iterables.concat(protoGeneratedSources, protoGeneratedHeaders)) + .setExecutable(new PathFragment("/usr/bin/python")) + .setCommandLine(commandLineBuilder.build()) + .setExecutionInfo(ImmutableMap.of(ExecutionRequirements.REQUIRES_DARWIN, "")) + .build(ruleContext)); + } + + IntermediateArtifacts intermediateArtifacts = + ObjcRuleClasses.intermediateArtifacts(ruleContext); + CompilationArtifacts compilationArtifacts = new CompilationArtifacts.Builder() + .addNonArcSrcs(protoGeneratedSources) + .setIntermediateArtifacts(intermediateArtifacts) + .setPchFile(Optional.<Artifact>absent()) + .build(); + + ImmutableSet<PathFragment> searchPathEntries = new ImmutableSet.Builder<PathFragment>() + .add(workspaceRelativeOutputDir) + .add(generatedProtoDir) + .addAll(Iterables.transform(protoGeneratedHeaders, PARENT_PATHFRAGMENT)) + .build(); + ObjcCommon common = new ObjcCommon.Builder(ruleContext) + .setCompilationArtifacts(compilationArtifacts) + .addUserHeaderSearchPaths(searchPathEntries) + .addDepObjcProviders(ruleContext.getPrerequisites( + ObjcProtoLibraryRule.LIBPROTOBUF_ATTR, Mode.TARGET, ObjcProvider.class)) + .setIntermediateArtifacts(intermediateArtifacts) + .addHeaders(protoGeneratedHeaders) + .addHeaders(protoGeneratedSources) + .build(); + + XcodeProvider xcodeProvider = new XcodeProvider.Builder() + .setLabel(ruleContext.getLabel()) + .addUserHeaderSearchPaths(searchPathEntries) + .addDependencies(ruleContext.getPrerequisites( + ObjcProtoLibraryRule.LIBPROTOBUF_ATTR, Mode.TARGET, XcodeProvider.class)) + .addCopts(ObjcRuleClasses.objcConfiguration(ruleContext).getCopts()) + .setProductType(LIBRARY_STATIC) + .addHeaders(protoGeneratedHeaders) + .setCompilationArtifacts(common.getCompilationArtifacts().get()) + .setObjcProvider(common.getObjcProvider()) + .build(); + + ObjcActionsBuilder actionsBuilder = ObjcRuleClasses.actionsBuilder(ruleContext); + actionsBuilder + .registerCompileAndArchiveActions( + compilationArtifacts, common.getObjcProvider(), OptionsProvider.DEFAULT); + actionsBuilder.registerXcodegenActions( + new ObjcRuleClasses.Tools(ruleContext), + ruleContext.getImplicitOutputArtifact(XcodeSupport.PBXPROJ), + XcodeProvider.Project.fromTopLevelTarget(xcodeProvider)); + + return common.configuredTarget( + NestedSetBuilder.<Artifact>stableOrder() + .addAll(common.getCompiledArchive().asSet()) + .addAll(protoGeneratedSources) + .addAll(protoGeneratedHeaders) + .add(ruleContext.getImplicitOutputArtifact(XcodeSupport.PBXPROJ)) + .build(), + Optional.of(xcodeProvider), + Optional.of(common.getObjcProvider()), + Optional.<XcTestAppProvider>absent(), + Optional.<J2ObjcSrcsProvider>absent()); + } + + private NestedSet<Artifact> maybeGetProtoSources(RuleContext ruleContext) { + NestedSetBuilder<Artifact> artifacts = new NestedSetBuilder<>(Order.STABLE_ORDER); + Iterable<ProtoSourcesProvider> providers = + ruleContext.getPrerequisites("deps", Mode.TARGET, ProtoSourcesProvider.class); + for (ProtoSourcesProvider provider : providers) { + artifacts.addTransitive(provider.getTransitiveProtoSources()); + } + return artifacts.build(); + } + + private ImmutableList<Artifact> outputArtifacts(RuleContext ruleContext, + PathFragment rootRelativeOutputDir, Iterable<Artifact> protos, FileType newFileType, + boolean outputCpp) { + ImmutableList.Builder<Artifact> builder = new ImmutableList.Builder<>(); + for (Artifact proto : protos) { + String protoOutputName; + if (outputCpp) { + protoOutputName = proto.getFilename(); + } else { + String lowerUnderscoreBaseName = proto.getFilename().replace('-', '_').toLowerCase(); + protoOutputName = LOWER_UNDERSCORE.to(UPPER_CAMEL, lowerUnderscoreBaseName); + } + PathFragment rawFragment = new PathFragment( + rootRelativeOutputDir, + proto.getExecPath().getParentDirectory(), + new PathFragment(protoOutputName)); + @Nullable PathFragment outputFile = FileSystemUtils.replaceExtension( + rawFragment, + newFileType.getExtensions().get(0), + ".proto"); + if (outputFile != null) { + builder.add(ruleContext.getAnalysisEnvironment().getDerivedArtifact( + outputFile, ruleContext.getBinOrGenfilesDirectory())); + } + } + return builder.build(); + } +} |