Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend lint to reject 'blank' files #23994

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions lib/python/qmk/c_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def _get_chunks(it, size):
return iter(lambda: tuple(islice(it, size)), ())


def _preprocess_c_file(file):
def preprocess_c_file(file):
"""Load file and strip comments
"""
file_contents = file.read_text(encoding='utf-8')
Expand Down Expand Up @@ -66,7 +66,7 @@ def find_layouts(file):
parsed_layouts = {}

# Search the file for LAYOUT macros and aliases
file_contents = _preprocess_c_file(file)
file_contents = preprocess_c_file(file)

for line in file_contents.split('\n'):
if layout_macro_define_regex.match(line.lstrip()) and '(' in line and 'LAYOUT' in line:
Expand Down Expand Up @@ -248,7 +248,7 @@ def _parse_led_config(file, matrix_cols, matrix_rows):
current_row_index = 0
current_row = []

for _type, value in lex(_preprocess_c_file(file), CLexer()):
for _type, value in lex(preprocess_c_file(file), CLexer()):
if not found_g_led_config:
# Check for type
if value == 'led_config_t':
Expand Down
112 changes: 81 additions & 31 deletions lib/python/qmk/cli/lint.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from qmk.keymap import locate_keymap, list_keymaps
from qmk.path import keyboard
from qmk.git import git_get_ignored_files
from qmk.c_parse import c_source_files
from qmk.c_parse import c_source_files, preprocess_c_file

CHIBIOS_CONF_CHECKS = ['chconf.h', 'halconf.h', 'mcuconf.h', 'board.h']
INVALID_KB_FEATURES = set(['encoder_map', 'dip_switch_map', 'combo', 'tap_dance', 'via'])
Expand All @@ -32,12 +32,42 @@ def _list_defaultish_keymaps(kb):
return keymaps


def _get_build_files(kb, km=None):
"""Return potential keyboard/keymap build files
"""
search_path = locate_keymap(kb, km).parent if km else keyboard(kb)

build_files = []

if not km:
current_path = Path()
for path_part in search_path.parts:
current_path = current_path / path_part
build_files.extend(current_path.glob('*rules.mk'))

for file in search_path.glob("**/*rules.mk"):
# Ignore keymaps when only globing keyboard files
if not km and 'keymaps' in file.parts:
continue
build_files.append(file)

return set(build_files)


def _get_code_files(kb, km=None):
"""Return potential keyboard/keymap code files
"""
search_path = locate_keymap(kb, km).parent if km else keyboard(kb)

code_files = []

if not km:
current_path = Path()
for path_part in search_path.parts:
current_path = current_path / path_part
code_files.extend(current_path.glob('*.h'))
code_files.extend(current_path.glob('*.c'))

for file in c_source_files([search_path]):
# Ignore keymaps when only globing keyboard files
if not km and 'keymaps' in file.parts:
Expand All @@ -47,6 +77,24 @@ def _get_code_files(kb, km=None):
return code_files


def _is_empty_rules(file):
"""Check if file contains any useful content
"""
for line in file.read_text(encoding='utf-8').split("\n"):
if len(line) > 0 and not line.isspace() and not line.startswith('#'):
return False
return True


def _is_empty_include(file):
"""Check if file contains any useful content
"""
for line in preprocess_c_file(file).split("\n"):
if len(line) > 0 and not line.isspace() and not line.startswith('#pragma once'):
return False
return True


def _has_license(file):
"""Check file has a license header
"""
Expand Down Expand Up @@ -90,37 +138,28 @@ def _chibios_conf_includenext_check(target):
return None


def _rules_mk_assignment_only(kb):
def _rules_mk_assignment_only(rules_mk):
"""Check the keyboard-level rules.mk to ensure it only has assignments.
"""
keyboard_path = keyboard(kb)
current_path = Path()
errors = []
continuation = None
for i, line in enumerate(rules_mk.open()):
line = line.strip()

for path_part in keyboard_path.parts:
current_path = current_path / path_part
rules_mk = current_path / 'rules.mk'
if '#' in line:
line = line[:line.index('#')]

if rules_mk.exists():
if continuation:
line = continuation + line
continuation = None

for i, line in enumerate(rules_mk.open()):
line = line.strip()

if '#' in line:
line = line[:line.index('#')]

if continuation:
line = continuation + line
continuation = None

if line:
if line[-1] == '\\':
continuation = line[:-1]
continue
if line:
if line[-1] == '\\':
continuation = line[:-1]
continue

if line and '=' not in line:
errors.append(f'Non-assignment code on line +{i} {rules_mk}: {line}')
if line and '=' not in line:
errors.append(f'Non-assignment code on line +{i} {rules_mk}: {line}')

return errors

Expand Down Expand Up @@ -169,25 +208,36 @@ def keyboard_check(kb):
if not _handle_invalid_features(kb, kb_info):
ok = False

rules_mk_assignment_errors = _rules_mk_assignment_only(kb)
if rules_mk_assignment_errors:
ok = False
cli.log.error('%s: Non-assignment code found in rules.mk. Move it to post_rules.mk instead.', kb)
for assignment_error in rules_mk_assignment_errors:
cli.log.error(assignment_error)

invalid_files = git_get_ignored_files(f'keyboards/{kb}/')
for file in invalid_files:
if 'keymap' in file:
continue
cli.log.error(f'{kb}: The file "{file}" should not exist!')
ok = False

for file in _get_build_files(kb):
if _is_empty_rules(file):
cli.log.error(f'{kb}: The file "{file}" is effectively empty and should be removed!')
ok = False

if file.suffix in ['rules.mk']:
rules_mk_assignment_errors = _rules_mk_assignment_only(file)
if rules_mk_assignment_errors:
ok = False
cli.log.error('%s: Non-assignment code found in rules.mk. Move it to post_rules.mk instead.', kb)
for assignment_error in rules_mk_assignment_errors:
cli.log.error(assignment_error)

for file in _get_code_files(kb):
if not _has_license(file):
cli.log.error(f'{kb}: The file "{file}" does not have a license header!')
ok = False

if file.name in ['config.h']:
if _is_empty_include(file):
cli.log.error(f'{kb}: The file "{file}" is effectively empty and should be removed!')
ok = False

if file.name in CHIBIOS_CONF_CHECKS:
check_error = _chibios_conf_includenext_check(file)
if check_error is not None:
Expand Down
Loading