blob: 193c0fdb615e4038ed6269659e1d47cfa98a107c [file] [log] [blame]
// Copyright 2015 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.java;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.analysis.FilesToRunProvider;
import com.google.devtools.build.lib.analysis.RuleContext;
import com.google.devtools.build.lib.analysis.actions.CustomCommandLine;
import com.google.devtools.build.lib.analysis.actions.SpawnAction;
import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode;
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.packages.BuildType;
import java.util.Collection;
import java.util.Map;
/**
* Helpers for implementing rules which export Proguard specs.
*
* <p>This is not a ConfiguredTargetFactory; $proguard_library, which this class implements, is an
* abstract rule class, and simply contributes this functionality to other rules.
*/
public final class ProguardLibrary {
private static final String LOCAL_SPEC_ATTRIBUTE = "proguard_specs";
private static final ImmutableMultimap<Mode, String> DEPENDENCY_ATTRIBUTES =
ImmutableMultimap.<Mode, String>builder()
.putAll(Mode.TARGET, "deps", "exports", "runtime_deps")
.putAll(Mode.HOST, "plugins", "exported_plugins")
.build();
private final RuleContext ruleContext;
/** Creates a new ProguardLibrary wrapping the given RuleContext. */
public ProguardLibrary(RuleContext ruleContext) {
this.ruleContext = ruleContext;
}
/** Collects the validated proguard specs exported by this rule and its dependencies. */
public NestedSet<Artifact> collectProguardSpecs() {
return collectProguardSpecs(DEPENDENCY_ATTRIBUTES);
}
/**
* Collects the validated proguard specs exported by this rule and its dependencies through the
* given attributes.
*/
public NestedSet<Artifact> collectProguardSpecs(Multimap<Mode, String> attributes) {
NestedSetBuilder<Artifact> specsBuilder = NestedSetBuilder.naiveLinkOrder();
for (Map.Entry<Mode, String> attribute : attributes.entries()) {
specsBuilder.addTransitive(
collectProguardSpecsFromAttribute(attribute.getValue(), attribute.getKey()));
}
Collection<Artifact> localSpecs = collectLocalProguardSpecs();
if (!localSpecs.isEmpty()) {
// Pass our local proguard configs through the validator, which checks a whitelist.
FilesToRunProvider proguardWhitelister =
ruleContext.getExecutablePrerequisite("$proguard_whitelister", Mode.HOST);
for (Artifact specToValidate : localSpecs) {
specsBuilder.add(validateProguardSpec(proguardWhitelister, specToValidate));
}
}
return specsBuilder.build();
}
/** Collects the unvalidated proguard specs exported by this rule. */
public ImmutableList<Artifact> collectLocalProguardSpecs() {
if (!ruleContext.attributes().has(LOCAL_SPEC_ATTRIBUTE, BuildType.LABEL_LIST)) {
return ImmutableList.of();
}
return ruleContext.getPrerequisiteArtifacts(LOCAL_SPEC_ATTRIBUTE, Mode.TARGET).list();
}
/**
* Collects the proguard specs exported by dependencies on the given LABEL_LIST/LABEL attribute.
*/
private NestedSet<Artifact> collectProguardSpecsFromAttribute(String attributeName, Mode mode) {
if (!ruleContext.attributes().has(attributeName, BuildType.LABEL_LIST)
&& !ruleContext.attributes().has(attributeName, BuildType.LABEL)) {
return NestedSetBuilder.emptySet(Order.NAIVE_LINK_ORDER);
}
NestedSetBuilder<Artifact> dependencySpecsBuilder = NestedSetBuilder.naiveLinkOrder();
for (ProguardSpecProvider provider :
ruleContext.getPrerequisites(attributeName, mode, ProguardSpecProvider.PROVIDER)) {
dependencySpecsBuilder.addTransitive(provider.getTransitiveProguardSpecs());
}
return dependencySpecsBuilder.build();
}
/**
* Creates an action to run the Proguard whitelister over the given Proguard spec and returns the
* validated Proguard spec, ready to be exported.
*/
private Artifact validateProguardSpec(
FilesToRunProvider proguardWhitelister, Artifact specToValidate) {
// If we're validating j/a/b/testapp/proguard.cfg, the output will be:
// j/a/b/testapp/proguard.cfg_valid
Artifact output =
ruleContext.getUniqueDirectoryArtifact(
"validated_proguard",
specToValidate
.getRootRelativePath()
.replaceName(specToValidate.getFilename() + "_valid"),
ruleContext.getBinOrGenfilesDirectory());
ruleContext.registerAction(
new SpawnAction.Builder()
.addInput(specToValidate)
.addOutput(output)
.setExecutable(proguardWhitelister)
.setProgressMessage("Validating proguard configuration")
.setMnemonic("ValidateProguard")
.addCommandLine(
CustomCommandLine.builder()
.addExecPath("--path", specToValidate)
.addExecPath("--output", output)
.build())
.build(ruleContext));
return output;
}
}