Files
Tasmota/pio-tools/dump-defines.py
T

143 lines
5.1 KiB
Python

# Dump preprocessor `#define` macros that matter for Berry solidification:
# - USE_* feature flags (from my_user_config.h, user_config_override.h,
# tasmota_options.h, tasmota_configurations.h)
# - Language strings D_JSON_*, D_CMND_*, ... (from i18n.h + the language
# file selected via MY_LANGUAGE)
# - Tasmota enums (from tasmota.h)
#
# Output: tasmota/tasmota_defines_for_berry.h (gitignored)
#
# Runs as a POST-script so $CXX is already set to the real cross-compiler
# (xtensa-esp32-elf-g++, riscv32-esp-elf-g++, etc.) by the time this
# script executes. This guarantees correct int/pointer widths and
# target-specific built-in macros without any -m32 hack.
#
# We pass `-D` flags from the project's build_flags so that USE_* gates,
# MY_LANGUAGE, ESP32, etc. are defined the same way the real build sees
# them.
Import("env")
import pathlib
import re
import subprocess
from colorama import Fore
from tasmotapiolib import is_non_build_target
# Skip during the espidf libs compile phase that runs first when
# `custom_sdkconfig` is set. The Arduino-as-component / hybrid build
# triggers a recursive SConscript("espidf.py") which sets
# ARDUINO_LIB_COMPILE_FLAG="Build" - in that pass we don't want any
# Berry action.
if env.subst("$ARDUINO_LIB_COMPILE_FLAG") == "Build":
print("berry-dump-defines: skipped (ESP-IDF libs compile phase)")
Return()
# Skip for non-compiling targets (upload, erase, monitor, ...).
if is_non_build_target(env):
print("berry-dump-defines: skipped (non-build target)")
Return()
_IS_ESP32 = env["PIOPLATFORM"] == "espressif32"
def _run_dump():
project_dir = pathlib.Path(env.subst("$PROJECT_DIR"))
output_file = project_dir / "tasmota" / "tasmota_defines_for_berry.h"
output_file.parent.mkdir(parents=True, exist_ok=True)
# Working dir for the sdkconfig.h stub.
stub_parent = pathlib.Path(env.subst("$BUILD_DIR"))
stub_parent.mkdir(parents=True, exist_ok=True)
cxx = env.subst("$CXX")
if not cxx:
print(Fore.YELLOW + "dump-defines: $CXX not set, skipping")
return
forced_includes = [
project_dir / "tasmota" / "include" / "tasmota.h",
project_dir / "tasmota" / "my_user_config.h",
project_dir / "include" / "tasmota_options.h",
project_dir / "tasmota" / "include" / "i18n.h",
]
# Extract -D flags from build_flags. Other flags (-I, -W, -f, ...) we
# don't need: our forced includes are absolute paths and we supply
# explicit -I for the Tasmota header roots below.
build_flags = env.subst(" ".join(env.get("BUILD_FLAGS") or []))
define_flags = re.findall(r"-D\s*\S+", build_flags)
# Also surface ESP32 unconditionally (real build always defines it via
# the platform) so #ifdef ESP32 in tasmota_configurations.h fires.
if not any(f.startswith("-DESP32") for f in define_flags):
define_flags.append("-DESP32")
include_flags = [
"-I" + str(project_dir / "include"),
"-I" + str(project_dir / "tasmota" / "include"),
"-I" + str(project_dir / "tasmota"),
]
# tasmota_configurations_ESP32.h and the ESP-IDF headers pull in
# `sdkconfig.h`, which is generated per MCU variant by the framework
# and not present at pre-script time. For Berry solidification we
# don't need its CONFIG_* values - the macros the solidifier cares
# about live in Tasmota headers. Supply an empty stub so the
# preprocessor stops complaining.
stub_dir = stub_parent / "dump-defines-stubs"
stub_dir.mkdir(parents=True, exist_ok=True)
(stub_dir / "sdkconfig.h").write_text(
"/* auto-generated empty stub for dump-defines.py */\n"
)
# Prepend so the stub shadows any real header on CPPPATH.
include_flags.insert(0, "-I" + str(stub_dir))
cmd = [cxx, "-E", "-dM", "-x", "c++"]
cmd += define_flags
cmd += include_flags
for header in forced_includes:
cmd += ["-include", str(header)]
cmd.append("-") # read empty stdin
print(f"Berry dumping defines: {output_file.relative_to(project_dir)}")
try:
with open(output_file, "w", encoding="utf-8") as out:
proc = subprocess.run(
cmd,
input="",
stdout=out,
stderr=subprocess.PIPE,
text=True,
check=False,
)
except FileNotFoundError as e:
print(Fore.YELLOW + f"dump-defines: {cxx} not runnable ({e}), skipping")
return
if proc.returncode != 0:
print(Fore.YELLOW + f"dump-defines: {cxx} returned {proc.returncode}")
if proc.stderr:
for line in proc.stderr.splitlines()[:10]:
print(Fore.YELLOW + f" {line}")
def _action(*args, **kwargs):
_run_dump()
if _IS_ESP32:
_run_dump()
env.AddCustomTarget(
name="dump_defines",
dependencies=None,
actions=[_action],
title="Dump preprocessor defines",
description="Run the preprocessor over Tasmota config/i18n headers and write macros to tasmota/tasmota_defines_for_berry.h",
)