// Copyright 2016 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.android; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Function; import com.google.common.base.Functions; import com.google.common.base.Joiner; import com.google.common.base.Objects; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterators; import com.google.common.collect.Sets; import com.google.devtools.build.lib.actions.CommandLineItem; import com.google.devtools.build.lib.analysis.actions.CustomCommandLine; import com.google.devtools.build.lib.analysis.actions.CustomCommandLine.VectorArg; import com.google.devtools.build.lib.rules.android.ResourceContainerConverter.ToArg.Includes; import com.google.devtools.build.lib.skyframe.serialization.autocodec.AutoCodec; import com.google.devtools.build.lib.vfs.PathFragment; import java.util.HashSet; import java.util.Set; import java.util.function.Consumer; /** * Factory for functions to convert a {@link ResourceContainer} to a commandline argument, or a * collection of artifacts. Uses a certain convention for commandline arguments (e.g., separators, * and ordering of container elements). * * @deprecated Use {@link AndroidDataConverter} instead. */ @VisibleForTesting @Deprecated public class ResourceContainerConverter { static Builder builder() { return new Builder(); } static class ToArg extends CommandLineItem.ParametrizedMapFn { private final Set includes; private final SeparatorType separatorType; private final Joiner argJoiner; private final Function escaper; enum Includes { ResourceRoots, Label, Manifest, RTxt, SymbolsBin, CompiledSymbols, StaticLibrary, Aapt2RTxt } enum SeparatorType { COLON_COMMA, SEMICOLON_AMPERSAND } ToArg(Builder builder) { this.includes = Sets.immutableEnumSet(builder.includes); this.separatorType = builder.separatorType; switch (separatorType) { case COLON_COMMA: argJoiner = COLON_JOINER; // We currently use ":" to separate components of an argument and "," to separate // arguments in a list of arguments. Those characters require escaping if used in a label // (part of the set of allowed characters in a label). if (includes.contains(Includes.Label)) { escaper = COLON_COMMA; } else { escaper = IDENTITY; } break; case SEMICOLON_AMPERSAND: argJoiner = SEMICOLON_JOINER; escaper = IDENTITY; break; default: throw new IllegalStateException("Unknown separator type " + separatorType); } } @AutoCodec @AutoCodec.VisibleForSerialization static final Function IDENTITY = Functions.identity(); @AutoCodec @AutoCodec.VisibleForSerialization static final Function COLON_COMMA = (input) -> input.replace(":", "\\:").replace(",", "\\,"); @AutoCodec @AutoCodec.VisibleForSerialization static final Joiner SEMICOLON_JOINER = Joiner.on(";"); @AutoCodec @AutoCodec.VisibleForSerialization static final Joiner COLON_JOINER = Joiner.on(":"); @Override public void expandToCommandLine(ValidatedAndroidData container, Consumer args) { args.accept(map(container)); } String map(ValidatedAndroidData container) { ImmutableList.Builder cmdPieces = ImmutableList.builder(); if (includes.contains(Includes.ResourceRoots)) { cmdPieces.add(convertRoots(container.getResourceRoots())); cmdPieces.add(convertRoots(container.getAssetRoots())); } if (includes.contains(Includes.Label)) { cmdPieces.add(escaper.apply(container.getLabel().toString())); } if (includes.contains(Includes.Manifest)) { cmdPieces.add(container.getManifest().getExecPathString()); } if (includes.contains(Includes.RTxt)) { cmdPieces.add(container.getRTxt() == null ? "" : container.getRTxt().getExecPathString()); } if (includes.contains(Includes.Aapt2RTxt)) { cmdPieces.add( container.getAapt2RTxt() == null ? "" : container.getAapt2RTxt().getExecPathString()); } if (includes.contains(Includes.StaticLibrary)) { cmdPieces.add( container.getStaticLibrary() == null ? "" : container.getStaticLibrary().getExecPathString()); } if (includes.contains(Includes.CompiledSymbols)) { cmdPieces.add( container.getCompiledSymbols() == null ? "" : container.getCompiledSymbols().getExecPathString()); } if (includes.contains(Includes.SymbolsBin)) { cmdPieces.add( container.getSymbols() == null ? "" : container.getSymbols().getExecPathString()); } return argJoiner.join(cmdPieces.build()); } String listSeparator() { switch (separatorType) { case COLON_COMMA: return ","; case SEMICOLON_AMPERSAND: return "&"; default: Preconditions.checkState(false, "Unknown separator type " + separatorType); return null; } } @Override public int maxInstancesAllowed() { // This is the max number of resource converters we expect to statically // construct for any given blaze instance. // Do not increase recklessly. return 10; } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()) { return false; } ToArg toArg = (ToArg) o; return includes.equals(toArg.includes) && separatorType == toArg.separatorType; } @Override public int hashCode() { return Objects.hashCode(includes, separatorType); } } static class Builder { private final Set includes = new HashSet<>(); private ToArg.SeparatorType separatorType; Builder() {} Builder include(Includes include) { includes.add(include); return this; } Builder withSeparator(ToArg.SeparatorType type) { separatorType = type; return this; } ToArg toArgConverter() { return new ToArg(this); } } @VisibleForTesting public static String convertRoots(Iterable roots) { return Joiner.on("#").join(Iterators.transform(roots.iterator(), Functions.toStringFunction())); } /** * Convert ResourceDependencies to commandline args and artifacts, assuming the commandline * arguments should be split into direct deps and transitive deps. */ static void addToCommandLine( ResourceDependencies dependencies, CustomCommandLine.Builder cmdBuilder, ToArg toArg) { cmdBuilder.addAll( "--data", VectorArg.join(toArg.listSeparator()) .each(dependencies.getTransitiveResourceContainers()) .mapped(toArg)); cmdBuilder.addAll( "--directData", VectorArg.join(toArg.listSeparator()) .each(dependencies.getDirectResourceContainers()) .mapped(toArg)); } }