initial commit
This commit is contained in:
171
libraries/FastLED/ci/compiler/unity_archive.py
Normal file
171
libraries/FastLED/ci/compiler/unity_archive.py
Normal file
@@ -0,0 +1,171 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
from concurrent.futures import Future
|
||||
from pathlib import Path
|
||||
from typing import List, Sequence
|
||||
|
||||
from ci.compiler.clang_compiler import Compiler, CompilerOptions, Result
|
||||
from ci.compiler.test_example_compilation import (
|
||||
create_fastled_compiler,
|
||||
get_fastled_core_sources,
|
||||
)
|
||||
|
||||
|
||||
def _sorted_src_files(files: Sequence[Path]) -> List[Path]:
|
||||
project_root = Path.cwd()
|
||||
|
||||
def key_fn(p: Path) -> str:
|
||||
try:
|
||||
rel = p.relative_to(project_root)
|
||||
except Exception:
|
||||
rel = p
|
||||
return rel.as_posix()
|
||||
|
||||
return sorted(files, key=key_fn)
|
||||
|
||||
|
||||
def _partition(files: List[Path], chunks: int) -> List[List[Path]]:
|
||||
total = len(files)
|
||||
if chunks <= 0:
|
||||
raise ValueError("chunks must be >= 1")
|
||||
chunks = min(chunks, total) if total > 0 else 1
|
||||
base = total // chunks
|
||||
rem = total % chunks
|
||||
partitions: List[List[Path]] = []
|
||||
start = 0
|
||||
for i in range(chunks):
|
||||
size = base + (1 if i < rem else 0)
|
||||
end = start + size
|
||||
if size > 0:
|
||||
partitions.append(files[start:end])
|
||||
start = end
|
||||
return partitions
|
||||
|
||||
|
||||
def build_unity_chunks_and_archive(
|
||||
compiler: Compiler,
|
||||
chunks: int,
|
||||
output_archive: Path,
|
||||
unity_dir: Path,
|
||||
no_parallel: bool,
|
||||
) -> Path:
|
||||
unity_dir.mkdir(parents=True, exist_ok=True)
|
||||
# Gather and sort source files consistently with existing library logic
|
||||
files = get_fastled_core_sources()
|
||||
# Exclude any main-like TU
|
||||
files = [p for p in files if p.name != "stub_main.cpp"]
|
||||
files = _sorted_src_files(files)
|
||||
if not files:
|
||||
raise RuntimeError("No source files found under src/** for unity build")
|
||||
|
||||
partitions = _partition(files, chunks)
|
||||
|
||||
# Prepare per-chunk unity paths
|
||||
unity_cpp_paths: List[Path] = [
|
||||
unity_dir / f"unity{i + 1}.cpp" for i in range(len(partitions))
|
||||
]
|
||||
unity_obj_paths: List[Path] = [p.with_suffix(".o") for p in unity_cpp_paths]
|
||||
|
||||
# Compile chunks
|
||||
futures: List[Future[Result]] = []
|
||||
compile_opts = CompilerOptions(
|
||||
include_path=compiler.settings.include_path,
|
||||
compiler=compiler.settings.compiler,
|
||||
defines=compiler.settings.defines,
|
||||
std_version=compiler.settings.std_version,
|
||||
compiler_args=compiler.settings.compiler_args,
|
||||
use_pch=False,
|
||||
additional_flags=["-c"],
|
||||
parallel=not no_parallel,
|
||||
)
|
||||
|
||||
def submit(idx: int) -> Future[Result]:
|
||||
chunk_files = partitions[idx]
|
||||
unity_cpp = unity_cpp_paths[idx]
|
||||
return compiler.compile_unity(compile_opts, chunk_files, unity_cpp)
|
||||
|
||||
if no_parallel:
|
||||
# Sequential execution
|
||||
for i in range(len(partitions)):
|
||||
result = submit(i).result()
|
||||
if not result.ok:
|
||||
raise RuntimeError(f"Unity chunk {i + 1} failed: {result.stderr}")
|
||||
else:
|
||||
futures = [submit(i) for i in range(len(partitions))]
|
||||
for i, fut in enumerate(futures):
|
||||
result = fut.result()
|
||||
if not result.ok:
|
||||
raise RuntimeError(f"Unity chunk {i + 1} failed: {result.stderr}")
|
||||
|
||||
# Create archive from chunk objects
|
||||
from ci.compiler.clang_compiler import LibarchiveOptions
|
||||
|
||||
output_archive.parent.mkdir(parents=True, exist_ok=True)
|
||||
archive_result = compiler.create_archive(
|
||||
unity_obj_paths, output_archive, LibarchiveOptions()
|
||||
).result()
|
||||
if not archive_result.ok:
|
||||
raise RuntimeError(f"Archive creation failed: {archive_result.stderr}")
|
||||
|
||||
return output_archive
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Chunked UNITY build and archive for FastLED src/**"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--chunks", type=int, default=1, help="Number of unity chunks (1-4 typical)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-parallel", action="store_true", help="Compile unity chunks sequentially"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--output",
|
||||
type=str,
|
||||
default=str(Path(".build/fastled/libfastled.a")),
|
||||
help="Path to output archive libfastled.a",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--unity-dir",
|
||||
type=str,
|
||||
default=str(Path(".build/fastled/unity")),
|
||||
help="Directory to place generated unity{N}.cpp/.o files",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--use-pch",
|
||||
action="store_true",
|
||||
help="Enable PCH (not recommended for unity builds)",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# Anchor to project root (two levels up from this file)
|
||||
here = Path(__file__).resolve()
|
||||
project_root = here.parent.parent.parent
|
||||
os.chdir(project_root)
|
||||
|
||||
try:
|
||||
compiler = create_fastled_compiler(
|
||||
use_pch=args.use_pch, parallel=not args.no_parallel
|
||||
)
|
||||
out = build_unity_chunks_and_archive(
|
||||
compiler=compiler,
|
||||
chunks=args.chunks,
|
||||
output_archive=Path(args.output),
|
||||
unity_dir=Path(args.unity_dir),
|
||||
no_parallel=args.no_parallel,
|
||||
)
|
||||
print(f"[UNITY] Created archive: {out}")
|
||||
return 0
|
||||
except KeyboardInterrupt:
|
||||
raise
|
||||
except Exception as e:
|
||||
print(f"\x1b[31m[UNITY] FAILED: {e}\x1b[0m")
|
||||
return 1
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user