Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Export ProGuard specs from java_import. #14966

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -256,44 +256,22 @@ private static NestedSet<Artifact> getCompileTimeJarsFromCollection(
return isDirect ? provider.getDirectCompileTimeJars() : provider.getTransitiveCompileTimeJars();
}

/**
* Collect Proguard Specs from transitives and proguard.txt if it exists in the AAR file. In the
* case the proguard.txt file does exists, we need to extract it from the AAR file
*/
/* Collects transitive and local ProGuard specs, including any embedded in the AAR. */
private NestedSet<Artifact> extractProguardSpecs(RuleContext ruleContext, Artifact aar) {

NestedSet<Artifact> proguardSpecs =
new ProguardLibrary(ruleContext).collectProguardSpecs(ImmutableSet.of("deps", "exports"));

Artifact proguardSpecArtifact = createAarArtifact(ruleContext, PROGUARD_SPEC);

Artifact extractedSpec = createAarArtifact(ruleContext, PROGUARD_SPEC);
ruleContext.registerAction(
createAarEmbeddedProguardExtractorActions(ruleContext, aar, proguardSpecArtifact));

NestedSetBuilder<Artifact> builder = NestedSetBuilder.naiveLinkOrder();
return builder.addTransitive(proguardSpecs).add(proguardSpecArtifact).build();
}

/**
* Creates action to extract embedded Proguard.txt from an AAR. If the file is not found, an empty
* file will be created
*/
private static SpawnAction createAarEmbeddedProguardExtractorActions(
RuleContext ruleContext, Artifact aar, Artifact proguardSpecArtifact) {
return new SpawnAction.Builder()
.useDefaultShellEnvironment()
.setExecutable(
ruleContext.getExecutablePrerequisite(AarImportBaseRule.AAR_EMBEDDED_PROGUARD_EXTACTOR))
new SpawnAction.Builder().useDefaultShellEnvironment()
.setExecutable(ruleContext.getExecutablePrerequisite(
AarImportBaseRule.AAR_EMBEDDED_PROGUARD_EXTACTOR))
.setMnemonic("AarEmbeddedProguardExtractor")
.setProgressMessage("Extracting proguard.txt from %s", aar.getFilename())
.addInput(aar)
.addOutput(proguardSpecArtifact)
.addCommandLine(
CustomCommandLine.builder()
.addExecPath("--input_aar", aar)
.addExecPath("--output_proguard_file", proguardSpecArtifact)
.build())
.build(ruleContext);
.addOutput(extractedSpec)
.addCommandLine(CustomCommandLine.builder()
.addExecPath("--input_aar", aar)
.addExecPath("--output_proguard_file", extractedSpec).build())
.build(ruleContext));
return new ProguardLibrary(ruleContext).collectProguardSpecs(extractedSpec);
}

private NestedSet<Artifact> getBootclasspath(RuleContext ruleContext) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import com.google.common.collect.ImmutableMap;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException;
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.ConfiguredTarget;
import com.google.devtools.build.lib.analysis.FileProvider;
import com.google.devtools.build.lib.analysis.OutputGroupInfo;
Expand Down Expand Up @@ -121,7 +123,7 @@ public ConfiguredTarget create(RuleContext ruleContext)
.build());
}

NestedSet<Artifact> proguardSpecs = new ProguardLibrary(ruleContext).collectProguardSpecs();
NestedSet<Artifact> proguardSpecs = collectProguardSpecs(ruleContext, jars);

JavaRuleOutputJarsProvider ruleOutputJarsProvider = ruleOutputJarsProviderBuilder.build();
JavaSourceJarsProvider sourceJarsProvider =
Expand Down Expand Up @@ -219,4 +221,23 @@ private ImmutableList<Artifact> processWithIjarIfNeeded(
}
return interfaceJarsBuilder.build();
}

