// 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 com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList.Builder; import com.google.common.collect.ImmutableMap; import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; import com.google.devtools.build.lib.events.Event; import com.google.devtools.build.lib.events.Location; import com.google.devtools.build.lib.events.NullEventHandler; import com.google.devtools.build.lib.events.StoredEventHandler; import com.google.devtools.build.lib.packages.License.DistributionType; import com.google.devtools.build.lib.packages.License.LicenseParsingException; import com.google.devtools.build.lib.packages.Package.AbstractBuilder.GeneratedLabelConflict; import com.google.devtools.build.lib.packages.Package.NameConflictException; import com.google.devtools.build.lib.packages.RuleClass.ParsedAttributeValue; import com.google.devtools.build.lib.query2.proto.proto2api.Build; import com.google.devtools.build.lib.query2.proto.proto2api.Build.StringDictUnaryEntry; 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 com.google.devtools.build.lib.syntax.Label.SyntaxException; import com.google.devtools.build.lib.vfs.FileSystem; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.PathFragment; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; /** * Functionality to deserialize loaded packages. */ public class PackageDeserializer { // Workaround for Java serialization not allowing to pass in a context manually. // volatile is needed to ensure that the objects are published safely. // TODO(bazel-team): Subclass ObjectOutputStream to pass through environment variables. public static volatile RuleClassProvider defaultRuleClassProvider; public static volatile FileSystem defaultDeserializerFileSystem; private class Context { private final Package.Builder packageBuilder; private final Path buildFilePath; public Context(Path buildFilePath, Package.Builder packageBuilder) { this.buildFilePath = buildFilePath; this.packageBuilder = packageBuilder; } Location deserializeLocation(Build.Location location) { return new ExplicitLocation(buildFilePath, location); } ParsedAttributeValue deserializeAttribute(Type expectedType, Build.Attribute attrPb) throws PackageDeserializationException { Object value = deserializeAttributeValue(expectedType, attrPb); return new ParsedAttributeValue( attrPb.hasExplicitlySpecified() && attrPb.getExplicitlySpecified(), value, deserializeLocation(attrPb.getParseableLocation())); } void deserializeInputFile(Build.SourceFile sourceFile) throws PackageDeserializationException { InputFile inputFile; try { inputFile = packageBuilder.createInputFile( deserializeLabel(sourceFile.getName()).getName(), deserializeLocation(sourceFile.getParseableLocation())); } catch (GeneratedLabelConflict e) { throw new PackageDeserializationException(e); } if (!sourceFile.getVisibilityLabelList().isEmpty() || sourceFile.hasLicense()) { packageBuilder.setVisibilityAndLicense(inputFile, PackageFactory.getVisibility(deserializeLabels(sourceFile.getVisibilityLabelList())), deserializeLicense(sourceFile.getLicense())); } } void deserializePackageGroup(Build.PackageGroup packageGroupPb) throws PackageDeserializationException { List specifications = new ArrayList<>(); for (String containedPackage : packageGroupPb.getContainedPackageList()) { specifications.add("//" + containedPackage); } try { packageBuilder.addPackageGroup( deserializeLabel(packageGroupPb.getName()).getName(), specifications, deserializeLabels(packageGroupPb.getIncludedPackageGroupList()), NullEventHandler.INSTANCE, // TODO(bazel-team): Handle errors properly deserializeLocation(packageGroupPb.getParseableLocation())); } catch (Label.SyntaxException | Package.NameConflictException e) { throw new PackageDeserializationException(e); } } void deserializeRule(Build.Rule rulePb) throws PackageDeserializationException { RuleClass ruleClass = ruleClassProvider.getRuleClassMap().get(rulePb.getRuleClass()); if (ruleClass == null) { throw new PackageDeserializationException( String.format("Invalid rule class '%s'", ruleClass)); } Map attributeValues = new HashMap<>(); for (Build.Attribute attrPb : rulePb.getAttributeList()) { Type type = ruleClass.getAttributeByName(attrPb.getName()).getType(); attributeValues.put(attrPb.getName(), deserializeAttribute(type, attrPb)); } Label ruleLabel = deserializeLabel(rulePb.getName()); Location ruleLocation = deserializeLocation(rulePb.getParseableLocation()); try { Rule rule = ruleClass.createRuleWithParsedAttributeValues( ruleLabel, packageBuilder, ruleLocation, attributeValues, NullEventHandler.INSTANCE); packageBuilder.addRule(rule); Preconditions.checkState(!rule.containsErrors()); } catch (NameConflictException | SyntaxException e) { throw new PackageDeserializationException(e); } } } private final FileSystem fileSystem; private final RuleClassProvider ruleClassProvider; @Immutable private static final class ExplicitLocation extends Location { private final PathFragment path; private final int startLine; private final int startColumn; private final int endLine; private final int endColumn; private ExplicitLocation(Path path, Build.Location location) { super( location.hasStartOffset() && location.hasEndOffset() ? location.getStartOffset() : 0, location.hasStartOffset() && location.hasEndOffset() ? location.getEndOffset() : 0); this.path = path.asFragment(); if (location.hasStartLine() && location.hasStartColumn() && location.hasEndLine() && location.hasEndColumn()) { this.startLine = location.getStartLine(); this.startColumn = location.getStartColumn(); this.endLine = location.getEndLine(); this.endColumn = location.getEndColumn(); } else { this.startLine = 0; this.startColumn = 0; this.endLine = 0; this.endColumn = 0; } } @Override public PathFragment getPath() { return path; } @Override public LineAndColumn getStartLineAndColumn() { return new LineAndColumn(startLine, startColumn); } @Override public LineAndColumn getEndLineAndColumn() { return new LineAndColumn(endLine, endColumn); } } public PackageDeserializer(FileSystem fileSystem, RuleClassProvider ruleClassProvider) { if (fileSystem == null) { fileSystem = defaultDeserializerFileSystem; } this.fileSystem = Preconditions.checkNotNull(fileSystem); if (ruleClassProvider == null) { ruleClassProvider = defaultRuleClassProvider; } this.ruleClassProvider = Preconditions.checkNotNull(ruleClassProvider); } /** * Exception thrown when something goes wrong during package deserialization. */ public static class PackageDeserializationException extends Exception { private PackageDeserializationException(String message) { super(message); } private PackageDeserializationException(String message, Exception reason) { super(message, reason); } private PackageDeserializationException(Exception reason) { super(reason); } } private static Label deserializeLabel(String labelName) throws PackageDeserializationException { try { return Label.parseRepositoryLabel(labelName); } catch (Label.SyntaxException e) { throw new PackageDeserializationException("Invalid label: " + e.getMessage(), e); } } private static List