1# -*- coding: utf-8 -*-
2"""
3Core module for RT-Thread build system.
4
5This module provides the central BuildContext class that manages the build state
6and coordinates between different components.
7"""
8
9import os
10import logging
11from typing import Dict, List, Optional, Any
12from dataclasses import dataclass, field
13
14from .config import ConfigManager
15from .project import ProjectRegistry
16from .toolchain import ToolchainManager
17from .generator import GeneratorRegistry
18from .utils import PathService
19
20
21class BuildContext:
22    """
23    Central build context that manages all build-related state.
24
25    This class replaces the global variables in building.py with a proper
26    object-oriented design while maintaining compatibility.
27    """
28
29    # Class variable to store the current context (for backward compatibility)
30    _current_context: Optional['BuildContext'] = None
31
32    def __init__(self, root_directory: str):
33        """
34        Initialize build context.
35
36        Args:
37            root_directory: RT-Thread root directory path
38        """
39        self.root_directory = os.path.abspath(root_directory)
40        self.bsp_directory = os.getcwd()
41
42        # Initialize managers
43        self.config_manager = ConfigManager()
44        self.project_registry = ProjectRegistry()
45        self.toolchain_manager = ToolchainManager()
46        self.generator_registry = GeneratorRegistry()
47        self.path_service = PathService(self.bsp_directory)
48
49        # Build environment
50        self.environment = None
51        self.build_options = {}
52
53        # Logging
54        self.logger = self._setup_logger()
55
56        # Set as current context
57        BuildContext._current_context = self
58
59    @classmethod
60    def get_current(cls) -> Optional['BuildContext']:
61        """Get the current build context."""
62        return cls._current_context
63
64    @classmethod
65    def set_current(cls, context: Optional['BuildContext']) -> None:
66        """Set the current build context."""
67        cls._current_context = context
68
69    def _setup_logger(self) -> logging.Logger:
70        """Setup logger for build system."""
71        logger = logging.getLogger('rtthread.build')
72        if not logger.handlers:
73            handler = logging.StreamHandler()
74            formatter = logging.Formatter('[%(levelname)s] %(message)s')
75            handler.setFormatter(formatter)
76            logger.addHandler(handler)
77            logger.setLevel(logging.INFO)
78        return logger
79
80    def prepare_environment(self, env) -> None:
81        """
82        Prepare the build environment.
83
84        Args:
85            env: SCons Environment object
86        """
87        self.environment = env
88
89        # Set environment variables
90        env['RTT_ROOT'] = self.root_directory
91        env['BSP_ROOT'] = self.bsp_directory
92
93        # Add to Python path
94        import sys
95        tools_path = os.path.join(self.root_directory, 'tools')
96        if tools_path not in sys.path:
97            sys.path.insert(0, tools_path)
98
99        self.logger.debug(f"Prepared environment with RTT_ROOT={self.root_directory}")
100
101    def load_configuration(self, config_file: str = 'rtconfig.h') -> None:
102        """
103        Load configuration from rtconfig.h.
104
105        Args:
106            config_file: Path to configuration file
107        """
108        config_path = os.path.join(self.bsp_directory, config_file)
109        if os.path.exists(config_path):
110            self.config_manager.load_from_file(config_path)
111            self.build_options = self.config_manager.get_all_options()
112            self.logger.info(f"Loaded configuration from {config_file}")
113        else:
114            self.logger.warning(f"Configuration file {config_file} not found")
115
116    def get_dependency(self, depend: Any) -> bool:
117        """
118        Check if dependency is satisfied.
119
120        Args:
121            depend: Dependency name or list of names
122
123        Returns:
124            True if dependency is satisfied
125        """
126        return self.config_manager.get_dependency(depend)
127
128    def register_project_group(self, group) -> None:
129        """
130        Register a project group.
131
132        Args:
133            group: ProjectGroup instance
134        """
135        self.project_registry.register_group(group)
136
137    def merge_groups(self) -> List:
138        """
139        Merge all registered project groups.
140
141        Returns:
142            List of build objects
143        """
144        return self.project_registry.merge_groups(self.environment)
145
146
147@dataclass
148class BuildOptions:
149    """Build options container."""
150    verbose: bool = False
151    strict: bool = False
152    target: Optional[str] = None
153    jobs: int = 1
154    clean: bool = False
155
156
157@dataclass
158class ProjectInfo:
159    """Project information for generators."""
160    name: str = "rtthread"
161    target_name: str = "rtthread.elf"
162
163    # File collections
164    source_files: List[str] = field(default_factory=list)
165    include_paths: List[str] = field(default_factory=list)
166    defines: Dict[str, str] = field(default_factory=dict)
167
168    # Compiler options
169    cflags: str = ""
170    cxxflags: str = ""
171    asflags: str = ""
172    ldflags: str = ""
173
174    # Libraries
175    libs: List[str] = field(default_factory=list)
176    lib_paths: List[str] = field(default_factory=list)