Skip to content
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
48 changes: 9 additions & 39 deletions third_party/BUILD
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
load("@rules_java//java:defs.bzl", "java_binary", "java_import", "java_library", "java_plugin")
load("@rules_java//java:defs.bzl", "java_import", "java_library", "java_plugin")
load("@rules_license//rules:license.bzl", "license")
load("//src/tools/bzlmod:utils.bzl", "get_repo_root")
load("//tools/distributions:distribution_rules.bzl", "distrib_jar_filegroup", "distrib_java_import")
load("//tools/build_defs/proguard:proguard.bzl", "proguard_jar")

package(default_visibility = ["//visibility:public"])

Expand Down Expand Up @@ -317,50 +318,19 @@ alias(
# When using new classes from this dependency, make sure to update fastutil.proguard.
java_import(
name = "fastutil",
jars = [":fastutil_stripped_jar"],
jars = [":fastutil-stripped.jar"],
)

genrule(
name = "fastutil_stripped_jar",
proguard_jar(
name = "fastutil_proguard",
srcs = [
"@maven//:it_unimi_dsi_fastutil_file",
],
out = "fastutil-stripped.jar",
proguard_spec = "fastutil.proguard",
deps = [
"@rules_java//toolchains:platformclasspath_nostrip",
],
outs = ["fastutil-stripped.jar"],
# ProGuard output is silenced below because it prints
# ...
# Caused by: java.net.UnknownHostException: bk-docker-3gmr: Temporary failure in name resolution
# ...
# when running in the Bazel sandbox, which throws off bazel_determinism_test.
cmd = """
$(location :proguard_bin) \
-injars $(execpath @maven//:it_unimi_dsi_fastutil_file) \
-outjars $@ \
-libraryjars $(execpath @rules_java//toolchains:platformclasspath_nostrip) \
@$(location //tools:fastutil.proguard) > /dev/null
# Null out the file times stored in the jar to make the output reproducible.
TMPDIR=$$(mktemp -d)
trap 'rm -rf $$TMPDIR' EXIT
unzip -q $@ -d $$TMPDIR
rm $@
find $$TMPDIR -type f -print0 | xargs -0 touch -t 198001010000.00
OUTPUT="$$(pwd)/$@"
(cd $$TMPDIR && find . -type f | LC_ALL=C sort | zip -qDX0r@ "$$OUTPUT")
""",
tools = [
":proguard_bin",
"//tools:fastutil.proguard",
],
)

java_binary(
name = "proguard_bin",
jvm_flags = [
# Prevent ProGuard from calling out to the internet through log4j.
"-Dlog4j.rootLogger=OFF",
],
main_class = "proguard.ProGuard",
runtime_deps = ["@maven//:com_guardsquare_proguard_base"],
)

java_library(
Expand Down
File renamed without changes.
2 changes: 0 additions & 2 deletions tools/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ load("@rules_shell//shell:sh_binary.bzl", "sh_binary")

package(default_visibility = ["//visibility:public"])

exports_files(["fastutil.proguard"])

filegroup(
name = "srcs",
srcs = glob(["**"]) + [
Expand Down
1 change: 1 addition & 0 deletions tools/build_defs/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ filegroup(
"//tools/build_defs/hash:srcs",
"//tools/build_defs/inspect:srcs",
"//tools/build_defs/pkg:srcs",
"//tools/build_defs/proguard:srcs",
"//tools/build_defs/repo:srcs",
],
visibility = ["//tools:__pkg__"],
Expand Down
39 changes: 39 additions & 0 deletions tools/build_defs/proguard/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
load("@rules_java//java:defs.bzl", "java_binary")
load("@rules_python//python:py_binary.bzl", "py_binary")

licenses(["notice"]) # Apache 2.0

filegroup(
name = "srcs",
srcs = glob(["**"]),
visibility = ["//tools:__subpackages__"],
)

exports_files(
["proguard.bzl"],
visibility = ["//visibility:public"],
)

# Utility used to run proguard and fix timestamps.
py_binary(
name = "wrapper_private",
srcs = ["wrapper.py"],
data = [
":proguard_bin",
],
main = "wrapper.py",
visibility = ["//visibility:private"],
deps = [
"@rules_python//python/runfiles",
],
)

java_binary(
name = "proguard_bin",
jvm_flags = [
# Prevent ProGuard from calling out to the internet through log4j.
"-Dlog4j.rootLogger=OFF",
],
main_class = "proguard.ProGuard",
runtime_deps = ["@maven//:com_guardsquare_proguard_base"],
)
37 changes: 37 additions & 0 deletions tools/build_defs/proguard/proguard.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""Apply proguard rules to a JAR file."""

def _proguard_jar_impl(ctx):
inputs = ctx.files.srcs + ctx.files.deps + [ctx.file.proguard_spec]
output = ctx.outputs.out

args = ctx.actions.args()
args.add_joined("--srcs", ctx.files.srcs, join_with = ",")
args.add_joined("--deps", ctx.files.deps, join_with = ",")
args.add("--proguard_spec", ctx.file.proguard_spec)
args.add("--output", output)
args.add("--timestamp", "1980-01-01 00:00:00")

ctx.actions.run(
inputs = inputs,
mnemonic = "ProguardJar",
outputs = [output],
executable = ctx.executable._wrapper,
arguments = [args],
)

return DefaultInfo(files = depset([output]))

proguard_jar = rule(
implementation = _proguard_jar_impl,
attrs = {
"srcs": attr.label_list(),
"deps": attr.label_list(),
"proguard_spec": attr.label(allow_single_file = True),
"out": attr.output(),
"_wrapper": attr.label(
cfg = "exec",
default = ":wrapper_private",
executable = True,
),
},
)
115 changes: 115 additions & 0 deletions tools/build_defs/proguard/wrapper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# Copyright 2026 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
#
# https://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.

import argparse
import datetime
import os
import platform
import subprocess
import tempfile
import zipfile

from python.runfiles import runfiles

PROGUARD_BIN_PATH = "_main/tools/build_defs/proguard/proguard_bin"
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The path will likely end with .exe on Windows. In any case it's better to pass this in from location expansion of $(rlocationpath ...)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed this in a dumb way, but it works.

I couldn't get the rlocationpath solution to work: any args on the py_binary seem to be swallowed up by the actual ctx.actions.args created in the action.


def lookup_binary(path):
if platform.system() == "Windows":
path = path + ".exe"
rf = runfiles.Create()
binary = rf.Rlocation(path)
if not binary:
raise Exception(f"Runfiles failed to resolve {path}")
elif not os.path.exists(binary):
raise Exception(f"Runfiles resolved {path} to {binary} but the file does not exist")
return binary

def apply_proguard(srcs, deps, proguard_spec, output_jar):

proguard_binary = lookup_binary(PROGUARD_BIN_PATH)

command = [
proguard_binary,
"-injars", srcs,
"-libraryjars", deps,
"-outjars", output_jar,
"@" + proguard_spec
]

#print("Running proguard: %s" % " ".join(command))
p = subprocess.run(command, capture_output=True)

if p.returncode != 0:
message = f"Proguard failed ({p.returncode})"
stdout = p.stdout.decode()
if stdout:
message += f"\n stdout:\n{stdout}"
stderr = p.stderr.decode()
if stderr:
message += f"\n stderr:\n{stderr}"
raise Exception(message)

def reset_timestamps(input, output, timestamp):
#print("Resetting timestamps in %s to %s, writing to %s" % (input, timestamp, output))

with zipfile.ZipFile(input, mode="r") as src:
with zipfile.ZipFile(output, mode="w") as dest:
for info in src.infolist():
#print(f"Filename: {info.filename}")
#print(f" Modified: {datetime.datetime(*info.date_time)}")
data = src.read(info)
info.date_time = timestamp.timetuple()[:6]
dest.writestr(info, data)

def main() -> None:
parser = argparse.ArgumentParser(
description="Resets timestamps in ZIP files", fromfile_prefix_chars="@"
)
parser.add_argument(
"--srcs",
required=True,
help="Input jar files, mandatory."
)
parser.add_argument(
"--deps",
default=[],
help="Library jar files, optional."
)
parser.add_argument(
"--proguard_spec",
required=True,
help="Proguard spec file, mandatory."
)
parser.add_argument(
"--output",
required=True,
help="The output file, mandatory."
)
parser.add_argument(
"--timestamp",
default = "1980-01-01 00:00:00",
type=datetime.datetime.fromisoformat,
help = "The timestamp (in ISO format) to set all files to.",
)
opts = parser.parse_args()

with tempfile.TemporaryDirectory() as wdir:
output_jar = os.path.join(wdir, "stripped.jar")
apply_proguard(opts.srcs, opts.deps, opts.proguard_spec, output_jar)
reset_timestamps(output_jar, opts.output, opts.timestamp)


if __name__ == "__main__":
main()