1414
1515"""A repository rule for integrating the Android NDK."""
1616
17- def _android_ndk_repository_impl (ctx ):
17+ DEFAULT_API_LEVEL = 31
18+
19+ _EXEC_CONSTRAINTS = {
20+ "darwin-arm64" : [
21+ "@platforms//os:macos" ,
22+ "@platforms//cpu:aarch64" ,
23+ ],
24+ "darwin-x86_64" : [
25+ "@platforms//os:macos" ,
26+ "@platforms//cpu:x86_64" ,
27+ ],
28+ "linux-x86_64" : [
29+ "@platforms//os:linux" ,
30+ "@platforms//cpu:x86_64" ,
31+ ],
32+ "windows-x86_64" : [
33+ "@platforms//os:windows" ,
34+ "@platforms//cpu:x86_64" ,
35+ ],
36+ }
37+
38+ def _get_clang_resource_dir (ctx , clang_directory , is_windows ):
39+ clang_resource_dir = getattr (ctx .attr , "clang_resource_dir" , None )
40+ if clang_resource_dir :
41+ return clang_resource_dir
42+
43+ result = ctx .execute ([clang_directory + "/bin/clang" , "--print-resource-dir" ])
44+ if result .return_code != 0 :
45+ fail ("Failed to execute clang: %s" % result .stderr )
46+ stdout = result .stdout .strip ()
47+ if is_windows :
48+ stdout = stdout .replace ("\\ " , "/" )
49+ return stdout .split (clang_directory )[1 ].strip ("/" )
50+
51+ def _android_ndk_repository_impl (ctx , ndk_path = None ):
1852 """Install the Android NDK files.
1953
2054 Args:
2155 ctx: An implementation context.
56+ ndk_path: The path to the ndk
2257
2358 Returns:
2459 A final dict of configuration attributes and values.
2560 """
26- ndk_path = ctx .attr .path or ctx .getenv ("ANDROID_NDK_HOME" , None )
27- if not ndk_path :
28- fail ("Either the ANDROID_NDK_HOME environment variable or the " +
29- "path attribute of android_ndk_repository must be set." )
61+ if ndk_path == None :
62+ ndk_path = ctx .path (Label (ctx .attr .anchor )).dirname
63+
3064 if ndk_path .startswith ("$WORKSPACE_ROOT" ):
3165 ndk_path = str (ctx .workspace_root ) + ndk_path .removeprefix ("$WORKSPACE_ROOT" )
3266
3367 is_windows = False
3468 executable_extension = ""
35- if ctx .os .name == "linux" :
69+ exec_compatible_with = None
70+ platform = ctx .os .name
71+ if hasattr (ctx .attr , "platform" ):
72+ platform = ctx .attr .platform
73+ exec_compatible_with = _EXEC_CONSTRAINTS [platform ]
74+
75+ if platform .startswith ("linux" ):
3676 clang_directory = "toolchains/llvm/prebuilt/linux-x86_64"
37- elif ctx . os . name == "mac os x" :
77+ elif platform . startswith (( "mac" , "darwin" )) :
3878 # Note: darwin-x86_64 does indeed contain fat binaries with arm64 slices, too.
3979 clang_directory = "toolchains/llvm/prebuilt/darwin-x86_64"
40- elif ctx . os . name .startswith ("windows" ):
80+ elif platform .startswith ("windows" ):
4181 clang_directory = "toolchains/llvm/prebuilt/windows-x86_64"
4282 is_windows = True
4383 executable_extension = ".exe"
@@ -48,15 +88,9 @@ def _android_ndk_repository_impl(ctx):
4888
4989 _create_symlinks (ctx , ndk_path , clang_directory , sysroot_directory )
5090
51- api_level = ctx .attr .api_level or 31
91+ api_level = ctx .attr .api_level or DEFAULT_API_LEVEL
5292
53- result = ctx .execute ([clang_directory + "/bin/clang" , "--print-resource-dir" ])
54- if result .return_code != 0 :
55- fail ("Failed to execute clang: %s" % result .stderr )
56- stdout = result .stdout .strip ()
57- if is_windows :
58- stdout = stdout .replace ("\\ " , "/" )
59- clang_resource_directory = stdout .split (clang_directory )[1 ].strip ("/" )
93+ clang_resource_directory = _get_clang_resource_dir (ctx , clang_directory , is_windows )
6094
6195 # Use a label relative to the workspace from which this repository rule came
6296 # to get the workspace name.
@@ -67,6 +101,7 @@ def _android_ndk_repository_impl(ctx):
67101 ctx .attr ._template_ndk_root ,
68102 {
69103 "{clang_directory}" : clang_directory ,
104+ "{exec_compatible_with}" : repr (exec_compatible_with ),
70105 },
71106 executable = False ,
72107 )
@@ -83,11 +118,11 @@ def _android_ndk_repository_impl(ctx):
83118 "%s/BUILD.bazel" % clang_directory ,
84119 ctx .attr ._template_ndk_clang ,
85120 {
86- "{repository_name}" : repository_name ,
87121 "{api_level}" : str (api_level ),
88122 "{clang_resource_directory}" : clang_resource_directory ,
89- "{sysroot_directory}" : sysroot_directory ,
90123 "{executable_extension}" : executable_extension ,
124+ "{repository_name}" : repository_name ,
125+ "{sysroot_directory}" : sysroot_directory ,
91126 },
92127 executable = False ,
93128 )
@@ -124,16 +159,68 @@ def _create_symlinks(ctx, ndk_path, clang_directory, sysroot_directory):
124159 # TODO(#32): Remove this hack
125160 ctx .symlink (ndk_path + "sources" , "ndk/sources" )
126161
162+ _COMMON_ATTR = {
163+ "api_level" : attr .int (
164+ doc = "The minimum Android API level to target." ,
165+ default = DEFAULT_API_LEVEL ,
166+ ),
167+ "_build" : attr .label (
168+ default = Label ("//:BUILD" ),
169+ allow_single_file = True ,
170+ ),
171+ "_template_ndk_clang" : attr .label (
172+ default = Label ("//:BUILD.ndk_clang.tpl" ),
173+ allow_single_file = True ,
174+ ),
175+ "_template_ndk_root" : attr .label (
176+ default = Label ("//:BUILD.ndk_root.tpl" ),
177+ allow_single_file = True ,
178+ ),
179+ "_template_ndk_sysroot" : attr .label (
180+ default = Label (":BUILD.ndk_sysroot.tpl" ),
181+ allow_single_file = True ,
182+ ),
183+ "_template_target_systems" : attr .label (
184+ default = Label ("//:target_systems.bzl.tpl" ),
185+ allow_single_file = True ,
186+ ),
187+ }
188+
127189android_ndk_repository = repository_rule (
128- attrs = {
129- "path" : attr .string (),
130- "api_level" : attr .int (),
131- "_build" : attr .label (default = ":BUILD" , allow_single_file = True ),
132- "_template_ndk_root" : attr .label (default = ":BUILD.ndk_root.tpl" , allow_single_file = True ),
133- "_template_target_systems" : attr .label (default = ":target_systems.bzl.tpl" , allow_single_file = True ),
134- "_template_ndk_clang" : attr .label (default = ":BUILD.ndk_clang.tpl" , allow_single_file = True ),
135- "_template_ndk_sysroot" : attr .label (default = ":BUILD.ndk_sysroot.tpl" , allow_single_file = True ),
190+ doc = "A repository rule that integrates the Android NDK from a workspace. Uses an anchor label to locate the NDK and requires the host platform and Clang resource directory to be specified. For local NDK installations, use local_android_ndk_repository instead." ,
191+ implementation = _android_ndk_repository_impl ,
192+ attrs = _COMMON_ATTR | {
193+ "anchor" : attr .string (
194+ doc = "A label to a file in the NDK directory. The directory containing this file is used as the NDK root path." ,
195+ mandatory = True ,
196+ ),
197+ "clang_resource_dir" : attr .string (
198+ doc = "The Clang resource directory path. Pass an empty string to auto-detect by running clang --print-resource-dir." ,
199+ mandatory = True ,
200+ ),
201+ "platform" : attr .string (
202+ doc = "The execution platform for the NDK toolchain (e.g., 'linux-x86_64', 'darwin-arm64', 'windows-x86_64'). Determines which prebuilt toolchain directory is used." ,
203+ values = _EXEC_CONSTRAINTS .keys (),
204+ mandatory = True ,
205+ ),
206+ },
207+ )
208+
209+ def _local_android_ndk_repository_impl (ctx ):
210+ ndk_path = ctx .attr .path or ctx .getenv ("ANDROID_NDK_HOME" , None )
211+ if not ndk_path :
212+ fail ("Either the ANDROID_NDK_HOME environment variable or the " +
213+ "path attribute of android_ndk_repository must be set." )
214+
215+ return _android_ndk_repository_impl (ctx , ndk_path )
216+
217+ local_android_ndk_repository = repository_rule (
218+ doc = "A repository rule that integrates the Android NDK from a local path. Uses ANDROID_NDK_HOME environment variable or the path attribute. This is the rule used by the bzlmod extension." ,
219+ implementation = _local_android_ndk_repository_impl ,
220+ attrs = _COMMON_ATTR | {
221+ "path" : attr .string (
222+ doc = "The path to the local Android NDK installation. If not set, ANDROID_NDK_HOME environment variable is used. May start with $WORKSPACE_ROOT to reference the workspace root." ,
223+ ),
136224 },
137225 local = True ,
138- implementation = _android_ndk_repository_impl ,
139226)
0 commit comments