diff --git a/CHANGELOG.md b/CHANGELOG.md index 6986ae31..34f9fe13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `populate` to create step-by-step assembly instructions With support for `pcbdraw` and `render_3d`. - `panelize` to create a PCB panel containing N copies of the PCB. + - `stencil_3d` to create 3D self-registering printable stencils. - generic filters: options to filter by PCB side - BoM: - Option to link to Mouser site. diff --git a/README.md b/README.md index a4379485..bc267cd6 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,8 @@ For example, it's common that you might want for each board rev: * PCB 3D model in STEP format * PCB 3D render in PNG format * Compare PCB/SCHs +* Panelization +* Stencil creation You want to do this in a one-touch way, and make sure everything you need to do so is securely saved in version control, not on the back of an old @@ -142,6 +144,9 @@ Notes: - Show KiAuto installation information for `info` (v2.0.0) - Print the page frame in GUI mode for `pcb_print` (v1.6.7) +[**KiKit**](https://github.com/yaqwsx/KiKit) [![Tool](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/llave-inglesa-22x22.png)](https://github.com/yaqwsx/KiKit) ![Auto-download](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/auto_download-22x22.png) +- Mandatory for: `panelize`, `stencil_3d` + [**LXML**](https://pypi.org/project/LXML/) [![Python module](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/Python-logo-notext-22x22.png)](https://pypi.org/project/LXML/) [![Debian](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/debian-openlogo-22x22.png)](https://packages.debian.org/bullseye/python3-lxml) ![Auto-download](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/auto_download-22x22.png) - Mandatory for: `pcb_print`, `pcbdraw` @@ -158,12 +163,12 @@ Notes: [**KiCad PCB/SCH Diff**](https://github.com/INTI-CMNB/KiDiff) v2.4.3 [![Tool](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/llave-inglesa-22x22.png)](https://github.com/INTI-CMNB/KiDiff) ![Auto-download](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/auto_download-22x22.png) - Mandatory for `diff` -[**KiKit**](https://github.com/yaqwsx/KiKit) [![Tool](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/llave-inglesa-22x22.png)](https://github.com/yaqwsx/KiKit) ![Auto-download](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/auto_download-22x22.png) -- Mandatory for `panelize` - [**mistune**](https://pypi.org/project/mistune/) [![Python module](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/Python-logo-notext-22x22.png)](https://pypi.org/project/mistune/) [![Debian](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/debian-openlogo-22x22.png)](https://packages.debian.org/bullseye/python3-mistune) - Mandatory for `populate` +[**OpenSCAD**](https://openscad.org/) [![Tool](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/llave-inglesa-22x22.png)](https://openscad.org/) [![Debian](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/debian-openlogo-22x22.png)](https://packages.debian.org/bullseye/openscad) +- Mandatory for `stencil_3d` + [**QRCodeGen**](https://pypi.org/project/QRCodeGen/) [![Python module](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/Python-logo-notext-22x22.png)](https://pypi.org/project/QRCodeGen/) [![PyPi dependency](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/PyPI_logo_simplified-22x22.png)](https://pypi.org/project/QRCodeGen/) [![Debian](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/debian-openlogo-22x22.png)](https://packages.debian.org/bullseye/python3-qrcodegen) ![Auto-download](https://raw.githubusercontent.com/INTI-CMNB/KiBot/master/docs/images/auto_download-22x22.png) - Mandatory for `qr_lib` @@ -3555,6 +3560,55 @@ Notes: Internally we use 10 for low priority, 90 for high priority and 50 for most outputs. - `run_by_default`: [boolean=true] When enabled this output will be created when no specific outputs are requested. +* 3D Printed Stencils + * Type: `stencil_3d` + * Description: Creates a 3D self-registering model of a stencil you can easily print on + SLA printer, you can use it to apply solder paste to your PCB. + These stencils are quick solution when you urgently need a stencil but probably + they don't last long and might come with imperfections. + It currently uses KiKit, so please read + [KiKit docs](https://github.com/yaqwsx/KiKit/blob/master/doc/stencil.md). + Note that we don't implement `--ignore` option, you should use a variant for this + * Valid keys: + - **`comment`**: [string=''] A comment for documentation purposes. + - **`dir`**: [string='./'] Output directory for the generated files. + If it starts with `+` the rest is concatenated to the default dir. + - **`name`**: [string=''] Used to identify this particular output definition. + - **`options`**: [dict] Options for the `Stencil_3D` output. + * Valid keys: + - **`output`**: [string='%f-%i%I%v.%x'] Filename for the output (%i='stencil_3d_top'|'stencil_3d_bottom'|'stencil_3d_edge', + %x='stl'|'scad'|'dxf'). Affected by global options. + - **`thickness`**: [number=0.15] Stencil thickness [mm]. Defines amount of paste dispensed. + - `cutout`: [string|list(string)] List of components to add a cutout based on the component courtyard. + This is useful when you have already pre-populated board and you want to populate more + components. + - `dnf_filter`: [string|list(string)='_none'] Name of the filter to mark components as not fitted. + A short-cut to use for simple cases where a variant is an overkill. + - *enlarge_holes*: Alias for enlarge_holes. + - `enlargeholes`: [number=0] Enlarge pad holes by x mm. + - *frame_clearance*: Alias for frameclearance. + - *frame_width*: Alias for framewidth. + - `frameclearance`: [number=0] Clearance for the stencil register [mm]. + - `framewidth`: [number=1] Register frame width. + - `include_scad`: [boolean=true] Include the generated OpenSCAD files. Note that this also includes the DXF files. + - *pcb_thickness*: Alias for pcbthickness. + - `pcbthickness`: [number=0] PCB thickness [mm]. If 0 we will ask KiCad. + - `pre_transform`: [string|list(string)='_none'] Name of the filter to transform fields before applying other filters. + A short-cut to use for simple cases where a variant is an overkill. + - `side`: [string='auto'] [top,bottom,auto,both] Which side of the PCB we want. Using `auto` will detect which + side contains solder paste. + - `variant`: [string=''] Board variant to apply. + - `category`: [string|list(string)=''] The category for this output. If not specified an internally defined category is used. + Categories looks like file system paths, i.e. PCB/fabrication/gerber. + - `disable_run_by_default`: [string|boolean] Use it to disable the `run_by_default` status of other output. + Useful when this output extends another and you don't want to generate the original. + Use the boolean true value to disable the output you are extending. + - `extends`: [string=''] Copy the `options` section from the indicated output. + - `output_id`: [string=''] Text to use for the %I expansion content. To differentiate variations of this output. + - `priority`: [number=50] [0,100] Priority for this output. High priority outputs are created first. + Internally we use 10 for low priority, 90 for high priority and 50 for most outputs. + - `run_by_default`: [boolean=true] When enabled this output will be created when no specific outputs are requested. + * STEP (ISO 10303-21 Clear Text Encoding of the Exchange Structure) * Type: `step` * Description: Exports the PCB as a 3D model. diff --git a/docs/README.in b/docs/README.in index 16623c57..e6b3f6ea 100644 --- a/docs/README.in +++ b/docs/README.in @@ -96,6 +96,8 @@ For example, it's common that you might want for each board rev: * PCB 3D model in STEP format * PCB 3D render in PNG format * Compare PCB/SCHs +* Panelization +* Stencil creation You want to do this in a one-touch way, and make sure everything you need to do so is securely saved in version control, not on the back of an old diff --git a/docs/samples/generic_plot.kibot.yaml b/docs/samples/generic_plot.kibot.yaml index d8b9961e..d1477638 100644 --- a/docs/samples/generic_plot.kibot.yaml +++ b/docs/samples/generic_plot.kibot.yaml @@ -2460,6 +2460,52 @@ outputs: title: '' # [string=''] Board variant to apply variant: '' + # 3D Printed Stencils: + # SLA printer, you can use it to apply solder paste to your PCB. + # These stencils are quick solution when you urgently need a stencil but probably + # they don't last long and might come with imperfections. + # It currently uses KiKit, so please read + # [KiKit docs](https://github.com/yaqwsx/KiKit/blob/master/doc/stencil.md). + # Note that we don't implement `--ignore` option, you should use a variant for this + - name: 'stencil_3d_example' + comment: 'Creates a 3D self-registering model of a stencil you can easily print on' + type: 'stencil_3d' + dir: 'Example/stencil_3d_dir' + options: + # [string|list(string)] List of components to add a cutout based on the component courtyard. + # This is useful when you have already pre-populated board and you want to populate more + # components + cutout: '' + # [string|list(string)='_none'] Name of the filter to mark components as not fitted. + # A short-cut to use for simple cases where a variant is an overkill + dnf_filter: '_none' + # `enlarge_holes` is an alias for `enlarge_holes` + # [number=0] Enlarge pad holes by x mm + enlargeholes: 0 + # `frame_clearance` is an alias for `frameclearance` + # `frame_width` is an alias for `framewidth` + # [number=0] Clearance for the stencil register [mm] + frameclearance: 0 + # [number=1] Register frame width + framewidth: 1 + # [boolean=true] Include the generated OpenSCAD files. Note that this also includes the DXF files + include_scad: true + # [string='%f-%i%I%v.%x'] Filename for the output (%i='stencil_3d_top'|'stencil_3d_bottom'|'stencil_3d_edge', + # %x='stl'|'scad'|'dxf'). Affected by global options + output: '%f-%i%I%v.%x' + # `pcb_thickness` is an alias for `pcbthickness` + # [number=0] PCB thickness [mm]. If 0 we will ask KiCad + pcbthickness: 0 + # [string|list(string)='_none'] Name of the filter to transform fields before applying other filters. + # A short-cut to use for simple cases where a variant is an overkill + pre_transform: '_none' + # [string='auto'] [top,bottom,auto,both] Which side of the PCB we want. Using `auto` will detect which + # side contains solder paste + side: 'auto' + # [number=0.15] Stencil thickness [mm]. Defines amount of paste dispensed + thickness: 0.15 + # [string=''] Board variant to apply + variant: '' # STEP (ISO 10303-21 Clear Text Encoding of the Exchange Structure): # This is the most common 3D format for exchange purposes. # This output is what you get from the 'File/Export/STEP' menu in pcbnew. diff --git a/kibot/dep_downloader.py b/kibot/dep_downloader.py index f75d240e..80479f9e 100644 --- a/kibot/dep_downloader.py +++ b/kibot/dep_downloader.py @@ -69,6 +69,10 @@ Dependencies: debian: python3-lxml arch: python-lxml downloader: python + - name: KiKit + github: yaqwsx/KiKit + pypi: KiKit + downloader: pytool """ import importlib import os @@ -559,6 +563,9 @@ def run_command(cmd, only_first_line=False, pre_ver_text=None, no_err_2=False): return None last_stderr = res_run.stderr.decode() res = res_run.stdout.decode().strip() + if len(res) == 0 and len(last_stderr) != 0: + # Ok, yes, OpenSCAD prints its version to stderr!!! + res = last_stderr if only_first_line: res = res.split('\n')[0] pre_vers = (cmd[0]+' version ', cmd[0]+' ', pre_ver_text) diff --git a/kibot/gs.py b/kibot/gs.py index 34426f00..df894f93 100644 --- a/kibot/gs.py +++ b/kibot/gs.py @@ -377,6 +377,19 @@ class GS(object): copy2(pro_name, pro_copy) return pro_copy + @staticmethod + def get_pcb_and_pro_names(name): + if GS.ki5: + return [name, name.replace('kicad_pcb', 'pro')] + return [name, name.replace('kicad_pcb', 'kicad_pro'), name.replace('kicad_pcb', 'kicad_prl')] + + @staticmethod + def remove_pcb_and_pro(name): + """ Used to remove temporal PCB and project files """ + for fn in GS.get_pcb_and_pro_names(name): + if os.path.isfile(fn): + os.remove(fn) + @staticmethod def load_board(): """ Will be repplaced by kiplot.py """ diff --git a/kibot/misc.py b/kibot/misc.py index f43a77b1..a67b9d59 100644 --- a/kibot/misc.py +++ b/kibot/misc.py @@ -242,6 +242,7 @@ W_PCBDRAW = '(W103) ' W_NOCRTYD = '(W104) ' W_PANELEMPTY = '(W105) ' W_ONWIN = '(W106) ' +W_AUTONONE = '(W106) ' # Somehow arbitrary, the colors are real, but can be different 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", diff --git a/kibot/out_base.py b/kibot/out_base.py index 05879aed..a34e2479 100644 --- a/kibot/out_base.py +++ b/kibot/out_base.py @@ -252,6 +252,11 @@ class VariantOptions(BaseOptions): return [] return [c.ref for c in self._comps if not c.fitted or not c.included] + # Here just to avoid pulling pcbnew for this + @staticmethod + def to_mm(val): + return ToMM(val) + @staticmethod def create_module_element(m): if GS.ki6: @@ -334,6 +339,22 @@ class VariantOptions(BaseOptions): for line in restore: m.Remove(line) + def detect_solder_paste(self, board): + """ Detects if the top and/or bottom layer has solder paste """ + fpaste = board.GetLayerID('F.Paste') + bpaste = board.GetLayerID('B.Paste') + top = bottom = False + for m in GS.get_modules_board(board): + for p in m.Pads(): + pad_layers = p.GetLayerSet() + if not top and fpaste in pad_layers.Seq(): + top = True + if not bottom and bpaste in pad_layers.Seq(): + bottom = True + if top and bottom: + return top, bottom + return top, bottom + def remove_paste_and_glue(self, board, comps_hash): """ Remove from solder paste layers the filtered components. """ if comps_hash is None or not (GS.global_remove_solder_paste_for_dnp or GS.global_remove_adhesive_for_dnp): diff --git a/kibot/out_panelize.py b/kibot/out_panelize.py index 7131bdab..3a7c7a03 100644 --- a/kibot/out_panelize.py +++ b/kibot/out_panelize.py @@ -5,10 +5,7 @@ # Project: KiBot (formerly KiPlot) """ Dependencies: - - name: KiKit - github: yaqwsx/KiKit - pypi: KiKit - downloader: pytool + - from: KiKit role: mandatory """ import collections @@ -708,10 +705,7 @@ class PanelizeOptions(VariantOptions): fname = self.save_tmp_board() self.unfilter_pcb_components(GS.board, do_3D=True) self.restore_title() - to_remove.append(fname) - to_remove.append(fname.replace('kicad_pcb', 'kicad_pro')) - to_remove.append(fname.replace('kicad_pcb', 'kicad_prl')) - to_remove.append(fname.replace('kicad_pcb', 'pro')) + to_remove.extend(GS.get_pcb_and_pro_names(fname)) logger.debug('- Modified PCB: '+fname) else: fname = GS.pcb_file diff --git a/kibot/out_stencil_3d.py b/kibot/out_stencil_3d.py new file mode 100644 index 00000000..38e935c1 --- /dev/null +++ b/kibot/out_stencil_3d.py @@ -0,0 +1,180 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2022 Salvador E. Tropea +# Copyright (c) 2022 Instituto Nacional de TecnologĂ­a Industrial +# License: GPL-3.0 +# Project: KiBot (formerly KiPlot) +""" +Dependencies: + - from: KiKit + role: mandatory + - name: OpenSCAD + url: https://openscad.org/ + url_down: https://openscad.org/downloads.html + command: openscad + debian: openscad + arch: openscad + role: mandatory +""" +import os +import shutil +import tempfile +from .error import PlotError +from .gs import GS +from .kiplot import run_command +from .out_base import VariantOptions +from .misc import W_AUTONONE +from .macros import macros, document, output_class # noqa: F401 +from . import log + +logger = log.get_logger() + + +class Stencil_3D_Options(VariantOptions): + def __init__(self): + with document: + self.output = GS.def_global_output + """ *Filename for the output (%i='stencil_3d_top'|'stencil_3d_bottom'|'stencil_3d_edge', + %x='stl'|'scad'|'dxf') """ + self.side = 'auto' + """ [top,bottom,auto,both] Which side of the PCB we want. Using `auto` will detect which + side contains solder paste """ + self.include_scad = True + """ Include the generated OpenSCAD files. Note that this also includes the DXF files """ + self.cutout = '' + """ [string|list(string)] List of components to add a cutout based on the component courtyard. + This is useful when you have already pre-populated board and you want to populate more + components """ + self.pcbthickness = 0 + """ PCB thickness [mm]. If 0 we will ask KiCad """ + self.pcb_thickness = None + """ {pcbthickness} """ + self.thickness = 0.15 + """ *Stencil thickness [mm]. Defines amount of paste dispensed """ + self.framewidth = 1 + """ Register frame width """ + self.frame_width = None + """ {framewidth} """ + self.frameclearance = 0 + """ Clearance for the stencil register [mm] """ + self.frame_clearance = None + """ {frameclearance} """ + self.enlargeholes = 0 + """ Enlarge pad holes by x mm """ + self.enlarge_holes = None + """ {enlarge_holes} """ + super().__init__() + + def config(self, parent): + super().config(parent) + self.cutout = ','.join(self.force_list(self.cutout)) + + def move_output(self, src_dir, src_file, id, ext, replacement=None, patch=False): + self._expand_id = id + self._expand_ext = ext + dst_name = self._parent.expand_filename(self._parent.output_dir, self.output) + src_name = os.path.join(src_dir, src_file) + if not os.path.isfile(src_name): + raise PlotError('Missing output file {}'.format(src_name)) + if patch: + # Adjust the names of the DXF files + with open(src_name, 'r') as f: + content = f.read() + for k, v in replacement.items(): + content = content.replace(k, v) + with open(dst_name, 'w') as f: + f.write(content) + else: + shutil.move(src_name, dst_name) + if replacement is not None: + replacement[src_name] = os.path.basename(dst_name) + + def get_targets(self, out_dir): + # TODO: auto side is tricky, needs variants applied + return [self._parent.expand_filename(out_dir, self.output)] + + def run(self, output): + cmd_kikit = self.ensure_tool('KiKit') + self.ensure_tool('OpenSCAD') + super().run(output) + # Apply variants and filters + filtered = self.filter_pcb_components(GS.board) + if self.side == 'auto': + detected_top, detected_bottom = self.detect_solder_paste(GS.board) + fname = self.save_tmp_board() if filtered else GS.pcb_file + if filtered: + self.unfilter_pcb_components(GS.board) + # Avoid running the tool if we will generate useless models + if self.side == 'auto' and not detected_top and not detected_bottom: + logger.warning(W_AUTONONE+'No solder paste detected, skipping 3D stencil generation') + return + # If no PCB thickness indicated ask KiCad + if not self.pcbthickness: + ds = GS.board.GetDesignSettings() + self.pcbthickness = self.to_mm(ds.GetBoardThickness()) + # Create the command line + cmd = [cmd_kikit, 'stencil', 'createprinted', + '--thickness', str(self.thickness), + '--framewidth', str(self.framewidth), + '--pcbthickness', str(self.pcbthickness)] + if self.cutout: + cmd.extend(['--coutout', self.cutout]) + if self.frameclearance: + cmd.extend(['--frameclearance', str(self.frameclearance)]) + if self.enlargeholes: + cmd.extend(['--enlargeholes', str(self.enlargeholes)]) + with tempfile.TemporaryDirectory() as tmp: + cmd.append(fname) + cmd.append(tmp) + try: + run_command(cmd) + finally: + # Remove temporal variant + if filtered: + GS.remove_pcb_and_pro(fname) + # Now copy the files we want + # - Which side? + do_top = do_bottom = False + if self.side == 'top': + do_top = True + elif self.side == 'bottom': + do_bottom = True + elif self.side == 'both': + do_top = True + do_bottom = True + else: # auto + do_top = detected_top + do_bottom = detected_bottom + prj_name = os.path.splitext(os.path.basename(fname))[0] + replacements = {} + # The edge is needed by any of the OpenSCAD files + if (do_top or do_bottom) and self.include_scad: + self.move_output(tmp, prj_name+'-EdgeCuts.dxf', 'stencil_3d_edge', 'dxf', replacements) + # Top side + if do_top: + self.move_output(tmp, 'topStencil.stl', 'stencil_3d_top', 'stl') + if self.include_scad: + self.move_output(tmp, prj_name+'-PasteTop.dxf', 'stencil_3d_top', 'dxf', replacements) + self.move_output(tmp, 'topStencil.scad', 'stencil_3d_top', 'scad', replacements, patch=True) + # Bottom side + if do_bottom: + self.move_output(tmp, 'bottomStencil.stl', 'stencil_3d_bottom', 'stl') + if self.include_scad: + self.move_output(tmp, prj_name+'-PasteBottom.dxf', 'stencil_3d_bottom', 'dxf', replacements) + self.move_output(tmp, 'bottomStencil.scad', 'stencil_3d_bottom', 'scad', replacements, patch=True) + + +@output_class +class Stencil_3D(BaseOutput): # noqa: F821 + """ 3D Printed Stencils + Creates a 3D self-registering model of a stencil you can easily print on + SLA printer, you can use it to apply solder paste to your PCB. + These stencils are quick solution when you urgently need a stencil but probably + they don't last long and might come with imperfections. + It currently uses KiKit, so please read + [KiKit docs](https://github.com/yaqwsx/KiKit/blob/master/doc/stencil.md). + Note that we don't implement `--ignore` option, you should use a variant for this """ + def __init__(self): + super().__init__() + with document: + self.options = Stencil_3D_Options + """ *[dict] Options for the `Stencil_3D` output """ diff --git a/src/kibot-check b/src/kibot-check index d2c28f05..cdc62e24 100755 --- a/src/kibot-check +++ b/src/kibot-check @@ -558,7 +558,7 @@ deps = '{\ "extra_arch": null,\ "extra_deb": null,\ "help_option": "--version",\ - "importance": 10000,\ + "importance": 20000,\ "in_debian": false,\ "is_kicad_plugin": false,\ "is_python": false,\ @@ -575,6 +575,13 @@ deps = '{\ "max_version": null,\ "output": "panelize",\ "version": null\ + },\ + {\ + "desc": null,\ + "mandatory": true,\ + "max_version": null,\ + "output": "stencil_3d",\ + "version": null\ }\ ],\ "tests": [],\ @@ -622,6 +629,39 @@ deps = '{\ "url": null,\ "url_down": null\ },\ + "OpenSCAD": {\ + "arch": "openscad",\ + "command": "openscad",\ + "comments": [],\ + "deb_package": "openscad",\ + "downloader": null,\ + "downloader_str": null,\ + "extra_arch": null,\ + "extra_deb": null,\ + "help_option": "--version",\ + "importance": 10000,\ + "in_debian": true,\ + "is_kicad_plugin": false,\ + "is_python": false,\ + "name": "OpenSCAD",\ + "no_cmd_line_version": false,\ + "no_cmd_line_version_old": false,\ + "output": "stencil_3d",\ + "plugin_dirs": null,\ + "pypi_name": "OpenSCAD",\ + "roles": [\ + {\ + "desc": null,\ + "mandatory": true,\ + "max_version": null,\ + "output": "stencil_3d",\ + "version": null\ + }\ + ],\ + "tests": [],\ + "url": "https://openscad.org/",\ + "url_down": "https://openscad.org/downloads.html"\ + },\ "Pandoc": {\ "arch": "pandoc",\ "command": "pandoc",\ @@ -1027,7 +1067,7 @@ def run_command(cmd, only_first_line=False, pre_ver_text=None, no_err_2=False): cmd[0] = cmd_full last_cmd = None try: - cmd_output = subprocess.check_output(cmd, stderr=subprocess.DEVNULL) + res_run = subprocess.run(cmd, check=True, capture_output=True) last_cmd = cmd[0] except FileNotFoundError as e: last_ok = False @@ -1039,7 +1079,9 @@ def run_command(cmd, only_first_line=False, pre_ver_text=None, no_err_2=False): print('Output from command: '+e.output.decode()) last_ok = False return UNKNOWN - res = cmd_output.decode().strip() + res = res_run.stdout.decode().strip() + if len(res) == 0: + res = res_run.stderr.decode().strip() if only_first_line: res = res.split('\n')[0] pre_vers = (cmd[0]+' version ', cmd[0]+' ', pre_ver_text) diff --git a/src/kibot-check.in b/src/kibot-check.in index 203948e7..c36bea15 100755 --- a/src/kibot-check.in +++ b/src/kibot-check.in @@ -80,7 +80,7 @@ def run_command(cmd, only_first_line=False, pre_ver_text=None, no_err_2=False): cmd[0] = cmd_full last_cmd = None try: - cmd_output = subprocess.check_output(cmd, stderr=subprocess.DEVNULL) + res_run = subprocess.run(cmd, check=True, capture_output=True) last_cmd = cmd[0] except FileNotFoundError as e: last_ok = False @@ -92,7 +92,9 @@ def run_command(cmd, only_first_line=False, pre_ver_text=None, no_err_2=False): print('Output from command: '+e.output.decode()) last_ok = False return UNKNOWN - res = cmd_output.decode().strip() + res = res_run.stdout.decode().strip() + if len(res) == 0: + res = res_run.stderr.decode().strip() if only_first_line: res = res.split('\n')[0] pre_vers = (cmd[0]+' version ', cmd[0]+' ', pre_ver_text) diff --git a/tests/yaml_samples/stencil_3D_1.kibot.yaml b/tests/yaml_samples/stencil_3D_1.kibot.yaml new file mode 100644 index 00000000..941fa3b9 --- /dev/null +++ b/tests/yaml_samples/stencil_3D_1.kibot.yaml @@ -0,0 +1,9 @@ +# Example KiBot config file for a basic 3D stencil +kibot: + version: 1 + +outputs: + - name: 'stencil' + comment: "Creates a 3D printable stencil" + type: stencil_3d + dir: stencil/3D