10
10
import shutil
11
11
import subprocess
12
12
import sys
13
- from typing import Optional
14
- from . import release_tag
13
+ from typing import Optional , cast
15
14
16
- from . import install_os , RESET_COLOR , suffix , YELLOW
17
- from .util import download_file , verify_sha512 , get_sha_checksum , parse_version
15
+ from . import release_tag , install_os , RESET_COLOR , suffix , YELLOW
16
+ from .util import (
17
+ download_file ,
18
+ verify_sha512 ,
19
+ get_sha_checksum ,
20
+ parse_version ,
21
+ VERSION_TUPLE ,
22
+ )
18
23
19
24
20
25
#: This pattern is designed to match only the major version number.
21
26
RE_PARSE_VERSION = re .compile (rb"version\s([\d\.]+)" , re .MULTILINE )
22
27
23
28
24
- def is_installed (tool_name : str , version : str ) -> Optional [Path ]:
29
+ def is_installed (tool_name : str , version : VERSION_TUPLE ) -> Optional [Path ]:
25
30
"""Detect if the specified tool is installed.
26
31
27
32
:param tool_name: The name of the specified tool.
28
33
:param version: The specific version to expect.
29
34
30
35
:returns: The path to the detected tool (if found), otherwise `None`.
31
36
"""
32
- version_tuple = parse_version (version )
33
- ver_major = version_tuple [0 ]
37
+ ver_major = version [0 ]
34
38
exe_name = (
35
39
f"{ tool_name } " + (f"-{ ver_major } " if install_os != "windows" else "" ) + suffix
36
40
)
@@ -44,24 +48,28 @@ def is_installed(tool_name: str, version: str) -> Optional[Path]:
44
48
except (FileNotFoundError , subprocess .CalledProcessError ):
45
49
return None # tool is not installed
46
50
ver_num = RE_PARSE_VERSION .search (result .stdout )
51
+ assert ver_num is not None , "Failed to parse version from tool output"
52
+ ver_match = cast (bytes , ver_num .groups (0 )[0 ]).decode (encoding = "utf-8" )
47
53
print (
48
54
f"Found a installed version of { tool_name } :" ,
49
- ver_num . groups ( 0 )[ 0 ]. decode ( encoding = "utf-8" ) ,
55
+ ver_match ,
50
56
end = " " ,
51
57
)
52
- path = shutil .which (exe_name ) # find the installed binary
53
- if path is None :
58
+ exe_path = shutil .which (exe_name ) # find the installed binary
59
+ if exe_path is None :
54
60
print () # print end-of-line
55
61
return None # failed to locate the binary
56
- path = Path (path ).resolve ()
62
+ path = Path (exe_path ).resolve ()
57
63
print ("at" , str (path ))
58
- ver_num = ver_num . groups ( 0 )[ 0 ]. decode ( encoding = "utf-8" ) .split ("." )
59
- if ver_num is None or ver_num [0 ] != str (ver_major ):
64
+ ver_tuple = ver_match .split ("." )
65
+ if ver_tuple is None or ver_tuple [0 ] != str (ver_major ):
60
66
return None # version is unknown or not the desired major release
61
67
return path
62
68
63
69
64
- def clang_tools_binary_url (tool : str , version : str , tag : str = release_tag ) -> str :
70
+ def clang_tools_binary_url (
71
+ tool : str , version : VERSION_TUPLE , tag : str = release_tag
72
+ ) -> str :
65
73
"""Assemble the URL to the binary.
66
74
67
75
:param tool: The name of the tool to download.
@@ -74,12 +82,12 @@ def clang_tools_binary_url(tool: str, version: str, tag: str = release_tag) -> s
74
82
"https://github.com/cpp-linter/clang-tools-static-binaries/releases/download/"
75
83
+ tag
76
84
)
77
- download_url = f"{ base_url } /{ tool } -{ version } _{ install_os } -amd64{ suffix } "
85
+ download_url = f"{ base_url } /{ tool } -{ version [ 0 ] } _{ install_os } -amd64{ suffix } "
78
86
return download_url .replace (" " , "" )
79
87
80
88
81
89
def install_tool (
82
- tool_name : str , version : str , directory : str , no_progress_bar : bool
90
+ tool_name : str , version : VERSION_TUPLE , directory : str , no_progress_bar : bool
83
91
) -> bool :
84
92
"""An abstract function that can install either clang-tidy or clang-format.
85
93
@@ -91,21 +99,21 @@ def install_tool(
91
99
:returns: `True` if the binary had to be downloaded and installed.
92
100
`False` if the binary was not downloaded but is installed in ``directory``.
93
101
"""
94
- destination = Path (directory , f"{ tool_name } -{ version } { suffix } " )
102
+ destination = Path (directory , f"{ tool_name } -{ version [ 0 ] } { suffix } " )
95
103
bin_url = clang_tools_binary_url (tool_name , version )
96
104
if destination .exists ():
97
- print (f"{ tool_name } -{ version } " , "already installed..." )
105
+ print (f"{ tool_name } -{ version [ 0 ] } " , "already installed..." )
98
106
print (" checking SHA512..." , end = " " )
99
107
if verify_sha512 (get_sha_checksum (bin_url ), destination .read_bytes ()):
100
108
print ("valid" )
101
109
return False
102
110
print ("invalid" )
103
111
uninstall_tool (tool_name , version , directory )
104
- print ("Downloading" , tool_name , f"(version { version } )" )
112
+ print ("Downloading" , tool_name , f"(version { version [ 0 ] } )" )
105
113
bin_name = str (PurePath (bin_url ).stem )
106
114
if download_file (bin_url , bin_name , no_progress_bar ) is None :
107
115
raise OSError (f"Failed to download { bin_name } from { bin_url } " )
108
- move_and_chmod_bin (bin_name , f"{ tool_name } -{ version } { suffix } " , directory )
116
+ move_and_chmod_bin (bin_name , f"{ tool_name } -{ version [ 0 ] } { suffix } " , directory )
109
117
if not verify_sha512 (get_sha_checksum (bin_url ), destination .read_bytes ()):
110
118
raise ValueError (
111
119
f"File was corrupted during download from { bin_url } "
@@ -154,10 +162,10 @@ def move_and_chmod_bin(old_bin_name: str, new_bin_name: str, install_dir: str) -
154
162
155
163
def create_sym_link (
156
164
tool_name : str ,
157
- version : str ,
165
+ version : VERSION_TUPLE ,
158
166
install_dir : str ,
159
167
overwrite : bool = False ,
160
- target : Path = None ,
168
+ target : Optional [ Path ] = None ,
161
169
) -> bool :
162
170
"""Create a symlink to the installed binary that
163
171
doesn't have the version number appended.
@@ -178,7 +186,7 @@ def create_sym_link(
178
186
link_root_path .mkdir (parents = True )
179
187
link = link_root_path / (tool_name + suffix )
180
188
if target is None :
181
- target = link_root_path / f"{ tool_name } -{ version } { suffix } "
189
+ target = link_root_path / f"{ tool_name } -{ version [ 0 ] } { suffix } "
182
190
if link .exists ():
183
191
if not link .is_symlink ():
184
192
print (
@@ -212,15 +220,15 @@ def create_sym_link(
212
220
return False
213
221
214
222
215
- def uninstall_tool (tool_name : str , version : str , directory : str ):
223
+ def uninstall_tool (tool_name : str , version : VERSION_TUPLE , directory : str ):
216
224
"""Remove a specified tool of a given version.
217
225
218
226
:param tool_name: The name of the clang tool to uninstall.
219
227
:param version: The version of the clang-tools to remove.
220
228
:param directory: The directory from which to remove the
221
229
installed clang-tools.
222
230
"""
223
- tool_path = Path (directory , f"{ tool_name } -{ version } { suffix } " )
231
+ tool_path = Path (directory , f"{ tool_name } -{ version [ 0 ] } { suffix } " )
224
232
if tool_path .exists ():
225
233
print ("Removing" , tool_path .name , "from" , str (tool_path .parent ))
226
234
tool_path .unlink ()
@@ -241,12 +249,17 @@ def uninstall_clang_tools(version: str, directory: str):
241
249
"""
242
250
install_dir = install_dir_name (directory )
243
251
print (f"Uninstalling version { version } from { str (install_dir )} " )
252
+ version_tuple = parse_version (version )
244
253
for tool in ("clang-format" , "clang-tidy" ):
245
- uninstall_tool (tool , version , install_dir )
254
+ uninstall_tool (tool , version_tuple , install_dir )
246
255
247
256
248
257
def install_clang_tools (
249
- version : str , tools : str , directory : str , overwrite : bool , no_progress_bar : bool
258
+ version : VERSION_TUPLE ,
259
+ tools : str ,
260
+ directory : str ,
261
+ overwrite : bool ,
262
+ no_progress_bar : bool ,
250
263
) -> None :
251
264
"""Wraps functions used to individually install tools.
252
265
@@ -258,7 +271,7 @@ def install_clang_tools(
258
271
:param no_progress_bar: A flag used to disable the downloads' progress bar.
259
272
"""
260
273
install_dir = install_dir_name (directory )
261
- if install_dir .rstrip (os .sep ) not in os .environ .get ("PATH" ):
274
+ if install_dir .rstrip (os .sep ) not in os .environ .get ("PATH" , "" ):
262
275
print (
263
276
f"{ YELLOW } { install_dir } " ,
264
277
f"directory is not in your environment variable PATH.{ RESET_COLOR } " ,
0 commit comments