From e3aafe120d8087dbcad9a018b1b2974883f9ae20 Mon Sep 17 00:00:00 2001 From: Mauricio Galindo Date: Wed, 21 Jun 2023 10:43:45 -0700 Subject: [PATCH] Add option to extract proguard configurations in jvm_import Some jars include proguard specs in META-INF/proguard/ META-INF/com.android.tools We need to extract these files in order to pass them correctly to R8 for android builds. Normally R8 should detect these files automatically inside the jar, but if proguard specs are specified in META-INF/com.android.tools and META-INF/proguard the files under META-INF/proguard are ignored. See https://r8.googlesource.com/r8/+/refs/heads/main/src/main/java/com/android/tools/r8/R8Command.java#1394 Given that bazel uses a single deploy jar when running R8 it is likely that both directories exist and several needed configs are ignored. see https://github.com/bazelbuild/bazel/pull/14966#issuecomment-1563483175 --- private/rules/jvm_import.bzl | 31 +++++- .../bazelbuild/rules_jvm_external/jar/BUILD | 11 +++ .../jar/ExtractProguardConfig.java | 94 +++++++++++++++++++ settings/BUILD | 11 ++- settings/extract_proguard_config.bzl | 9 ++ 5 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 private/tools/java/com/github/bazelbuild/rules_jvm_external/jar/ExtractProguardConfig.java create mode 100644 settings/extract_proguard_config.bzl diff --git a/private/rules/jvm_import.bzl b/private/rules/jvm_import.bzl index 77c6fc86..0bc164bb 100644 --- a/private/rules/jvm_import.bzl +++ b/private/rules/jvm_import.bzl @@ -8,6 +8,7 @@ # [1]: https://github.com/bazelbuild/bazel/issues/4549 load("//settings:stamp_manifest.bzl", "StampManifestProvider") +load("//settings:extract_proguard_config.bzl", "ExtractProguardConfigProvider") def _jvm_import_impl(ctx): if len(ctx.files.jars) != 1: @@ -30,6 +31,26 @@ def _jvm_import_impl(ctx): else: outjar = injar + proguard_info = [] + if ctx.attr._extract_proguard_config[ExtractProguardConfigProvider].extract_proguard_config: + spec = ctx.actions.declare_file("%s_proguard.pro" % injar.basename.split(".jar")[0]) + + args = ctx.actions.args() + args.add("--jar_to_spec", "%s:%s" % (injar.path, spec.path)) + + ctx.actions.run( + inputs = [injar], + outputs = [spec], + executable = ctx.executable._proguard_config_extractor, + arguments = [args], + mnemonic = "ExtractProguardSpec", + progress_message = "Extracting proguard config of %s" % ctx.label, + ) + + proguard_info = [ + ProguardSpecProvider(depset(direct = [spec])), + ] + compilejar = ctx.actions.declare_file("header_" + injar.basename, sibling = injar) args = ctx.actions.args() args.add_all(["--source", outjar, "--output", compilejar]) @@ -69,7 +90,7 @@ def _jvm_import_impl(ctx): ], neverlink = ctx.attr.neverlink, ), - ] + ] + proguard_info jvm_import = rule( attrs = { @@ -95,9 +116,17 @@ jvm_import = rule( cfg = "exec", default = "//private/tools/java/com/github/bazelbuild/rules_jvm_external/jar:AddJarManifestEntry", ), + "_proguard_config_extractor": attr.label( + executable = True, + cfg = "exec", + default = "//private/tools/java/com/github/bazelbuild/rules_jvm_external/jar:ExtractProguardConfig", + ), "_stamp_manifest": attr.label( default = "@rules_jvm_external//settings:stamp_manifest", ), + "_extract_proguard_config": attr.label( + default = "@rules_jvm_external//settings:extract_proguard_config", + ), }, implementation = _jvm_import_impl, provides = [JavaInfo], diff --git a/private/tools/java/com/github/bazelbuild/rules_jvm_external/jar/BUILD b/private/tools/java/com/github/bazelbuild/rules_jvm_external/jar/BUILD index c8f794ba..dd711b38 100644 --- a/private/tools/java/com/github/bazelbuild/rules_jvm_external/jar/BUILD +++ b/private/tools/java/com/github/bazelbuild/rules_jvm_external/jar/BUILD @@ -51,3 +51,14 @@ java_binary( "//private/tools/java/com/github/bazelbuild/rules_jvm_external/zip", ], ) + +java_binary( + name = "ExtractProguardConfig", + srcs = [ + "ExtractProguardConfig.java", + ], + main_class = "com.github.bazelbuild.rules_jvm_external.jar.ExtractProguardConfig", + visibility = [ + "//visibility:public", + ], +) diff --git a/private/tools/java/com/github/bazelbuild/rules_jvm_external/jar/ExtractProguardConfig.java b/private/tools/java/com/github/bazelbuild/rules_jvm_external/jar/ExtractProguardConfig.java new file mode 100644 index 00000000..7128d7b4 --- /dev/null +++ b/private/tools/java/com/github/bazelbuild/rules_jvm_external/jar/ExtractProguardConfig.java @@ -0,0 +1,94 @@ +package com.github.bazelbuild.rules_jvm_external.jar; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Enumeration; +import java.util.List; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +public final class ExtractProguardConfig { + + private List jarToSpec; + + // Directories to search for proguard configurations. + // The list is sorted from highest priorty to lowest priority + // Only the first one is extracted. + private List proguardDirs = Arrays.asList( + "META-INF/com.android.tools/r8", + "META-INF/com.android.tools/proguard", + "META-INF/proguard" + ); + + public ExtractProguardConfig(List jarToSpec) { + this.jarToSpec = jarToSpec; + } + + private boolean maybeCopySpec(String jarPath, String directoryName, String spec) { + try { + JarFile jarFile = new JarFile(jarPath); + Enumeration entries = jarFile.entries(); + while (entries.hasMoreElements()) { + JarEntry entry = entries.nextElement(); + if (!entry.isDirectory() && entry.getName().startsWith(directoryName)) { + File outputFile = new File(spec); + FileOutputStream outputStream = new FileOutputStream(outputFile); + jarFile.getInputStream(entry).transferTo(outputStream); + + outputStream.close(); + return true; + } + } + jarFile.close(); + } catch (IOException e) { + e.printStackTrace(); + } + return false; + } + + private void extractSpec(String jar, String spec) { + boolean hadSpec = false; + for (String dir : proguardDirs) { + hadSpec = maybeCopySpec(jar, dir, spec); + if (hadSpec) { + break; + } + } + + if (!hadSpec) { + try { + File file = new File(spec); + file.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + public void run() { + for (String js : jarToSpec) { + String[] parts = js.split(":"); + if (parts.length != 2) { + throw new IllegalArgumentException("Invalid jar_to_spec value: " + js); + } + extractSpec(parts[0], parts[1]); + } + } + + public static void main(String[] args) { + List jarToSpec = new ArrayList(); + for (int i = 0; i < args.length; i++) { + if (args[i].equals("--jar_to_spec")) { + if (i + 1 < args.length) { + jarToSpec.add(args[i + 1]); + i++; + } + } + } + + new ExtractProguardConfig(jarToSpec).run(); + } +} diff --git a/settings/BUILD b/settings/BUILD index b97db1f0..dfd6f354 100644 --- a/settings/BUILD +++ b/settings/BUILD @@ -1,12 +1,21 @@ load("//settings:stamp_manifest.bzl", "stamp_manifest") +load("//settings:extract_proguard_config.bzl", "extract_proguard_config") package( default_visibility = ["//visibility:public"], ) -exports_files(["stamp_manifest.bzl"]) +exports_files([ + "extract_proguard_config.bzl", + "stamp_manifest.bzl", +]) stamp_manifest( name = "stamp_manifest", build_setting_default = True, ) + +extract_proguard_config( + name = "extract_proguard_config", + build_setting_default = False, +) diff --git a/settings/extract_proguard_config.bzl b/settings/extract_proguard_config.bzl new file mode 100644 index 00000000..7d55ff6e --- /dev/null +++ b/settings/extract_proguard_config.bzl @@ -0,0 +1,9 @@ +ExtractProguardConfigProvider = provider(fields = ["extract_proguard_config"]) + +def _impl(ctx): + return ExtractProguardConfigProvider(extract_proguard_config = ctx.build_setting_value) + +extract_proguard_config = rule( + implementation = _impl, + build_setting = config.bool(flag = True), +)