@@ -210,38 +210,48 @@ def make_build_python(context):
210210#
211211# If you're a member of the Python core team, and you'd like to be able to push
212212# these tags yourself, please contact Malcolm Smith or Russell Keith-Magee.
213- def unpack_deps (host , prefix_dir ):
213+ def unpack_deps (host , prefix_dir , cache_dir ):
214214 os .chdir (prefix_dir )
215215 deps_url = "https://github.com/beeware/cpython-android-source-deps/releases/download"
216216 for name_ver in ["bzip2-1.0.8-3" , "libffi-3.4.4-3" , "openssl-3.5.5-0" ,
217217 "sqlite-3.50.4-0" , "xz-5.4.6-1" , "zstd-1.5.7-2" ]:
218218 filename = f"{ name_ver } -{ host } .tar.gz"
219- download (f"{ deps_url } /{ name_ver } /{ filename } " )
220- shutil .unpack_archive (filename )
221- os .remove (filename )
219+ out_path = download (f"{ deps_url } /{ name_ver } /{ filename } " , cache_dir )
220+ shutil .unpack_archive (out_path )
222221
223222
224- def download (url , target_dir = "." ):
225- out_path = f"{ target_dir } /{ basename (url )} "
226- run (["curl" , "-Lf" , "--retry" , "5" , "--retry-all-errors" , "-o" , out_path , url ])
223+ def download (url , cache_dir ):
224+ out_path = cache_dir / basename (url )
225+ cache_dir .mkdir (parents = True , exist_ok = True )
226+ if not out_path .is_file ():
227+ run (["curl" , "-Lf" , "--retry" , "5" , "--retry-all-errors" , "-o" , out_path , url ])
228+ else :
229+ print (f"Using cached version of { basename (url )} " )
227230 return out_path
228231
229232
230- def configure_host_python (context ):
233+ def configure_host_python (context , host = None ):
234+ if host is None :
235+ host = context .host
231236 if context .clean :
232- clean (context . host )
237+ clean (host )
233238
234- host_dir = subdir (context . host , create = True )
239+ host_dir = subdir (host , create = True )
235240 prefix_dir = host_dir / "prefix"
236241 if not prefix_dir .exists ():
237242 prefix_dir .mkdir ()
238- unpack_deps (context .host , prefix_dir )
243+ cache_dir = (
244+ Path (context .cache_dir ).resolve ()
245+ if context .cache_dir
246+ else CROSS_BUILD_DIR / "downloads"
247+ )
248+ unpack_deps (host , prefix_dir , cache_dir )
239249
240250 os .chdir (host_dir )
241251 command = [
242252 # Basic cross-compiling configuration
243253 relpath (PYTHON_DIR / "configure" ),
244- f"--host={ context . host } " ,
254+ f"--host={ host } " ,
245255 f"--build={ sysconfig .get_config_var ('BUILD_GNU_TYPE' )} " ,
246256 f"--with-build-python={ build_python_path ()} " ,
247257 "--without-ensurepip" ,
@@ -257,14 +267,16 @@ def configure_host_python(context):
257267
258268 if context .args :
259269 command .extend (context .args )
260- run (command , host = context . host )
270+ run (command , host = host )
261271
262272
263- def make_host_python (context ):
273+ def make_host_python (context , host = None ):
274+ if host is None :
275+ host = context .host
264276 # The CFLAGS and LDFLAGS set in android-env include the prefix dir, so
265277 # delete any previous Python installation to prevent it being used during
266278 # the build.
267- host_dir = subdir (context . host )
279+ host_dir = subdir (host )
268280 prefix_dir = host_dir / "prefix"
269281 for pattern in ("include/python*" , "lib/libpython*" , "lib/python*" ):
270282 delete_glob (f"{ prefix_dir } /{ pattern } " )
@@ -283,20 +295,28 @@ def make_host_python(context):
283295 )
284296
285297
286- def build_all (context ):
287- steps = [configure_build_python , make_build_python , configure_host_python ,
288- make_host_python ]
289- for step in steps :
290- step (context )
298+ def build_targets (context ):
299+ if context .target in {"all" , "build" }:
300+ configure_build_python (context )
301+ make_build_python (context )
302+
303+ for host in HOSTS :
304+ if context .target in {"all" , "hosts" , host }:
305+ configure_host_python (context , host )
306+ make_host_python (context , host )
291307
292308
293309def clean (host ):
294310 delete_glob (CROSS_BUILD_DIR / host )
295311
296312
297- def clean_all (context ):
298- for host in HOSTS + ["build" ]:
299- clean (host )
313+ def clean_targets (context ):
314+ if context .target in {"all" , "build" }:
315+ clean ("build" )
316+
317+ for host in HOSTS :
318+ if context .target in {"all" , "hosts" , host }:
319+ clean (host )
300320
301321
302322def setup_ci ():
@@ -859,31 +879,85 @@ def add_parser(*args, **kwargs):
859879
860880 # Subcommands
861881 build = add_parser (
862- "build" , help = "Run configure-build, make-build, configure-host and "
863- "make-host" )
882+ "build" ,
883+ help = "Run configure and make for the selected target"
884+ )
864885 configure_build = add_parser (
865886 "configure-build" , help = "Run `configure` for the build Python" )
866- add_parser (
887+ make_build = add_parser (
867888 "make-build" , help = "Run `make` for the build Python" )
868889 configure_host = add_parser (
869890 "configure-host" , help = "Run `configure` for Android" )
870891 make_host = add_parser (
871892 "make-host" , help = "Run `make` for Android" )
872893
873- add_parser ("clean" , help = "Delete all build directories" )
894+ clean = add_parser (
895+ "clean" ,
896+ help = "Delete build directories for the selected target"
897+ )
898+
874899 add_parser ("build-testbed" , help = "Build the testbed app" )
875900 test = add_parser ("test" , help = "Run the testbed app" )
876901 package = add_parser ("package" , help = "Make a release package" )
877902 ci = add_parser ("ci" , help = "Run build, package and test" )
878903 env = add_parser ("env" , help = "Print environment variables" )
879904
880905 # Common arguments
906+ # --cross-build-dir argument
907+ for cmd in [
908+ clean ,
909+ configure_build ,
910+ make_build ,
911+ configure_host ,
912+ make_host ,
913+ build ,
914+ package ,
915+ test ,
916+ ci ,
917+ ]:
918+ cmd .add_argument (
919+ "--cross-build-dir" ,
920+ action = "store" ,
921+ default = os .environ .get ("CROSS_BUILD_DIR" ),
922+ dest = "cross_build_dir" ,
923+ type = Path ,
924+ help = (
925+ "Path to the cross-build directory "
926+ f"(default: { CROSS_BUILD_DIR } ). Can also be set "
927+ "with the CROSS_BUILD_DIR environment variable."
928+ ),
929+ )
930+
931+ # --cache-dir option
932+ for cmd in [configure_host , build , ci ]:
933+ cmd .add_argument (
934+ "--cache-dir" ,
935+ default = os .environ .get ("CACHE_DIR" ),
936+ help = "The directory to store cached downloads." ,
937+ )
938+
939+ # --clean option
881940 for subcommand in [build , configure_build , configure_host , ci ]:
882941 subcommand .add_argument (
883942 "--clean" , action = "store_true" , default = False , dest = "clean" ,
884943 help = "Delete the relevant build directories first" )
885944
886- host_commands = [build , configure_host , make_host , package , ci ]
945+ # Allow "all", "build" and "hosts" targets for some commands
946+ for subcommand in [clean , build ]:
947+ subcommand .add_argument (
948+ "target" ,
949+ nargs = "?" ,
950+ default = "all" ,
951+ choices = ["all" , "build" , "hosts" ] + HOSTS ,
952+ help = (
953+ "The host triplet (e.g., aarch64-linux-android), "
954+ "or 'build' for just the build platform, or 'hosts' for all "
955+ "host platforms, or 'all' for the build platform and all "
956+ "hosts. Defaults to 'all'"
957+ ),
958+ )
959+
960+ host_commands = [configure_host , make_host , package , ci ]
887961 if in_source_tree :
888962 host_commands .append (env )
889963 for subcommand in host_commands :
@@ -945,13 +1019,19 @@ def main():
9451019 stream .reconfigure (line_buffering = True )
9461020
9471021 context = parse_args ()
1022+
1023+ # Set the CROSS_BUILD_DIR if an argument was provided
1024+ if context .cross_build_dir :
1025+ global CROSS_BUILD_DIR
1026+ CROSS_BUILD_DIR = context .cross_build_dir .resolve ()
1027+
9481028 dispatch = {
9491029 "configure-build" : configure_build_python ,
9501030 "make-build" : make_build_python ,
9511031 "configure-host" : configure_host_python ,
9521032 "make-host" : make_host_python ,
953- "build" : build_all ,
954- "clean" : clean_all ,
1033+ "build" : build_targets ,
1034+ "clean" : clean_targets ,
9551035 "build-testbed" : build_testbed ,
9561036 "test" : run_testbed ,
9571037 "package" : package ,
0 commit comments