Simplified the KiAuto use

- Temporal video remove
- Other temporals
- Messages on execution error
This commit is contained in:
Salvador E. Tropea 2023-01-03 10:04:44 -03:00
parent cfee399110
commit f7b9092a2a
19 changed files with 176 additions and 246 deletions

View File

@ -1,5 +1,5 @@
__author__ = 'Salvador E. Tropea, John Beard' __author__ = 'Salvador E. Tropea, John Beard'
__copyright__ = 'Copyright 2018-2022, Salvador E. Tropea/INTI/John Beard' __copyright__ = 'Copyright 2018-2023, Salvador E. Tropea/INTI/John Beard'
__credits__ = ['Salvador E. Tropea', 'John Beard'] __credits__ = ['Salvador E. Tropea', 'John Beard']
__license__ = 'GPL v3+' __license__ = 'GPL v3+'
__email__ = 'stropea@inti.gob.ar' __email__ = 'stropea@inti.gob.ar'

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2020-2022 Salvador E. Tropea # Copyright (c) 2020-2023 Salvador E. Tropea
# Copyright (c) 2020-2022 Instituto Nacional de Tecnología Industrial # Copyright (c) 2020-2023 Instituto Nacional de Tecnología Industrial
# License: GPL-3.0 # License: GPL-3.0
# Project: KiBot (formerly KiPlot) # Project: KiBot (formerly KiPlot)
import os import os
@ -498,3 +498,31 @@ class GS(object):
if shape == pcbnew.S_CIRCLE: if shape == pcbnew.S_CIRCLE:
return g.GetArcStart() return g.GetArcStart()
return g.GetEnd() return g.GetEnd()
@staticmethod
def get_kiauto_video_name(cmd):
""" Compute the name for the video captured by KiAuto """
command = os.path.basename(cmd[0])[:-3]
subcommand = next(filter(lambda x: x[0] != '-' and (not x[0].isdigit() or x[1] == 'd'), cmd[1:]))
if command == 'pcbnew':
return command+'_'+subcommand+'_screencast.ogv'
if command == 'eeschema':
return subcommand+'_'+command+'_screencast.ogv'
return command+'_screencast.ogv'
@staticmethod
def add_extra_options(cmd):
is_gitlab_ci = 'GITLAB_CI' in os.environ
video_remove = (not GS.debug_enabled) and is_gitlab_ci
if GS.debug_enabled:
cmd.insert(1, '-'+'v'*GS.debug_level)
if GS.debug_enabled or is_gitlab_ci:
# Forcing record on GitLab CI/CD (black magic)
cmd.insert(1, '-r')
if GS.global_kiauto_time_out_scale:
cmd.insert(1, str(GS.global_kiauto_time_out_scale))
cmd.insert(1, '--time_out_scale')
if GS.global_kiauto_wait_start:
cmd.insert(1, str(GS.global_kiauto_wait_start))
cmd.insert(1, '--wait_start')
return cmd, video_remove

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2020-2022 Salvador E. Tropea # Copyright (c) 2020-2023 Salvador E. Tropea
# Copyright (c) 2020-2022 Instituto Nacional de Tecnología Industrial # Copyright (c) 2020-2023 Instituto Nacional de Tecnología Industrial
# Copyright (c) 2018 John Beard # Copyright (c) 2018 John Beard
# License: GPL-3.0 # License: GPL-3.0
# Project: KiBot (formerly KiPlot) # Project: KiBot (formerly KiPlot)
@ -171,7 +171,7 @@ def run_command(command, change_to=None, just_raise=False, use_x11=False):
return res.stdout.decode().rstrip() return res.stdout.decode().rstrip()
def exec_with_retry(cmd): def exec_with_retry(cmd, exit_with=None):
cmd_str = shlex.join(cmd) cmd_str = shlex.join(cmd)
logger.debug('Executing: '+cmd_str) logger.debug('Executing: '+cmd_str)
if GS.debug_level > 2: if GS.debug_level > 2:
@ -191,26 +191,12 @@ def exec_with_retry(cmd):
if 'Timed out' in err: if 'Timed out' in err:
logger.warning(W_TIMEOUT+'Time out detected, on slow machines or complex projects try:') logger.warning(W_TIMEOUT+'Time out detected, on slow machines or complex projects try:')
logger.warning(W_TIMEOUT+'`kiauto_time_out_scale` and/or `kiauto_wait_start` global options') logger.warning(W_TIMEOUT+'`kiauto_time_out_scale` and/or `kiauto_wait_start` global options')
if exit_with is not None and ret:
logger.error(cmd[0]+' returned %d', ret)
exit(exit_with)
return ret return ret
def add_extra_options(cmd):
is_gitlab_ci = 'GITLAB_CI' in os.environ
video_remove = (not GS.debug_enabled) and is_gitlab_ci
if GS.debug_enabled:
cmd.insert(1, '-'+'v'*GS.debug_level)
if GS.debug_enabled or is_gitlab_ci:
# Forcing record on GitLab CI/CD (black magic)
cmd.insert(1, '-r')
if GS.global_kiauto_time_out_scale:
cmd.insert(1, str(GS.global_kiauto_time_out_scale))
cmd.insert(1, '--time_out_scale')
if GS.global_kiauto_wait_start:
cmd.insert(1, str(GS.global_kiauto_wait_start))
cmd.insert(1, '--wait_start')
return cmd, video_remove
def load_board(pcb_file=None, forced=False): def load_board(pcb_file=None, forced=False):
if GS.board is not None and not forced: if GS.board is not None and not forced:
# Already loaded # Already loaded

View File

