diff options
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/packages/PackageSerializer.java')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/packages/PackageSerializer.java | 462 |
1 files changed, 462 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/packages/PackageSerializer.java b/src/main/java/com/google/devtools/build/lib/packages/PackageSerializer.java new file mode 100644 index 0000000000..8971cf247b --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/packages/PackageSerializer.java @@ -0,0 +1,462 @@ +// 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.packages; + +import static com.google.devtools.build.lib.packages.Type.BOOLEAN; +import static com.google.devtools.build.lib.packages.Type.DISTRIBUTIONS; +import static com.google.devtools.build.lib.packages.Type.FILESET_ENTRY_LIST; +import static com.google.devtools.build.lib.packages.Type.INTEGER; +import static com.google.devtools.build.lib.packages.Type.INTEGER_LIST; +import static com.google.devtools.build.lib.packages.Type.LABEL; +import static com.google.devtools.build.lib.packages.Type.LABEL_LIST; +import static com.google.devtools.build.lib.packages.Type.LABEL_LIST_DICT; +import static com.google.devtools.build.lib.packages.Type.LICENSE; +import static com.google.devtools.build.lib.packages.Type.NODEP_LABEL; +import static com.google.devtools.build.lib.packages.Type.NODEP_LABEL_LIST; +import static com.google.devtools.build.lib.packages.Type.OUTPUT; +import static com.google.devtools.build.lib.packages.Type.OUTPUT_LIST; +import static com.google.devtools.build.lib.packages.Type.STRING; +import static com.google.devtools.build.lib.packages.Type.STRING_DICT; +import static com.google.devtools.build.lib.packages.Type.STRING_DICT_UNARY; +import static com.google.devtools.build.lib.packages.Type.STRING_LIST; +import static com.google.devtools.build.lib.packages.Type.STRING_LIST_DICT; +import static com.google.devtools.build.lib.packages.Type.TRISTATE; + +import com.google.common.collect.ImmutableList; +import com.google.devtools.build.lib.events.Event; +import com.google.devtools.build.lib.events.Location; +import com.google.devtools.build.lib.packages.License.DistributionType; +import com.google.devtools.build.lib.packages.MakeEnvironment.Binding; +import com.google.devtools.build.lib.query2.proto.proto2api.Build; +import com.google.devtools.build.lib.syntax.FilesetEntry; +import com.google.devtools.build.lib.syntax.GlobCriteria; +import com.google.devtools.build.lib.syntax.GlobList; +import com.google.devtools.build.lib.syntax.Label; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * Functionality to serialize loaded packages. + */ +public class PackageSerializer { + private static Build.SourceFile serializeInputFile(InputFile inputFile) { + Build.SourceFile.Builder result = Build.SourceFile.newBuilder(); + result.setName(inputFile.getLabel().toString()); + if (inputFile.isVisibilitySpecified()) { + for (Label visibilityLabel : inputFile.getVisibility().getDeclaredLabels()) { + result.addVisibilityLabel(visibilityLabel.toString()); + } + } + if (inputFile.isLicenseSpecified()) { + result.setLicense(serializeLicense(inputFile.getLicense())); + } + + result.setParseableLocation(serializeLocation(inputFile.getLocation())); + return result.build(); + } + + private static Build.Location serializeLocation(Location location) { + Build.Location.Builder result = Build.Location.newBuilder(); + + result.setStartOffset(location.getStartOffset()); + if (location.getStartLineAndColumn() != null) { + result.setStartLine(location.getStartLineAndColumn().getLine()); + result.setStartColumn(location.getStartLineAndColumn().getColumn()); + } + + result.setEndOffset(location.getEndOffset()); + if (location.getEndLineAndColumn() != null) { + result.setEndLine(location.getEndLineAndColumn().getLine()); + result.setEndColumn(location.getEndLineAndColumn().getColumn()); + } + + return result.build(); + } + + private static Build.PackageGroup serializePackageGroup(PackageGroup packageGroup) { + Build.PackageGroup.Builder result = Build.PackageGroup.newBuilder(); + + result.setName(packageGroup.getLabel().toString()); + result.setParseableLocation(serializeLocation(packageGroup.getLocation())); + + for (PackageSpecification packageSpecification : packageGroup.getPackageSpecifications()) { + result.addContainedPackage(packageSpecification.toString()); + } + + for (Label include : packageGroup.getIncludes()) { + result.addIncludedPackageGroup(include.toString()); + } + + return result.build(); + } + + private static Build.Rule serializeRule(Rule rule) { + Build.Rule.Builder result = Build.Rule.newBuilder(); + result.setName(rule.getLabel().toString()); + result.setRuleClass(rule.getRuleClass()); + result.setParseableLocation(serializeLocation(rule.getLocation())); + for (Attribute attribute : rule.getAttributes()) { + Object value = attribute.getName().equals("visibility") + ? rule.getVisibility().getDeclaredLabels() + // TODO(bazel-team): support configurable attributes. AggregatingAttributeMapper + // may make more sense here. Computed defaults may add complications. + : RawAttributeMapper.of(rule).get(attribute.getName(), attribute.getType()); + if (value != null) { + PackageSerializer.addAttributeToProto(result, attribute, value, + rule.getAttributeLocation(attribute.getName()), + rule.isAttributeValueExplicitlySpecified(attribute), + true); + } + } + + return result.build(); + } + + private static List<Build.MakeVar> serializeMakeEnvironment(MakeEnvironment makeEnv) { + List<Build.MakeVar> result = new ArrayList<>(); + + for (Map.Entry<String, ImmutableList<Binding>> var : makeEnv.getBindings().entrySet()) { + Build.MakeVar.Builder varPb = Build.MakeVar.newBuilder(); + varPb.setName(var.getKey()); + for (Binding binding : var.getValue()) { + Build.MakeVarBinding.Builder bindingPb = Build.MakeVarBinding.newBuilder(); + bindingPb.setValue(binding.getValue()); + bindingPb.setPlatformSetRegexp(binding.getPlatformSetRegexp()); + varPb.addBinding(bindingPb); + } + + result.add(varPb.build()); + } + + return result; + } + + private static Build.License serializeLicense(License license) { + Build.License.Builder result = Build.License.newBuilder(); + + for (License.LicenseType licenseType : license.getLicenseTypes()) { + result.addLicenseType(licenseType.toString()); + } + + for (Label exception : license.getExceptions()) { + result.addException(exception.toString()); + } + return result.build(); + } + + private static Build.Event serializeEvent(Event event) { + Build.Event.Builder result = Build.Event.newBuilder(); + result.setMessage(event.getMessage()); + if (event.getLocation() != null) { + result.setLocation(serializeLocation(event.getLocation())); + } + + Build.Event.EventKind kind; + + switch (event.getKind()) { + case ERROR: + kind = Build.Event.EventKind.ERROR; + break; + case WARNING: + kind = Build.Event.EventKind.WARNING; + break; + case INFO: + kind = Build.Event.EventKind.INFO; + break; + case PROGRESS: + kind = Build.Event.EventKind.PROGRESS; + break; + default: throw new IllegalStateException(); + } + + result.setKind(kind); + return result.build(); + } + + private static void serializePackageInternal(Package pkg, Build.Package.Builder builder) { + builder.setName(pkg.getName()); + builder.setRepository(pkg.getPackageIdentifier().getRepository().toString()); + builder.setBuildFilePath(pkg.getFilename().getPathString()); + // The extra bit is needed to handle the corner case when the default visibility is [], i.e. + // zero labels. + builder.setDefaultVisibilitySet(pkg.isDefaultVisibilitySet()); + if (pkg.isDefaultVisibilitySet()) { + for (Label visibilityLabel : pkg.getDefaultVisibility().getDeclaredLabels()) { + builder.addDefaultVisibilityLabel(visibilityLabel.toString()); + } + } + + builder.setDefaultObsolete(pkg.getDefaultObsolete()); + builder.setDefaultTestonly(pkg.getDefaultTestOnly()); + if (pkg.getDefaultDeprecation() != null) { + builder.setDefaultDeprecation(pkg.getDefaultDeprecation()); + } + + for (String defaultCopt : pkg.getDefaultCopts()) { + builder.addDefaultCopt(defaultCopt); + } + + if (pkg.isDefaultHdrsCheckSet()) { + builder.setDefaultHdrsCheck(pkg.getDefaultHdrsCheck()); + } + + builder.setDefaultLicense(serializeLicense(pkg.getDefaultLicense())); + + for (DistributionType distributionType : pkg.getDefaultDistribs()) { + builder.addDefaultDistrib(distributionType.toString()); + } + + for (String feature : pkg.getFeatures()) { + builder.addDefaultSetting(feature); + } + + for (Label subincludeLabel : pkg.getSubincludeLabels()) { + builder.addSubincludeLabel(subincludeLabel.toString()); + } + + for (Label skylarkLabel : pkg.getSkylarkFileDependencies()) { + builder.addSkylarkLabel(skylarkLabel.toString()); + } + + for (Build.MakeVar makeVar : + serializeMakeEnvironment(pkg.getMakeEnvironment())) { + builder.addMakeVariable(makeVar); + } + + for (Target target : pkg.getTargets()) { + if (target instanceof InputFile) { + builder.addSourceFile(serializeInputFile((InputFile) target)); + } else if (target instanceof OutputFile) { + // Output files are ignored; they are recorded in rules. + } else if (target instanceof PackageGroup) { + builder.addPackageGroup(serializePackageGroup((PackageGroup) target)); + } else if (target instanceof Rule) { + builder.addRule(serializeRule((Rule) target)); + } + } + + for (Event event : pkg.getEvents()) { + builder.addEvent(serializeEvent(event)); + } + + builder.setContainsErrors(pkg.containsErrors()); + builder.setContainsTemporaryErrors(pkg.containsTemporaryErrors()); + } + + /** + * Serialize a package to a protocol message. The inverse of + * {@link PackageDeserializer#deserialize}. + */ + public static Build.Package serializePackage(Package pkg) { + Build.Package.Builder builder = Build.Package.newBuilder(); + serializePackageInternal(pkg, builder); + return builder.build(); + } + + /** + * Adds the serialized version of the specified attribute to the specified message. + * + * @param result the message to amend + * @param attr the attribute to add + * @param value the value of the attribute + * @param location the location of the attribute in the source file + * @param explicitlySpecified whether the attribute was explicitly specified or not + * @param includeGlobs add glob expression for attributes that contain them + */ + // TODO(bazel-team): This is a copy of the code in ProtoOutputFormatter. + @SuppressWarnings("unchecked") + public static void addAttributeToProto( + Build.Rule.Builder result, Attribute attr, Object value, Location location, + Boolean explicitlySpecified, boolean includeGlobs) { + // Get the attribute type. We need to convert and add appropriately + com.google.devtools.build.lib.packages.Type<?> type = attr.getType(); + + Build.Attribute.Builder attrPb = Build.Attribute.newBuilder(); + + // Set the type, name and source + Build.Attribute.Discriminator attributeProtoType = ProtoUtils.getDiscriminatorFromType(type); + attrPb.setName(attr.getName()); + attrPb.setType(attributeProtoType); + + if (location != null) { + attrPb.setParseableLocation(serializeLocation(location)); + } + + if (explicitlySpecified != null) { + attrPb.setExplicitlySpecified(explicitlySpecified); + } + + /* + * Set the appropriate type and value. Since string and string list store + * values for multiple types, use the toString() method on the objects + * instead of casting them. Note that Boolean and TriState attributes have + * both an integer and string representation. + */ + if (type == INTEGER) { + attrPb.setIntValue((Integer) value); + } else if (type == STRING || type == LABEL || type == NODEP_LABEL || type == OUTPUT) { + attrPb.setStringValue(value.toString()); + } else if (type == STRING_LIST || type == LABEL_LIST || type == NODEP_LABEL_LIST + || type == OUTPUT_LIST || type == DISTRIBUTIONS) { + Collection<?> values = (Collection<?>) value; + for (Object entry : values) { + attrPb.addStringListValue(entry.toString()); + } + } else if (type == INTEGER_LIST) { + Collection<Integer> values = (Collection<Integer>) value; + for (Integer entry : values) { + attrPb.addIntListValue(entry); + } + } else if (type == BOOLEAN) { + if ((Boolean) value) { + attrPb.setStringValue("true"); + attrPb.setBooleanValue(true); + } else { + attrPb.setStringValue("false"); + attrPb.setBooleanValue(false); + } + // This maintains partial backward compatibility for external users of the + // protobuf that were expecting an integer field and not a true boolean. + attrPb.setIntValue((Boolean) value ? 1 : 0); + } else if (type == TRISTATE) { + switch ((TriState) value) { + case AUTO: + attrPb.setIntValue(-1); + attrPb.setStringValue("auto"); + attrPb.setTristateValue(Build.Attribute.Tristate.AUTO); + break; + case NO: + attrPb.setIntValue(0); + attrPb.setStringValue("no"); + attrPb.setTristateValue(Build.Attribute.Tristate.NO); + break; + case YES: + attrPb.setIntValue(1); + attrPb.setStringValue("yes"); + attrPb.setTristateValue(Build.Attribute.Tristate.YES); + break; + } + } else if (type == LICENSE) { + License license = (License) value; + Build.License.Builder licensePb = Build.License.newBuilder(); + for (License.LicenseType licenseType : license.getLicenseTypes()) { + licensePb.addLicenseType(licenseType.toString()); + } + for (Label exception : license.getExceptions()) { + licensePb.addException(exception.toString()); + } + attrPb.setLicense(licensePb); + } else if (type == STRING_DICT) { + Map<String, String> dict = (Map<String, String>) value; + for (Map.Entry<String, String> dictEntry : dict.entrySet()) { + Build.StringDictEntry entry = Build.StringDictEntry.newBuilder() + .setKey(dictEntry.getKey()) + .setValue(dictEntry.getValue()) + .build(); + attrPb.addStringDictValue(entry); + } + } else if (type == STRING_DICT_UNARY) { + Map<String, String> dict = (Map<String, String>) value; + for (Map.Entry<String, String> dictEntry : dict.entrySet()) { + Build.StringDictUnaryEntry entry = Build.StringDictUnaryEntry.newBuilder() + .setKey(dictEntry.getKey()) + .setValue(dictEntry.getValue()) + .build(); + attrPb.addStringDictUnaryValue(entry); + } + } else if (type == STRING_LIST_DICT) { + Map<String, List<String>> dict = (Map<String, List<String>>) value; + for (Map.Entry<String, List<String>> dictEntry : dict.entrySet()) { + Build.StringListDictEntry.Builder entry = Build.StringListDictEntry.newBuilder() + .setKey(dictEntry.getKey()); + for (Object dictEntryValue : dictEntry.getValue()) { + entry.addValue(dictEntryValue.toString()); + } + attrPb.addStringListDictValue(entry); + } + } else if (type == LABEL_LIST_DICT) { + Map<String, List<Label>> dict = (Map<String, List<Label>>) value; + for (Map.Entry<String, List<Label>> dictEntry : dict.entrySet()) { + Build.LabelListDictEntry.Builder entry = Build.LabelListDictEntry.newBuilder() + .setKey(dictEntry.getKey()); + for (Object dictEntryValue : dictEntry.getValue()) { + entry.addValue(dictEntryValue.toString()); + } + attrPb.addLabelListDictValue(entry); + } + } else if (type == FILESET_ENTRY_LIST) { + List<FilesetEntry> filesetEntries = (List<FilesetEntry>) value; + for (FilesetEntry filesetEntry : filesetEntries) { + Build.FilesetEntry.Builder filesetEntryPb = Build.FilesetEntry.newBuilder() + .setSource(filesetEntry.getSrcLabel().toString()) + .setDestinationDirectory(filesetEntry.getDestDir().getPathString()) + .setSymlinkBehavior(symlinkBehaviorToPb(filesetEntry.getSymlinkBehavior())) + .setStripPrefix(filesetEntry.getStripPrefix()) + .setFilesPresent(filesetEntry.getFiles() != null); + + if (filesetEntry.getFiles() != null) { + for (Label file : filesetEntry.getFiles()) { + filesetEntryPb.addFile(file.toString()); + } + } + + if (filesetEntry.getExcludes() != null) { + for (String exclude : filesetEntry.getExcludes()) { + filesetEntryPb.addExclude(exclude); + } + } + + attrPb.addFilesetListValue(filesetEntryPb); + } + } else { + throw new IllegalStateException("Unknown type: " + type); + } + + if (includeGlobs && value instanceof GlobList<?>) { + GlobList<?> globList = (GlobList<?>) value; + + for (GlobCriteria criteria : globList.getCriteria()) { + Build.GlobCriteria.Builder criteriaPb = Build.GlobCriteria.newBuilder(); + criteriaPb.setGlob(criteria.isGlob()); + for (String include : criteria.getIncludePatterns()) { + criteriaPb.addInclude(include); + } + for (String exclude : criteria.getExcludePatterns()) { + criteriaPb.addExclude(exclude); + } + + attrPb.addGlobCriteria(criteriaPb); + } + } + result.addAttribute(attrPb); + } + + // This is needed because I do not want to use the SymlinkBehavior from the + // protocol buffer all over the place, so there are two classes that do + // essentially the same thing. + private static Build.FilesetEntry.SymlinkBehavior symlinkBehaviorToPb( + FilesetEntry.SymlinkBehavior symlinkBehavior) { + switch (symlinkBehavior) { + case COPY: + return Build.FilesetEntry.SymlinkBehavior.COPY; + case DEREFERENCE: + return Build.FilesetEntry.SymlinkBehavior.DEREFERENCE; + default: + throw new AssertionError("Unhandled FilesetEntry.SymlinkBehavior"); + } + } +} |