Skip to content

Commit c71e99d

Browse files
brettchabotcopybara-androidxtest
authored andcommitted
Replace use of @bazel_tools//tools/jdk:jar with custom utility.
bazel 7.5 no longer exposes direct access to the 'jar' executable. Use of it was suboptimal anyway, since its use is cryptic and by default can produce a jar that contains files with timestamps, hampering build reproducibility. Instead, this commit creates a simple standalone java_binary that can create a jar. Also remove unused maven_artifact:override_license_file PiperOrigin-RevId: 730642408
1 parent 3f0144a commit c71e99d

File tree

8 files changed

+195
-52
lines changed

8 files changed

+195
-52
lines changed

build_extensions/axt_android_local_test.bzl

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,7 @@
11
"""A rule wrapper for generating android_local_test ."""
22

33
load("@io_bazel_rules_kotlin//kotlin:android.bzl", "kt_android_library")
4-
5-
_CONFIG_JAR_COMMAND = """
6-
set -e
7-
JAR="$(location @bazel_tools//tools/jdk:jar)"
8-
SRC="$<"
9-
[[ "$$(basename "$${SRC}")" = 'robolectric.properties' ]] || {
10-
echo 'Must be named: robolectric.properties';
11-
exit 1;
12-
}
13-
$${JAR} -cf "$@" -C "$$(dirname "$${SRC}")" "$$(basename "$${SRC}")"
14-
"""
4+
load("//build_extensions:create_jar.bzl", "create_jar")
155