@ -1,13 +1,12 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2020-2022 Salvador E. Tropea # Copyright (c) 2020-2023 Salvador E. Tropea
# Copyright (c) 2020-2022 Instituto Nacional de Tecnología Industrial # Copyright (c) 2020-2023 Instituto Nacional de Tecnología Industrial
# License: GPL-3.0 # License: GPL-3.0
# Project: KiBot (formerly KiPlot) # Project: KiBot (formerly KiPlot)
import os import os
from shutil import rmtree
from .pre_base import BasePreFlight from .pre_base import BasePreFlight
from .gs import GS from .gs import GS
from .kiplot import exec_with_retry, add_extra_options from .kiplot import exec_with_retry
from .misc import PDF_PCB_PRINT from .misc import PDF_PCB_PRINT
from .out_base import VariantOptions from .out_base import VariantOptions
from .macros import macros, document, output_class # noqa: F401 from .macros import macros, document, output_class # noqa: F401
@ -56,14 +55,15 @@ class Any_PCB_PrintOptions(VariantOptions):
def filter_components(self): def filter_components(self):
if not self.will_filter_pcb_components() and self.title == '': if not self.will_filter_pcb_components() and self.title == '':
return GS.pcb_file, None return GS.pcb_file
self.filter_pcb_components() self.filter_pcb_components()
self.set_title(self.title) self.set_title(self.title)
# Save the PCB to a temporal dir # Save the PCB to a temporal dir
fname, pcb_dir = self.save_tmp_dir_board('pdf_pcb_print') fname, pcb_dir = self.save_tmp_dir_board('pdf_pcb_print')
self.restore_title() self.restore_title()
self.unfilter_pcb_components() self.unfilter_pcb_components()
return fname, pcb_dir self._files_to_remove.append(pcb_dir)
return fname
def get_targets(self, out_dir): def get_targets(self, out_dir):
return [self._parent.expand_filename(out_dir, self.output)] return [self._parent.expand_filename(out_dir, self.output)]
@ -88,26 +88,17 @@ class Any_PCB_PrintOptions(VariantOptions):
cmd.extend(['--color_theme', self.color_theme]) cmd.extend(['--color_theme', self.color_theme])
if svg: if svg:
cmd.append('--svg') cmd.append('--svg')
board_name, board_dir = self.filter_components() board_name = self.filter_components()
cmd.extend([board_name, os.path.dirname(output)]) cmd.extend([board_name, os.path.dirname(output)])
cmd, video_remove = add_extra_options(cmd) # Add the extra options before the layers (so the output dir is the last option)
cmd = self.add_extra_options(cmd)
# Add the layers # Add the layers
cmd.extend([la.layer for la in self._layers]) cmd.extend([la.layer for la in self._layers])
if GS.ki6 and self.force_edge_cuts and not self.separated: if GS.ki6 and self.force_edge_cuts and not self.separated:
cmd.append('Edge.Cuts') cmd.append('Edge.Cuts')
# Execute it # Execute it
ret = exec_with_retry(cmd) exec_with_retry(cmd, PDF_PCB_PRINT)
# Remove the temporal PCB self.remove_temporals()
if board_dir:
logger.debug('Removing temporal variant dir `{}`'.format(board_dir))
rmtree(board_dir)
if ret:
logger.error(command+' returned %d', ret)
exit(PDF_PCB_PRINT)
if video_remove:
video_name = os.path.join(self.expand_filename_pcb(GS.out_dir), 'pcbnew_export_screencast.ogv')
if os.path.isfile(video_name):
os.remove(video_name)
def set_layers(self, layers): def set_layers(self, layers):
layers = Layer.solve(layers) layers = Layer.solve(layers)

View File

@ -1,13 +1,13 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2020-2022 Salvador E. Tropea # Copyright (c) 2020-2023 Salvador E. Tropea
# Copyright (c) 2020-2022 Instituto Nacional de Tecnología Industrial # Copyright (c) 2020-2023 Instituto Nacional de Tecnología Industrial
# License: GPL-3.0 # License: GPL-3.0
# Project: KiBot (formerly KiPlot) # Project: KiBot (formerly KiPlot)
import os import os
from tempfile import mkdtemp from tempfile import mkdtemp
from shutil import rmtree, copy2 from shutil import copy2
from .gs import GS from .gs import GS
from .kiplot import add_extra_options, exec_with_retry from .kiplot import exec_with_retry
from .out_base import VariantOptions from .out_base import VariantOptions
from .kicad.config import KiConf from .kicad.config import KiConf
from .macros import macros, document, output_class # noqa: F401 from .macros import macros, document, output_class # noqa: F401
@ -50,17 +50,15 @@ class Any_SCH_PrintOptions(VariantOptions):
def run(self, name): def run(self, name):
super().run(name) super().run(name)
output_dir = os.path.dirname(name)
our_name = self._expand_ext+'_sch_print'
command = self.ensure_tool('KiAuto') command = self.ensure_tool('KiAuto')
if self._comps: if self._comps:
# Save it to a temporal dir # Save it to a temporal dir
sch_dir = mkdtemp(prefix='tmp-kibot-'+our_name+'-') sch_dir = mkdtemp(prefix='tmp-kibot-'+self._expand_ext+'_sch_print-')
copy_project(sch_dir) copy_project(sch_dir)
fname = GS.sch.save_variant(sch_dir) fname = GS.sch.save_variant(sch_dir)
sch_file = os.path.join(sch_dir, fname) sch_file = os.path.join(sch_dir, fname)
self._files_to_remove.append(sch_dir)
else: else:
sch_dir = None
sch_file = GS.sch_file sch_file = GS.sch_file
cmd = [command, 'export', '--file_format', self._expand_ext, '-o', name] cmd = [command, 'export', '--file_format', self._expand_ext, '-o', name]
if self.monochrome: if self.monochrome:
@ -69,17 +67,6 @@ class Any_SCH_PrintOptions(VariantOptions):
cmd.append('--no_frame') cmd.append('--no_frame')
if self.all_pages: if self.all_pages:
cmd.append('--all_pages') cmd.append('--all_pages')
cmd.extend([sch_file, output_dir]) cmd.extend([sch_file, os.path.dirname(name)])
cmd, video_remove = add_extra_options(cmd) exec_with_retry(self.add_extra_options(cmd), self._exit_error)
ret = exec_with_retry(cmd) self.remove_temporals()
if ret:
logger.error(command+' returned %d', ret)
exit(self._exit_error)
# Remove the temporal dir if needed
if sch_dir:
logger.debug('Removing temporal variant dir `{}`'.format(sch_dir))
rmtree(sch_dir)
if video_remove:
video_name = os.path.join(output_dir, 'export_eeschema_screencast.ogv')
if os.path.isfile(video_name):
os.remove(video_name)

