clang-tool-chain 1.0.43__py3-none-any.whl → 1.0.45__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.
- clang_tool_chain/__init__.py +31 -0
- clang_tool_chain/__version__.py +1 -1
- clang_tool_chain/deployment/dll_deployer.py +16 -23
- clang_tool_chain/deployment/dylib_deployer.py +8 -8
- clang_tool_chain/deployment/so_deployer.py +11 -5
- clang_tool_chain/env_utils.py +136 -0
- clang_tool_chain/execution/__init__.py +6 -1
- clang_tool_chain/execution/arg_transformers.py +52 -22
- clang_tool_chain/execution/build.py +10 -4
- clang_tool_chain/execution/build_pipeline.py +7 -2
- clang_tool_chain/execution/sanitizer_env.py +77 -12
- clang_tool_chain/linker/lld.py +63 -26
- clang_tool_chain/wrapper.py +13 -0
- {clang_tool_chain-1.0.43.dist-info → clang_tool_chain-1.0.45.dist-info}/METADATA +56 -8
- {clang_tool_chain-1.0.43.dist-info → clang_tool_chain-1.0.45.dist-info}/RECORD +18 -17
- {clang_tool_chain-1.0.43.dist-info → clang_tool_chain-1.0.45.dist-info}/WHEEL +0 -0
- {clang_tool_chain-1.0.43.dist-info → clang_tool_chain-1.0.45.dist-info}/entry_points.txt +0 -0
- {clang_tool_chain-1.0.43.dist-info → clang_tool_chain-1.0.45.dist-info}/licenses/LICENSE +0 -0
clang_tool_chain/__init__.py
CHANGED
|
@@ -1 +1,32 @@
|
|
|
1
1
|
__version__ = "1.0.30"
|
|
2
|
+
|
|
3
|
+
# Re-export commonly used functions for convenient access
|
|
4
|
+
from clang_tool_chain.env_utils import (
|
|
5
|
+
CONTROLLABLE_FEATURES,
|
|
6
|
+
get_disabled_features,
|
|
7
|
+
is_auto_disabled,
|
|
8
|
+
is_feature_disabled,
|
|
9
|
+
)
|
|
10
|
+
from clang_tool_chain.execution.sanitizer_env import (
|
|
11
|
+
detect_sanitizers_from_flags,
|
|
12
|
+
get_symbolizer_path,
|
|
13
|
+
prepare_sanitizer_environment,
|
|
14
|
+
)
|
|
15
|
+
from clang_tool_chain.platform import find_tool_binary, get_platform_binary_dir, get_platform_info
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"__version__",
|
|
19
|
+
# Platform utilities
|
|
20
|
+
"get_platform_info",
|
|
21
|
+
"get_platform_binary_dir",
|
|
22
|
+
"find_tool_binary",
|
|
23
|
+
# Environment utilities
|
|
24
|
+
"is_feature_disabled",
|
|
25
|
+
"is_auto_disabled",
|
|
26
|
+
"get_disabled_features",
|
|
27
|
+
"CONTROLLABLE_FEATURES",
|
|
28
|
+
# Sanitizer environment
|
|
29
|
+
"prepare_sanitizer_environment",
|
|
30
|
+
"get_symbolizer_path",
|
|
31
|
+
"detect_sanitizers_from_flags",
|
|
32
|
+
]
|
clang_tool_chain/__version__.py
CHANGED
|
@@ -12,6 +12,7 @@ import re
|
|
|
12
12
|
import subprocess
|
|
13
13
|
from pathlib import Path
|
|
14
14
|
|
|
15
|
+
from clang_tool_chain.env_utils import is_feature_disabled
|
|
15
16
|
from clang_tool_chain.interrupt_utils import handle_keyboard_interrupt_properly
|
|
16
17
|
|
|
17
18
|
from .base_deployer import BaseLibraryDeployer
|
|
@@ -510,9 +511,10 @@ def post_link_dll_deployment(output_exe_path: Path, platform_name: str, use_gnu_
|
|
|
510
511
|
None
|
|
511
512
|
|
|
512
513
|
Environment Variables:
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
514
|
+
CLANG_TOOL_CHAIN_NO_DEPLOY_LIBS: Set to "1" to disable all library deployment
|
|
515
|
+
CLANG_TOOL_CHAIN_NO_DEPLOY_SHARED_LIB: Set to "1" to disable deployment for shared library outputs only
|
|
516
|
+
CLANG_TOOL_CHAIN_NO_AUTO: Set to "1" to disable all automatic features
|
|
517
|
+
CLANG_TOOL_CHAIN_LIB_DEPLOY_VERBOSE: Set to "1" for verbose logging
|
|
516
518
|
|
|
517
519
|
Examples:
|
|
518
520
|
>>> post_link_dll_deployment(Path("test.exe"), "win", True)
|
|
@@ -520,13 +522,12 @@ def post_link_dll_deployment(output_exe_path: Path, platform_name: str, use_gnu_
|
|
|
520
522
|
>>> post_link_dll_deployment(Path("mylib.dll"), "win", True)
|
|
521
523
|
# Deploys runtime DLLs alongside the shared library
|
|
522
524
|
"""
|
|
523
|
-
# Check opt-out environment variable
|
|
524
|
-
if
|
|
525
|
-
logger.debug("DLL deployment disabled via CLANG_TOOL_CHAIN_NO_DEPLOY_DLLS")
|
|
525
|
+
# Check opt-out environment variable (via NO_DEPLOY_LIBS or NO_AUTO)
|
|
526
|
+
if is_feature_disabled("DEPLOY_LIBS"):
|
|
526
527
|
return
|
|
527
528
|
|
|
528
529
|
# Enable verbose logging if requested
|
|
529
|
-
if os.environ.get("
|
|
530
|
+
if os.environ.get("CLANG_TOOL_CHAIN_LIB_DEPLOY_VERBOSE") == "1":
|
|
530
531
|
logger.setLevel(logging.DEBUG)
|
|
531
532
|
|
|
532
533
|
# Guard: only deploy on Windows
|
|
@@ -542,9 +543,8 @@ def post_link_dll_deployment(output_exe_path: Path, platform_name: str, use_gnu_
|
|
|
542
543
|
# Guard: only deploy for .exe and .dll files
|
|
543
544
|
suffix = output_exe_path.suffix.lower()
|
|
544
545
|
if suffix == ".dll":
|
|
545
|
-
# Check for
|
|
546
|
-
if
|
|
547
|
-
logger.debug("DLL deployment for .dll outputs disabled via CLANG_TOOL_CHAIN_NO_DEPLOY_DLLS_FOR_DLLS")
|
|
546
|
+
# Check for shared library-specific opt-out (via NO_DEPLOY_SHARED_LIB or NO_AUTO)
|
|
547
|
+
if is_feature_disabled("DEPLOY_SHARED_LIB"):
|
|
548
548
|
return
|
|
549
549
|
elif suffix != ".exe":
|
|
550
550
|
logger.debug(f"DLL deployment skipped: not .exe or .dll file (suffix={output_exe_path.suffix})")
|
|
@@ -596,10 +596,9 @@ def post_link_dependency_deployment(output_path: Path, platform_name: str, use_g
|
|
|
596
596
|
None
|
|
597
597
|
|
|
598
598
|
Environment Variables:
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
CLANG_TOOL_CHAIN_LIB_DEPLOY_VERBOSE: Set to "1" for verbose logging (alias)
|
|
599
|
+
CLANG_TOOL_CHAIN_NO_DEPLOY_LIBS: Set to "1" to disable all library deployment
|
|
600
|
+
CLANG_TOOL_CHAIN_NO_AUTO: Set to "1" to disable all automatic features
|
|
601
|
+
CLANG_TOOL_CHAIN_LIB_DEPLOY_VERBOSE: Set to "1" for verbose logging
|
|
603
602
|
|
|
604
603
|
Examples:
|
|
605
604
|
>>> post_link_dependency_deployment(Path("mylib.dll"), "win", True)
|
|
@@ -609,17 +608,11 @@ def post_link_dependency_deployment(output_path: Path, platform_name: str, use_g
|
|
|
609
608
|
>>> post_link_dependency_deployment(Path("mylib.dylib"), "darwin", False)
|
|
610
609
|
# Deploys libunwind.1.dylib, libc++.1.dylib, etc. to mylib.dylib directory
|
|
611
610
|
"""
|
|
612
|
-
# Check opt-out environment variables (
|
|
613
|
-
if
|
|
614
|
-
logger.debug("Dependency deployment disabled via CLANG_TOOL_CHAIN_NO_DEPLOY_DLLS")
|
|
615
|
-
return
|
|
616
|
-
if os.environ.get("CLANG_TOOL_CHAIN_NO_DEPLOY_LIBS") == "1":
|
|
617
|
-
logger.debug("Dependency deployment disabled via CLANG_TOOL_CHAIN_NO_DEPLOY_LIBS")
|
|
611
|
+
# Check opt-out environment variables (NO_DEPLOY_LIBS or NO_AUTO)
|
|
612
|
+
if is_feature_disabled("DEPLOY_LIBS"):
|
|
618
613
|
return
|
|
619
614
|
|
|
620
|
-
# Enable verbose logging if requested
|
|
621
|
-
if os.environ.get("CLANG_TOOL_CHAIN_DLL_DEPLOY_VERBOSE") == "1":
|
|
622
|
-
logger.setLevel(logging.DEBUG)
|
|
615
|
+
# Enable verbose logging if requested
|
|
623
616
|
if os.environ.get("CLANG_TOOL_CHAIN_LIB_DEPLOY_VERBOSE") == "1":
|
|
624
617
|
logger.setLevel(logging.DEBUG)
|
|
625
618
|
|
|
@@ -18,11 +18,12 @@ Example:
|
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
20
|
import logging
|
|
21
|
-
import os
|
|
22
21
|
import re
|
|
23
22
|
import subprocess
|
|
24
23
|
from pathlib import Path
|
|
25
24
|
|
|
25
|
+
from clang_tool_chain.env_utils import is_feature_disabled
|
|
26
|
+
|
|
26
27
|
from .base_deployer import BaseLibraryDeployer
|
|
27
28
|
|
|
28
29
|
logger = logging.getLogger(__name__)
|
|
@@ -499,7 +500,8 @@ def post_link_dylib_deployment(output_path: Path, arch: str = "x86_64") -> int:
|
|
|
499
500
|
|
|
500
501
|
Environment variables:
|
|
501
502
|
- CLANG_TOOL_CHAIN_NO_DEPLOY_LIBS=1 - Disable all library deployment
|
|
502
|
-
-
|
|
503
|
+
- CLANG_TOOL_CHAIN_NO_DEPLOY_SHARED_LIB=1 - Disable deployment for shared library outputs
|
|
504
|
+
- CLANG_TOOL_CHAIN_NO_AUTO=1 - Disable all automatic features
|
|
503
505
|
|
|
504
506
|
Args:
|
|
505
507
|
output_path: Path to executable or shared library
|
|
@@ -512,14 +514,12 @@ def post_link_dylib_deployment(output_path: Path, arch: str = "x86_64") -> int:
|
|
|
512
514
|
>>> # Called automatically after linking
|
|
513
515
|
>>> count = post_link_dylib_deployment(Path("program"), arch="arm64")
|
|
514
516
|
"""
|
|
515
|
-
# Check
|
|
516
|
-
if
|
|
517
|
-
logger.debug("dylib deployment disabled (CLANG_TOOL_CHAIN_NO_DEPLOY_LIBS=1)")
|
|
517
|
+
# Check environment variables (NO_DEPLOY_LIBS or NO_AUTO)
|
|
518
|
+
if is_feature_disabled("DEPLOY_LIBS"):
|
|
518
519
|
return 0
|
|
519
520
|
|
|
520
|
-
# Check
|
|
521
|
-
if
|
|
522
|
-
logger.debug("dylib deployment disabled (CLANG_TOOL_CHAIN_NO_DEPLOY_DYLIBS=1)")
|
|
521
|
+
# Check if output is a shared library (.dylib) - if so, check NO_DEPLOY_SHARED_LIB
|
|
522
|
+
if output_path.suffix == ".dylib" and is_feature_disabled("DEPLOY_SHARED_LIB"):
|
|
523
523
|
return 0
|
|
524
524
|
|
|
525
525
|
# Check if output is executable or .dylib
|
|
@@ -12,6 +12,7 @@ import re
|
|
|
12
12
|
import subprocess
|
|
13
13
|
from pathlib import Path
|
|
14
14
|
|
|
15
|
+
from clang_tool_chain.env_utils import is_feature_disabled
|
|
15
16
|
from clang_tool_chain.interrupt_utils import handle_keyboard_interrupt_properly
|
|
16
17
|
|
|
17
18
|
from .base_deployer import BaseLibraryDeployer
|
|
@@ -309,14 +310,19 @@ def post_link_so_deployment(
|
|
|
309
310
|
|
|
310
311
|
Returns:
|
|
311
312
|
Number of .so files deployed
|
|
313
|
+
|
|
314
|
+
Environment Variables:
|
|
315
|
+
CLANG_TOOL_CHAIN_NO_DEPLOY_LIBS: Set to "1" to disable all library deployment
|
|
316
|
+
CLANG_TOOL_CHAIN_NO_DEPLOY_SHARED_LIB: Set to "1" to disable deployment for shared library outputs
|
|
317
|
+
CLANG_TOOL_CHAIN_NO_AUTO: Set to "1" to disable all automatic features
|
|
312
318
|
"""
|
|
313
|
-
# Check environment variables
|
|
314
|
-
if
|
|
315
|
-
logger.debug("Linux .so deployment disabled by CLANG_TOOL_CHAIN_NO_DEPLOY_LIBS")
|
|
319
|
+
# Check environment variables (NO_DEPLOY_LIBS or NO_AUTO)
|
|
320
|
+
if is_feature_disabled("DEPLOY_LIBS"):
|
|
316
321
|
return 0
|
|
317
322
|
|
|
318
|
-
if
|
|
319
|
-
|
|
323
|
+
# Check if output is a shared library (.so) - if so, check NO_DEPLOY_SHARED_LIB
|
|
324
|
+
is_shared_lib = output_path.suffix == ".so" or ".so." in output_path.name
|
|
325
|
+
if is_shared_lib and is_feature_disabled("DEPLOY_SHARED_LIB"):
|
|
320
326
|
return 0
|
|
321
327
|
|
|
322
328
|
# Check if output is a deployable binary
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Environment variable utilities for clang-tool-chain.
|
|
3
|
+
|
|
4
|
+
This module provides centralized handling of environment variable checks,
|
|
5
|
+
including support for the aggregate CLANG_TOOL_CHAIN_NO_AUTO variable
|
|
6
|
+
that disables all automatic features.
|
|
7
|
+
|
|
8
|
+
Example:
|
|
9
|
+
>>> from clang_tool_chain.env_utils import is_feature_disabled
|
|
10
|
+
>>> if is_feature_disabled("DIRECTIVES"):
|
|
11
|
+
... # Skip directive parsing
|
|
12
|
+
... pass
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import logging
|
|
16
|
+
import os
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
# Truthy values for boolean environment variables
|
|
21
|
+
_TRUTHY_VALUES = ("1", "true", "yes")
|
|
22
|
+
|
|
23
|
+
# All features that can be disabled via NO_AUTO
|
|
24
|
+
# Maps feature name to the specific environment variable suffix
|
|
25
|
+
CONTROLLABLE_FEATURES = {
|
|
26
|
+
"DIRECTIVES": "Inlined build directives (@link, @std, @cflags)",
|
|
27
|
+
"SHARED_ASAN": "Automatic -shared-libasan injection (Linux)",
|
|
28
|
+
"SANITIZER_ENV": "Automatic ASAN_OPTIONS/LSAN_OPTIONS injection",
|
|
29
|
+
"RPATH": "Automatic rpath injection for library loading",
|
|
30
|
+
"SYSROOT": "Automatic macOS SDK detection (-isysroot)",
|
|
31
|
+
"DEPLOY_LIBS": "Cross-platform library deployment (all outputs)",
|
|
32
|
+
"DEPLOY_SHARED_LIB": "Library deployment for shared library outputs only (.dll, .so, .dylib)",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _is_truthy(value: str | None) -> bool:
|
|
37
|
+
"""Check if an environment variable value is truthy."""
|
|
38
|
+
if value is None:
|
|
39
|
+
return False
|
|
40
|
+
return value.lower() in _TRUTHY_VALUES
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def is_auto_disabled() -> bool:
|
|
44
|
+
"""
|
|
45
|
+
Check if all automatic features are disabled via CLANG_TOOL_CHAIN_NO_AUTO.
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
True if CLANG_TOOL_CHAIN_NO_AUTO is set to a truthy value.
|
|
49
|
+
|
|
50
|
+
Example:
|
|
51
|
+
>>> os.environ["CLANG_TOOL_CHAIN_NO_AUTO"] = "1"
|
|
52
|
+
>>> is_auto_disabled()
|
|
53
|
+
True
|
|
54
|
+
"""
|
|
55
|
+
return _is_truthy(os.environ.get("CLANG_TOOL_CHAIN_NO_AUTO"))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def is_feature_disabled(feature: str) -> bool:
|
|
59
|
+
"""
|
|
60
|
+
Check if a specific feature is disabled.
|
|
61
|
+
|
|
62
|
+
A feature is disabled if either:
|
|
63
|
+
1. The aggregate CLANG_TOOL_CHAIN_NO_AUTO=1 is set, OR
|
|
64
|
+
2. The specific CLANG_TOOL_CHAIN_NO_{feature}=1 is set
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
feature: The feature name (e.g., "DIRECTIVES", "SHARED_ASAN", "DEPLOY_LIBS").
|
|
68
|
+
Should match one of the keys in CONTROLLABLE_FEATURES.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
True if the feature is disabled, False otherwise.
|
|
72
|
+
|
|
73
|
+
Example:
|
|
74
|
+
>>> os.environ["CLANG_TOOL_CHAIN_NO_DIRECTIVES"] = "1"
|
|
75
|
+
>>> is_feature_disabled("DIRECTIVES")
|
|
76
|
+
True
|
|
77
|
+
|
|
78
|
+
>>> os.environ["CLANG_TOOL_CHAIN_NO_AUTO"] = "1"
|
|
79
|
+
>>> is_feature_disabled("SHARED_ASAN") # Disabled by NO_AUTO
|
|
80
|
+
True
|
|
81
|
+
"""
|
|
82
|
+
# Check the aggregate NO_AUTO variable first
|
|
83
|
+
if is_auto_disabled():
|
|
84
|
+
logger.debug(f"Feature {feature} disabled via CLANG_TOOL_CHAIN_NO_AUTO=1")
|
|
85
|
+
return True
|
|
86
|
+
|
|
87
|
+
# Check the specific feature variable
|
|
88
|
+
var_name = f"CLANG_TOOL_CHAIN_NO_{feature}"
|
|
89
|
+
if _is_truthy(os.environ.get(var_name)):
|
|
90
|
+
logger.debug(f"Feature {feature} disabled via {var_name}=1")
|
|
91
|
+
return True
|
|
92
|
+
|
|
93
|
+
return False
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def get_disabled_features() -> list[str]:
|
|
97
|
+
"""
|
|
98
|
+
Get a list of all currently disabled features.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
List of feature names that are disabled.
|
|
102
|
+
|
|
103
|
+
Example:
|
|
104
|
+
>>> os.environ["CLANG_TOOL_CHAIN_NO_AUTO"] = "1"
|
|
105
|
+
>>> get_disabled_features()
|
|
106
|
+
['DIRECTIVES', 'SHARED_ASAN', 'SANITIZER_ENV', ...]
|
|
107
|
+
"""
|
|
108
|
+
disabled = []
|
|
109
|
+
|
|
110
|
+
# If NO_AUTO is set, all features are disabled
|
|
111
|
+
if is_auto_disabled():
|
|
112
|
+
return list(CONTROLLABLE_FEATURES.keys())
|
|
113
|
+
|
|
114
|
+
# Check each feature individually
|
|
115
|
+
for feature in CONTROLLABLE_FEATURES:
|
|
116
|
+
var_name = f"CLANG_TOOL_CHAIN_NO_{feature}"
|
|
117
|
+
if _is_truthy(os.environ.get(var_name)):
|
|
118
|
+
disabled.append(feature)
|
|
119
|
+
|
|
120
|
+
return disabled
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def log_disabled_features_summary() -> None:
|
|
124
|
+
"""
|
|
125
|
+
Log a summary of disabled features (useful for debugging).
|
|
126
|
+
|
|
127
|
+
Only logs if at least one feature is disabled.
|
|
128
|
+
"""
|
|
129
|
+
disabled = get_disabled_features()
|
|
130
|
+
if not disabled:
|
|
131
|
+
return
|
|
132
|
+
|
|
133
|
+
if is_auto_disabled():
|
|
134
|
+
logger.info("All automatic features disabled via CLANG_TOOL_CHAIN_NO_AUTO=1")
|
|
135
|
+
else:
|
|
136
|
+
logger.info(f"Disabled features: {', '.join(disabled)}")
|
|
@@ -10,7 +10,11 @@ This package provides:
|
|
|
10
10
|
|
|
11
11
|
from clang_tool_chain.execution.build import build_main, build_run_main
|
|
12
12
|
from clang_tool_chain.execution.core import execute_tool, run_tool, sccache_clang_cpp_main, sccache_clang_main
|
|
13
|
-
from clang_tool_chain.execution.sanitizer_env import
|
|
13
|
+
from clang_tool_chain.execution.sanitizer_env import (
|
|
14
|
+
detect_sanitizers_from_flags,
|
|
15
|
+
get_symbolizer_path,
|
|
16
|
+
prepare_sanitizer_environment,
|
|
17
|
+
)
|
|
14
18
|
|
|
15
19
|
__all__ = [
|
|
16
20
|
# Core execution
|
|
@@ -25,4 +29,5 @@ __all__ = [
|
|
|
25
29
|
# Sanitizer utilities
|
|
26
30
|
"prepare_sanitizer_environment",
|
|
27
31
|
"detect_sanitizers_from_flags",
|
|
32
|
+
"get_symbolizer_path",
|
|
28
33
|
]
|
|
@@ -35,6 +35,8 @@ from dataclasses import dataclass
|
|
|
35
35
|
from pathlib import Path
|
|
36
36
|
from typing import TYPE_CHECKING
|
|
37
37
|
|
|
38
|
+
from clang_tool_chain.env_utils import is_feature_disabled
|
|
39
|
+
|
|
38
40
|
if TYPE_CHECKING:
|
|
39
41
|
from clang_tool_chain.directives.parser import ParsedDirectives
|
|
40
42
|
|
|
@@ -111,6 +113,7 @@ class DirectivesTransformer(ArgumentTransformer):
|
|
|
111
113
|
|
|
112
114
|
Environment Variables:
|
|
113
115
|
CLANG_TOOL_CHAIN_NO_DIRECTIVES: Set to '1' to disable directive parsing
|
|
116
|
+
CLANG_TOOL_CHAIN_NO_AUTO: Set to '1' to disable all automatic features
|
|
114
117
|
CLANG_TOOL_CHAIN_DIRECTIVE_VERBOSE: Set to '1' to enable debug logging
|
|
115
118
|
"""
|
|
116
119
|
|
|
@@ -119,9 +122,8 @@ class DirectivesTransformer(ArgumentTransformer):
|
|
|
119
122
|
|
|
120
123
|
def transform(self, args: list[str], context: ToolContext) -> list[str]:
|
|
121
124
|
"""Add arguments from inlined build directives in source files."""
|
|
122
|
-
# Check if directives are disabled
|
|
123
|
-
if
|
|
124
|
-
logger.debug("Directives disabled via CLANG_TOOL_CHAIN_NO_DIRECTIVES=1")
|
|
125
|
+
# Check if directives are disabled (via NO_DIRECTIVES or NO_AUTO)
|
|
126
|
+
if is_feature_disabled("DIRECTIVES"):
|
|
125
127
|
return args
|
|
126
128
|
|
|
127
129
|
# Only apply to clang/clang++ compilation commands
|
|
@@ -209,9 +211,10 @@ class LLDLinkerTransformer(ArgumentTransformer):
|
|
|
209
211
|
|
|
210
212
|
Priority: 200 (runs after SDK but before ABI)
|
|
211
213
|
|
|
212
|
-
This transformer adds
|
|
213
|
-
|
|
214
|
-
-
|
|
214
|
+
This transformer adds -fuse-ld=lld linker flag. The clang driver
|
|
215
|
+
automatically dispatches to the correct linker binary:
|
|
216
|
+
- macOS: ld64.lld (Mach-O linker)
|
|
217
|
+
- Linux: ld.lld (ELF linker)
|
|
215
218
|
|
|
216
219
|
It also translates GNU ld flags to ld64.lld equivalents on macOS:
|
|
217
220
|
- --no-undefined -> -undefined error
|
|
@@ -293,20 +296,28 @@ class ASANRuntimeTransformer(ArgumentTransformer):
|
|
|
293
296
|
This transformer ensures proper ASAN runtime linking on Linux:
|
|
294
297
|
- Detects -fsanitize=address flag
|
|
295
298
|
- Adds -shared-libasan to use shared runtime library
|
|
299
|
+
- Adds -Wl,--allow-shlib-undefined when building shared libraries with ASAN
|
|
296
300
|
- Prevents undefined symbol errors during linking
|
|
297
301
|
|
|
298
302
|
The shared runtime library (libclang_rt.asan.so) contains the full
|
|
299
303
|
ASAN implementation, while the static wrapper library only contains stubs.
|
|
300
304
|
|
|
305
|
+
When building shared libraries with sanitizers, the library may have undefined
|
|
306
|
+
symbols that will be provided by the sanitizer runtime when loaded. LLD by
|
|
307
|
+
default enforces no undefined symbols, so we need to allow them explicitly.
|
|
308
|
+
|
|
301
309
|
Environment Variables:
|
|
302
310
|
CLANG_TOOL_CHAIN_NO_SHARED_ASAN: Set to '1' to disable shared ASAN
|
|
311
|
+
CLANG_TOOL_CHAIN_NO_AUTO: Set to '1' to disable all automatic features
|
|
303
312
|
"""
|
|
304
313
|
|
|
305
314
|
def priority(self) -> int:
|
|
306
315
|
return 250
|
|
307
316
|
|
|
308
317
|
def transform(self, args: list[str], context: ToolContext) -> list[str]:
|
|
309
|
-
"""Add -shared-libasan when using ASAN on Linux."""
|
|
318
|
+
"""Add -shared-libasan and --allow-shlib-undefined when using ASAN on Linux."""
|
|
319
|
+
import sys
|
|
320
|
+
|
|
310
321
|
# Only applies to Linux clang/clang++
|
|
311
322
|
if context.platform_name != "linux" or context.tool_name not in ("clang", "clang++"):
|
|
312
323
|
return args
|
|
@@ -316,19 +327,38 @@ class ASANRuntimeTransformer(ArgumentTransformer):
|
|
|
316
327
|
if not has_asan:
|
|
317
328
|
return args
|
|
318
329
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
330
|
+
result = list(args)
|
|
331
|
+
injected_flags = []
|
|
332
|
+
|
|
333
|
+
# Check if user disabled shared ASAN (via NO_SHARED_ASAN or NO_AUTO)
|
|
334
|
+
# Also check if -shared-libasan already present
|
|
335
|
+
if not is_feature_disabled("SHARED_ASAN") and "-shared-libasan" not in args:
|
|
336
|
+
# Add -shared-libasan to use shared runtime library
|
|
337
|
+
# This prevents undefined symbol errors during linking
|
|
338
|
+
logger.info("Adding -shared-libasan for ASAN runtime linking on Linux")
|
|
339
|
+
result = ["-shared-libasan"] + result
|
|
340
|
+
injected_flags.append("-shared-libasan")
|
|
341
|
+
|
|
342
|
+
# Check if building a shared library with ASAN
|
|
343
|
+
# Shared libraries need to allow undefined symbols that will be provided
|
|
344
|
+
# by the sanitizer runtime when the runner loads them
|
|
345
|
+
is_shared_lib = "-shared" in args
|
|
346
|
+
if is_shared_lib:
|
|
347
|
+
# Check if --allow-shlib-undefined already present
|
|
348
|
+
has_allow_shlib_undefined = any("--allow-shlib-undefined" in arg for arg in args)
|
|
349
|
+
if not has_allow_shlib_undefined:
|
|
350
|
+
logger.info("Adding -Wl,--allow-shlib-undefined for shared library with ASAN")
|
|
351
|
+
result = ["-Wl,--allow-shlib-undefined"] + result
|
|
352
|
+
injected_flags.append("-Wl,--allow-shlib-undefined")
|
|
353
|
+
|
|
354
|
+
# Warn on stderr if we injected flags
|
|
355
|
+
if injected_flags:
|
|
356
|
+
print(
|
|
357
|
+
f"clang-tool-chain: note: automatically injected sanitizer flags: {' '.join(injected_flags)}",
|
|
358
|
+
file=sys.stderr,
|
|
359
|
+
)
|
|
323
360
|
|
|
324
|
-
|
|
325
|
-
if "-shared-libasan" in args:
|
|
326
|
-
return args
|
|
327
|
-
|
|
328
|
-
# Add -shared-libasan to use shared runtime library
|
|
329
|
-
# This prevents undefined symbol errors during linking
|
|
330
|
-
logger.info("Adding -shared-libasan for ASAN runtime linking on Linux")
|
|
331
|
-
return ["-shared-libasan"] + args
|
|
361
|
+
return result
|
|
332
362
|
|
|
333
363
|
|
|
334
364
|
class RPathTransformer(ArgumentTransformer):
|
|
@@ -348,6 +378,7 @@ class RPathTransformer(ArgumentTransformer):
|
|
|
348
378
|
|
|
349
379
|
Environment Variables:
|
|
350
380
|
CLANG_TOOL_CHAIN_NO_RPATH: Set to '1' to disable automatic rpath injection
|
|
381
|
+
CLANG_TOOL_CHAIN_NO_AUTO: Set to '1' to disable all automatic features
|
|
351
382
|
"""
|
|
352
383
|
|
|
353
384
|
def priority(self) -> int:
|
|
@@ -371,9 +402,8 @@ class RPathTransformer(ArgumentTransformer):
|
|
|
371
402
|
if not has_deploy_flag and not deploy_from_env:
|
|
372
403
|
return args
|
|
373
404
|
|
|
374
|
-
# Check if user disabled rpath
|
|
375
|
-
if
|
|
376
|
-
logger.debug("Rpath injection disabled via CLANG_TOOL_CHAIN_NO_RPATH=1")
|
|
405
|
+
# Check if user disabled rpath (via NO_RPATH or NO_AUTO)
|
|
406
|
+
if is_feature_disabled("RPATH"):
|
|
377
407
|
return args
|
|
378
408
|
|
|
379
409
|
# Check if rpath already present
|
|
@@ -26,6 +26,7 @@ from typing import NoReturn
|
|
|
26
26
|
|
|
27
27
|
from clang_tool_chain.cli_parsers import parse_build_args, parse_build_run_args
|
|
28
28
|
from clang_tool_chain.directives import DirectiveParser
|
|
29
|
+
from clang_tool_chain.env_utils import is_feature_disabled
|
|
29
30
|
from clang_tool_chain.execution.core import execute_tool
|
|
30
31
|
from clang_tool_chain.interrupt_utils import handle_keyboard_interrupt_properly
|
|
31
32
|
from clang_tool_chain.platform import get_platform_info
|
|
@@ -47,9 +48,13 @@ def _get_directive_args(source_path: Path) -> list[str]:
|
|
|
47
48
|
|
|
48
49
|
Returns:
|
|
49
50
|
List of compiler/linker arguments derived from directives
|
|
51
|
+
|
|
52
|
+
Environment Variables:
|
|
53
|
+
CLANG_TOOL_CHAIN_NO_DIRECTIVES: Set to '1' to disable directive parsing
|
|
54
|
+
CLANG_TOOL_CHAIN_NO_AUTO: Set to '1' to disable all automatic features
|
|
50
55
|
"""
|
|
51
|
-
# Check if directives parsing is disabled via
|
|
52
|
-
if
|
|
56
|
+
# Check if directives parsing is disabled (via NO_DIRECTIVES or NO_AUTO)
|
|
57
|
+
if is_feature_disabled("DIRECTIVES"):
|
|
53
58
|
return []
|
|
54
59
|
|
|
55
60
|
try:
|
|
@@ -106,10 +111,11 @@ def get_directive_args_from_compiler_args(args: list[str]) -> list[str]:
|
|
|
106
111
|
|
|
107
112
|
Environment Variables:
|
|
108
113
|
CLANG_TOOL_CHAIN_NO_DIRECTIVES: Set to '1' to disable directive parsing
|
|
114
|
+
CLANG_TOOL_CHAIN_NO_AUTO: Set to '1' to disable all automatic features
|
|
109
115
|
CLANG_TOOL_CHAIN_DIRECTIVE_VERBOSE: Set to '1' to show parsed directives
|
|
110
116
|
"""
|
|
111
|
-
# Check if directives parsing is disabled via
|
|
112
|
-
if
|
|
117
|
+
# Check if directives parsing is disabled (via NO_DIRECTIVES or NO_AUTO)
|
|
118
|
+
if is_feature_disabled("DIRECTIVES"):
|
|
113
119
|
return []
|
|
114
120
|
|
|
115
121
|
directive_args: list[str] = []
|
|
@@ -24,6 +24,7 @@ from pathlib import Path
|
|
|
24
24
|
from typing import NoReturn
|
|
25
25
|
|
|
26
26
|
from clang_tool_chain.directives import DirectiveParser
|
|
27
|
+
from clang_tool_chain.env_utils import is_feature_disabled
|
|
27
28
|
from clang_tool_chain.execution.core import run_tool
|
|
28
29
|
from clang_tool_chain.interrupt_utils import handle_keyboard_interrupt_properly
|
|
29
30
|
|
|
@@ -109,9 +110,13 @@ def _get_directive_args(source_path: Path) -> list[str]:
|
|
|
109
110
|
|
|
110
111
|
Returns:
|
|
111
112
|
List of compiler/linker arguments derived from directives
|
|
113
|
+
|
|
114
|
+
Environment Variables:
|
|
115
|
+
CLANG_TOOL_CHAIN_NO_DIRECTIVES: Set to '1' to disable directive parsing
|
|
116
|
+
CLANG_TOOL_CHAIN_NO_AUTO: Set to '1' to disable all automatic features
|
|
112
117
|
"""
|
|
113
|
-
# Check if directives parsing is disabled via
|
|
114
|
-
if
|
|
118
|
+
# Check if directives parsing is disabled (via NO_DIRECTIVES or NO_AUTO)
|
|
119
|
+
if is_feature_disabled("DIRECTIVES"):
|
|
115
120
|
return []
|
|
116
121
|
|
|
117
122
|
try:
|
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Sanitizer runtime environment configuration.
|
|
3
3
|
|
|
4
|
-
This module provides automatic injection of ASAN_OPTIONS and
|
|
5
|
-
environment variables to improve stack trace quality
|
|
6
|
-
compiled with Address Sanitizer or Leak Sanitizer.
|
|
4
|
+
This module provides automatic injection of ASAN_OPTIONS, LSAN_OPTIONS, and
|
|
5
|
+
ASAN_SYMBOLIZER_PATH environment variables to improve stack trace quality
|
|
6
|
+
when running executables compiled with Address Sanitizer or Leak Sanitizer.
|
|
7
7
|
|
|
8
8
|
The default options fix <unknown module> entries in stack traces from
|
|
9
9
|
dlopen()'d shared libraries by enabling slow unwinding and symbolization.
|
|
10
|
+
|
|
11
|
+
The symbolizer path is automatically detected from the clang-tool-chain
|
|
12
|
+
installation, enabling proper address-to-symbol resolution without manual
|
|
13
|
+
configuration.
|
|
10
14
|
"""
|
|
11
15
|
|
|
12
16
|
import logging
|
|
13
17
|
import os
|
|
18
|
+
import shutil
|
|
19
|
+
|
|
20
|
+
from clang_tool_chain.env_utils import is_feature_disabled
|
|
14
21
|
|
|
15
22
|
logger = logging.getLogger(__name__)
|
|
16
23
|
|
|
@@ -22,6 +29,46 @@ DEFAULT_ASAN_OPTIONS = "fast_unwind_on_malloc=0:symbolize=1:detect_leaks=1"
|
|
|
22
29
|
DEFAULT_LSAN_OPTIONS = "fast_unwind_on_malloc=0:symbolize=1"
|
|
23
30
|
|
|
24
31
|
|
|
32
|
+
def get_symbolizer_path() -> str | None:
|
|
33
|
+
"""
|
|
34
|
+
Get the path to llvm-symbolizer from the clang-tool-chain installation.
|
|
35
|
+
|
|
36
|
+
This function finds the llvm-symbolizer binary bundled with clang-tool-chain,
|
|
37
|
+
which is required by ASAN/LSAN to convert memory addresses into function names
|
|
38
|
+
and source locations in stack traces.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Absolute path to llvm-symbolizer, or None if not found.
|
|
42
|
+
|
|
43
|
+
Example:
|
|
44
|
+
>>> path = get_symbolizer_path()
|
|
45
|
+
>>> if path:
|
|
46
|
+
... os.environ["ASAN_SYMBOLIZER_PATH"] = path
|
|
47
|
+
|
|
48
|
+
Note:
|
|
49
|
+
Falls back to system PATH if the clang-tool-chain binary is not available.
|
|
50
|
+
This allows the function to work even when clang-tool-chain is not fully
|
|
51
|
+
installed (e.g., during development or in CI environments).
|
|
52
|
+
"""
|
|
53
|
+
# Try to find llvm-symbolizer from clang-tool-chain installation
|
|
54
|
+
try:
|
|
55
|
+
from clang_tool_chain.platform.paths import find_tool_binary
|
|
56
|
+
|
|
57
|
+
symbolizer = find_tool_binary("llvm-symbolizer")
|
|
58
|
+
return str(symbolizer)
|
|
59
|
+
except (ImportError, RuntimeError) as e:
|
|
60
|
+
logger.debug(f"Could not find llvm-symbolizer in clang-tool-chain: {e}")
|
|
61
|
+
|
|
62
|
+
# Fall back to system PATH
|
|
63
|
+
system_symbolizer = shutil.which("llvm-symbolizer")
|
|
64
|
+
if system_symbolizer:
|
|
65
|
+
logger.debug(f"Using system llvm-symbolizer: {system_symbolizer}")
|
|
66
|
+
return system_symbolizer
|
|
67
|
+
|
|
68
|
+
logger.debug("llvm-symbolizer not found in clang-tool-chain or system PATH")
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
|
|
25
72
|
def detect_sanitizers_from_flags(compiler_flags: list[str]) -> tuple[bool, bool]:
|
|
26
73
|
"""
|
|
27
74
|
Detect which sanitizers are enabled from compiler flags.
|
|
@@ -64,12 +111,16 @@ def prepare_sanitizer_environment(
|
|
|
64
111
|
compiler_flags: list[str] | None = None,
|
|
65
112
|
) -> dict[str, str]:
|
|
66
113
|
"""
|
|
67
|
-
Prepare environment with optimal sanitizer options.
|
|
114
|
+
Prepare environment with optimal sanitizer options and symbolizer path.
|
|
68
115
|
|
|
69
|
-
This function injects ASAN_OPTIONS
|
|
70
|
-
if they are not already set by the user AND the
|
|
71
|
-
enabled during compilation. The injected options
|
|
72
|
-
for executables using dlopen()'d shared libraries.
|
|
116
|
+
This function injects ASAN_OPTIONS, LSAN_OPTIONS, and ASAN_SYMBOLIZER_PATH
|
|
117
|
+
environment variables if they are not already set by the user AND the
|
|
118
|
+
corresponding sanitizer was enabled during compilation. The injected options
|
|
119
|
+
improve stack trace quality for executables using dlopen()'d shared libraries.
|
|
120
|
+
|
|
121
|
+
The ASAN_SYMBOLIZER_PATH is automatically detected from the clang-tool-chain
|
|
122
|
+
installation, enabling proper address-to-symbol resolution (function names,
|
|
123
|
+
file paths, line numbers) without manual configuration.
|
|
73
124
|
|
|
74
125
|
Args:
|
|
75
126
|
base_env: Base environment dictionary to modify. If None, uses os.environ.
|
|
@@ -83,20 +134,21 @@ def prepare_sanitizer_environment(
|
|
|
83
134
|
Environment Variables:
|
|
84
135
|
CLANG_TOOL_CHAIN_NO_SANITIZER_ENV: Set to "1", "true", or "yes" to
|
|
85
136
|
disable automatic injection of sanitizer options.
|
|
137
|
+
CLANG_TOOL_CHAIN_NO_AUTO: Set to "1" to disable all automatic features.
|
|
86
138
|
ASAN_OPTIONS: If already set, preserved as-is (user config takes priority).
|
|
87
139
|
LSAN_OPTIONS: If already set, preserved as-is (user config takes priority).
|
|
140
|
+
ASAN_SYMBOLIZER_PATH: If already set, preserved as-is (user config takes priority).
|
|
88
141
|
|
|
89
142
|
Example:
|
|
90
143
|
>>> env = prepare_sanitizer_environment(compiler_flags=["-fsanitize=address"])
|
|
91
|
-
>>> # env now contains ASAN_OPTIONS and
|
|
144
|
+
>>> # env now contains ASAN_OPTIONS, LSAN_OPTIONS, and ASAN_SYMBOLIZER_PATH
|
|
92
145
|
>>> env = prepare_sanitizer_environment(compiler_flags=["-O2"])
|
|
93
146
|
>>> # env unchanged - no sanitizers enabled
|
|
94
147
|
"""
|
|
95
148
|
env = base_env.copy() if base_env is not None else os.environ.copy()
|
|
96
149
|
|
|
97
|
-
# Check if disabled via environment variable
|
|
98
|
-
if
|
|
99
|
-
logger.debug("Sanitizer environment injection disabled via CLANG_TOOL_CHAIN_NO_SANITIZER_ENV")
|
|
150
|
+
# Check if disabled via environment variable (NO_SANITIZER_ENV or NO_AUTO)
|
|
151
|
+
if is_feature_disabled("SANITIZER_ENV"):
|
|
100
152
|
return env
|
|
101
153
|
|
|
102
154
|
# If no compiler flags provided, don't inject anything (safe default)
|
|
@@ -117,4 +169,17 @@ def prepare_sanitizer_environment(
|
|
|
117
169
|
env["LSAN_OPTIONS"] = DEFAULT_LSAN_OPTIONS
|
|
118
170
|
logger.info(f"Injecting LSAN_OPTIONS={DEFAULT_LSAN_OPTIONS}")
|
|
119
171
|
|
|
172
|
+
# Inject ASAN_SYMBOLIZER_PATH if any sanitizer is enabled and not already set
|
|
173
|
+
if (asan_enabled or lsan_enabled) and "ASAN_SYMBOLIZER_PATH" not in env:
|
|
174
|
+
symbolizer_path = get_symbolizer_path()
|
|
175
|
+
if symbolizer_path:
|
|
176
|
+
env["ASAN_SYMBOLIZER_PATH"] = symbolizer_path
|
|
177
|
+
logger.info(f"Injecting ASAN_SYMBOLIZER_PATH={symbolizer_path}")
|
|
178
|
+
else:
|
|
179
|
+
logger.warning(
|
|
180
|
+
"llvm-symbolizer not found - ASAN/LSAN stack traces may show "
|
|
181
|
+
"raw addresses instead of function names. Install llvm-symbolizer "
|
|
182
|
+
"or ensure clang-tool-chain is properly installed."
|
|
183
|
+
)
|
|
184
|
+
|
|
120
185
|
return env
|
clang_tool_chain/linker/lld.py
CHANGED
|
@@ -35,11 +35,12 @@ def _llvm_supports_ld64_lld_flag() -> bool:
|
|
|
35
35
|
"""
|
|
36
36
|
Check if the current platform's LLVM version supports -fuse-ld=ld64.lld.
|
|
37
37
|
|
|
38
|
-
The -fuse-ld=ld64.lld
|
|
39
|
-
|
|
38
|
+
Note: The clang driver does NOT recognize -fuse-ld=ld64.lld as a valid option.
|
|
39
|
+
This function exists for backward compatibility but always returns False in practice
|
|
40
|
+
because -fuse-ld=lld should be used instead (clang auto-dispatches to ld64.lld on Darwin).
|
|
40
41
|
|
|
41
42
|
Returns:
|
|
42
|
-
True if LLVM >= 21.x
|
|
43
|
+
True if LLVM >= 21.x (but we always fall back to -fuse-ld=lld anyway)
|
|
43
44
|
"""
|
|
44
45
|
from ..platform.detection import get_platform_info
|
|
45
46
|
|
|
@@ -272,6 +273,39 @@ def _user_specified_lld_on_macos(args: list[str]) -> bool:
|
|
|
272
273
|
return "-fuse-ld=lld" in args_str or "-fuse-ld=ld64.lld" in args_str
|
|
273
274
|
|
|
274
275
|
|
|
276
|
+
def _user_specified_ld64_lld(args: list[str]) -> bool:
|
|
277
|
+
"""
|
|
278
|
+
Check if the user explicitly specified -fuse-ld=ld64.lld.
|
|
279
|
+
|
|
280
|
+
This is used to emit a warning that the flag will be auto-converted
|
|
281
|
+
to -fuse-ld=lld since clang driver doesn't recognize ld64.lld.
|
|
282
|
+
|
|
283
|
+
Args:
|
|
284
|
+
args: Command-line arguments
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
True if user specified -fuse-ld=ld64.lld
|
|
288
|
+
"""
|
|
289
|
+
return any("-fuse-ld=ld64.lld" in arg for arg in args)
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def _convert_ld64_lld_to_lld(args: list[str]) -> list[str]:
|
|
293
|
+
"""
|
|
294
|
+
Convert -fuse-ld=ld64.lld to -fuse-ld=lld in arguments.
|
|
295
|
+
|
|
296
|
+
The clang driver does not recognize -fuse-ld=ld64.lld as a valid option.
|
|
297
|
+
This function converts it to -fuse-ld=lld which the driver recognizes
|
|
298
|
+
and automatically dispatches to ld64.lld on Darwin targets.
|
|
299
|
+
|
|
300
|
+
Args:
|
|
301
|
+
args: Original compiler arguments
|
|
302
|
+
|
|
303
|
+
Returns:
|
|
304
|
+
Modified arguments with -fuse-ld=ld64.lld replaced by -fuse-ld=lld
|
|
305
|
+
"""
|
|
306
|
+
return [arg.replace("-fuse-ld=ld64.lld", "-fuse-ld=lld") for arg in args]
|
|
307
|
+
|
|
308
|
+
|
|
275
309
|
# pyright: reportUnusedFunction=false
|
|
276
310
|
def _add_lld_linker_if_needed(platform_name: str, args: list[str]) -> list[str]:
|
|
277
311
|
"""
|
|
@@ -284,9 +318,10 @@ def _add_lld_linker_if_needed(platform_name: str, args: list[str]) -> list[str]:
|
|
|
284
318
|
- Faster linking performance
|
|
285
319
|
- Uniform toolchain across all platforms
|
|
286
320
|
|
|
287
|
-
Platform-specific
|
|
288
|
-
-
|
|
289
|
-
-
|
|
321
|
+
Platform-specific behavior:
|
|
322
|
+
- Uses -fuse-ld=lld on all platforms (clang driver auto-dispatches)
|
|
323
|
+
- macOS: Clang driver finds ld64.lld (Mach-O linker)
|
|
324
|
+
- Linux: Clang driver finds ld.lld (ELF linker)
|
|
290
325
|
|
|
291
326
|
The function is skipped when:
|
|
292
327
|
- User sets CLANG_TOOL_CHAIN_USE_SYSTEM_LD=1
|
|
@@ -302,14 +337,25 @@ def _add_lld_linker_if_needed(platform_name: str, args: list[str]) -> list[str]:
|
|
|
302
337
|
args: Original compiler arguments
|
|
303
338
|
|
|
304
339
|
Returns:
|
|
305
|
-
Modified arguments with
|
|
340
|
+
Modified arguments with -fuse-ld=lld flag prepended if needed
|
|
306
341
|
"""
|
|
307
342
|
# On macOS, if user explicitly specified LLD, we still need to translate flags
|
|
308
343
|
# and ensure ld64.lld symlink exists, even though we don't inject the -fuse-ld flag ourselves
|
|
309
344
|
if platform_name == "darwin" and _user_specified_lld_on_macos(args):
|
|
310
345
|
logger.debug("User specified LLD on macOS, translating GNU ld flags to ld64.lld equivalents")
|
|
311
|
-
# Ensure ld64.lld symlink exists for
|
|
346
|
+
# Ensure ld64.lld symlink exists for lld to dispatch to Mach-O mode
|
|
312
347
|
_ensure_ld64_lld_symlink()
|
|
348
|
+
|
|
349
|
+
# Check if user specified -fuse-ld=ld64.lld (which is not a valid clang driver option)
|
|
350
|
+
# and emit a warning about the auto-conversion to -fuse-ld=lld
|
|
351
|
+
if _user_specified_ld64_lld(args):
|
|
352
|
+
print(
|
|
353
|
+
"[clang-tool-chain] Warning: -fuse-ld=ld64.lld is not a valid clang driver option. "
|
|
354
|
+
"Auto-converting to -fuse-ld=lld (clang driver auto-dispatches to ld64.lld on Darwin).",
|
|
355
|
+
file=sys.stderr,
|
|
356
|
+
)
|
|
357
|
+
args = _convert_ld64_lld_to_lld(args)
|
|
358
|
+
|
|
313
359
|
return _translate_linker_flags_for_macos_lld(args)
|
|
314
360
|
|
|
315
361
|
if not _should_force_lld(platform_name, args):
|
|
@@ -320,22 +366,13 @@ def _add_lld_linker_if_needed(platform_name: str, args: list[str]) -> list[str]:
|
|
|
320
366
|
# On macOS, translate GNU ld flags to ld64.lld equivalents
|
|
321
367
|
if platform_name == "darwin":
|
|
322
368
|
args = _translate_linker_flags_for_macos_lld(args)
|
|
369
|
+
# Ensure ld64.lld symlink exists for lld to dispatch to Mach-O mode
|
|
370
|
+
_ensure_ld64_lld_symlink()
|
|
323
371
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
version = _get_llvm_version_for_platform()
|
|
332
|
-
version_str = f"{version[0]}.{version[1]}.{version[2]}"
|
|
333
|
-
print(
|
|
334
|
-
f"[clang-tool-chain] LLVM {version_str} does not support -fuse-ld=ld64.lld, "
|
|
335
|
-
f"using -fuse-ld=lld instead (lld auto-detects Mach-O from target)",
|
|
336
|
-
file=sys.stderr,
|
|
337
|
-
)
|
|
338
|
-
return ["-fuse-ld=lld"] + args
|
|
339
|
-
else:
|
|
340
|
-
# Linux uses standard lld
|
|
341
|
-
return ["-fuse-ld=lld"] + args
|
|
372
|
+
# Always use -fuse-ld=lld on all platforms.
|
|
373
|
+
# Note: -fuse-ld=ld64.lld is NOT a valid clang driver option.
|
|
374
|
+
# The clang driver only recognizes generic names like "lld", "gold", "bfd".
|
|
375
|
+
# When -fuse-ld=lld is used, clang automatically dispatches to:
|
|
376
|
+
# - Darwin: ld64.lld (Mach-O linker)
|
|
377
|
+
# - Linux: ld.lld (ELF linker)
|
|
378
|
+
return ["-fuse-ld=lld"] + args
|
clang_tool_chain/wrapper.py
CHANGED
|
@@ -151,6 +151,15 @@ from clang_tool_chain.execution.lldb import (
|
|
|
151
151
|
get_lldb_binary_dir,
|
|
152
152
|
)
|
|
153
153
|
|
|
154
|
+
# ============================================================================
|
|
155
|
+
# Sanitizer Environment
|
|
156
|
+
# ============================================================================
|
|
157
|
+
from clang_tool_chain.execution.sanitizer_env import (
|
|
158
|
+
detect_sanitizers_from_flags,
|
|
159
|
+
get_symbolizer_path,
|
|
160
|
+
prepare_sanitizer_environment,
|
|
161
|
+
)
|
|
162
|
+
|
|
154
163
|
# ============================================================================
|
|
155
164
|
# Linker Configuration
|
|
156
165
|
# ============================================================================
|
|
@@ -218,6 +227,10 @@ __all__ = [
|
|
|
218
227
|
"run_tool",
|
|
219
228
|
"sccache_clang_main",
|
|
220
229
|
"sccache_clang_cpp_main",
|
|
230
|
+
# Sanitizer Environment
|
|
231
|
+
"prepare_sanitizer_environment",
|
|
232
|
+
"detect_sanitizers_from_flags",
|
|
233
|
+
"get_symbolizer_path",
|
|
221
234
|
# Emscripten
|
|
222
235
|
"ensure_nodejs_available",
|
|
223
236
|
"execute_emscripten_tool",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: clang-tool-chain
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.45
|
|
4
4
|
Summary: Clang Tool Chain - C/C++ compilation toolchain utilities
|
|
5
5
|
Project-URL: Homepage, https://github.com/zackees/clang-tool-chain
|
|
6
6
|
Project-URL: Repository, https://github.com/zackees/clang-tool-chain
|
|
@@ -717,11 +717,8 @@ clang-tool-chain-cpp main.cpp -o program --deploy-dependencies -lunwind
|
|
|
717
717
|
|
|
718
718
|
**Disable deployment:**
|
|
719
719
|
```bash
|
|
720
|
-
# Cross-platform
|
|
720
|
+
# Cross-platform
|
|
721
721
|
export CLANG_TOOL_CHAIN_NO_DEPLOY_LIBS=1
|
|
722
|
-
|
|
723
|
-
# Windows-specific (legacy)
|
|
724
|
-
export CLANG_TOOL_CHAIN_NO_DEPLOY_DLLS=1
|
|
725
722
|
```
|
|
726
723
|
|
|
727
724
|
**Enable verbose logging:**
|
|
@@ -791,7 +788,7 @@ jobs:
|
|
|
791
788
|
**Key Environment Variables:**
|
|
792
789
|
- `CLANG_TOOL_CHAIN_DOWNLOAD_PATH` - Override installation location
|
|
793
790
|
- `CLANG_TOOL_CHAIN_NO_DEPLOY_LIBS` - Disable automatic library deployment (all platforms)
|
|
794
|
-
- `
|
|
791
|
+
- `CLANG_TOOL_CHAIN_NO_DEPLOY_SHARED_LIB` - Disable library deployment for shared library outputs only
|
|
795
792
|
- `CLANG_TOOL_CHAIN_LIB_DEPLOY_VERBOSE` - Enable verbose library deployment logging
|
|
796
793
|
- `CLANG_TOOL_CHAIN_USE_SYSTEM_LD` - Use system linker instead of LLD
|
|
797
794
|
- `CLANG_TOOL_CHAIN_NO_DIRECTIVES` - Disable inlined build directives
|
|
@@ -863,16 +860,42 @@ When running executables via `clang-tool-chain-build-run`, optimal sanitizer opt
|
|
|
863
860
|
|
|
864
861
|
- `ASAN_OPTIONS=fast_unwind_on_malloc=0:symbolize=1:detect_leaks=1` (when `-fsanitize=address` is used)
|
|
865
862
|
- `LSAN_OPTIONS=fast_unwind_on_malloc=0:symbolize=1` (when `-fsanitize=address` or `-fsanitize=leak` is used)
|
|
863
|
+
- `ASAN_SYMBOLIZER_PATH=/path/to/llvm-symbolizer` (automatically detected from clang-tool-chain)
|
|
866
864
|
|
|
867
865
|
**What these options fix:**
|
|
868
866
|
- `<unknown module>` entries in stack traces from `dlopen()`'d shared libraries
|
|
869
|
-
- Missing function names in crash reports
|
|
867
|
+
- Missing function names in crash reports (raw addresses like `0x7f5a4f15ac1d` instead of `my_function`)
|
|
870
868
|
- Incomplete leak detection
|
|
871
869
|
|
|
872
|
-
**Your options are always preserved** - if you set `ASAN_OPTIONS` or `
|
|
870
|
+
**Your options are always preserved** - if you set `ASAN_OPTIONS`, `LSAN_OPTIONS`, or `ASAN_SYMBOLIZER_PATH` yourself, clang-tool-chain won't override them.
|
|
873
871
|
|
|
874
872
|
**Regular builds are unaffected** - sanitizer options are only injected when the compiler flags indicate sanitizers are being used.
|
|
875
873
|
|
|
874
|
+
### Programmatic API (For Build Systems)
|
|
875
|
+
|
|
876
|
+
External build systems can use the sanitizer environment API to get properly configured environments:
|
|
877
|
+
|
|
878
|
+
```python
|
|
879
|
+
from clang_tool_chain import prepare_sanitizer_environment, get_symbolizer_path
|
|
880
|
+
|
|
881
|
+
# Option A: Complete environment setup (recommended)
|
|
882
|
+
env = prepare_sanitizer_environment(
|
|
883
|
+
base_env=os.environ.copy(),
|
|
884
|
+
compiler_flags=["-fsanitize=address", "-O2"]
|
|
885
|
+
)
|
|
886
|
+
# env now contains ASAN_OPTIONS, LSAN_OPTIONS, and ASAN_SYMBOLIZER_PATH
|
|
887
|
+
|
|
888
|
+
# Option B: Just get the symbolizer path
|
|
889
|
+
symbolizer = get_symbolizer_path()
|
|
890
|
+
if symbolizer:
|
|
891
|
+
os.environ["ASAN_SYMBOLIZER_PATH"] = symbolizer
|
|
892
|
+
```
|
|
893
|
+
|
|
894
|
+
**Available functions:**
|
|
895
|
+
- `prepare_sanitizer_environment(base_env, compiler_flags)` - Returns environment dict with all sanitizer variables
|
|
896
|
+
- `get_symbolizer_path()` - Returns path to `llvm-symbolizer` or `None`
|
|
897
|
+
- `detect_sanitizers_from_flags(flags)` - Returns `(asan_enabled, lsan_enabled)` tuple
|
|
898
|
+
|
|
876
899
|
### Configuration
|
|
877
900
|
|
|
878
901
|
```bash
|
|
@@ -888,6 +911,31 @@ export CLANG_TOOL_CHAIN_NO_SHARED_ASAN=1
|
|
|
888
911
|
- **Windows**: Works with both GNU and MSVC ABIs
|
|
889
912
|
- **macOS**: Uses bundled LLVM ASAN runtime
|
|
890
913
|
|
|
914
|
+
### Dynamically Loaded Libraries
|
|
915
|
+
|
|
916
|
+
If you see `<unknown module>` in ASAN/LSAN stack traces for code in dynamically loaded libraries, apply these fixes:
|
|
917
|
+
|
|
918
|
+
**1. Use `RTLD_NOW | RTLD_GLOBAL` (not `RTLD_LAZY`)**
|
|
919
|
+
|
|
920
|
+
```cpp
|
|
921
|
+
void* handle = dlopen(path, RTLD_NOW | RTLD_GLOBAL); // Good
|
|
922
|
+
void* handle = dlopen(path, RTLD_LAZY); // Bad - causes <unknown module>
|
|
923
|
+
```
|
|
924
|
+
|
|
925
|
+
**2. Skip `dlclose()` when ASAN is active**
|
|
926
|
+
|
|
927
|
+
ASAN symbolizes at exit; if the library is unloaded, addresses become `<unknown module>`.
|
|
928
|
+
|
|
929
|
+
```cpp
|
|
930
|
+
#if !defined(__SANITIZE_ADDRESS__)
|
|
931
|
+
dlclose(handle); // Only close when not running under ASAN
|
|
932
|
+
#endif
|
|
933
|
+
```
|
|
934
|
+
|
|
935
|
+
On Windows, use the same pattern with `FreeLibrary()`.
|
|
936
|
+
|
|
937
|
+
See: https://github.com/google/sanitizers/issues/899
|
|
938
|
+
|
|
891
939
|
---
|
|
892
940
|
|
|
893
941
|
## 🔧 How It Works
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
clang_tool_chain/__init__.py,sha256=
|
|
2
|
-
clang_tool_chain/__version__.py,sha256=
|
|
1
|
+
clang_tool_chain/__init__.py,sha256=u-qgOuQxh5W2g7n8BgUtBnUWsK66rM4N6LJXY9PiEx0,880
|
|
2
|
+
clang_tool_chain/__version__.py,sha256=QaMskYdjKBucedCuNyKFMIEERieKWeom7OJJ_fjGce4,72
|
|
3
3
|
clang_tool_chain/archive.py,sha256=t3reh7cm5XP2rhTqfRIDAQZv5XQq7SsstyiROYg8wFA,27697
|
|
4
4
|
clang_tool_chain/archive_cache.py,sha256=5ZmlwXIJZDcrDFkTbdgBQYN9sulGn0WyI6qwWqC4HEU,6806
|
|
5
5
|
clang_tool_chain/checksums.py,sha256=KFXeAeDz5ZlcZVOxsHDpNDCrm9UDoJ8bMA4PeNhuzdA,9868
|
|
@@ -9,6 +9,7 @@ clang_tool_chain/cli_parsers.py,sha256=cHVDyW75N_Kao1VYeAgw_L-1K65DjyvWUigQScTFi
|
|
|
9
9
|
clang_tool_chain/component_db.py,sha256=ohtlycsrEokui83iZyPsTyxmvXgkD7_OpVwAn1Cq8gI,9096
|
|
10
10
|
clang_tool_chain/downloader.py,sha256=fqErxTaI3oEYbloLZnrWXJ26j2iGOngPnl4i8kqOWNs,6041
|
|
11
11
|
clang_tool_chain/env_breadcrumbs.py,sha256=bvPTz8xABILhzrXTEBzdGrSbpEXLf2YVgcYEe-IdhNY,2335
|
|
12
|
+
clang_tool_chain/env_utils.py,sha256=cNlsZ-BchlKh5BDe6kmGYKU3A8jxFFSn4atv2_pPc1Y,4169
|
|
12
13
|
clang_tool_chain/fetch.py,sha256=DwsNl5DZkNqEYXL-FbCTnp6IA2iCAa9pMl5oPjyuOS4,4696
|
|
13
14
|
clang_tool_chain/installer.py,sha256=GuGeUvVcAw4HMj9jrub-I11ixmksw-vgtSOFrlupmPA,3323
|
|
14
15
|
clang_tool_chain/interrupt_utils.py,sha256=7YvazvGzyItRVDZ_pzUSK6at8PCw-Dgih69HLSz0tT4,1153
|
|
@@ -21,7 +22,7 @@ clang_tool_chain/paths.py,sha256=DwgWAAiLmPInpLDFMqyNFUyNYmkowWN626SwrMMx200,280
|
|
|
21
22
|
clang_tool_chain/permissions.py,sha256=Okt91_fk4I0UmEiUAktqxm3OZfZQ3rUW-0r2X_seuxY,8517
|
|
22
23
|
clang_tool_chain/sccache_runner.py,sha256=xkepFC4Ti8s6wv7GXA24REFj8jli86nSUqcY7TCOQzk,12810
|
|
23
24
|
clang_tool_chain/settings_warnings.py,sha256=mspKHUT0Bod_SnSLyEo6Tix0ANTMn0erVLsn82jWr4I,3523
|
|
24
|
-
clang_tool_chain/wrapper.py,sha256=
|
|
25
|
+
clang_tool_chain/wrapper.py,sha256=mMLydq43AZjUc24QU6TtiMCH8jyVh1BBHY5XMWLSeX0,8493
|
|
25
26
|
clang_tool_chain/abi/__init__.py,sha256=GBiAN5W2TovaZ2MzVFFV5J3udiOn4ME0CN6oGS2FMDM,922
|
|
26
27
|
clang_tool_chain/abi/windows_gnu.py,sha256=ZN7xVWwoBEILi7A_FG8NBBiN5gtrXYscgmixFMEm9zg,10517
|
|
27
28
|
clang_tool_chain/abi/windows_msvc.py,sha256=Z-f6HGjkBcrguDfo62qOP5cI_2EYwjQ9k3Cgnpf42Fo,3617
|
|
@@ -29,17 +30,17 @@ clang_tool_chain/commands/__init__.py,sha256=KpJD6wLVa5t3JVSjS5BQ8EUaALc5pc7SqyW
|
|
|
29
30
|
clang_tool_chain/commands/entry_points.py,sha256=mJ3ZZkYJqoT2JqMi6m8us0w8tf5_b7QnmDR30_yij00,13824
|
|
30
31
|
clang_tool_chain/deployment/__init__.py,sha256=1Bv8kpwyTd3LN9HNHVWFwkznXLbTdbOC5PLvrnDhtR0,356
|
|
31
32
|
clang_tool_chain/deployment/base_deployer.py,sha256=-02O-EJllRTAYnPcBWAilfuvGWTGvpK1EG6TRnBC8_k,11565
|
|
32
|
-
clang_tool_chain/deployment/dll_deployer.py,sha256=
|
|
33
|
+
clang_tool_chain/deployment/dll_deployer.py,sha256=x3_hXsPgyQMkp1orzZV4lAZa4NMP27t0B1ajYBBbzmA,24840
|
|
33
34
|
clang_tool_chain/deployment/dll_detector.py,sha256=Zf2NAjAg6bEg6uvb7dFkj0qvk8Mvzj4sjyPIoxNVvJE,8629
|
|
34
|
-
clang_tool_chain/deployment/dylib_deployer.py,sha256=
|
|
35
|
+
clang_tool_chain/deployment/dylib_deployer.py,sha256=6GLn_CMLYUAdQZgwcTMx0XXo_AxGvUKUFYt04v2Kip0,19315
|
|
35
36
|
clang_tool_chain/deployment/factory.py,sha256=Mq6cMMUctoYBZ81I16_hXUmDLQGSAngCmBzPlu_0iaU,6205
|
|
36
|
-
clang_tool_chain/deployment/so_deployer.py,sha256=
|
|
37
|
+
clang_tool_chain/deployment/so_deployer.py,sha256=i3ezy1gOOHlzH5lHNytPl2FBXu7AX-F3r1uXHFEKfmg,11892
|
|
37
38
|
clang_tool_chain/directives/__init__.py,sha256=MJDNYL_MD2MF0HFsrTsSTX645bYo6vtjq2pOTtfykaU,198
|
|
38
39
|
clang_tool_chain/directives/parser.py,sha256=6J7mO1JtvuHkkKS0Xges5b_jT9b3uTF6ULI0ZiwGAdw,11179
|
|
39
|
-
clang_tool_chain/execution/__init__.py,sha256=
|
|
40
|
-
clang_tool_chain/execution/arg_transformers.py,sha256=
|
|
41
|
-
clang_tool_chain/execution/build.py,sha256=
|
|
42
|
-
clang_tool_chain/execution/build_pipeline.py,sha256=
|
|
40
|
+
clang_tool_chain/execution/__init__.py,sha256=fFQEW9VejCdbBvt4quNJ2X8udh_PKgewTfzUXHmXzyc,940
|
|
41
|
+
clang_tool_chain/execution/arg_transformers.py,sha256=8RDaUF2r3dgSqQ_2XkGiXnmoLGD3ltaf4YbqkyjG2CI,19772
|
|
42
|
+
clang_tool_chain/execution/build.py,sha256=YHS1BJTZg5pBS9czVko41mBdfswSPad5hxfitMoLvsI,13275
|
|
43
|
+
clang_tool_chain/execution/build_pipeline.py,sha256=ORJEJ8WYp8c7bhWAa-e3w_ySXwenpUczlmXgoGByToY,17823
|
|
43
44
|
clang_tool_chain/execution/core.py,sha256=7CJ0azznC5lq5bw8amk2kwCIN2I_OnDiKytpapkvrdY,25273
|
|
44
45
|
clang_tool_chain/execution/cosmocc.py,sha256=oGlaPK6Jpz3FaohEkWpRz8sRNu2sT-HYCoIf9xBxJHk,13319
|
|
45
46
|
clang_tool_chain/execution/emscripten.py,sha256=lgxPQpeB1_wWxNILgeyyrW5lEn117dHS9dQ3ikHRn1w,44235
|
|
@@ -47,7 +48,7 @@ clang_tool_chain/execution/iwyu.py,sha256=bmP0d_PZObA1JfmFYp3qIOKCb7y32AWPm2_ReF
|
|
|
47
48
|
clang_tool_chain/execution/lldb.py,sha256=VpxkWTPS6PsyskaKTALeziR5Z5NLwarW174Fm1SMX9k,20718
|
|
48
49
|
clang_tool_chain/execution/nodejs_resolver.py,sha256=8QsJWvIfmt5mBDV7n0ypSjsPyXS-eZTizhBli937I-g,11172
|
|
49
50
|
clang_tool_chain/execution/platform_executor.py,sha256=sF4GyW0ujy2EewG8y2Eo1gUWGzss5G5iIkv02w7-2_o,14362
|
|
50
|
-
clang_tool_chain/execution/sanitizer_env.py,sha256=
|
|
51
|
+
clang_tool_chain/execution/sanitizer_env.py,sha256=g07hDdicGI2QqHzahLm8CKm6c-SthLAkR8kANbPxBIU,7583
|
|
51
52
|
clang_tool_chain/installers/__init__.py,sha256=NAV5woPCEDKSbFr1UmfQsrg4Ua5UdghN4q7H3ymvRsg,279
|
|
52
53
|
clang_tool_chain/installers/base.py,sha256=OS78bau9zoYPitmhla7pKsfCPEj-zLY0DkvVzjE31Tw,15437
|
|
53
54
|
clang_tool_chain/installers/clang.py,sha256=rUtheVRF7mq_1YdmQ3XzIybrJqsHbm2Xf0cbhRbH7RQ,16994
|
|
@@ -57,7 +58,7 @@ clang_tool_chain/installers/iwyu.py,sha256=9aAhdGtOTY6BrLuPtladY8Y2mz1i7FjgbMxZf
|
|
|
57
58
|
clang_tool_chain/installers/lldb.py,sha256=FpG8NMNQk8PoNfg6aeU_plmSQrVET7zo-pTvoK8z838,2261
|
|
58
59
|
clang_tool_chain/installers/nodejs.py,sha256=5N07rotgmCfUaDm1uJfBlIAFKC1iTpgZT0HBRuoYwKI,9343
|
|
59
60
|
clang_tool_chain/linker/__init__.py,sha256=ghzDFpZ2-gPmdDO6K05C7yNbY6pZLANPuUks9TaQwVY,537
|
|
60
|
-
clang_tool_chain/linker/lld.py,sha256=
|
|
61
|
+
clang_tool_chain/linker/lld.py,sha256=08qkvoK4-8yACm34GfZzp3vM8rZYHCv_M9fjUeSetd0,13826
|
|
61
62
|
clang_tool_chain/platform/__init__.py,sha256=WkV9Y25ua0mtzEGcsIxF-qExtroSTAMKkcElWuQF2BE,342
|
|
62
63
|
clang_tool_chain/platform/detection.py,sha256=PLHyUfmQ5xuohhpz0KSXJWK3d0u0fCsjx1DbM8f1CxQ,5470
|
|
63
64
|
clang_tool_chain/platform/paths.py,sha256=K0IjeVwbmgPlAWQO8mS3r1WS4C2dN6IYrSqPpckeT5c,6088
|
|
@@ -67,8 +68,8 @@ clang_tool_chain/sdk/windows.py,sha256=8zMLAoFz2OKMz-w6Kqxr3A-6Cofto2VWZvCvRi7kn
|
|
|
67
68
|
clang_tool_chain/testing/__init__.py,sha256=-sYqOOCuTV_u-MkmExrD4uKdTHG4RmMwR3D1kIG281Q,208
|
|
68
69
|
clang_tool_chain/testing/diagnostic_runner.py,sha256=mnmFUEOQulY3-Ggu6hKVGZwjrKQNmV6kY80PRTUu2qU,5293
|
|
69
70
|
clang_tool_chain/testing/diagnostic_tests.py,sha256=GmtKWrDcddZTpx9_yIKfhRAy6YOde8dj7SksCWVEME4,6019
|
|
70
|
-
clang_tool_chain-1.0.
|
|
71
|
-
clang_tool_chain-1.0.
|
|
72
|
-
clang_tool_chain-1.0.
|
|
73
|
-
clang_tool_chain-1.0.
|
|
74
|
-
clang_tool_chain-1.0.
|
|
71
|
+
clang_tool_chain-1.0.45.dist-info/METADATA,sha256=ZZZzJS3byKeOW3nToE5HXQjpIiKFNNkY2KI-dvNc2E0,52205
|
|
72
|
+
clang_tool_chain-1.0.45.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
73
|
+
clang_tool_chain-1.0.45.dist-info/entry_points.txt,sha256=DBfnqZJzHFoO6rldGY3ESaX-z2ydAvIueUPM9ss9M-w,2728
|
|
74
|
+
clang_tool_chain-1.0.45.dist-info/licenses/LICENSE,sha256=51FO1oc2pZbQNI0v0_THnznnZIF4iFgawG1xnQ58kKo,10997
|
|
75
|
+
clang_tool_chain-1.0.45.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|