Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .bazelrc
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@ common --java_runtime_version=remotejdk_17
common --tool_java_language_version=17
common --tool_java_runtime_version=remotejdk_17

# Required by hermetic_android_toolchains for the pinned SDK version.
common --repo_env=ACCEPTED_ANDROID_SDK_LICENSE_VERSION=34

try-import %workspace%/.bazelrc.user
33 changes: 31 additions & 2 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ module(
compatibility_level = 1,
)

bazel_dep(name = "rules_android", version = "0.7.1")
bazel_dep(name = "hermetic_android_toolchains", version = "0.1.1", dev_dependency = True)

bazel_dep(name = "rules_android", version = "0.7.3")
bazel_dep(name = "rules_java", version = "9.3.0")
bazel_dep(name = "rules_jvm_external", version = "6.9")
bazel_dep(name = "rules_kotlin", version = "2.2.2")
Expand All @@ -31,7 +33,34 @@ bazel_binaries = use_extension(
bazel_binaries.download(version_file = "//:.bazelversion")
use_repo(bazel_binaries, "bazel_binaries", "bazel_binaries_bazelisk", "build_bazel_bazel_.bazelversion")

register_toolchains("//toolchains:android_lint_default_toolchain")
android = use_extension(
"@hermetic_android_toolchains//:extensions.bzl",
"android",
dev_dependency = True,
)
android.sdk(
build_tools_version = "35.0.0",
version = "34",
)
android.ndk(version = "r25c")
use_repo(android, "androidsdk")

rules_android_sdk = use_extension(
"@rules_android//rules/android_sdk_repository:rule.bzl",
"android_sdk_repository_extension",
)

override_repo(rules_android_sdk, "androidsdk")

register_toolchains(
"@androidsdk//:all",
dev_dependency = True,
)

register_toolchains(
"//toolchains:android_lint_default_toolchain",
dev_dependency = True,
)

# TODO(bencodes) Lint needs to be downloaded dynamically
maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@ common --java_runtime_version=remotejdk_17
common --tool_java_runtime_version=remotejdk_17
```

The default lint toolchain uses `hermetic_android_toolchains` to provide the Android SDK
through Bazel. Accept the pinned SDK license in your `.bazelrc`:

```
common --repo_env=ACCEPTED_ANDROID_SDK_LICENSE_VERSION=34
```

### Using a different Android Lint version

The lint binary is supplied by the toolchain. To pin your own version, build a deploy jar
Expand Down
3 changes: 3 additions & 0 deletions examples/simple-android/.bazelrc
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ common --java_language_version=17
common --java_runtime_version=remotejdk_17
common --tool_java_language_version=17
common --tool_java_runtime_version=remotejdk_17

# Required by hermetic_android_toolchains for the pinned SDK version.
common --repo_env=ACCEPTED_ANDROID_SDK_LICENSE_VERSION=34
1 change: 1 addition & 0 deletions examples/simple-android/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ android_lint_test(

android_lint_toolchain(
name = "android_lint_default",
android_home = "@androidsdk//:files",
android_lint_config = "lint_global.xml",
visibility = ["//visibility:public"],
)
Expand Down
18 changes: 11 additions & 7 deletions examples/simple-android/MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,24 @@ local_path_override(
path = "../../",
)

bazel_dep(name = "hermetic_android_toolchains", version = "0.1.1")
bazel_dep(name = "platforms", version = "1.1.0")
bazel_dep(name = "rules_android", version = "0.7.3")
bazel_dep(name = "rules_jvm_external", version = "7.0")

remote_android_extensions = use_extension(
"@rules_android//bzlmod_extensions:android_extensions.bzl",
"remote_android_tools_extensions",
android = use_extension("@hermetic_android_toolchains//:extensions.bzl", "android")
android.sdk(
build_tools_version = "35.0.0",
version = "34",
)
use_repo(remote_android_extensions, "android_tools")
android.ndk(version = "r25c")
use_repo(android, "androidsdk")

android_sdk_repository_extension = use_extension("@rules_android//rules/android_sdk_repository:rule.bzl", "android_sdk_repository_extension")
use_repo(android_sdk_repository_extension, "androidsdk")
rules_android_sdk = use_extension("@rules_android//rules/android_sdk_repository:rule.bzl", "android_sdk_repository_extension")

register_toolchains("@androidsdk//:sdk-toolchain", "@androidsdk//:all")
override_repo(rules_android_sdk, "androidsdk")

register_toolchains("@androidsdk//:all")

maven = use_extension("@rules_jvm_external//:extensions.bzl", "maven")
maven.install(
Expand Down
8 changes: 6 additions & 2 deletions src/cli/AndroidLintRunner.kt
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,13 @@ internal class AndroidLintRunner(
// Run Android Lint
val androidCacheFolder = workingDirectory.resolve("android-cache")
Files.createDirectory(androidCacheFolder)
val lintUserHomeFolder = workingDirectory.resolve("lint-user-home")
Files.createDirectory(lintUserHomeFolder)

val invoker = invokerCache.acquire(listOf(args.androidLintCliTool))
val exitCode =
try {
System.setProperty("user.home", lintUserHomeFolder.toString())
invokeAndroidLintCLI(
invoker = invoker,
actionArgs = args,
Expand Down Expand Up @@ -128,8 +132,8 @@ internal class AndroidLintRunner(
args.add(actionArgs.disableChecks.joinToString(","))
}

if (actionArgs.androidHome?.isNotEmpty() != null) {
var androidHomePath =
if (actionArgs.androidHome?.isNotEmpty() == true) {
val androidHomePath =
Paths.get(System.getenv("PWD"), actionArgs.androidHome).absolutePathString()
args.add("--sdk-home")
args.add(androidHomePath)
Expand Down
1 change: 0 additions & 1 deletion tests/scripts/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ script_test(
name = "android_lint_aar_scenarios_test",
size = "enormous",
srcs = ["android_lint_aar_scenarios_test.sh"],
additional_env_inherit = ["ANDROID_HOME"],
bazel_binaries = bazel_binaries,
bazel_version = bazel_binaries.versions.current,
tags = ["exclusive"],
Expand Down
2 changes: 1 addition & 1 deletion tests/scripts/android_lint_aar_scenarios_test.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/bash
#
# Scenarios for custom lint checks embedded in AAR dependencies (lint.jar), exercising the
# collect_aar_outputs_aspect extraction and auto-discovery path. Requires ANDROID_HOME.
# collect_aar_outputs_aspect extraction and auto-discovery path.

# --- begin runfiles.bash initialization v2 ---
# Copy-pasted from the Bazel Bash runfiles library v2.
Expand Down
85 changes: 62 additions & 23 deletions tests/scripts/lint_helper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,33 @@ function set_up_lint_workspace() {
cat > "${dest}/MODULE.bazel" <<EOF
module(name = "rules_android_lint")

bazel_dep(name = "rules_android", version = "0.7.1")
bazel_dep(name = "hermetic_android_toolchains", version = "0.1.1", dev_dependency = True)
bazel_dep(name = "rules_android", version = "0.7.3")
bazel_dep(name = "rules_java", version = "9.3.0")
bazel_dep(name = "rules_python", version = "1.7.0")
bazel_dep(name = "bazel_skylib", version = "1.9.0")
bazel_dep(name = "platforms", version = "1.0.0")

register_toolchains("//toolchains:default_toolchain")
android = use_extension(
"@hermetic_android_toolchains//:extensions.bzl",
"android",
dev_dependency = True,
)
android.sdk(
build_tools_version = "35.0.0",
version = "34",
)
android.ndk(version = "r25c")
use_repo(android, "androidsdk")

rules_android_sdk = use_extension(
"@rules_android//rules/android_sdk_repository:rule.bzl",
"android_sdk_repository_extension",
)
override_repo(rules_android_sdk, "androidsdk")

register_toolchains("@androidsdk//:all", dev_dependency = True)
register_toolchains("//toolchains:default_toolchain", dev_dependency = True)
EOF

cat > "${dest}/src/BUILD.bazel" <<EOF
Expand Down Expand Up @@ -78,6 +98,7 @@ toolchain_type(

android_lint_toolchain(
name = "default_toolchain_impl",
android_home = "@androidsdk//:files",
android_lint = "//third_party:com_android_tools_lint_lint_deploy.jar",
)

Expand All @@ -91,10 +112,46 @@ EOF
# The consumer workspace, in the CWD.
cat > MODULE.bazel <<EOF
bazel_dep(name = "rules_android_lint")
bazel_dep(name = "hermetic_android_toolchains", version = "0.1.1")
bazel_dep(name = "rules_android", version = "0.7.3")

local_path_override(
module_name = "rules_android_lint",
path = "${dest}",
)

android = use_extension("@hermetic_android_toolchains//:extensions.bzl", "android")
android.sdk(
build_tools_version = "35.0.0",
version = "34",
)
android.ndk(version = "r25c")
use_repo(android, "androidsdk")

rules_android_sdk = use_extension(
"@rules_android//rules/android_sdk_repository:rule.bzl",
"android_sdk_repository_extension",
)
override_repo(rules_android_sdk, "androidsdk")

register_toolchains("@androidsdk//:all")
register_toolchains("//toolchains:default_toolchain")
EOF

mkdir -p toolchains
cat > toolchains/BUILD.bazel <<EOF
load("@rules_android_lint//toolchains:toolchain.bzl", "android_lint_toolchain")

android_lint_toolchain(
name = "default_toolchain_impl",
android_home = "@androidsdk//:files",
)

toolchain(
name = "default_toolchain",
toolchain = ":default_toolchain_impl",
toolchain_type = "@rules_android_lint//toolchains:toolchain_type",
)
EOF

cat > .bazelrc <<EOF
Expand All @@ -104,6 +161,8 @@ common --java_language_version=17
common --java_runtime_version=remotejdk_17
common --tool_java_language_version=17
common --tool_java_runtime_version=remotejdk_17
# Required by hermetic_android_toolchains for the pinned SDK version.
common --repo_env=ACCEPTED_ANDROID_SDK_LICENSE_VERSION=34
# Shared across the test functions in one script_test run.
common --repository_cache=${TEST_TMPDIR}/repository_cache
EOF
Expand Down Expand Up @@ -238,28 +297,8 @@ fun GoodButton(modifier: Modifier = Modifier) {
EOF
}

# Extends the consumer workspace with rules_android and an SDK so aar_import works.
# Requires ANDROID_HOME in the environment.
# Enables rules_android's legacy aar_import API in the consumer workspace.
function enable_android_in_workspace() {
cat >> MODULE.bazel <<EOF

bazel_dep(name = "rules_android", version = "0.7.1")

remote_android_extensions = use_extension(
"@rules_android//bzlmod_extensions:android_extensions.bzl",
"remote_android_tools_extensions",
)
use_repo(remote_android_extensions, "android_tools")

android_sdk_repository_extension = use_extension(
"@rules_android//rules/android_sdk_repository:rule.bzl",
"android_sdk_repository_extension",
)
use_repo(android_sdk_repository_extension, "androidsdk")

register_toolchains("@androidsdk//:sdk-toolchain", "@androidsdk//:all")
EOF

cat >> .bazelrc <<EOF
common --experimental_google_legacy_api
EOF
Expand Down
3 changes: 3 additions & 0 deletions tests/src/cli/AndroidLintActionArgsTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ class AndroidLintActionArgsTest {
"test",
"--android-lint-cli-tool",
"path/to/cli.jar",
"--android-home",
"external/androidsdk",
"--src",
"path/to/Foo.kt",
"--output",
Expand Down Expand Up @@ -54,6 +56,7 @@ class AndroidLintActionArgsTest {
)

assertThat(parseArgs.label).isEqualTo("test")
assertThat(parseArgs.androidHome).isEqualTo("external/androidsdk")
assertThat(parseArgs.srcs).containsExactly(Paths.get("path/to/Foo.kt"))
assertThat(parseArgs.output).isEqualTo(Paths.get("output.jar"))
assertThat(parseArgs.resources).containsExactly(Paths.get("path/to/resource/strings.xml"))
Expand Down
60 changes: 59 additions & 1 deletion tests/src/cli/AndroidLintActionTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
import java.util.jar.JarEntry
import java.util.jar.JarOutputStream

Expand Down Expand Up @@ -58,9 +59,55 @@ class AndroidLintActionTest {
assertThat(cache.createdCount).isEqualTo(1)
}

@Test
fun `lint runs with an invocation-scoped user home`() {
val cache = AndroidLintCliInvokerCache()
val executor = AndroidLintAction.AndroidLintExecutor(cache)
val jar = writeStubLintJar()

val exitCode = executor.processWorkRequest(workRequestArgs(jar, "user-home"), System.err)

assertThat(exitCode).isEqualTo(0)
assertThat(Main.recordedRuns).hasSize(1)
assertThat(Main.recordedRuns.single().userHome)
.contains("lint-user-home")
}

@Test
fun `lint receives sdk home from android home`() {
val cache = AndroidLintCliInvokerCache()
val executor = AndroidLintAction.AndroidLintExecutor(cache)
val jar = writeStubLintJar()

val exitCode =
executor.processWorkRequest(
workRequestArgs(
lintJar = jar,
label = "android-home",
extraArgs =
listOf(
"--android-home",
"external/androidsdk",
),
),
System.err,
)

val rootDir = Paths.get(System.getenv("PWD")).toAbsolutePath().normalize()
val sdkHome =
Main.recordedRuns
.single()
.args
.argumentAfter("--sdk-home")

assertThat(exitCode).isEqualTo(0)
assertThat(sdkHome).isEqualTo(rootDir.resolve("external/androidsdk").toString())
}

private fun workRequestArgs(
lintJar: Path,
label: String,
extraArgs: List<String> = emptyList(),
): List<String> {
val output = temporaryFolder.root.toPath().resolve("$label-output.xml")
return listOf(
Expand All @@ -76,7 +123,7 @@ class AndroidLintActionTest {
"17",
"--kotlin-language-level",
"1.9",
)
) + extraArgs
}

private fun writeStubLintJar(): Path {
Expand All @@ -89,3 +136,14 @@ class AndroidLintActionTest {
return jar
}
}

private fun List<String>.argumentAfter(argument: String): String {
val argumentIndex = indexOf(argument)
assertThat(argumentIndex)
.describedAs("argument index for %s in %s", argument, this)
.isGreaterThanOrEqualTo(0)
assertThat(argumentIndex + 1)
.describedAs("value index for %s in %s", argument, this)
.isLessThan(size)
return this[argumentIndex + 1]
}
Loading
Loading