blob: d86ada91c5ac21539f4e517f6f9903292c6d4455 [file] [log] [blame]
// 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.Preconditions;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.actions.CustomCommandLine;
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.rules.android.AndroidResourcesProvider.ResourceContainer;
import com.google.devtools.build.lib.rules.android.AndroidResourcesProvider.ResourceType;
import javax.annotation.Nullable;
/**
* 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).
*/
@VisibleForTesting
public class ResourceContainerConverter {
static Builder builder() {
return new Builder();
}
interface ToArg extends Function<ResourceContainer, String> {
String listSeparator();
}
interface ToArtifacts extends Function<ResourceContainer, NestedSet<Artifact>> {
}
static class Builder {
private boolean includeResourceRoots;
private boolean includeLabel;
private boolean includeManifest;
private boolean includeRTxt;
private boolean includeSymbolsBin;
private SeparatorType separatorType;
private Joiner argJoiner;
private Function<String, String> escaper = Functions.identity();
enum SeparatorType {
COLON_COMMA,
SEMICOLON_AMPERSAND
}
Builder() {
}
Builder includeResourceRoots() {
includeResourceRoots = true;
return this;
}
Builder includeLabel() {
includeLabel = true;
return this;
}
Builder includeManifest() {
includeManifest = true;
return this;
}
Builder includeRTxt() {
includeRTxt = true;
return this;
}
Builder includeSymbolsBin() {
includeSymbolsBin = true;
return this;
}
Builder withSeparator(SeparatorType type) {
separatorType = type;
return this;
}
ToArg toArgConverter() {
switch (separatorType) {
case COLON_COMMA:
argJoiner = Joiner.on(":");
// 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 (includeLabel) {
escaper = new Function<String, String>() {
@Override
public String apply(String input) {
return input.replace(":", "\\:").replace(",", "\\,");
}
};
}
break;
case SEMICOLON_AMPERSAND:
argJoiner = Joiner.on(";");
break;
default:
Preconditions.checkState(false, "Unknown separator type " + separatorType);
break;
}
return new ToArg() {
@Override
public String apply(ResourceContainer container) {
ImmutableList.Builder<String> cmdPieces = ImmutableList.builder();
if (includeResourceRoots) {
cmdPieces.add(convertRoots(container, ResourceType.RESOURCES));
cmdPieces.add(convertRoots(container, ResourceType.ASSETS));
}
if (includeLabel) {
cmdPieces.add(escaper.apply(container.getLabel().toString()));
}
if (includeManifest) {
cmdPieces.add(container.getManifest().getExecPathString());
}
if (includeRTxt) {
cmdPieces.add(
container.getRTxt() == null ? "" : container.getRTxt().getExecPathString());
}
if (includeSymbolsBin) {
cmdPieces.add(
container.getSymbolsTxt() == null
? ""
: container.getSymbolsTxt().getExecPathString());
}
return argJoiner.join(cmdPieces.build());
}
@Override
public String listSeparator() {
switch (separatorType) {
case COLON_COMMA:
return ",";
case SEMICOLON_AMPERSAND:
return "&";
default:
Preconditions.checkState(false, "Unknown separator type " + separatorType);
return null;
}
}
};
}
ToArtifacts toArtifactConverter() {
return new ToArtifacts() {
@Override
public NestedSet<Artifact> apply(ResourceContainer container) {
NestedSetBuilder<Artifact> artifacts = NestedSetBuilder.naiveLinkOrder();
if (includeResourceRoots) {
artifacts.addAll(container.getArtifacts());
}
if (includeManifest) {
addIfNotNull(container.getManifest(), artifacts);
}
if (includeRTxt) {
addIfNotNull(container.getRTxt(), artifacts);
}
if (includeSymbolsBin) {
addIfNotNull(container.getSymbolsTxt(), artifacts);
}
return artifacts.build();
}
};
}
}
private static void addIfNotNull(
@Nullable Artifact artifact, NestedSetBuilder<Artifact> artifacts) {
if (artifact != null) {
artifacts.add(artifact);
}
}
@VisibleForTesting
public static String convertRoots(ResourceContainer container, ResourceType resourceType) {
return Joiner.on("#")
.join(
Iterators.transform(
container.getRoots(resourceType).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 convertDependencies(
ResourceDependencies dependencies,
CustomCommandLine.Builder cmdBuilder,
NestedSetBuilder<Artifact> inputs,
ToArg toArg,
ToArtifacts toArtifacts) {
if (dependencies != null) {
// TODO(bazel-team): Find an appropriately lazy method to deduplicate the dependencies between
// the direct and transitive data.
// Add transitive data inside an unmodifiableIterable to ensure it won't be expanded until
// iteration.
if (!dependencies.getTransitiveResources().isEmpty()) {
cmdBuilder.addJoinStrings(
"--data",
toArg.listSeparator(),
Iterables.unmodifiableIterable(
Iterables.transform(dependencies.getTransitiveResources(), toArg)));
}
// Add direct data inside an unmodifiableIterable to ensure it won't be expanded until
// iteration.
if (!dependencies.getDirectResources().isEmpty()) {
cmdBuilder.addJoinStrings(
"--directData",
toArg.listSeparator(),
Iterables.unmodifiableIterable(
Iterables.transform(dependencies.getDirectResources(), toArg)));
}
// This flattens the nested set. Since each ResourceContainer needs to be transformed into
// Artifacts, and the NestedSetBuilder.wrap doesn't support lazy Iterator evaluation
// and SpawnActionBuilder.addInputs evaluates Iterables, it becomes necessary to make the
// best effort and let it get flattened.
inputs.addTransitive(
NestedSetBuilder.wrap(
Order.NAIVE_LINK_ORDER,
FluentIterable.from(dependencies.getResources()).transformAndConcat(toArtifacts)));
}
}
}