blob: df00949600c986a83b0f33d96331d5adecb7aa94 [file]
# 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.
"""Wrapper script to execute proguard and then normalize timestamps in the output jar file."""
import argparse
import datetime
import os
import subprocess
import tempfile
import zipfile
def apply_proguard(
java_executable, proguard_jar, srcs, deps, proguard_spec, output_jar
):
"""Call proguard on the given source jars with the spec.
Args:
java_executable: The execution-platform Java executable.
proguard_jar: The ProGuard deploy JAR.
srcs: The source jars to be modified.
deps: Dependency jars needed to resolve the source jars.
proguard_spec: The path to the proguard spec file describing what
modifications to make.
output_jar: The path to write the resulting modified jar file to.
Raises:
RuntimeError: When the proguard binary fails, includes the stdout and
stderr.
"""
command = [
java_executable,
"-Dlog4j.rootLogger=OFF",
"-jar",
proguard_jar,
"-injars",
srcs,
"-libraryjars",
deps,
"-outjars",
output_jar,
"@" + proguard_spec,
]
# print("Running proguard: %s" % " ".join(command))
p = subprocess.run(command, capture_output=True, check=False)
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 RuntimeError(message)
def reset_timestamps(input_jar, output_jar, timestamp):
"""Rewrite the given jar file to reset all timestamps to a known value.
Args:
input_jar: The jar file to be modified.
output_jar: The path to write the destination jar to.
timestamp: The known timestamp to modify the output_jar with.
"""
# print("Resetting timestamps in %s to %s, writing to %s" % (input,
# timestamp, output))
with zipfile.ZipFile(input_jar, mode="r") as src:
with zipfile.ZipFile(output_jar, 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(
"--java_executable", required=True, help="Execution-platform Java."
)
parser.add_argument(
"--proguard_jar", required=True, help="ProGuard deploy JAR."
)
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.java_executable,
opts.proguard_jar,
opts.srcs,
opts.deps,
opts.proguard_spec,
output_jar,
)
reset_timestamps(output_jar, opts.output, opts.timestamp)
if __name__ == "__main__":
main()