View File

@ -1,13 +1,13 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2020-2022 Salvador E. Tropea # Copyright (c) 2020-2023 Salvador E. Tropea
# Copyright (c) 2020-2022 Instituto Nacional de Tecnología Industrial # Copyright (c) 2020-2023 Instituto Nacional de Tecnología Industrial
# License: GPL-3.0 # License: GPL-3.0
# Project: KiBot (formerly KiPlot) # Project: KiBot (formerly KiPlot)
from copy import deepcopy from copy import deepcopy
from glob import glob
import math import math
import os import os
import re import re
from shutil import rmtree
from tempfile import NamedTemporaryFile, mkdtemp from tempfile import NamedTemporaryFile, mkdtemp
from .gs import GS from .gs import GS
from .kiplot import load_sch, get_board_comps_data from .kiplot import load_sch, get_board_comps_data
@ -650,6 +650,7 @@ class VariantOptions(BaseOptions):
return return
with NamedTemporaryFile(mode='w', suffix='.wrl', delete=False) as f: with NamedTemporaryFile(mode='w', suffix='.wrl', delete=False) as f:
self._highlight_3D_file = f.name self._highlight_3D_file = f.name
self._files_to_remove.append(f.name)
logger.debug('Creating temporal highlight file '+f.name) logger.debug('Creating temporal highlight file '+f.name)
f.write(HIGHLIGHT_3D_WRL) f.write(HIGHLIGHT_3D_WRL)
@ -772,12 +773,6 @@ class VariantOptions(BaseOptions):
if self._sub_pcb: if self._sub_pcb:
self._sub_pcb.revert(self.comps_hash) self._sub_pcb.revert(self.comps_hash)
def remove_highlight_3D_file(self):
# Remove the highlight 3D file if it was created
if self._highlight_3D_file:
os.remove(self._highlight_3D_file)
self._highlight_3D_file = None
def set_title(self, title, sch=False): def set_title(self, title, sch=False):
self.old_title = None self.old_title = None
if title: if title:
@ -848,8 +843,7 @@ class VariantOptions(BaseOptions):
else: else:
m.SetValue(data) m.SetValue(data)
@staticmethod def save_tmp_board(self, dir=None):
def save_tmp_board(dir=None):
""" Save the PCB to a temporal file. """ Save the PCB to a temporal file.
Advantage: all relative paths inside the file remains valid Advantage: all relative paths inside the file remains valid
Disadvantage: the name of the file gets altered """ Disadvantage: the name of the file gets altered """
@ -860,9 +854,10 @@ class VariantOptions(BaseOptions):
logger.debug('Storing modified PCB to `{}`'.format(fname)) logger.debug('Storing modified PCB to `{}`'.format(fname))
GS.board.Save(fname) GS.board.Save(fname)
GS.copy_project(fname) GS.copy_project(fname)
self._files_to_remove.extend(GS.get_pcb_and_pro_names(fname))
return fname return fname
def save_tmp_board_if_variant(self, to_remove, new_title='', dir=None, do_3D=False): def save_tmp_board_if_variant(self, new_title='', dir=None, do_3D=False):
""" If we have a variant apply it and save the PCB to a file """ """ If we have a variant apply it and save the PCB to a file """
if not self.will_filter_pcb_components() and not new_title: if not self.will_filter_pcb_components() and not new_title:
return GS.pcb_file return GS.pcb_file
@ -872,7 +867,6 @@ class VariantOptions(BaseOptions):
fname = self.save_tmp_board() fname = self.save_tmp_board()
self.restore_title() self.restore_title()
self.unfilter_pcb_components(do_3D=do_3D) self.unfilter_pcb_components(do_3D=do_3D)
to_remove.extend(GS.get_pcb_and_pro_names(fname))
logger.debug('- Modified PCB: '+fname) logger.debug('- Modified PCB: '+fname)
return fname return fname
@ -889,13 +883,6 @@ class VariantOptions(BaseOptions):
KiConf.fix_page_layout(pro_name) KiConf.fix_page_layout(pro_name)
return fname, pcb_dir return fname, pcb_dir
def remove_tmp_board(self, board_name):
# Remove the temporal PCB
if board_name != GS.pcb_file:
# KiCad likes to create project files ...
for f in glob(board_name.replace('.kicad_pcb', '.*')):
os.remove(f)
def solve_kf_filters(self, components): def solve_kf_filters(self, components):
""" Solves references to KiBot filters in the list of components to show. """ Solves references to KiBot filters in the list of components to show.
They are not yet expanded, just solved to filter objects """ They are not yet expanded, just solved to filter objects """
@ -944,8 +931,27 @@ class VariantOptions(BaseOptions):
new_list += ext_list new_list += ext_list
return new_list return new_list
def remove_temporals(self):
logger.debug('Removing temporal files')
for f in self._files_to_remove:
if os.path.isfile(f):
logger.debug('- File `{}`'.format(f))
os.remove(f)
elif os.path.isdir(f):
logger.debug('- Dir `{}`'.format(f))
rmtree(f)
self._files_to_remove = []
self._highlight_3D_file = None
def add_extra_options(self, cmd, dir=None):
cmd, video_remove = GS.add_extra_options(cmd)
if video_remove:
self._files_to_remove.append(os.path.join(dir or cmd[-1], GS.get_kiauto_video_name(cmd)))
return cmd
def run(self, output_dir): def run(self, output_dir):
""" Makes the list of components available """ """ Makes the list of components available """
self._files_to_remove = []
if not self.dnf_filter and not self.variant and not self.pre_transform: if not self.dnf_filter and not self.variant and not self.pre_transform:
return return
load_sch() load_sch()

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2020-2022 Salvador E. Tropea # Copyright (c) 2020-2023 Salvador E. Tropea
# Copyright (c) 2020-2022 Instituto Nacional de Tecnología Industrial # Copyright (c) 2020-2023 Instituto Nacional de Tecnología Industrial
# License: GPL-3.0 # License: GPL-3.0
# Project: KiBot (formerly KiPlot) # Project: KiBot (formerly KiPlot)
from fnmatch import fnmatch from fnmatch import fnmatch
@ -82,6 +82,7 @@ class Base3DOptions(VariantOptions):
return None return None
if self._tmp_dir is None: if self._tmp_dir is None:
self._tmp_dir = tempfile.mkdtemp() self._tmp_dir = tempfile.mkdtemp()
self._files_to_remove.append(self._tmp_dir)
rel_dirs.append(self._tmp_dir) rel_dirs.append(self._tmp_dir)
logger.debug('Using `{}` as temporal dir for downloaded files'.format(self._tmp_dir)) logger.debug('Using `{}` as temporal dir for downloaded files'.format(self._tmp_dir))
dest = os.path.join(self._tmp_dir, fname) dest = os.path.join(self._tmp_dir, fname)
@ -210,6 +211,10 @@ class Base3DOptions(VariantOptions):
def get_targets(self, out_dir): def get_targets(self, out_dir):
return [self._parent.expand_filename(out_dir, self.output)] return [self._parent.expand_filename(out_dir, self.output)]
def remove_temporals(self):
super().remove_temporals()
self._tmp_dir = None
class Base3D(BaseOutput): class Base3D(BaseOutput):
def __init__(self): def __init__(self):