166
def axt_android_local_test(name, srcs = [], deps = [], manifest = "//build_extensions:AndroidManifest_robolectric.xml", tags = ["robolectric"], **kwargs):
177
"""A wrapper around android_local_test that provides sensible defaults for androidx.test.
@@ -72,21 +62,14 @@ def _robolectric_config(name, src):
7262
name: a string, the name of the rule
7363
src: a label, the properties file to package
7464
"""
75-
native.genrule(
65+
create_jar(
7666
name = name + "_gen",
7767
srcs = [src],
78-
outs = ["%s.jar" % name],
79-
message = "Generating Robolectric config...",
80-
cmd = _CONFIG_JAR_COMMAND,
81-
tools = [
82-
"@bazel_tools//tools/jdk:jar",
83-
],
84-
visibility = ["//visibility:private"],
8568
)
8669
native.java_import(
8770
name = name,
8871
constraints = ["android"],
89-
jars = [name + "_gen"],
72+
jars = [name + "_gen.jar"],
9073
)
9174

9275
def _is_kotlin(srcs):

build_extensions/create_jar.bzl

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
"""Build rule to create a single jar from given files of any type."""
2+
3+
def _create_jar_impl(ctx):
4+
"""
5+
Construct a single jar from given files of any type.
6+
"""
7+
8+
input_paths = []
9+
for target in ctx.attr.srcs:
10+
input_paths.extend(target.files.to_list())
11+
12+
args = ctx.actions.args()
13+
args.add(ctx.outputs.output)
14+
args.add_all(input_paths)
15+
16+
ctx.actions.run(
17+
inputs = input_paths,
18+
outputs = [ctx.outputs.output],
19+
executable = ctx.executable._create_jar_java,
20+
arguments = [args],
21+
mnemonic = "CreateJAR",
22+
)
23+
24+
create_jar = rule(
25+
attrs = {
26+
"srcs": attr.label_list(allow_files = True),
27+
"_create_jar_java": attr.label(
28+
executable = True,
29+
cfg = "exec",
30+
allow_files = True,
31+
default = Label("//build_extensions/jar_creator/java/androidx/test/tools/jarcreator:jarcreator"),
32+
),
33+
},
34+
outputs = {
35+
"output": "%{name}.jar",
36+
},
37+
implementation = _create_jar_impl,
38+
)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
load("@io_bazel_rules_kotlin//kotlin:jvm.bzl", "kt_jvm_library")
2+
3+
package(
4+
default_visibility = [
5+
"//:__subpackages__",
6+
],
7+
)
8+
9+
kt_jvm_library(
10+
name = "jarcreator_lib",
11+
srcs = glob([
12+
"*.kt",
13+
]),
14+
)
15+
16+
java_binary(
17+
name = "jarcreator",
18+
srcs = ["Main.java"],
19+
main_class = "androidx.test.tools.jarcreator.Main",
20+
deps = [
21+
":jarcreator_lib",
22+
],
23+
)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright 2025 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package androidx.test.tools.jarcreator
17+
18+
import java.io.BufferedInputStream
19+
import java.io.File
20+
import java.io.FileInputStream
21+
import java.io.FileOutputStream
22+
import java.io.InputStream
23+
import java.util.jar.JarEntry
24+
import java.util.jar.JarOutputStream
25+
26+
/**
27+
* This is a simple utility that creates a jar file of input files.
28+
*
29+
* Unlike other solutions like invoking jar command directly or using bazel's
30+
* java_common.pack_sources, this will create a jar without timestamp and with files in the root
31+
* directory of the jar.
32+
*/
33+
fun createJar(args: Array<String>) {
34+
require(args.size >= 2) { "Must provide at least two files: <output> <input...>" }
35+
36+
val outputFile = args[0]
37+
val jarOutputStream = JarOutputStream(FileOutputStream(outputFile, false))
38+
jarOutputStream.use {
39+
for (i in 1 until args.size) {
40+
val inputFile = File(args[i])
41+
val inputStream = BufferedInputStream(FileInputStream(inputFile))
42+
inputStream.use { addToJar(jarOutputStream, inputStream, inputFile) }
43+
}
44+
}
45+
}
46+
47+
private fun addToJar(jarOutputStream: JarOutputStream, inputStream: InputStream, inputFile: File) {
48+
val entry = JarEntry(inputFile.name)
49+
jarOutputStream.putNextEntry(entry)
50+
inputStream.transferTo(jarOutputStream)
51+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright 2025 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package androidx.test.tools.jarcreator;
17+
18+
public class Main {
19+
private Main() {}
20+
21+
public static void main(String[] args) {
22+
JarCreatorKt.createJar(args);
23+
}
24+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
load("@io_bazel_rules_kotlin//kotlin:jvm.bzl", "kt_jvm_test")
2+
3+
kt_jvm_test(
4+
name = "JarCreatorTest",
5+
srcs = ["JarCreatorTest.kt"],
6+
deps = [
7+
"//build_extensions/jar_creator/java/androidx/test/tools/jarcreator:jarcreator_lib",
8+
"@maven//:com_google_truth_truth",
9+
"@maven//:junit_junit",
10+
],
11+
)
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (C) 2023 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package androidx.test.tools.jarcreator
18+
19+
import com.google.common.truth.Truth.assertThat
20+
import java.io.File
21+
import java.util.jar.JarFile
22+
import org.junit.Assert.assertThrows
23+
import org.junit.Test
24+
import org.junit.runner.RunWith
25+
import org.junit.runners.JUnit4
26+
27+
@RunWith(JUnit4::class)
28+
class JarCreatorTest {
29+
30+
@Test
31+
fun invalidInput() {
32+
assertThrows(java.lang.IllegalArgumentException::class.java) { createJar(emptyArray()) }
33+
}
34+
35+
@Test
36+
fun combineJars() {
37+
val outFile = File.createTempFile("createJarOut", ".jar")
38+
val fileToInclude = File.createTempFile("include", ".txt")
39+
40+
createJar(arrayOf(outFile.absolutePath, fileToInclude.absolutePath))
41+
42+
val contents = JarFile(outFile.absolutePath).stream().map { it.name }
43+
assertThat(contents).containsExactly(fileToInclude.name)
44+
}
45+
}

build_extensions/maven/maven_artifact.bzl

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -179,32 +179,6 @@ def _rename_artifact(ctx, tpl_string, src_file, packaging_type, artifact_id, ver
179179
)
180180
return artifact
181181

182-
def _override_license_file(ctx, src_file):
183-
"""Append a LICENSE file into the src if exists"""
184-
artifact_with_license = ctx.actions.declare_file("%s-with-LICENSE.%s" % (src_file.basename, src_file.extension))
185-
commands = []
186-
commands.append("cp %s %s" % (src_file.path, artifact_with_license.path))
187-
188-
# Append a given LICENSE file to the root directory.
189-
commands.append("%s -uf %s -C %s %s" % (
190-
ctx.executable._jar.path,
191-
artifact_with_license.path,
192-
ctx.file.license_file.dirname,
193-
ctx.file.license_file.basename,
194-
))
195-
196-
# Remove the LICENSE files in META_INF.
197-
# (Ignore zip error: Nothing to do)
198-
commands.append("(zip -dq %s META-INF/LICENSE META-INF/LICENSE.txt || true)" % (artifact_with_license.path))
199-
200-
ctx.actions.run_shell(
201-
inputs = [src_file, ctx.file.license_file, ctx.executable._jar],
202-
outputs = [artifact_with_license],
203-
command = "&&".join(commands),
204-
tools = [ctx.executable._jar],
205-
)
206-
return artifact_with_license
207-
208182
def _maven_artifact_impl(ctx):
209183
"""Generates maven repository for a single artifact."""
210184

@@ -277,12 +251,6 @@ maven_artifact = rule(
277251
mandatory = False,
278252
doc = "Map of maven dependency to a csv list of excluded dependencies. eg {\"com.google.foo:foo\":\"com.google.bar:bar,com.google.bar:bar-none\"}",
279253
),
280-
"_jar": attr.label(
281-
default = Label("@bazel_tools//tools/jdk:jar"),
282-
executable = True,
283-
allow_files = True,
284-
cfg = "exec",
285-
),
286254
"_maven_artifact_sh": attr.label(
287255
default = Label("//build_extensions/maven:maven_artifact_sh"),
288256
executable = True,

0 commit comments

Comments
 (0)