2019-05-12 23:04:36 +02:00
|
|
|
import codecs
|
|
|
|
import json
|
|
|
|
import os.path
|
|
|
|
import re
|
|
|
|
import subprocess
|
|
|
|
import sys
|
|
|
|
|
2021-03-07 20:03:16 +01:00
|
|
|
root_path = os.path.abspath(os.path.normpath(os.path.join(__file__, "..", "..")))
|
|
|
|
basepath = os.path.join(root_path, "esphome")
|
|
|
|
temp_header_file = os.path.join(root_path, ".temp-clang-tidy.cpp")
|
2019-05-12 23:04:36 +02:00
|
|
|
|
|
|
|
|
|
|
|
def shlex_quote(s):
|
|
|
|
if not s:
|
2019-12-07 18:28:55 +01:00
|
|
|
return "''"
|
2021-03-07 20:03:16 +01:00
|
|
|
if re.search(r"[^\w@%+=:,./-]", s) is None:
|
2019-05-12 23:04:36 +02:00
|
|
|
return s
|
|
|
|
|
2019-12-07 18:28:55 +01:00
|
|
|
return "'" + s.replace("'", "'\"'\"'") + "'"
|
2019-05-12 23:04:36 +02:00
|
|
|
|
|
|
|
|
|
|
|
def build_all_include():
|
|
|
|
# Build a cpp file that includes all header files in this repo.
|
|
|
|
# Otherwise header-only integrations would not be tested by clang-tidy
|
|
|
|
headers = []
|
|
|
|
for path in walk_files(basepath):
|
2021-03-07 20:03:16 +01:00
|
|
|
filetypes = (".h",)
|
2019-05-12 23:04:36 +02:00
|
|
|
ext = os.path.splitext(path)[1]
|
|
|
|
if ext in filetypes:
|
|
|
|
path = os.path.relpath(path, root_path)
|
2021-03-07 20:03:16 +01:00
|
|
|
include_p = path.replace(os.path.sep, "/")
|
2019-12-07 18:28:55 +01:00
|
|
|
headers.append(f'#include "{include_p}"')
|
2019-05-12 23:04:36 +02:00
|
|
|
headers.sort()
|
2021-03-07 20:03:16 +01:00
|
|
|
headers.append("")
|
|
|
|
content = "\n".join(headers)
|
|
|
|
with codecs.open(temp_header_file, "w", encoding="utf-8") as f:
|
2019-05-12 23:04:36 +02:00
|
|
|
f.write(content)
|
|
|
|
|
|
|
|
|
|
|
|
def build_compile_commands():
|
2021-03-07 20:03:16 +01:00
|
|
|
gcc_flags_json = os.path.join(root_path, ".gcc-flags.json")
|
2019-05-12 23:04:36 +02:00
|
|
|
if not os.path.isfile(gcc_flags_json):
|
|
|
|
print("Could not find {} file which is required for clang-tidy.")
|
2021-03-07 20:03:16 +01:00
|
|
|
print(
|
|
|
|
'Please run "pio init --ide atom" in the root esphome folder to generate that file.'
|
|
|
|
)
|
2019-05-12 23:04:36 +02:00
|
|
|
sys.exit(1)
|
2021-03-07 20:03:16 +01:00
|
|
|
with codecs.open(gcc_flags_json, "r", encoding="utf-8") as f:
|
2019-05-12 23:04:36 +02:00
|
|
|
gcc_flags = json.load(f)
|
2021-03-07 20:03:16 +01:00
|
|
|
exec_path = gcc_flags["execPath"]
|
|
|
|
include_paths = gcc_flags["gccIncludePaths"].split(",")
|
|
|
|
includes = [f"-I{p}" for p in include_paths]
|
|
|
|
cpp_flags = gcc_flags["gccDefaultCppFlags"].split(" ")
|
|
|
|
defines = [flag for flag in cpp_flags if flag.startswith("-D")]
|
2019-05-12 23:04:36 +02:00
|
|
|
command = [exec_path]
|
|
|
|
command.extend(includes)
|
|
|
|
command.extend(defines)
|
2021-03-07 20:03:16 +01:00
|
|
|
command.append("-std=gnu++11")
|
|
|
|
command.append("-Wall")
|
|
|
|
command.append("-Wno-delete-non-virtual-dtor")
|
|
|
|
command.append("-Wno-unused-variable")
|
|
|
|
command.append("-Wunreachable-code")
|
2019-05-12 23:04:36 +02:00
|
|
|
|
|
|
|
source_files = []
|
|
|
|
for path in walk_files(basepath):
|
2021-03-07 20:03:16 +01:00
|
|
|
filetypes = (".cpp",)
|
2019-05-12 23:04:36 +02:00
|
|
|
ext = os.path.splitext(path)[1]
|
|
|
|
if ext in filetypes:
|
|
|
|
source_files.append(os.path.abspath(path))
|
|
|
|
source_files.append(temp_header_file)
|
|
|
|
source_files.sort()
|
2021-03-07 20:03:16 +01:00
|
|
|
compile_commands = [
|
|
|
|
{
|
|
|
|
"directory": root_path,
|
|
|
|
"command": " ".join(
|
|
|
|
shlex_quote(x) for x in (command + ["-o", p + ".o", "-c", p])
|
|
|
|
),
|
|
|
|
"file": p,
|
|
|
|
}
|
|
|
|
for p in source_files
|
|
|
|
]
|
|
|
|
compile_commands_json = os.path.join(root_path, "compile_commands.json")
|
2019-05-12 23:04:36 +02:00
|
|
|
if os.path.isfile(compile_commands_json):
|
2021-03-07 20:03:16 +01:00
|
|
|
with codecs.open(compile_commands_json, "r", encoding="utf-8") as f:
|
2019-05-12 23:04:36 +02:00
|
|
|
try:
|
|
|
|
if json.load(f) == compile_commands:
|
|
|
|
return
|
2021-03-07 20:03:16 +01:00
|
|
|
# pylint: disable=bare-except
|
2019-05-12 23:04:36 +02:00
|
|
|
except:
|
|
|
|
pass
|
2021-03-07 20:03:16 +01:00
|
|
|
with codecs.open(compile_commands_json, "w", encoding="utf-8") as f:
|
2019-05-12 23:04:36 +02:00
|
|
|
json.dump(compile_commands, f, indent=2)
|
|
|
|
|
|
|
|
|
|
|
|
def walk_files(path):
|
|
|
|
for root, _, files in os.walk(path):
|
|
|
|
for name in files:
|
|
|
|
yield os.path.join(root, name)
|
|
|
|
|
|
|
|
|
|
|
|
def get_output(*args):
|
|
|
|
proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
|
|
output, err = proc.communicate()
|
2021-03-07 20:03:16 +01:00
|
|
|
return output.decode("utf-8")
|
|
|
|
|
|
|
|
|
|
|
|
def get_err(*args):
|
|
|
|
proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
|
|
output, err = proc.communicate()
|
|
|
|
return err.decode("utf-8")
|
2019-05-12 23:04:36 +02:00
|
|
|
|
|
|
|
|
|
|
|
def splitlines_no_ends(string):
|
|
|
|
return [s.strip() for s in string.splitlines()]
|
|
|
|
|
|
|
|
|
|
|
|
def changed_files():
|
2021-03-07 20:03:16 +01:00
|
|
|
check_remotes = ["upstream", "origin"]
|
|
|
|
check_remotes.extend(splitlines_no_ends(get_output("git", "remote")))
|
2020-07-14 14:34:44 +02:00
|
|
|
for remote in check_remotes:
|
2021-03-07 20:03:16 +01:00
|
|
|
command = ["git", "merge-base", f"refs/remotes/{remote}/dev", "HEAD"]
|
2019-05-12 23:04:36 +02:00
|
|
|
try:
|
|
|
|
merge_base = splitlines_no_ends(get_output(*command))[0]
|
|
|
|
break
|
2021-03-07 20:03:16 +01:00
|
|
|
# pylint: disable=bare-except
|
2019-05-12 23:04:36 +02:00
|
|
|
except:
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
raise ValueError("Git not configured")
|
2021-03-07 20:03:16 +01:00
|
|
|
command = ["git", "diff", merge_base, "--name-only"]
|
2019-05-12 23:04:36 +02:00
|
|
|
changed = splitlines_no_ends(get_output(*command))
|
|
|
|
changed = [os.path.relpath(f, os.getcwd()) for f in changed]
|
|
|
|
changed.sort()
|
|
|
|
return changed
|
|
|
|
|
|
|
|
|
|
|
|
def filter_changed(files):
|
|
|
|
changed = changed_files()
|
|
|
|
files = [f for f in files if f in changed]
|
|
|
|
print("Changed files:")
|
|
|
|
if not files:
|
|
|
|
print(" No changed files!")
|
|
|
|
for c in files:
|
2019-12-07 18:28:55 +01:00
|
|
|
print(f" {c}")
|
2019-05-12 23:04:36 +02:00
|
|
|
return files
|
2019-05-24 17:20:06 +02:00
|
|
|
|
|
|
|
|
|
|
|
def git_ls_files():
|
2021-03-07 20:03:16 +01:00
|
|
|
command = ["git", "ls-files", "-s"]
|
2019-05-24 17:20:06 +02:00
|
|
|
proc = subprocess.Popen(command, stdout=subprocess.PIPE)
|
|
|
|
output, err = proc.communicate()
|
2021-03-07 20:03:16 +01:00
|
|
|
lines = [x.split() for x in output.decode("utf-8").splitlines()]
|
|
|
|
return {s[3].strip(): int(s[0]) for s in lines}
|