View File

@ -7,7 +7,7 @@ import fnmatch
import glob import glob
import os import os
import re import re
from shutil import copy2, rmtree from shutil import copy2
from sys import exit from sys import exit
from .error import KiPlotConfigurationError from .error import KiPlotConfigurationError
from .gs import GS from .gs import GS
@ -197,15 +197,12 @@ class Copy_FilesOptions(Base3DOptions):
dest = os.path.relpath(dest, d) dest = os.path.relpath(dest, d)
break break
else: else:
logger.error(fname)
logger.error(self.rel_dirs)
dest = os.path.basename(fname) dest = os.path.basename(fname)
if mode_3d_append: if mode_3d_append:
dest = os.path.join(f.dest[:-1], dest) dest = os.path.join(f.dest[:-1], dest)
else: else:
dest = os.path.relpath(dest, src_dir) dest = os.path.relpath(dest, src_dir)
files.append((fname_real, dest)) files.append((fname_real, dest))
return files return files
def get_targets(self, out_dir): def get_targets(self, out_dir):
@ -242,9 +239,7 @@ class Copy_FilesOptions(Base3DOptions):
copy2(src, dest) copy2(src, dest)
copied[dest] = src copied[dest] = src
# Remove the downloaded 3D models # Remove the downloaded 3D models
if self._tmp_dir: self.remove_temporals()
rmtree(self._tmp_dir)
self._tmp_dir = None
@output_class @output_class

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2022 Salvador E. Tropea # Copyright (c) 2022-2023 Salvador E. Tropea
# Copyright (c) 2022 Instituto Nacional de Tecnología Industrial # Copyright (c) 2022-2023 Instituto Nacional de Tecnología Industrial
# License: GPL-3.0 # License: GPL-3.0
# Project: KiBot (formerly KiPlot) # Project: KiBot (formerly KiPlot)
""" """
@ -13,7 +13,7 @@ import os
from .gs import GS from .gs import GS
from .out_base import VariantOptions from .out_base import VariantOptions
from .misc import FAILED_EXECUTE from .misc import FAILED_EXECUTE
from .kiplot import exec_with_retry, add_extra_options from .kiplot import exec_with_retry
from .macros import macros, document, output_class # noqa: F401 from .macros import macros, document, output_class # noqa: F401
from . import log from . import log
@ -46,9 +46,8 @@ class GenCADOptions(VariantOptions):
def run(self, name): def run(self, name):
command = self.ensure_tool('KiAuto') command = self.ensure_tool('KiAuto')
super().run(name) super().run(name)
to_remove = [] board_name = self.save_tmp_board_if_variant()
board_name = self.save_tmp_board_if_variant(to_remove) # Create the command line
# Output file name
cmd = [command, 'export_gencad', '--output_name', os.path.basename(name)] cmd = [command, 'export_gencad', '--output_name', os.path.basename(name)]
if self.flip_bottom_padstacks: if self.flip_bottom_padstacks:
cmd.append('--flip_bottom_padstacks') cmd.append('--flip_bottom_padstacks')
@ -61,17 +60,10 @@ class GenCADOptions(VariantOptions):
if self.save_origin: if self.save_origin:
cmd.append('--save_origin') cmd.append('--save_origin')
cmd.extend([board_name, os.path.dirname(name)]) cmd.extend([board_name, os.path.dirname(name)])
cmd, video_remove = add_extra_options(cmd) cmd = self.add_extra_options(cmd)
if video_remove:
to_remove.append(os.path.join(self.expand_filename_pcb(GS.out_dir), 'pcbnew_export_gencad_screencast.ogv'))
# Execute it # Execute it
ret = exec_with_retry(cmd) exec_with_retry(cmd, FAILED_EXECUTE)
if ret: self.remove_temporals()
logger.error(command+' returned %d', ret)
exit(FAILED_EXECUTE)
for f in to_remove:
if os.path.isfile(f):
os.remove(f)
@output_class @output_class

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2022 Salvador E. Tropea # Copyright (c) 2022-2023 Salvador E. Tropea
# Copyright (c) 2022 Instituto Nacional de Tecnología Industrial # Copyright (c) 2022-2023 Instituto Nacional de Tecnología Industrial
# License: GPL-3.0 # License: GPL-3.0
# Project: KiBot (formerly KiPlot) # Project: KiBot (formerly KiPlot)
""" """
@ -14,7 +14,7 @@ import os
from .gs import GS from .gs import GS
from .out_base import VariantOptions from .out_base import VariantOptions
from .misc import FAILED_EXECUTE from .misc import FAILED_EXECUTE
from .kiplot import exec_with_retry, add_extra_options from .kiplot import exec_with_retry
from .macros import macros, document, output_class # noqa: F401 from .macros import macros, document, output_class # noqa: F401
from . import log from . import log
@ -50,28 +50,18 @@ class NetlistOptions(VariantOptions):
def run(self, name): def run(self, name):
command = self.ensure_tool('KiAuto') command = self.ensure_tool('KiAuto')
super().run(name) super().run(name)
to_remove = []
if self.format == 'ipc': if self.format == 'ipc':
command = command.replace('eeschema_do', 'pcbnew_do') command = command.replace('eeschema_do', 'pcbnew_do')
subcommand = 'ipc_netlist' subcommand = 'ipc_netlist'
file = self.save_tmp_board_if_variant(to_remove) file = self.save_tmp_board_if_variant()
else: else:
subcommand = 'netlist' subcommand = 'netlist'
file = GS.sch_file file = GS.sch_file
output_dir = os.path.dirname(name) # Create the command line
# Output file name cmd = self.add_extra_options([command, subcommand, '--output_name', name, file, os.path.dirname(name)])
cmd = [command, subcommand, '--output_name', name, file, output_dir]
cmd, video_remove = add_extra_options(cmd)
if video_remove:
to_remove.append(os.path.join(self.expand_filename_pcb(GS.out_dir), command[:-3]+'_'+subcommand+'_screencast.ogv'))
# Execute it # Execute it
ret = exec_with_retry(cmd) exec_with_retry(cmd, FAILED_EXECUTE)
if ret: self.remove_temporals()
logger.error(command+' returned %d', ret)
exit(FAILED_EXECUTE)
for f in to_remove:
if os.path.isfile(f):
os.remove(f)
@output_class @output_class

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2022 Salvador E. Tropea # Copyright (c) 2022-2023 Salvador E. Tropea
# Copyright (c) 2022 Instituto Nacional de Tecnología Industrial # Copyright (c) 2022-2023 Instituto Nacional de Tecnología Industrial
# License: GPL-3.0 # License: GPL-3.0
# Project: KiBot (formerly KiPlot) # Project: KiBot (formerly KiPlot)
""" """
@ -692,9 +692,7 @@ class PanelizeOptions(VariantOptions):
if GS.ki5 and version >= (1, 1, 0): if GS.ki5 and version >= (1, 1, 0):
raise KiPlotConfigurationError("Installed KiKit doesn't support KiCad 5") raise KiPlotConfigurationError("Installed KiKit doesn't support KiCad 5")
super().run(output) super().run(output)
to_remove = [] fname = self.save_tmp_board_if_variant(new_title=self.title, do_3D=True)
fname = self.save_tmp_board_if_variant(to_remove, new_title=self.title, do_3D=True)
# Create the command # Create the command
cmd = [cmd_kikit, 'panelize'] # , '--dump', 'test.json' cmd = [cmd_kikit, 'panelize'] # , '--dump', 'test.json'
# Add all the configurations # Add all the configurations
@ -706,7 +704,7 @@ class PanelizeOptions(VariantOptions):
cmd.append(cfg) cmd.append(cfg)
else: else:
cfg_f = self.create_config(cfg) cfg_f = self.create_config(cfg)
to_remove.append(cfg_f) self._files_to_remove.append(cfg_f)
cmd.append(cfg_f) cmd.append(cfg_f)
# Add the PCB and output # Add the PCB and output
cmd.append(fname) cmd.append(fname)
@ -715,10 +713,7 @@ class PanelizeOptions(VariantOptions):
run_command(cmd) run_command(cmd)
self.create_preview_file(output) self.create_preview_file(output)
finally: finally:
# Remove temporals self.remove_temporals()
for f in to_remove:
if os.path.isfile(f):
os.remove(f)
def get_targets(self, out_dir): def get_targets(self, out_dir):
pcb_name = self._parent.expand_filename(out_dir, self.output) pcb_name = self._parent.expand_filename(out_dir, self.output)

