22
22
23
23
SCRIPT_NAME = Path (__file__ ).name
24
24
ANDROID_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
+
26
30
TESTBED_DIR = ANDROID_DIR / "testbed"
27
- CROSS_BUILD_DIR = CHECKOUT / "cross-build"
31
+ CROSS_BUILD_DIR = PYTHON_DIR / "cross-build"
28
32
29
33
HOSTS = ["aarch64-linux-android" ,"x86_64-linux-android" ]
30
34
APP_ID = "org.python.testbed"
@@ -74,41 +78,62 @@ def subdir(*parts, create=False):
74
78
75
79
def run (command ,* ,host = None ,env = None ,log = True ,** kwargs ):
76
80
kwargs .setdefault ("check" ,True )
81
+
77
82
if env is None :
78
83
env = os .environ .copy ()
79
- original_env = env .copy ()
80
-
81
84
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 ))
106
86
107
87
if log :
108
88
print (">" ," " .join (map (str ,command )))
109
89
return subprocess .run (command ,env = env ,** kwargs )
110
90
111
91
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
+
112
137
def build_python_path ():
113
138
"""The path to the build Python binary."""
114
139
build_dir = subdir ("build" )
@@ -127,7 +152,7 @@ def configure_build_python(context):
127
152
clean ("build" )
128
153
os .chdir (subdir ("build" ,create = True ))
129
154
130
- command = [relpath (CHECKOUT / "configure" )]
155
+ command = [relpath (PYTHON_DIR / "configure" )]
131
156
if context .args :
132
157
command .extend (context .args )
133
158
run (command )
@@ -168,7 +193,7 @@ def configure_host_python(context):
168
193
os .chdir (host_dir )
169
194
command = [
170
195
# Basic cross-compiling configuration
171
- relpath (CHECKOUT / "configure" ),
196
+ relpath (PYTHON_DIR / "configure" ),
172
197
f"--host={ context .host } " ,
173
198
f"--build={ sysconfig .get_config_var ('BUILD_GNU_TYPE' )} " ,
174
199
f"--with-build-python={ build_python_path ()} " ,
@@ -624,8 +649,7 @@ def parse_args():
624
649
configure_build = subcommands .add_parser ("configure-build" ,
625
650
help = "Run `configure` for the "
626
651
"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" )
629
653
configure_host = subcommands .add_parser ("configure-host" ,
630
654
help = "Run `configure` for Android" )
631
655
make_host = subcommands .add_parser ("make-host" ,
@@ -637,16 +661,22 @@ def parse_args():
637
661
test = subcommands .add_parser (
638
662
"test" ,help = "Run the test suite" )
639
663
package = subcommands .add_parser ("package" ,help = "Make a release package" )
664
+ env = subcommands .add_parser ("env" ,help = "Print environment variables" )
640
665
641
666
# Common arguments
642
667
for subcommand in build ,configure_build ,configure_host :
643
668
subcommand .add_argument (
644
669
"--clean" ,action = "store_true" ,default = False ,dest = "clean" ,
645
670
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 :
647
676
subcommand .add_argument (
648
677
"host" ,metavar = "HOST" ,choices = HOSTS ,
649
678
help = "Host triplet: choices=[%(choices)s]" )
679
+
650
680
for subcommand in build ,configure_build ,configure_host :
651
681
subcommand .add_argument ("args" ,nargs = "*" ,
652
682
help = "Extra arguments to pass to `configure`" )
@@ -690,6 +720,7 @@ def main():
690
720
"build-testbed" :build_testbed ,
691
721
"test" :run_testbed ,
692
722
"package" :package ,
723
+ "env" :print_env ,
693
724
}
694
725
695
726
try :