/*
* 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;
import com.facebook.buck.io.ProjectFilesystem;
import com.facebook.buck.model.BuildTarget;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.hash.HashCode;
public abstract class AbstractNodeBuilderWithImmutableArg<
TArgBuilder, TArg, TDescription extends Description<TArg>, TBuildRule extends BuildRule>
extends AbstractNodeBuilder<TArg, TDescription, TBuildRule> {
protected final TArgBuilder argBuilder;
protected AbstractNodeBuilderWithImmutableArg(TDescription description, BuildTarget target) {
super(description, target);
this.argBuilder = makeArgBuilder(description);
}
protected AbstractNodeBuilderWithImmutableArg(
TDescription description, BuildTarget target, ProjectFilesystem projectFilesystem) {
super(description, target, projectFilesystem);
this.argBuilder = makeArgBuilder(description);
}
protected AbstractNodeBuilderWithImmutableArg(
TDescription description,
BuildTarget target,
ProjectFilesystem projectFilesystem,
HashCode hashCode) {
super(description, target, projectFilesystem, hashCode);
this.argBuilder = makeArgBuilder(description);
}
@SuppressWarnings("unchecked")
private TArgBuilder makeArgBuilder(TDescription description) {
Class<? extends TArg> constructorArgType = description.getConstructorArgType();
TArgBuilder builder;
try {
builder = (TArgBuilder) constructorArgType.getMethod("builder").invoke(null);
// Set a default value for name from the target. The real coercer stack implicitly sets name,
// but we're not going through that stack so we emulate it instead.
// If setName is explicitly called, its value with override this one.
builder.getClass().getMethod("setName", String.class).invoke(builder, target.getShortName());
} catch (Exception e) {
throw new RuntimeException(e);
}
return builder;
}
@Override
protected TArgBuilder getArgForPopulating() {
return argBuilder;
}
@Override
@SuppressWarnings("unchecked")
protected TArg getPopulatedArg() {
try {
return (TArg) argBuilder.getClass().getMethod("build").invoke(argBuilder);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@SuppressWarnings("unchecked")
@Override
protected final ImmutableSortedSet<BuildTarget> getDepsFromArg(TArg arg) {
// Not all rules have deps, but all rules call them deps. When they do, they're always optional.
// Grab them in the unsafest way I know.
try {
// Here's a whole series of assumptions in one lump of a Bad Idea.
return (ImmutableSortedSet<BuildTarget>) arg.getClass().getMethod("getDeps").invoke(arg);
} catch (ReflectiveOperationException ignored) {
// Method doesn't exist: no deps.
return ImmutableSortedSet.of();
}
}
}