build_component.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. import os
  2. import subprocess
  3. # Configuration to feature mapping table for components
  4. # This table defines which RT-Thread configurations should enable which component features
  5. # All feature configurations are now defined in feature_config_component.py
  6. CONFIG_COMPONENT_FEATURE_MAP = {}
  7. class ComponentBuildError(Exception):
  8. pass
  9. def check_component_dependencies(component_dir, required_dependencies):
  10. """
  11. Check if a component has the required dependencies
  12. Args:
  13. component_dir: Component directory path
  14. required_dependencies: List of dependency names to check
  15. Returns:
  16. bool: True if all required dependencies are present
  17. """
  18. if not component_dir or not required_dependencies:
  19. return True
  20. cargo_toml_path = os.path.join(component_dir, 'Cargo.toml')
  21. if not os.path.exists(cargo_toml_path):
  22. return False
  23. try:
  24. import toml
  25. with open(cargo_toml_path, 'r') as f:
  26. cargo_data = toml.load(f)
  27. dependencies = cargo_data.get('dependencies', {})
  28. # Check if all required dependencies are present
  29. for dep in required_dependencies:
  30. if dep not in dependencies:
  31. return False
  32. return True
  33. except Exception as e:
  34. print(f"Warning: Failed to parse {cargo_toml_path}: {e}")
  35. return False
  36. def collect_component_features(has_func, component_dir=None):
  37. """
  38. Collect component features based on RT-Thread configuration using extensible mapping table
  39. Args:
  40. has_func: Function to check if a configuration is enabled
  41. component_dir: Component directory to check dependencies (optional)
  42. Returns:
  43. list: List of features to enable
  44. """
  45. features = []
  46. # Iterate through all configured mappings
  47. for config_name, config_info in CONFIG_COMPONENT_FEATURE_MAP.items():
  48. # Check if this RT-Thread configuration is enabled
  49. if has_func(config_name):
  50. feature_name = config_info['feature']
  51. required_deps = config_info.get('dependencies', [])
  52. # If component_dir is provided, check dependencies
  53. if component_dir:
  54. if check_component_dependencies(component_dir, required_deps):
  55. features.append(feature_name)
  56. print(f"Enabling component feature '{feature_name}' for {config_name} in {os.path.basename(component_dir)}")
  57. else:
  58. # If no component_dir provided, enable for all (backward compatibility)
  59. features.append(feature_name)
  60. print(f"Enabling component feature '{feature_name}' for {config_name}")
  61. return features
  62. def cargo_build_component_staticlib(rust_dir, target, features, debug, rustflags=None, build_root=None):
  63. """
  64. Build a Rust component as a static library using Cargo.
  65. Args:
  66. rust_dir: Directory containing the Rust component
  67. target: Rust target architecture
  68. features: List of features to enable
  69. debug: Whether this is a debug build
  70. rustflags: Additional Rust compilation flags
  71. build_root: Build root directory (if not provided, will raise error)
  72. Returns:
  73. str: Path to the built library file, or None if build failed
  74. """
  75. if not build_root:
  76. raise ComponentBuildError("build_root parameter is required")
  77. build_root = os.path.abspath(build_root)
  78. os.makedirs(build_root, exist_ok=True)
  79. env = os.environ.copy()
  80. if rustflags:
  81. prev = env.get("RUSTFLAGS", "").strip()
  82. env["RUSTFLAGS"] = (prev + " " + rustflags).strip() if prev else rustflags
  83. env["CARGO_TARGET_DIR"] = build_root
  84. cmd = [
  85. "cargo", "build",
  86. "--target", target,
  87. "--manifest-path", os.path.join(rust_dir, "Cargo.toml")
  88. ]
  89. if not debug:
  90. cmd.insert(2, "--release")
  91. if features:
  92. cmd += ["--no-default-features", "--features", ",".join(features)]
  93. print("Building example component log (cargo)…")
  94. res = subprocess.run(cmd, cwd=rust_dir, env=env, capture_output=True, text=True)
  95. if res.returncode != 0:
  96. print("Warning: Example component build failed")
  97. if res.stderr:
  98. print(res.stderr)
  99. return None
  100. mode = "debug" if debug else "release"
  101. # Try target-specific path first, then fallback to direct path
  102. lib_path = os.path.join(build_root, target, mode, "libem_component_registry.a")
  103. if os.path.exists(lib_path):
  104. print("Example component log built successfully")
  105. return lib_path
  106. print("Warning: Library not found at expected location")
  107. print(f"Expected: {lib_path}")
  108. return None
  109. def build_example_component(cwd, has_func, rtconfig, build_root=None):
  110. """
  111. Build the example component.
  112. Args:
  113. cwd: Current working directory (component directory)
  114. has_func: Function to check if a configuration is enabled
  115. rtconfig: RT-Thread configuration module
  116. build_root: Optional build root directory
  117. Returns:
  118. tuple: (LIBS, LIBPATH, LINKFLAGS) for SCons
  119. """
  120. LIBS = []
  121. LIBPATH = []
  122. LINKFLAGS = ""
  123. # Import build support functions
  124. import sys
  125. sys.path.append(os.path.join(cwd, '../rust/tools'))
  126. from build_support import (
  127. detect_rust_target,
  128. make_rustflags,
  129. )
  130. target = detect_rust_target(has_func, rtconfig)
  131. # Build mode and features
  132. debug = bool(has_func('RUST_DEBUG_BUILD'))
  133. # Build the component registry
  134. registry_dir = os.path.join(cwd, 'component_registry')
  135. features = collect_component_features(has_func, registry_dir)
  136. rustflags = make_rustflags(rtconfig, target)
  137. rust_lib = cargo_build_component_staticlib(
  138. rust_dir=registry_dir,
  139. target=target,
  140. features=features,
  141. debug=debug,
  142. rustflags=rustflags,
  143. build_root=build_root
  144. )
  145. if rust_lib:
  146. LIBS = ['em_component_registry']
  147. LIBPATH = [os.path.dirname(rust_lib)]
  148. # Add LINKFLAGS to ensure component is linked into final binary
  149. LINKFLAGS += " -Wl,--whole-archive -lem_component_registry -Wl,--no-whole-archive"
  150. LINKFLAGS += " -Wl,--allow-multiple-definition"
  151. print('Example component registry library linked successfully')
  152. else:
  153. print('Warning: Failed to build example component registry library')
  154. return LIBS, LIBPATH, LINKFLAGS