// 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.RuleConfiguredTarget.Mode; import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.analysis.actions.SpawnAction; 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.Entry; /** * 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 (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. */ private Collection<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.class)) { 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) .setExecutable(proguardWhitelister) .setProgressMessage("Validating proguard configuration") .setMnemonic("ValidateProguard") .addArgument("--path") .addArgument(specToValidate.getExecPathString()) .addArgument("--output") .addArgument(output.getExecPathString()) .addOutput(output) .build(ruleContext)); return output; } }