mirror of
https://github.com/esphome/esphome.git
synced 2025-07-28 14:16:40 +00:00
[clang] clang tidy support with zephyr (#8352)
Co-authored-by: Keith Burzinski <kbx81x@gmail.com>
This commit is contained in:
parent
f4eb75e4e0
commit
7c0546c9f0
9
.github/workflows/ci.yml
vendored
9
.github/workflows/ci.yml
vendored
@ -292,6 +292,11 @@ jobs:
|
||||
name: Run script/clang-tidy for ESP32 IDF
|
||||
options: --environment esp32-idf-tidy --grep USE_ESP_IDF
|
||||
pio_cache_key: tidyesp32-idf
|
||||
- id: clang-tidy
|
||||
name: Run script/clang-tidy for ZEPHYR
|
||||
options: --environment nrf52-tidy --grep USE_ZEPHYR
|
||||
pio_cache_key: tidy-zephyr
|
||||
ignore_errors: true
|
||||
|
||||
steps:
|
||||
- name: Check out code from GitHub
|
||||
@ -331,13 +336,13 @@ jobs:
|
||||
- name: Run clang-tidy
|
||||
run: |
|
||||
. venv/bin/activate
|
||||
script/clang-tidy --all-headers --fix ${{ matrix.options }}
|
||||
script/clang-tidy --all-headers --fix ${{ matrix.options }} ${{ matrix.ignore_errors && '|| true' || '' }}
|
||||
env:
|
||||
# Also cache libdeps, store them in a ~/.platformio subfolder
|
||||
PLATFORMIO_LIBDEPS_DIR: ~/.platformio/libdeps
|
||||
|
||||
- name: Suggested changes
|
||||
run: script/ci-suggest-changes
|
||||
run: script/ci-suggest-changes ${{ matrix.ignore_errors && '|| true' || '' }}
|
||||
# yamllint disable-line rule:line-length
|
||||
if: always()
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/core/application.h"
|
||||
#include "esphome/core/component.h"
|
||||
#include "esphome/core/hal.h"
|
||||
@ -28,6 +27,11 @@ using SPIInterface = spi_host_device_t;
|
||||
|
||||
#endif // USE_ESP_IDF
|
||||
|
||||
#ifdef USE_ZEPHYR
|
||||
// TODO supprse clang-tidy. Remove after SPI driver for nrf52 is added.
|
||||
using SPIInterface = void *;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Implementation of SPI Controller mode.
|
||||
*/
|
||||
|
@ -20,11 +20,6 @@
|
||||
|
||||
// Feature flags
|
||||
#define USE_ALARM_CONTROL_PANEL
|
||||
#define USE_AUDIO_FLAC_SUPPORT
|
||||
#define USE_AUDIO_MP3_SUPPORT
|
||||
#define USE_API
|
||||
#define USE_API_NOISE
|
||||
#define USE_API_PLAINTEXT
|
||||
#define USE_BINARY_SENSOR
|
||||
#define USE_BUTTON
|
||||
#define USE_CLIMATE
|
||||
@ -79,20 +74,10 @@
|
||||
#define USE_LVGL_TEXTAREA
|
||||
#define USE_LVGL_TILEVIEW
|
||||
#define USE_LVGL_TOUCHSCREEN
|
||||
#define USE_MD5
|
||||
#define USE_MDNS
|
||||
#define USE_MEDIA_PLAYER
|
||||
#define USE_MQTT
|
||||
#define USE_NETWORK
|
||||
#define USE_NEXTION_TFT_UPLOAD
|
||||
#define USE_NUMBER
|
||||
#define USE_ONLINE_IMAGE_BMP_SUPPORT
|
||||
#define USE_ONLINE_IMAGE_PNG_SUPPORT
|
||||
#define USE_ONLINE_IMAGE_JPEG_SUPPORT
|
||||
#define USE_OTA
|
||||
#define USE_OTA_PASSWORD
|
||||
#define USE_OTA_STATE_CALLBACK
|
||||
#define USE_OTA_VERSION 2
|
||||
#define USE_OUTPUT
|
||||
#define USE_POWER_SUPPLY
|
||||
#define USE_QR_CODE
|
||||
@ -107,9 +92,28 @@
|
||||
#define USE_UART_DEBUGGER
|
||||
#define USE_UPDATE
|
||||
#define USE_VALVE
|
||||
|
||||
// Feature flags which do not work for zephyr
|
||||
#ifndef USE_ZEPHYR
|
||||
#define USE_AUDIO_FLAC_SUPPORT
|
||||
#define USE_AUDIO_MP3_SUPPORT
|
||||
#define USE_API
|
||||
#define USE_API_NOISE
|
||||
#define USE_API_PLAINTEXT
|
||||
#define USE_MD5
|
||||
#define USE_MQTT
|
||||
#define USE_NETWORK
|
||||
#define USE_ONLINE_IMAGE_BMP_SUPPORT
|
||||
#define USE_ONLINE_IMAGE_PNG_SUPPORT
|
||||
#define USE_ONLINE_IMAGE_JPEG_SUPPORT
|
||||
#define USE_OTA
|
||||
#define USE_OTA_PASSWORD
|
||||
#define USE_OTA_STATE_CALLBACK
|
||||
#define USE_OTA_VERSION 2
|
||||
#define USE_WIFI
|
||||
#define USE_WIFI_AP
|
||||
#define USE_WIREGUARD
|
||||
#endif
|
||||
|
||||
// Arduino-specific feature flags
|
||||
#ifdef USE_ARDUINO
|
||||
|
@ -194,6 +194,26 @@ build_flags =
|
||||
-DUSE_LIBRETINY
|
||||
build_src_flags = -include Arduino.h
|
||||
|
||||
; This is the common settings for the nRF52 using Zephyr.
|
||||
[common:nrf52-zephyr]
|
||||
extends = common
|
||||
platform = https://github.com/tomaszduda23/platform-nordicnrf52/archive/refs/tags/v10.3.0-1.zip
|
||||
framework = zephyr
|
||||
platform_packages =
|
||||
platformio/framework-zephyr @ https://github.com/tomaszduda23/framework-sdk-nrf/archive/refs/tags/v2.6.1-4.zip
|
||||
platformio/toolchain-gccarmnoneeabi@https://github.com/tomaszduda23/toolchain-sdk-ng/archive/refs/tags/v0.16.1-1.zip
|
||||
build_flags =
|
||||
${common.build_flags}
|
||||
-DUSE_ZEPHYR
|
||||
-DUSE_NRF52
|
||||
lib_deps =
|
||||
bblanchon/ArduinoJson@7.0.0 ; json
|
||||
wjtje/qr-code-generator-library@1.7.0 ; qr_code
|
||||
pavlodn/HaierProtocol@0.9.31 ; haier
|
||||
functionpointer/arduino-MLX90393@1.0.2 ; mlx90393
|
||||
https://github.com/Sensirion/arduino-gas-index-algorithm.git#3.2.1 ; Sensirion Gas Index Algorithm Arduino Library
|
||||
lvgl/lvgl@8.4.0 ; lvgl
|
||||
|
||||
; All the actual environments are defined below.
|
||||
|
||||
;;;;;;;; ESP8266 ;;;;;;;;
|
||||
@ -440,3 +460,19 @@ build_flags =
|
||||
${common.build_flags}
|
||||
-DUSE_HOST
|
||||
-std=c++17
|
||||
|
||||
;;;;;;;; nRF52 ;;;;;;;;
|
||||
|
||||
[env:nrf52]
|
||||
extends = common:nrf52-zephyr
|
||||
board = adafruit_feather_nrf52840
|
||||
build_flags =
|
||||
${common:nrf52-zephyr.build_flags}
|
||||
${flags:runtime.build_flags}
|
||||
|
||||
[env:nrf52-tidy]
|
||||
extends = common:nrf52-zephyr
|
||||
board = adafruit_feather_nrf52840
|
||||
build_flags =
|
||||
${common:nrf52-zephyr.build_flags}
|
||||
${flags:clangtidy.build_flags}
|
||||
|
@ -558,6 +558,7 @@ def lint_relative_py_import(fname):
|
||||
"esphome/components/rp2040/core.cpp",
|
||||
"esphome/components/libretiny/core.cpp",
|
||||
"esphome/components/host/core.cpp",
|
||||
"esphome/components/zephyr/core.cpp",
|
||||
"esphome/components/http_request/httplib.h",
|
||||
],
|
||||
)
|
||||
|
@ -40,12 +40,37 @@ def clang_options(idedata):
|
||||
else:
|
||||
cmd.append(f"--target={triplet}")
|
||||
|
||||
omit_flags = (
|
||||
"-free",
|
||||
"-fipa-pta",
|
||||
"-fstrict-volatile-bitfields",
|
||||
"-mlongcalls",
|
||||
"-mtext-section-literals",
|
||||
"-mdisable-hardware-atomics",
|
||||
"-mfix-esp32-psram-cache-issue",
|
||||
"-mfix-esp32-psram-cache-strategy=memw",
|
||||
"-fno-tree-switch-conversion",
|
||||
)
|
||||
|
||||
if "zephyr" in triplet:
|
||||
omit_flags += (
|
||||
"-fno-reorder-functions",
|
||||
"-mfp16-format=ieee",
|
||||
"--param=min-pagesize=0",
|
||||
)
|
||||
else:
|
||||
cmd.extend(
|
||||
[
|
||||
# disable built-in include directories from the host
|
||||
"-nostdinc++",
|
||||
]
|
||||
)
|
||||
|
||||
# set flags
|
||||
cmd.extend(
|
||||
[
|
||||
# disable built-in include directories from the host
|
||||
"-nostdinc",
|
||||
"-nostdinc++",
|
||||
# replace pgmspace.h, as it uses GNU extensions clang doesn't support
|
||||
# https://github.com/earlephilhower/newlib-xtensa/pull/18
|
||||
"-D_PGMSPACE_H_",
|
||||
@ -70,22 +95,7 @@ def clang_options(idedata):
|
||||
)
|
||||
|
||||
# copy compiler flags, except those clang doesn't understand.
|
||||
cmd.extend(
|
||||
flag
|
||||
for flag in idedata["cxx_flags"]
|
||||
if flag
|
||||
not in (
|
||||
"-free",
|
||||
"-fipa-pta",
|
||||
"-fstrict-volatile-bitfields",
|
||||
"-mlongcalls",
|
||||
"-mtext-section-literals",
|
||||
"-mdisable-hardware-atomics",
|
||||
"-mfix-esp32-psram-cache-issue",
|
||||
"-mfix-esp32-psram-cache-strategy=memw",
|
||||
"-fno-tree-switch-conversion",
|
||||
)
|
||||
)
|
||||
cmd.extend(flag for flag in idedata["cxx_flags"] if flag not in omit_flags)
|
||||
|
||||
# defines
|
||||
cmd.extend(f"-D{define}" for define in idedata["defines"])
|
||||
@ -100,13 +110,16 @@ def clang_options(idedata):
|
||||
# add library include directories using -isystem to suppress their errors
|
||||
for directory in list(idedata["includes"]["build"]):
|
||||
# skip our own directories, we add those later
|
||||
if not directory.startswith(f"{root_path}") or directory.startswith(
|
||||
if (
|
||||
not directory.startswith(f"{root_path}")
|
||||
or directory.startswith(
|
||||
(
|
||||
f"{root_path}/.pio",
|
||||
f"{root_path}/.platformio",
|
||||
f"{root_path}/.temp",
|
||||
f"{root_path}/managed_components",
|
||||
)
|
||||
)
|
||||
or (directory.startswith(f"{root_path}") and "/.pio/" in directory)
|
||||
):
|
||||
cmd.extend(["-isystem", directory])
|
||||
|
||||
|
@ -5,6 +5,7 @@ import re
|
||||
import subprocess
|
||||
|
||||
import colorama
|
||||
import helpers_zephyr
|
||||
|
||||
root_path = os.path.abspath(os.path.normpath(os.path.join(__file__, "..", "..")))
|
||||
basepath = os.path.join(root_path, "esphome")
|
||||
@ -147,10 +148,14 @@ def load_idedata(environment):
|
||||
# ensure temp directory exists before running pio, as it writes sdkconfig to it
|
||||
Path(temp_folder).mkdir(exist_ok=True)
|
||||
|
||||
stdout = subprocess.check_output(["pio", "run", "-t", "idedata", "-e", environment])
|
||||
if "nrf" in environment:
|
||||
data = helpers_zephyr.load_idedata(environment, temp_folder, platformio_ini)
|
||||
else:
|
||||
stdout = subprocess.check_output(
|
||||
["pio", "run", "-t", "idedata", "-e", environment]
|
||||
)
|
||||
match = re.search(r'{\s*".*}', stdout.decode("utf-8"))
|
||||
data = json.loads(match.group())
|
||||
|
||||
temp_idedata.write_text(json.dumps(data, indent=2) + "\n")
|
||||
return data
|
||||
|
||||
|
124
script/helpers_zephyr.py
Normal file
124
script/helpers_zephyr.py
Normal file
@ -0,0 +1,124 @@
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
|
||||
def load_idedata(environment, temp_folder, platformio_ini):
|
||||
build_environment = environment.replace("-tidy", "")
|
||||
build_dir = Path(temp_folder) / f"build-{build_environment}"
|
||||
Path(build_dir).mkdir(exist_ok=True)
|
||||
Path(build_dir / "platformio.ini").write_text(
|
||||
Path(platformio_ini).read_text(encoding="utf-8"), encoding="utf-8"
|
||||
)
|
||||
esphome_dir = Path(build_dir / "esphome")
|
||||
esphome_dir.mkdir(exist_ok=True)
|
||||
Path(esphome_dir / "main.cpp").write_text(
|
||||
"""
|
||||
#include <zephyr/kernel.h>
|
||||
int main() { return 0;}
|
||||
""",
|
||||
encoding="utf-8",
|
||||
)
|
||||
zephyr_dir = Path(build_dir / "zephyr")
|
||||
zephyr_dir.mkdir(exist_ok=True)
|
||||
Path(zephyr_dir / "prj.conf").write_text(
|
||||
"""
|
||||
CONFIG_NEWLIB_LIBC=y
|
||||
""",
|
||||
encoding="utf-8",
|
||||
)
|
||||
subprocess.run(["pio", "run", "-e", build_environment, "-d", build_dir], check=True)
|
||||
|
||||
def extract_include_paths(command):
|
||||
include_paths = []
|
||||
include_pattern = re.compile(r'("-I\s*[^"]+)|(-isystem\s*[^\s]+)|(-I\s*[^\s]+)')
|
||||
for match in include_pattern.findall(command):
|
||||
split_strings = re.split(
|
||||
r"\s*-\s*(?:I|isystem)", list(filter(lambda x: x, match))[0]
|
||||
)
|
||||
include_paths.append(split_strings[1])
|
||||
return include_paths
|
||||
|
||||
def extract_defines(command):
|
||||
defines = []
|
||||
define_pattern = re.compile(r"-D\s*([^\s]+)")
|
||||
for match in define_pattern.findall(command):
|
||||
if match not in ("_ASMLANGUAGE"):
|
||||
defines.append(match)
|
||||
return defines
|
||||
|
||||
def find_cxx_path(commands):
|
||||
for entry in commands:
|
||||
command = entry["command"]
|
||||
cxx_path = command.split()[0]
|
||||
if not cxx_path.endswith("++"):
|
||||
continue
|
||||
return cxx_path
|
||||
|
||||
def get_builtin_include_paths(compiler):
|
||||
result = subprocess.run(
|
||||
[compiler, "-E", "-x", "c++", "-", "-v"],
|
||||
input="",
|
||||
text=True,
|
||||
stderr=subprocess.PIPE,
|
||||
stdout=subprocess.DEVNULL,
|
||||
check=True,
|
||||
)
|
||||
include_paths = []
|
||||
start_collecting = False
|
||||
for line in result.stderr.splitlines():
|
||||
if start_collecting:
|
||||
if line.startswith(" "):
|
||||
include_paths.append(line.strip())
|
||||
else:
|
||||
break
|
||||
if "#include <...> search starts here:" in line:
|
||||
start_collecting = True
|
||||
return include_paths
|
||||
|
||||
def extract_cxx_flags(command):
|
||||
flags = []
|
||||
# Extracts CXXFLAGS from the command string, excluding includes and defines.
|
||||
flag_pattern = re.compile(
|
||||
r"(-O[0-3s]|-g|-std=[^\s]+|-Wall|-Wextra|-Werror|--[^\s]+|-f[^\s]+|-m[^\s]+|-imacros\s*[^\s]+)"
|
||||
)
|
||||
for match in flag_pattern.findall(command):
|
||||
flags.append(match.replace("-imacros ", "-imacros"))
|
||||
return flags
|
||||
|
||||
def transform_to_idedata_format(compile_commands):
|
||||
cxx_path = find_cxx_path(compile_commands)
|
||||
idedata = {
|
||||
"includes": {
|
||||
"toolchain": get_builtin_include_paths(cxx_path),
|
||||
"build": set(),
|
||||
},
|
||||
"defines": set(),
|
||||
"cxx_path": cxx_path,
|
||||
"cxx_flags": set(),
|
||||
}
|
||||
|
||||
for entry in compile_commands:
|
||||
command = entry["command"]
|
||||
exec = command.split()[0]
|
||||
if exec != cxx_path:
|
||||
continue
|
||||
|
||||
idedata["includes"]["build"].update(extract_include_paths(command))
|
||||
idedata["defines"].update(extract_defines(command))
|
||||
idedata["cxx_flags"].update(extract_cxx_flags(command))
|
||||
|
||||
# Convert sets to lists for JSON serialization
|
||||
idedata["includes"]["build"] = list(idedata["includes"]["build"])
|
||||
idedata["defines"] = list(idedata["defines"])
|
||||
idedata["cxx_flags"] = list(idedata["cxx_flags"])
|
||||
|
||||
return idedata
|
||||
|
||||
compile_commands = json.loads(
|
||||
Path(
|
||||
build_dir / ".pio" / "build" / build_environment / "compile_commands.json"
|
||||
).read_text(encoding="utf-8")
|
||||
)
|
||||
return transform_to_idedata_format(compile_commands)
|
Loading…
x
Reference in New Issue
Block a user