@@ -205,38 +205,48 @@ def make_build_python(context):
205205#
206206# If you're a member of the Python core team, and you'd like to be able to push
207207# these tags yourself, please contact Malcolm Smith or Russell Keith-Magee.
208- def unpack_deps (host , prefix_dir ):
208+ def unpack_deps (host , prefix_dir , cache_dir ):
209209 os .chdir (prefix_dir )
210210 deps_url = "https://github.com/beeware/cpython-android-source-deps/releases/download"
211211 for name_ver in ["bzip2-1.0.8-3" , "libffi-3.4.4-3" , "openssl-3.0.19-1" ,
212212 "sqlite-3.50.4-0" , "xz-5.4.6-1" ]:
213213 filename = f"{ name_ver } -{ host } .tar.gz"
214- download (f"{ deps_url } /{ name_ver } /{ filename } " )
215- shutil .unpack_archive (filename )
216- os .remove (filename )
214+ out_path = download (f"{ deps_url } /{ name_ver } /{ filename } " , cache_dir )
215+ shutil .unpack_archive (out_path )
217216
218217
219- def download (url , target_dir = "." ):
220- out_path = f"{ target_dir } /{ basename (url )} "
221- run (["curl" , "-Lf" , "--retry" , "5" , "--retry-all-errors" , "-o" , out_path , url ])
218+ def download (url , cache_dir ):
219+ out_path = cache_dir / basename (url )
220+ cache_dir .mkdir (parents = True , exist_ok = True )
221+ if not out_path .is_file ():
222+ run (["curl" , "-Lf" , "--retry" , "5" , "--retry-all-errors" , "-o" , out_path , url ])
223+ else :
224+ print (f"Using cached version of { basename (url )} " )
222225 return out_path
223226
224227
225- def configure_host_python (context ):
228+ def configure_host_python (context , host = None ):
229+ if host is None :
230+ host = context .host
226231 if context .clean :
227- clean (context . host )
232+ clean (host )
228233
229- host_dir = subdir (context . host , create = True )
234+ host_dir = subdir (host , create = True )
230235 prefix_dir = host_dir / "prefix"
231236 if not prefix_dir .exists ():
232237 prefix_dir .mkdir ()
233- unpack_deps (context .host , prefix_dir )
238+ cache_dir = (
239+ Path (context .cache_dir ).resolve ()
240+ if context .cache_dir
241+ else CROSS_BUILD_DIR / "downloads"
242+ )
243+ unpack_deps (host , prefix_dir , cache_dir )
234244
235245 os .chdir (host_dir )
236246 command = [
237247 # Basic cross-compiling configuration
238248 relpath (PYTHON_DIR / "configure" ),
239- f"--host={ context . host } " ,
249+ f"--host={ host } " ,
240250 f"--build={ sysconfig .get_config_var ('BUILD_GNU_TYPE' )} " ,
241251 f"--with-build-python={ build_python_path ()} " ,
242252 "--without-ensurepip" ,
@@ -252,14 +262,16 @@ def configure_host_python(context):
252262
253263 if context .args :
254264 command .extend (context .args )
255- run (command , host = context . host )
265+ run (command , host = host )
256266
257267
258- def make_host_python (context ):
268+ def make_host_python (context , host = None ):
269+ if host is None :
270+ host = context .host
259271 # The CFLAGS and LDFLAGS set in android-env include the prefix dir, so
260272 # delete any previous Python installation to prevent it being used during
261273 # the build.
262- host_dir = subdir (context . host )
274+ host_dir = subdir (host )
263275 prefix_dir = host_dir / "prefix"
264276 for pattern in ("include/python*" , "lib/libpython*" , "lib/python*" ):
265277 delete_glob (f"{ prefix_dir } /{ pattern } " )
@@ -278,20 +290,28 @@ def make_host_python(context):
278290 )
279291
280292
281- def build_all (context ):
282- steps = [configure_build_python , make_build_python , configure_host_python ,
283- make_host_python ]
284- for step in steps :
285- step (context )
293+ def build_targets (context ):
294+ if context .target in {"all" , "build" }:
295+ configure_build_python (context )
296+ make_build_python (context )
297+
298+ for host in HOSTS :
299+ if context .target in {"all" , "hosts" , host }:
300+ configure_host_python (context , host )
301+ make_host_python (context , host )
286302
287303
288304def clean (host ):
289305 delete_glob (CROSS_BUILD_DIR / host )
290306
291307
292- def clean_all (context ):
293- for host in HOSTS + ["build" ]:
294- clean (host )
308+ def clean_targets (context ):
309+ if context .target in {"all" , "build" }:
310+ clean ("build" )
311+
312+ for host in HOSTS :
313+ if context .target in {"all" , "hosts" , host }:
314+ clean (host )
295315
296316
297317def setup_ci ():
@@ -853,31 +873,85 @@ def add_parser(*args, **kwargs):
853873
854874 # Subcommands
855875 build = add_parser (
856- "build" , help = "Run configure-build, make-build, configure-host and "
857- "make-host" )
876+ "build" ,
877+ help = "Run configure and make for the selected target"
878+ )
858879 configure_build = add_parser (
859880 "configure-build" , help = "Run `configure` for the build Python" )
860- add_parser (
881+ make_build = add_parser (
861882 "make-build" , help = "Run `make` for the build Python" )
862883 configure_host = add_parser (
863884 "configure-host" , help = "Run `configure` for Android" )
864885 make_host = add_parser (
865886 "make-host" , help = "Run `make` for Android" )
866887
867- add_parser ("clean" , help = "Delete all build directories" )
888+ clean = add_parser (
889+ "clean" ,
890+ help = "Delete build directories for the selected target"
891+ )
892+
868893 add_parser ("build-testbed" , help = "Build the testbed app" )
869894 test = add_parser ("test" , help = "Run the testbed app" )
870895 package = add_parser ("package" , help = "Make a release package" )
871896 ci = add_parser ("ci" , help = "Run build, package and test" )
872897 env = add_parser ("env" , help = "Print environment variables" )
873898
874899 # Common arguments
900+ # --cross-build-dir argument
901+ for cmd in [
902+ clean ,
903+ configure_build ,
904+ make_build ,
905+ configure_host ,
906+ make_host ,
907+ build ,
908+ package ,
909+ test ,
910+ ci ,
911+ ]:
912+ cmd .add_argument (
913+ "--cross-build-dir" ,
914+ action = "store" ,
915+ default = os .environ .get ("CROSS_BUILD_DIR" ),
916+ dest = "cross_build_dir" ,
917+ type = Path ,
918+ help = (
919+ "Path to the cross-build directory "
920+ f"(default: { CROSS_BUILD_DIR } ). Can also be set "
921+ "with the CROSS_BUILD_DIR environment variable."
922+ ),
923+ )
924+
925+ # --cache-dir option
926+ for cmd in [configure_host , build , ci ]:
927+ cmd .add_argument (
928+ "--cache-dir" ,
929+ default = os .environ .get ("CACHE_DIR" ),
930+ help = "The directory to store cached downloads." ,
931+ )
932+
933+ # --clean option
875934 for subcommand in [build , configure_build , configure_host , ci ]:
876935 subcommand .add_argument (
877936 "--clean" , action = "store_true" , default = False , dest = "clean" ,
878937 help = "Delete the relevant build directories first" )
879938
880- host_commands = [build , configure_host , make_host , package , ci ]
939+ # Allow "all", "build" and "hosts" targets for some commands
940+ for subcommand in [clean , build ]:
941+ subcommand .add_argument (
942+ "target" ,
943+ nargs = "?" ,
944+ default = "all" ,
945+ choices = ["all" , "build" , "hosts" ] + HOSTS ,
946+ help = (
947+ "The host triplet (e.g., aarch64-linux-android), "
948+ "or 'build' for just the build platform, or 'hosts' for all "
949+ "host platforms, or 'all' for the build platform and all "
950+ "hosts. Defaults to 'all'"
951+ ),
952+ )
953+
954+ host_commands = [configure_host , make_host , package , ci ]
881955 if in_source_tree :
882956 host_commands .append (env )
883957 for subcommand in host_commands :
@@ -939,13 +1013,19 @@ def main():
9391013 stream .reconfigure (line_buffering = True )
9401014
9411015 context = parse_args ()
1016+
1017+ # Set the CROSS_BUILD_DIR if an argument was provided
1018+ if context .cross_build_dir :
1019+ global CROSS_BUILD_DIR
1020+ CROSS_BUILD_DIR = context .cross_build_dir .resolve ()
1021+
9421022 dispatch = {
9431023 "configure-build" : configure_build_python ,
9441024 "make-build" : make_build_python ,
9451025 "configure-host" : configure_host_python ,
9461026 "make-host" : make_host_python ,
947- "build" : build_all ,
948- "clean" : clean_all ,
1027+ "build" : build_targets ,
1028+ "clean" : clean_targets ,
9491029 "build-testbed" : build_testbed ,
9501030 "test" : run_testbed ,
9511031 "package" : package ,
0 commit comments