/*
* Copyright 2017-present Facebook, Inc.
*
* 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.facebook.buck.rules.args;
import com.facebook.buck.model.BuildTarget;
import com.facebook.buck.model.MacroException;
import com.facebook.buck.rules.BuildRule;
import com.facebook.buck.rules.BuildRuleResolver;
import com.facebook.buck.rules.CellPathResolver;
import com.facebook.buck.rules.RuleKeyObjectSink;
import com.facebook.buck.rules.SourcePath;
import com.facebook.buck.rules.SourcePathResolver;
import com.facebook.buck.rules.SourcePathRuleFinder;
import com.facebook.buck.rules.macros.AbstractMacroExpander;
import com.facebook.buck.rules.macros.Macro;
import com.facebook.buck.rules.macros.StringWithMacros;
import com.facebook.buck.util.HumanReadableException;
import com.facebook.buck.util.RichStream;
import com.facebook.buck.util.immutables.BuckStyleTuple;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import javax.annotation.Nullable;
import org.immutables.value.Value;
/**
* Packages a {@link StringWithMacros} along with necessary objects to implement the {@link Arg}
* interface.
*/
@Value.Immutable
@BuckStyleTuple
abstract class AbstractStringWithMacrosArg implements Arg {
abstract StringWithMacros getStringWithMacros();
abstract ImmutableList<AbstractMacroExpander<? extends Macro>> getExpanders();
abstract BuildTarget getBuildTarget();
abstract CellPathResolver getCellPathResolver();
abstract BuildRuleResolver getBuildRuleResolver();
@Value.Derived
ImmutableMap<Class<? extends Macro>, AbstractMacroExpander<? extends Macro>> getClassExpanders() {
ImmutableMap.Builder<Class<? extends Macro>, AbstractMacroExpander<? extends Macro>> builder =
ImmutableMap.builder();
for (AbstractMacroExpander<? extends Macro> expander : getExpanders()) {
builder.put(expander.getInputClass(), expander);
}
return builder.build();
}
@SuppressWarnings("unchecked")
private <M extends Macro> AbstractMacroExpander<M> getExpander(M macro) throws MacroException {
AbstractMacroExpander<M> expander =
(AbstractMacroExpander<M>) getClassExpanders().get(macro.getClass());
if (expander == null) {
throw new MacroException(String.format("unexpected macro %s", macro.getClass()));
}
return expander;
}
private HumanReadableException toHumanReadableException(MacroException e) {
return new HumanReadableException(e, "%s: %s", getBuildTarget(), e.getMessage());
}
private <M extends Macro> ImmutableCollection<BuildRule> extractDeps(M macro) {
try {
return getExpander(macro)
.extractBuildTimeDepsFrom(
getBuildTarget(), getCellPathResolver(), getBuildRuleResolver(), macro);
} catch (MacroException e) {
throw toHumanReadableException(e);
}
}
@Nullable
private <M extends Macro> Object extractRuleKeyAppendables(M macro) {
try {
return getExpander(macro)
.extractRuleKeyAppendablesFrom(
getBuildTarget(), getCellPathResolver(), getBuildRuleResolver(), macro);
} catch (MacroException e) {
throw toHumanReadableException(e);
}
}
private <M extends Macro> String expand(M macro) {
try {
return getExpander(macro)
.expandFrom(getBuildTarget(), getCellPathResolver(), getBuildRuleResolver(), macro);
} catch (MacroException e) {
throw toHumanReadableException(e);
}
}
/** @return the build-time deps from all embedded macros. */
public ImmutableCollection<BuildRule> getDeps() {
return RichStream.from(getStringWithMacros().getMacros())
.map(this::extractDeps)
.flatMap(ImmutableCollection::stream)
.toImmutableList();
}
@Override
public ImmutableCollection<BuildRule> getDeps(SourcePathRuleFinder ruleFinder) {
return getDeps();
}
/** @return the inputs from all embedded macros. */
@Override
public ImmutableCollection<SourcePath> getInputs() {
return RichStream.from(getDeps()).map(BuildRule::getSourcePathToOutput).toImmutableList();
}
/** Expands all macros to strings and append them to the given builder. */
@Override
public void appendToCommandLine(
ImmutableCollection.Builder<String> builder, SourcePathResolver pathResolver) {
builder.add(getStringWithMacros().format(this::expand));
}
/** Add the macros to the rule key. */
@Override
public void appendToRuleKey(RuleKeyObjectSink sink) {
sink.setReflectively(
"macros", getStringWithMacros().map(s -> s, this::extractRuleKeyAppendables));
}
}