| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304 |
- # -*- 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 or directory containing it
-
- Returns:
- List of build objects
- """
- import json
-
- # Find package.json
- if package_path is None:
- package_path = 'package.json'
- elif os.path.isdir(package_path):
- package_path = os.path.join(package_path, 'package.json')
-
- if not os.path.exists(package_path):
- env.GetContext().logger.error(f"Package file not found: {package_path}")
- return []
-
- # Load package definition
- with open(package_path, 'r') as f:
- package = json.load(f)
-
- # Process package
- name = package.get('name', 'unnamed')
- dependencies = package.get('dependencies', {})
-
- # Check main dependency
- if 'RT_USING_' + name.upper() not in dependencies:
- main_depend = 'RT_USING_' + name.upper().replace('-', '_')
- else:
- main_depend = list(dependencies.keys())[0]
-
- if not env.GetDepend(main_depend):
- return []
-
- # Collect sources
- src = []
- include_paths = []
-
- sources = package.get('sources', {})
- for category, config in sources.items():
- # Check condition
- condition = config.get('condition')
- if condition and not eval(condition, {'env': env, 'GetDepend': env.GetDepend}):
- continue
-
- # Add source files
- source_files = config.get('source_files', [])
- for pattern in source_files:
- src.extend(env.Glob(pattern))
-
- # Add header paths
- header_path = config.get('header_path', [])
- include_paths.extend(header_path)
-
- # Create group
- return env.DefineGroup(name, src, depend=main_depend, CPPPATH=include_paths)
-
- @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', '')
|