View File

@ -46,7 +46,7 @@ from .kicad.config import KiConf
from .kicad.v5_sch import SchError from .kicad.v5_sch import SchError
from .kicad.pcb import PCB from .kicad.pcb import PCB
from .misc import PDF_PCB_PRINT, W_PDMASKFAIL, KICAD5_SVG_SCALE, W_MISSTOOL, PCBDRAW_ERR, W_PCBDRAW from .misc import PDF_PCB_PRINT, W_PDMASKFAIL, KICAD5_SVG_SCALE, W_MISSTOOL, PCBDRAW_ERR, W_PCBDRAW
from .kiplot import exec_with_retry, add_extra_options from .kiplot import exec_with_retry
from .create_pdf import create_pdf_from_pages from .create_pdf import create_pdf_from_pages
from .macros import macros, document, output_class # noqa: F401 from .macros import macros, document, output_class # noqa: F401
from .drill_marks import DRILL_MARKS_MAP, add_drill_marks from .drill_marks import DRILL_MARKS_MAP, add_drill_marks
@ -473,28 +473,23 @@ class PCB_PrintOptions(VariantOptions):
# Move all the drawings away # Move all the drawings away
# KiCad 5 always prints Edge.Cuts, so we make it empty # KiCad 5 always prints Edge.Cuts, so we make it empty
self.clear_layer(layer) self.clear_layer(layer)
# Start with a fresh list of files to remove
cur_files_to_remove = self._files_to_remove
self._files_to_remove = []
# Save the PCB # Save the PCB
pcb_name, pcb_dir = self.save_tmp_dir_board('pcb_print') pcb_name, pcb_dir = self.save_tmp_dir_board('pcb_print')
self._files_to_remove.append(pcb_dir)
# Restore the layer # Restore the layer
self.restore_layer() self.restore_layer()
# Output file name # Output file name
cmd = [command, 'export', '--output_name', output, '--monochrome', '--svg', '--pads', '0', cmd = [command, 'export', '--output_name', output, '--monochrome', '--svg', '--pads', '0',
pcb_name, dir_name, layer] pcb_name, dir_name, layer]
cmd, video_remove = add_extra_options(cmd)
# Execute it # Execute it
ret = exec_with_retry(cmd) exec_with_retry(self.add_extra_options(cmd, dir_name), PDF_PCB_PRINT)
# Remove the temporal PCB
logger.debug('Removing temporal PCB used for frame `{}`'.format(pcb_dir))
rmtree(pcb_dir)
if ret:
logger.error(command+' returned %d', ret)
exit(PDF_PCB_PRINT)
if video_remove:
video_name = os.path.join(self.expand_filename_pcb(GS.out_dir), 'pcbnew_export_screencast.ogv')
if os.path.isfile(video_name):
os.remove(video_name)
# Rotate the paper size if needed and remove the background (or it will be over the drawings) # Rotate the paper size if needed and remove the background (or it will be over the drawings)
patch_svg_file(output, remove_bkg=True, is_portrait=self.paper_portrait) patch_svg_file(output, remove_bkg=True, is_portrait=self.paper_portrait)
self.remove_temporals()
self._files_to_remove = cur_files_to_remove
def plot_pads(self, la, pc, p, filelist): def plot_pads(self, la, pc, p, filelist):
id = la._id id = la._id

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2021-2022 Salvador E. Tropea # Copyright (c) 2021-2023 Salvador E. Tropea
# Copyright (c) 2021-2022 Instituto Nacional de Tecnología Industrial # Copyright (c) 2021-2023 Instituto Nacional de Tecnología Industrial
# License: GPL-3.0 # License: GPL-3.0
# Project: KiBot (formerly KiPlot) # Project: KiBot (formerly KiPlot)
# KiCad 6/6.0.1 bug: https://gitlab.com/kicad/code/kicad/-/issues/9890 # KiCad 6/6.0.1 bug: https://gitlab.com/kicad/code/kicad/-/issues/9890
@ -14,12 +14,11 @@ Dependencies:
""" """
import os import os
import shlex import shlex
from shutil import rmtree
import subprocess import subprocess
from .misc import (RENDER_3D_ERR, PCB_MAT_COLORS, PCB_FINISH_COLORS, SOLDER_COLORS, SILK_COLORS, from .misc import (RENDER_3D_ERR, PCB_MAT_COLORS, PCB_FINISH_COLORS, SOLDER_COLORS, SILK_COLORS,
KICAD_VERSION_6_0_2, MISSING_TOOL) KICAD_VERSION_6_0_2, MISSING_TOOL)
from .gs import GS from .gs import GS
from .kiplot import exec_with_retry, add_extra_options, load_sch, get_board_comps_data from .kiplot import exec_with_retry, load_sch, get_board_comps_data
from .optionable import Optionable from .optionable import Optionable
from .out_base_3d import Base3DOptions, Base3D from .out_base_3d import Base3DOptions, Base3D
from .macros import macros, document, output_class # noqa: F401 from .macros import macros, document, output_class # noqa: F401
@ -301,22 +300,9 @@ class Render3DOptions(Base3DOptions):
board_name = self.filter_components(highlight=set(self.expand_kf_components(self.highlight))) board_name = self.filter_components(highlight=set(self.expand_kf_components(self.highlight)))
self.undo_show_components() self.undo_show_components()
cmd.extend([board_name, os.path.dirname(output)]) cmd.extend([board_name, os.path.dirname(output)])
cmd, video_remove = add_extra_options(cmd)
# Execute it # Execute it
ret = exec_with_retry(cmd) exec_with_retry(self.add_extra_options(cmd), RENDER_3D_ERR)
# Remove the temporal PCB self.remove_temporals()
self.remove_tmp_board(board_name)
self.remove_highlight_3D_file()
# Remove the downloaded 3D models
if self._tmp_dir:
rmtree(self._tmp_dir)
if ret:
logger.error(command+' returned %d', ret)
exit(RENDER_3D_ERR)
if video_remove:
video_name = os.path.join(self.expand_filename_pcb(GS.out_dir), 'pcbnew_3d_view_screencast.ogv')
if os.path.isfile(video_name):
os.remove(video_name)
if self.auto_crop: if self.auto_crop:
_run_command([convert_command, output, '-trim', '+repage', '-trim', '+repage', output]) _run_command([convert_command, output, '-trim', '+repage', '-trim', '+repage', output])
if self.transparent_background: if self.transparent_background:

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2020-2022 Salvador E. Tropea # Copyright (c) 2020-2023 Salvador E. Tropea
# Copyright (c) 2020-2022 Instituto Nacional de Tecnología Industrial # Copyright (c) 2020-2023 Instituto Nacional de Tecnología Industrial
# License: GPL-3.0 # License: GPL-3.0
# Project: KiBot (formerly KiPlot) # Project: KiBot (formerly KiPlot)
# KiCad 6 bug: https://gitlab.com/kicad/code/kicad/-/issues/10075 # KiCad 6 bug: https://gitlab.com/kicad/code/kicad/-/issues/10075
@ -13,14 +13,11 @@ Dependencies:
""" """
import os import os
import re import re
import shlex
from subprocess import check_output, STDOUT, CalledProcessError
from shutil import rmtree
from .error import KiPlotConfigurationError from .error import KiPlotConfigurationError
from .misc import KICAD2STEP_ERR from .misc import KICAD2STEP_ERR
from .gs import GS from .gs import GS
from .out_base_3d import Base3DOptions, Base3D from .out_base_3d import Base3DOptions, Base3D
from .kiplot import add_extra_options from .kiplot import exec_with_retry
from .macros import macros, document, output_class # noqa: F401 from .macros import macros, document, output_class # noqa: F401
from . import log from . import log
@ -87,30 +84,9 @@ class STEPOptions(Base3DOptions):
# The board # The board
board_name = self.filter_components() board_name = self.filter_components()
cmd.append(board_name) cmd.append(board_name)
cmd, video_remove = add_extra_options(cmd) # Execute it
# Execute and inform is successful exec_with_retry(self.add_extra_options(cmd, os.path.dirname(output)), KICAD2STEP_ERR)
logger.debug('Executing: '+shlex.join(cmd)) self.remove_temporals()
# Ensure KIPRJMOD is correct:
# KiCad sets KIPRJMOD each time we call BOARD.Save() but then Python `os.environ` becomes unsynchronized
# We don't even know the actual value and any call to Save could destroy it
os.environ['KIPRJMOD'] = os.path.dirname(board_name)
try:
cmd_output = check_output(cmd, stderr=STDOUT)
except CalledProcessError as e:
logger.error('Failed to create Step file, error %d', e.returncode)
if e.output:
logger.debug('Output from command: '+e.output.decode())
exit(KICAD2STEP_ERR)
finally:
self.remove_tmp_board(board_name)
# Remove the downloaded 3D models
if self._tmp_dir:
rmtree(self._tmp_dir)
if video_remove:
video_name = os.path.join(self.expand_filename_pcb(GS.out_dir), 'kicad2step_screencast.ogv')
if os.path.isfile(video_name):
os.remove(video_name)
logger.debug('Output from command:\n'+cmd_output.decode())
@output_class @output_class

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2022 Salvador E. Tropea # Copyright (c) 2022-2023 Salvador E. Tropea
# Copyright (c) 2022 Instituto Nacional de Tecnología Industrial # Copyright (c) 2022-2023 Instituto Nacional de Tecnología Industrial
# License: GPL-3.0 # License: GPL-3.0
# Project: KiBot (formerly KiPlot) # Project: KiBot (formerly KiPlot)
""" """
@ -13,7 +13,7 @@ import os
from .gs import GS from .gs import GS
from .out_base_3d import Base3DOptions, Base3D from .out_base_3d import Base3DOptions, Base3D
from .misc import FAILED_EXECUTE from .misc import FAILED_EXECUTE
from .kiplot import exec_with_retry, add_extra_options from .kiplot import exec_with_retry
from .macros import macros, document, output_class # noqa: F401 from .macros import macros, document, output_class # noqa: F401
from . import log from . import log
@ -75,19 +75,9 @@ class VRMLOptions(Base3DOptions):
units = self.ref_units units = self.ref_units
cmd.extend(['-x', str(x), '-y', str(x), '-u', units]) cmd.extend(['-x', str(x), '-y', str(x), '-u', units])
cmd.extend([board_name, os.path.dirname(name)]) cmd.extend([board_name, os.path.dirname(name)])
cmd, video_remove = add_extra_options(cmd)
# Execute it # Execute it
try: exec_with_retry(self.add_extra_options(cmd), FAILED_EXECUTE)
ret = exec_with_retry(cmd) self.remove_temporals()
finally:
self.remove_tmp_board(board_name)
if ret:
logger.error(command+' returned %d', ret)
exit(FAILED_EXECUTE)
if video_remove:
video_name = os.path.join(self.expand_filename_pcb(GS.out_dir), 'pcbnew_export_vrml_screencast.ogv')
if os.path.isfile(video_name):
os.remove(video_name)
@output_class @output_class

