fbuild 1.1.0__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 fbuild might be problematic. Click here for more details.

Files changed (93) hide show
  1. fbuild/__init__.py +0 -0
  2. fbuild/assets/example.txt +1 -0
  3. fbuild/build/__init__.py +117 -0
  4. fbuild/build/archive_creator.py +186 -0
  5. fbuild/build/binary_generator.py +444 -0
  6. fbuild/build/build_component_factory.py +131 -0
  7. fbuild/build/build_state.py +325 -0
  8. fbuild/build/build_utils.py +98 -0
  9. fbuild/build/compilation_executor.py +422 -0
  10. fbuild/build/compiler.py +165 -0
  11. fbuild/build/compiler_avr.py +574 -0
  12. fbuild/build/configurable_compiler.py +612 -0
  13. fbuild/build/configurable_linker.py +637 -0
  14. fbuild/build/flag_builder.py +186 -0
  15. fbuild/build/library_dependency_processor.py +185 -0
  16. fbuild/build/linker.py +708 -0
  17. fbuild/build/orchestrator.py +67 -0
  18. fbuild/build/orchestrator_avr.py +656 -0
  19. fbuild/build/orchestrator_esp32.py +797 -0
  20. fbuild/build/orchestrator_teensy.py +543 -0
  21. fbuild/build/source_compilation_orchestrator.py +220 -0
  22. fbuild/build/source_scanner.py +516 -0
  23. fbuild/cli.py +566 -0
  24. fbuild/cli_utils.py +312 -0
  25. fbuild/config/__init__.py +16 -0
  26. fbuild/config/board_config.py +457 -0
  27. fbuild/config/board_loader.py +92 -0
  28. fbuild/config/ini_parser.py +209 -0
  29. fbuild/config/mcu_specs.py +88 -0
  30. fbuild/daemon/__init__.py +34 -0
  31. fbuild/daemon/client.py +929 -0
  32. fbuild/daemon/compilation_queue.py +293 -0
  33. fbuild/daemon/daemon.py +474 -0
  34. fbuild/daemon/daemon_context.py +196 -0
  35. fbuild/daemon/error_collector.py +263 -0
  36. fbuild/daemon/file_cache.py +332 -0
  37. fbuild/daemon/lock_manager.py +270 -0
  38. fbuild/daemon/logging_utils.py +149 -0
  39. fbuild/daemon/messages.py +301 -0
  40. fbuild/daemon/operation_registry.py +288 -0
  41. fbuild/daemon/process_tracker.py +366 -0
  42. fbuild/daemon/processors/__init__.py +12 -0
  43. fbuild/daemon/processors/build_processor.py +157 -0
  44. fbuild/daemon/processors/deploy_processor.py +327 -0
  45. fbuild/daemon/processors/monitor_processor.py +146 -0
  46. fbuild/daemon/request_processor.py +401 -0
  47. fbuild/daemon/status_manager.py +216 -0
  48. fbuild/daemon/subprocess_manager.py +316 -0
  49. fbuild/deploy/__init__.py +17 -0
  50. fbuild/deploy/deployer.py +67 -0
  51. fbuild/deploy/deployer_esp32.py +314 -0
  52. fbuild/deploy/monitor.py +495 -0
  53. fbuild/interrupt_utils.py +34 -0
  54. fbuild/packages/__init__.py +53 -0
  55. fbuild/packages/archive_utils.py +1098 -0
  56. fbuild/packages/arduino_core.py +412 -0
  57. fbuild/packages/cache.py +249 -0
  58. fbuild/packages/downloader.py +366 -0
  59. fbuild/packages/framework_esp32.py +538 -0
  60. fbuild/packages/framework_teensy.py +346 -0
  61. fbuild/packages/github_utils.py +96 -0
  62. fbuild/packages/header_trampoline_cache.py +394 -0
  63. fbuild/packages/library_compiler.py +203 -0
  64. fbuild/packages/library_manager.py +549 -0
  65. fbuild/packages/library_manager_esp32.py +413 -0
  66. fbuild/packages/package.py +163 -0
  67. fbuild/packages/platform_esp32.py +383 -0
  68. fbuild/packages/platform_teensy.py +312 -0
  69. fbuild/packages/platform_utils.py +131 -0
  70. fbuild/packages/platformio_registry.py +325 -0
  71. fbuild/packages/sdk_utils.py +231 -0
  72. fbuild/packages/toolchain.py +436 -0
  73. fbuild/packages/toolchain_binaries.py +196 -0
  74. fbuild/packages/toolchain_esp32.py +484 -0
  75. fbuild/packages/toolchain_metadata.py +185 -0
  76. fbuild/packages/toolchain_teensy.py +404 -0
  77. fbuild/platform_configs/esp32.json +150 -0
  78. fbuild/platform_configs/esp32c2.json +144 -0
  79. fbuild/platform_configs/esp32c3.json +143 -0
  80. fbuild/platform_configs/esp32c5.json +151 -0
  81. fbuild/platform_configs/esp32c6.json +151 -0
  82. fbuild/platform_configs/esp32p4.json +149 -0
  83. fbuild/platform_configs/esp32s3.json +151 -0
  84. fbuild/platform_configs/imxrt1062.json +56 -0
  85. fbuild-1.1.0.dist-info/METADATA +447 -0
  86. fbuild-1.1.0.dist-info/RECORD +93 -0
  87. fbuild-1.1.0.dist-info/WHEEL +5 -0
  88. fbuild-1.1.0.dist-info/entry_points.txt +5 -0
  89. fbuild-1.1.0.dist-info/licenses/LICENSE +21 -0
  90. fbuild-1.1.0.dist-info/top_level.txt +2 -0
  91. fbuild_lint/__init__.py +0 -0
  92. fbuild_lint/ruff_plugins/__init__.py +0 -0
  93. fbuild_lint/ruff_plugins/keyboard_interrupt_checker.py +158 -0
@@ -0,0 +1,413 @@
1
+ """ESP32 library dependency management.
2
+
3
+ This module handles downloading and compiling external libraries for ESP32 builds.
4
+ It uses the PlatformIO registry to resolve and download libraries, then compiles
5
+ them with the ESP32 toolchain.
6
+ """
7
+
8
+ import json
9
+ import subprocess
10
+ from pathlib import Path
11
+ from typing import List, Optional
12
+
13
+ from fbuild.packages.platformio_registry import (
14
+ LibrarySpec,
15
+ PlatformIORegistry,
16
+ RegistryError,
17
+ )
18
+
19
+
20
+ class LibraryErrorESP32(Exception):
21
+ """Exception for ESP32 library management errors."""
22
+
23
+ pass
24
+
25
+
26
+ class LibraryESP32:
27
+ """Represents a downloaded and compiled ESP32 library."""
28
+
29
+ def __init__(self, lib_dir: Path, name: str):
30
+ """Initialize ESP32 library.
31
+
32
+ Args:
33
+ lib_dir: Root directory for the library
34
+ name: Library name
35
+ """
36
+ self.lib_dir = lib_dir
37
+ self.name = name
38
+ self.src_dir = lib_dir / "src"
39
+ self.info_file = lib_dir / "library.json"
40
+ self.archive_file = lib_dir / f"lib{name}.a"
41
+ self.build_info_file = lib_dir / "build_info.json"
42
+
43
+ @property
44
+ def exists(self) -> bool:
45
+ """Check if library is downloaded and compiled."""
46
+ return self.lib_dir.exists() and self.src_dir.exists() and self.info_file.exists()
47
+
48
+ @property
49
+ def is_compiled(self) -> bool:
50
+ """Check if library is compiled."""
51
+ return self.archive_file.exists() and self.build_info_file.exists()
52
+
53
+ def get_source_files(self) -> List[Path]:
54
+ """Find all source files (.c, .cpp, .cc, .cxx) in the library.
55
+
56
+ Returns:
57
+ List of source file paths
58
+ """
59
+ if not self.src_dir.exists():
60
+ return []
61
+
62
+ sources = []
63
+
64
+ # Check for src/src/ structure (some libraries have this)
65
+ src_src = self.src_dir / "src"
66
+ search_dir = src_src if (src_src.exists() and src_src.is_dir()) else self.src_dir
67
+
68
+ # Find all source files recursively
69
+ for pattern in ["**/*.c", "**/*.cpp", "**/*.cc", "**/*.cxx"]:
70
+ for path in search_dir.glob(pattern):
71
+ # Skip examples and tests (check relative path only)
72
+ rel_path = str(path.relative_to(search_dir)).lower()
73
+ if "example" not in rel_path and "test" not in rel_path:
74
+ sources.append(path)
75
+
76
+ return sources
77
+
78
+ def get_include_dirs(self) -> List[Path]:
79
+ """Get include directories for this library.
80
+
81
+ Returns:
82
+ List of include directory paths
83
+ """
84
+ include_dirs = []
85
+
86
+ if not self.src_dir.exists():
87
+ return include_dirs
88
+
89
+ # Check for src/src/ structure
90
+ src_src = self.src_dir / "src"
91
+ if src_src.exists() and src_src.is_dir():
92
+ include_dirs.append(src_src)
93
+ else:
94
+ include_dirs.append(self.src_dir)
95
+
96
+ # Look for additional include directories
97
+ for name in ["include", "Include", "INCLUDE"]:
98
+ inc_dir = self.lib_dir / name
99
+ if inc_dir.exists():
100
+ include_dirs.append(inc_dir)
101
+
102
+ return include_dirs
103
+
104
+
105
+ class LibraryManagerESP32:
106
+ """Manages ESP32 library dependencies."""
107
+
108
+ def __init__(self, build_dir: Path, registry: Optional[PlatformIORegistry] = None):
109
+ """Initialize library manager.
110
+
111
+ Args:
112
+ build_dir: Build directory (.fbuild/build/{board})
113
+ registry: Optional registry client
114
+ """
115
+ self.build_dir = Path(build_dir)
116
+ self.libs_dir = self.build_dir / "libs"
117
+ self.registry = registry or PlatformIORegistry()
118
+
119
+ # Ensure libs directory exists
120
+ self.libs_dir.mkdir(parents=True, exist_ok=True)
121
+
122
+ def _sanitize_name(self, name: str) -> str:
123
+ """Sanitize library name for filesystem.
124
+
125
+ Args:
126
+ name: Library name
127
+
128
+ Returns:
129
+ Sanitized name
130
+ """
131
+ return name.lower().replace("/", "_").replace("@", "_")
132
+
133
+ def get_library(self, spec: LibrarySpec) -> LibraryESP32:
134
+ """Get a library instance for a specification.
135
+
136
+ Args:
137
+ spec: Library specification
138
+
139
+ Returns:
140
+ LibraryESP32 instance
141
+ """
142
+ lib_name = self._sanitize_name(spec.name)
143
+ lib_dir = self.libs_dir / lib_name
144
+ return LibraryESP32(lib_dir, lib_name)
145
+
146
+ def download_library(self, spec: LibrarySpec, show_progress: bool = True) -> LibraryESP32:
147
+ """Download a library from PlatformIO registry.
148
+
149
+ Args:
150
+ spec: Library specification
151
+ show_progress: Whether to show progress
152
+
153
+ Returns:
154
+ LibraryESP32 instance
155
+
156
+ Raises:
157
+ LibraryErrorESP32: If download fails
158
+ """
159
+ try:
160
+ library = self.get_library(spec)
161
+
162
+ # Skip if already downloaded
163
+ if library.exists:
164
+ if show_progress:
165
+ print(f"Library '{spec.name}' already downloaded")
166
+ return library
167
+
168
+ # Download from registry
169
+ self.registry.download_library(spec, library.lib_dir, show_progress=show_progress)
170
+
171
+ return library
172
+
173
+ except RegistryError as e:
174
+ raise LibraryErrorESP32(f"Failed to download library {spec}: {e}") from e
175
+
176
+ def needs_rebuild(self, library: LibraryESP32, compiler_flags: List[str]) -> tuple[bool, str]:
177
+ """Check if a library needs to be rebuilt.
178
+
179
+ Args:
180
+ library: Library to check
181
+ compiler_flags: Current compiler flags
182
+
183
+ Returns:
184
+ Tuple of (needs_rebuild, reason)
185
+ """
186
+ if not library.archive_file.exists():
187
+ return True, "Archive not found"
188
+
189
+ if not library.build_info_file.exists():
190
+ return True, "Build info missing"
191
+
192
+ try:
193
+ with open(library.build_info_file, "r", encoding="utf-8") as f:
194
+ build_info = json.load(f)
195
+
196
+ # Check if compiler flags changed
197
+ stored_flags = build_info.get("compiler_flags", [])
198
+ if stored_flags != compiler_flags:
199
+ return True, "Compiler flags changed"
200
+
201
+ except KeyboardInterrupt as ke:
202
+ from fbuild.interrupt_utils import handle_keyboard_interrupt_properly
203
+
204
+ handle_keyboard_interrupt_properly(ke)
205
+ raise # Never reached, but satisfies type checker
206
+ except Exception:
207
+ return True, "Could not load build info"
208
+
209
+ return False, ""
210
+
211
+ def compile_library(
212
+ self,
213
+ library: LibraryESP32,
214
+ toolchain_path: Path,
215
+ compiler_flags: List[str],
216
+ include_paths: List[Path],
217
+ show_progress: bool = True,
218
+ ) -> Path:
219
+ """Compile a library into a static archive.
220
+
221
+ Args:
222
+ library: Library to compile
223
+ toolchain_path: Path to toolchain bin directory
224
+ compiler_flags: Compiler flags
225
+ include_paths: Include directories
226
+ show_progress: Whether to show progress
227
+
228
+ Returns:
229
+ Path to compiled archive file
230
+
231
+ Raises:
232
+ LibraryErrorESP32: If compilation fails
233
+ """
234
+ try:
235
+ if show_progress:
236
+ print(f"Compiling library: {library.name}")
237
+
238
+ # Get source files
239
+ sources = library.get_source_files()
240
+ if not sources:
241
+ raise LibraryErrorESP32(f"No source files found in library '{library.name}'")
242
+
243
+ if show_progress:
244
+ print(f" Found {len(sources)} source file(s)")
245
+
246
+ # Get library's own include directories
247
+ lib_includes = library.get_include_dirs()
248
+ all_includes = list(include_paths) + lib_includes
249
+
250
+ # Compile each source file
251
+ object_files = []
252
+ gcc_path = toolchain_path / "riscv32-esp-elf-gcc"
253
+ gxx_path = toolchain_path / "riscv32-esp-elf-g++"
254
+
255
+ # Create response file for include paths (avoid Windows command line length limit)
256
+ include_flags = [f"-I{str(inc).replace(chr(92), '/')}" for inc in all_includes]
257
+ response_file = library.lib_dir / "includes.rsp"
258
+ with open(response_file, "w") as f:
259
+ f.write("\n".join(include_flags))
260
+
261
+ for source in sources:
262
+ # Determine compiler based on extension
263
+ if source.suffix in [".cpp", ".cc", ".cxx"]:
264
+ compiler = gxx_path
265
+ else:
266
+ compiler = gcc_path
267
+
268
+ # Output object file
269
+ obj_file = library.lib_dir / f"{source.stem}.o"
270
+
271
+ # Build compile command
272
+ cmd = [str(compiler), "-c"]
273
+ cmd.extend(compiler_flags)
274
+
275
+ # Use response file for include paths
276
+ cmd.append(f"@{response_file}")
277
+ # Add source and output
278
+ cmd.extend(["-o", str(obj_file), str(source)])
279
+
280
+ # Compile
281
+ if show_progress:
282
+ print(f" Compiling {source.name}...")
283
+
284
+ result = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8")
285
+
286
+ if result.returncode != 0:
287
+ raise LibraryErrorESP32(f"Failed to compile {source.name}:\n{result.stderr}")
288
+
289
+ object_files.append(obj_file)
290
+
291
+ # Create static archive using ar
292
+ ar_path = toolchain_path / "riscv32-esp-elf-ar"
293
+
294
+ if show_progress:
295
+ print(f" Creating archive: {library.archive_file.name}")
296
+
297
+ # Remove old archive if exists
298
+ if library.archive_file.exists():
299
+ library.archive_file.unlink()
300
+
301
+ # Create new archive
302
+ cmd = [str(ar_path), "rcs", str(library.archive_file)]
303
+ cmd.extend([str(obj) for obj in object_files])
304
+
305
+ result = subprocess.run(cmd, capture_output=True, text=True, encoding="utf-8")
306
+
307
+ if result.returncode != 0:
308
+ raise LibraryErrorESP32(f"Failed to create archive for {library.name}:\n{result.stderr}")
309
+
310
+ # Save build info
311
+ build_info = {
312
+ "compiler_flags": compiler_flags,
313
+ "source_count": len(sources),
314
+ "object_files": [str(obj) for obj in object_files],
315
+ }
316
+ with open(library.build_info_file, "w", encoding="utf-8") as f:
317
+ json.dump(build_info, f, indent=2)
318
+
319
+ if show_progress:
320
+ print(f"Library '{library.name}' compiled successfully")
321
+
322
+ return library.archive_file
323
+
324
+ except subprocess.CalledProcessError as e:
325
+ raise LibraryErrorESP32(f"Compilation failed for library '{library.name}': {e}") from e
326
+ except KeyboardInterrupt as ke:
327
+ from fbuild.interrupt_utils import handle_keyboard_interrupt_properly
328
+
329
+ handle_keyboard_interrupt_properly(ke)
330
+ raise # Never reached, but satisfies type checker
331
+ except Exception as e:
332
+ raise LibraryErrorESP32(f"Failed to compile library '{library.name}': {e}") from e
333
+
334
+ def ensure_libraries(
335
+ self,
336
+ lib_specs: List[str],
337
+ toolchain_path: Path,
338
+ compiler_flags: List[str],
339
+ include_paths: List[Path],
340
+ show_progress: bool = True,
341
+ ) -> List[LibraryESP32]:
342
+ """Ensure all library dependencies are downloaded and compiled.
343
+
344
+ Args:
345
+ lib_specs: List of library specification strings
346
+ toolchain_path: Path to toolchain bin directory
347
+ compiler_flags: Compiler flags
348
+ include_paths: Include directories
349
+ show_progress: Whether to show progress
350
+
351
+ Returns:
352
+ List of compiled LibraryESP32 instances
353
+ """
354
+ libraries = []
355
+
356
+ for spec_str in lib_specs:
357
+ # Parse library specification
358
+ spec = LibrarySpec.parse(spec_str)
359
+
360
+ # Download library
361
+ library = self.download_library(spec, show_progress)
362
+
363
+ # Check if rebuild needed
364
+ needs_rebuild, reason = self.needs_rebuild(library, compiler_flags)
365
+
366
+ if needs_rebuild:
367
+ if show_progress and reason:
368
+ print(f"Rebuilding library '{library.name}': {reason}")
369
+
370
+ self.compile_library(
371
+ library,
372
+ toolchain_path,
373
+ compiler_flags,
374
+ include_paths,
375
+ show_progress,
376
+ )
377
+ else:
378
+ if show_progress:
379
+ print(f"Library '{library.name}' is up to date")
380
+
381
+ libraries.append(library)
382
+
383
+ return libraries
384
+
385
+ def get_library_archives(self) -> List[Path]:
386
+ """Get paths to all compiled library archives.
387
+
388
+ Returns:
389
+ List of .a archive file paths
390
+ """
391
+ archives = []
392
+ if self.libs_dir.exists():
393
+ for lib_dir in self.libs_dir.iterdir():
394
+ if lib_dir.is_dir():
395
+ archive = lib_dir / f"lib{lib_dir.name}.a"
396
+ if archive.exists():
397
+ archives.append(archive)
398
+ return archives
399
+
400
+ def get_library_include_paths(self) -> List[Path]:
401
+ """Get all include paths from downloaded libraries.
402
+
403
+ Returns:
404
+ List of include directory paths
405
+ """
406
+ include_paths = []
407
+ if self.libs_dir.exists():
408
+ for lib_dir in self.libs_dir.iterdir():
409
+ if lib_dir.is_dir():
410
+ library = LibraryESP32(lib_dir, lib_dir.name)
411
+ if library.exists:
412
+ include_paths.extend(library.get_include_dirs())
413
+ return include_paths
@@ -0,0 +1,163 @@
1
+ """Abstract base classes for package management.
2
+
3
+ This module defines the interface for platform-specific package managers
4
+ (toolchains, frameworks, cores, etc.) to ensure consistent behavior across
5
+ different platforms.
6
+ """
7
+
8
+ from abc import ABC, abstractmethod
9
+ from pathlib import Path
10
+ from typing import Any, Dict, Optional
11
+
12
+
13
+ class PackageError(Exception):
14
+ """Base exception for package management errors."""
15
+
16
+ pass
17
+
18
+
19
+ class IPackage(ABC):
20
+ """Interface for downloadable packages.
21
+
22
+ This interface defines the common contract for all package types:
23
+ - Toolchains (AVR, RISC-V, Xtensa)
24
+ - Frameworks (Arduino-ESP32)
25
+ - Cores (Arduino AVR Core)
26
+ - Platforms (ESP32 Platform)
27
+ """
28
+
29
+ @abstractmethod
30
+ def ensure_package(self) -> Path:
31
+ """Ensure package is downloaded and extracted.
32
+
33
+ Returns:
34
+ Path to the extracted package directory
35
+
36
+ Raises:
37
+ PackageError: If download or extraction fails
38
+ """
39
+ pass
40
+
41
+ @abstractmethod
42
+ def is_installed(self) -> bool:
43
+ """Check if package is already installed.
44
+
45
+ Returns:
46
+ True if package directory exists and is valid
47
+ """
48
+ pass
49
+
50
+ @abstractmethod
51
+ def get_package_info(self) -> Dict[str, Any]:
52
+ """Get information about the package.
53
+
54
+ Returns:
55
+ Dictionary with package metadata (version, path, etc.)
56
+ """
57
+ pass
58
+
59
+
60
+ class IToolchain(IPackage):
61
+ """Interface for toolchain packages.
62
+
63
+ Toolchains provide compiler, linker, and binary utilities for
64
+ specific architectures (AVR, RISC-V, Xtensa).
65
+ """
66
+
67
+ @abstractmethod
68
+ def get_gcc_path(self) -> Optional[Path]:
69
+ """Get path to GCC compiler.
70
+
71
+ Returns:
72
+ Path to gcc binary or None if not found
73
+ """
74
+ pass
75
+
76
+ @abstractmethod
77
+ def get_gxx_path(self) -> Optional[Path]:
78
+ """Get path to G++ compiler.
79
+
80
+ Returns:
81
+ Path to g++ binary or None if not found
82
+ """
83
+ pass
84
+
85
+ @abstractmethod
86
+ def get_ar_path(self) -> Optional[Path]:
87
+ """Get path to archiver (ar).
88
+
89
+ Returns:
90
+ Path to ar binary or None if not found
91
+ """
92
+ pass
93
+
94
+ @abstractmethod
95
+ def get_objcopy_path(self) -> Optional[Path]:
96
+ """Get path to objcopy utility.
97
+
98
+ Returns:
99
+ Path to objcopy binary or None if not found
100
+ """
101
+ pass
102
+
103
+ @abstractmethod
104
+ def get_size_path(self) -> Optional[Path]:
105
+ """Get path to size utility.
106
+
107
+ Returns:
108
+ Path to size binary or None if not found
109
+ """
110
+ pass
111
+
112
+ @abstractmethod
113
+ def get_bin_dir(self) -> Optional[Path]:
114
+ """Get path to toolchain bin directory.
115
+
116
+ Returns:
117
+ Path to bin directory containing compiler binaries
118
+ """
119
+ pass
120
+
121
+ @abstractmethod
122
+ def get_all_tools(self) -> Dict[str, Path]:
123
+ """Get paths to all required tools.
124
+
125
+ Returns:
126
+ Dictionary mapping tool names to their paths
127
+ """
128
+ pass
129
+
130
+
131
+ class IFramework(IPackage):
132
+ """Interface for framework packages.
133
+
134
+ Frameworks provide core Arduino implementation, variants,
135
+ and built-in libraries for specific platforms.
136
+ """
137
+
138
+ @abstractmethod
139
+ def get_cores_dir(self) -> Path:
140
+ """Get path to cores directory.
141
+
142
+ Returns:
143
+ Path to cores directory containing Arduino core implementation
144
+ """
145
+ pass
146
+
147
+ @abstractmethod
148
+ def get_variants_dir(self) -> Path:
149
+ """Get path to variants directory.
150
+
151
+ Returns:
152
+ Path to variants directory containing board-specific configurations
153
+ """
154
+ pass
155
+
156
+ @abstractmethod
157
+ def get_libraries_dir(self) -> Path:
158
+ """Get path to built-in libraries directory.
159
+
160
+ Returns:
161
+ Path to libraries directory
162
+ """
163
+ pass