mirror of
https://github.com/esphome/esphome.git
synced 2026-02-18 15:35:59 -07:00
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com> Co-authored-by: J. Nick Koston <nick@koston.org>
140 lines
4.0 KiB
Python
140 lines
4.0 KiB
Python
"""ESP-IDF direct build generator for ESPHome."""
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
from esphome.components.esp32 import get_esp32_variant
|
|
from esphome.core import CORE
|
|
from esphome.helpers import mkdir_p, write_file_if_changed
|
|
|
|
|
|
def get_available_components() -> list[str] | None:
|
|
"""Get list of available ESP-IDF components from project_description.json.
|
|
|
|
Returns only internal ESP-IDF components, excluding external/managed
|
|
components (from idf_component.yml).
|
|
"""
|
|
project_desc = Path(CORE.build_path) / "build" / "project_description.json"
|
|
if not project_desc.exists():
|
|
return None
|
|
|
|
try:
|
|
with open(project_desc, encoding="utf-8") as f:
|
|
data = json.load(f)
|
|
|
|
component_info = data.get("build_component_info", {})
|
|
|
|
result = []
|
|
for name, info in component_info.items():
|
|
# Exclude our own src component
|
|
if name == "src":
|
|
continue
|
|
|
|
# Exclude managed/external components
|
|
comp_dir = info.get("dir", "")
|
|
if "managed_components" in comp_dir:
|
|
continue
|
|
|
|
result.append(name)
|
|
|
|
return result
|
|
except (json.JSONDecodeError, OSError):
|
|
return None
|
|
|
|
|
|
def has_discovered_components() -> bool:
|
|
"""Check if we have discovered components from a previous configure."""
|
|
return get_available_components() is not None
|
|
|
|
|
|
def get_project_cmakelists() -> str:
|
|
"""Generate the top-level CMakeLists.txt for ESP-IDF project."""
|
|
# Get IDF target from ESP32 variant (e.g., ESP32S3 -> esp32s3)
|
|
variant = get_esp32_variant()
|
|
idf_target = variant.lower().replace("-", "")
|
|
|
|
return f"""\
|
|
# Auto-generated by ESPHome
|
|
cmake_minimum_required(VERSION 3.16)
|
|
|
|
set(IDF_TARGET {idf_target})
|
|
set(EXTRA_COMPONENT_DIRS ${{CMAKE_SOURCE_DIR}}/src)
|
|
|
|
include($ENV{{IDF_PATH}}/tools/cmake/project.cmake)
|
|
project({CORE.name})
|
|
"""
|
|
|
|
|
|
def get_component_cmakelists(minimal: bool = False) -> str:
|
|
"""Generate the main component CMakeLists.txt."""
|
|
idf_requires = [] if minimal else (get_available_components() or [])
|
|
requires_str = " ".join(idf_requires)
|
|
|
|
# Extract compile definitions from build flags (-DXXX -> XXX)
|
|
compile_defs = [flag[2:] for flag in CORE.build_flags if flag.startswith("-D")]
|
|
compile_defs_str = "\n ".join(compile_defs) if compile_defs else ""
|
|
|
|
# Extract compile options (-W flags, excluding linker flags)
|
|
compile_opts = [
|
|
flag
|
|
for flag in CORE.build_flags
|
|
if flag.startswith("-W") and not flag.startswith("-Wl,")
|
|
]
|
|
compile_opts_str = "\n ".join(compile_opts) if compile_opts else ""
|
|
|
|
# Extract linker options (-Wl, flags)
|
|
link_opts = [flag for flag in CORE.build_flags if flag.startswith("-Wl,")]
|
|
link_opts_str = "\n ".join(link_opts) if link_opts else ""
|
|
|
|
return f"""\
|
|
# Auto-generated by ESPHome
|
|
file(GLOB_RECURSE app_sources
|
|
"${{CMAKE_CURRENT_SOURCE_DIR}}/*.cpp"
|
|
"${{CMAKE_CURRENT_SOURCE_DIR}}/*.c"
|
|
"${{CMAKE_CURRENT_SOURCE_DIR}}/esphome/*.cpp"
|
|
"${{CMAKE_CURRENT_SOURCE_DIR}}/esphome/*.c"
|
|
)
|
|
|
|
idf_component_register(
|
|
SRCS ${{app_sources}}
|
|
INCLUDE_DIRS "." "esphome"
|
|
REQUIRES {requires_str}
|
|
)
|
|
|
|
# Apply C++ standard
|
|
target_compile_features(${{COMPONENT_LIB}} PUBLIC cxx_std_20)
|
|
|
|
# ESPHome compile definitions
|
|
target_compile_definitions(${{COMPONENT_LIB}} PUBLIC
|
|
{compile_defs_str}
|
|
)
|
|
|
|
# ESPHome compile options
|
|
target_compile_options(${{COMPONENT_LIB}} PUBLIC
|
|
{compile_opts_str}
|
|
)
|
|
|
|
# ESPHome linker options
|
|
target_link_options(${{COMPONENT_LIB}} PUBLIC
|
|
{link_opts_str}
|
|
)
|
|
"""
|
|
|
|
|
|
def write_project(minimal: bool = False) -> None:
|
|
"""Write ESP-IDF project files."""
|
|
mkdir_p(CORE.build_path)
|
|
mkdir_p(CORE.relative_src_path())
|
|
|
|
# Write top-level CMakeLists.txt
|
|
write_file_if_changed(
|
|
CORE.relative_build_path("CMakeLists.txt"),
|
|
get_project_cmakelists(),
|
|
)
|
|
|
|
# Write component CMakeLists.txt in src/
|
|
write_file_if_changed(
|
|
CORE.relative_src_path("CMakeLists.txt"),
|
|
get_component_cmakelists(minimal=minimal),
|
|
)
|