View File

@ -1,8 +1,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2020-2022 Salvador E. Tropea # Copyright (c) 2020-2023 Salvador E. Tropea
# Copyright (c) 2020-2022 Instituto Nacional de Tecnología Industrial # Copyright (c) 2020-2023 Instituto Nacional de Tecnología Industrial
# License: GPL-3.0 # License: GPL-3.0
# Project: KiBot (formerly KiPlot) # Project: KiBot (formerly KiPlot)
import os
from shutil import rmtree
from .gs import GS from .gs import GS
from .registrable import Registrable from .registrable import Registrable
from .optionable import Optionable from .optionable import Optionable
@ -28,6 +30,7 @@ class BasePreFlight(Registrable):
self._enabled = True self._enabled = True
self._expand_id = '' self._expand_id = ''
self._expand_ext = '' self._expand_ext = ''
self._files_to_remove = []
@staticmethod @staticmethod
def add_preflight(o_pre): def add_preflight(o_pre):
@ -162,3 +165,21 @@ class BasePreFlight(Registrable):
def check_tool(self, name): def check_tool(self, name):
""" Looks for a dependency """ """ Looks for a dependency """
return GS.check_tool_dep(self._name, name, fatal=False) return GS.check_tool_dep(self._name, name, fatal=False)
def add_extra_options(self, cmd, dir=None):
""" KiAuto extra options (debug, record, etc.) """
cmd, video_remove = GS.add_extra_options(cmd)
if video_remove:
self._files_to_remove.append(os.path.join(dir or cmd[-1], GS.get_kiauto_video_name(cmd)))
return cmd
def remove_temporals(self):
logger.debug('Removing temporal files')
for f in self._files_to_remove:
if os.path.isfile(f):
logger.debug('- File `{}`'.format(f))
os.remove(f)
elif os.path.isdir(f):
logger.debug('- Dir `{}`'.format(f))
rmtree(f)
self._files_to_remove = []

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2020-2022 Salvador E. Tropea # Copyright (c) 2020-2023 Salvador E. Tropea
# Copyright (c) 2020-2022 Instituto Nacional de Tecnología Industrial # Copyright (c) 2020-2023 Instituto Nacional de Tecnología Industrial
# License: GPL-3.0 # License: GPL-3.0
# Project: KiBot (formerly KiPlot) # Project: KiBot (formerly KiPlot)
""" """
@ -15,7 +15,7 @@ from .macros import macros, pre_class # noqa: F401
from .error import KiPlotConfigurationError from .error import KiPlotConfigurationError
from .gs import GS from .gs import GS
from .optionable import Optionable from .optionable import Optionable
from .kiplot import exec_with_retry, load_board, add_extra_options from .kiplot import exec_with_retry, load_board
from .misc import DRC_ERROR from .misc import DRC_ERROR
from .log import get_logger from .log import get_logger
@ -61,13 +61,10 @@ class Run_DRC(BasePreFlight): # noqa: F821
cmd.append('-i') cmd.append('-i')
cmd.extend([GS.pcb_file, self.expand_dirname(GS.out_dir)]) cmd.extend([GS.pcb_file, self.expand_dirname(GS.out_dir)])
# If we are in verbose mode enable debug in the child # If we are in verbose mode enable debug in the child
cmd, video_remove = add_extra_options(cmd) cmd = self.add_extra_options(cmd)
logger.info('- Running the DRC') logger.info('- Running the DRC')
ret = exec_with_retry(cmd) ret = exec_with_retry(cmd)
if video_remove: self.remove_temporals()
video_name = os.path.join(self.expand_dirname(GS.out_dir), 'pcbnew_run_drc_screencast.ogv')
if os.path.isfile(video_name):
os.remove(video_name)
if ret: if ret:
if ret > 127: if ret > 127:
ret = -(256-ret) ret = -(256-ret)

