3
3
"""Script to download and extract tools
4
4
5
5
This script will download and extract required tools into the current directory.
6
- Tools list is obtained from package/package_esp8266com_index .template.json file.
6
+ Tools list is obtained from package/package_esp32_index .template.json file.
7
7
"""
8
8
9
9
from __future__ import print_function
22
22
import tarfile
23
23
import zipfile
24
24
import re
25
+ import time
26
+ import argparse
27
+
28
+ # Initialize start_time globally
29
+ start_time = - 1
25
30
26
31
if sys .version_info [0 ] == 3 :
27
32
from urllib .request import urlretrieve
42
47
elif __file__ :
43
48
current_dir = os .path .dirname (os .path .realpath (unicode (__file__ )))
44
49
45
- # current_dir = os.path.dirname(os.path.realpath(unicode(__file__)))
46
50
dist_dir = current_dir + "/dist/"
47
51
48
52
@@ -62,53 +66,175 @@ def mkdir_p(path):
62
66
raise
63
67
64
68
65
- def report_progress (count , blockSize , totalSize ):
69
+ def format_time (seconds ):
70
+ minutes , seconds = divmod (seconds , 60 )
71
+ return "{:02}:{:05.2f}" .format (int (minutes ), seconds )
72
+
73
+
74
+ def report_progress (block_count , block_size , total_size , start_time ):
75
+ downloaded_size = block_count * block_size
76
+ time_elapsed = time .time () - start_time
77
+ current_speed = downloaded_size / (time_elapsed )
78
+
66
79
if sys .stdout .isatty ():
67
- if totalSize > 0 :
68
- percent = int (count * blockSize * 100 / totalSize )
69
- percent = min (100 , percent )
70
- sys .stdout .write ("\r %d%%" % percent )
80
+ if total_size > 0 :
81
+ percent_complete = min ((downloaded_size / total_size ) * 100 , 100 )
82
+ sys .stdout .write (
83
+ f"\r Downloading... { percent_complete :.2f} % - { downloaded_size / 1024 / 1024 :.2f} MB downloaded - Elapsed Time: { format_time (time_elapsed )} - Speed: { current_speed / 1024 / 1024 :.2f} MB/s" # noqa: E501
84
+ )
71
85
else :
72
- sofar = (count * blockSize ) / 1024
73
- if sofar >= 1000 :
74
- sofar /= 1024
75
- sys .stdout .write ("\r %dMB " % (sofar ))
76
- else :
77
- sys .stdout .write ("\r %dKB" % (sofar ))
86
+ sys .stdout .write (
87
+ f"\r Downloading... { downloaded_size / 1024 / 1024 :.2f} MB downloaded - Elapsed Time: { format_time (time_elapsed )} - Speed: { current_speed / 1024 / 1024 :.2f} MB/s" # noqa: E501
88
+ )
78
89
sys .stdout .flush ()
79
90
80
91
81
- def unpack (filename , destination ):
92
+ def print_verification_progress (total_files , i , t1 ):
93
+ if sys .stdout .isatty ():
94
+ sys .stdout .write (f"\r Elapsed time { format_time (time .time () - t1 )} " )
95
+ sys .stdout .flush ()
96
+
97
+
98
+ def verify_files (filename , destination , rename_to ):
99
+ # Set the path of the extracted directory
100
+ extracted_dir_path = destination
101
+ t1 = time .time ()
102
+ if filename .endswith (".zip" ):
103
+ try :
104
+ with zipfile .ZipFile (filename , "r" ) as archive :
105
+ first_dir = archive .namelist ()[0 ].split ("/" )[0 ]
106
+ total_files = len (archive .namelist ())
107
+ for i , zipped_file in enumerate (archive .namelist (), 1 ):
108
+ local_path = os .path .join (extracted_dir_path , zipped_file .replace (first_dir , rename_to , 1 ))
109
+ if not os .path .exists (local_path ):
110
+ print (f"\n Missing { zipped_file } on location: { extracted_dir_path } " )
111
+ print (f"Verification failed; aborted in { format_time (time .time () - t1 )} " )
112
+ return False
113
+ print_verification_progress (total_files , i , t1 )
114
+ except zipfile .BadZipFile :
115
+ print (f"Verification failed; aborted in { format_time (time .time () - t1 )} " )
116
+ return False
117
+ elif filename .endswith (".tar.gz" ):
118
+ try :
119
+ with tarfile .open (filename , "r:gz" ) as archive :
120
+ first_dir = archive .getnames ()[0 ].split ("/" )[0 ]
121
+ total_files = len (archive .getnames ())
122
+ for i , zipped_file in enumerate (archive .getnames (), 1 ):
123
+ local_path = os .path .join (extracted_dir_path , zipped_file .replace (first_dir , rename_to , 1 ))
124
+ if not os .path .exists (local_path ):
125
+ print (f"\n Missing { zipped_file } on location: { extracted_dir_path } " )
126
+ print (f"Verification failed; aborted in { format_time (time .time () - t1 )} " )
127
+ return False
128
+ print_verification_progress (total_files , i , t1 )
129
+ except tarfile .ReadError :
130
+ print (f"Verification failed; aborted in { format_time (time .time () - t1 )} " )
131
+ return False
132
+ elif filename .endswith (".tar.xz" ):
133
+ try :
134
+ with tarfile .open (filename , "r:xz" ) as archive :
135
+ first_dir = archive .getnames ()[0 ].split ("/" )[0 ]
136
+ total_files = len (archive .getnames ())
137
+ for i , zipped_file in enumerate (archive .getnames (), 1 ):
138
+ local_path = os .path .join (extracted_dir_path , zipped_file .replace (first_dir , rename_to , 1 ))
139
+ if not os .path .exists (local_path ):
140
+ print (f"\n Missing { zipped_file } on location: { extracted_dir_path } " )
141
+ print (f"Verification failed; aborted in { format_time (time .time () - t1 )} " )
142
+ return False
143
+ print_verification_progress (total_files , i , t1 )
144
+ except tarfile .ReadError :
145
+ print (f"Verification failed; aborted in { format_time (time .time () - t1 )} " )
146
+ return False
147
+ else :
148
+ raise NotImplementedError ("Unsupported archive type" )
149
+
150
+ if verbose :
151
+ print (f"\n Verification passed; completed in { format_time (time .time () - t1 )} " )
152
+
153
+ return True
154
+
155
+
156
+ def unpack (filename , destination , force_extract ): # noqa: C901
82
157
dirname = ""
83
- print ("Extracting {0} ..." .format (os .path .basename (filename )))
84
- sys .stdout .flush ()
158
+ cfile = None # Compressed file
159
+ file_is_corrupted = False
160
+ if not force_extract :
161
+ print (" > Verify archive... " , end = "" , flush = True )
162
+
163
+ try :
164
+ if filename .endswith ("tar.gz" ):
165
+ if tarfile .is_tarfile (filename ):
166
+ cfile = tarfile .open (filename , "r:gz" )
167
+ dirname = cfile .getnames ()[0 ].split ("/" )[0 ]
168
+ else :
169
+ print ("File corrupted!" )
170
+ file_is_corrupted = True
171
+ elif filename .endswith ("tar.xz" ):
172
+ if tarfile .is_tarfile (filename ):
173
+ cfile = tarfile .open (filename , "r:xz" )
174
+ dirname = cfile .getnames ()[0 ].split ("/" )[0 ]
175
+ else :
176
+ print ("File corrupted!" )
177
+ file_is_corrupted = True
178
+ elif filename .endswith ("zip" ):
179
+ if zipfile .is_zipfile (filename ):
180
+ cfile = zipfile .ZipFile (filename )
181
+ dirname = cfile .namelist ()[0 ].split ("/" )[0 ]
182
+ else :
183
+ print ("File corrupted!" )
184
+ file_is_corrupted = True
185
+ else :
186
+ raise NotImplementedError ("Unsupported archive type" )
187
+ except EOFError :
188
+ print ("File corrupted or incomplete!" )
189
+ cfile = None
190
+ file_is_corrupted = True
191
+
192
+ if file_is_corrupted :
193
+ corrupted_filename = filename + ".corrupted"
194
+ os .rename (filename , corrupted_filename )
195
+ if verbose :
196
+ print (f"Renaming corrupted archive to { corrupted_filename } " )
197
+ return False
198
+
199
+ # A little trick to rename tool directories so they don't contain version number
200
+ rename_to = re .match (r"^([a-z][^\-]*\-*)+" , dirname ).group (0 ).strip ("-" )
201
+ if rename_to == dirname and dirname .startswith ("esp32-arduino-libs-" ):
202
+ rename_to = "esp32-arduino-libs"
203
+
204
+ if not force_extract :
205
+ if verify_files (filename , destination , rename_to ):
206
+ print (" Files ok. Skipping Extraction" )
207
+ return True
208
+ else :
209
+ print (" Extracting archive..." )
210
+ else :
211
+ print (" Forcing extraction" )
212
+
85
213
if filename .endswith ("tar.gz" ):
86
- tfile = tarfile . open ( filename , "r:gz" )
87
- tfile . extractall ( destination )
88
- dirname = tfile . getnames ()[ 0 ]
214
+ if not cfile :
215
+ cfile = tarfile . open ( filename , "r:gz" )
216
+ cfile . extractall ( destination )
89
217
elif filename .endswith ("tar.xz" ):
90
- tfile = tarfile . open ( filename , "r:xz" )
91
- tfile . extractall ( destination )
92
- dirname = tfile . getnames ()[ 0 ]
218
+ if not cfile :
219
+ cfile = tarfile . open ( filename , "r:xz" )
220
+ cfile . extractall ( destination )
93
221
elif filename .endswith ("zip" ):
94
- zfile = zipfile . ZipFile ( filename )
95
- zfile . extractall ( destination )
96
- dirname = zfile . namelist ()[ 0 ]
222
+ if not cfile :
223
+ cfile = zipfile . ZipFile ( filename )
224
+ cfile . extractall ( destination )
97
225
else :
98
226
raise NotImplementedError ("Unsupported archive type" )
99
227
100
- # a little trick to rename tool directories so they don't contain version number
101
- rename_to = re .match (r"^([a-z][^\-]*\-*)+" , dirname ).group (0 ).strip ("-" )
102
- if rename_to == dirname and dirname .startswith ("esp32-arduino-libs-" ):
103
- rename_to = "esp32-arduino-libs"
104
228
if rename_to != dirname :
105
229
print ("Renaming {0} to {1} ..." .format (dirname , rename_to ))
106
230
if os .path .isdir (rename_to ):
107
231
shutil .rmtree (rename_to )
108
232
shutil .move (dirname , rename_to )
109
233
234
+ return True
235
+
110
236
111
- def download_file_with_progress (url , filename ):
237
+ def download_file_with_progress (url , filename , start_time ):
112
238
import ssl
113
239
import contextlib
114
240
@@ -124,16 +250,16 @@ def download_file_with_progress(url, filename):
124
250
with open (filename , "wb" ) as out_file :
125
251
out_file .write (block )
126
252
block_count += 1
127
- report_progress (block_count , block_size , total_size )
253
+ report_progress (block_count , block_size , total_size , start_time )
128
254
while True :
129
255
block = fp .read (block_size )
130
256
if not block :
131
257
break
132
258
out_file .write (block )
133
259
block_count += 1
134
- report_progress (block_count , block_size , total_size )
260
+ report_progress (block_count , block_size , total_size , start_time )
135
261
else :
136
- raise Exception ("nonexisting file or connection error" )
262
+ raise Exception ("Non-existing file or connection error" )
137
263
138
264
139
265
def download_file (url , filename ):
@@ -155,16 +281,21 @@ def download_file(url, filename):
155
281
break
156
282
out_file .write (block )
157
283
else :
158
- raise Exception ("nonexisting file or connection error" )
284
+ raise Exception ("Non-existing file or connection error" )
159
285
160
286
161
- def get_tool (tool ):
287
+ def get_tool (tool , force_download , force_extract ):
162
288
sys_name = platform .system ()
163
289
archive_name = tool ["archiveFileName" ]
290
+ checksum = tool ["checksum" ][8 :]
164
291
local_path = dist_dir + archive_name
165
292
url = tool ["url" ]
166
- if not os .path .isfile (local_path ):
167
- print ("Downloading " + archive_name + " ..." )
293
+ start_time = time .time ()
294
+ if not os .path .isfile (local_path ) or force_download :
295
+ if verbose :
296
+ print ("Downloading '" + archive_name + "' to '" + local_path + "'" )
297
+ else :
298
+ print ("Downloading '" + archive_name + "' ..." )
168
299
sys .stdout .flush ()
169
300
if "CYGWIN_NT" in sys_name :
170
301
import ssl
@@ -186,13 +317,18 @@ def get_tool(tool):
186
317
try :
187
318
urlretrieve (url , local_path , report_progress )
188
319
except : # noqa: E722
189
- download_file_with_progress (url , local_path )
190
- sys .stdout .write ("\r Done \n " )
320
+ download_file_with_progress (url , local_path , start_time )
321
+ sys .stdout .write (" - Done \n " )
191
322
sys .stdout .flush ()
192
323
else :
193
324
print ("Tool {0} already downloaded" .format (archive_name ))
194
325
sys .stdout .flush ()
195
- unpack (local_path , "." )
326
+
327
+ if "esp32-arduino-libs" not in archive_name and sha256sum (local_path ) != checksum :
328
+ print ("Checksum mismatch for {0}" .format (archive_name ))
329
+ return False
330
+
331
+ return unpack (local_path , "." , force_extract )
196
332
197
333
198
334
def load_tools_list (filename , platform ):
@@ -241,9 +377,44 @@ def identify_platform():
241
377
242
378
243
379
if __name__ == "__main__" :
244
- is_test = len (sys .argv ) > 1 and sys .argv [1 ] == "-h"
380
+ parser = argparse .ArgumentParser (description = "Download and extract tools" )
381
+
382
+ parser .add_argument ("-v" , "--verbose" , type = bool , default = False , required = False , help = "Print verbose output" )
383
+
384
+ parser .add_argument (
385
+ "-d" , "--force_download" , type = bool , default = False , required = False , help = "Force download of tools"
386
+ )
387
+
388
+ parser .add_argument (
389
+ "-e" , "--force_extract" , type = bool , default = False , required = False , help = "Force extraction of tools"
390
+ )
391
+
392
+ parser .add_argument (
393
+ "-f" , "--force_all" , type = bool , default = False , required = False , help = "Force download and extraction of tools"
394
+ )
395
+
396
+ parser .add_argument ("-t" , "--test" , type = bool , default = False , required = False , help = argparse .SUPPRESS )
397
+
398
+ args = parser .parse_args ()
399
+
400
+ verbose = args .verbose
401
+ force_download = args .force_download
402
+ force_extract = args .force_extract
403
+ force_all = args .force_all
404
+ is_test = args .test
405
+
406
+ if is_test and (force_download or force_extract or force_all ):
407
+ print ("Cannot combine test (-t) and forced execution (-d | -e | -f)" )
408
+ parser .print_help (sys .stderr )
409
+ sys .exit (1 )
410
+
245
411
if is_test :
246
412
print ("Test run!" )
413
+
414
+ if force_all :
415
+ force_download = True
416
+ force_extract = True
417
+
247
418
identified_platform = identify_platform ()
248
419
print ("Platform: {0}" .format (identified_platform ))
249
420
tools_to_download = load_tools_list (
@@ -254,5 +425,11 @@ def identify_platform():
254
425
if is_test :
255
426
print ("Would install: {0}" .format (tool ["archiveFileName" ]))
256
427
else :
257
- get_tool (tool )
428
+ if not get_tool (tool , force_download , force_extract ):
429
+ if verbose :
430
+ print (f"Tool { tool ['archiveFileName' ]} was corrupted. Re-downloading...\n " )
431
+ if not get_tool (tool , True , force_extract ):
432
+ print (f"Tool { tool ['archiveFileName' ]} was corrupted, but re-downloading did not help!\n " )
433
+ sys .exit (1 )
434
+
258
435
print ("Platform Tools Installed" )
0 commit comments