2222
2323SCRIPT_NAME = Path (__file__ ).name
2424ANDROID_DIR = Path (__file__ ).resolve ().parent
25- CHECKOUT = ANDROID_DIR .parent
25+ PYTHON_DIR = ANDROID_DIR .parent
26+ in_source_tree = (
27+ ANDROID_DIR .name == "Android" and (PYTHON_DIR / "pyconfig.h.in" ).exists ()
28+ )
29+
2630TESTBED_DIR = ANDROID_DIR / "testbed"
27- CROSS_BUILD_DIR = CHECKOUT / "cross-build"
31+ CROSS_BUILD_DIR = PYTHON_DIR / "cross-build"
2832
2933HOSTS = ["aarch64-linux-android" , "x86_64-linux-android" ]
3034APP_ID = "org.python.testbed"
@@ -74,41 +78,62 @@ def subdir(*parts, create=False):
7478
7579def run (command , * , host = None , env = None , log = True , ** kwargs ):
7680 kwargs .setdefault ("check" , True )
81+
7782 if env is None :
7883 env = os .environ .copy ()
79- original_env = env .copy ()
80-
8184 if host :
82- env_script = ANDROID_DIR / "android-env.sh"
83- env_output = subprocess .run (
84- f"set -eu; "
85- f"HOST={ host } ; "
86- f"PREFIX={ subdir (host )} /prefix; "
87- f". { env_script } ; "
88- f"export" ,
89- check = True , shell = True , text = True , stdout = subprocess .PIPE
90- ).stdout
91-
92- for line in env_output .splitlines ():
93- # We don't require every line to match, as there may be some other
94- # output from installing the NDK.
95- if match := re .search (
96- "^(declare -x |export )?(\\ w+)=['\" ]?(.*?)['\" ]?$" , line
97- ):
98- key , value = match [2 ], match [3 ]
99- if env .get (key ) != value :
100- print (line )
101- env [key ] = value
102-
103- if env == original_env :
104- raise ValueError (f"Found no variables in { env_script .name } output:\n "
105- + env_output )
85+ env .update (android_env (host ))
10686
10787 if log :
10888 print (">" , " " .join (map (str , command )))
10989 return subprocess .run (command , env = env , ** kwargs )
11090
11191
92+ def print_env (context ):
93+ android_env (getattr (context , "host" , None ))
94+
95+
96+ def android_env (host ):
97+ if host :
98+ prefix = subdir (host ) / "prefix"
99+ else :
100+ prefix = ANDROID_DIR / "prefix"
101+ sysconfigdata_files = prefix .glob ("lib/python*/_sysconfigdata__android_*.py" )
102+ host = re .fullmatch (
103+ r"_sysconfigdata__android_(.+).py" , next (sysconfigdata_files ).name
104+ )[1 ]
105+
106+ env_script = ANDROID_DIR / "android-env.sh"
107+ env_output = subprocess .run (
108+ f"set -eu; "
109+ f"export HOST={ host } ; "
110+ f"PREFIX={ prefix } ; "
111+ f". { env_script } ; "
112+ f"export" ,
113+ check = True , shell = True , capture_output = True , text = True ,
114+ ).stdout
115+
116+ env = {}
117+ for line in env_output .splitlines ():
118+ # We don't require every line to match, as there may be some other
119+ # output from installing the NDK.
120+ if match := re .search (
121+ "^(declare -x |export )?(\\ w+)=['\" ]?(.*?)['\" ]?$" , line
122+ ):
123+ key , value = match [2 ], match [3 ]
124+ if os .environ .get (key ) != value :
125+ env [key ] = value
126+
127+ if not env :
128+ raise ValueError (f"Found no variables in { env_script .name } output:\n "
129+ + env_output )
130+
131+ # Format the environment so it can be pasted into a shell.
132+ for key , value in sorted (env .items ()):
133+ print (f"export { key } ={ shlex .quote (value )} " )
134+ return env
135+
136+
112137def build_python_path ():
113138 """The path to the build Python binary."""
114139 build_dir = subdir ("build" )
@@ -127,7 +152,7 @@ def configure_build_python(context):
127152 clean ("build" )
128153 os .chdir (subdir ("build" , create = True ))
129154
130- command = [relpath (CHECKOUT / "configure" )]
155+ command = [relpath (PYTHON_DIR / "configure" )]
131156 if context .args :
132157 command .extend (context .args )
133158 run (command )
@@ -168,7 +193,7 @@ def configure_host_python(context):
168193 os .chdir (host_dir )
169194 command = [
170195 # Basic cross-compiling configuration
171- relpath (CHECKOUT / "configure" ),
196+ relpath (PYTHON_DIR / "configure" ),
172197 f"--host={ context .host } " ,
173198 f"--build={ sysconfig .get_config_var ('BUILD_GNU_TYPE' )} " ,
174199 f"--with-build-python={ build_python_path ()} " ,
@@ -624,8 +649,7 @@ def parse_args():
624649 configure_build = subcommands .add_parser ("configure-build" ,
625650 help = "Run `configure` for the "
626651 "build Python" )
627- make_build = subcommands .add_parser ("make-build" ,
628- help = "Run `make` for the build Python" )
652+ subcommands .add_parser ("make-build" , help = "Run `make` for the build Python" )
629653 configure_host = subcommands .add_parser ("configure-host" ,
630654 help = "Run `configure` for Android" )
631655 make_host = subcommands .add_parser ("make-host" ,
@@ -637,16 +661,22 @@ def parse_args():
637661 test = subcommands .add_parser (
638662 "test" , help = "Run the test suite" )
639663 package = subcommands .add_parser ("package" , help = "Make a release package" )
664+ env = subcommands .add_parser ("env" , help = "Print environment variables" )
640665
641666 # Common arguments
642667 for subcommand in build , configure_build , configure_host :
643668 subcommand .add_argument (
644669 "--clean" , action = "store_true" , default = False , dest = "clean" ,
645670 help = "Delete the relevant build and prefix directories first" )
646- for subcommand in [build , configure_host , make_host , package ]:
671+
672+ host_commands = [build , configure_host , make_host , package ]
673+ if in_source_tree :
674+ host_commands .append (env )
675+ for subcommand in host_commands :
647676 subcommand .add_argument (
648677 "host" , metavar = "HOST" , choices = HOSTS ,
649678 help = "Host triplet: choices=[%(choices)s]" )
679+
650680 for subcommand in build , configure_build , configure_host :
651681 subcommand .add_argument ("args" , nargs = "*" ,
652682 help = "Extra arguments to pass to `configure`" )
@@ -690,6 +720,7 @@ def main():
690720 "build-testbed" : build_testbed ,
691721 "test" : run_testbed ,
692722 "package" : package ,
723+ "env" : print_env ,
693724 }
694725
695726 try :
0 commit comments