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
|
name: Run script/clang-tidy for ESP32 IDF
|
||||||
options: --environment esp32-idf-tidy --grep USE_ESP_IDF
|
options: --environment esp32-idf-tidy --grep USE_ESP_IDF
|
||||||
pio_cache_key: tidyesp32-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:
|
steps:
|
||||||
- name: Check out code from GitHub
|
- name: Check out code from GitHub
|
||||||
@ -331,13 +336,13 @@ jobs:
|
|||||||
- name: Run clang-tidy
|
- name: Run clang-tidy
|
||||||
run: |
|
run: |
|
||||||
. venv/bin/activate
|
. venv/bin/activate
|
||||||
script/clang-tidy --all-headers --fix ${{ matrix.options }}
|
script/clang-tidy --all-headers --fix ${{ matrix.options }} ${{ matrix.ignore_errors && '|| true' || '' }}
|
||||||
env:
|
env:
|
||||||
# Also cache libdeps, store them in a ~/.platformio subfolder
|
# Also cache libdeps, store them in a ~/.platformio subfolder
|
||||||
PLATFORMIO_LIBDEPS_DIR: ~/.platformio/libdeps
|
PLATFORMIO_LIBDEPS_DIR: ~/.platformio/libdeps
|
||||||
|
|
||||||
- name: Suggested changes
|
- name: Suggested changes
|
||||||
run: script/ci-suggest-changes
|
run: script/ci-suggest-changes ${{ matrix.ignore_errors && '|| true' || '' }}
|
||||||
# yamllint disable-line rule:line-length
|
# yamllint disable-line rule:line-length
|
||||||
if: always()
|
if: always()
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esphome/core/application.h"
|
#include "esphome/core/application.h"
|
||||||
#include "esphome/core/component.h"
|
#include "esphome/core/component.h"
|
||||||
#include "esphome/core/hal.h"
|
#include "esphome/core/hal.h"
|
||||||
@ -28,6 +27,11 @@ using SPIInterface = spi_host_device_t;
|
|||||||
|
|
||||||
#endif // USE_ESP_IDF
|
#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.
|
* Implementation of SPI Controller mode.
|
||||||
*/
|
*/
|
||||||
|
@ -20,11 +20,6 @@
|
|||||||
|
|
||||||
// Feature flags
|
// Feature flags
|
||||||
#define USE_ALARM_CONTROL_PANEL
|
#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_BINARY_SENSOR
|
||||||
#define USE_BUTTON
|
#define USE_BUTTON
|
||||||
#define USE_CLIMATE
|
#define USE_CLIMATE
|
||||||
@ -79,20 +74,10 @@
|
|||||||
#define USE_LVGL_TEXTAREA
|
#define USE_LVGL_TEXTAREA
|
||||||
#define USE_LVGL_TILEVIEW
|
#define USE_LVGL_TILEVIEW
|
||||||
#define USE_LVGL_TOUCHSCREEN
|
#define USE_LVGL_TOUCHSCREEN
|
||||||
#define USE_MD5
|
|
||||||
#define USE_MDNS
|
#define USE_MDNS
|
||||||
#define USE_MEDIA_PLAYER
|
#define USE_MEDIA_PLAYER
|
||||||
#define USE_MQTT
|
|
||||||
#define USE_NETWORK
|
|
||||||
#define USE_NEXTION_TFT_UPLOAD
|
#define USE_NEXTION_TFT_UPLOAD
|
||||||
#define USE_NUMBER
|
#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_OUTPUT
|
||||||
#define USE_POWER_SUPPLY
|
#define USE_POWER_SUPPLY
|
||||||
#define USE_QR_CODE
|
#define USE_QR_CODE
|
||||||
@ -107,9 +92,28 @@
|
|||||||
#define USE_UART_DEBUGGER
|
#define USE_UART_DEBUGGER
|
||||||
#define USE_UPDATE
|
#define USE_UPDATE
|
||||||
#define USE_VALVE
|
#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
|
||||||
#define USE_WIFI_AP
|
#define USE_WIFI_AP
|
||||||
#define USE_WIREGUARD
|
#define USE_WIREGUARD
|
||||||
|
#endif
|
||||||
|
|
||||||
// Arduino-specific feature flags
|
// Arduino-specific feature flags
|
||||||
#ifdef USE_ARDUINO
|
#ifdef USE_ARDUINO
|
||||||
|
@ -194,6 +194,26 @@ build_flags =
|
|||||||
-DUSE_LIBRETINY
|
-DUSE_LIBRETINY
|
||||||
build_src_flags = -include Arduino.h
|
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.
|
; All the actual environments are defined below.
|
||||||
|
|
||||||
;;;;;;;; ESP8266 ;;;;;;;;
|
;;;;;;;; ESP8266 ;;;;;;;;
|
||||||
@ -440,3 +460,19 @@ build_flags =
|
|||||||
${common.build_flags}
|
${common.build_flags}
|
||||||
-DUSE_HOST
|
-DUSE_HOST
|
||||||
-std=c++17
|
-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/rp2040/core.cpp",
|
||||||
"esphome/components/libretiny/core.cpp",
|
"esphome/components/libretiny/core.cpp",
|
||||||
"esphome/components/host/core.cpp",
|
"esphome/components/host/core.cpp",
|
||||||
|
"esphome/components/zephyr/core.cpp",
|
||||||
"esphome/components/http_request/httplib.h",
|
"esphome/components/http_request/httplib.h",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -40,12 +40,37 @@ def clang_options(idedata):
|
|||||||
else:
|
else:
|
||||||
cmd.append(f"--target={triplet}")
|
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
|
# set flags
|
||||||
cmd.extend(
|
cmd.extend(
|
||||||
[
|
[
|
||||||
# disable built-in include directories from the host
|
# disable built-in include directories from the host
|
||||||
"-nostdinc",
|
"-nostdinc",
|
||||||
"-nostdinc++",
|
|
||||||
# replace pgmspace.h, as it uses GNU extensions clang doesn't support
|
# replace pgmspace.h, as it uses GNU extensions clang doesn't support
|
||||||
# https://github.com/earlephilhower/newlib-xtensa/pull/18
|
# https://github.com/earlephilhower/newlib-xtensa/pull/18
|
||||||
"-D_PGMSPACE_H_",
|
"-D_PGMSPACE_H_",
|
||||||
@ -70,22 +95,7 @@ def clang_options(idedata):
|
|||||||
)
|
)
|
||||||
|
|
||||||
# copy compiler flags, except those clang doesn't understand.
|
# copy compiler flags, except those clang doesn't understand.
|
||||||
cmd.extend(
|
cmd.extend(flag for flag in idedata["cxx_flags"] if flag not in omit_flags)
|
||||||
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",
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# defines
|
# defines
|
||||||
cmd.extend(f"-D{define}" for define in idedata["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
|
# add library include directories using -isystem to suppress their errors
|
||||||
for directory in list(idedata["includes"]["build"]):
|
for directory in list(idedata["includes"]["build"]):
|
||||||
# skip our own directories, we add those later
|
# 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}/.platformio",
|
||||||
f"{root_path}/.temp",
|
f"{root_path}/.temp",
|
||||||
f"{root_path}/managed_components",
|
f"{root_path}/managed_components",
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
or (directory.startswith(f"{root_path}") and "/.pio/" in directory)
|
||||||
):
|
):
|
||||||
cmd.extend(["-isystem", directory])
|
cmd.extend(["-isystem", directory])
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import re
|
|||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
import colorama
|
import colorama
|
||||||
|
import helpers_zephyr
|
||||||
|
|
||||||
root_path = os.path.abspath(os.path.normpath(os.path.join(__file__, "..", "..")))
|
root_path = os.path.abspath(os.path.normpath(os.path.join(__file__, "..", "..")))
|
||||||
basepath = os.path.join(root_path, "esphome")
|
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
|
# ensure temp directory exists before running pio, as it writes sdkconfig to it
|
||||||
Path(temp_folder).mkdir(exist_ok=True)
|
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"))
|
match = re.search(r'{\s*".*}', stdout.decode("utf-8"))
|
||||||
data = json.loads(match.group())
|
data = json.loads(match.group())
|
||||||
|
|
||||||
temp_idedata.write_text(json.dumps(data, indent=2) + "\n")
|
temp_idedata.write_text(json.dumps(data, indent=2) + "\n")
|
||||||
return data
|
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