/* Collects transitive and local ProGuard specs, including any embedded in the JARs. */
private NestedSet<Artifact> collectProguardSpecs(
RuleContext ruleContext, ImmutableList<Artifact> jars) {
Artifact extractedSpec = ruleContext.getUniqueDirectoryArtifact(
"_jar_import", "proguard.txt", ruleContext.getBinOrGenfilesDirectory());
ruleContext.registerAction(
new SpawnAction.Builder().useDefaultShellEnvironment()
.setMnemonic("JarEmbeddedProguardExtractor")
.setProgressMessage("Extracting ProGuard specs from JARs in %s",
ruleContext.getTarget().getLabel())
.setExecutable(ruleContext.getExecutablePrerequisite("$jar_embedded_proguard_extractor"))
.addCommandLine(
CustomCommandLine.builder().addExecPaths(jars).addExecPath(extractedSpec).build())
.addInputs(jars)
.addOutput(extractedSpec)
.build(ruleContext));
return new ProguardLibrary(ruleContext).collectProguardSpecs(extractedSpec);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import static com.google.devtools.build.lib.packages.Type.STRING_LIST;

import com.google.devtools.build.lib.analysis.BaseRuleClasses;
import com.google.devtools.build.lib.analysis.config.ExecutionTransitionFactory;
import com.google.devtools.build.lib.analysis.RuleDefinition;
import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment;
import com.google.devtools.build.lib.packages.RuleClass;
Expand Down Expand Up @@ -63,6 +64,11 @@ public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment envi
.orderIndependent()
.nonconfigurable(
"used in Attribute.validityPredicate implementations (loading time)"))
.add(
attr("$jar_embedded_proguard_extractor", LABEL)
.cfg(ExecutionTransitionFactory.create())
.exec()
.value(environment.getToolsLabel("//tools/jdk:jar_embedded_proguard_extractor")))
.advertiseStarlarkProvider(StarlarkProviderIdentifier.forKey(JavaInfo.PROVIDER.getKey()))
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import com.google.devtools.build.lib.collect.nestedset.Order;
import com.google.devtools.build.lib.packages.BuildType;
import java.util.Collection;
import java.util.Collections;

/**
* Helpers for implementing rules which export Proguard specs.
Expand All @@ -51,19 +52,47 @@ public NestedSet<Artifact> collectProguardSpecs() {
return collectProguardSpecs(DEPENDENCY_ATTRIBUTES);
}

/**
* Collects the validated proguard specs exported by this rule and its dependencies, adding an
* additional local spec from outside of the normal attributes, e.g., embedded in a JAR or AAR.
*/
public NestedSet<Artifact> collectProguardSpecs(Artifact additionalLocalSpec) {
return collectProguardSpecs(DEPENDENCY_ATTRIBUTES, additionalLocalSpec);
}

/**
* Collects the validated proguard specs exported by this rule and its dependencies through the
* given attributes.
*/
public NestedSet<Artifact> collectProguardSpecs(Iterable<String> attributes) {
return collectProguardSpecs(attributes, Collections.emptySet());
}

/**
* Collects the validated proguard specs exported by this rule and its dependencies through the
* given attributes, adding an additional local spec from outside of the normal attributes, e.g.,
* embedded in a JAR or AAR.
*/
public NestedSet<Artifact> collectProguardSpecs(
Iterable<String> attributes, Artifact additionalLocalSpec) {
return collectProguardSpecs(attributes, Collections.singleton(additionalLocalSpec));
}

