Added text variable replace in the title block items
- There are some limitations inm the depth of vars + %X usage Closes #162
This commit is contained in:
parent
4b6885283a
commit
342fb40c59
|
|
@ -304,6 +304,18 @@ def detect_kicad():
|
||||||
logger.debug('KiCad config path {}'.format(GS.kicad_conf_path))
|
logger.debug('KiCad config path {}'.format(GS.kicad_conf_path))
|
||||||
|
|
||||||
|
|
||||||
|
def solve_project_file():
|
||||||
|
if GS.pcb_file:
|
||||||
|
pro_name = GS.pcb_no_ext+GS.pro_ext
|
||||||
|
if os.path.isfile(pro_name):
|
||||||
|
return pro_name
|
||||||
|
if GS.sch_file:
|
||||||
|
pro_name = GS.sch_no_ext+GS.pro_ext
|
||||||
|
if os.path.isfile(pro_name):
|
||||||
|
return pro_name
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
set_locale()
|
set_locale()
|
||||||
ver = 'KiBot '+__version__+' - '+__copyright__+' - License: '+__license__
|
ver = 'KiBot '+__version__+' - '+__copyright__+' - License: '+__license__
|
||||||
|
|
@ -357,6 +369,8 @@ def main():
|
||||||
GS.set_sch(solve_schematic(args.schematic, args.board_file, plot_config))
|
GS.set_sch(solve_schematic(args.schematic, args.board_file, plot_config))
|
||||||
# Determine the PCB file
|
# Determine the PCB file
|
||||||
GS.set_pcb(solve_board_file(GS.sch_file, args.board_file))
|
GS.set_pcb(solve_board_file(GS.sch_file, args.board_file))
|
||||||
|
# Determine the project file
|
||||||
|
GS.set_pro(solve_project_file())
|
||||||
|
|
||||||
# Read the config file
|
# Read the config file
|
||||||
cr = CfgYamlReader()
|
cr = CfgYamlReader()
|
||||||
|
|
|
||||||
81
kibot/gs.py
81
kibot/gs.py
|
|
@ -4,6 +4,8 @@
|
||||||
# License: GPL-3.0
|
# License: GPL-3.0
|
||||||
# Project: KiBot (formerly KiPlot)
|
# Project: KiBot (formerly KiPlot)
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
|
import json
|
||||||
try:
|
try:
|
||||||
import pcbnew
|
import pcbnew
|
||||||
except ImportError:
|
except ImportError:
|
||||||
|
|
@ -12,7 +14,7 @@ except ImportError:
|
||||||
pass
|
pass
|
||||||
from datetime import datetime, date
|
from datetime import datetime, date
|
||||||
from sys import exit
|
from sys import exit
|
||||||
from .misc import EXIT_BAD_ARGS, W_DATEFORMAT, KICAD_VERSION_5_99
|
from .misc import EXIT_BAD_ARGS, W_DATEFORMAT, KICAD_VERSION_5_99, W_UNKVAR
|
||||||
from .log import get_logger
|
from .log import get_logger
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
logger = get_logger(__name__)
|
||||||
|
|
@ -33,6 +35,14 @@ class GS(object):
|
||||||
sch_no_ext = None # /.../dir/file
|
sch_no_ext = None # /.../dir/file
|
||||||
sch_dir = None # /.../dir
|
sch_dir = None # /.../dir
|
||||||
sch_basename = None # file
|
sch_basename = None # file
|
||||||
|
# Project and useful parts
|
||||||
|
pro_file = None # /.../dir/file.kicad_pro (or .pro)
|
||||||
|
pro_no_ext = None # /.../dir/file
|
||||||
|
pro_dir = None # /.../dir
|
||||||
|
pro_basename = None # file
|
||||||
|
pro_ext = '.pro'
|
||||||
|
pro_variables = None # KiCad 6 text variables defined in the project
|
||||||
|
vars_regex = re.compile(r'\$\{([^\}]+)\}')
|
||||||
# Main output dir
|
# Main output dir
|
||||||
out_dir = None
|
out_dir = None
|
||||||
out_dir_in_cmd_line = False
|
out_dir_in_cmd_line = False
|
||||||
|
|
@ -48,7 +58,6 @@ class GS(object):
|
||||||
kicad_share_path = None
|
kicad_share_path = None
|
||||||
kicad_dir = 'kicad'
|
kicad_dir = 'kicad'
|
||||||
kicad_plugins_dirs = []
|
kicad_plugins_dirs = []
|
||||||
pro_ext = '.pro'
|
|
||||||
work_layer = 'Rescue'
|
work_layer = 'Rescue'
|
||||||
# KiCad version: major*1e6+minor*1e3+patch
|
# KiCad version: major*1e6+minor*1e3+patch
|
||||||
kicad_version_n = 0
|
kicad_version_n = 0
|
||||||
|
|
@ -127,16 +136,39 @@ class GS(object):
|
||||||
GS.pcb_no_ext = os.path.splitext(name)[0]
|
GS.pcb_no_ext = os.path.splitext(name)[0]
|
||||||
GS.pcb_dir = os.path.dirname(name)
|
GS.pcb_dir = os.path.dirname(name)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_pro(name):
|
||||||
|
if name:
|
||||||
|
name = os.path.abspath(name)
|
||||||
|
GS.pro_file = name
|
||||||
|
GS.pro_basename = os.path.splitext(os.path.basename(name))[0]
|
||||||
|
GS.pro_no_ext = os.path.splitext(name)[0]
|
||||||
|
GS.pro_dir = os.path.dirname(name)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def load_pro_variables():
|
||||||
|
if GS.pro_variables is not None:
|
||||||
|
return GS.pro_variables
|
||||||
|
if GS.pro_file is None or GS.pro_ext == '.pro':
|
||||||
|
return {}
|
||||||
|
# Get the text_variables
|
||||||
|
with open(GS.pro_file, 'rt') as f:
|
||||||
|
pro_text = f.read()
|
||||||
|
data = json.loads(pro_text)
|
||||||
|
GS.pro_variables = data.get('text_variables', {})
|
||||||
|
logger.debug("Current text variables: {}".format(GS.pro_variables))
|
||||||
|
return GS.pro_variables
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load_sch_title_block():
|
def load_sch_title_block():
|
||||||
if GS.sch_title is not None:
|
if GS.sch_title is not None:
|
||||||
return
|
return
|
||||||
assert GS.sch is not None
|
assert GS.sch is not None
|
||||||
GS.sch_title = GS.sch.title
|
GS.sch_title = GS.expand_text_variables(GS.sch.title)
|
||||||
GS.sch_date = GS.sch.date
|
GS.sch_date = GS.expand_text_variables(GS.sch.date)
|
||||||
GS.sch_rev = GS.sch.revision
|
GS.sch_rev = GS.expand_text_variables(GS.sch.revision)
|
||||||
GS.sch_comp = GS.sch.company
|
GS.sch_comp = GS.expand_text_variables(GS.sch.company)
|
||||||
GS.sch_com = GS.sch.comment
|
GS.sch_com = map(GS.expand_text_variables, GS.sch.comment)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def format_date(d, fname, what):
|
def format_date(d, fname, what):
|
||||||
|
|
@ -234,6 +266,29 @@ class GS(object):
|
||||||
def ki5():
|
def ki5():
|
||||||
return GS.kicad_version_n < KICAD_VERSION_5_99
|
return GS.kicad_version_n < KICAD_VERSION_5_99
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def expand_text_variables(text):
|
||||||
|
vars = GS.load_pro_variables()
|
||||||
|
new_text = ''
|
||||||
|
last = 0
|
||||||
|
text_l = len(text)
|
||||||
|
for match in GS.vars_regex.finditer(text):
|
||||||
|
vname = match.group(1)
|
||||||
|
value = vars.get(vname, None)
|
||||||
|
if value is None:
|
||||||
|
value = '${'+vname+'}'
|
||||||
|
logger.warning(W_UNKVAR+"Unknown text variable `{}`".format(vname))
|
||||||
|
if match.start():
|
||||||
|
new_text += text[last:match.start()]
|
||||||
|
new_text += value
|
||||||
|
last = match.end()
|
||||||
|
if last < text_l:
|
||||||
|
new_text += text[last:text_l]
|
||||||
|
if new_text != text:
|
||||||
|
if GS.debug_level > 3:
|
||||||
|
logger.debug('Replacing KiCad text variables: {} -> {}'.format(text, new_text))
|
||||||
|
return new_text
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def load_pcb_title_block():
|
def load_pcb_title_block():
|
||||||
if GS.pcb_title is not None:
|
if GS.pcb_title is not None:
|
||||||
|
|
@ -243,15 +298,15 @@ class GS(object):
|
||||||
GS.pcb_rev = ''
|
GS.pcb_rev = ''
|
||||||
GS.pcb_comp = ''
|
GS.pcb_comp = ''
|
||||||
# This is based on InterativeHtmlBom expansion
|
# This is based on InterativeHtmlBom expansion
|
||||||
title_block = GS.board.GetTitleBlock()
|
title_block = GS.expand_text_variables(GS.board.GetTitleBlock())
|
||||||
GS.pcb_date = GS.format_date(title_block.GetDate(), GS.pcb_file, 'PCB')
|
GS.pcb_date = GS.format_date(GS.expand_text_variables(title_block.GetDate()), GS.pcb_file, 'PCB')
|
||||||
GS.pcb_title = title_block.GetTitle()
|
GS.pcb_title = GS.expand_text_variables(title_block.GetTitle())
|
||||||
if not GS.pcb_title:
|
if not GS.pcb_title:
|
||||||
GS.pcb_title = GS.pcb_basename
|
GS.pcb_title = GS.pcb_basename
|
||||||
GS.pcb_rev = title_block.GetRevision()
|
GS.pcb_rev = GS.expand_text_variables(title_block.GetRevision())
|
||||||
GS.pcb_comp = title_block.GetCompany()
|
GS.pcb_comp = GS.expand_text_variables(title_block.GetCompany())
|
||||||
for num in range(9):
|
for num in range(9):
|
||||||
GS.pcb_com[num] = GS.get_pcb_comment(title_block, num)
|
GS.pcb_com[num] = GS.expand_text_variables(GS.get_pcb_comment(title_block, num))
|
||||||
logger.debug("PCB title: `{}`".format(GS.pcb_title))
|
logger.debug("PCB title: `{}`".format(GS.pcb_title))
|
||||||
logger.debug("PCB date: `{}`".format(GS.pcb_date))
|
logger.debug("PCB date: `{}`".format(GS.pcb_date))
|
||||||
logger.debug("PCB revision: `{}`".format(GS.pcb_rev))
|
logger.debug("PCB revision: `{}`".format(GS.pcb_rev))
|
||||||
|
|
|
||||||
|
|
@ -223,6 +223,7 @@ W_KICOSTFLD = '(W078) '
|
||||||
W_MIXVARIANT = '(W079) '
|
W_MIXVARIANT = '(W079) '
|
||||||
W_NOTPDF = '(W080) '
|
W_NOTPDF = '(W080) '
|
||||||
W_NOREF = '(W081) '
|
W_NOREF = '(W081) '
|
||||||
|
W_UNKVAR = '(W082) '
|
||||||
# Somehow arbitrary, the colors are real, but can be different
|
# Somehow arbitrary, the colors are real, but can be different
|
||||||
PCB_MAT_COLORS = {'fr1': "937042", 'fr2': "949d70", 'fr3': "adacb4", 'fr4': "332B16", 'fr5': "6cc290"}
|
PCB_MAT_COLORS = {'fr1': "937042", 'fr2': "949d70", 'fr3': "adacb4", 'fr4': "332B16", 'fr5': "6cc290"}
|
||||||
PCB_FINISH_COLORS = {'hal': "8b898c", 'hasl': "8b898c", 'imag': "8b898c", 'enig': "cfb96e", 'enepig': "cfb96e",
|
PCB_FINISH_COLORS = {'hal': "8b898c", 'hasl': "8b898c", 'imag': "8b898c", 'enig': "cfb96e", 'enepig': "cfb96e",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright (c) 2020-2021 Salvador E. Tropea
|
# Copyright (c) 2020-2022 Salvador E. Tropea
|
||||||
# Copyright (c) 2020-2021 Instituto Nacional de Tecnología Industrial
|
# Copyright (c) 2020-2022 Instituto Nacional de Tecnología Industrial
|
||||||
# License: GPL-3.0
|
# License: GPL-3.0
|
||||||
# Project: KiBot (formerly KiPlot)
|
# Project: KiBot (formerly KiPlot)
|
||||||
""" Base class for output options """
|
""" Base class for output options """
|
||||||
|
|
@ -269,6 +269,8 @@ class Optionable(object):
|
||||||
if GS.debug_level > 3:
|
if GS.debug_level > 3:
|
||||||
logger.debug('Expanding `{}` in {} context for {} parent: {}'.
|
logger.debug('Expanding `{}` in {} context for {} parent: {}'.
|
||||||
format(name, 'SCH' if is_sch else 'PCB', self, parent))
|
format(name, 'SCH' if is_sch else 'PCB', self, parent))
|
||||||
|
# Replace KiCad 6 variables first
|
||||||
|
name = GS.expand_text_variables(name)
|
||||||
# Determine if we need to expand SCH and/or PCB related data
|
# Determine if we need to expand SCH and/or PCB related data
|
||||||
has_dep_exp = any(map(lambda x: x in name, ['%c', '%d', '%F', '%f', '%p', '%r', '%C1', '%C2', '%C3', '%C4']))
|
has_dep_exp = any(map(lambda x: x in name, ['%c', '%d', '%F', '%f', '%p', '%r', '%C1', '%C2', '%C3', '%C4']))
|
||||||
do_sch = is_sch and has_dep_exp
|
do_sch = is_sch and has_dep_exp
|
||||||
|
|
@ -304,6 +306,8 @@ class Optionable(object):
|
||||||
name = name.replace('%r', _cl(GS.sch_rev))
|
name = name.replace('%r', _cl(GS.sch_rev))
|
||||||
for num, val in enumerate(GS.sch_com):
|
for num, val in enumerate(GS.sch_com):
|
||||||
name = name.replace('%C'+str(num+1), _cl(val))
|
name = name.replace('%C'+str(num+1), _cl(val))
|
||||||
|
# Also replace KiCad 6 variables after it
|
||||||
|
name = GS.expand_text_variables(name)
|
||||||
if make_safe:
|
if make_safe:
|
||||||
# sanitize the name to avoid characters illegal in file systems
|
# sanitize the name to avoid characters illegal in file systems
|
||||||
name = name.replace('\\', '/')
|
name = name.replace('\\', '/')
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ class Any_PCB_PrintOptions(VariantOptions):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _copy_project(fname):
|
def _copy_project(fname):
|
||||||
pro_name = GS.pcb_file.replace('.kicad_pcb', GS.pro_ext)
|
pro_name = GS.pro_file
|
||||||
if not os.path.isfile(pro_name):
|
if not os.path.isfile(pro_name):
|
||||||
return None
|
return None
|
||||||
pro_copy = fname.replace('.kicad_pcb', GS.pro_ext)
|
pro_copy = fname.replace('.kicad_pcb', GS.pro_ext)
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ logger = log.get_logger()
|
||||||
def copy_project(sch_dir):
|
def copy_project(sch_dir):
|
||||||
""" Copy the project file to the temporal dir """
|
""" Copy the project file to the temporal dir """
|
||||||
ext = GS.pro_ext
|
ext = GS.pro_ext
|
||||||
source = GS.sch_no_ext+ext
|
source = GS.pro_file
|
||||||
prj_file = os.path.join(sch_dir, GS.sch_basename+ext)
|
prj_file = os.path.join(sch_dir, GS.sch_basename+ext)
|
||||||
if os.path.isfile(source):
|
if os.path.isfile(source):
|
||||||
copy2(source, prj_file)
|
copy2(source, prj_file)
|
||||||
|
|
|
||||||
|
|
@ -86,8 +86,8 @@ class Set_Text_Variables(BasePreFlight): # noqa: F821
|
||||||
return
|
return
|
||||||
if GS.ki5():
|
if GS.ki5():
|
||||||
raise KiPlotConfigurationError("The `set_text_variables` preflight is for KiCad 6 or newer")
|
raise KiPlotConfigurationError("The `set_text_variables` preflight is for KiCad 6 or newer")
|
||||||
pro_name = GS.pcb_file.replace('.kicad_pcb', GS.pro_ext)
|
pro_name = GS.pro_file
|
||||||
if not os.path.isfile(pro_name):
|
if not pro_name or not os.path.isfile(pro_name):
|
||||||
raise KiPlotConfigurationError("Trying to define KiCad 6 variables but the project is missing ({})".
|
raise KiPlotConfigurationError("Trying to define KiCad 6 variables but the project is missing ({})".
|
||||||
format(pro_name))
|
format(pro_name))
|
||||||
# Get the current definitions
|
# Get the current definitions
|
||||||
|
|
@ -95,10 +95,13 @@ class Set_Text_Variables(BasePreFlight): # noqa: F821
|
||||||
pro_text = f.read()
|
pro_text = f.read()
|
||||||
data = json.loads(pro_text)
|
data = json.loads(pro_text)
|
||||||
text_variables = data.get('text_variables', {})
|
text_variables = data.get('text_variables', {})
|
||||||
|
GS.pro_variables = text_variables
|
||||||
logger.debug("- Current variables: {}".format(text_variables))
|
logger.debug("- Current variables: {}".format(text_variables))
|
||||||
# Define the requested variables
|
# Define the requested variables
|
||||||
os.environ['KIBOT_PCB_NAME'] = GS.pcb_file
|
if GS.pcb_file:
|
||||||
os.environ['KIBOT_SCH_NAME'] = GS.sch_file
|
os.environ['KIBOT_PCB_NAME'] = GS.pcb_file
|
||||||
|
if GS.sch_file:
|
||||||
|
os.environ['KIBOT_SCH_NAME'] = GS.sch_file
|
||||||
for r in o:
|
for r in o:
|
||||||
text = r.text
|
text = r.text
|
||||||
if not text:
|
if not text:
|
||||||
|
|
@ -112,10 +115,17 @@ class Set_Text_Variables(BasePreFlight): # noqa: F821
|
||||||
continue
|
continue
|
||||||
text = result.stdout.strip()
|
text = result.stdout.strip()
|
||||||
text = r.before + text + r.after
|
text = r.before + text + r.after
|
||||||
if r.expand_kibot_patterns:
|
|
||||||
text = Optionable.expand_filename_both(self, text, make_safe=False)
|
|
||||||
logger.debug(' - ' + r.name + ' -> ' + text)
|
logger.debug(' - ' + r.name + ' -> ' + text)
|
||||||
text_variables[r.name] = text
|
text_variables[r.name] = text
|
||||||
|
logger.debug("- Expanding %X patterns in variables")
|
||||||
|
# Now that we have the variables defined expand the %X patterns (they could use variables)
|
||||||
|
for r in o:
|
||||||
|
if r.expand_kibot_patterns:
|
||||||
|
text = text_variables[r.name]
|
||||||
|
new_text = Optionable.expand_filename_both(self, text, make_safe=False)
|
||||||
|
if text != new_text:
|
||||||
|
logger.debug(' - ' + r.name + ' -> ' + new_text)
|
||||||
|
text_variables[r.name] = new_text
|
||||||
logger.debug("- New list of variables: {}".format(text_variables))
|
logger.debug("- New list of variables: {}".format(text_variables))
|
||||||
# Store the modified project
|
# Store the modified project
|
||||||
data['text_variables'] = text_variables
|
data['text_variables'] = text_variables
|
||||||
|
|
|
||||||
|
|
@ -238,20 +238,21 @@ def test_pcb_replace_1(test_dir):
|
||||||
|
|
||||||
def test_set_text_variables_1(test_dir):
|
def test_set_text_variables_1(test_dir):
|
||||||
""" KiCad 6 variables """
|
""" KiCad 6 variables """
|
||||||
prj = 'light_control'
|
prj = 'test_vars'
|
||||||
ctx = context.TestContext(test_dir, 'test_set_text_variables_1', prj, 'set_text_variables_1', '')
|
ctx = context.TestContextSCH(test_dir, 'test_set_text_variables_1', prj, 'set_text_variables_1', '')
|
||||||
if context.ki5():
|
if context.ki5():
|
||||||
ctx.run(EXIT_BAD_CONFIG)
|
ctx.run(EXIT_BAD_CONFIG)
|
||||||
else:
|
else:
|
||||||
ctx.run()
|
ctx.run(extra_debug=True)
|
||||||
file = os.path.join(ctx.get_board_dir(), ctx.board_name+context.PRO_EXT)
|
file = os.path.join(ctx.get_board_dir(), ctx.board_name+context.PRO_EXT)
|
||||||
file_back = file + '-bak'
|
file_back = file + '-bak'
|
||||||
assert os.path.isfile(file_back), file_back
|
assert os.path.isfile(file_back), file_back
|
||||||
assert os.path.getsize(file_back) > 0
|
assert os.path.getsize(file_back) > 0
|
||||||
try:
|
try:
|
||||||
logging.debug(file)
|
logging.debug(file)
|
||||||
cmd = ['/bin/bash', '-c', "git log -1 --format='%h' " + ctx.board_file]
|
cmd = ['/bin/bash', '-c', "git log -1 --format='%h' " + ctx.sch_file]
|
||||||
text = "Git_hash:'"+run(cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True).stdout.strip()+"' ({})".format(prj)
|
hash = run(cmd, stdout=PIPE, stderr=PIPE, universal_newlines=True).stdout.strip()
|
||||||
|
text = "Git_hash:'{}' ({})".format(hash, prj)
|
||||||
with open(file, 'rt') as f:
|
with open(file, 'rt') as f:
|
||||||
c = f.read()
|
c = f.read()
|
||||||
data = json.loads(c)
|
data = json.loads(c)
|
||||||
|
|
@ -260,4 +261,5 @@ def test_set_text_variables_1(test_dir):
|
||||||
assert data['text_variables']['Comment4'] == text
|
assert data['text_variables']['Comment4'] == text
|
||||||
finally:
|
finally:
|
||||||
os.rename(file_back, file)
|
os.rename(file_back, file)
|
||||||
|
ctx.expect_out_file(prj+'-bom_'+hash+'.csv')
|
||||||
ctx.clean_up(keep_project=True)
|
ctx.clean_up(keep_project=True)
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,19 @@ kibot:
|
||||||
preflight:
|
preflight:
|
||||||
set_text_variables:
|
set_text_variables:
|
||||||
- variable: "Comment4"
|
- variable: "Comment4"
|
||||||
command: git log -1 --format="%h" $KIBOT_PCB_NAME
|
command: git log -1 --format="%h" $KIBOT_SCH_NAME
|
||||||
before: "Git_hash:'"
|
before: "Git_hash:'"
|
||||||
after: "' (%f)"
|
after: "' (%f)"
|
||||||
|
- variable: "git_hash"
|
||||||
|
command: git log -1 --format="%h" $KIBOT_SCH_NAME
|
||||||
|
|
||||||
|
outputs:
|
||||||
|
- name: 'bom_internal'
|
||||||
|
comment: "Bill of Materials in CSV format"
|
||||||
|
type: bom
|
||||||
|
options:
|
||||||
|
csv:
|
||||||
|
hide_pcb_info: true
|
||||||
|
hide_stats_info: true
|
||||||
|
output: '%f-%i_%r.%x'
|
||||||
|
columns: [References, Value]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue