1
+ #!/usr/bin/env python3
2
+
1
3
import subprocess
2
4
import sys
3
5
import os
6
+ import shutil
7
+ import json
8
+ import time
4
9
5
- def run (* command ):
10
+ def run (* command , cwd = None ):
6
11
print (command )
7
12
try :
8
- result = subprocess .run (command )
13
+ result = subprocess .run (command , cwd = cwd )
9
14
except OSError :
10
15
return 127 , []
11
- return result .returncode , result .stdout .decode ('utf-8' ).strip ().split ('\n ' )
16
+ return result .returncode , "" # TODO
17
+
18
+ def read_properties (fname ):
19
+ data = {}
20
+ with open (fname , 'r' ) as f :
21
+ for line in f :
22
+ fields = [x .strip () for x in line .split ('=' )]
23
+ data [fields [0 ]] = fields [1 ]
24
+ return data
12
25
13
26
def check_git ():
14
27
rcode , stdout = run ('git' , '--version' )
@@ -20,18 +33,175 @@ def check_python():
20
33
return 1
21
34
else :
22
35
if os .path .exists ('python/.git' ):
23
- return 0
36
+ return 2
24
37
else :
25
38
return 1
39
+ else :
40
+ return 0
41
+
42
+ def check_ndk (ndk_dir , min_ver , checking_sub = False ):
43
+ if not os .path .exists (ndk_dir ):
44
+ return 1
45
+ if os .path .exists (ndk_dir + '/source.properties' ):
46
+ info = read_properties (ndk_dir + '/source.properties' )
47
+ if not 'Pkg.Revision' in info :
48
+ return 1
49
+ vn = [int (x ) for x in info ['Pkg.Revision' ].split ('.' )]
50
+ for i in range (len (vn )):
51
+ if i >= len (min_ver ):
52
+ print ('versions equal' )
53
+ return 0
54
+ elif vn [i ] > min_ver [i ]:
55
+ print ('later version' )
56
+ return 0
57
+ elif vn [i ] < min_ver [i ]:
58
+ print ('earlier version' )
59
+ return 1
60
+ print ('versions equal' )
61
+ return 0
26
62
else :
27
63
return 1
28
64
65
+ def trymkdir (dir ):
66
+ if not os .path .exists (dir ):
67
+ os .mkdir (dir )
68
+ return 0
69
+ elif not os .path .isdir (dir ):
70
+ print ("'%s' exists and is a file. Please move or delete it." % dir )
71
+ return 1
72
+ else :
73
+ return 0
74
+
75
+ def copy (src , dest ):
76
+ print ("%s -> %s" % (src , dest ))
77
+ shutil .copy (src , dest )
78
+
29
79
def main ():
30
80
print ("Building Python for Android 24 64-bit ARM (aarch64-linux-android)" )
31
81
print ("Checking/installing dependencies" )
32
82
83
+ # TODO arguments
84
+ py_version = 'v3.9.2'
85
+ ndk_dir = os .getenv ('HOME' ) + '/Android/Sdk/ndk/22.0.7026061'
86
+ ndk_min_ver = (22 ,)
87
+ make_jobs = 6
88
+ target = 'aarch64-linux-android'
89
+ android_ver = '24'
90
+ shell = '/usr/bin/bash'
91
+ make = '/usr/bin/make'
92
+ tar = '/usr/bin/tar'
93
+ force_build = False
94
+
33
95
if not check_git :
34
96
print ("Could not find Git. Is it in your PATH?" )
35
97
sys .exit (1 )
36
98
37
-
99
+ res = check_python ()
100
+ if res == 1 :
101
+ print ("'python' is already a file or directory. Please delete or move it." )
102
+ sys .exit (1 )
103
+ elif res == 2 :
104
+ print ("'python' Git clone found" )
105
+ else :
106
+ rval = run ('git' , 'clone' , '-n' , 'https://github.com/python/cpython' )[0 ]
107
+ if rval != 0 :
108
+ print ("Clone failed!" )
109
+ sys .exit (2 )
110
+
111
+ print ("Checkout version '%s'" % py_version )
112
+ rval = run ('git' , '-C' , 'python/' , 'checkout' , py_version )[0 ]
113
+ if rval != 0 :
114
+ print ("Checkout failed!" )
115
+ sys .exit (2 )
116
+
117
+ print ("Checking NDK" )
118
+ if check_ndk (ndk_dir , ndk_min_ver ) != 0 :
119
+ print ("NDK not found. Please install NDK version %s or later and specify --ndk-dir" % ('.' .join (min_ver )))
120
+ sys .exit (1 )
121
+
122
+ print ("Setting up build environment" )
123
+ if trymkdir ('build' ) != 0 :
124
+ sys .exit (1 )
125
+ with open ('build/config.site' , 'w' ) as f :
126
+ f .write ('ac_cv_file__dev_ptmx=no\n ' )
127
+ f .write ('ac_cv_file__dev_ptc=no\n ' )
128
+
129
+ arguments = [shell , '../python/configure' ]
130
+
131
+ toolchain = ndk_dir + '/toolchains/llvm/prebuilt/linux-x86_64'
132
+ arguments .append ('--srcdir=../python' )
133
+ arguments .append ('--prefix=' + os .path .realpath ('out/' ))
134
+ arguments .append ('--build=x86_64-pc-linux-gnu' )
135
+ arguments .append ('--host=' + target )
136
+ arguments .append ('--disable-ipv6' )
137
+ arguments .append ('CONFIG_SITE=config.site' )
138
+ arguments .append ('TOOLCHAIN=' + toolchain )
139
+ arguments .append ('TARGET=' + target )
140
+ arguments .append ('API=' + android_ver )
141
+ arguments .append ('AR=' + toolchain + '/bin/llvm-ar' )
142
+ arguments .append ('CC=' + toolchain + '/bin/' + target + android_ver + '-clang' )
143
+ arguments .append ('AS=' + toolchain + '/bin/' + target + android_ver + '-clang' )
144
+ arguments .append ('CXX=' + toolchain + '/bin/' + target + android_ver + '-clang++' )
145
+ arguments .append ('LD=' + toolchain + '/bin/ld' )
146
+ arguments .append ('RANLIB=' + toolchain + '/bin/llvm-ranlib' )
147
+ arguments .append ('STRIP=' + toolchain + '/bin/llvm-strip' )
148
+ arguments .append ('READELF=' + toolchain + '/bin/llvm-readelf' )
149
+ arguments .append ('LD_LIBRARY_PATH=' + toolchain + '/sysroot/usr/lib/' + target + \
150
+ ':' + toolchain + '/sysroot/usr/lib' + target + '/' + android_ver )
151
+
152
+ if not os .path .exists ('out/bin' ) or force_build :
153
+ rval = run (* arguments , cwd = 'build/' )[0 ]
154
+ if rval != 0 :
155
+ print ("configure failed (%d)" % rval )
156
+ sys .exit (2 )
157
+
158
+ rval = run (make , 'clean' , cwd = 'build/' )[0 ]
159
+ if rval != 0 :
160
+ print ("clean failed (%d)" % rval )
161
+ sys .exit (2 )
162
+
163
+ rval = run (make , '-j' + str (make_jobs ), cwd = 'build/' )[0 ]
164
+ if rval != 0 :
165
+ print ("compilation failed (%d)" % rval )
166
+ sys .exit (2 )
167
+
168
+ rval = run (make , 'install' , cwd = 'build/' )[0 ]
169
+ if rval != 0 :
170
+ print ("install failed (%d)" % rval )
171
+ sys .exit (2 )
172
+
173
+ pyver_numbers = py_version [1 :].split ('.' )
174
+ python_executable = 'python' + '.' .join (pyver_numbers [0 :2 ])
175
+
176
+ print ("Building install archive" )
177
+ if trymkdir ('install' ) != 0 :
178
+ sys .exit (2 )
179
+
180
+ copy ('out/bin/' + python_executable , 'install/' + python_executable )
181
+ install_archive = 'install/' + python_executable + '.tar.gz'
182
+ if os .path .exists (install_archive ):
183
+ os .remove (install_archive )
184
+ run (tar , '-C' , 'out/lib' , '-cvzf' , install_archive , 'python3.9/' )
185
+
186
+ build_time = time .localtime ()
187
+
188
+ install_info = {
189
+ 'version' : py_version [1 :],
190
+ 'python' : python_executable ,
191
+ 'build-tool-version' : '1.0.0' ,
192
+ 'build-date' : time .strftime ('%Y-%m-%d %H:%M' , build_time )
193
+ }
194
+
195
+ with open ('install/install.txt' , 'w' ) as f :
196
+ json .dump (install_info , f )
197
+
198
+ # copy install scripts
199
+ copy ('install-tools/install.py' , 'install/install.py' )
200
+ copy ('install-tools/install.sh' , 'install/install.sh' )
201
+ copy ('install-tools/README.txt' , 'install/README.txt' )
202
+
203
+ output_name = 'android-python-%s-%s' % (time .strftime ('%Y%m%d_%H%M' , build_time ))
204
+ shutil .make_archive (output_name , 'zip' , 'install' )
205
+
206
+ if __name__ == '__main__' :
207
+ main ()
0 commit comments