/**
* Collects the validated proguard specs exported by this rule and its dependencies through the
* given attributes, adding additional local specs from outside of the normal attributes, e.g.,
* embedded in a JAR or AAR.
*/
public NestedSet<Artifact> collectProguardSpecs(
Iterable<String> attributes, Iterable<Artifact> additionalLocalSpecs) {
NestedSetBuilder<Artifact> specsBuilder = NestedSetBuilder.naiveLinkOrder();

for (String attribute : attributes) {
specsBuilder.addTransitive(collectProguardSpecsFromAttribute(attribute));
}

Collection<Artifact> localSpecs = collectLocalProguardSpecs();
if (!localSpecs.isEmpty()) {
if (!localSpecs.isEmpty() || additionalLocalSpecs.iterator().hasNext()) {
// Pass our local proguard configs through the validator, which checks an allowlist.
FilesToRunProvider proguardAllowlister =
JavaToolchainProvider.from(ruleContext).getProguardAllowlister();
Expand All @@ -75,6 +104,9 @@ public NestedSet<Artifact> collectProguardSpecs(Iterable<String> attributes) {
for (Artifact specToValidate : localSpecs) {
specsBuilder.add(validateProguardSpec(ruleContext, proguardAllowlister, specToValidate));
}
for (Artifact specToValidate : additionalLocalSpecs) {
specsBuilder.add(validateProguardSpec(ruleContext, proguardAllowlister, specToValidate));
}
}

return specsBuilder.build();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,30 +166,10 @@ public void proguardSpecsProvided() throws Exception {
.map(Artifact::getRootRelativePathString)
.collect(Collectors.toSet()))
.containsExactly(
"a/_aar/bar/proguard.txt", "a/_aar/foo/proguard.txt", "a/_aar/baz/proguard.txt");
}

@Test
public void testProguardExtractor() throws Exception {
ConfiguredTarget target = getConfiguredTarget("//a:bar");
Artifact proguardSpecsAritfact =
target.get(ProguardSpecProvider.PROVIDER).getTransitiveProguardSpecs().toList().get(0);

Artifact aarProguardExtractor =
getDirectPrerequisite(
target,
ruleClassProvider.getToolsRepository()
+ "//tools/android:aar_embedded_proguard_extractor")
.getProvider(FilesToRunProvider.class)
.getExecutable();

assertThat(getGeneratingSpawnAction(proguardSpecsAritfact).getArguments())
.containsExactly(
aarProguardExtractor.getExecPathString(),
"--input_aar",
"a/bar.aar",
"--output_proguard_file",
proguardSpecsAritfact.getExecPathString());
"a/validated_proguard/bar/a/_aar/bar/proguard.txt_valid",
"java/validated_proguard/baz/java/_jar_import/baz/proguard.txt_valid",
"a/validated_proguard/baz/a/_aar/baz/proguard.txt_valid",
"a/validated_proguard/foo/a/_aar/foo/proguard.txt_valid");
}

@Test
Expand Down
1 change: 1 addition & 0 deletions tools/jdk/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ filegroup(
"jdk.BUILD",
"local_java_repository.bzl",
"nosystemjdk/README",
"jar_embedded_proguard_extractor.py",
"proguard_whitelister.py",
"proguard_whitelister_test.py",
"proguard_whitelister_test_input.pgcfg",
Expand Down
5 changes: 5 additions & 0 deletions tools/jdk/BUILD.tools
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,11 @@ filegroup(
visibility = ["//tools:__pkg__"],
)

py_binary(
name = "jar_embedded_proguard_extractor",
srcs = ["jar_embedded_proguard_extractor.py"],
)

py_binary(
name = "proguard_whitelister",
srcs = [
Expand Down
40 changes: 40 additions & 0 deletions tools/jdk/jar_embedded_proguard_extractor.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Copyright 2022 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.

"""A tool for extracting proguard specs from JARs.

Usage:
Takes positional arguments: input_jar1 input_jar2 <...> output_proguard_spec

Background:
A JAR may contain proguard specs under /META-INF/proguard/
[https://developer.android.com/studio/build/shrink-code]

This tool extracts them all into a single spec for easy usage."""


import sys
import zipfile


if __name__ == "__main__":
with open(sys.argv[-1], 'wb') as output_proguard_spec:
for jar_path in sys.argv[1:-1]:
with zipfile.ZipFile(jar_path) as jar:
for entry in jar.namelist():
if entry.startswith('META-INF/proguard'):
# zip directories are empty and therefore ok to output
output_proguard_spec.write(jar.read(entry))
# gracefully handle any lack of trailing newline
output_proguard_spec.write(b'\n')