summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--etc/defaults/lint/TARGETS.just3
-rw-r--r--lint/TARGETS43
-rwxr-xr-xlint/run_clang_tidy.py53
-rwxr-xr-xlint/summary.py50
4 files changed, 149 insertions, 0 deletions
diff --git a/etc/defaults/lint/TARGETS.just b/etc/defaults/lint/TARGETS.just
new file mode 100644
index 00000000..de52b000
--- /dev/null
+++ b/etc/defaults/lint/TARGETS.just
@@ -0,0 +1,3 @@
+{ "defaults":
+ {"type": "defaults", "base": [["CC", "defaults"], ["shell", "defaults"]]}
+}
diff --git a/lint/TARGETS b/lint/TARGETS
new file mode 100644
index 00000000..a5456c85
--- /dev/null
+++ b/lint/TARGETS
@@ -0,0 +1,43 @@
+{ "":
+ { "type": "install"
+ , "tainted": ["lint", "test"]
+ , "dirs": [["LINT: clang-tidy", "clang-tidy"]]
+ }
+, "clang toolchain":
+ { "type": "configure"
+ , "arguments_config": ["TOOLCHAIN_CONFIG"]
+ , "target": ["@", "clang", "", "toolchain"]
+ , "config":
+ { "type": "`"
+ , "$1":
+ { "TOOLCHAIN_CONFIG":
+ { "type": ","
+ , "$1":
+ { "type": "map_union"
+ , "$1":
+ [ { "type": "var"
+ , "name": "TOOLCHAIN_CONFIG"
+ , "default": {"type": "empty_map"}
+ }
+ , {"type": "'", "$1": {"INCLUDE_LINTER": true}}
+ ]
+ }
+ }
+ }
+ }
+ }
+, "clang": {"type": "install", "dirs": [["clang toolchain", "toolchain"]]}
+, "LINT: clang-tidy":
+ { "type": ["@", "rules", "lint", "targets"]
+ , "tainted": ["test"]
+ , "name": ["clang-tidy"]
+ , "linter": ["run_clang_tidy.py"]
+ , "summarizer": ["summary.py"]
+ , "config": [["@", "src", "", ".clang-tidy"], "clang"]
+ , "targets":
+ [ ["@", "src", "src/buildtool/main", "just"]
+ , ["@", "src", "src/other_tools/just_mr", "just-mr"]
+ , ["@", "tests", "", "ALL"]
+ ]
+ }
+}
diff --git a/lint/run_clang_tidy.py b/lint/run_clang_tidy.py
new file mode 100755
index 00000000..3fb8a595
--- /dev/null
+++ b/lint/run_clang_tidy.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python3
+# Copyright 2025 Huawei Cloud Computing Technology Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+import os
+import shutil
+import subprocess
+import sys
+
+
+def dump_meta(src, cmd):
+ OUT = os.environ.get("OUT")
+ if OUT:
+ with open(os.path.join(OUT, "config.json"), "w") as f:
+ json.dump({"src": src, "cmd": cmd}, f)
+
+
+def run_lint(src, cmd):
+ dump_meta(src, cmd)
+ config = os.environ.get("CONFIG")
+ shutil.copyfile(os.path.join(config, ".clang-tidy"), ".clang-tidy")
+ extra = ["-Wno-unused-command-line-argument"]
+ if src.endswith(".tpp"):
+ extra += ["-x", "c++"]
+ db = [{
+ "directory": os.getcwd(),
+ "arguments": cmd[:1] + extra + cmd[1:],
+ "file": src
+ }]
+ with open("compile_commands.json", "w") as f:
+ json.dump(db, f)
+ new_cmd = [
+ os.path.join(config, "toolchain", "bin", "clang-tidy"),
+ src,
+ ]
+ print("Running cmd %r with db %r" % (new_cmd, db), file=sys.stderr)
+ return subprocess.run(new_cmd).returncode
+
+
+if __name__ == "__main__":
+ sys.exit(run_lint(sys.argv[1], sys.argv[2:]))
diff --git a/lint/summary.py b/lint/summary.py
new file mode 100755
index 00000000..ca7445db
--- /dev/null
+++ b/lint/summary.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python3
+# Copyright 2025 Huawei Cloud Computing Technology Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import json
+import os
+import sys
+
+FAILED = {}
+
+for lint in sorted(os.listdir()):
+ if os.path.isdir(lint):
+ with open(os.path.join(lint, "result")) as f:
+ result = f.read().strip()
+ if result != "PASS":
+ record = {}
+ with open(os.path.join(lint, "out/config.json")) as f:
+ record["config"] = json.load(f)
+ with open(os.path.join(lint, "stdout")) as f:
+ log = f.read()
+ with open(os.path.join(lint, "stderr")) as f:
+ log += f.read()
+ record["log"] = log
+ FAILED[lint] = record
+
+with open(os.path.join(os.environ.get("OUT"), "failures.json"), "w") as f:
+ json.dump(FAILED, f)
+
+failures = list(FAILED.keys())
+
+for f in failures:
+ src = FAILED[f]["config"]["src"]
+ log = FAILED[f]["log"]
+
+ print("%s %s" % (f, src))
+ print("".join([" " + line + "\n" for line in log.splitlines()]))
+
+if failures:
+ sys.exit(1)