def new_keyboard(cli): """Creates a new keyboard. """ cli.log.info('{style_bright}Generating a new QMK keyboard directory{style_normal}') cli.echo('') kb_name = cli.args.keyboard if cli.args.keyboard else prompt_keyboard() user_name = cli.config.new_keyboard.name if cli.config.new_keyboard.name else prompt_user() real_name = cli.args.realname or cli.config.new_keyboard.name if cli.args.realname or cli.config.new_keyboard.name else prompt_name(user_name) default_layout = cli.args.layout if cli.args.layout else prompt_layout() mcu = cli.args.type if cli.args.type else prompt_mcu() bootloader = select_default_bootloader(mcu) if not validate_keyboard_name(kb_name): cli.log.error('Keyboard names must contain only {fg_cyan}lowercase a-z{fg_reset}, {fg_cyan}0-9{fg_reset}, and {fg_cyan}_{fg_reset}! Please choose a different name.') return 1 if keyboard(kb_name).exists(): cli.log.error(f'Keyboard {{fg_cyan}}{kb_name}{{fg_reset}} already exists! Please choose a different name.') return 1 tokens = { # Comment here is to force multiline formatting 'YEAR': str(date.today().year), 'KEYBOARD': kb_name, 'USER_NAME': user_name, 'REAL_NAME': real_name, 'LAYOUT': default_layout, 'MCU': mcu, 'BOOTLOADER': bootloader } if cli.config.general.verbose: cli.log.info("Creating keyboard with:") for key, value in tokens.items(): cli.echo(f" {key.ljust(10)}: {value}") # TODO: detach community layout and rename to just "LAYOUT" if default_layout == 'none of the above': default_layout = "ortho_4x4" # begin with making the deepest folder in the tree keymaps_path = keyboard(kb_name) / 'keymaps/' keymaps_path.mkdir(parents=True) # copy in keymap.c or keymap.json community_keymap = Path(COMMUNITY / f'{default_layout}/default_{default_layout}/') shutil.copytree(community_keymap, keymaps_path / 'default') # process template files for file in list(TEMPLATE.iterdir()): replace_placeholders(file, keyboard(kb_name) / file.name, tokens) # merge in infos community_info = Path(COMMUNITY / f'{default_layout}/info.json') augment_community_info(community_info, keyboard(kb_name) / community_info.name) cli.log.info(f'{{fg_green}}Created a new keyboard called {{fg_cyan}}{kb_name}{{fg_green}}.{{fg_reset}}') cli.log.info(f'To start working on things, `cd` into {{fg_cyan}}keyboards/{kb_name}{{fg_reset}},') cli.log.info('or open the directory in your preferred text editor.') cli.log.info(f"And build with {{fg_yellow}}qmk compile -kb {kb_name} -km default{{fg_reset}}.")
def _rules_mk_assignment_only(kb): """Check the keyboard-level rules.mk to ensure it only has assignments. """ keyboard_path = keyboard(kb) current_path = Path() errors = [] for path_part in keyboard_path.parts: current_path = current_path / path_part rules_mk = current_path / 'rules.mk' if rules_mk.exists(): 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 and '=' not in line: errors.append( f'Non-assignment code on line +{i} {rules_mk}: {line}' ) return errors
def lint(cli): """Check keyboard and keymap for common mistakes. """ if not cli.config.lint.keyboard: cli.log.error('Missing required argument: --keyboard') cli.print_help() return False if not is_keyboard(cli.config.lint.keyboard): cli.log.error('No such keyboard: %s', cli.config.lint.keyboard) return False # Gather data about the keyboard. ok = True keyboard_path = keyboard(cli.config.lint.keyboard) keyboard_info = info_json(cli.config.lint.keyboard) readme_path = find_readme(cli.config.lint.keyboard) missing_readme_path = keyboard_path / 'readme.md' # Check for errors in the info.json if keyboard_info['parse_errors']: ok = False cli.log.error('Errors found when generating info.json.') if cli.config.lint.strict and keyboard_info['parse_warnings']: ok = False cli.log.error( 'Warnings found when generating info.json (Strict mode enabled.)') # Check for a readme.md and warn if it doesn't exist if not readme_path: ok = False cli.log.error('Missing %s', missing_readme_path) # Keymap specific checks if cli.config.lint.keymap: keymap_path = locate_keymap(cli.config.lint.keyboard, cli.config.lint.keymap) if not keymap_path: ok = False cli.log.error("Can't find %s keymap for %s keyboard.", cli.config.lint.keymap, cli.config.lint.keyboard) else: keymap_readme = keymap_path.parent / 'readme.md' if not keymap_readme.exists(): cli.log.warning('Missing %s', keymap_readme) if cli.config.lint.strict: ok = False # Check and report the overall status if ok: cli.log.info('Lint check passed!') return True cli.log.error('Lint check failed!') return False
def prompt_keyboard(): prompt = """{fg_yellow}Name Your Keyboard Project{style_reset_all} For more infomation, see: https://docs.qmk.fm/#/hardware_keyboard_guidelines?id=naming-your-keyboardproject Keyboard Name? """ errmsg = 'Keyboard already exists! Please choose a different name:' return _question(prompt, reprompt=errmsg, validate=lambda x: not keyboard(x).exists())
def lint(cli): """Check keyboard and keymap for common mistakes. """ failed = [] # Determine our keyboard list if cli.args.all_kb: if cli.args.keyboard: cli.log.warning('Both --all-kb and --keyboard passed, --all-kb takes presidence.') keyboard_list = list_keyboards() elif not cli.config.lint.keyboard: cli.log.error('Missing required arguments: --keyboard or --all-kb') cli.print_help() return False else: keyboard_list = cli.config.lint.keyboard.split(',') # Lint each keyboard for kb in keyboard_list: if not is_keyboard(kb): cli.log.error('No such keyboard: %s', kb) continue # Gather data about the keyboard. ok = True keyboard_path = keyboard(kb) keyboard_info = info_json(kb) # Check for errors in the info.json if keyboard_info['parse_errors']: ok = False cli.log.error('%s: Errors found when generating info.json.', kb) if cli.config.lint.strict and keyboard_info['parse_warnings']: ok = False cli.log.error('%s: Warnings found when generating info.json (Strict mode enabled.)', kb) # Check the rules.mk file(s) rules_mk_assignment_errors = rules_mk_assignment_only(keyboard_path) 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) # Keymap specific checks if cli.config.lint.keymap: if not keymap_check(kb, cli.config.lint.keymap): ok = False # Report status if not ok: failed.append(kb) # Check and report the overall status if failed: cli.log.error('Lint check failed for: %s', ', '.join(failed)) return False cli.log.info('Lint check passed!') return True