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.

@@ -0,0 +1,575 @@
1
+ """
2
+ Main entry point for clang-tool-chain CLI.
3
+
4
+ Provides commands for managing and using the LLVM toolchain.
5
+ """
6
+
7
+ import argparse
8
+ import subprocess
9
+ import sys
10
+ from typing import NoReturn
11
+
12
+ from . import sccache_runner, wrapper
13
+
14
+ try:
15
+ from .__version__ import __llvm_version__, __version__
16
+ except ImportError:
17
+ __version__ = "unknown"
18
+ __llvm_version__ = "unknown"
19
+
20
+
21
+ def cmd_info(args: argparse.Namespace) -> int:
22
+ """Display information about the toolchain installation."""
23
+ print("Clang Tool Chain - LLVM/Clang Distribution")
24
+ print("=" * 60)
25
+ print()
26
+
27
+ # Platform information
28
+ try:
29
+ platform_name, arch = wrapper.get_platform_info()
30
+ print(f"Platform: {platform_name}")
31
+ print(f"Architecture: {arch}")
32
+ print()
33
+ except RuntimeError as e:
34
+ print(f"Error detecting platform: {e}")
35
+ return 1
36
+
37
+ # Windows GNU ABI information
38
+ if platform_name == "win":
39
+ print("Windows Target Configuration:")
40
+ print(" Default ABI: GNU (x86_64-w64-mingw32)")
41
+ print(" MSVC ABI: Available via clang-tool-chain-c-msvc")
42
+ print(" and clang-tool-chain-cpp-msvc")
43
+ print()
44
+ print("Why GNU ABI is default:")
45
+ print(" - Cross-platform consistency (same ABI on Linux/macOS/Windows)")
46
+ print(" - C++11 strict mode support (MSVC headers require C++14+)")
47
+ print(" - Arduino/embedded compatibility (matches GCC toolchain)")
48
+ print()
49
+
50
+ # Assets directory
51
+ assets_dir = wrapper.get_assets_dir()
52
+ print(f"Assets directory: {assets_dir}")
53
+ print(f"Assets exist: {assets_dir.exists()}")
54
+ print()
55
+
56
+ # Binary directory
57
+ try:
58
+ bin_dir = wrapper.get_platform_binary_dir()
59
+ print(f"Binary directory: {bin_dir}")
60
+ print("Binaries installed: Yes")
61
+ print()
62
+
63
+ # List available tools
64
+ if bin_dir.exists():
65
+ binaries = sorted(
66
+ [f.stem for f in bin_dir.iterdir() if f.is_file()],
67
+ key=str.lower,
68
+ )
69
+ print(f"Available tools ({len(binaries)}):")
70
+ for binary in binaries:
71
+ print(f" - {binary}")
72
+ except RuntimeError:
73
+ print("Binaries installed: No")
74
+ print()
75
+ print("To install binaries, run:")
76
+ print(" python scripts/download_binaries.py --current-only")
77
+ print(" python scripts/strip_binaries.py <extracted_dir> <output_dir> --platform <platform>")
78
+
79
+ return 0
80
+
81
+
82
+ def cmd_version(args: argparse.Namespace) -> int:
83
+ """Display version of a specific tool."""
84
+ tool_name: str = args.tool
85
+
86
+ # Map common names to actual tool names
87
+ tool_map = {
88
+ "c": "clang",
89
+ "cpp": "clang++",
90
+ "c++": "clang++",
91
+ "ld": "lld",
92
+ }
93
+
94
+ actual_tool: str = tool_map.get(tool_name, tool_name)
95
+
96
+ try:
97
+ wrapper.find_tool_binary(actual_tool)
98
+ result = wrapper.run_tool(actual_tool, ["--version"])
99
+ return result
100
+ except RuntimeError as e:
101
+ print(f"Error: {e}", file=sys.stderr)
102
+ return 1
103
+
104
+
105
+ def cmd_list_tools(args: argparse.Namespace) -> int:
106
+ """List all available wrapper tools."""
107
+ print("Available clang-tool-chain commands:")
108
+ print("=" * 60)
109
+ print()
110
+
111
+ tools = [
112
+ ("clang-tool-chain-c", "C compiler (clang) - GNU ABI on Windows"),
113
+ ("clang-tool-chain-cpp", "C++ compiler (clang++) - GNU ABI on Windows"),
114
+ ("clang-tool-chain-c-msvc", "C compiler (clang) - MSVC ABI (Windows only)"),
115
+ ("clang-tool-chain-cpp-msvc", "C++ compiler (clang++) - MSVC ABI (Windows only)"),
116
+ ("clang-tool-chain-ld", "LLVM linker (lld/lld-link)"),
117
+ ("clang-tool-chain-ar", "Archive tool (llvm-ar)"),
118
+ ("clang-tool-chain-nm", "Symbol table viewer (llvm-nm)"),
119
+ ("clang-tool-chain-objdump", "Object file dumper (llvm-objdump)"),
120
+ ("clang-tool-chain-objcopy", "Object copying tool (llvm-objcopy)"),
121
+ ("clang-tool-chain-ranlib", "Archive index generator (llvm-ranlib)"),
122
+ ("clang-tool-chain-strip", "Symbol stripper (llvm-strip)"),
123
+ ("clang-tool-chain-readelf", "ELF file reader (llvm-readelf)"),
124
+ ("clang-tool-chain-as", "LLVM assembler (llvm-as)"),
125
+ ("clang-tool-chain-dis", "LLVM disassembler (llvm-dis)"),
126
+ ("clang-tool-chain-format", "Code formatter (clang-format)"),
127
+ ("clang-tool-chain-tidy", "Static analyzer (clang-tidy)"),
128
+ ]
129
+
130
+ for cmd, desc in tools:
131
+ print(f" {cmd:30s} - {desc}")
132
+
133
+ print()
134
+ print("sccache integration (requires sccache in PATH):")
135
+ print("=" * 60)
136
+ print()
137
+
138
+ sccache_tools = [
139
+ ("clang-tool-chain-sccache", "Direct sccache access (stats, management)"),
140
+ ("clang-tool-chain-sccache-c", "sccache + C compiler (clang)"),
141
+ ("clang-tool-chain-sccache-cpp", "sccache + C++ compiler (clang++)"),
142
+ ]
143
+
144
+ for cmd, desc in sccache_tools:
145
+ print(f" {cmd:30s} - {desc}")
146
+
147
+ print()
148
+ print("For more information, run:")
149
+ print(" clang-tool-chain info")
150
+
151
+ return 0
152
+
153
+
154
+ def cmd_path(args: argparse.Namespace) -> int:
155
+ """Display the path to the binary directory or a specific tool."""
156
+ try:
157
+ if args.tool:
158
+ tool_name: str = args.tool
159
+ # Map common names to actual tool names
160
+ tool_map = {
161
+ "c": "clang",
162
+ "cpp": "clang++",
163
+ "c++": "clang++",
164
+ "ld": "lld",
165
+ }
166
+
167
+ actual_tool: str = tool_map.get(tool_name, tool_name)
168
+ tool_path = wrapper.find_tool_binary(actual_tool)
169
+ print(tool_path)
170
+ else:
171
+ bin_dir = wrapper.get_platform_binary_dir()
172
+ print(bin_dir)
173
+ return 0
174
+ except RuntimeError as e:
175
+ print(f"Error: {e}", file=sys.stderr)
176
+ return 1
177
+
178
+
179
+ def cmd_package_version(args: argparse.Namespace) -> int:
180
+ """Display the package version and target LLVM version."""
181
+ print(f"clang-tool-chain version: {__version__}")
182
+ print(f"Target LLVM version: {__llvm_version__}")
183
+ print()
184
+
185
+ if args.verbose:
186
+ # Show more detailed version information
187
+ print("Package Information:")
188
+ print("=" * 60)
189
+ print(" Package: clang-tool-chain")
190
+ print(f" Version: {__version__}")
191
+ print(f" LLVM Version: {__llvm_version__}")
192
+ print()
193
+
194
+ # Try to get actual clang version from installed binaries
195
+ try:
196
+ platform_name, arch = wrapper.get_platform_info()
197
+ print("Platform Information:")
198
+ print(f" Platform: {platform_name}")
199
+ print(f" Architecture: {arch}")
200
+ print()
201
+
202
+ # Try to get installed clang version
203
+ try:
204
+ wrapper.find_tool_binary("clang")
205
+ print("Installed LLVM/Clang:")
206
+ result = wrapper.run_tool("clang", ["--version"])
207
+ if result != 0:
208
+ print(" (Unable to determine version)")
209
+ except RuntimeError:
210
+ print("Installed LLVM/Clang:")
211
+ print(" Not installed yet")
212
+ print()
213
+ print("To install binaries, run:")
214
+ print(" python scripts/download_binaries.py --current-only")
215
+ print(" python scripts/strip_binaries.py <extracted_dir> <output_dir> --platform <platform>")
216
+ except RuntimeError as e:
217
+ print(f"Error: {e}", file=sys.stderr)
218
+
219
+ return 0
220
+
221
+
222
+ def cmd_test(args: argparse.Namespace) -> int:
223
+ """Run diagnostic tests to verify the toolchain installation."""
224
+ import tempfile
225
+ from pathlib import Path
226
+
227
+ print("Clang Tool Chain - Diagnostic Tests")
228
+ print("=" * 70)
229
+ print()
230
+
231
+ # Test 1: Platform Detection
232
+ print("[1/7] Testing platform detection...")
233
+ try:
234
+ platform_name, arch = wrapper.get_platform_info()
235
+ print(f" Platform: {platform_name}/{arch}")
236
+ print(" ✓ PASSED")
237
+ except Exception as e:
238
+ print(f" ✗ FAILED: {e}")
239
+ return 1
240
+ print()
241
+
242
+ # Test 2: Toolchain Download/Installation
243
+ print("[2/7] Testing toolchain installation...")
244
+ try:
245
+ bin_dir = wrapper.get_platform_binary_dir()
246
+ if bin_dir.exists():
247
+ print(f" Binary directory: {bin_dir}")
248
+ print(" ✓ PASSED")
249
+ else:
250
+ print(f" ✗ FAILED: Binary directory does not exist: {bin_dir}")
251
+ return 1
252
+ except Exception as e:
253
+ print(f" ✗ FAILED: {e}")
254
+ return 1
255
+ print()
256
+
257
+ # Test 3: Finding clang binary
258
+ print("[3/7] Testing binary resolution (clang)...")
259
+ try:
260
+ clang_path = wrapper.find_tool_binary("clang")
261
+ print(f" Found: {clang_path}")
262
+ if not clang_path.exists():
263
+ print(f" ✗ FAILED: Binary does not exist: {clang_path}")
264
+ return 1
265
+ print(" ✓ PASSED")
266
+ except Exception as e:
267
+ print(f" ✗ FAILED: {e}")
268
+ return 1
269
+ print()
270
+
271
+ # Test 4: Finding clang++ binary
272
+ print("[4/7] Testing binary resolution (clang++)...")
273
+ try:
274
+ clang_cpp_path = wrapper.find_tool_binary("clang++")
275
+ print(f" Found: {clang_cpp_path}")
276
+ if not clang_cpp_path.exists():
277
+ print(f" ✗ FAILED: Binary does not exist: {clang_cpp_path}")
278
+ return 1
279
+ print(" ✓ PASSED")
280
+ except Exception as e:
281
+ print(f" ✗ FAILED: {e}")
282
+ return 1
283
+ print()
284
+
285
+ # Test 5: Version check for clang
286
+ print("[5/7] Testing clang version...")
287
+ try:
288
+ result = subprocess.run([str(clang_path), "--version"], capture_output=True, text=True, timeout=10)
289
+ if result.returncode == 0:
290
+ version_line = result.stdout.split("\n")[0]
291
+ print(f" {version_line}")
292
+ print(" ✓ PASSED")
293
+ else:
294
+ print(f" ✗ FAILED: clang --version returned {result.returncode}")
295
+ print(f" stderr: {result.stderr}")
296
+ return 1
297
+ except Exception as e:
298
+ print(f" ✗ FAILED: {e}")
299
+ return 1
300
+ print()
301
+
302
+ # Test 6: Simple compilation test
303
+ print("[6/7] Testing C compilation...")
304
+ with tempfile.TemporaryDirectory() as tmpdir:
305
+ tmpdir_path = Path(tmpdir)
306
+ test_c = tmpdir_path / "test.c"
307
+ test_out = tmpdir_path / "test"
308
+ if platform_name == "win":
309
+ test_out = test_out.with_suffix(".exe")
310
+
311
+ # Write simple C program
312
+ test_c.write_text(
313
+ """
314
+ #include <stdio.h>
315
+ int main() {
316
+ printf("Hello from clang-tool-chain!\\n");
317
+ return 0;
318
+ }
319
+ """
320
+ )
321
+
322
+ try:
323
+ # Compile
324
+ result = subprocess.run(
325
+ [str(clang_path), str(test_c), "-o", str(test_out)], capture_output=True, text=True, timeout=30
326
+ )
327
+ if result.returncode != 0:
328
+ print(" ✗ FAILED: Compilation failed")
329
+ print(f" stdout: {result.stdout}")
330
+ print(f" stderr: {result.stderr}")
331
+ return 1
332
+
333
+ # Verify output file was created
334
+ if not test_out.exists():
335
+ print(f" ✗ FAILED: Output binary not created: {test_out}")
336
+ return 1
337
+
338
+ print(f" Compiled: {test_out}")
339
+ print(" ✓ PASSED")
340
+ except Exception as e:
341
+ print(f" ✗ FAILED: {e}")
342
+ return 1
343
+ print()
344
+
345
+ # Test 7: Simple C++ compilation test
346
+ print("[7/7] Testing C++ compilation...")
347
+ with tempfile.TemporaryDirectory() as tmpdir:
348
+ tmpdir_path = Path(tmpdir)
349
+ test_cpp = tmpdir_path / "test.cpp"
350
+ test_out = tmpdir_path / "test"
351
+ if platform_name == "win":
352
+ test_out = test_out.with_suffix(".exe")
353
+
354
+ # Write simple C++ program
355
+ test_cpp.write_text(
356
+ """
357
+ #include <iostream>
358
+ int main() {
359
+ std::cout << "Hello from clang-tool-chain C++!" << std::endl;
360
+ return 0;
361
+ }
362
+ """
363
+ )
364
+
365
+ try:
366
+ # Compile
367
+ result = subprocess.run(
368
+ [str(clang_cpp_path), str(test_cpp), "-o", str(test_out)], capture_output=True, text=True, timeout=30
369
+ )
370
+ if result.returncode != 0:
371
+ print(" ✗ FAILED: Compilation failed")
372
+ print(f" stdout: {result.stdout}")
373
+ print(f" stderr: {result.stderr}")
374
+ return 1
375
+
376
+ # Verify output file was created
377
+ if not test_out.exists():
378
+ print(f" ✗ FAILED: Output binary not created: {test_out}")
379
+ return 1
380
+
381
+ print(f" Compiled: {test_out}")
382
+ print(" ✓ PASSED")
383
+ except Exception as e:
384
+ print(f" ✗ FAILED: {e}")
385
+ return 1
386
+ print()
387
+
388
+ print("=" * 70)
389
+ print("All tests passed! ✓")
390
+ print()
391
+ return 0
392
+
393
+
394
+ def main() -> int:
395
+ """Main entry point for the clang-tool-chain CLI."""
396
+ parser = argparse.ArgumentParser(
397
+ prog="clang-tool-chain",
398
+ description="LLVM/Clang toolchain management and wrapper utilities",
399
+ epilog="For more information, visit: https://github.com/your-repo/clang-tool-chain",
400
+ )
401
+
402
+ subparsers = parser.add_subparsers(
403
+ dest="command",
404
+ help="Available commands",
405
+ )
406
+
407
+ # info command
408
+ parser_info = subparsers.add_parser(
409
+ "info",
410
+ help="Display information about the toolchain installation",
411
+ )
412
+ parser_info.set_defaults(func=cmd_info)
413
+
414
+ # version command
415
+ parser_version = subparsers.add_parser(
416
+ "version",
417
+ help="Display version of a specific tool",
418
+ )
419
+ parser_version.add_argument(
420
+ "tool",
421
+ help="Tool name (e.g., clang, clang++, lld)",
422
+ )
423
+ parser_version.set_defaults(func=cmd_version)
424
+
425
+ # list-tools command
426
+ parser_list = subparsers.add_parser(
427
+ "list-tools",
428
+ help="List all available wrapper tools",
429
+ )
430
+ parser_list.set_defaults(func=cmd_list_tools)
431
+
432
+ # path command
433
+ parser_path = subparsers.add_parser(
434
+ "path",
435
+ help="Display the path to the binary directory or a specific tool",
436
+ )
437
+ parser_path.add_argument(
438
+ "tool",
439
+ nargs="?",
440
+ help="Tool name (optional, prints binary directory if not specified)",
441
+ )
442
+ parser_path.set_defaults(func=cmd_path)
443
+
444
+ # package-version command
445
+ parser_pkg_version = subparsers.add_parser(
446
+ "package-version",
447
+ help="Display the package version and target LLVM version",
448
+ )
449
+ parser_pkg_version.add_argument(
450
+ "-v",
451
+ "--verbose",
452
+ action="store_true",
453
+ help="Show detailed version information including installed LLVM version",
454
+ )
455
+ parser_pkg_version.set_defaults(func=cmd_package_version)
456
+
457
+ # test command
458
+ parser_test = subparsers.add_parser(
459
+ "test",
460
+ help="Run diagnostic tests to verify the toolchain installation",
461
+ )
462
+ parser_test.set_defaults(func=cmd_test)
463
+
464
+ # Parse arguments
465
+ args = parser.parse_args()
466
+
467
+ # If no command specified, show help
468
+ if not args.command:
469
+ parser.print_help()
470
+ return 0
471
+
472
+ # Execute command
473
+ return args.func(args)
474
+
475
+
476
+ def test_main() -> int:
477
+ """
478
+ Standalone entry point for the test command.
479
+
480
+ This allows running: clang-tool-chain-test
481
+ """
482
+ import argparse
483
+
484
+ args = argparse.Namespace() # Empty namespace
485
+ return cmd_test(args)
486
+
487
+
488
+ def sccache_main() -> int:
489
+ """
490
+ Entry point for direct sccache passthrough command.
491
+
492
+ This command allows users to run sccache directly for commands like:
493
+ - clang-tool-chain-sccache --show-stats
494
+ - clang-tool-chain-sccache --zero-stats
495
+ - clang-tool-chain-sccache --start-server
496
+ - clang-tool-chain-sccache --stop-server
497
+
498
+ If sccache is not found in PATH, automatically uses iso-env to run it in an isolated environment.
499
+ """
500
+ args = sys.argv[1:]
501
+ return sccache_runner.run_sccache(args)
502
+
503
+
504
+ def sccache_c_main() -> int:
505
+ """
506
+ Entry point for sccache + clang C compiler wrapper.
507
+
508
+ This command wraps the clang C compiler with sccache for compilation caching.
509
+ If sccache is not found in PATH, automatically uses iso-env to run it in an isolated environment.
510
+ """
511
+ args = sys.argv[1:]
512
+
513
+ # Find the clang binary from clang-tool-chain
514
+ try:
515
+ clang_path = wrapper.find_tool_binary("clang")
516
+ except RuntimeError as e:
517
+ print("=" * 70, file=sys.stderr)
518
+ print("ERROR: Failed to locate clang binary", file=sys.stderr)
519
+ print("=" * 70, file=sys.stderr)
520
+ print(file=sys.stderr)
521
+ print(str(e), file=sys.stderr)
522
+ print(file=sys.stderr)
523
+ print("=" * 70, file=sys.stderr)
524
+ return 1
525
+
526
+ return sccache_runner.run_sccache_with_compiler(str(clang_path), args)
527
+
528
+
529
+ def sccache_cpp_main() -> int:
530
+ """
531
+ Entry point for sccache + clang++ C++ compiler wrapper.
532
+
533
+ This command wraps the clang++ C++ compiler with sccache for compilation caching.
534
+ If sccache is not found in PATH, automatically uses iso-env to run it in an isolated environment.
535
+ """
536
+ args = sys.argv[1:]
537
+
538
+ # Find the clang++ binary from clang-tool-chain
539
+ try:
540
+ clang_cpp_path = wrapper.find_tool_binary("clang++")
541
+ except RuntimeError as e:
542
+ print("=" * 70, file=sys.stderr)
543
+ print("ERROR: Failed to locate clang++ binary", file=sys.stderr)
544
+ print("=" * 70, file=sys.stderr)
545
+ print(file=sys.stderr)
546
+ print(str(e), file=sys.stderr)
547
+ print(file=sys.stderr)
548
+ print("=" * 70, file=sys.stderr)
549
+ return 1
550
+
551
+ return sccache_runner.run_sccache_with_compiler(str(clang_cpp_path), args)
552
+
553
+
554
+ def sccache_c_msvc_main() -> NoReturn:
555
+ """
556
+ Entry point for sccache + clang C compiler wrapper with MSVC ABI.
557
+
558
+ This command wraps the clang C compiler with sccache for compilation caching,
559
+ using MSVC ABI target on Windows.
560
+ """
561
+ wrapper.sccache_clang_main(use_msvc=True)
562
+
563
+
564
+ def sccache_cpp_msvc_main() -> NoReturn:
565
+ """
566
+ Entry point for sccache + clang++ C++ compiler wrapper with MSVC ABI.
567
+
568
+ This command wraps the clang++ C++ compiler with sccache for compilation caching,
569
+ using MSVC ABI target on Windows.
570
+ """
571
+ wrapper.sccache_clang_cpp_main(use_msvc=True)
572
+
573
+
574
+ if __name__ == "__main__":
575
+ sys.exit(main())