From 11dd1b16bfb72217b30c24558d252c927c111710 Mon Sep 17 00:00:00 2001 From: "Salvador E. Tropea" Date: Tue, 26 Apr 2022 16:23:20 -0300 Subject: [PATCH] Now --quick-start is recursive - Fixed some obvious details about cross pcb/sch generation - Improved .kicad_sch guess when more than one and no project --- kibot/__main__.py | 9 ++-- kibot/kiplot.py | 105 ++++++++++++++++++++++++++++++++++---------- kibot/out_bom.py | 3 +- kibot/out_gerber.py | 6 ++- 4 files changed, 96 insertions(+), 27 deletions(-) diff --git a/kibot/__main__.py b/kibot/__main__.py index 175ac277..6f095b18 100644 --- a/kibot/__main__.py +++ b/kibot/__main__.py @@ -12,7 +12,7 @@ Usage: [-q | -v...] [-i] [-C] [-m MKFILE] [-g DEF]... [TARGET...] kibot [-v...] [-b BOARD] [-e SCHEMA] [-c PLOT_CONFIG] --list kibot [-v...] [-b BOARD] [-d OUT_DIR] [-p | -P] --example - kibot [-v...] --quick-start + kibot [-v...] [--start PATH] --quick-start kibot [-v...] --help-filters kibot [-v...] --help-global-options kibot [-v...] --help-list-outputs @@ -32,8 +32,10 @@ Options: -C, --cli-order Generate outputs using the indicated order -d OUT_DIR, --out-dir OUT_DIR The output directory [default: .] -e SCHEMA, --schematic SCHEMA The schematic file (.sch) + --start PATH Starting point for --quick-start -g DEF, --global-redef DEF Overwrite a global value (VAR=VAL) --help-filters List supported filters and details + --help-global-options List supported global variables --help-list-outputs List supported outputs --help-output HELP_OUTPUT Help for this particular output --help-outputs List supported outputs and details @@ -44,10 +46,11 @@ Options: -p, --copy-options Copy plot options from the PCB file -P, --copy-and-expand As -p but expand the list of layers -q, --quiet Remove information logs + --quick-start Generates demo config files and their outputs -s PRE, --skip-pre PRE Skip preflights, comma separated or `all` -v, --verbose Show debugging information -V, --version Show program's version number and exit - -x, --example Create a template configuration file. + -x, --example Create a template configuration file """ __author__ = 'Salvador E. Tropea, John Beard' @@ -275,7 +278,7 @@ def main(): sys.exit(0) if args.quick_start: # Some kind of wizard to get usable examples - generate_examples() + generate_examples(args.start) sys.exit(0) # Determine the YAML file diff --git a/kibot/kiplot.py b/kibot/kiplot.py index 96b528bc..6942b12b 100644 --- a/kibot/kiplot.py +++ b/kibot/kiplot.py @@ -25,7 +25,7 @@ from .registrable import RegOutput from .misc import (PLOT_ERROR, MISSING_TOOL, CMD_EESCHEMA_DO, URL_EESCHEMA_DO, CORRUPTED_PCB, EXIT_BAD_ARGS, CORRUPTED_SCH, EXIT_BAD_CONFIG, WRONG_INSTALL, UI_SMD, UI_VIRTUAL, MOD_SMD, MOD_THROUGH_HOLE, MOD_VIRTUAL, W_PCBNOSCH, W_NONEEDSKIP, W_WRONGCHAR, name2make, W_TIMEOUT, - W_KIAUTO, W_VARSCH, NO_SCH_FILE, NO_PCB_FILE, W_VARPCB, NO_YAML_MODULE) + W_KIAUTO, W_VARSCH, NO_SCH_FILE, NO_PCB_FILE, W_VARPCB, NO_YAML_MODULE, WRONG_ARGUMENTS) from .error import PlotError, KiPlotConfigurationError, config_error, trace_dump from .pre_base import BasePreFlight from .kicad.v5_sch import Schematic, SchFileError, SchError @@ -571,7 +571,21 @@ def generate_makefile(makefile, cfg_file, outputs, kibot_sys=False): f.write('.PHONY: '+' '.join(extra_targets+list(targets.keys()))+'\n') -def solve_schematic(base_dir, a_schematic=None, a_board_file=None, config=None): +def guess_ki6_sch(schematics): + schematics = list(filter(lambda x: x.endswith('.kicad_sch'), schematics)) + if len(schematics) == 1: + return schematics[0] + if len(schematics) == 0: + return None + for fname in schematics: + with open(fname, 'rt') as f: + text = f.read() + if 'sheet_instances' in text: + return fname + return None + + +def solve_schematic(base_dir, a_schematic=None, a_board_file=None, config=None, sug_e=True): schematic = a_schematic if not schematic and a_board_file: base = os.path.splitext(a_board_file)[0] @@ -583,7 +597,9 @@ def solve_schematic(base_dir, a_schematic=None, a_board_file=None, config=None): if os.path.isfile(sch): schematic = sch if not schematic: - schematics = glob(os.path.join(base_dir, '*.sch'))+glob(os.path.join(base_dir, '*.kicad_sch')) + schematics = glob(os.path.join(base_dir, '*.sch')) + if GS.ki6(): + schematics += glob(os.path.join(base_dir, '*.kicad_sch')) if len(schematics) == 1: schematic = schematics[0] logger.info('Using SCH file: '+os.path.relpath(schematic)) @@ -600,7 +616,7 @@ def solve_schematic(base_dir, a_schematic=None, a_board_file=None, config=None): sch = os.path.join(base_dir, config+'.sch') if os.path.isfile(sch): schematic = sch - else: + elif GS.ki6(): # Try KiCad 6 sch = os.path.join(base_dir, config+'.kicad_sch') if os.path.isfile(sch): @@ -616,9 +632,13 @@ def solve_schematic(base_dir, a_schematic=None, a_board_file=None, config=None): break else: # No way to select one, just take the first - schematic = schematics[0] - logger.warning(W_VARSCH + 'More than one SCH file found in current directory.\n' - ' Using '+schematic+' if you want to use another use -e option.') + if GS.ki6(): + schematic = guess_ki6_sch(schematics) + if not schematic: + schematic = schematics[0] + msg = ' if you want to use another use -e option' if sug_e else '' + logger.warning(W_VARSCH + 'More than one SCH file found in `'+base_dir+'`.\n' + ' Using '+schematic+msg+'.') if schematic and not os.path.isfile(schematic): logger.error("Schematic file not found: "+schematic) exit(NO_SCH_FILE) @@ -636,13 +656,14 @@ def check_board_file(board_file): exit(NO_PCB_FILE) -def solve_board_file(base_dir, a_board_file=None): +def solve_board_file(base_dir, a_board_file=None, sug_b=True): schematic = GS.sch_file board_file = a_board_file if not board_file and schematic: pcb = os.path.join(base_dir, os.path.splitext(schematic)[0]+'.kicad_pcb') if os.path.isfile(pcb): board_file = pcb + logger.info('Using PCB file: '+os.path.relpath(board_file)) if not board_file: board_files = glob(os.path.join(base_dir, '*.kicad_pcb')) if len(board_files) == 1: @@ -650,8 +671,9 @@ def solve_board_file(base_dir, a_board_file=None): logger.info('Using PCB file: '+os.path.relpath(board_file)) elif len(board_files) > 1: board_file = board_files[0] - logger.warning(W_VARPCB + 'More than one PCB file found in current directory.\n' - ' Using '+board_file+' if you want to use another use -b option.') + msg = ' if you want to use another use -b option' if sug_b else '' + logger.warning(W_VARPCB + 'More than one PCB file found in `'+base_dir+'`.\n' + ' Using '+board_file+msg+'.') check_board_file(board_file) if board_file: logger.debug('Using PCB: `{}`'.format(board_file)) @@ -709,23 +731,23 @@ def look_for_used_layers(): return layers -def generate_examples(): - # Set default global options - glb = GS.global_opts_class() - glb.set_tree({}) - glb.config(None) - - outs = RegOutput.get_registered() - fname = 'kibot_generated.kibot.yaml' - GS.set_sch(solve_schematic('.')) - GS.set_pcb(solve_board_file('.')) +def generate_one_example(dest_dir): + GS.pcb_file = None + GS.sch_file = None + # Check if we have useful files + fname = os.path.join(dest_dir, 'kibot_generated.kibot.yaml') + GS.set_sch(solve_schematic(dest_dir, sug_e=False)) + GS.set_pcb(solve_board_file(dest_dir, sug_b=False)) GS.set_pro(solve_project_file()) + # Abort if none if not GS.pcb_file and not GS.sch_file: - return + return None + # Reset the board and schematic GS.board = None GS.sch = None + # Create the config with open(fname, 'wt') as f: - logger.info('Creating {} example configuration'.format(fname)) + logger.info('- Creating {} example configuration'.format(fname)) f.write("# This is a working example.\n") f.write("# For a more complete reference use `--example`\n") f.write('kibot:\n version: 1\n\n') @@ -774,6 +796,45 @@ def generate_examples(): else: logger.debug('- {}, nothing to do'.format(n)) f.write(yaml.dump({'outputs': outputs}, sort_keys=False)) + return fname + + +def _walk(path, depth): + """ Recursively list files and directories up to a certain depth """ + depth -= 1 + with os.scandir(path) as p: + for entry in p: + yield entry.path + if entry.is_dir() and depth > 0: + yield from _walk(entry.path, depth) + + +def generate_examples(start_dir): + if not start_dir: + start_dir = '.' + else: + if not os.path.isdir(start_dir): + logger.error('Invalid dir {} to quick start'.format(start_dir)) + exit(WRONG_ARGUMENTS) + # Set default global options + glb = GS.global_opts_class() + glb.set_tree({}) + glb.config(None) + # Look for candidate dirs + k_files_regex = re.compile(r'([^/]+)\.(kicad_pcb|kicad_sch|sch)$') + candidates = set() + for f in _walk(start_dir, 6): + if k_files_regex.search(f): + candidates.add(os.path.dirname(f)) + # Try to generate the configs in the candidate places + confs = [] + for c in candidates: + logger.info('Analyzing `{}` dir'.format(c)) + res = generate_one_example(c) + if res: + confs.append(res) + logger.info('') + logger.debug(confs) # To avoid circular dependencies: Optionable needs it, but almost everything needs Optionable diff --git a/kibot/out_bom.py b/kibot/out_bom.py index f4b74acd..68bfd3c3 100644 --- a/kibot/out_bom.py +++ b/kibot/out_bom.py @@ -798,7 +798,8 @@ class BoM(BaseOutput): # noqa: F821 columns.remove(to_remove) # Currently we have a position example (XYRS) out['dir'] = 'Position' - outs.append(out) + if not out['name'].endswith('_xyrs') or GS.pcb_file: + outs.append(out) @staticmethod def get_conf_examples(name, layers, templates): diff --git a/kibot/out_gerber.py b/kibot/out_gerber.py index 6b22a9c2..7cc54a27 100644 --- a/kibot/out_gerber.py +++ b/kibot/out_gerber.py @@ -124,14 +124,18 @@ class Gerber(AnyLayer): # Add the list of layers to the templates for tpl in templates: for out in tpl: + skip = False if out['type'] == 'gerber': out['layers'] = tpl_layers elif out['type'] == 'position': out['options']['variant'] = 'place_holder' + if out['type'] == 'bom' and not GS.sch_file: + skip = True if out['type'] == 'compress': out['dir'] = 'Manufacturers' out['options']['move_files'] = True else: out['dir'] = os.path.join('Manufacturers', out['dir']) - outs.append(out) + if not skip: + outs.append(out) return outs