View File

@ -15,7 +15,7 @@ from sys import exit
from .macros import macros, pre_class # noqa: F401 from .macros import macros, pre_class # noqa: F401
from .gs import GS from .gs import GS
from .optionable import Optionable from .optionable import Optionable
from .kiplot import exec_with_retry, load_sch, add_extra_options from .kiplot import exec_with_retry, load_sch
from .error import KiPlotConfigurationError from .error import KiPlotConfigurationError
from .misc import ERC_ERROR from .misc import ERC_ERROR
from .log import get_logger from .log import get_logger
@ -60,13 +60,10 @@ class Run_ERC(BasePreFlight): # noqa: F821
cmd.extend(['-f', GS.filter_file]) cmd.extend(['-f', GS.filter_file])
cmd.extend([GS.sch_file, self.expand_dirname(GS.out_dir)]) cmd.extend([GS.sch_file, self.expand_dirname(GS.out_dir)])
# If we are in verbose mode enable debug in the child # If we are in verbose mode enable debug in the child
cmd, video_remove = add_extra_options(cmd) cmd = self.add_extra_options(cmd)
logger.info('- Running the ERC') logger.info('- Running the ERC')
ret = exec_with_retry(cmd) ret = exec_with_retry(cmd)
if video_remove: self.remove_temporals()
video_name = os.path.join(self.expand_dirname(GS.out_dir), 'run_erc_eeschema_screencast.ogv')
if os.path.isfile(video_name):
os.remove(video_name)
if ret: if ret:
if ret > 127: if ret > 127:
ret = -(256-ret) ret = -(256-ret)

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (c) 2020-2022 Salvador E. Tropea # Copyright (c) 2020-2023 Salvador E. Tropea
# Copyright (c) 2020-2022 Instituto Nacional de Tecnología Industrial # Copyright (c) 2020-2023 Instituto Nacional de Tecnología Industrial
# License: GPL-3.0 # License: GPL-3.0
# Project: KiBot (formerly KiPlot) # Project: KiBot (formerly KiPlot)
""" """
@ -16,7 +16,7 @@ import xml.etree.ElementTree as ET
from .macros import macros, document, pre_class # noqa: F401 from .macros import macros, document, pre_class # noqa: F401
from .error import KiPlotConfigurationError from .error import KiPlotConfigurationError
from .gs import GS from .gs import GS
from .kiplot import exec_with_retry, add_extra_options, load_board from .kiplot import exec_with_retry, load_board
from .misc import BOM_ERROR, NETLIST_DIFF, W_PARITY, MISSING_TOOL from .misc import BOM_ERROR, NETLIST_DIFF, W_PARITY, MISSING_TOOL
from .log import get_logger from .log import get_logger
from .optionable import Optionable from .optionable import Optionable
@ -170,21 +170,14 @@ class Update_XML(BasePreFlight): # noqa: F821
out_dir = self.expand_dirname(GS.out_dir) out_dir = self.expand_dirname(GS.out_dir)
cmd = [command, 'bom_xml', GS.sch_file, out_dir] cmd = [command, 'bom_xml', GS.sch_file, out_dir]
# If we are in verbose mode enable debug in the child # If we are in verbose mode enable debug in the child
cmd, video_remove = add_extra_options(cmd) cmd = self.add_extra_options(cmd)
# While creating the XML we run a BoM plug-in that creates a useless BoM # While creating the XML we run a BoM plug-in that creates a useless BoM
# We remove it, unless this is already there # We remove it, unless this is already there
side_effect_file = os.path.join(out_dir, GS.sch_basename+'.csv') side_effect_file = os.path.join(out_dir, GS.sch_basename+'.csv')
remove_side_effect_file = not os.path.isfile(side_effect_file) if not os.path.isfile(side_effect_file):
self._files_to_remove.append(side_effect_file)
logger.info('- Updating BoM in XML format') logger.info('- Updating BoM in XML format')
ret = exec_with_retry(cmd) exec_with_retry(cmd, BOM_ERROR)
if remove_side_effect_file and os.path.isfile(side_effect_file): self.remove_temporals()
os.remove(side_effect_file)
if ret:
logger.error('Failed to update the BoM, error %d', ret)
exit(BOM_ERROR)
if video_remove:
video_name = os.path.join(self.expand_dirname(GS.out_dir), 'bom_xml_eeschema_screencast.ogv')
if os.path.isfile(video_name):
os.remove(video_name)
if self._check_pcb_parity: if self._check_pcb_parity:
self.check_pcb_parity() self.check_pcb_parity()