clang-tool-chain 1.1.4__py3-none-any.whl → 1.1.6__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.
@@ -9,6 +9,9 @@ from clang_tool_chain.env_utils import (
9
9
  )
10
10
  from clang_tool_chain.execution.sanitizer_env import (
11
11
  detect_sanitizers_from_flags,
12
+ get_all_sanitizer_runtime_dlls,
13
+ get_asan_runtime_dll,
14
+ get_runtime_dll_paths,
12
15
  get_symbolizer_path,
13
16
  prepare_sanitizer_environment,
14
17
  )
@@ -27,6 +30,9 @@ __all__ = [
27
30
  "CONTROLLABLE_FEATURES",
28
31
  # Sanitizer environment
29
32
  "prepare_sanitizer_environment",
33
+ "get_runtime_dll_paths",
30
34
  "get_symbolizer_path",
31
35
  "detect_sanitizers_from_flags",
36
+ "get_asan_runtime_dll",
37
+ "get_all_sanitizer_runtime_dlls",
32
38
  ]
@@ -1,3 +1,3 @@
1
1
  """Version information for clang-tool-chain."""
2
2
 
3
- __version__ = "1.1.4"
3
+ __version__ = "1.1.6"
@@ -12,6 +12,7 @@ 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
13
  from clang_tool_chain.execution.sanitizer_env import (
14
14
  detect_sanitizers_from_flags,
15
+ get_runtime_dll_paths,
15
16
  get_symbolizer_path,
16
17
  prepare_sanitizer_environment,
17
18
  )
@@ -29,5 +30,6 @@ __all__ = [
29
30
  # Sanitizer utilities
30
31
  "prepare_sanitizer_environment",
31
32
  "detect_sanitizers_from_flags",
33
+ "get_runtime_dll_paths",
32
34
  "get_symbolizer_path",
33
35
  ]
@@ -1,9 +1,9 @@
1
1
  """
2
2
  Sanitizer runtime environment configuration.
3
3
 
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.
4
+ This module provides automatic injection of ASAN_OPTIONS, LSAN_OPTIONS,
5
+ ASAN_SYMBOLIZER_PATH, and PATH environment variables to ensure executables
6
+ compiled with Address Sanitizer or Leak Sanitizer can run correctly.
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.
@@ -11,6 +11,10 @@ dlopen()'d shared libraries by enabling slow unwinding and symbolization.
11
11
  The symbolizer path is automatically detected from the clang-tool-chain
12
12
  installation, enabling proper address-to-symbol resolution without manual
13
13
  configuration.
14
+
15
+ On Windows with shared ASAN runtime (-shared-libasan), the clang runtime
16
+ DLL directory is automatically added to PATH to ensure the ASAN DLL can
17
+ be found at runtime.
14
18
  """
15
19
 
16
20
  import logging
@@ -71,6 +75,224 @@ def get_symbolizer_path() -> str | None:
71
75
  return None
72
76
 
73
77
 
