3
3
import asyncio
4
4
import argparse
5
5
import os
6
+ import platform
6
7
import re
7
8
import shlex
8
9
import shutil
@@ -247,7 +248,13 @@ def make_host_python(context):
247
248
# flags to be duplicated. So we don't use the `host` argument here.
248
249
os .chdir (host_dir )
249
250
run (["make" , "-j" , str (os .cpu_count ())])
250
- run (["make" , "install" , f"prefix={ prefix_dir } " ])
251
+
252
+ # The `make install` output is very verbose and rarely useful, so
253
+ # suppress it by default.
254
+ run (
255
+ ["make" , "install" , f"prefix={ prefix_dir } " ],
256
+ capture_output = not context .verbose ,
257
+ )
251
258
252
259
253
260
def build_all (context ):
@@ -266,6 +273,18 @@ def clean_all(context):
266
273
clean (host )
267
274
268
275
276
+ def setup_ci ():
277
+ # https://github.blog/changelog/2024-04-02-github-actions-hardware-accelerated-android-virtualization-now-available/
278
+ if "GITHUB_ACTIONS" in os .environ and platform .system () == "Linux" :
279
+ run (
280
+ ["sudo" , "tee" , "/etc/udev/rules.d/99-kvm4all.rules" ],
281
+ input = 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"\n ' ,
282
+ text = True ,
283
+ )
284
+ run (["sudo" , "udevadm" , "control" , "--reload-rules" ])
285
+ run (["sudo" , "udevadm" , "trigger" , "--name-match=kvm" ])
286
+
287
+
269
288
def setup_sdk ():
270
289
sdkmanager = android_home / (
271
290
"cmdline-tools/latest/bin/sdkmanager"
@@ -578,6 +597,7 @@ async def gradle_task(context):
578
597
579
598
580
599
async def run_testbed (context ):
600
+ setup_ci ()
581
601
setup_sdk ()
582
602
setup_testbed ()
583
603
@@ -671,11 +691,63 @@ def package(context):
671
691
else :
672
692
shutil .copy2 (src , dst , follow_symlinks = False )
673
693
694
+ # Strip debug information.
695
+ if not context .debug :
696
+ so_files = glob (f"{ temp_dir } /**/*.so" , recursive = True )
697
+ run ([android_env (context .host )["STRIP" ], * so_files ], log = False )
698
+
674
699
dist_dir = subdir (context .host , "dist" , create = True )
675
700
package_path = shutil .make_archive (
676
701
f"{ dist_dir } /python-{ version } -{ context .host } " , "gztar" , temp_dir
677
702
)
678
703
print (f"Wrote { package_path } " )
704
+ return package_path
705
+
706
+
707
+ def ci (context ):
708
+ for step in [
709
+ configure_build_python ,
710
+ make_build_python ,
711
+ configure_host_python ,
712
+ make_host_python ,
713
+ package ,
714
+ ]:
715
+ caption = (
716
+ step .__name__ .replace ("_" , " " )
717
+ .capitalize ()
718
+ .replace ("python" , "Python" )
719
+ )
720
+ print (f"::group::{ caption } " )
721
+ result = step (context )
722
+ if step is package :
723
+ package_path = result
724
+ print ("::endgroup::" )
725
+
726
+ if (
727
+ "GITHUB_ACTIONS" in os .environ
728
+ and (platform .system (), platform .machine ()) != ("Linux" , "x86_64" )
729
+ ):
730
+ print (
731
+ "Skipping tests: GitHub Actions does not support the Android "
732
+ "emulator on this platform."
733
+ )
734
+ else :
735
+ with TemporaryDirectory (prefix = SCRIPT_NAME ) as temp_dir :
736
+ print ("::group::Tests" )
737
+ # Prove the package is self-contained by using it to run the tests.
738
+ shutil .unpack_archive (package_path , temp_dir )
739
+
740
+ # Arguments are similar to --fast-ci, but in single-process mode.
741
+ launcher_args = ["--managed" , "maxVersion" , "-v" ]
742
+ test_args = [
743
+ "--single-process" , "--fail-env-changed" , "--rerun" , "--slowest" ,
744
+ "--verbose3" , "-u" , "all,-cpu" , "--timeout=600"
745
+ ]
746
+ run (
747
+ ["./android.py" , "test" , * launcher_args , "--" , * test_args ],
748
+ cwd = temp_dir
749
+ )
750
+ print ("::endgroup::" )
679
751
680
752
681
753
def env (context ):
@@ -695,49 +767,52 @@ def parse_args():
695
767
parser = argparse .ArgumentParser ()
696
768
subcommands = parser .add_subparsers (dest = "subcommand" , required = True )
697
769
770
+ def add_parser (* args , ** kwargs ):
771
+ parser = subcommands .add_parser (* args , ** kwargs )
772
+ parser .add_argument (
773
+ "-v" , "--verbose" , action = "count" , default = 0 ,
774
+ help = "Show verbose output. Use twice to be even more verbose." )
775
+ return parser
776
+
698
777
# Subcommands
699
- build = subcommands . add_parser (
778
+ build = add_parser (
700
779
"build" , help = "Run configure-build, make-build, configure-host and "
701
780
"make-host" )
702
- configure_build = subcommands . add_parser (
781
+ configure_build = add_parser (
703
782
"configure-build" , help = "Run `configure` for the build Python" )
704
- subcommands . add_parser (
783
+ add_parser (
705
784
"make-build" , help = "Run `make` for the build Python" )
706
- configure_host = subcommands . add_parser (
785
+ configure_host = add_parser (
707
786
"configure-host" , help = "Run `configure` for Android" )
708
- make_host = subcommands . add_parser (
787
+ make_host = add_parser (
709
788
"make-host" , help = "Run `make` for Android" )
710
789
711
- subcommands .add_parser ("clean" , help = "Delete all build directories" )
712
- subcommands .add_parser ("build-testbed" , help = "Build the testbed app" )
713
- test = subcommands .add_parser ("test" , help = "Run the testbed app" )
714
- package = subcommands .add_parser ("package" , help = "Make a release package" )
715
- env = subcommands .add_parser ("env" , help = "Print environment variables" )
790
+ add_parser ("clean" , help = "Delete all build directories" )
791
+ add_parser ("build-testbed" , help = "Build the testbed app" )
792
+ test = add_parser ("test" , help = "Run the testbed app" )
793
+ package = add_parser ("package" , help = "Make a release package" )
794
+ ci = add_parser ("ci" , help = "Run build, package and test" )
795
+ env = add_parser ("env" , help = "Print environment variables" )
716
796
717
797
# Common arguments
718
- for subcommand in build , configure_build , configure_host :
798
+ for subcommand in [ build , configure_build , configure_host , ci ] :
719
799
subcommand .add_argument (
720
800
"--clean" , action = "store_true" , default = False , dest = "clean" ,
721
801
help = "Delete the relevant build directories first" )
722
802
723
- host_commands = [build , configure_host , make_host , package ]
803
+ host_commands = [build , configure_host , make_host , package , ci ]
724
804
if in_source_tree :
725
805
host_commands .append (env )
726
806
for subcommand in host_commands :
727
807
subcommand .add_argument (
728
808
"host" , metavar = "HOST" , choices = HOSTS ,
729
809
help = "Host triplet: choices=[%(choices)s]" )
730
810
731
- for subcommand in build , configure_build , configure_host :
811
+ for subcommand in [ build , configure_build , configure_host , ci ] :
732
812
subcommand .add_argument ("args" , nargs = "*" ,
733
813
help = "Extra arguments to pass to `configure`" )
734
814
735
815
# Test arguments
736
- test .add_argument (
737
- "-v" , "--verbose" , action = "count" , default = 0 ,
738
- help = "Show Gradle output, and non-Python logcat messages. "
739
- "Use twice to include high-volume messages which are rarely useful." )
740
-
741
816
device_group = test .add_mutually_exclusive_group (required = True )
742
817
device_group .add_argument (
743
818
"--connected" , metavar = "SERIAL" , help = "Run on a connected device. "
@@ -765,6 +840,12 @@ def parse_args():
765
840
"args" , nargs = "*" , help = f"Arguments to add to sys.argv. "
766
841
f"Separate them from { SCRIPT_NAME } 's own arguments with `--`." )
767
842
843
+ # Package arguments.
844
+ for subcommand in [package , ci ]:
845
+ subcommand .add_argument (
846
+ "-g" , action = "store_true" , default = False , dest = "debug" ,
847
+ help = "Include debug information in package" )
848
+
768
849
return parser .parse_args ()
769
850
770
851
@@ -788,6 +869,7 @@ def main():
788
869
"build-testbed" : build_testbed ,
789
870
"test" : run_testbed ,
790
871
"package" : package ,
872
+ "ci" : ci ,
791
873
"env" : env ,
792
874
}
793
875
@@ -803,6 +885,8 @@ def main():
803
885
def print_called_process_error (e ):
804
886
for stream_name in ["stdout" , "stderr" ]:
805
887
content = getattr (e , stream_name )
888
+ if isinstance (content , bytes ):
889
+ content = content .decode (* DECODE_ARGS )
806
890
stream = getattr (sys , stream_name )
807
891
if content :
808
892
stream .write (content )
0 commit comments