Now layers are solved when we really need it.
So we can do some operations (like --list) without loading the board. Also: now the names for the layers are asked to pcbnew classes (not from file)
This commit is contained in:
parent
ef4b2f8f95
commit
42d1f14056
|
|
@ -11,6 +11,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- Drill outputs: map.type and report.filename now should be map and report.
|
- Drill outputs: map.type and report.filename now should be map and report.
|
||||||
The old mechanism is currently supported, but deprecated.
|
The old mechanism is currently supported, but deprecated.
|
||||||
- Now the command line usage is more clearly documented, but also more strict.
|
- Now the command line usage is more clearly documented, but also more strict.
|
||||||
|
- The --list option doesn't need a PCB file anymore.
|
||||||
|
Note that passing it is now considered an error.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
- Help for the supported outputs (--help-list-outputs, --help-outputs and
|
- Help for the supported outputs (--help-list-outputs, --help-outputs and
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
Usage:
|
Usage:
|
||||||
kiplot [-b BOARD] [-c CONFIG] [-d OUT_DIR] [-s PRE] [-q | -v...] [-i]
|
kiplot [-b BOARD] [-c CONFIG] [-d OUT_DIR] [-s PRE] [-q | -v...] [-i]
|
||||||
[TARGET...]
|
[TARGET...]
|
||||||
kiplot [-b BOARD] [-c PLOT_CONFIG] --list
|
kiplot [-c PLOT_CONFIG] --list
|
||||||
kiplot --help-list-outputs
|
kiplot --help-list-outputs
|
||||||
kiplot --help-output=HELP_OUTPUT
|
kiplot --help-output=HELP_OUTPUT
|
||||||
kiplot --help-outputs
|
kiplot --help-outputs
|
||||||
|
|
@ -90,30 +90,6 @@ def main():
|
||||||
print_preflights_help()
|
print_preflights_help()
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
# Determine the PCB file
|
|
||||||
if args.board_file is None:
|
|
||||||
board_files = glob('*.kicad_pcb')
|
|
||||||
if len(board_files) == 1:
|
|
||||||
board_file = board_files[0]
|
|
||||||
logger.info('Using PCB file: '+board_file)
|
|
||||||
elif len(board_files) > 1:
|
|
||||||
board_file = board_files[0]
|
|
||||||
logger.warning('More than one PCB file found in current directory.\n'
|
|
||||||
' Using '+board_file+' if you want to use another use -b option.')
|
|
||||||
else:
|
|
||||||
logger.error('No PCB file found (*.kicad_pcb), use -b to specify one.')
|
|
||||||
sys.exit(EXIT_BAD_ARGS)
|
|
||||||
else:
|
|
||||||
board_file = args.board_file
|
|
||||||
if not os.path.isfile(board_file):
|
|
||||||
logger.error("Board file not found: "+board_file)
|
|
||||||
sys.exit(NO_PCB_FILE)
|
|
||||||
GS.pcb_file = board_file
|
|
||||||
GS.sch_file = os.path.splitext(board_file)[0] + '.sch'
|
|
||||||
if not os.path.isfile(GS.sch_file):
|
|
||||||
logger.warning('Missing schematic file: ' + GS.sch_file)
|
|
||||||
GS.sch_file = None
|
|
||||||
|
|
||||||
# Determine the YAML file
|
# Determine the YAML file
|
||||||
if args.plot_config is None:
|
if args.plot_config is None:
|
||||||
plot_configs = glob('*.kiplot.yaml')
|
plot_configs = glob('*.kiplot.yaml')
|
||||||
|
|
@ -151,6 +127,30 @@ def main():
|
||||||
list_pre_and_outs(logger, outputs)
|
list_pre_and_outs(logger, outputs)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
# Determine the PCB file
|
||||||
|
if args.board_file is None:
|
||||||
|
board_files = glob('*.kicad_pcb')
|
||||||
|
if len(board_files) == 1:
|
||||||
|
board_file = board_files[0]
|
||||||
|
logger.info('Using PCB file: '+board_file)
|
||||||
|
elif len(board_files) > 1:
|
||||||
|
board_file = board_files[0]
|
||||||
|
logger.warning('More than one PCB file found in current directory.\n'
|
||||||
|
' Using '+board_file+' if you want to use another use -b option.')
|
||||||
|
else:
|
||||||
|
logger.error('No PCB file found (*.kicad_pcb), use -b to specify one.')
|
||||||
|
sys.exit(EXIT_BAD_ARGS)
|
||||||
|
else:
|
||||||
|
board_file = args.board_file
|
||||||
|
if not os.path.isfile(board_file):
|
||||||
|
logger.error("Board file not found: "+board_file)
|
||||||
|
sys.exit(NO_PCB_FILE)
|
||||||
|
GS.pcb_file = board_file
|
||||||
|
GS.sch_file = os.path.splitext(board_file)[0] + '.sch'
|
||||||
|
if not os.path.isfile(GS.sch_file):
|
||||||
|
logger.warning('Missing schematic file: ' + GS.sch_file)
|
||||||
|
GS.sch_file = None
|
||||||
|
|
||||||
generate_outputs(outputs, args.target, args.invert_sel, args.skip_pre)
|
generate_outputs(outputs, args.target, args.invert_sel, args.skip_pre)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,11 @@
|
||||||
Class to read KiPlot config files
|
Class to read KiPlot config files
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
|
||||||
import sys
|
import sys
|
||||||
from collections import OrderedDict
|
from collections import OrderedDict
|
||||||
|
|
||||||
import pcbnew
|
|
||||||
|
|
||||||
from .error import (KiPlotConfigurationError)
|
from .error import (KiPlotConfigurationError)
|
||||||
from .kiplot import (Layer, get_layer_id_from_pcb)
|
from .kiplot import (Layer)
|
||||||
from .misc import (NO_YAML_MODULE, EXIT_BAD_CONFIG, EXIT_BAD_ARGS)
|
from .misc import (NO_YAML_MODULE, EXIT_BAD_CONFIG, EXIT_BAD_ARGS)
|
||||||
from mcpy import activate # noqa: F401
|
from mcpy import activate # noqa: F401
|
||||||
# Output classes
|
# Output classes
|
||||||
|
|
@ -69,52 +66,6 @@ class CfgYamlReader(object):
|
||||||
config_error("Unknown KiPlot config version: "+str(version))
|
config_error("Unknown KiPlot config version: "+str(version))
|
||||||
return version
|
return version
|
||||||
|
|
||||||
def _get_layer_from_str(self, s):
|
|
||||||
"""
|
|
||||||
Get the pcbnew layer from a string in the config
|
|
||||||
"""
|
|
||||||
D = {
|
|
||||||
'F.Cu': pcbnew.F_Cu,
|
|
||||||
'B.Cu': pcbnew.B_Cu,
|
|
||||||
'F.Adhes': pcbnew.F_Adhes,
|
|
||||||
'B.Adhes': pcbnew.B_Adhes,
|
|
||||||
'F.Paste': pcbnew.F_Paste,
|
|
||||||
'B.Paste': pcbnew.B_Paste,
|
|
||||||
'F.SilkS': pcbnew.F_SilkS,
|
|
||||||
'B.SilkS': pcbnew.B_SilkS,
|
|
||||||
'F.Mask': pcbnew.F_Mask,
|
|
||||||
'B.Mask': pcbnew.B_Mask,
|
|
||||||
'Dwgs.User': pcbnew.Dwgs_User,
|
|
||||||
'Cmts.User': pcbnew.Cmts_User,
|
|
||||||
'Eco1.User': pcbnew.Eco1_User,
|
|
||||||
'Eco2.User': pcbnew.Eco2_User,
|
|
||||||
'Edge.Cuts': pcbnew.Edge_Cuts,
|
|
||||||
'Margin': pcbnew.Margin,
|
|
||||||
'F.CrtYd': pcbnew.F_CrtYd,
|
|
||||||
'B.CrtYd': pcbnew.B_CrtYd,
|
|
||||||
'F.Fab': pcbnew.F_Fab,
|
|
||||||
'B.Fab': pcbnew.B_Fab,
|
|
||||||
}
|
|
||||||
layer = None
|
|
||||||
# Priority
|
|
||||||
# 1) Internal list
|
|
||||||
if s in D:
|
|
||||||
layer = Layer(D[s], False, s)
|
|
||||||
else:
|
|
||||||
id = get_layer_id_from_pcb(s)
|
|
||||||
if id is not None:
|
|
||||||
# 2) List from the PCB
|
|
||||||
layer = Layer(id, id < pcbnew.B_Cu, s)
|
|
||||||
elif s.startswith("Inner"):
|
|
||||||
# 3) Inner.N names
|
|
||||||
m = re.match(r"^Inner\.([0-9]+)$", s)
|
|
||||||
if not m:
|
|
||||||
raise KiPlotConfigurationError("Malformed inner layer name: {}, use Inner.N".format(s))
|
|
||||||
layer = Layer(int(m.group(1)), True, s)
|
|
||||||
else:
|
|
||||||
raise KiPlotConfigurationError('Unknown layer name: '+s)
|
|
||||||
return layer
|
|
||||||
|
|
||||||
def _parse_layers(self, layers_to_parse):
|
def _parse_layers(self, layers_to_parse):
|
||||||
# Check we have a list of layers
|
# Check we have a list of layers
|
||||||
if not isinstance(layers_to_parse, list):
|
if not isinstance(layers_to_parse, list):
|
||||||
|
|
@ -139,9 +90,7 @@ class CfgYamlReader(object):
|
||||||
if layer is None:
|
if layer is None:
|
||||||
raise KiPlotConfigurationError("Missing `layer` attribute for layer entry ({})".format(l))
|
raise KiPlotConfigurationError("Missing `layer` attribute for layer entry ({})".format(l))
|
||||||
# Create an object for it
|
# Create an object for it
|
||||||
layer = self._get_layer_from_str(layer)
|
layers.append(Layer(layer, suffix, description))
|
||||||
layer.set_extra(suffix, description)
|
|
||||||
layers.append(layer)
|
|
||||||
return layers
|
return layers
|
||||||
|
|
||||||
def _parse_output(self, o_obj):
|
def _parse_output(self, o_obj):
|
||||||
|
|
|
||||||
|
|
@ -36,53 +36,78 @@ class GS(object):
|
||||||
out_dir = None
|
out_dir = None
|
||||||
filter_file = None
|
filter_file = None
|
||||||
debug_enabled = False
|
debug_enabled = False
|
||||||
pcb_layers = None
|
|
||||||
|
|
||||||
|
|
||||||
class Layer(object):
|
class Layer(object):
|
||||||
""" A layer description """
|
""" A layer description """
|
||||||
def __init__(self, id, is_inner, name):
|
# Default names
|
||||||
self.id = id
|
DEFAULT_LAYER_NAMES = {
|
||||||
self.is_inner = is_inner
|
'F.Cu': pcbnew.F_Cu,
|
||||||
self.name = name
|
'B.Cu': pcbnew.B_Cu,
|
||||||
self.suffix = None
|
'F.Adhes': pcbnew.F_Adhes,
|
||||||
self.desc = None
|
'B.Adhes': pcbnew.B_Adhes,
|
||||||
|
'F.Paste': pcbnew.F_Paste,
|
||||||
|
'B.Paste': pcbnew.B_Paste,
|
||||||
|
'F.SilkS': pcbnew.F_SilkS,
|
||||||
|
'B.SilkS': pcbnew.B_SilkS,
|
||||||
|
'F.Mask': pcbnew.F_Mask,
|
||||||
|
'B.Mask': pcbnew.B_Mask,
|
||||||
|
'Dwgs.User': pcbnew.Dwgs_User,
|
||||||
|
'Cmts.User': pcbnew.Cmts_User,
|
||||||
|
'Eco1.User': pcbnew.Eco1_User,
|
||||||
|
'Eco2.User': pcbnew.Eco2_User,
|
||||||
|
'Edge.Cuts': pcbnew.Edge_Cuts,
|
||||||
|
'Margin': pcbnew.Margin,
|
||||||
|
'F.CrtYd': pcbnew.F_CrtYd,
|
||||||
|
'B.CrtYd': pcbnew.B_CrtYd,
|
||||||
|
'F.Fab': pcbnew.F_Fab,
|
||||||
|
'B.Fab': pcbnew.B_Fab,
|
||||||
|
}
|
||||||
|
# Names from the board file
|
||||||
|
pcb_layers = {}
|
||||||
|
|
||||||
def set_extra(self, suffix, desc):
|
def __init__(self, name, suffix, desc):
|
||||||
|
self.id = pcbnew.UNDEFINED_LAYER
|
||||||
|
self.is_inner = False
|
||||||
|
self.name = name
|
||||||
self.suffix = suffix
|
self.suffix = suffix
|
||||||
self.desc = desc
|
self.desc = desc
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_pcb_layers(board):
|
||||||
|
for id in range(pcbnew.PCBNEW_LAYER_ID_START, pcbnew.PCB_LAYER_ID_COUNT):
|
||||||
|
Layer.pcb_layers[board.GetLayerName(id)] = id
|
||||||
|
|
||||||
|
def get_layer_id_from_name(self, layer_cnt):
|
||||||
|
""" Get the pcbnew layer from the string provided in the config """
|
||||||
|
# Priority
|
||||||
|
# 1) Internal list
|
||||||
|
if self.name in Layer.DEFAULT_LAYER_NAMES:
|
||||||
|
self.id = Layer.DEFAULT_LAYER_NAMES[self.name]
|
||||||
|
else:
|
||||||
|
id = Layer.pcb_layers.get(self.name)
|
||||||
|
if id is not None:
|
||||||
|
# 2) List from the PCB
|
||||||
|
self.id = id
|
||||||
|
self.is_inner = id < pcbnew.B_Cu
|
||||||
|
elif self.name.startswith("Inner"):
|
||||||
|
# 3) Inner.N names
|
||||||
|
m = re.match(r"^Inner\.([0-9]+)$", self.name)
|
||||||
|
if not m:
|
||||||
|
raise PlotError("Malformed inner layer name: {}, use Inner.N".format(self.name))
|
||||||
|
self.id = int(m.group(1))
|
||||||
|
self.is_inner = True
|
||||||
|
else:
|
||||||
|
raise PlotError("Unknown layer name: "+self.name)
|
||||||
|
# Check if the layer is in use
|
||||||
|
if self.is_inner and (self.id < 1 or self.id >= layer_cnt - 1):
|
||||||
|
raise PlotError("Inner layer `{}` is not valid for this board".format(self))
|
||||||
|
return self.id
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return "{} ({} '{}' {})".format(self.name, self.id, self.desc, self.suffix)
|
return "{} ({} '{}' {})".format(self.name, self.id, self.desc, self.suffix)
|
||||||
|
|
||||||
|
|
||||||
def load_pcb_layers():
|
|
||||||
""" Load layer names from the PCB """
|
|
||||||
GS.pcb_layers = {}
|
|
||||||
with open(GS.pcb_file, "r") as pcb_file:
|
|
||||||
collect_layers = False
|
|
||||||
for line in pcb_file:
|
|
||||||
if collect_layers:
|
|
||||||
z = re.match(r'\s+\((\d+)\s+(\S+)', line)
|
|
||||||
if z:
|
|
||||||
res = z.groups()
|
|
||||||
# print(res[1]+'->'+res[0])
|
|
||||||
GS.pcb_layers[res[1]] = int(res[0])
|
|
||||||
else:
|
|
||||||
if re.search(r'^\s+\)$', line):
|
|
||||||
collect_layers = False
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
if re.search(r'\s+\(layers', line):
|
|
||||||
collect_layers = True
|
|
||||||
|
|
||||||
|
|
||||||
def get_layer_id_from_pcb(name):
|
|
||||||
if GS.pcb_layers is None:
|
|
||||||
load_pcb_layers()
|
|
||||||
return GS.pcb_layers.get(name)
|
|
||||||
|
|
||||||
|
|
||||||
def check_version(command, version):
|
def check_version(command, version):
|
||||||
cmd = [command, '--version']
|
cmd = [command, '--version']
|
||||||
logger.debug('Running: '+str(cmd))
|
logger.debug('Running: '+str(cmd))
|
||||||
|
|
@ -120,6 +145,8 @@ def load_board():
|
||||||
board = pcbnew.LoadBoard(GS.pcb_file)
|
board = pcbnew.LoadBoard(GS.pcb_file)
|
||||||
if BasePreFlight.get_option('check_zone_fills'):
|
if BasePreFlight.get_option('check_zone_fills'):
|
||||||
pcbnew.ZONE_FILLER(board).Fill(board.Zones())
|
pcbnew.ZONE_FILLER(board).Fill(board.Zones())
|
||||||
|
# Now we know the names of the layers for this board
|
||||||
|
Layer.set_pcb_layers(board)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
logger.error('Error loading PCB file. Currupted?')
|
logger.error('Error loading PCB file. Currupted?')
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
|
|
|
||||||
|
|
@ -99,11 +99,7 @@ class AnyLayer(BaseOutput):
|
||||||
for l in self._layers:
|
for l in self._layers:
|
||||||
suffix = l.suffix
|
suffix = l.suffix
|
||||||
desc = l.desc
|
desc = l.desc
|
||||||
id = l.id
|
id = l.get_layer_id_from_name(layer_cnt)
|
||||||
# for inner layers, we can now check if the layer exists
|
|
||||||
if l.is_inner:
|
|
||||||
if id < 1 or id >= layer_cnt - 1:
|
|
||||||
raise PlotError("Inner layer `{}` is not valid for this board".format(l))
|
|
||||||
# Set current layer
|
# Set current layer
|
||||||
plot_ctrl.SetLayer(id)
|
plot_ctrl.SetLayer(id)
|
||||||
# Skipping NPTH is controlled by whether or not this is
|
# Skipping NPTH is controlled by whether or not this is
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import os
|
import os
|
||||||
from subprocess import (call)
|
from subprocess import (call)
|
||||||
from .pre_base import BasePreFlight
|
from .pre_base import BasePreFlight
|
||||||
from .error import (KiPlotConfigurationError, PlotError)
|
from .error import (KiPlotConfigurationError)
|
||||||
from .kiplot import (check_script, GS)
|
from .kiplot import (check_script, GS)
|
||||||
from .misc import (CMD_PCBNEW_PRINT_LAYERS, URL_PCBNEW_PRINT_LAYERS, PDF_PCB_PRINT)
|
from .misc import (CMD_PCBNEW_PRINT_LAYERS, URL_PCBNEW_PRINT_LAYERS, PDF_PCB_PRINT)
|
||||||
from kiplot.macros import macros, document, output_class # noqa: F401
|
from kiplot.macros import macros, document, output_class # noqa: F401
|
||||||
|
|
@ -34,10 +34,7 @@ class PDF_Pcb_Print(BaseOutput): # noqa: F821
|
||||||
# Verify the inner layers
|
# Verify the inner layers
|
||||||
layer_cnt = board.GetCopperLayerCount()
|
layer_cnt = board.GetCopperLayerCount()
|
||||||
for l in self._layers:
|
for l in self._layers:
|
||||||
# for inner layers, we can now check if the layer exists
|
l.get_layer_id_from_name(layer_cnt)
|
||||||
if l.is_inner:
|
|
||||||
if l.id < 1 or l.id >= layer_cnt - 1:
|
|
||||||
raise PlotError("Inner layer `{}` is not valid for this board".format(l))
|
|
||||||
# Output file name
|
# Output file name
|
||||||
output = self.output_name
|
output = self.output_name
|
||||||
if not output:
|
if not output:
|
||||||
|
|
|
||||||
|
|
@ -206,7 +206,7 @@ def test_auto_pcb_and_cfg_2():
|
||||||
|
|
||||||
def test_list():
|
def test_list():
|
||||||
ctx = context.TestContext('List', '3Rs', 'pre_and_position', POS_DIR)
|
ctx = context.TestContext('List', '3Rs', 'pre_and_position', POS_DIR)
|
||||||
ctx.run(extra=['--list'], no_verbose=True, no_out_dir=True)
|
ctx.run(extra=['--list'], no_verbose=True, no_out_dir=True, no_board_file=True)
|
||||||
|
|
||||||
assert ctx.search_err('run_erc: True')
|
assert ctx.search_err('run_erc: True')
|
||||||
assert ctx.search_err('run_drc: True')
|
assert ctx.search_err('run_drc: True')
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ def test_drill_map_wrong_type():
|
||||||
|
|
||||||
def test_wrong_layer_1():
|
def test_wrong_layer_1():
|
||||||
ctx = context.TestContext('ErrorWrongLayer1', '3Rs', 'error_wrong_layer_1', None)
|
ctx = context.TestContext('ErrorWrongLayer1', '3Rs', 'error_wrong_layer_1', None)
|
||||||
ctx.run(EXIT_BAD_CONFIG)
|
ctx.run(PLOT_ERROR)
|
||||||
assert ctx.search_err("Unknown layer name: F.Bogus")
|
assert ctx.search_err("Unknown layer name: F.Bogus")
|
||||||
ctx.clean_up()
|
ctx.clean_up()
|
||||||
|
|
||||||
|
|
@ -113,7 +113,7 @@ def test_wrong_layer_2():
|
||||||
|
|
||||||
def test_wrong_layer_3():
|
def test_wrong_layer_3():
|
||||||
ctx = context.TestContext('ErrorWrongLayer3', '3Rs', 'error_wrong_layer_3', None)
|
ctx = context.TestContext('ErrorWrongLayer3', '3Rs', 'error_wrong_layer_3', None)
|
||||||
ctx.run(EXIT_BAD_CONFIG)
|
ctx.run(PLOT_ERROR)
|
||||||
assert ctx.search_err("Malformed inner layer name: Inner_1,")
|
assert ctx.search_err("Malformed inner layer name: Inner_1,")
|
||||||
ctx.clean_up()
|
ctx.clean_up()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue