247 lines
8.1 KiB
Python
247 lines
8.1 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
FastLED All Source Build Script
|
|
|
|
This script implements the unified source build system for FastLED by:
|
|
1. Starting with ONE file for testing (allocator.cpp)
|
|
2. Copying original .cpp content to .cpp.hpp
|
|
3. Replacing .cpp with conditional wrapper code
|
|
4. Creating src/fastled_compile.cpp to include .cpp.hpp files
|
|
5. Adding FASTLED_ALL_SRC logic to compiler_control.h
|
|
|
|
CRITICAL: This script processes original .cpp content CORRECTLY:
|
|
- Step 1: Copy original .cpp -> .cpp.hpp (preserves source)
|
|
- Step 2: Replace .cpp with wrapper (conditional include)
|
|
"""
|
|
|
|
import os
|
|
import shutil
|
|
import glob
|
|
from pathlib import Path
|
|
from typing import List, Set
|
|
|
|
|
|
def find_cpp_files(base_dirs: List[str]) -> List[Path]:
|
|
"""Find all .cpp files in the specified directories and subdirectories."""
|
|
cpp_files = []
|
|
for base_dir in base_dirs:
|
|
if not os.path.exists(base_dir):
|
|
continue
|
|
pattern = os.path.join(base_dir, "**", "*.cpp")
|
|
cpp_files.extend(Path(f) for f in glob.glob(pattern, recursive=True))
|
|
return sorted(cpp_files)
|
|
|
|
|
|
def get_relative_include_path(file_path: Path, from_src: bool = True) -> str:
|
|
"""Get the relative include path from src/ directory."""
|
|
if from_src:
|
|
# Remove 'src/' prefix if present
|
|
parts = file_path.parts
|
|
if parts[0] == 'src':
|
|
return '/'.join(parts[1:])
|
|
return str(file_path)
|
|
return str(file_path)
|
|
|
|
|
|
def create_cpp_wrapper(original_cpp: Path, hpp_file: Path) -> str:
|
|
"""Create the wrapper .cpp file content with conditional includes."""
|
|
relative_hpp_path = get_relative_include_path(hpp_file)
|
|
|
|
content = f'''#include "fl/compiler_control.h"
|
|
|
|
#if !FASTLED_ALL_SRC
|
|
#include "{relative_hpp_path}"
|
|
#endif
|
|
'''
|
|
return content
|
|
|
|
|
|
def update_compiler_control_h(compiler_control_path: Path) -> None:
|
|
"""Add FASTLED_ALL_SRC logic to compiler_control.h."""
|
|
with open(compiler_control_path, 'r') as f:
|
|
content = f.read()
|
|
|
|
# Check if FASTLED_ALL_SRC is already defined
|
|
if 'FASTLED_ALL_SRC' in content:
|
|
print(f"FASTLED_ALL_SRC already defined in {compiler_control_path}")
|
|
return
|
|
|
|
# Add FASTLED_ALL_SRC logic at the end
|
|
fastled_all_src_logic = '''
|
|
|
|
// All Source Build Control
|
|
// When FASTLED_ALL_SRC is enabled, all source is compiled into a single translation unit
|
|
// Debug/testing builds use individual compilation for better error isolation
|
|
#ifndef FASTLED_ALL_SRC
|
|
#if defined(DEBUG) || defined(FASTLED_TESTING)
|
|
#define FASTLED_ALL_SRC 0
|
|
#elif !defined(RELEASE) || (RELEASE == 0)
|
|
#define FASTLED_ALL_SRC 1
|
|
#else
|
|
#define FASTLED_ALL_SRC 0
|
|
#endif
|
|
#endif
|
|
'''
|
|
|
|
updated_content = content + fastled_all_src_logic
|
|
|
|
with open(compiler_control_path, 'w') as f:
|
|
f.write(updated_content)
|
|
|
|
print(f"Updated {compiler_control_path} with FASTLED_ALL_SRC logic")
|
|
|
|
|
|
def create_fastled_compile_cpp(cpp_hpp_files: List[Path], output_path: Path) -> None:
|
|
"""Create src/fastled_compile.cpp with all .cpp.hpp includes."""
|
|
includes = []
|
|
for hpp_file in sorted(cpp_hpp_files):
|
|
relative_path = get_relative_include_path(hpp_file)
|
|
includes.append(f'#include "{relative_path}"')
|
|
|
|
content = f'''// FastLED All Source Build File
|
|
// This file includes all .cpp.hpp files for unified compilation
|
|
// Generated automatically by scripts/all_source_build.py
|
|
|
|
#include "fl/compiler_control.h"
|
|
|
|
#if FASTLED_ALL_SRC
|
|
|
|
{chr(10).join(includes)}
|
|
|
|
#endif // FASTLED_ALL_SRC
|
|
'''
|
|
|
|
with open(output_path, 'w') as f:
|
|
f.write(content)
|
|
|
|
print(f"Created {output_path} with {len(includes)} includes")
|
|
|
|
|
|
def process_single_cpp_file(cpp_file: Path, dry_run: bool = False) -> Path:
|
|
"""
|
|
Process a single .cpp file, converting it to the new format.
|
|
|
|
CRITICAL LOGIC:
|
|
1. Copy original .cpp content to .cpp.hpp (PRESERVES SOURCE)
|
|
2. Replace .cpp with wrapper code (CONDITIONAL INCLUDE)
|
|
|
|
Returns the .cpp.hpp file path.
|
|
"""
|
|
hpp_file = cpp_file.with_suffix('.cpp.hpp')
|
|
|
|
print(f"Processing {cpp_file} -> {hpp_file}")
|
|
|
|
if not dry_run:
|
|
# STEP 1: Copy original .cpp content to .cpp.hpp (PRESERVE ORIGINAL SOURCE)
|
|
print(f" Step 1: Copying original content {cpp_file} -> {hpp_file}")
|
|
shutil.copy2(cpp_file, hpp_file)
|
|
|
|
# STEP 2: Replace .cpp with wrapper code (CONDITIONAL INCLUDE)
|
|
print(f" Step 2: Replacing {cpp_file} with wrapper code")
|
|
wrapper_content = create_cpp_wrapper(cpp_file, hpp_file)
|
|
with open(cpp_file, 'w') as f:
|
|
f.write(wrapper_content)
|
|
|
|
return hpp_file
|
|
|
|
|
|
def process_cpp_files(cpp_files: List[Path], dry_run: bool = False) -> List[Path]:
|
|
"""Process all .cpp files, converting them to the new format."""
|
|
processed_hpp_files = []
|
|
|
|
for cpp_file in cpp_files:
|
|
hpp_file = process_single_cpp_file(cpp_file, dry_run)
|
|
processed_hpp_files.append(hpp_file)
|
|
|
|
return processed_hpp_files
|
|
|
|
|
|
def main():
|
|
"""Main function to run the all source build transformation."""
|
|
import argparse
|
|
|
|
parser = argparse.ArgumentParser(description='FastLED All Source Build Script')
|
|
parser.add_argument('--dry-run', action='store_true',
|
|
help='Show what would be done without making changes')
|
|
parser.add_argument('--src-dir', default='src',
|
|
help='Source directory (default: src)')
|
|
parser.add_argument('--single-file', action='store_true',
|
|
help='Process only allocator.cpp for testing')
|
|
|
|
args = parser.parse_args()
|
|
|
|
# Define target directories
|
|
target_dirs = [
|
|
os.path.join(args.src_dir, 'fl'),
|
|
os.path.join(args.src_dir, 'sensors'),
|
|
os.path.join(args.src_dir, 'fx')
|
|
]
|
|
|
|
# Check if directories exist
|
|
for dir_path in target_dirs:
|
|
if not os.path.exists(dir_path):
|
|
print(f"Warning: Directory {dir_path} does not exist")
|
|
|
|
# Find all .cpp files
|
|
all_cpp_files = find_cpp_files(target_dirs)
|
|
|
|
# If single-file mode, only process allocator.cpp
|
|
if args.single_file:
|
|
allocator_files = [f for f in all_cpp_files if f.name == 'allocator.cpp']
|
|
if not allocator_files:
|
|
print("ERROR: allocator.cpp not found!")
|
|
return
|
|
cpp_files = allocator_files
|
|
print(f"SINGLE FILE MODE: Processing only {cpp_files[0]}")
|
|
else:
|
|
cpp_files = all_cpp_files
|
|
print(f"Found {len(cpp_files)} .cpp files:")
|
|
for cpp_file in cpp_files:
|
|
print(f" {cpp_file}")
|
|
|
|
if not cpp_files:
|
|
print("No .cpp files found!")
|
|
return
|
|
|
|
if args.dry_run:
|
|
print("\n--- DRY RUN MODE ---")
|
|
print("The following actions would be performed:")
|
|
|
|
for cpp_file in cpp_files:
|
|
hpp_file = cpp_file.with_suffix('.cpp.hpp')
|
|
print(f" 1. Copy {cpp_file} -> {hpp_file} (preserve original)")
|
|
print(f" 2. Replace {cpp_file} with conditional wrapper")
|
|
|
|
print(f" 3. Update {args.src_dir}/fl/compiler_control.h")
|
|
print(f" 4. Create {args.src_dir}/fastled_compile.cpp")
|
|
return
|
|
|
|
print(f"\nStarting all source build transformation...")
|
|
|
|
# Process .cpp files
|
|
processed_hpp_files = process_cpp_files(cpp_files, dry_run=args.dry_run)
|
|
|
|
# Update compiler_control.h
|
|
compiler_control_path = Path(args.src_dir) / 'fl' / 'compiler_control.h'
|
|
if compiler_control_path.exists():
|
|
update_compiler_control_h(compiler_control_path)
|
|
else:
|
|
print(f"Warning: {compiler_control_path} not found")
|
|
|
|
# Create fastled_compile.cpp
|
|
fastled_compile_path = Path(args.src_dir) / 'fastled_compile.cpp'
|
|
create_fastled_compile_cpp(processed_hpp_files, fastled_compile_path)
|
|
|
|
print(f"\nAll source build transformation complete!")
|
|
print(f"Processed {len(cpp_files)} .cpp files")
|
|
print(f"Created {len(processed_hpp_files)} .cpp.hpp files")
|
|
|
|
if args.single_file:
|
|
print(f"\n*** SINGLE FILE MODE COMPLETE ***")
|
|
print(f"Test with: bash test")
|
|
print(f"If successful, run again without --single-file for full conversion")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|