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
This commit is contained in:
Salvador E. Tropea 2022-04-26 16:23:20 -03:00
parent 50ebaf0a15
commit 11dd1b16bf
4 changed files with 96 additions and 27 deletions

View File

@ -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

View File

@ -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

View File

@ -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):

View File

@ -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