197 lines
6.5 KiB
Python
197 lines
6.5 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
FastLED Dynamic Library Builder
|
|
Builds FastLED as a shared/dynamic library for unit testing
|
|
"""
|
|
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import List
|
|
|
|
from ci.compiler.clang_compiler import (
|
|
BuildFlags,
|
|
Compiler,
|
|
CompilerOptions,
|
|
Result,
|
|
)
|
|
from ci.util.paths import PROJECT_ROOT
|
|
|
|
|
|
def build_fastled_dynamic_library(build_dir: Path) -> Path:
|
|
"""Build FastLED as a dynamic library"""
|
|
print("Building FastLED dynamic library...")
|
|
|
|
# Define library path with appropriate extension
|
|
if sys.platform == "win32":
|
|
fastled_lib_path = build_dir / "fastled.dll"
|
|
else:
|
|
fastled_lib_path = build_dir / "libfastled.so"
|
|
|
|
# Configure compiler with shared library flags
|
|
settings = CompilerOptions(
|
|
include_path=str(Path(PROJECT_ROOT) / "src"),
|
|
defines=[
|
|
"STUB_PLATFORM",
|
|
"ARDUINO=10808",
|
|
"FASTLED_USE_STUB_ARDUINO",
|
|
"SKETCH_HAS_LOTS_OF_MEMORY=1",
|
|
"FASTLED_STUB_IMPL",
|
|
"FASTLED_FORCE_NAMESPACE=1",
|
|
"FASTLED_TESTING",
|
|
"FASTLED_NO_AUTO_NAMESPACE",
|
|
"FASTLED_NO_PINMAP",
|
|
"HAS_HARDWARE_PIN_SUPPORT",
|
|
# "FASTLED_DEBUG_LEVEL=1",
|
|
# "BUILDING_FASTLED_DLL", # New define for DLL exports
|
|
# "FASTLED_EXPORT=__declspec(dllexport)" if sys.platform == "win32" else "FASTLED_EXPORT=", # DLL exports for Windows
|
|
],
|
|
std_version="c++17",
|
|
compiler="clang++",
|
|
compiler_args=[
|
|
# NOTE: All compiler flags should come from build_unit.toml
|
|
# Keep only platform-specific include paths and shared library flags
|
|
"-I" + str(Path(PROJECT_ROOT) / "src/platforms/stub"),
|
|
"-I" + str(Path(PROJECT_ROOT) / "tests"),
|
|
# Add shared library flags - platform specific
|
|
"-shared" if sys.platform != "win32" else "/DLL",
|
|
"-fPIC" if sys.platform != "win32" else "",
|
|
],
|
|
use_pch=True,
|
|
parallel=True,
|
|
)
|
|
|
|
# Load build flags from TOML
|
|
build_flags_path = Path(PROJECT_ROOT) / "ci" / "build_unit.toml"
|
|
build_flags = BuildFlags.parse(
|
|
build_flags_path, quick_build=False, strict_mode=False
|
|
)
|
|
|
|
compiler = Compiler(settings, build_flags)
|
|
|
|
# Compile all FastLED source files
|
|
fastled_src_dir = Path(PROJECT_ROOT) / "src"
|
|
all_cpp_files = list(fastled_src_dir.rglob("*.cpp"))
|
|
|
|
print(f"Found {len(all_cpp_files)} FastLED source files")
|
|
|
|
# Compile to object files
|
|
object_files: List[Path] = []
|
|
for src_file in all_cpp_files:
|
|
relative_path = src_file.relative_to(fastled_src_dir)
|
|
safe_name = (
|
|
str(relative_path.with_suffix("")).replace("/", "_").replace("\\", "_")
|
|
)
|
|
obj_path = build_dir / f"{safe_name}_fastled.o"
|
|
|
|
future = compiler.compile_cpp_file(
|
|
src_file,
|
|
output_path=obj_path,
|
|
additional_flags=[
|
|
"-c",
|
|
"-DFASTLED_STUB_IMPL",
|
|
"-DFASTLED_FORCE_NAMESPACE=1",
|
|
"-DFASTLED_NO_AUTO_NAMESPACE",
|
|
"-DFASTLED_NO_PINMAP",
|
|
"-DPROGMEM=",
|
|
"-DHAS_HARDWARE_PIN_SUPPORT",
|
|
"-DFASTLED_ENABLE_JSON=1",
|
|
"-fno-exceptions",
|
|
"-fno-rtti",
|
|
"-fPIC", # Position Independent Code for shared library
|
|
],
|
|
)
|
|
result: Result = future.result()
|
|
if not result.ok:
|
|
print(f"ERROR: Failed to compile {src_file}: {result.stderr}")
|
|
continue
|
|
|
|
object_files.append(obj_path)
|
|
|
|
print(f"Linking {len(object_files)} object files into dynamic library...")
|
|
|
|
# Link as shared library
|
|
if sys.platform == "win32":
|
|
# Use lld-link for Windows DLL creation
|
|
link_cmd = [
|
|
"lld-link",
|
|
"/DLL",
|
|
"/NOLOGO",
|
|
"/OUT:" + str(fastled_lib_path),
|
|
"/LIBPATH:C:/Program Files (x86)/Windows Kits/10/Lib/10.0.19041.0/um/x64",
|
|
"/LIBPATH:C:/Program Files (x86)/Windows Kits/10/Lib/10.0.19041.0/ucrt/x64",
|
|
"/LIBPATH:C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.37.32822/lib/x64",
|
|
*[str(obj) for obj in object_files],
|
|
"msvcrt.lib",
|
|
"legacy_stdio_definitions.lib",
|
|
"kernel32.lib",
|
|
"user32.lib",
|
|
]
|
|
else:
|
|
link_cmd = [
|
|
"clang++",
|
|
"-shared",
|
|
"-o",
|
|
str(fastled_lib_path),
|
|
*[str(obj) for obj in object_files],
|
|
]
|
|
|
|
try:
|
|
# Use streaming to prevent buffer overflow
|
|
process = subprocess.Popen(
|
|
link_cmd,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE,
|
|
text=True,
|
|
bufsize=1,
|
|
encoding="utf-8",
|
|
errors="replace",
|
|
)
|
|
|
|
stdout_lines: list[str] = []
|
|
stderr_lines: list[str] = []
|
|
|
|
while True:
|
|
stdout_line = process.stdout.readline() if process.stdout else ""
|
|
stderr_line = process.stderr.readline() if process.stderr else ""
|
|
|
|
if stdout_line:
|
|
stdout_lines.append(stdout_line.rstrip())
|
|
if stderr_line:
|
|
stderr_lines.append(stderr_line.rstrip())
|
|
|
|
if process.poll() is not None:
|
|
remaining_stdout = process.stdout.read() if process.stdout else ""
|
|
remaining_stderr = process.stderr.read() if process.stderr else ""
|
|
|
|
if remaining_stdout:
|
|
for line in remaining_stdout.splitlines():
|
|
stdout_lines.append(line.rstrip())
|
|
if remaining_stderr:
|
|
for line in remaining_stderr.splitlines():
|
|
stderr_lines.append(line.rstrip())
|
|
break
|
|
|
|
stderr_result = "\n".join(stderr_lines)
|
|
if process.returncode != 0:
|
|
raise Exception(f"Failed to create dynamic library: {stderr_result}")
|
|
|
|
print(f"Successfully created dynamic library: {fastled_lib_path}")
|
|
return fastled_lib_path
|
|
|
|
except Exception as e:
|
|
print(f"ERROR: Exception during library creation: {e}")
|
|
raise
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# Create build directory in temp
|
|
import tempfile
|
|
|
|
build_dir = Path(tempfile.gettempdir()) / "fastled_test_build"
|
|
build_dir.mkdir(parents=True, exist_ok=True)
|
|
|
|
# Build the dynamic library
|
|
build_fastled_dynamic_library(build_dir)
|