blob: 1bcd12ed3ece4e7464606cfd459e34705a54f5ce [file] [log] [blame]
// Copyright 2019 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.bazel.rules.ninja.parser;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSortedMap;
import com.google.devtools.build.lib.collect.ImmutableSortedKeyListMultimap;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.errorprone.annotations.Immutable;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/** Ninja target (build statement) representation. */
public final class NinjaTarget {
/** Builder for {@link NinjaTarget}. */
public static class Builder {
private String ruleName;
private final ImmutableSortedKeyListMultimap.Builder<InputKind, PathFragment> inputsBuilder;
private final ImmutableSortedKeyListMultimap.Builder<OutputKind, PathFragment> outputsBuilder;
private final NinjaScope scope;
private final int offset;
private final ImmutableSortedMap.Builder<String, String> variablesBuilder;
private Builder(NinjaScope scope, int offset) {
this.scope = scope;
this.offset = offset;
inputsBuilder = ImmutableSortedKeyListMultimap.builder();
outputsBuilder = ImmutableSortedKeyListMultimap.builder();
variablesBuilder = ImmutableSortedMap.naturalOrder();
}
public Builder setRuleName(String ruleName) {
this.ruleName = ruleName;
return this;
}
public Builder addInputs(InputKind kind, Collection<PathFragment> inputs) {
inputsBuilder.putAll(kind, inputs);
return this;
}
public Builder addOutputs(OutputKind kind, Collection<PathFragment> outputs) {
outputsBuilder.putAll(kind, outputs);
return this;
}
public Builder addVariable(String key, String value) {
variablesBuilder.put(key, value);
return this;
}
public NinjaTarget build() {
Preconditions.checkNotNull(ruleName);
return new NinjaTarget(
ruleName,
inputsBuilder.build(),
outputsBuilder.build(),
variablesBuilder.build(),
scope,
offset);
}
}
/** Enum with possible kinds of inputs. */
@Immutable
public enum InputKind implements InputOutputKind {
USUAL,
IMPLICIT,
ORDER_ONLY
}
/** Enum with possible kinds of outputs. */
@Immutable
public enum OutputKind implements InputOutputKind {
USUAL,
IMPLICIT
}
/**
* Marker interface, so that it is possible to address {@link InputKind} and {@link OutputKind}
* together in one map.
*/
@Immutable
public interface InputOutputKind {}
private final String ruleName;
private final ImmutableSortedKeyListMultimap<InputKind, PathFragment> inputs;
private final ImmutableSortedKeyListMultimap<OutputKind, PathFragment> outputs;
private final ImmutableSortedMap<String, String> variables;
private final NinjaScope scope;
private final int offset;
public NinjaTarget(
String ruleName,
ImmutableSortedKeyListMultimap<InputKind, PathFragment> inputs,
ImmutableSortedKeyListMultimap<OutputKind, PathFragment> outputs,
ImmutableSortedMap<String, String> variables,
NinjaScope scope,
int offset) {
this.ruleName = ruleName;
this.inputs = inputs;
this.outputs = outputs;
this.variables = variables;
this.scope = scope;
this.offset = offset;
}
public String getRuleName() {
return ruleName;
}
public ImmutableSortedMap<String, String> getVariables() {
return variables;
}
public boolean hasInputs() {
return !inputs.isEmpty();
}
public List<PathFragment> getOutputs() {
return outputs.get(OutputKind.USUAL);
}
public List<PathFragment> getImplicitOutputs() {
return outputs.get(OutputKind.IMPLICIT);
}
public Collection<PathFragment> getAllOutputs() {
return outputs.values();
}
public Collection<PathFragment> getAllInputs() {
return inputs.values();
}
public Collection<PathFragment> getUsualInputs() {
return inputs.get(InputKind.USUAL);
}
public Collection<PathFragment> getImplicitInputs() {
return inputs.get(InputKind.IMPLICIT);
}
public Collection<PathFragment> getOrderOnlyInputs() {
return inputs.get(InputKind.ORDER_ONLY);
}
public NinjaScope getScope() {
return scope;
}
public int getOffset() {
return offset;
}
public static Builder builder(NinjaScope scope, int offset) {
return new Builder(scope, offset);
}
public String prettyPrint() {
return "build "
+ prettyPrintPaths("\n", getOutputs())
+ prettyPrintPaths("\n| ", getImplicitOutputs())
+ "\n: "
+ this.ruleName
+ prettyPrintPaths("\n", getUsualInputs())
+ prettyPrintPaths("\n| ", getImplicitInputs())
+ prettyPrintPaths("\n|| ", getOrderOnlyInputs());
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
NinjaTarget that = (NinjaTarget) o;
// Note: NinjaScope is compared with default Object#equals; it is enough
// because we do not do any copies of NinjaScope.
return offset == that.offset && scope.equals(that.scope);
}
@Override
public int hashCode() {
return Objects.hash(scope, offset);
}
private static String prettyPrintPaths(String startDelimiter, Collection<PathFragment> paths) {
if (paths.isEmpty()) {
return "";
}
return startDelimiter
+ paths.stream().map(PathFragment::getPathString).collect(Collectors.joining("$\n"));
}
}