Skip to content

Commit

Permalink
DO NOT SUBMIT
Browse files Browse the repository at this point in the history
Implement android_sdk_repository in Starlark.

Fixes bazelbuild#76.

PiperOrigin-RevId: 531575425
Change-Id: Ia6acb8ab9b2d4a8fd7ea3da7edcb592454d85b52
  • Loading branch information
katre committed Aug 16, 2023
1 parent a764568 commit 1d83177
Show file tree
Hide file tree
Showing 17 changed files with 3,141 additions and 6 deletions.
15 changes: 15 additions & 0 deletions MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,18 @@ use_repo(

remote_android_extensions = use_extension("@bazel_tools//tools/android:android_extensions.bzl", "remote_android_tools_extensions")
use_repo(remote_android_extensions, "android_tools", "android_gmaven_r8")

# integration test setup
bazel_dep(
name = "rules_bazel_integration_test",
version = "0.15.0",
dev_dependency = True,
)

bazel_binaries = use_extension(
"@rules_bazel_integration_test//:extensions.bzl",
"bazel_binaries",
dev_dependency = True,
)
bazel_binaries.download(version = "6.3.0")
use_repo(bazel_binaries, "bazel_binaries")
14 changes: 13 additions & 1 deletion defs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ load("@rules_java//java:repositories.bzl", "rules_java_dependencies", "rules_jav
load("@rules_jvm_external//:defs.bzl", "maven_install")
load("@rules_proto//proto:repositories.bzl", "rules_proto_dependencies", "rules_proto_toolchains")
load("@rules_python//python:repositories.bzl", "py_repositories")
load("@rules_bazel_integration_test//bazel_integration_test:defs.bzl", "bazel_binaries")
load("@cgrindel_bazel_starlib//:deps.bzl", "bazel_starlib_dependencies")

def rules_android_workspace():
""" Sets up workspace dependencies for rules_android."""
Expand Down Expand Up @@ -83,4 +85,14 @@ def rules_android_workspace():
rules_proto_dependencies()
rules_proto_toolchains()

py_repositories()
py_repositories()

# Integration test setup
bazel_starlib_dependencies()

bazel_binaries(
current = "6.3.0",
versions = [
"6.3.0",
],
)
17 changes: 17 additions & 0 deletions prereqs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -134,3 +134,20 @@ def rules_android_prereqs():
sha256 = "84aec9e21cc56fbc7f1335035a71c850d1b9b5cc6ff497306f84cced9a769841",
)

maybe(
http_archive,
name = "rules_bazel_integration_test",
sha256 = "3d4a10fe1b4f87327833184c733e4d2400edde0757343254426799171b281d9a",
urls = [
"https://github.com/bazel-contrib/rules_bazel_integration_test/releases/download/v0.15.0/rules_bazel_integration_test.v0.15.0.tar.gz",
],
)

maybe(
http_archive,
name = "cgrindel_bazel_starlib",
sha256 = "ee0033d029b5eaddc21836b2944cf37c95eb5f214eb39834136a316dbc252a73",
urls = [
"https://github.com/cgrindel/bazel-starlib/releases/download/v0.16.0/bazel-starlib.v0.16.0.tar.gz",
],
)
14 changes: 14 additions & 0 deletions rules/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,20 @@ bzl_library(
visibility = ["//:__subpackages__"],
)

bzl_library(
name = "android_sdk_repository_bzl",
srcs = [
"android_sdk_repository.bzl",
"android_sdk_repository.empty.template.bzl",
"android_sdk_repository.template.bzl",
"android_sdk_repository_helper.bzl",
],
visibility = ["//:__subpackages__"],
deps = [
":android_revision_bzl",
],
)

bzl_library(
name = "common_bzl",
srcs = [
Expand Down
184 changes: 179 additions & 5 deletions rules/android_sdk_repository.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,186 @@

"""Bazel rule for Android sdk repository."""

def android_sdk_repository(**attrs):
"""Bazel android_sdk_repository rule.
load("android_revision.bzl", "compare_android_revisions", "parse_android_revision")

https://docs.bazel.build/versions/master/be/android.html#android_sdk_repository
_SDK_REPO_TEMPLATE = Label("//rules:android_sdk_repository.template.bzl")
_EMPTY_SDK_REPO_TEMPLATE = Label("//rules:android_sdk_repository.empty.template.bzl")

_BUILD_TOOLS_DIR = "build-tools"
_PLATFORMS_DIR = "platforms"
_SYSTEM_IMAGES_DIR = "system-images"
_LOCAL_MAVEN_REPOS = [
"extras/android/m2repository",
"extras/google/m2repository",
"extras/m2repository",
]
_DIRS_TO_LINK = [
_BUILD_TOOLS_DIR,
"emulator",
"platform-tools",
_PLATFORMS_DIR,
_SYSTEM_IMAGES_DIR,
] + _LOCAL_MAVEN_REPOS

_MIN_BUILD_TOOLS_VERSION = parse_android_revision("30.0.0")

def _read_api_levels(repo_ctx, android_sdk_path):
platforms_dir = "%s/%s" % (android_sdk_path, _PLATFORMS_DIR)
api_levels = []
for entry in repo_ctx.execute(["ls", platforms_dir]).stdout.splitlines():
if entry.startswith("android-"):
level = int(entry[len("android-"):])
api_levels.append(level)

return api_levels

def _newest_build_tools(repo_ctx, android_sdk_path):
build_tools_dir = "%s/%s" % (android_sdk_path, _BUILD_TOOLS_DIR)
highest = None
for entry in repo_ctx.execute(["ls", build_tools_dir]).stdout.splitlines():
revision = parse_android_revision(entry)
highest = compare_android_revisions(highest, revision)
return highest

def _find_system_images(repo_ctx, android_sdk_path):
system_images_dir = "%s/%s" % (android_sdk_path, _SYSTEM_IMAGES_DIR)
system_images = []

# system image directories are typically "system-images/android-API/apis-enabled/arch"
for entry in repo_ctx.execute(["find", system_images_dir, "-mindepth", "3", "-maxdepth", "3"]).stdout.splitlines():
entry = entry[len(android_sdk_path) + 1:]
system_images.append(entry)
return system_images

# TODO(katre): Implement SDK maven file support.
# Ideally we'd either delegate to rules_jvm_external or let the user handle it.
# https://cs.opensource.google/bazel/bazel/+/master:src/main/java/com/google/devtools/build/lib/bazel/rules/android/AndroidSdkRepositoryFunction.java;drc=818c5c8693c43fe490c9f6b2c05149eb8f45cf52;l=330
#def _find_local_maven_files(
# repo_ctx,
# android_sdk_path):
# #for local_maven_dir in _LOCAL_MAVEN_REPOS:
# # full_dir = "%s/%s" % (android_sdk_path, local_maven_dir)
# # Find .pom files
# # Parse each for aar and jar files.
# return []

def _android_sdk_repository_impl(repo_ctx):
# Determine the SDK path to use, either from the attribute or the environment.
android_sdk_path = repo_ctx.attr.path
if not android_sdk_path:
android_sdk_path = repo_ctx.os.environ.get("ANDROID_HOME")
if not android_sdk_path:
# Create an empty repository that allows non-Android code to build.
repo_ctx.template("BUILD.bazel", _EMPTY_SDK_REPO_TEMPLATE)
return None

# Symlink the needed contents to this repository.
for dir_to_link in _DIRS_TO_LINK:
source = "%s/%s" % (android_sdk_path, dir_to_link)
dest = dir_to_link
repo_ctx.symlink(source, dest)

# Read list of supported SDK levels
api_levels = _read_api_levels(repo_ctx, android_sdk_path)
if len(api_levels) == 0:
fail("No Android SDK apis found in the Android SDK at %s. Please install APIs from the Android SDK Manager." % android_sdk_path)

# Determine default SDK level.
default_api_level = max(api_levels)
if repo_ctx.attr.api_level:
default_api_level = int(repo_ctx.attr.api_level)
if default_api_level not in api_levels:
fail("Android SDK api level %s was requested but it is not installed in the Android SDK at %s. The api levels found were %s. Please choose an available api level or install api level %s from the Android SDK Manager." % (
default_api_level,
android_sdk_path,
api_levels,
default_api_level,
))

# Determine build_tools directory (and version)
build_tools = None
if repo_ctx.attr.build_tools_version:
build_tools = parse_android_revision(repo_ctx.attr.build_tools_version)
else:
build_tools = _newest_build_tools(repo_ctx, android_sdk_path)

# Check validity of build_tools
if not build_tools:
fail("Unable to determine build tools version")
if compare_android_revisions(build_tools, _MIN_BUILD_TOOLS_VERSION) != build_tools:
fail("Bazel requires Android build tools version %s or newer, %s was provided" % (
_MIN_BUILD_TOOLS_VERSION.dir,
build_tools.dir,
))

# Determine system image dirs
system_images = _find_system_images(repo_ctx, android_sdk_path)

# TODO(katre): Handle local maven files.
#local_maven_files = _find_local_maven_files(repo_ctx, android_sdk_path)
#print("found %d local maven files" % len(local_maven_files))

# Write the build file.
repo_ctx.symlink(Label(":android_sdk_repository_helper.bzl"), "android_sdk_repository_helper.bzl")
repo_ctx.template(
"BUILD.bazel",
_SDK_REPO_TEMPLATE,
substitutions = {
"__repository_name__": repo_ctx.name,
"__build_tools_version__": build_tools.version,
"__build_tools_directory__": build_tools.dir,
"__api_levels__": ",".join([str(level) for level in api_levels]),
"__default_api_level__": str(default_api_level),
"__system_image_dirs__": "\n".join(["'%s'," % d for d in system_images]),
# TODO(katre): implement these.
#"__exported_files__": "",
},
)

# repo is reproducible
return None

_android_sdk_repository = repository_rule(
implementation = _android_sdk_repository_impl,
attrs = {
"api_level": attr.int(default = 0),
"build_tools_version": attr.string(),
"path": attr.string(),
},
environ = ["ANDROID_HOME"],
local = True,
)

def _bind(repo_name, bind_name, target):
native.bind(name = bind_name, actual = "@%s//%s" % (repo_name, target))

def android_sdk_repository(
name,
path = "",
api_level = 0,
build_tools_version = ""):
"""Create a repository with Android SDK bindings and toolchains.
The SDK will be located at the given path, or via the ANDROID_HOME
environment variable if the path attribute is unset.
Args:
**attrs: Rule attributes
name: The repository name.
api_level: The SDK API level to use.
build_tools_version: The build_tools in the SDK to use.
path: The path to the Android SDK.
"""
native.android_sdk_repository(**attrs)

_android_sdk_repository(
name = name,
path = path,
api_level = api_level,
build_tools_version = build_tools_version,
)

_bind(name, "android/sdk", ":sdk")
_bind(name, "android/d8_jar_import", ":d8_jar_import")
_bind(name, "android/dx_jar_import", ":dx_jar_import")
_bind(name, "android_sdk_for_testing", ":files")
_bind(name, "has_android_sdk", ":has_android_sdk")
native.register_toolchains("@%s//:all" % name)
58 changes: 58 additions & 0 deletions rules/android_sdk_repository.empty.template.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Copyright 2023 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
#
# http://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.

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

# android_sdk_repository was used without a valid Android SDK being set.
# Either the path attribute of android_sdk_repository or the ANDROID_HOME
# environment variable must be set.
# This is a minimal BUILD file to allow non-Android builds to continue.

alias(
name = "has_androidsdk",
actual = "@bazel_tools//tools/android:always_false",
)

filegroup(
name = "files",
srcs = [":error_message"],
)

filegroup(
name = "sdk",
srcs = [":error_message"],
)

filegroup(
name = "d8_jar_import",
srcs = [":error_message"],
)

filegroup(
name = "dx_jar_import",
srcs = [":error_message"],
)

genrule(
name = "invalid_android_sdk_repository_error",
outs = [
"error_message",
"error_message.jar",
],
cmd = """echo \
android_sdk_repository was used without a valid Android SDK being set. \
Either the path attribute of android_sdk_repository or the ANDROID_HOME \
environment variable must be set. ; \
exit 1 """,
)
Loading

0 comments on commit 1d83177

Please sign in to comment.