78
+ def get_runtime_dll_paths() -> list[str]:
79
+ """
80
+ Get paths to directories containing runtime DLLs (Windows only).
81
+
82
+ On Windows, when using shared ASAN runtime (-shared-libasan), the
83
+ libclang_rt.asan_dynamic-x86_64.dll must be findable at runtime.
84
+ This function returns the paths that should be added to PATH.
85
+
86
+ Returns:
87
+ List of directory paths containing runtime DLLs, or empty list
88
+ if not applicable (non-Windows or DLLs not found).
89
+
90
+ Example:
91
+ >>> paths = get_runtime_dll_paths()
92
+ >>> if paths:
93
+ ... os.environ["PATH"] = os.pathsep.join(paths) + os.pathsep + os.environ.get("PATH", "")
94
+ """
95
+ if platform.system() != "Windows":
96
+ return []
97
+
98
+ paths = []
99
+
100
+ try:
101
+ from clang_tool_chain.platform.detection import get_platform_binary_dir, get_platform_info
102
+
103
+ # Get platform info for sysroot lookup
104
+ _platform_name, arch = get_platform_info()
105
+
106
+ # Get the clang bin directory and sysroot bin directory
107
+ clang_bin_dir = get_platform_binary_dir()
108
+ clang_root = clang_bin_dir.parent
109
+
110
+ # Add clang bin directory (for some sanitizer DLLs)
111
+ if clang_bin_dir.exists():
112
+ paths.append(str(clang_bin_dir))
113
+
114
+ # Add MinGW sysroot bin directory (where libclang_rt.asan_dynamic-x86_64.dll lives)
115
+ if arch == "x86_64":
116
+ sysroot_name = "x86_64-w64-mingw32"
117
+ elif arch == "arm64":
118
+ sysroot_name = "aarch64-w64-mingw32"
119
+ else:
120
+ sysroot_name = None
121
+
122
+ if sysroot_name:
123
+ sysroot_bin = clang_root / sysroot_name / "bin"
124
+ if sysroot_bin.exists():
125
+ paths.append(str(sysroot_bin))
126
+ logger.debug(f"Found MinGW sysroot bin: {sysroot_bin}")
127
+
128
+ except (ImportError, RuntimeError) as e:
129
+ logger.debug(f"Could not get runtime DLL paths: {e}")
130
+
131
+ return paths
132
+
133
+
134
+ def get_asan_runtime_dll() -> Path | None:
135
+ """
136
+ Get the full path to the ASAN runtime DLL (Windows only).
137
+
138
+ This function locates the shared ASAN runtime DLL used when compiling
139
+ with -fsanitize=address -shared-libasan. The DLL must be accessible
140
+ at runtime for ASAN-instrumented executables to run.
141
+
142
+ This is particularly useful when running tests via build systems like
143
+ Meson that reset PATH and don't inherit ASAN DLL directories. By getting
144
+ the DLL path, consuming projects can copy it to their build directory
145
+ where the build system will automatically discover it.
146
+
147
+ Returns:
148
+ Path to libclang_rt.asan_dynamic-x86_64.dll (or ARM64 equivalent),
149
+ or None if not found or not on Windows.
150
+
151
+ Example:
152
+ >>> dll_path = get_asan_runtime_dll()
153
+ >>> if dll_path:
154
+ ... shutil.copy(dll_path, build_dir / dll_path.name)
155
+ ... # Now Meson tests will find the ASAN DLL in build_dir
156
+
157
+ Note:
158
+ This function ensures the toolchain is downloaded before searching.
159
+ The DLL is typically located in the MinGW sysroot bin directory:
160
+ ~/.clang-tool-chain/clang/win/x86_64/x86_64-w64-mingw32/bin/
161
+
162
+ See Also:
163
+ get_runtime_dll_paths: Returns directories containing runtime DLLs
164
+ prepare_sanitizer_environment: Adds DLL paths to PATH
165
+ """
166
+ if platform.system() != "Windows":
167
+ logger.debug("get_asan_runtime_dll: Not on Windows, returning None")
168
+ return None
169
+
170
+ try:
171
+ from clang_tool_chain.platform.detection import get_platform_binary_dir, get_platform_info
172
+
173
+ # Get platform info for sysroot lookup
174
+ _platform_name, arch = get_platform_info()
175
+
176
+ # Get the clang root directory
177
+ clang_bin_dir = get_platform_binary_dir()
178
+ clang_root = clang_bin_dir.parent
179
+
180
+ # Determine sysroot and DLL name based on architecture
181
+ if arch == "x86_64":
182
+ sysroot_name = "x86_64-w64-mingw32"
183
+ dll_name = "libclang_rt.asan_dynamic-x86_64.dll"
184
+ elif arch == "arm64":
185
+ sysroot_name = "aarch64-w64-mingw32"
186
+ dll_name = "libclang_rt.asan_dynamic-aarch64.dll"
187
+ else:
188
+ logger.debug(f"get_asan_runtime_dll: Unsupported architecture {arch}")
189
+ return None
190
+
191
+ # Check MinGW sysroot bin directory first (primary location)
192
+ sysroot_bin = clang_root / sysroot_name / "bin"
193
+ dll_path = sysroot_bin / dll_name
194
+ if dll_path.exists():
195
+ logger.debug(f"Found ASAN runtime DLL: {dll_path}")
196
+ return dll_path
197
+
198
+ # Fallback: check clang bin directory
199
+ dll_path = clang_bin_dir / dll_name
200
+ if dll_path.exists():
201
+ logger.debug(f"Found ASAN runtime DLL (fallback): {dll_path}")
202
+ return dll_path
203
+
204
+ # Try glob pattern for any ASAN DLL (version may vary)
205
+ for search_dir in [sysroot_bin, clang_bin_dir]:
206
+ if search_dir.exists():
207
+ for dll_file in search_dir.glob("libclang_rt.asan*.dll"):
208
+ logger.debug(f"Found ASAN runtime DLL (glob): {dll_file}")
209
+ return dll_file
210
+
211
+ logger.debug(f"ASAN runtime DLL not found in {sysroot_bin} or {clang_bin_dir}")
212
+ return None
213
+
214
+ except (ImportError, RuntimeError) as e:
215
+ logger.debug(f"Could not get ASAN runtime DLL path: {e}")
216
+ return None
217
+
218
+
219
+ def get_all_sanitizer_runtime_dlls() -> list[Path]:
220
+ """
221
+ Get all sanitizer runtime DLLs (Windows only).
222
+
223
+ This function locates all shared sanitizer runtime DLLs, including:
224
+ - Address Sanitizer (ASAN)
225
+ - Undefined Behavior Sanitizer (UBSAN)
226
+ - Thread Sanitizer (TSAN) - if available
227
+
228
+ This is useful for projects that want to copy all sanitizer DLLs to
229
+ their build directory to ensure all instrumented code can run.
230
+
231
+ Returns:
232
+ List of Paths to sanitizer runtime DLLs, or empty list if not
233
+ found or not on Windows.
234
+
235
+ Example:
236
+ >>> dlls = get_all_sanitizer_runtime_dlls()
237
+ >>> for dll in dlls:
238
+ ... shutil.copy(dll, build_dir / dll.name)
239
+
240
+ See Also:
241
+ get_asan_runtime_dll: Returns just the ASAN DLL path
242
+ """
243
+ if platform.system() != "Windows":
244
+ return []
245
+
246
+ dlls: list[Path] = []
247
+
248
+ try:
249
+ from clang_tool_chain.platform.detection import get_platform_binary_dir, get_platform_info
250
+
251
+ # Get platform info for sysroot lookup
252
+ _platform_name, arch = get_platform_info()
253
+
254
+ # Get the clang root directory
255
+ clang_bin_dir = get_platform_binary_dir()
256
+ clang_root = clang_bin_dir.parent
257
+
258
+ # Determine sysroot based on architecture
259
+ if arch == "x86_64":
260
+ sysroot_name = "x86_64-w64-mingw32"
261
+ elif arch == "arm64":
262
+ sysroot_name = "aarch64-w64-mingw32"
263
+ else:
264
+ return []
265
+
266
+ # Search directories
267
+ search_dirs = [
268
+ clang_root / sysroot_name / "bin",
269
+ clang_bin_dir,
270
+ ]
271
+
272
+ # Patterns for sanitizer DLLs
273
+ patterns = [
274
+ "libclang_rt.asan*.dll", # Address Sanitizer
275
+ "libclang_rt.ubsan*.dll", # Undefined Behavior Sanitizer
276
+ "libclang_rt.tsan*.dll", # Thread Sanitizer
277
+ "libclang_rt.lsan*.dll", # Leak Sanitizer (standalone)
278
+ ]
279
+
280
+ seen_names: set[str] = set()
281
+ for search_dir in search_dirs:
282
+ if search_dir.exists():
283
+ for pattern in patterns:
284
+ for dll_file in search_dir.glob(pattern):
285
+ if dll_file.name not in seen_names:
286
+ dlls.append(dll_file)
287
+ seen_names.add(dll_file.name)
288
+ logger.debug(f"Found sanitizer DLL: {dll_file}")
289
+
290
+ except (ImportError, RuntimeError) as e:
291
+ logger.debug(f"Could not get sanitizer runtime DLLs: {e}")
292
+
293
+ return dlls
294
+
295
+
74
296
  def detect_sanitizers_from_flags(compiler_flags: list[str]) -> tuple[bool, bool]:
