environment.py 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298
  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. If None, looks for package.json in current directory.
  141. Returns:
  142. List of build objects
  143. """
  144. # Import the existing package module
  145. import sys
  146. import os
  147. # Get the building module path
  148. building_path = os.path.dirname(os.path.abspath(__file__))
  149. tools_path = os.path.dirname(building_path)
  150. # Add to path if not already there
  151. if tools_path not in sys.path:
  152. sys.path.insert(0, tools_path)
  153. # Import and use the existing BuildPackage
  154. try:
  155. from package import BuildPackage as build_package_func
  156. # BuildPackage uses global functions, so we need to set up the context
  157. # Save current directory
  158. current_dir = os.getcwd()
  159. # Change to the directory where we want to build
  160. if package_path is None:
  161. work_dir = env.GetCurrentDir()
  162. elif os.path.isdir(package_path):
  163. work_dir = package_path
  164. else:
  165. work_dir = os.path.dirname(package_path)
  166. os.chdir(work_dir)
  167. try:
  168. # Call the original BuildPackage
  169. result = build_package_func(package_path)
  170. finally:
  171. # Restore directory
  172. os.chdir(current_dir)
  173. return result
  174. except ImportError:
  175. # Fallback if import fails
  176. context = BuildContext.get_current()
  177. if context:
  178. context.logger.error("Failed to import package module")
  179. return []
  180. @staticmethod
  181. def Glob(env, pattern: str) -> List[str]:
  182. """
  183. Enhanced glob with better error handling.
  184. Args:
  185. env: SCons Environment
  186. pattern: File pattern
  187. Returns:
  188. List of matching files
  189. """
  190. try:
  191. files = Glob(pattern, strings=True)
  192. return sorted(files) # Sort for consistent ordering
  193. except Exception as e:
  194. context = BuildContext.get_current()
  195. if context:
  196. context.logger.warning(f"Glob pattern '{pattern}' failed: {e}")
  197. return []
  198. @staticmethod
  199. def GetBuildOptions(env) -> Dict[str, Any]:
  200. """
  201. Get build options.
  202. Args:
  203. env: SCons Environment
  204. Returns:
  205. Dictionary of build options
  206. """
  207. context = BuildContext.get_current()
  208. if context:
  209. return context.build_options
  210. return {}
  211. @staticmethod
  212. def GetContext(env) -> Optional[BuildContext]:
  213. """
  214. Get current build context.
  215. Args:
  216. env: SCons Environment
  217. Returns:
  218. BuildContext instance or None
  219. """
  220. return BuildContext.get_current()
  221. @staticmethod
  222. def GetRTTRoot(env) -> str:
  223. """
  224. Get RT-Thread root directory.
  225. Args:
  226. env: SCons Environment
  227. Returns:
  228. RT-Thread root path
  229. """
  230. return env.get('RTT_ROOT', '')
  231. @staticmethod
  232. def GetBSPRoot(env) -> str:
  233. """
  234. Get BSP root directory.
  235. Args:
  236. env: SCons Environment
  237. Returns:
  238. BSP root path
  239. """
  240. return env.get('BSP_ROOT', '')