clang-tool-chain 1.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of clang-tool-chain might be problematic. Click here for more details.
- clang_tool_chain/__init__.py +0 -0
- clang_tool_chain/__version__.py +4 -0
- clang_tool_chain/checksums.py +270 -0
- clang_tool_chain/cli.py +575 -0
- clang_tool_chain/downloader.py +1325 -0
- clang_tool_chain/downloads/README.md +144 -0
- clang_tool_chain/downloads/__init__.py +22 -0
- clang_tool_chain/downloads/__main__.py +11 -0
- clang_tool_chain/downloads/create_hardlink_archive.py +390 -0
- clang_tool_chain/downloads/create_iwyu_archives.py +330 -0
- clang_tool_chain/downloads/deduplicate_binaries.py +217 -0
- clang_tool_chain/downloads/download_binaries.py +463 -0
- clang_tool_chain/downloads/expand_archive.py +260 -0
- clang_tool_chain/downloads/extract_mingw_sysroot.py +349 -0
- clang_tool_chain/downloads/fetch_and_archive.py +1376 -0
- clang_tool_chain/downloads/strip_binaries.py +436 -0
- clang_tool_chain/downloads/test_compression.py +259 -0
- clang_tool_chain/fetch.py +158 -0
- clang_tool_chain/paths.py +93 -0
- clang_tool_chain/sccache_runner.py +160 -0
- clang_tool_chain/wrapper.py +1383 -0
- clang_tool_chain-1.0.2.dist-info/METADATA +1766 -0
- clang_tool_chain-1.0.2.dist-info/RECORD +26 -0
- clang_tool_chain-1.0.2.dist-info/WHEEL +4 -0
- clang_tool_chain-1.0.2.dist-info/entry_points.txt +31 -0
- clang_tool_chain-1.0.2.dist-info/licenses/LICENSE +204 -0
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Download pre-built LLVM/Clang binaries for multiple platforms.
|
|
4
|
+
|
|
5
|
+
This script downloads official LLVM releases from GitHub and prepares them
|
|
6
|
+
for packaging with the clang-tool-chain Python package.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
import hashlib
|
|
11
|
+
import platform
|
|
12
|
+
import shutil
|
|
13
|
+
import subprocess
|
|
14
|
+
import sys
|
|
15
|
+
import tarfile
|
|
16
|
+
import urllib.request
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
# Import checksum database
|
|
20
|
+
# Note: This import may fail when running the script standalone (before package installation)
|
|
21
|
+
try:
|
|
22
|
+
from clang_tool_chain.checksums import get_checksum, has_checksum
|
|
23
|
+
except ImportError:
|
|
24
|
+
# Fallback when running standalone
|
|
25
|
+
def get_checksum(version: str, platform: str) -> str | None:
|
|
26
|
+
return None
|
|
27
|
+
|
|
28
|
+
def has_checksum(version: str, platform: str) -> bool:
|
|
29
|
+
return False
|
|
30
|
+
|
|
31
|
+
def format_platform_key(os_name: str, arch: str) -> str:
|
|
32
|
+
return f"{os_name}-{arch}"
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
# Default LLVM version to download
|
|
36
|
+
DEFAULT_VERSION = "21.1.5"
|
|
37
|
+
|
|
38
|
+
# Base URLs for LLVM releases
|
|
39
|
+
GITHUB_RELEASE_URL = "https://github.com/llvm/llvm-project/releases/download"
|
|
40
|
+
|
|
41
|
+
# Platform-specific binary URLs and filenames
|
|
42
|
+
BINARY_CONFIGS: dict[str, dict[str, str]] = {
|
|
43
|
+
"win-x86_64": {
|
|
44
|
+
"filename": "LLVM-{version}-win64.exe",
|
|
45
|
+
"url": f"{GITHUB_RELEASE_URL}/llvmorg-{{version}}/LLVM-{{version}}-win64.exe",
|
|
46
|
+
"type": "installer",
|
|
47
|
+
"alt_filename": "clang+llvm-{version}-x86_64-pc-windows-msvc.tar.xz",
|
|
48
|
+
"alt_url": f"{GITHUB_RELEASE_URL}/llvmorg-{{version}}/clang+llvm-{{version}}-x86_64-pc-windows-msvc.tar.xz",
|
|
49
|
+
},
|
|
50
|
+
"linux-x86_64": {
|
|
51
|
+
"filename": "LLVM-{version}-Linux-X64.tar.xz",
|
|
52
|
+
"url": f"{GITHUB_RELEASE_URL}/llvmorg-{{version}}/LLVM-{{version}}-Linux-X64.tar.xz",
|
|
53
|
+
"type": "archive",
|
|
54
|
+
},
|
|
55
|
+
"linux-aarch64": {
|
|
56
|
+
"filename": "LLVM-{version}-Linux-ARM64.tar.xz",
|
|
57
|
+
"url": f"{GITHUB_RELEASE_URL}/llvmorg-{{version}}/LLVM-{{version}}-Linux-ARM64.tar.xz",
|
|
58
|
+
"type": "archive",
|
|
59
|
+
},
|
|
60
|
+
"darwin-x86_64": {
|
|
61
|
+
"filename": "clang+llvm-{version}-x86_64-apple-darwin.tar.xz",
|
|
62
|
+
"url": f"{GITHUB_RELEASE_URL}/llvmorg-{{version}}/clang+llvm-{{version}}-x86_64-apple-darwin.tar.xz",
|
|
63
|
+
"type": "archive",
|
|
64
|
+
},
|
|
65
|
+
"darwin-arm64": {
|
|
66
|
+
"filename": "clang+llvm-{version}-arm64-apple-darwin.tar.xz",
|
|
67
|
+
"url": f"{GITHUB_RELEASE_URL}/llvmorg-{{version}}/clang+llvm-{{version}}-arm64-apple-darwin.tar.xz",
|
|
68
|
+
"type": "archive",
|
|
69
|
+
},
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class BinaryDownloader:
|
|
74
|
+
"""Download and extract LLVM binaries for different platforms."""
|
|
75
|
+
|
|
76
|
+
def __init__(self, version: str = DEFAULT_VERSION, output_dir: str = "downloads", verify_checksums: bool = True):
|
|
77
|
+
"""
|
|
78
|
+
Initialize the downloader.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
version: LLVM version to download (e.g., "21.1.5")
|
|
82
|
+
output_dir: Directory to store downloaded files
|
|
83
|
+
verify_checksums: Whether to verify SHA256 checksums after download
|
|
84
|
+
"""
|
|
85
|
+
self.version = version
|
|
86
|
+
self.output_dir = Path(output_dir)
|
|
87
|
+
self.output_dir.mkdir(parents=True, exist_ok=True)
|
|
88
|
+
self.verify_checksums = verify_checksums
|
|
89
|
+
|
|
90
|
+
def compute_sha256(self, file_path: Path) -> str:
|
|
91
|
+
"""
|
|
92
|
+
Compute SHA256 checksum of a file.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
file_path: Path to the file
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
SHA256 checksum as hex string
|
|
99
|
+
"""
|
|
100
|
+
sha256_hash = hashlib.sha256()
|
|
101
|
+
with open(file_path, "rb") as f:
|
|
102
|
+
# Read file in chunks to handle large files
|
|
103
|
+
for byte_block in iter(lambda: f.read(4096), b""):
|
|
104
|
+
sha256_hash.update(byte_block)
|
|
105
|
+
return sha256_hash.hexdigest()
|
|
106
|
+
|
|
107
|
+
def verify_checksum(self, file_path: Path, expected_checksum: str | None = None) -> bool:
|
|
108
|
+
"""
|
|
109
|
+
Verify SHA256 checksum of a downloaded file.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
file_path: Path to the file to verify
|
|
113
|
+
expected_checksum: Expected SHA256 checksum (optional)
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
True if checksum matches or verification is skipped, False otherwise
|
|
117
|
+
"""
|
|
118
|
+
if not self.verify_checksums:
|
|
119
|
+
return True
|
|
120
|
+
|
|
121
|
+
if expected_checksum is None:
|
|
122
|
+
print(f"Note: No checksum provided for {file_path.name}, skipping verification")
|
|
123
|
+
return True
|
|
124
|
+
|
|
125
|
+
try:
|
|
126
|
+
print(f"Verifying checksum for {file_path.name}...")
|
|
127
|
+
actual_checksum = self.compute_sha256(file_path)
|
|
128
|
+
|
|
129
|
+
if actual_checksum.lower() == expected_checksum.lower():
|
|
130
|
+
print(f"✓ Checksum verified: {actual_checksum[:16]}...")
|
|
131
|
+
return True
|
|
132
|
+
else:
|
|
133
|
+
print("✗ Checksum mismatch!")
|
|
134
|
+
print(f" Expected: {expected_checksum}")
|
|
135
|
+
print(f" Actual: {actual_checksum}")
|
|
136
|
+
return False
|
|
137
|
+
|
|
138
|
+
except Exception as e:
|
|
139
|
+
print(f"Error verifying checksum: {e}")
|
|
140
|
+
return False
|
|
141
|
+
|
|
142
|
+
def download_file(
|
|
143
|
+
self, url: str, destination: Path, show_progress: bool = True, expected_checksum: str | None = None
|
|
144
|
+
) -> bool:
|
|
145
|
+
"""
|
|
146
|
+
Download a file from URL to destination.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
url: URL to download from
|
|
150
|
+
destination: Path to save the file
|
|
151
|
+
show_progress: Whether to show download progress
|
|
152
|
+
expected_checksum: Expected SHA256 checksum for verification (optional)
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
True if download was successful, False otherwise
|
|
156
|
+
"""
|
|
157
|
+
try:
|
|
158
|
+
print(f"Downloading {url}...")
|
|
159
|
+
|
|
160
|
+
# Check if file already exists
|
|
161
|
+
if destination.exists():
|
|
162
|
+
print(f"File already exists: {destination}")
|
|
163
|
+
# Verify existing file's checksum if provided
|
|
164
|
+
if expected_checksum and self.verify_checksums:
|
|
165
|
+
if self.verify_checksum(destination, expected_checksum):
|
|
166
|
+
print("Existing file verified, skipping download.")
|
|
167
|
+
return True
|
|
168
|
+
else:
|
|
169
|
+
print("Existing file failed verification, re-downloading...")
|
|
170
|
+
else:
|
|
171
|
+
response = input("Overwrite? (y/n): ").lower()
|
|
172
|
+
if response != "y":
|
|
173
|
+
print("Skipping download.")
|
|
174
|
+
return True
|
|
175
|
+
|
|
176
|
+
# Download with progress reporting
|
|
177
|
+
def reporthook(blocknum: int, blocksize: int, totalsize: int) -> None:
|
|
178
|
+
if show_progress and totalsize > 0:
|
|
179
|
+
downloaded = blocknum * blocksize
|
|
180
|
+
percent = min(100, downloaded * 100 / totalsize)
|
|
181
|
+
mb_downloaded = downloaded / (1024 * 1024)
|
|
182
|
+
mb_total = totalsize / (1024 * 1024)
|
|
183
|
+
print(f"\rProgress: {percent:.1f}% ({mb_downloaded:.1f}/{mb_total:.1f} MB)", end="")
|
|
184
|
+
|
|
185
|
+
urllib.request.urlretrieve(url, destination, reporthook if show_progress else None)
|
|
186
|
+
|
|
187
|
+
if show_progress:
|
|
188
|
+
print() # New line after progress
|
|
189
|
+
|
|
190
|
+
print(f"Successfully downloaded to {destination}")
|
|
191
|
+
|
|
192
|
+
# Verify checksum if provided
|
|
193
|
+
if expected_checksum and not self.verify_checksum(destination, expected_checksum):
|
|
194
|
+
print("Warning: Downloaded file failed checksum verification")
|
|
195
|
+
print("The file may be corrupted or tampered with")
|
|
196
|
+
# Don't return False to allow continuation, but warn user
|
|
197
|
+
return False
|
|
198
|
+
|
|
199
|
+
return True
|
|
200
|
+
|
|
201
|
+
except Exception as e:
|
|
202
|
+
print(f"Error downloading {url}: {e}")
|
|
203
|
+
return False
|
|
204
|
+
|
|
205
|
+
def extract_archive(self, archive_path: Path, extract_dir: Path) -> bool:
|
|
206
|
+
"""
|
|
207
|
+
Extract a tar.xz archive.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
archive_path: Path to the archive file
|
|
211
|
+
extract_dir: Directory to extract to
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
True if extraction was successful, False otherwise
|
|
215
|
+
"""
|
|
216
|
+
try:
|
|
217
|
+
print(f"Extracting {archive_path}...")
|
|
218
|
+
extract_dir.mkdir(parents=True, exist_ok=True)
|
|
219
|
+
|
|
220
|
+
with tarfile.open(archive_path, "r:xz") as tar:
|
|
221
|
+
tar.extractall(extract_dir)
|
|
222
|
+
|
|
223
|
+
print(f"Successfully extracted to {extract_dir}")
|
|
224
|
+
return True
|
|
225
|
+
|
|
226
|
+
except Exception as e:
|
|
227
|
+
print(f"Error extracting {archive_path}: {e}")
|
|
228
|
+
return False
|
|
229
|
+
|
|
230
|
+
def extract_windows_installer(self, installer_path: Path, extract_dir: Path) -> bool:
|
|
231
|
+
"""
|
|
232
|
+
Extract Windows .exe installer using 7zip or fallback method.
|
|
233
|
+
|
|
234
|
+
Args:
|
|
235
|
+
installer_path: Path to the .exe installer
|
|
236
|
+
extract_dir: Directory to extract to
|
|
237
|
+
|
|
238
|
+
Returns:
|
|
239
|
+
True if extraction was successful, False otherwise
|
|
240
|
+
"""
|
|
241
|
+
try:
|
|
242
|
+
print(f"Extracting Windows installer {installer_path}...")
|
|
243
|
+
extract_dir.mkdir(parents=True, exist_ok=True)
|
|
244
|
+
|
|
245
|
+
# Try using 7zip if available
|
|
246
|
+
if shutil.which("7z"):
|
|
247
|
+
result = subprocess.run(
|
|
248
|
+
["7z", "x", str(installer_path), f"-o{extract_dir}", "-y"],
|
|
249
|
+
capture_output=True,
|
|
250
|
+
text=True,
|
|
251
|
+
)
|
|
252
|
+
if result.returncode == 0:
|
|
253
|
+
print(f"Successfully extracted to {extract_dir}")
|
|
254
|
+
return True
|
|
255
|
+
else:
|
|
256
|
+
print(f"7zip extraction failed: {result.stderr}")
|
|
257
|
+
|
|
258
|
+
# Fallback: suggest manual extraction
|
|
259
|
+
print("Warning: Could not automatically extract Windows installer.")
|
|
260
|
+
print(f"Please manually extract {installer_path} to {extract_dir}")
|
|
261
|
+
print("You can use 7-Zip or run the installer in extract-only mode.")
|
|
262
|
+
return False
|
|
263
|
+
|
|
264
|
+
except Exception as e:
|
|
265
|
+
print(f"Error extracting {installer_path}: {e}")
|
|
266
|
+
return False
|
|
267
|
+
|
|
268
|
+
def download_platform(self, platform_key: str, expected_checksum: str | None = None) -> Path | None:
|
|
269
|
+
"""
|
|
270
|
+
Download binaries for a specific platform.
|
|
271
|
+
|
|
272
|
+
Args:
|
|
273
|
+
platform_key: Platform identifier (e.g., "linux-x86_64")
|
|
274
|
+
expected_checksum: Optional SHA256 checksum to verify download
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
Path to the extracted directory, or None if download failed
|
|
278
|
+
"""
|
|
279
|
+
if platform_key not in BINARY_CONFIGS:
|
|
280
|
+
print(f"Unknown platform: {platform_key}")
|
|
281
|
+
print(f"Available platforms: {', '.join(BINARY_CONFIGS.keys())}")
|
|
282
|
+
return None
|
|
283
|
+
|
|
284
|
+
config = BINARY_CONFIGS[platform_key]
|
|
285
|
+
filename = config["filename"].format(version=self.version)
|
|
286
|
+
url = config["url"].format(version=self.version)
|
|
287
|
+
|
|
288
|
+
# Try to get checksum from database if not provided
|
|
289
|
+
if expected_checksum is None and self.verify_checksums:
|
|
290
|
+
# Normalize platform key for checksum lookup
|
|
291
|
+
# Convert platform_key format to checksum format
|
|
292
|
+
# e.g., "darwin-x86_64" -> "mac-x86_64", "linux-aarch64" -> "linux-arm64"
|
|
293
|
+
checksum_platform = platform_key
|
|
294
|
+
if platform_key.startswith("darwin-"):
|
|
295
|
+
checksum_platform = platform_key.replace("darwin-", "mac-")
|
|
296
|
+
if "aarch64" in platform_key:
|
|
297
|
+
checksum_platform = checksum_platform.replace("aarch64", "arm64")
|
|
298
|
+
|
|
299
|
+
db_checksum = get_checksum(self.version, checksum_platform)
|
|
300
|
+
if db_checksum:
|
|
301
|
+
print(f"Using checksum from database for {checksum_platform}")
|
|
302
|
+
expected_checksum = db_checksum
|
|
303
|
+
elif has_checksum(self.version, checksum_platform):
|
|
304
|
+
# Checksum exists but is empty (should not happen with current implementation)
|
|
305
|
+
print(f"Warning: Empty checksum found in database for {checksum_platform}")
|
|
306
|
+
else:
|
|
307
|
+
print(f"Note: No checksum available in database for {checksum_platform} version {self.version}")
|
|
308
|
+
print(" The file will be downloaded but cannot be automatically verified.")
|
|
309
|
+
print(" To add checksum verification, see: src/clang_tool_chain/checksums.py")
|
|
310
|
+
|
|
311
|
+
# Download the file
|
|
312
|
+
download_path = self.output_dir / filename
|
|
313
|
+
if not self.download_file(url, download_path, expected_checksum=expected_checksum):
|
|
314
|
+
# Try alternative URL if available
|
|
315
|
+
if "alt_url" in config and "alt_filename" in config:
|
|
316
|
+
print("Trying alternative download URL...")
|
|
317
|
+
filename = config["alt_filename"].format(version=self.version)
|
|
318
|
+
url = config["alt_url"].format(version=self.version)
|
|
319
|
+
download_path = self.output_dir / filename
|
|
320
|
+
if not self.download_file(url, download_path, expected_checksum=expected_checksum):
|
|
321
|
+
return None
|
|
322
|
+
else:
|
|
323
|
+
return None
|
|
324
|
+
|
|
325
|
+
# Extract the archive
|
|
326
|
+
extract_dir = self.output_dir / f"{platform_key}-extracted"
|
|
327
|
+
|
|
328
|
+
if config["type"] == "installer":
|
|
329
|
+
if not self.extract_windows_installer(download_path, extract_dir):
|
|
330
|
+
print(f"Note: You may need to manually extract {download_path}")
|
|
331
|
+
else:
|
|
332
|
+
if not self.extract_archive(download_path, extract_dir):
|
|
333
|
+
return None
|
|
334
|
+
|
|
335
|
+
return extract_dir
|
|
336
|
+
|
|
337
|
+
def download_all(self, platforms: list[str] | None = None) -> dict[str, Path | None]:
|
|
338
|
+
"""
|
|
339
|
+
Download binaries for all or specified platforms.
|
|
340
|
+
|
|
341
|
+
Args:
|
|
342
|
+
platforms: List of platform keys to download, or None for all
|
|
343
|
+
|
|
344
|
+
Returns:
|
|
345
|
+
Dictionary mapping platform keys to extracted directory paths
|
|
346
|
+
"""
|
|
347
|
+
if platforms is None:
|
|
348
|
+
platforms = list(BINARY_CONFIGS.keys())
|
|
349
|
+
|
|
350
|
+
results = {}
|
|
351
|
+
for platform_key in platforms:
|
|
352
|
+
print(f"\n{'='*60}")
|
|
353
|
+
print(f"Downloading {platform_key}")
|
|
354
|
+
print(f"{'='*60}\n")
|
|
355
|
+
|
|
356
|
+
extract_dir = self.download_platform(platform_key)
|
|
357
|
+
results[platform_key] = extract_dir
|
|
358
|
+
|
|
359
|
+
if extract_dir:
|
|
360
|
+
print(f"✓ {platform_key}: Success")
|
|
361
|
+
else:
|
|
362
|
+
print(f"✗ {platform_key}: Failed")
|
|
363
|
+
|
|
364
|
+
return results
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def get_current_platform() -> str | None:
|
|
368
|
+
"""
|
|
369
|
+
Detect the current platform and return its key.
|
|
370
|
+
|
|
371
|
+
Returns:
|
|
372
|
+
Platform key string, or None if platform not supported
|
|
373
|
+
"""
|
|
374
|
+
system = platform.system().lower()
|
|
375
|
+
machine = platform.machine().lower()
|
|
376
|
+
|
|
377
|
+
if system == "windows":
|
|
378
|
+
return "win-x86_64"
|
|
379
|
+
elif system == "linux":
|
|
380
|
+
if machine in ("x86_64", "amd64"):
|
|
381
|
+
return "linux-x86_64"
|
|
382
|
+
elif machine in ("aarch64", "arm64"):
|
|
383
|
+
return "linux-aarch64"
|
|
384
|
+
elif system == "darwin":
|
|
385
|
+
if machine == "x86_64":
|
|
386
|
+
return "darwin-x86_64"
|
|
387
|
+
elif machine in ("arm64", "aarch64"):
|
|
388
|
+
return "darwin-arm64"
|
|
389
|
+
|
|
390
|
+
return None
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
def main() -> None:
|
|
394
|
+
"""Main entry point for the download script."""
|
|
395
|
+
parser = argparse.ArgumentParser(description="Download pre-built LLVM/Clang binaries for multiple platforms")
|
|
396
|
+
parser.add_argument(
|
|
397
|
+
"--version",
|
|
398
|
+
default=DEFAULT_VERSION,
|
|
399
|
+
help=f"LLVM version to download (default: {DEFAULT_VERSION})",
|
|
400
|
+
)
|
|
401
|
+
parser.add_argument(
|
|
402
|
+
"--output",
|
|
403
|
+
default="downloads",
|
|
404
|
+
help="Output directory for downloaded files (default: downloads)",
|
|
405
|
+
)
|
|
406
|
+
parser.add_argument(
|
|
407
|
+
"--platform",
|
|
408
|
+
action="append",
|
|
409
|
+
choices=list(BINARY_CONFIGS.keys()),
|
|
410
|
+
help="Platform to download (can specify multiple times, default: all)",
|
|
411
|
+
)
|
|
412
|
+
parser.add_argument(
|
|
413
|
+
"--current-only",
|
|
414
|
+
action="store_true",
|
|
415
|
+
help="Only download binaries for the current platform",
|
|
416
|
+
)
|
|
417
|
+
parser.add_argument(
|
|
418
|
+
"--no-verify",
|
|
419
|
+
action="store_true",
|
|
420
|
+
help="Skip SHA256 checksum verification (not recommended)",
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
args = parser.parse_args()
|
|
424
|
+
|
|
425
|
+
# Determine which platforms to download
|
|
426
|
+
platforms = None
|
|
427
|
+
if args.current_only:
|
|
428
|
+
current_platform = get_current_platform()
|
|
429
|
+
if current_platform:
|
|
430
|
+
platforms = [current_platform]
|
|
431
|
+
print(f"Detected current platform: {current_platform}")
|
|
432
|
+
else:
|
|
433
|
+
print("Error: Could not detect current platform")
|
|
434
|
+
sys.exit(1)
|
|
435
|
+
elif args.platform:
|
|
436
|
+
platforms = args.platform
|
|
437
|
+
|
|
438
|
+
# Download binaries
|
|
439
|
+
downloader = BinaryDownloader(version=args.version, output_dir=args.output, verify_checksums=not args.no_verify)
|
|
440
|
+
results = downloader.download_all(platforms=platforms)
|
|
441
|
+
|
|
442
|
+
# Print summary
|
|
443
|
+
print(f"\n{'='*60}")
|
|
444
|
+
print("Download Summary")
|
|
445
|
+
print(f"{'='*60}\n")
|
|
446
|
+
|
|
447
|
+
success_count = sum(1 for path in results.values() if path is not None)
|
|
448
|
+
total_count = len(results)
|
|
449
|
+
|
|
450
|
+
for platform_key, extract_dir in results.items():
|
|
451
|
+
status = "✓ Success" if extract_dir else "✗ Failed"
|
|
452
|
+
print(f"{platform_key:20s} {status}")
|
|
453
|
+
if extract_dir:
|
|
454
|
+
print(f" Location: {extract_dir}")
|
|
455
|
+
|
|
456
|
+
print(f"\nTotal: {success_count}/{total_count} successful")
|
|
457
|
+
|
|
458
|
+
if success_count < total_count:
|
|
459
|
+
sys.exit(1)
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
if __name__ == "__main__":
|
|
463
|
+
main()
|