Skip to content

Commit 00fb799

Browse files
me-no-devPilnyTomaslucasssvazpre-commit-ci-lite[bot]
authored
feat(tools): Updated get.py with ability to verify extracted files and skip if ok (#8720)
* Updated get.py with ability to verify extracted files and skip if ok * Updates * Minor fixes * Push binary to tools * Fix formatting and add checksum comparison * Push binary to tools * Skip checksum for libs * Push binary to tools * ci(pre-commit): Apply automatic fixes * Push binary to tools * Make the linter happy * Push binary to tools * refactor(get.py): Small improvements * fix(get.py): Fix parent folder name * ci(pre-commit): Apply automatic fixes * Push binary to tools * fix(get.py): Fix header comment * Push binary to tools --------- Co-authored-by: Tomas Pilny <[email protected]> Co-authored-by: Lucas Saavedra Vaz <[email protected]> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: pre-commit-ci-lite[bot] <pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: lucasssvaz <[email protected]>
1 parent c16a325 commit 00fb799

File tree

2 files changed

+219
-42
lines changed

2 files changed

+219
-42
lines changed

Diff for: tools/get.exe

108 KB
Binary file not shown.

Diff for: tools/get.py

+219-42
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"""Script to download and extract tools
44
55
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.
77
"""
88

99
from __future__ import print_function
@@ -22,6 +22,11 @@
2222
import tarfile
2323
import zipfile
2424
import re
25+
import time
26+
import argparse
27+
28+
# Initialize start_time globally
29+
start_time = -1
2530

2631
if sys.version_info[0] == 3:
2732
from urllib.request import urlretrieve
@@ -42,7 +47,6 @@
4247
elif __file__:
4348
current_dir = os.path.dirname(os.path.realpath(unicode(__file__)))
4449

45-
# current_dir = os.path.dirname(os.path.realpath(unicode(__file__)))
4650
dist_dir = current_dir + "/dist/"
4751

4852

@@ -62,53 +66,175 @@ def mkdir_p(path):
6266
raise
6367

6468

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+
6679
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"\rDownloading... {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+
)
7185
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"\rDownloading... {downloaded_size / 1024 / 1024:.2f} MB downloaded - Elapsed Time: {format_time(time_elapsed)} - Speed: {current_speed / 1024 / 1024:.2f} MB/s" # noqa: E501
88+
)
7889
sys.stdout.flush()
7990

8091

81-
def unpack(filename, destination):
92+
def print_verification_progress(total_files, i, t1):
93+
if sys.stdout.isatty():
94+
sys.stdout.write(f"\rElapsed 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"\nMissing {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"\nMissing {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"\nMissing {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"\nVerification passed; completed in {format_time(time.time() - t1)}")
152+
153+
return True
154+
155+
156+
def unpack(filename, destination, force_extract): # noqa: C901
82157
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+
85213
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)
89217
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)
93221
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)
97225
else:
98226
raise NotImplementedError("Unsupported archive type")
99227

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"
104228
if rename_to != dirname:
105229
print("Renaming {0} to {1} ...".format(dirname, rename_to))
106230
if os.path.isdir(rename_to):
107231
shutil.rmtree(rename_to)
108232
shutil.move(dirname, rename_to)
109233

234+
return True
235+
110236

111-
def download_file_with_progress(url, filename):
237+
def download_file_with_progress(url, filename, start_time):
112238
import ssl
113239
import contextlib
114240

@@ -124,16 +250,16 @@ def download_file_with_progress(url, filename):
124250
with open(filename, "wb") as out_file:
125251
out_file.write(block)
126252
block_count += 1
127-
report_progress(block_count, block_size, total_size)
253+
report_progress(block_count, block_size, total_size, start_time)
128254
while True:
129255
block = fp.read(block_size)
130256
if not block:
131257
break
132258
out_file.write(block)
133259
block_count += 1
134-
report_progress(block_count, block_size, total_size)
260+
report_progress(block_count, block_size, total_size, start_time)
135261
else:
136-
raise Exception("nonexisting file or connection error")
262+
raise Exception("Non-existing file or connection error")
137263

138264

139265
def download_file(url, filename):
@@ -155,16 +281,21 @@ def download_file(url, filename):
155281
break
156282
out_file.write(block)
157283
else:
158-
raise Exception("nonexisting file or connection error")
284+
raise Exception("Non-existing file or connection error")
159285

160286

161-
def get_tool(tool):
287+
def get_tool(tool, force_download, force_extract):
162288
sys_name = platform.system()
163289
archive_name = tool["archiveFileName"]
290+
checksum = tool["checksum"][8:]
164291
local_path = dist_dir + archive_name
165292
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 + "' ...")
168299
sys.stdout.flush()
169300
if "CYGWIN_NT" in sys_name:
170301
import ssl
@@ -186,13 +317,18 @@ def get_tool(tool):
186317
try:
187318
urlretrieve(url, local_path, report_progress)
188319
except: # noqa: E722
189-
download_file_with_progress(url, local_path)
190-
sys.stdout.write("\rDone \n")
320+
download_file_with_progress(url, local_path, start_time)
321+
sys.stdout.write(" - Done\n")
191322
sys.stdout.flush()
192323
else:
193324
print("Tool {0} already downloaded".format(archive_name))
194325
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)
196332

197333

198334
def load_tools_list(filename, platform):
@@ -241,9 +377,44 @@ def identify_platform():
241377

242378

243379
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+
245411
if is_test:
246412
print("Test run!")
413+
414+
if force_all:
415+
force_download = True
416+
force_extract = True
417+
247418
identified_platform = identify_platform()
248419
print("Platform: {0}".format(identified_platform))
249420
tools_to_download = load_tools_list(
@@ -254,5 +425,11 @@ def identify_platform():
254425
if is_test:
255426
print("Would install: {0}".format(tool["archiveFileName"]))
256427
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+
258435
print("Platform Tools Installed")

0 commit comments

Comments
 (0)