--convert-tabs
--exclude=subprojects
--exclude=build
--i
--j
--f
--A2
--U
--p
--xg
--k3
--w
+--ignore-exclude-errors
+--add-braces
+--break-blocks
+--style=attach
+--unpad-paren
+--pad-oper
+--pad-return-type
+--align-pointer=name
+--indent-preproc-define
--formatted
--- /dev/null
+#!/usr/bin/env python3
+
+"""Run linters on project code. Add --fix to autofix files with linters that support it."""
+
+import sys
+import subprocess as subp
+from glob import glob
+from os import path, environ, chdir
+
+DRY = "--fix" not in sys.argv or environ.get("CI")
+HEADER = "#" * 24
+
+if DRY:
+ MSG = """
+You're running linters in non-destructive readonly mode.
+Some of them support automated fixes (like reformatting code).
+To apply them, run `lint.py --fix` or `ninja -C build reformat`.
+"""
+ print(MSG, file=sys.stderr)
+
+source_root = path.dirname(path.realpath(__file__))
+source_root = environ.get("MESON_SOURCE_ROOT", source_root)
+chdir(source_root)
+
+# It's best not to use globs that cover everything in the project — if integration
+# tests are run with a large --repeat value, test working directory can reach
+# enormous sizes, and linters either get very slow, or start crashing.
+linters = (
+ [
+ "astyle",
+ "--recursive",
+ "--options=.astylerc",
+ "--dry-run" if DRY else "--formatted",
+ "./*.c",
+ "./*.h",
+ ],
+ ["shfmt", "-d" if DRY else "-w", "-i", "2", "-s", "."],
+ ["black", "--check" if DRY else ".", "."],
+ ["pylint", "."],
+ ["mypy", "--exclude", "build", "."],
+ ["shellcheck", "-x", *glob(".ci/**/*.sh", recursive=True)],
+)
+
+failed: bool = False
+
+for cmd in linters:
+ exe = cmd[0]
+ print(f"{HEADER} Running linter '{exe}' {HEADER}")
+
+ try:
+ res = subp.run(
+ cmd,
+ check=False,
+ stdout=subp.PIPE,
+ encoding="utf-8",
+ )
+ failed = (
+ failed
+ or bool(res.returncode)
+ or (exe == "astyle" and "Formatted " in res.stdout)
+ )
+ print(res.stdout)
+ except FileNotFoundError as e:
+ print(f"Warning: linter {exe} is missing", file=sys.stderr)
+
+sys.exit(int(failed))
endif
run_target('reformat', command: [
- find_program('python3'),
- '@SOURCE_ROOT@/reformat.py',
+ python,
+ '@SOURCE_ROOT@/lint.py',
+ '--fix',
+])
+
+run_target('lint', command: [
+ python,
+ '@SOURCE_ROOT@/lint.py',
])
+++ /dev/null
-#!/usr/bin/env python3
-
-from os import path, environ
-from sys import stderr
-import subprocess as subp
-import glob
-
-source_root = path.dirname(path.realpath(__file__))
-source_root = environ.get("MESON_SOURCE_ROOT", source_root)
-
-astyle_cmd = [
- "astyle",
- "--options=.astylerc",
- "--recursive",
- "*.c",
- "*.h",
-]
-
-shfmt_cmd = [
- "shfmt",
- "-i", "2",
- "-s",
- "-w",
-]
-
-for path in "**/*.sh", "**/*.test", ".ci/**/*.sh":
- shfmt_cmd.extend(glob.glob(path, root_dir=source_root, recursive=True))
-
-for cmd in astyle_cmd, shfmt_cmd:
- try:
- result = subp.run(cmd, cwd=source_root, check=True)
- except FileNotFoundError as e:
- print("Warning: missing", cmd[0], file=stderr)
#!/usr/bin/env python3
+"""Print current tinc version for using in build scripts.
+
+First try to determine the latest version using git tags. If this fails (because
+the .git directory is missing, git is not installed, or for some other reason),
+fall back to using the VERSION file. If it is not present or could not be read,
+use 'unknown'.
+"""
+
from os import path, environ
from sys import argv, stderr
import subprocess as subp
+import typing as T
-prefix = "release-"
-source_root = path.dirname(path.realpath(__file__))
-source_root = environ.get("MESON_SOURCE_ROOT", source_root)
+PREFIX = "release-"
+SOURCE_ROOT = path.dirname(path.realpath(__file__))
+SOURCE_ROOT = environ.get("MESON_SOURCE_ROOT", SOURCE_ROOT)
cmd = [
"git",
"--git-dir",
- path.join(source_root, ".git"),
+ path.join(SOURCE_ROOT, ".git"),
"describe",
"--always",
"--tags",
- "--match=" + prefix + "*",
+ "--match=" + PREFIX + "*",
]
if "short" in argv:
cmd.append("--abbrev=0")
-version = None
+version: T.Optional[str] = None
try:
- result = subp.run(cmd, stdout=subp.PIPE, encoding="utf-8")
+ result = subp.run(cmd, stdout=subp.PIPE, encoding="utf-8", check=False)
if not result.returncode:
version = result.stdout
except FileNotFoundError:
if not version:
try:
- with open(path.join(source_root, "VERSION"), "r") as f:
+ with open(path.join(SOURCE_ROOT, "VERSION"), "r", encoding="utf-8") as f:
version = f.read().strip()
except OSError as e:
print("could not read version from file", e, file=stderr)
-elif version.startswith(prefix):
- version = version[len(prefix):].strip()
+elif version.startswith(PREFIX):
+ version = version[len(PREFIX) :].strip()
print(version if version else "unknown", end="")