Skip to content

Commit c8c0d94

Browse files
benjaminRomanophilwo
authored andcommitted
Export proguard specs from aar_import
**Background** bazelbuild#3778 proguard specs from the `aar_import` rule do not get bubbled up to `android_binary`. In this PR, I wire up a `ProguardSpecProvider` from this rule that exports the `proguard.txt` within an AAR if it exists and any transitive proguard specs from the `exports` attribute. **Changes** * Add an `aar_embedded_proguard_extractor` script to extract `proguard.txt` from an AAR if it exists otherwise generate an empty proguard specs file * In AarImport, wire up the proguard extractor action and export results through a `ProguardSpecProvider`. Once this lands, the android rules would need to be bumped. **Test Plan** * Added tests for the extraction python script * Added tests for the `aar_import` rule changes Closes bazelbuild#12749. PiperOrigin-RevId: 359667674
1 parent f917dc1 commit c8c0d94

File tree

9 files changed

+249
-0
lines changed

9 files changed

+249
-0
lines changed

src/main/java/com/google/devtools/build/lib/rules/android/AarImport.java

+45
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
package com.google.devtools.build.lib.rules.android;
1515

1616
import com.google.common.collect.ImmutableList;
17+
import com.google.common.collect.ImmutableSet;
1718
import com.google.devtools.build.lib.actions.Action;
1819
import com.google.devtools.build.lib.actions.Artifact;
1920
import com.google.devtools.build.lib.actions.Artifact.SpecialArtifact;
@@ -45,6 +46,8 @@
4546
import com.google.devtools.build.lib.rules.java.JavaSourceInfoProvider;
4647
import com.google.devtools.build.lib.rules.java.JavaSourceJarsProvider;
4748
import com.google.devtools.build.lib.rules.java.JavaToolchainProvider;
49+
import com.google.devtools.build.lib.rules.java.ProguardLibrary;
50+
import com.google.devtools.build.lib.rules.java.ProguardSpecProvider;
4851
import com.google.devtools.build.lib.starlarkbuildapi.android.DataBindingV2ProviderApi;
4952
import com.google.devtools.build.lib.vfs.PathFragment;
5053
import javax.annotation.Nullable;
@@ -61,6 +64,7 @@
6164
public class AarImport implements RuleConfiguredTargetFactory {
6265
private static final String ANDROID_MANIFEST = "AndroidManifest.xml";
6366
private static final String MERGED_JAR = "classes_and_libs_merged.jar";
67+
private static final String PROGUARD_SPEC = "proguard.txt";
6468

6569
private final JavaSemantics javaSemantics;
6670
private final AndroidSemantics androidSemantics;
@@ -239,6 +243,7 @@ public ConfiguredTarget create(RuleContext ruleContext)
239243
.setFilesToBuild(filesToBuild)
240244
.addProvider(RunfilesProvider.class, RunfilesProvider.EMPTY)
241245
.addNativeDeclaredProvider(dataBindingV2Provider)
246+
.addNativeDeclaredProvider(new ProguardSpecProvider(extractProguardSpecs(ruleContext, aar)))
242247
.addNativeDeclaredProvider(
243248
new AndroidNativeLibsInfo(
244249
AndroidCommon.collectTransitiveNativeLibs(ruleContext).add(nativeLibs).build()))
@@ -256,6 +261,46 @@ private static NestedSet<Artifact> getCompileTimeJarsFromCollection(
256261
return isDirect ? provider.getDirectCompileTimeJars() : provider.getTransitiveCompileTimeJars();
257262
}
258263

264+
/**
265+
* Collect Proguard Specs from transitives and proguard.txt if it exists in the AAR file. In the
266+
* case the proguard.txt file does exists, we need to extract it from the AAR file
267+
*/
268+
private NestedSet<Artifact> extractProguardSpecs(RuleContext ruleContext, Artifact aar) {
269+
270+
NestedSet<Artifact> proguardSpecs =
271+
new ProguardLibrary(ruleContext).collectProguardSpecs(ImmutableSet.of("deps", "exports"));
272+
273+
Artifact proguardSpecArtifact = createAarArtifact(ruleContext, PROGUARD_SPEC);
274+
275+
ruleContext.registerAction(
276+
createAarEmbeddedProguardExtractorActions(ruleContext, aar, proguardSpecArtifact));
277+
278+
NestedSetBuilder<Artifact> builder = NestedSetBuilder.naiveLinkOrder();
279+
return builder.addTransitive(proguardSpecs).add(proguardSpecArtifact).build();
280+
}
281+
282+
/**
283+
* Create action to extract embedded Proguard.txt from an AAR. If the file is not found, an empty
284+
* file will be created
285+
*/
286+
private static Action[] createAarEmbeddedProguardExtractorActions(
287+
RuleContext ruleContext, Artifact aar, Artifact proguardSpecArtifact) {
288+
return new SpawnAction.Builder()
289+
.useDefaultShellEnvironment()
290+
.setExecutable(
291+
ruleContext.getExecutablePrerequisite(AarImportBaseRule.AAR_EMBEDDED_PROGUARD_EXTACTOR))
292+
.setMnemonic("AarEmbeddedProguardExtractor")
293+
.setProgressMessage("Extracting proguard.txt from %s", aar.getFilename())
294+
.addInput(aar)
295+
.addOutput(proguardSpecArtifact)
296+
.addCommandLine(
297+
CustomCommandLine.builder()
298+
.addExecPath("--input_aar", aar)
299+
.addExecPath("--output_proguard_file", proguardSpecArtifact)
300+
.build())
301+
.build(ruleContext);
302+
}
303+
259304
private NestedSet<Artifact> getBootclasspath(RuleContext ruleContext) {
260305
if (AndroidCommon.getAndroidConfig(ruleContext).desugarJava8()) {
261306
return NestedSetBuilder.<Artifact>stableOrder()

src/main/java/com/google/devtools/build/lib/rules/android/AarImportBaseRule.java

+6
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
public class AarImportBaseRule implements RuleDefinition {
3535

3636
static final String AAR_EMBEDDED_JARS_EXTACTOR = "$aar_embedded_jars_extractor";
37+
static final String AAR_EMBEDDED_PROGUARD_EXTACTOR = "$aar_embedded_proguard_extractor";
3738
static final String AAR_NATIVE_LIBS_ZIP_CREATOR = "$aar_native_libs_zip_creator";
3839
static final String AAR_RESOURCES_EXTRACTOR = "$aar_resources_extractor";
3940
static final String ZIPPER = "$zipper";
@@ -66,6 +67,11 @@ public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment env)
6667
.cfg(HostTransition.createFactory())
6768
.exec()
6869
.value(env.getToolsLabel("//tools/android:aar_embedded_jars_extractor")))
70+
.add(
71+
attr(AAR_EMBEDDED_PROGUARD_EXTACTOR, LABEL)
72+
.cfg(HostTransition.createFactory())
73+
.exec()
74+
.value(env.getToolsLabel("//tools/android:aar_embedded_proguard_extractor")))
6975
.add(
7076
attr(AAR_NATIVE_LIBS_ZIP_CREATOR, LABEL)
7177
.cfg(HostTransition.createFactory())

src/test/java/com/google/devtools/build/lib/analysis/mock/BazelAnalysisMock.java

+1
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,7 @@ private ImmutableList<String> createAndroidBuildContents() {
448448
.add(" jars = [ 'ZipFilterAction_deploy.jar' ])")
449449
.add("sh_binary(name = 'aar_resources_extractor', srcs = ['empty.sh'])")
450450
.add("sh_binary(name = 'aar_embedded_jars_extractor', srcs = ['empty.sh'])")
451+
.add("sh_binary(name = 'aar_embedded_proguard_extractor', srcs = ['empty.sh'])")
451452
.add("java_import(name = 'idlclass_import',")
452453
.add(" jars = [ 'idlclass.jar' ])")
453454
.add("exports_files(['adb', 'adb_static'])")

src/test/java/com/google/devtools/build/lib/rules/android/AarImportTest.java

+41
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import com.google.devtools.build.lib.rules.java.JavaRuleOutputJarsProvider.OutputJar;
4242
import com.google.devtools.build.lib.rules.java.JavaSourceInfoProvider;
4343
import com.google.devtools.build.lib.rules.java.JavaSourceJarsProvider;
44+
import com.google.devtools.build.lib.rules.java.ProguardSpecProvider;
4445
import java.util.Collection;
4546
import java.util.Iterator;
4647
import java.util.List;
@@ -156,6 +157,46 @@ public void setup() throws Exception {
156157
getAnalysisMock().ccSupport().setupCcToolchainConfigForCpu(mockToolsConfig, "armeabi-v7a");
157158
}
158159

160+
@Test
161+
public void proguardSpecsProvided() throws Exception {
162+
ConfiguredTarget binaryTarget = getConfiguredTarget("//a:bar");
163+
164+
NestedSet<Artifact> transitiveProguardSpecs =
165+
binaryTarget.get(ProguardSpecProvider.PROVIDER).getTransitiveProguardSpecs();
166+
167+
assertThat(
168+
transitiveProguardSpecs.toSet().stream()
169+
.map(Artifact::getRootRelativePathString)
170+
.collect(Collectors.toSet()))
171+
.containsExactly(
172+
"a/_aar/bar/proguard.txt", "a/_aar/foo/proguard.txt", "a/_aar/baz/proguard.txt");
173+
}
174+
175+
@Test
176+
public void testProguardExtractor() throws Exception {
177+
Artifact proguardSpecsAritfact =
178+
getConfiguredTarget("//a:bar")
179+
.get(ProguardSpecProvider.PROVIDER)
180+
.getTransitiveProguardSpecs()
181+
.toList()
182+
.get(0);
183+
184+
Artifact aarProguardExtractor =
185+
getHostConfiguredTarget(
186+
ruleClassProvider.getToolsRepository()
187+
+ "//tools/android:aar_embedded_proguard_extractor")
188+
.getProvider(FilesToRunProvider.class)
189+
.getExecutable();
190+
191+
assertThat(getGeneratingSpawnAction(proguardSpecsAritfact).getArguments())
192+
.containsExactly(
193+
aarProguardExtractor.getExecPathString(),
194+
"--input_aar",
195+
"a/bar.aar",
196+
"--output_proguard_file",
197+
proguardSpecsAritfact.getExecPathString());
198+
}
199+
159200
@Test
160201
public void aapt2RTxtProvided() throws Exception {
161202
useConfiguration("--android_sdk=//aapt2/sdk:sdk");

src/test/java/com/google/devtools/build/lib/rules/android/BUILD

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ java_test(
2626
"//src/main/java/com/google/devtools/build/lib/collect/nestedset",
2727
"//src/main/java/com/google/devtools/build/lib/rules/android",
2828
"//src/main/java/com/google/devtools/build/lib/rules/java:java-compilation",
29+
"//src/main/java/com/google/devtools/build/lib/rules/java:java-rules",
2930
"//src/test/java/com/google/devtools/build/lib/actions/util",
3031
"//src/test/java/com/google/devtools/build/lib/analysis/util",
3132
"//third_party:guava",

tools/android/BUILD

+15
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,21 @@ py_test(
118118
],
119119
)
120120

121+
py_binary(
122+
name = "aar_embedded_proguard_extractor",
123+
srcs = ["aar_embedded_proguard_extractor.py"],
124+
deps = [
125+
":junction_lib",
126+
"//third_party/py/abseil",
127+
],
128+
)
129+
130+
py_test(
131+
name = "aar_embedded_proguard_extractor_test",
132+
srcs = ["aar_embedded_proguard_extractor_test.py"],
133+
deps = [":aar_embedded_proguard_extractor"],
134+
)
135+
121136
py_binary(
122137
name = "aar_embedded_jars_extractor",
123138
srcs = ["aar_embedded_jars_extractor.py"],

tools/android/BUILD.tools

+10
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,16 @@ py_binary(
328328
],
329329
)
330330

331+
py_binary(
332+
name = "aar_embedded_proguard_extractor",
333+
srcs = ["aar_embedded_proguard_extractor.py"],
334+
python_version = "PY3",
335+
deps = [
336+
":junction_lib",
337+
"//third_party/py/abseil",
338+
],
339+
)
340+
331341
py_binary(
332342
name = "aar_embedded_jars_extractor",
333343
srcs = ["aar_embedded_jars_extractor.py"],
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Lint as: python2, python3
2+
# pylint: disable=g-direct-third-party-import
3+
# Copyright 2021 The Bazel Authors. All rights reserved.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
"""A tool for extracting the proguard spec file from an AAR."""
17+
18+
from __future__ import absolute_import
19+
from __future__ import division
20+
from __future__ import print_function
21+
22+
import os
23+
import sys
24+
import zipfile
25+
26+
# Do not edit this line. Copybara replaces it with PY2 migration helper.
27+
from absl import app
28+
from absl import flags
29+
30+
from tools.android import junction
31+
32+
FLAGS = flags.FLAGS
33+
34+
flags.DEFINE_string("input_aar", None, "Input AAR")
35+
flags.mark_flag_as_required("input_aar")
36+
flags.DEFINE_string("output_proguard_file", None,
37+
"Output parameter file for proguard")
38+
flags.mark_flag_as_required("output_proguard_file")
39+
40+
41+
# Attempt to extract proguard spec from AAR. If the file doesn't exist, an empty
42+
# proguard spec file will be created
43+
def ExtractEmbeddedProguard(aar, output):
44+
proguard_spec = "proguard.txt"
45+
46+
if proguard_spec in aar.namelist():
47+
output.write(aar.read(proguard_spec))
48+
49+
50+
def _Main(input_aar, output_proguard_file):
51+
with zipfile.ZipFile(input_aar, "r") as aar:
52+
with open(output_proguard_file, "wb") as output:
53+
ExtractEmbeddedProguard(aar, output)
54+
55+
56+
def main(unused_argv):
57+
if os.name == "nt":
58+
# Shorten paths unconditionally, because the extracted paths in
59+
# ExtractEmbeddedJars (which we cannot yet predict, because they depend on
60+
# the names of the Zip entries) may be longer than MAX_PATH.
61+
aar_long = os.path.abspath(FLAGS.input_aar)
62+
proguard_long = os.path.abspath(FLAGS.output_proguard_file)
63+
64+
with junction.TempJunction(os.path.dirname(aar_long)) as aar_junc:
65+
with junction.TempJunction(
66+
os.path.dirname(proguard_long)) as proguard_junc:
67+
_Main(
68+
os.path.join(aar_junc, os.path.basename(aar_long)),
69+
os.path.join(proguard_junc, os.path.basename(proguard_long)))
70+
else:
71+
_Main(FLAGS.input_aar, FLAGS.output_proguard_file)
72+
73+
74+
if __name__ == "__main__":
75+
FLAGS(sys.argv)
76+
app.run(main)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Copyright 2021 The Bazel Authors. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
"""Tests for aar_embedded_proguard_extractor."""
15+
16+
import io
17+
import os
18+
import unittest
19+
import zipfile
20+
21+
from tools.android import aar_embedded_proguard_extractor
22+
23+
24+
class AarEmbeddedProguardExtractor(unittest.TestCase):
25+
"""Unit tests for aar_embedded_proguard_extractor.py."""
26+
27+
# Python 2 alias
28+
if not hasattr(unittest.TestCase, "assertCountEqual"):
29+
30+
def assertCountEqual(self, *args):
31+
return self.assertItemsEqual(*args)
32+
33+
def setUp(self):
34+
super(AarEmbeddedProguardExtractor, self).setUp()
35+
os.chdir(os.environ["TEST_TMPDIR"])
36+
37+
def testNoProguardTxt(self):
38+
aar = zipfile.ZipFile(io.BytesIO(), "w")
39+
proguard_file = io.BytesIO()
40+
aar_embedded_proguard_extractor.ExtractEmbeddedProguard(aar, proguard_file)
41+
proguard_file.seek(0)
42+
self.assertEqual(b"", proguard_file.read())
43+
44+
def testWithProguardTxt(self):
45+
aar = zipfile.ZipFile(io.BytesIO(), "w")
46+
aar.writestr("proguard.txt", "hello world")
47+
proguard_file = io.BytesIO()
48+
aar_embedded_proguard_extractor.ExtractEmbeddedProguard(aar, proguard_file)
49+
proguard_file.seek(0)
50+
self.assertEqual(b"hello world", proguard_file.read())
51+
52+
53+
if __name__ == "__main__":
54+
unittest.main()

0 commit comments

Comments
 (0)