75
297
  """
76
298
  Detect which sanitizers are enabled from compiler flags.
@@ -224,6 +446,20 @@ def prepare_sanitizer_environment(
224
446
  "or ensure clang-tool-chain is properly installed."
225
447
  )
226
448
 
449
+ # On Windows, add runtime DLL paths to PATH for shared ASAN runtime
450
+ # This ensures libclang_rt.asan_dynamic-x86_64.dll can be found at runtime
451
+ if asan_enabled and platform.system() == "Windows":
452
+ dll_paths = get_runtime_dll_paths()
453
+ if dll_paths:
454
+ current_path = env.get("PATH", "")
455
+ # Prepend DLL paths to ensure they take priority
456
+ new_path = os.pathsep.join(dll_paths)
457
+ if current_path:
458
+ env["PATH"] = new_path + os.pathsep + current_path
459
+ else:
460
+ env["PATH"] = new_path
461
+ logger.info(f"Injecting runtime DLL paths to PATH: {dll_paths}")
462
+
227
463
  # Add platform-specific LSan suppressions if LSAN is enabled
228
464
  if lsan_enabled:
229
465
  # Check if user explicitly disabled built-in suppressions with ""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clang-tool-chain
3
- Version: 1.1.4
3
+ Version: 1.1.6
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
@@ -1,5 +1,5 @@
1
- clang_tool_chain/__init__.py,sha256=u-qgOuQxh5W2g7n8BgUtBnUWsK66rM4N6LJXY9PiEx0,880
2
- clang_tool_chain/__version__.py,sha256=OZTe0l-1KRXG_B5GhtooBTlUxPJy68FJ5pyGG4KC95U,71
1
+ clang_tool_chain/__init__.py,sha256=TS-fHsqErYIes4VJyZExhRMQjveVv7i99T9JjHJPpas,1064
2
+ clang_tool_chain/__version__.py,sha256=YTx56JW89_upt38FX8hlUWfRFbv_i6nx3Au3Ysdk01M,71
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
@@ -38,7 +38,7 @@ clang_tool_chain/deployment/libdeploy.py,sha256=cfyDFmbuUmimss5fv7exO7NOulDnw7ag
38
38
  clang_tool_chain/deployment/so_deployer.py,sha256=eAygmGojioi1iz5cHeqmKjJ5fI-5XooG9Ro-5TIA0gw,12867
39
39
  clang_tool_chain/directives/__init__.py,sha256=MJDNYL_MD2MF0HFsrTsSTX645bYo6vtjq2pOTtfykaU,198
40
40
  clang_tool_chain/directives/parser.py,sha256=6J7mO1JtvuHkkKS0Xges5b_jT9b3uTF6ULI0ZiwGAdw,11179
41
- clang_tool_chain/execution/__init__.py,sha256=fFQEW9VejCdbBvt4quNJ2X8udh_PKgewTfzUXHmXzyc,940
41
+ clang_tool_chain/execution/__init__.py,sha256=-jrL19U2UkP6qcUQVNq2qB0txg2TGXXTpXn_wXjVemg,996
42
42
  clang_tool_chain/execution/arg_transformers.py,sha256=Vut8XHp4IHkEUhVRI-NSvFaX6EvrGDd3FCEHbDe9gHo,26268
43
43
  clang_tool_chain/execution/build.py,sha256=YHS1BJTZg5pBS9czVko41mBdfswSPad5hxfitMoLvsI,13275
44
44
  clang_tool_chain/execution/build_pipeline.py,sha256=ORJEJ8WYp8c7bhWAa-e3w_ySXwenpUczlmXgoGByToY,17823
@@ -49,7 +49,7 @@ clang_tool_chain/execution/iwyu.py,sha256=bmP0d_PZObA1JfmFYp3qIOKCb7y32AWPm2_ReF
49
49
  clang_tool_chain/execution/lldb.py,sha256=VpxkWTPS6PsyskaKTALeziR5Z5NLwarW174Fm1SMX9k,20718
50
50
  clang_tool_chain/execution/nodejs_resolver.py,sha256=8QsJWvIfmt5mBDV7n0ypSjsPyXS-eZTizhBli937I-g,11172
51
51
  clang_tool_chain/execution/platform_executor.py,sha256=sF4GyW0ujy2EewG8y2Eo1gUWGzss5G5iIkv02w7-2_o,14362
52
- clang_tool_chain/execution/sanitizer_env.py,sha256=bLGUATKNnuB8WxWt1eopVgEh6bsZzkmpcpGxYOeRcHk,10642
52
+ clang_tool_chain/execution/sanitizer_env.py,sha256=DgmSKOOGd0JxoKJfLkT9pDFXIfm4C6IWtECeeRGc838,19269
53
53
  clang_tool_chain/installers/__init__.py,sha256=NAV5woPCEDKSbFr1UmfQsrg4Ua5UdghN4q7H3ymvRsg,279
54
54
  clang_tool_chain/installers/base.py,sha256=OS78bau9zoYPitmhla7pKsfCPEj-zLY0DkvVzjE31Tw,15437
55
55
  clang_tool_chain/installers/clang.py,sha256=rUtheVRF7mq_1YdmQ3XzIybrJqsHbm2Xf0cbhRbH7RQ,16994
@@ -71,8 +71,8 @@ clang_tool_chain/symbolizer/unwind_windows.h,sha256=XTWhJDv13AAMUPAzWCfRN7tO6EbN
71
71
  clang_tool_chain/testing/__init__.py,sha256=-sYqOOCuTV_u-MkmExrD4uKdTHG4RmMwR3D1kIG281Q,208
72
72
  clang_tool_chain/testing/diagnostic_runner.py,sha256=mnmFUEOQulY3-Ggu6hKVGZwjrKQNmV6kY80PRTUu2qU,5293
73
73
  clang_tool_chain/testing/diagnostic_tests.py,sha256=GmtKWrDcddZTpx9_yIKfhRAy6YOde8dj7SksCWVEME4,6019
74
- clang_tool_chain-1.1.4.dist-info/METADATA,sha256=EQviHfowQcUB26PHuj-VYtH5YfTNoE1b2D8DOXArOT8,60267
75
- clang_tool_chain-1.1.4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
76
- clang_tool_chain-1.1.4.dist-info/entry_points.txt,sha256=N0a0OVPkCFbf6BisRkHj-m2TcZ-f1mqxfXxAHQxfrQg,2800
77
- clang_tool_chain-1.1.4.dist-info/licenses/LICENSE,sha256=51FO1oc2pZbQNI0v0_THnznnZIF4iFgawG1xnQ58kKo,10997
78
- clang_tool_chain-1.1.4.dist-info/RECORD,,
74
+ clang_tool_chain-1.1.6.dist-info/METADATA,sha256=fUCTpvTHpVserQz52zpkBTZVZD0KoZR-AMxA4r3vmhc,60267
75
+ clang_tool_chain-1.1.6.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
76
+ clang_tool_chain-1.1.6.dist-info/entry_points.txt,sha256=N0a0OVPkCFbf6BisRkHj-m2TcZ-f1mqxfXxAHQxfrQg,2800
77
+ clang_tool_chain-1.1.6.dist-info/licenses/LICENSE,sha256=51FO1oc2pZbQNI0v0_THnznnZIF4iFgawG1xnQ58kKo,10997
78
+ clang_tool_chain-1.1.6.dist-info/RECORD,,