environment.py 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. # -*- coding: utf-8 -*-
  2. """
  3. Environment extensions for RT-Thread build system.
  4. This module provides methods that are injected into the SCons Environment object
  5. to provide RT-Thread-specific functionality.
  6. """
  7. import os
  8. from typing import List, Union, Dict, Any, Optional
  9. from SCons.Script import *
  10. from .core import BuildContext
  11. from .project import ProjectGroup
  12. class RTEnv:
  13. """
  14. RT-Thread environment extensions (RTEnv).
  15. This class provides methods that are added to the SCons Environment object.
  16. """
  17. @staticmethod
  18. def inject_methods(env):
  19. """
  20. Inject RT-Thread methods into SCons Environment.
  21. Args:
  22. env: SCons Environment object
  23. """
  24. # Core build methods
  25. env.AddMethod(RTEnv.DefineGroup, 'DefineGroup')
  26. env.AddMethod(RTEnv.GetDepend, 'GetDepend')
  27. env.AddMethod(RTEnv.SrcRemove, 'SrcRemove')
  28. env.AddMethod(RTEnv.GetCurrentDir, 'GetCurrentDir')
  29. env.AddMethod(RTEnv.BuildPackage, 'BuildPackage')
  30. # Utility methods
  31. env.AddMethod(RTEnv.Glob, 'GlobFiles')
  32. env.AddMethod(RTEnv.GetBuildOptions, 'GetBuildOptions')
  33. env.AddMethod(RTEnv.GetContext, 'GetContext')
  34. # Path utilities
  35. env.AddMethod(RTEnv.GetRTTRoot, 'GetRTTRoot')
  36. env.AddMethod(RTEnv.GetBSPRoot, 'GetBSPRoot')
  37. @staticmethod
  38. def DefineGroup(env, name: str, src: List[str], depend: Any = None, **kwargs) -> List:
  39. """
  40. Define a component group.
  41. This method maintains compatibility with the original DefineGroup function
  42. while using the new object-oriented implementation.
  43. Args:
  44. env: SCons Environment
  45. name: Group name
  46. src: Source file list
  47. depend: Dependency conditions
  48. **kwargs: Additional parameters (CPPPATH, CPPDEFINES, etc.)
  49. Returns:
  50. List of build objects
  51. """
  52. context = BuildContext.get_current()
  53. if not context:
  54. raise RuntimeError("BuildContext not initialized")
  55. # Check dependencies
  56. if depend and not env.GetDepend(depend):
  57. return []
  58. # Process source files
  59. if isinstance(src, str):
  60. src = [src]
  61. # Create project group
  62. group = ProjectGroup(
  63. name=name,
  64. sources=src,
  65. dependencies=depend if isinstance(depend, list) else [depend] if depend else [],
  66. environment=env
  67. )
  68. # Process parameters
  69. group.include_paths = kwargs.get('CPPPATH', [])
  70. group.defines = kwargs.get('CPPDEFINES', {})
  71. group.cflags = kwargs.get('CFLAGS', '')
  72. group.cxxflags = kwargs.get('CXXFLAGS', '')
  73. group.local_cflags = kwargs.get('LOCAL_CFLAGS', '')
  74. group.local_cxxflags = kwargs.get('LOCAL_CXXFLAGS', '')
  75. group.local_include_paths = kwargs.get('LOCAL_CPPPATH', [])
  76. group.local_defines = kwargs.get('LOCAL_CPPDEFINES', {})
  77. group.libs = kwargs.get('LIBS', [])
  78. group.lib_paths = kwargs.get('LIBPATH', [])
  79. # Build objects
  80. objects = group.build(env)
  81. # Register group
  82. context.register_project_group(group)
  83. return objects
  84. @staticmethod
  85. def GetDepend(env, depend: Any) -> bool:
  86. """
  87. Check if dependency is satisfied.
  88. Args:
  89. env: SCons Environment
  90. depend: Dependency name or list of names
  91. Returns:
  92. True if dependency is satisfied
  93. """
  94. context = BuildContext.get_current()
  95. if not context:
  96. # Fallback to checking environment variables
  97. if isinstance(depend, str):
  98. return env.get(depend, False)
  99. elif isinstance(depend, list):
  100. return all(env.get(d, False) for d in depend)
  101. return False
  102. return context.get_dependency(depend)
  103. @staticmethod
  104. def SrcRemove(env, src: List[str], remove: List[str]) -> None:
  105. """
  106. Remove files from source list.
  107. Args:
  108. env: SCons Environment
  109. src: Source file list (modified in place)
  110. remove: Files to remove
  111. """
  112. if not isinstance(remove, list):
  113. remove = [remove]
  114. for item in remove:
  115. # Handle both exact matches and pattern matches
  116. if item in src:
  117. src.remove(item)
  118. else:
  119. # Try pattern matching
  120. import fnmatch
  121. to_remove = [f for f in src if fnmatch.fnmatch(f, item)]
  122. for f in to_remove:
  123. src.remove(f)
  124. @staticmethod
  125. def GetCurrentDir(env) -> str:
  126. """
  127. Get current directory.
  128. Args:
  129. env: SCons Environment
  130. Returns:
  131. Current directory path
  132. """
  133. return Dir('.').abspath
  134. @staticmethod
  135. def BuildPackage(env, package_path: str = None) -> List:
  136. """
  137. Build package from package.json.
  138. Args:
  139. env: SCons Environment
  140. package_path: Path to package.json or directory containing it
  141. Returns:
  142. List of build objects
  143. """
  144. import json
  145. # Find package.json
  146. if package_path is None:
  147. package_path = 'package.json'
  148. elif os.path.isdir(package_path):
  149. package_path = os.path.join(package_path, 'package.json')
  150. if not os.path.exists(package_path):
  151. env.GetContext().logger.error(f"Package file not found: {package_path}")
  152. return []
  153. # Load package definition
  154. with open(package_path, 'r') as f:
  155. package = json.load(f)
  156. # Process package
  157. name = package.get('name', 'unnamed')
  158. dependencies = package.get('dependencies', {})
  159. # Check main dependency
  160. if 'RT_USING_' + name.upper() not in dependencies:
  161. main_depend = 'RT_USING_' + name.upper().replace('-', '_')
  162. else:
  163. main_depend = list(dependencies.keys())[0]
  164. if not env.GetDepend(main_depend):
  165. return []
  166. # Collect sources
  167. src = []
  168. include_paths = []
  169. sources = package.get('sources', {})
  170. for category, config in sources.items():
  171. # Check condition
  172. condition = config.get('condition')
  173. if condition and not eval(condition, {'env': env, 'GetDepend': env.GetDepend}):
  174. continue
  175. # Add source files
  176. source_files = config.get('source_files', [])
  177. for pattern in source_files:
  178. src.extend(env.Glob(pattern))
  179. # Add header paths
  180. header_path = config.get('header_path', [])
  181. include_paths.extend(header_path)
  182. # Create group
  183. return env.DefineGroup(name, src, depend=main_depend, CPPPATH=include_paths)
  184. @staticmethod
  185. def Glob(env, pattern: str) -> List[str]:
  186. """
  187. Enhanced glob with better error handling.
  188. Args:
  189. env: SCons Environment
  190. pattern: File pattern
  191. Returns:
  192. List of matching files
  193. """
  194. try:
  195. files = Glob(pattern, strings=True)
  196. return sorted(files) # Sort for consistent ordering
  197. except Exception as e:
  198. context = BuildContext.get_current()
  199. if context:
  200. context.logger.warning(f"Glob pattern '{pattern}' failed: {e}")
  201. return []
  202. @staticmethod
  203. def GetBuildOptions(env) -> Dict[str, Any]:
  204. """
  205. Get build options.
  206. Args:
  207. env: SCons Environment
  208. Returns:
  209. Dictionary of build options
  210. """
  211. context = BuildContext.get_current()
  212. if context:
  213. return context.build_options
  214. return {}
  215. @staticmethod
  216. def GetContext(env) -> Optional[BuildContext]:
  217. """
  218. Get current build context.
  219. Args:
  220. env: SCons Environment
  221. Returns:
  222. BuildContext instance or None
  223. """
  224. return BuildContext.get_current()
  225. @staticmethod
  226. def GetRTTRoot(env) -> str:
  227. """
  228. Get RT-Thread root directory.
  229. Args:
  230. env: SCons Environment
  231. Returns:
  232. RT-Thread root path
  233. """
  234. return env.get('RTT_ROOT', '')
  235. @staticmethod
  236. def GetBSPRoot(env) -> str:
  237. """
  238. Get BSP root directory.
  239. Args:
  240. env: SCons Environment
  241. Returns:
  242. BSP root path
  243. """
  244. return env.get('BSP_ROOT', '')