4
4
import shlex
5
5
import shutil
6
6
import subprocess
7
- import sys
8
7
9
8
10
9
TESTS_DIR = os .path .dirname (__file__ )
11
10
TOOL_ROOT = os .path .dirname (TESTS_DIR )
12
11
SRCDIR = os .path .dirname (os .path .dirname (TOOL_ROOT ))
13
12
14
- CONFIGURE = os .path .join (SRCDIR , 'configure' )
15
13
MAKE = shutil .which ('make' )
16
14
GIT = shutil .which ('git' )
17
15
FREEZE = os .path .join (TOOL_ROOT , 'freeze.py' )
@@ -22,20 +20,20 @@ class UnsupportedError(Exception):
22
20
"""The operation isn't supported."""
23
21
24
22
25
- def _run_cmd (cmd , cwd = None , verbose = True , showcmd = True , showerr = True ):
26
- if showcmd :
27
- print (f'# { " " .join (shlex .quote (a ) for a in cmd )} ' )
28
- proc = subprocess .run (
23
+ def _run_quiet (cmd , cwd = None ):
24
+ #print(f'# {" ".join(shlex.quote(a) for a in cmd)}')
25
+ return subprocess .run (
29
26
cmd ,
30
27
cwd = cwd ,
31
- capture_output = not verbose ,
28
+ capture_output = True ,
32
29
text = True ,
30
+ check = True ,
33
31
)
34
- if proc . returncode != 0 :
35
- if showerr :
36
- print ( proc . stderr , file = sys . stderr )
37
- proc . check_returncode ( )
38
- return proc .stdout
32
+
33
+
34
+ def _run_stdout ( cmd , cwd = None ):
35
+ proc = _run_quiet ( cmd , cwd )
36
+ return proc .stdout . strip ()
39
37
40
38
41
39
def find_opt (args , name ):
@@ -67,256 +65,130 @@ def ensure_opt(args, name, value):
67
65
args [pos ] = f'{ opt } ={ value } '
68
66
69
67
70
- def git_copy_repo (newroot , remote = None , * , verbose = True ):
68
+ def git_copy_repo (newroot , oldroot ):
71
69
if not GIT :
72
70
raise UnsupportedError ('git' )
73
- if not remote :
74
- remote = SRCDIR
71
+
75
72
if os .path .exists (newroot ):
76
73
print (f'updating copied repo { newroot } ...' )
77
74
if newroot == SRCDIR :
78
75
raise Exception ('this probably isn\' t what you wanted' )
79
- _run_cmd ([GIT , 'clean' , '-d' , '-f' ], newroot , verbose )
80
- _run_cmd ([GIT , 'reset' ], newroot , verbose )
81
- _run_cmd ([GIT , 'checkout' , '.' ], newroot , verbose )
82
- _run_cmd ([GIT , 'pull' , '-f' , remote ], newroot , verbose )
76
+ _run_quiet ([GIT , 'clean' , '-d' , '-f' ], newroot )
77
+ _run_quiet ([GIT , 'reset' ], newroot )
78
+ _run_quiet ([GIT , 'checkout' , '.' ], newroot )
79
+ _run_quiet ([GIT , 'pull' , '-f' , oldroot ], newroot )
83
80
else :
84
81
print (f'copying repo into { newroot } ...' )
85
- _run_cmd ([GIT , 'clone' , remote , newroot ], verbose = verbose )
86
- if os .path .exists (remote ):
87
- # Copy over any uncommited files.
88
- reporoot = remote
89
- text = _run_cmd ([GIT , 'status' , '-s' ], reporoot , verbose , showcmd = False )
90
- for line in text .splitlines ():
91
- _ , _ , relfile = line .strip ().partition (' ' )
92
- relfile = relfile .strip ()
93
- isdir = relfile .endswith (os .path .sep )
94
- relfile = relfile .rstrip (os .path .sep )
95
- srcfile = os .path .join (reporoot , relfile )
96
- dstfile = os .path .join (newroot , relfile )
97
- os .makedirs (os .path .dirname (dstfile ), exist_ok = True )
98
- if isdir :
99
- shutil .copytree (srcfile , dstfile , dirs_exist_ok = True )
100
- else :
101
- shutil .copy2 (srcfile , dstfile )
82
+ _run_quiet ([GIT , 'clone' , oldroot , newroot ])
83
+
84
+ # Copy over any uncommited files.
85
+ text = _run_stdout ([GIT , 'status' , '-s' ], oldroot )
86
+ for line in text .splitlines ():
87
+ _ , _ , relfile = line .strip ().partition (' ' )
88
+ relfile = relfile .strip ()
89
+ isdir = relfile .endswith (os .path .sep )
90
+ relfile = relfile .rstrip (os .path .sep )
91
+ srcfile = os .path .join (oldroot , relfile )
92
+ dstfile = os .path .join (newroot , relfile )
93
+ os .makedirs (os .path .dirname (dstfile ), exist_ok = True )
94
+ if isdir :
95
+ shutil .copytree (srcfile , dstfile , dirs_exist_ok = True )
96
+ else :
97
+ shutil .copy2 (srcfile , dstfile )
102
98
103
99
104
- ##################################
105
- # build queries
106
-
107
- def get_makefile_var (builddir , name , * , fail = True ):
100
+ def get_makefile_var (builddir , name ):
108
101
regex = re .compile (rf'^{ name } *=\s*(.*?)\s*$' )
109
102
filename = os .path .join (builddir , 'Makefile' )
110
103
try :
111
104
infile = open (filename )
112
105
except FileNotFoundError :
113
- if fail :
114
- raise # re-raise
115
106
return None
116
107
with infile :
117
108
for line in infile :
118
109
m = regex .match (line )
119
110
if m :
120
111
value , = m .groups ()
121
112
return value or ''
122
- if fail :
123
- raise KeyError (f'{ name !r} not in Makefile' , name = name )
124
113
return None
125
114
126
115
127
- def get_config_var (build , name , * , fail = True ):
128
- if os .path .isfile (build ):
129
- python = build
130
- builddir = os .path .dirname (build )
131
- else :
132
- builddir = build
133
- python = os .path .join (builddir , 'python' )
134
-
116
+ def get_config_var (builddir , name ):
117
+ python = os .path .join (builddir , 'python' )
135
118
if os .path .isfile (python ):
119
+ cmd = [python , '-c' ,
120
+ f'import sysconfig; print(sysconfig.get_config_var("{ name } "))' ]
136
121
try :
137
- text = _run_cmd (
138
- [python , '-c' ,
139
- f'import sysconfig; print(sysconfig.get_config_var("{ name } "))' ],
140
- showcmd = False ,
141
- showerr = False ,
142
- verbose = False ,
143
- )
144
- return text
122
+ return _run_stdout (cmd )
145
123
except subprocess .CalledProcessError :
146
124
pass
147
- return get_makefile_var (builddir , name , fail = fail )
125
+ return get_makefile_var (builddir , name )
148
126
149
127
150
- def get_configure_args (build , * , fail = True ):
151
- text = get_config_var (build , 'CONFIG_ARGS' , fail = fail )
152
- if not text :
153
- return None
154
- return shlex .split (text )
155
-
128
+ ##################################
129
+ # freezing
156
130
157
- def get_prefix (build = None ):
158
- if build and os .path .isfile (build ):
159
- return _run_cmd (
160
- [build , '-c' 'import sys; print(sys.prefix)' ],
161
- cwd = os .path .dirname (build ),
162
- showcmd = False ,
163
- )
164
- else :
165
- return get_makefile_var (build or '.' , 'prefix' , fail = False )
131
+ def prepare (script = None , outdir = None ):
132
+ if not outdir :
133
+ outdir = OUTDIR
134
+ os .makedirs (outdir , exist_ok = True )
166
135
136
+ # Write the script to disk.
137
+ if script :
138
+ scriptfile = os .path .join (outdir , 'app.py' )
139
+ with open (scriptfile , 'w' ) as outfile :
140
+ outfile .write (script )
167
141
168
- ##################################
169
- # building Python
170
-
171
- def configure_python (builddir = None , prefix = None , cachefile = None , args = None , * ,
172
- srcdir = None ,
173
- inherit = False ,
174
- verbose = True ,
175
- ):
176
- if not builddir :
177
- builddir = srcdir or SRCDIR
178
- if not srcdir :
179
- configure = os .path .join (builddir , 'configure' )
180
- if not os .path .isfile (configure ):
181
- srcdir = SRCDIR
182
- configure = CONFIGURE
183
- else :
184
- configure = os .path .join (srcdir , 'configure' )
185
-
186
- cmd = [configure ]
187
- if inherit :
188
- oldargs = get_configure_args (builddir , fail = False )
189
- if oldargs :
190
- cmd .extend (oldargs )
191
- if cachefile :
192
- if args and find_opt (args , 'cache-file' ) >= 0 :
193
- raise ValueError ('unexpected --cache-file' )
194
- ensure_opt (cmd , 'cache-file' , os .path .abspath (cachefile ))
195
- if prefix :
196
- if args and find_opt (args , 'prefix' ) >= 0 :
197
- raise ValueError ('unexpected --prefix' )
198
- ensure_opt (cmd , 'prefix' , os .path .abspath (prefix ))
199
- if args :
200
- cmd .extend (args )
142
+ # Make a copy of the repo to avoid affecting the current build.
143
+ srcdir = os .path .join (outdir , 'cpython' )
144
+ git_copy_repo (srcdir , SRCDIR )
201
145
202
- print (f'configuring python in { builddir } ...' )
146
+ # We use an out-of-tree build (instead of srcdir).
147
+ builddir = os .path .join (outdir , 'python-build' )
203
148
os .makedirs (builddir , exist_ok = True )
204
- _run_cmd (cmd , builddir , verbose )
205
- return builddir
206
149
150
+ # Run configure.
151
+ print (f'configuring python in { builddir } ...' )
152
+ cmd = [
153
+ os .path .join (srcdir , 'configure' ),
154
+ * shlex .split (get_config_var (builddir , 'CONFIG_ARGS' ) or '' ),
155
+ ]
156
+ ensure_opt (cmd , 'cache-file' , os .path .join (outdir , 'python-config.cache' ))
157
+ prefix = os .path .join (outdir , 'python-installation' )
158
+ ensure_opt (cmd , 'prefix' , prefix )
159
+ _run_quiet (cmd , builddir )
207
160
208
- def build_python (builddir , * , verbose = True ):
209
161
if not MAKE :
210
162
raise UnsupportedError ('make' )
211
163
212
- if not builddir :
213
- builddir = '.'
214
-
164
+ # Build python.
215
165
print ('building python...' )
216
- srcdir = get_config_var (builddir , 'srcdir' , fail = False ) or SRCDIR
217
- if os .path .abspath (builddir ) != srcdir :
218
- if os .path .exists (os .path .join (srcdir , 'Makefile' )):
219
- _run_cmd ([MAKE , '-C' , srcdir , 'clean' ], verbose = False )
220
- _run_cmd ([MAKE , '-C' , builddir , '-j8' ], verbose = verbose )
221
-
222
- return os .path .join (builddir , 'python' )
223
-
224
-
225
- def install_python (builddir , * , verbose = True ):
226
- if not MAKE :
227
- raise UnsupportedError ('make' )
228
-
229
- if not builddir :
230
- builddir = '.'
231
- prefix = get_prefix (builddir )
166
+ if os .path .exists (os .path .join (srcdir , 'Makefile' )):
167
+ # Out-of-tree builds require a clean srcdir.
168
+ _run_quiet ([MAKE , '-C' , srcdir , 'clean' ])
169
+ _run_quiet ([MAKE , '-C' , builddir , '-j8' ])
232
170
171
+ # Install the build.
233
172
print (f'installing python into { prefix } ...' )
234
- _run_cmd ([MAKE , '-C' , builddir , '-j' , 'install' ], verbose = verbose )
235
-
236
- if not prefix :
237
- return None
238
- return os .path .join (prefix , 'bin' , 'python3' )
239
-
240
-
241
- def ensure_python_installed (outdir , srcdir = None , * ,
242
- outoftree = True ,
243
- verbose = True ,
244
- ):
245
- cachefile = os .path .join (outdir , 'python-config.cache' )
246
- prefix = os .path .join (outdir , 'python-installation' )
247
- if outoftree :
248
- builddir = os .path .join (outdir , 'python-build' )
249
- else :
250
- builddir = srcdir or SRCDIR
251
- configure_python (builddir , prefix , cachefile ,
252
- srcdir = srcdir , inherit = True , verbose = verbose )
253
- build_python (builddir , verbose = verbose )
254
- return install_python (builddir , verbose = verbose )
173
+ _run_quiet ([MAKE , '-C' , builddir , '-j8' , 'install' ])
174
+ python = os .path .join (prefix , 'bin' , 'python3' )
255
175
256
-
257
- ##################################
258
- # freezing
259
-
260
- def prepare (script = None , outdir = None , * ,
261
- outoftree = True ,
262
- copy = False ,
263
- verbose = True ,
264
- ):
265
- if not outdir :
266
- outdir = OUTDIR
267
- os .makedirs (outdir , exist_ok = True )
268
- if script :
269
- if script .splitlines ()[0 ] == script and os .path .isfile (script ):
270
- scriptfile = script
271
- else :
272
- scriptfile = os .path .join (outdir , 'app.py' )
273
- with open (scriptfile , 'w' ) as outfile :
274
- outfile .write (script )
275
- else :
276
- scriptfile = None
277
- if copy :
278
- srcdir = os .path .join (outdir , 'cpython' )
279
- git_copy_repo (srcdir , SRCDIR , verbose = verbose )
280
- else :
281
- srcdir = SRCDIR
282
- python = ensure_python_installed (outdir , srcdir ,
283
- outoftree = outoftree , verbose = verbose )
284
176
return outdir , scriptfile , python
285
177
286
178
287
- def freeze (python , scriptfile , outdir = None , * , verbose = True ):
179
+ def freeze (python , scriptfile , outdir ):
288
180
if not MAKE :
289
181
raise UnsupportedError ('make' )
290
182
291
- if not outdir :
292
- outdir = OUTDIR
293
-
294
183
print (f'freezing { scriptfile } ...' )
295
184
os .makedirs (outdir , exist_ok = True )
296
- subprocess .run (
297
- [python , FREEZE , '-o' , outdir , scriptfile ],
298
- cwd = outdir ,
299
- capture_output = not verbose ,
300
- check = True ,
301
- )
302
-
303
- subprocess .run (
304
- [MAKE ],
305
- cwd = os .path .dirname (scriptfile ),
306
- capture_output = not verbose ,
307
- check = True ,
308
- )
185
+ _run_quiet ([python , FREEZE , '-o' , outdir , scriptfile ], outdir )
186
+ _run_quiet ([MAKE , '-C' , os .path .dirname (scriptfile )])
309
187
310
188
name = os .path .basename (scriptfile ).rpartition ('.' )[0 ]
311
189
executable = os .path .join (outdir , name )
312
190
return executable
313
191
314
192
315
193
def run (executable ):
316
- proc = subprocess .run (
317
- [executable ],
318
- capture_output = True ,
319
- text = True ,
320
- check = True ,
321
- )
322
- return proc .stdout .strip ()
194
+ return _run_stdout ([executable ])
0 commit comments