Files
rt-thread/tools/ng/environment.py

298 lines
8.8 KiB
Python

# -*- coding: utf-8 -*-
"""
Environment extensions for RT-Thread build system.
This module provides methods that are injected into the SCons Environment object
to provide RT-Thread-specific functionality.
"""
import os
from typing import List, Union, Dict, Any, Optional
from SCons.Script import *
from .core import BuildContext
from .project import ProjectGroup
class RTEnv:
"""
RT-Thread environment extensions (RTEnv).
This class provides methods that are added to the SCons Environment object.
"""
@staticmethod
def inject_methods(env):
"""
Inject RT-Thread methods into SCons Environment.
Args:
env: SCons Environment object
"""
# Core build methods
env.AddMethod(RTEnv.DefineGroup, 'DefineGroup')
env.AddMethod(RTEnv.GetDepend, 'GetDepend')
env.AddMethod(RTEnv.SrcRemove, 'SrcRemove')
env.AddMethod(RTEnv.GetCurrentDir, 'GetCurrentDir')
env.AddMethod(RTEnv.BuildPackage, 'BuildPackage')
# Utility methods
env.AddMethod(RTEnv.Glob, 'GlobFiles')
env.AddMethod(RTEnv.GetBuildOptions, 'GetBuildOptions')
env.AddMethod(RTEnv.GetContext, 'GetContext')
# Path utilities
env.AddMethod(RTEnv.GetRTTRoot, 'GetRTTRoot')
env.AddMethod(RTEnv.GetBSPRoot, 'GetBSPRoot')
@staticmethod
def DefineGroup(env, name: str, src: List[str], depend: Any = None, **kwargs) -> List:
"""
Define a component group.
This method maintains compatibility with the original DefineGroup function
while using the new object-oriented implementation.
Args:
env: SCons Environment
name: Group name
src: Source file list
depend: Dependency conditions
**kwargs: Additional parameters (CPPPATH, CPPDEFINES, etc.)
Returns:
List of build objects
"""
context = BuildContext.get_current()
if not context:
raise RuntimeError("BuildContext not initialized")
# Check dependencies
if depend and not env.GetDepend(depend):
return []
# Process source files
if isinstance(src, str):
src = [src]
# Create project group
group = ProjectGroup(
name=name,
sources=src,
dependencies=depend if isinstance(depend, list) else [depend] if depend else [],
environment=env
)
# Process parameters
group.include_paths = kwargs.get('CPPPATH', [])
group.defines = kwargs.get('CPPDEFINES', {})
group.cflags = kwargs.get('CFLAGS', '')
group.cxxflags = kwargs.get('CXXFLAGS', '')
group.local_cflags = kwargs.get('LOCAL_CFLAGS', '')
group.local_cxxflags = kwargs.get('LOCAL_CXXFLAGS', '')
group.local_include_paths = kwargs.get('LOCAL_CPPPATH', [])
group.local_defines = kwargs.get('LOCAL_CPPDEFINES', {})
group.libs = kwargs.get('LIBS', [])
group.lib_paths = kwargs.get('LIBPATH', [])
# Build objects
objects = group.build(env)
# Register group
context.register_project_group(group)
return objects
@staticmethod
def GetDepend(env, depend: Any) -> bool:
"""
Check if dependency is satisfied.
Args:
env: SCons Environment
depend: Dependency name or list of names
Returns:
True if dependency is satisfied
"""
context = BuildContext.get_current()
if not context:
# Fallback to checking environment variables
if isinstance(depend, str):
return env.get(depend, False)
elif isinstance(depend, list):
return all(env.get(d, False) for d in depend)
return False
return context.get_dependency(depend)
@staticmethod
def SrcRemove(env, src: List[str], remove: List[str]) -> None:
"""
Remove files from source list.
Args:
env: SCons Environment
src: Source file list (modified in place)
remove: Files to remove
"""
if not isinstance(remove, list):
remove = [remove]
for item in remove:
# Handle both exact matches and pattern matches
if item in src:
src.remove(item)
else:
# Try pattern matching
import fnmatch
to_remove = [f for f in src if fnmatch.fnmatch(f, item)]
for f in to_remove:
src.remove(f)
@staticmethod
def GetCurrentDir(env) -> str:
"""
Get current directory.
Args:
env: SCons Environment
Returns:
Current directory path
"""
return Dir('.').abspath
@staticmethod
def BuildPackage(env, package_path: str = None) -> List:
"""
Build package from package.json.
Args:
env: SCons Environment
package_path: Path to package.json. If None, looks for package.json in current directory.
Returns:
List of build objects
"""
# Import the existing package module
import sys
import os
# Get the building module path
building_path = os.path.dirname(os.path.abspath(__file__))
tools_path = os.path.dirname(building_path)
# Add to path if not already there
if tools_path not in sys.path:
sys.path.insert(0, tools_path)
# Import and use the existing BuildPackage
try:
from package import BuildPackage as build_package_func
# BuildPackage uses global functions, so we need to set up the context
# Save current directory
current_dir = os.getcwd()
# Change to the directory where we want to build
if package_path is None:
work_dir = env.GetCurrentDir()
elif os.path.isdir(package_path):
work_dir = package_path
else:
work_dir = os.path.dirname(package_path)
os.chdir(work_dir)
try:
# Call the original BuildPackage
result = build_package_func(package_path)
finally:
# Restore directory
os.chdir(current_dir)
return result
except ImportError:
# Fallback if import fails
context = BuildContext.get_current()
if context:
context.logger.error("Failed to import package module")
return []
@staticmethod
def Glob(env, pattern: str) -> List[str]:
"""
Enhanced glob with better error handling.
Args:
env: SCons Environment
pattern: File pattern
Returns:
List of matching files
"""
try:
files = Glob(pattern, strings=True)
return sorted(files) # Sort for consistent ordering
except Exception as e:
context = BuildContext.get_current()
if context:
context.logger.warning(f"Glob pattern '{pattern}' failed: {e}")
return []
@staticmethod
def GetBuildOptions(env) -> Dict[str, Any]:
"""
Get build options.
Args:
env: SCons Environment
Returns:
Dictionary of build options
"""
context = BuildContext.get_current()
if context:
return context.build_options
return {}
@staticmethod
def GetContext(env) -> Optional[BuildContext]:
"""
Get current build context.
Args:
env: SCons Environment
Returns:
BuildContext instance or None
"""
return BuildContext.get_current()
@staticmethod
def GetRTTRoot(env) -> str:
"""
Get RT-Thread root directory.
Args:
env: SCons Environment
Returns:
RT-Thread root path
"""
return env.get('RTT_ROOT', '')
@staticmethod
def GetBSPRoot(env) -> str:
"""
Get BSP root directory.
Args:
env: SCons Environment
Returns:
BSP root path
"""
return env.get('BSP_ROOT', '')