diff --git a/CHANGELOG.md b/CHANGELOG.md index c21124bd..d16ac457 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added +- `kibot-check` tool to check the installation - New outputs: - KiCad netlist generation - IPC-D-356 netlist generation (#197) diff --git a/README.md b/README.md index e3f2710a..fe767ed9 100644 --- a/README.md +++ b/README.md @@ -93,14 +93,15 @@ You can also run KiBot using docker images in a CI/CD environment like GitHub or Notes: - When installing from the Debian repo you don't need to worry about dependencies, just pay attention to *recommended* and *suggested* packages. - When installing using `pip` the dependencies marked as **PyPi dependency** will be automatically installed. +- The `kibot-check` tool can help you to know which dependencies are missing. -[**distutils**](https://pypi.org/project/distutils/) (python module) [Debian](https://packages.debian.org/bullseye/python3-distutils) +[**Distutils**](https://pypi.org/project/Distutils/) (python module) [Debian](https://packages.debian.org/bullseye/python3-distutils) - Mandatory [**PyYAML**](https://pypi.org/project/PyYAML/) (python module) (PyPi dependency) [Debian](https://packages.debian.org/bullseye/python3-yaml) - Mandatory -[**requests**](https://pypi.org/project/requests/) (python module) (PyPi dependency) [Debian](https://packages.debian.org/bullseye/python3-requests) +[**Requests**](https://pypi.org/project/Requests/) (python module) (PyPi dependency) [Debian](https://packages.debian.org/bullseye/python3-requests) - Mandatory [**KiCad Automation tools**](https://github.com/INTI-CMNB/KiAuto) v1.6.11 (tool) (PyPi dependency) @@ -110,24 +111,35 @@ Notes: - Mandatory for `kicost` - Optional to find components costs and specs for `bom` -[**Interactive HTML BoM**](https://github.com/INTI-CMNB/InteractiveHtmlBom) v2.4.1.3 (tool) +[**Interactive HTML BoM**](https://github.com/INTI-CMNB/InteractiveHtmlBom) v2.4.1.4 (tool) - Mandatory for `ibom` [**KiBoM**](https://github.com/INTI-CMNB/KiBoM) v1.8.0 (tool) - Mandatory for `kibom` -[**lxml**](https://pypi.org/project/lxml/) (python module) [Debian](https://packages.debian.org/bullseye/python3-lxml) +[**LXML**](https://pypi.org/project/LXML/) (python module) [Debian](https://packages.debian.org/bullseye/python3-lxml) - Mandatory for `pcb_print` [**PcbDraw**](https://github.com/INTI-CMNB/pcbdraw) v0.9.0 (tool) - Mandatory for `pcbdraw` -[**qrcodegen**](https://pypi.org/project/qrcodegen/) (python module) (PyPi dependency) [Debian](https://packages.debian.org/bullseye/python3-qrcodegen) +[**QRCodeGen**](https://pypi.org/project/QRCodeGen/) (python module) (PyPi dependency) [Debian](https://packages.debian.org/bullseye/python3-qrcodegen) - Mandatory for `qr_lib` -[**colorama**](https://pypi.org/project/colorama/) (python module) (PyPi dependency) [Debian](https://packages.debian.org/bullseye/python3-colorama) +[**Colorama**](https://pypi.org/project/Colorama/) (python module) (PyPi dependency) [Debian](https://packages.debian.org/bullseye/python3-colorama) - Optional to get color messages in a portable way for general use +[**Git**](https://git-scm.com/) (tool) [Debian](https://packages.debian.org/bullseye/git) +- Optional to: + - Find commit hash and/or date for `pcb_replace` + - Find commit hash and/or date for `sch_replace` + - Find commit hash and/or date for `set_text_variables` + +[**ImageMagick**](https://imagemagick.org/) (tool) [Debian](https://packages.debian.org/bullseye/imagemagick) +- Optional to: + - Create monochrome prints for `pcb_print` + - Create JPG images for `pcbdraw` + [**RSVG tools**](https://cran.r-project.org/web/packages/rsvg/index.html) (tool) [Debian](https://packages.debian.org/bullseye/librsvg2-bin) - Optional to: - Create PDF, PNG, EPS and PS formats for `pcb_print` @@ -136,16 +148,13 @@ Notes: [**Ghostscript**](https://www.ghostscript.com/) (tool) [Debian](https://packages.debian.org/bullseye/ghostscript) - Optional to create PS files for `pcb_print` -[**ImageMagick**](https://imagemagick.org/) (tool) [Debian](https://packages.debian.org/bullseye/imagemagick) -- Optional to create JPG images for `pcbdraw` - [**Pandoc**](https://pandoc.org/) (tool) [Debian](https://packages.debian.org/bullseye/pandoc) - Optional to create PDF/ODF/DOCX files for `report` [**RAR**](https://www.rarlab.com/) (tool) [Debian](https://packages.debian.org/bullseye/rar) - Optional to compress in RAR format for `compress` -[**xlsxwriter**](https://pypi.org/project/xlsxwriter/) (python module) (PyPi dependency) [Debian](https://packages.debian.org/bullseye/python3-xlsxwriter) +[**XLSXWriter**](https://pypi.org/project/XLSXWriter/) (python module) (PyPi dependency) [Debian](https://packages.debian.org/bullseye/python3-xlsxwriter) - Optional to create XLSX files for `bom` @@ -3026,7 +3035,7 @@ Usage: kibot [-v...] [--start PATH] [-d OUT_DIR] [--dry] [-t, --type TYPE]... --quick-start kibot [-v...] --help-filters - kibot [-v...] [--markdown] --help-dependencies + kibot [-v...] [--markdown|--json] --help-dependencies kibot [-v...] --help-global-options kibot [-v...] --help-list-outputs kibot [-v...] --help-output=HELP_OUTPUT diff --git a/docs/Makefile b/docs/Makefile index 7b9d938e..4cf7f4de 100755 --- a/docs/Makefile +++ b/docs/Makefile @@ -2,7 +2,7 @@ all: ../README.md samples/generic_plot.kibot.yaml ../kibot/report_templates/report_full_svg.txt ../kibot/config_templates/bom/MacroFab_XYRS.kibot.yaml \ ../kibot/config_templates/gerber/Elecrow.kibot.yaml ../kibot/config_templates/gerber/FusionPCB.kibot.yaml \ - ../kibot/config_templates/gerber/JLCPCB.kibot.yaml ../kibot/config_templates/gerber/PCBWay.kibot.yaml + ../kibot/config_templates/gerber/JLCPCB.kibot.yaml ../kibot/config_templates/gerber/PCBWay.kibot.yaml ../src/kibot-check ../kibot/config_templates/gerber/%.kibot.yaml: samples/%.kibot.yaml cp $< $@ @@ -13,6 +13,9 @@ all: ../README.md samples/generic_plot.kibot.yaml ../kibot/report_templates/rep ../README.md: README.in replace_tags.pl ../kibot/out_*.py ../kibot/pre_*.py ../kibot/fil_*.py ../kibot/__main__.py ../kibot/config_reader.py cat README.in | perl replace_tags.pl > ../README.md +../src/kibot-check: ../src/kibot-check.in replace_tags.pl ../kibot/out_*.py ../kibot/pre_*.py ../kibot/registrable.py ../kibot/misc.py ../kibot/config_reader.py + cat ../src/kibot-check.in | perl replace_tags.pl > ../src/kibot-check + samples/generic_plot.kibot.yaml: ../kibot/out_*.py ../kibot/pre_*.py ../kibot/config_reader.py rm -f example_template.kibot.yaml ../src/kibot -v --example diff --git a/docs/README.in b/docs/README.in index 6229dc21..13c076f3 100644 --- a/docs/README.in +++ b/docs/README.in @@ -93,6 +93,7 @@ You can also run KiBot using docker images in a CI/CD environment like GitHub or Notes: - When installing from the Debian repo you don't need to worry about dependencies, just pay attention to *recommended* and *suggested* packages. - When installing using `pip` the dependencies marked as **PyPi dependency** will be automatically installed. +- The `kibot-check` tool can help you to know which dependencies are missing. @dependencies@ diff --git a/docs/replace_tags.pl b/docs/replace_tags.pl index be0ac300..990d0c5d 100644 --- a/docs/replace_tags.pl +++ b/docs/replace_tags.pl @@ -6,6 +6,8 @@ $preflight=`../src/kibot --help-preflights`; $filters=`../src/kibot --help-filters`; $global_options=`../src/kibot --help-global-options`; $dependencies=`../src/kibot --help-dependencies --markdown`; +$json_dep=`../src/kibot --help-dependencies --json`; +$json_dep=~s/\n/\\\n/g; while (<>) { @@ -15,6 +17,7 @@ while (<>) $_ =~ s/\@filters\@/$filters/; $_ =~ s/\@global_options\@/$global_options/; $_ =~ s/\@dependencies\@/$dependencies/; + $_ =~ s/\@json_dep\@/$json_dep/; print $_; } diff --git a/kibot/__main__.py b/kibot/__main__.py index 4f0554de..98a6e2d4 100644 --- a/kibot/__main__.py +++ b/kibot/__main__.py @@ -15,7 +15,7 @@ Usage: kibot [-v...] [--start PATH] [-d OUT_DIR] [--dry] [-t, --type TYPE]... --quick-start kibot [-v...] --help-filters - kibot [-v...] [--markdown] --help-dependencies + kibot [-v...] [--markdown|--json] --help-dependencies kibot [-v...] --help-global-options kibot [-v...] --help-list-outputs kibot [-v...] --help-output=HELP_OUTPUT @@ -88,9 +88,9 @@ if os.environ.get('KIAUS_USE_NIGHTLY'): # pragma: no cover (nightly) else: os.environ['PYTHONPATH'] = pcbnew_path nightly = True -from .gs import (GS) -from .misc import (EXIT_BAD_ARGS, W_VARCFG, NO_PCBNEW_MODULE, W_NOKIVER, hide_stderr) -from .pre_base import (BasePreFlight) +from .gs import GS +from .misc import EXIT_BAD_ARGS, W_VARCFG, NO_PCBNEW_MODULE, W_NOKIVER, hide_stderr, TRY_INSTALL_CHECK +from .pre_base import BasePreFlight from .config_reader import (CfgYamlReader, print_outputs_help, print_output_help, print_preflights_help, create_example, print_filters_help, print_global_options_help, print_dependencies) from .kiplot import (generate_outputs, load_actions, config_output, generate_makefile, generate_examples, solve_schematic, @@ -157,6 +157,7 @@ def detect_kicad(): logger.error("Failed to import pcbnew Python module." " Is KiCad installed?" " Do you need to add it to PYTHONPATH?") + logger.error(TRY_INSTALL_CHECK) sys.exit(NO_PCBNEW_MODULE) try: GS.kicad_version = pcbnew.GetBuildVersion() @@ -267,7 +268,7 @@ def main(): print_global_options_help() sys.exit(0) if args.help_dependencies: - print_dependencies(args.markdown) + print_dependencies(args.markdown, args.json) sys.exit(0) if args.example: check_board_file(args.board_file) diff --git a/kibot/bom/xlsx_writer.py b/kibot/bom/xlsx_writer.py index 75243d9e..31fb4241 100644 --- a/kibot/bom/xlsx_writer.py +++ b/kibot/bom/xlsx_writer.py @@ -18,7 +18,7 @@ from base64 import b64decode from .columnlist import ColumnList from .kibot_logo import KIBOT_LOGO from .. import log -from ..misc import W_NOKICOST, W_UNKDIST, KICOST_ERROR, W_BADFIELD +from ..misc import W_NOKICOST, W_UNKDIST, KICOST_ERROR, W_BADFIELD, TRY_INSTALL_CHECK from ..error import trace_dump from ..gs import GS from .. import __version__ @@ -51,6 +51,7 @@ except ModuleNotFoundError: except ImportError: logger.error("Installed KiCost is older than the version we support.") logger.error("Try installing the last release or the current GIT code.") + logger.error(TRY_INSTALL_CHECK) KICOST_SUPPORT = False ProgressConsole = object diff --git a/kibot/config_reader.py b/kibot/config_reader.py index ef81a01b..d6e462f6 100644 --- a/kibot/config_reader.py +++ b/kibot/config_reader.py @@ -10,12 +10,13 @@ Class to read KiBot config files """ import os +import json from sys import (exit, maxsize) from collections import OrderedDict from .error import (KiPlotConfigurationError, config_error) from .misc import (NO_YAML_MODULE, EXIT_BAD_ARGS, EXAMPLE_CFG, WONT_OVERWRITE, W_NOOUTPUTS, W_UNKOUT, W_NOFILTERS, - W_NOVARIANTS, W_NOGLOBALS) + W_NOVARIANTS, W_NOGLOBALS, TRY_INSTALL_CHECK) from .gs import GS from .registrable import RegOutput, RegVariant, RegFilter, RegDependency from .pre_base import BasePreFlight @@ -35,6 +36,7 @@ try: except ImportError: log.init() logger.error('No yaml module for Python, install python3-yaml') + logger.error(TRY_INSTALL_CHECK) exit(NO_YAML_MODULE) @@ -694,7 +696,13 @@ def global2human(name): return '`'+name+'`' if name != 'global' else 'general use' -def print_dependencies(markdown=True): +class MyEncoder(json.JSONEncoder): + """ Simple JSON encoder for objects """ + def default(self, o): + return o.__dict__ + + +def print_dependencies(markdown=True, jsn=False): # Compute the importance of each dependency for dep in RegDependency.get_registered().values(): importance = 0 @@ -705,6 +713,10 @@ def print_dependencies(markdown=True): else: importance += LOCAL_OPTIONAL if local else GLOBAL_OPTIONAL dep.importance = importance + # The JSON output is just a dump + if jsn: + print(json.dumps(RegDependency.get_registered(), cls=MyEncoder, indent=4, sort_keys=True)) + return # Now print them sorted by importance (and by name as a second criteria) for name, dep in sorted(sorted(RegDependency.get_registered().items(), key=lambda x: x[0].lower()), # noqa C414 key=lambda x: x[1].importance, reverse=True): diff --git a/kibot/kiplot.py b/kibot/kiplot.py index bb8d21cb..6364b90e 100644 --- a/kibot/kiplot.py +++ b/kibot/kiplot.py @@ -23,7 +23,7 @@ from collections import OrderedDict from .gs import GS from .registrable import RegOutput from .misc import (PLOT_ERROR, MISSING_TOOL, CMD_EESCHEMA_DO, URL_EESCHEMA_DO, CORRUPTED_PCB, - EXIT_BAD_ARGS, CORRUPTED_SCH, EXIT_BAD_CONFIG, WRONG_INSTALL, UI_SMD, UI_VIRTUAL, + EXIT_BAD_ARGS, CORRUPTED_SCH, EXIT_BAD_CONFIG, WRONG_INSTALL, UI_SMD, UI_VIRTUAL, TRY_INSTALL_CHECK, MOD_SMD, MOD_THROUGH_HOLE, MOD_VIRTUAL, W_PCBNOSCH, W_NONEEDSKIP, W_WRONGCHAR, name2make, W_TIMEOUT, W_KIAUTO, W_VARSCH, NO_SCH_FILE, NO_PCB_FILE, W_VARPCB, NO_YAML_MODULE, WRONG_ARGUMENTS) from .error import PlotError, KiPlotConfigurationError, config_error, trace_dump @@ -44,6 +44,7 @@ try: except ImportError: log.init() logger.error('No yaml module for Python, install python3-yaml') + logger.error(TRY_INSTALL_CHECK) exit(NO_YAML_MODULE) @@ -122,6 +123,7 @@ def check_script(cmd, url, version=None): if which(cmd) is None: logger.error('No `'+cmd+'` command found.\n' 'Please install it, visit: '+url) + logger.error(TRY_INSTALL_CHECK) exit(MISSING_TOOL) if version is not None: check_version(cmd, version) diff --git a/kibot/misc.py b/kibot/misc.py index 1a75e2e2..9bc2d15d 100644 --- a/kibot/misc.py +++ b/kibot/misc.py @@ -95,6 +95,7 @@ PANDOC = 'pandoc' KICAD_VERSION_5_99 = 5099000 KICAD_VERSION_6_0_0 = 6000000 KICAD_VERSION_6_0_2 = 6000002 +TRY_INSTALL_CHECK = 'Try running the installation checker: kibot-check' # Internal filter names IFILT_MECHANICAL = '_mechanical' @@ -311,7 +312,8 @@ class ToolDependencyRole(object): class ToolDependency(object): """ Class used to define tools needed for an output """ def __init__(self, output, name, url=None, url_down=None, is_python=False, deb=None, in_debian=True, extra_deb=None, - roles=None, is_kicad_plugin=False, command=None, pypi_name=None): + roles=None, plugin_dirs=None, command=None, pypi_name=None, module_name=None, no_cmd_line_version=False, + help_option=None, no_cmd_line_version_old=False): # The associated output self.output = output # Name of the tool @@ -325,6 +327,8 @@ class ToolDependency(object): else: self.deb_package = deb self.is_python = is_python + if is_python: + self.module_name = module_name if module_name is not None else name.lower() # If this tool has an official Debian package self.in_debian = in_debian # Name at PyPi, can be fake for things that aren't at PyPi @@ -336,9 +340,13 @@ class ToolDependency(object): self.url = url self.url_down = url_down # Can be installed as a KiCad plug-in? - self.is_kicad_plugin = is_kicad_plugin + self.is_kicad_plugin = plugin_dirs is not None + self.plugin_dirs = plugin_dirs # Command we run - self.command = command + self.command = command if command is not None else name.lower() + self.no_cmd_line_version = no_cmd_line_version + self.no_cmd_line_version_old = no_cmd_line_version_old # An old version doesn't have version + self.help_option = help_option if help_option is not None else '--version' # Roles if roles is None: roles = [ToolDependencyRole()] @@ -352,4 +360,9 @@ class ToolDependency(object): def kiauto_dependency(output, version=None): role = None if version is None else ToolDependencyRole(version=version) return ToolDependency(output, 'KiCad Automation tools', URL_EESCHEMA_DO, url_down=URL_EESCHEMA_DO+'/releases', - in_debian=False, pypi_name='kiauto', roles=role) + in_debian=False, pypi_name='kiauto', command='pcbnew_do', roles=role) + + +def git_dependency(output): + return ToolDependency(output, 'Git', 'https://git-scm.com/', + roles=ToolDependencyRole(desc='Find commit hash and/or date')) diff --git a/kibot/out_compress.py b/kibot/out_compress.py index 98a01674..04436264 100644 --- a/kibot/out_compress.py +++ b/kibot/out_compress.py @@ -14,7 +14,8 @@ from tarfile import open as tar_open from collections import OrderedDict from .gs import GS from .kiplot import config_output, get_output_dir, run_output -from .misc import MISSING_TOOL, WRONG_INSTALL, W_EMPTYZIP, WRONG_ARGUMENTS, INTERNAL_ERROR, ToolDependency, ToolDependencyRole +from .misc import (MISSING_TOOL, WRONG_INSTALL, W_EMPTYZIP, WRONG_ARGUMENTS, INTERNAL_ERROR, ToolDependency, + ToolDependencyRole, TRY_INSTALL_CHECK) from .optionable import Optionable, BaseOptions from .registrable import RegOutput, RegDependency from .macros import macros, document, output_class # noqa: F401 @@ -22,7 +23,7 @@ from . import log logger = log.get_logger() RegDependency.register(ToolDependency('compress', 'RAR', 'https://www.rarlab.com/', - url_down='https://www.rarlab.com/download.htm', + url_down='https://www.rarlab.com/download.htm', help_option='-?', roles=ToolDependencyRole(desc='Compress in RAR format'))) @@ -107,6 +108,7 @@ class CompressOptions(BaseOptions): check_output(cmd, stderr=STDOUT) except FileNotFoundError: logger.error('Missing `rar` command, install it') + logger.error(TRY_INSTALL_CHECK) exit(MISSING_TOOL) except CalledProcessError as e: logger.error('Failed to invoke rar command, error {}'.format(e.returncode)) diff --git a/kibot/out_ibom.py b/kibot/out_ibom.py index dded5c5e..0dc29b0a 100644 --- a/kibot/out_ibom.py +++ b/kibot/out_ibom.py @@ -16,8 +16,9 @@ from . import log logger = log.get_logger() WARNING_MIX = "Avoid using it in conjunction with IBoM native filtering options" RegDependency.register(ToolDependency('ibom', 'Interactive HTML BoM', URL_IBOM, url_down=URL_IBOM+'/releases', - is_kicad_plugin=True, command=CMD_IBOM, in_debian=False, - roles=ToolDependencyRole(version=(2, 4, 1, 3)))) + command=CMD_IBOM, in_debian=False, no_cmd_line_version_old=True, + plugin_dirs=['InteractiveHtmlBom', 'InteractiveHtmlBom/InteractiveHtmlBom'], + roles=ToolDependencyRole(version=(2, 4, 1, 4)))) def check_tool(): diff --git a/kibot/out_pcb_print.py b/kibot/out_pcb_print.py index e67f50f2..bcae93d2 100644 --- a/kibot/out_pcb_print.py +++ b/kibot/out_pcb_print.py @@ -23,7 +23,7 @@ from .kicad.config import KiConf from .kicad.v5_sch import SchError from .kicad.pcb import PCB from .misc import (CMD_PCBNEW_PRINT_LAYERS, URL_PCBNEW_PRINT_LAYERS, PDF_PCB_PRINT, MISSING_TOOL, W_PDMASKFAIL, - KICAD5_SVG_SCALE, W_MISSTOOL, ToolDependency, ToolDependencyRole) + KICAD5_SVG_SCALE, W_MISSTOOL, ToolDependency, ToolDependencyRole, TRY_INSTALL_CHECK) from .kiplot import check_script, exec_with_retry, add_extra_options from .registrable import RegDependency from .create_pdf import create_pdf_from_pages @@ -44,11 +44,14 @@ DRAWING_LAYERS = ['Dwgs.User', 'Cmts.User', 'Eco1.User', 'Eco2.User'] EXTRA_LAYERS = ['F.Fab', 'B.Fab', 'F.CrtYd', 'B.CrtYd'] RegDependency.register(ToolDependency('pcb_print', 'RSVG tools', 'https://cran.r-project.org/web/packages/rsvg/index.html', deb='librsvg2-bin', + command=SVG2PDF, roles=ToolDependencyRole(desc='Create PDF, PNG, EPS and PS formats'))) RegDependency.register(ToolDependency('pcb_print', 'Ghostscript', 'https://www.ghostscript.com/', url_down='https://github.com/ArtifexSoftware/ghostpdl-downloads/releases', roles=ToolDependencyRole(desc='Create PS files'))) -RegDependency.register(ToolDependency('pcb_print', 'lxml', is_python=True)) +RegDependency.register(ToolDependency('pcb_print', 'ImageMagick', 'https://imagemagick.org/', command='convert', + roles=ToolDependencyRole(desc='Create monochrome prints'))) +RegDependency.register(ToolDependency('pcb_print', 'LXML', is_python=True)) def _run_command(cmd): @@ -636,6 +639,7 @@ class PCB_PrintOptions(VariantOptions): if monochrome: if which('convert') is None: logger.error('`convert` not installed. install `imagemagick` or equivalent') + logger.error(TRY_INSTALL_CHECK) exit(MISSING_TOOL) for img in self.last_worksheet.images: with NamedTemporaryFile(mode='wb', suffix='.png', delete=False) as f: @@ -771,6 +775,7 @@ class PCB_PrintOptions(VariantOptions): # Check PcbDraw is available if which('pcbdraw') is None: logger.error('`pcbdraw` not installed, needed for `realistic_solder_mask`') + logger.error(TRY_INSTALL_CHECK) exit(MISSING_TOOL) # Run PcbDraw to make the heavy work (find the Edge.Cuts path and create masks) pcbdraw_file = os.path.join(temp_dir, out_file.replace('.svg', '-pcbdraw.svg')) @@ -864,10 +869,12 @@ class PCB_PrintOptions(VariantOptions): def generate_output(self, output): if self.format != 'SVG' and which(SVG2PDF) is None: logger.error('`{}` not installed. Install `librsvg2-bin` or equivalent'.format(SVG2PDF)) + logger.error(TRY_INSTALL_CHECK) exit(MISSING_TOOL) if self.format == 'PS' and which(PDF2PS) is None: logger.error('`{}` not installed. '.format(PDF2PS)) logger.error('Install `librsvg2-bin` or equivalent') + logger.error(TRY_INSTALL_CHECK) exit(MISSING_TOOL) output_dir = os.path.dirname(output) if self.keep_temporal_files: diff --git a/kibot/out_pcbdraw.py b/kibot/out_pcbdraw.py index 234a3a3f..e0a24530 100644 --- a/kibot/out_pcbdraw.py +++ b/kibot/out_pcbdraw.py @@ -9,7 +9,7 @@ from tempfile import NamedTemporaryFile import subprocess import shutil from .misc import (PCBDRAW, PCBDRAW_ERR, URL_PCBDRAW, W_AMBLIST, W_UNRETOOL, W_USESVG2, W_USEIMAGICK, PCB_MAT_COLORS, - PCB_FINISH_COLORS, SOLDER_COLORS, SILK_COLORS, ToolDependency, ToolDependencyRole) + PCB_FINISH_COLORS, SOLDER_COLORS, SILK_COLORS, ToolDependency, ToolDependencyRole, TRY_INSTALL_CHECK) from .kiplot import check_script from .registrable import RegDependency from .gs import GS @@ -24,9 +24,9 @@ CONVERT = 'convert' # 0.9.0 implements KiCad 6 support MIN_VERSION = '0.9.0' RegDependency.register(ToolDependency('pcbdraw', 'RSVG tools', 'https://cran.r-project.org/web/packages/rsvg/index.html', - deb='librsvg2-bin', + deb='librsvg2-bin', command=SVG2PNG, roles=ToolDependencyRole(desc='Create PNG and JPG images'))) -RegDependency.register(ToolDependency('pcbdraw', 'ImageMagick', 'https://imagemagick.org/', +RegDependency.register(ToolDependency('pcbdraw', 'ImageMagick', 'https://imagemagick.org/', command='convert', roles=ToolDependencyRole(desc='Create JPG images'))) RegDependency.register(ToolDependency('pcbdraw', 'PcbDraw', URL_PCBDRAW, url_down=URL_PCBDRAW+'/releases', in_debian=False, roles=ToolDependencyRole(version=(0, 9, 0)))) @@ -251,10 +251,12 @@ class PcbDrawOptions(VariantOptions): if shutil.which(SVG2PNG) is None: logger.warning(W_UNRETOOL + '`{}` not installed, using unreliable PNG/JPG conversion'.format(SVG2PNG)) logger.warning(W_USESVG2 + 'If you experiment problems install `librsvg2-bin` or equivalent') + logger.warning(W_USESVG2 + TRY_INSTALL_CHECK) cmd.append(output) elif shutil.which(CONVERT) is None: logger.warning(W_UNRETOOL + '`{}` not installed, using unreliable PNG/JPG conversion'.format(CONVERT)) logger.warning(W_USEIMAGICK + 'If you experiment problems install `imagemagick` or equivalent') + logger.warning(W_USEIMAGICK + TRY_INSTALL_CHECK) cmd.append(output) else: svg = _get_tmp_name('.svg') diff --git a/kibot/out_qr_lib.py b/kibot/out_qr_lib.py index 96afef75..3090c87e 100644 --- a/kibot/out_qr_lib.py +++ b/kibot/out_qr_lib.py @@ -24,7 +24,7 @@ QR_ECCS = {'low': QrCode.Ecc.LOW, logger = log.get_logger() TO_SEPARATE = {'kicad_pcb', 'general', 'title_block', 'layers', 'setup', 'pcbplotparams', 'net_class', 'module', 'kicad_sch', 'lib_symbols', 'symbol', 'sheet', 'sheet_instances', 'symbol_instances'} -RegDependency.register(ToolDependency('qr_lib', 'qrcodegen', is_python=True, roles=ToolDependencyRole())) +RegDependency.register(ToolDependency('qr_lib', 'QRCodeGen', is_python=True, roles=ToolDependencyRole())) def is_symbol(name, sexp): diff --git a/kibot/out_report.py b/kibot/out_report.py index 65605d91..2f25ab7c 100644 --- a/kibot/out_report.py +++ b/kibot/out_report.py @@ -11,7 +11,8 @@ from shutil import which from .gs import GS from .misc import (UI_SMD, UI_VIRTUAL, MOD_THROUGH_HOLE, MOD_SMD, MOD_EXCLUDE_FROM_POS_FILES, PANDOC, MISSING_TOOL, - FAILED_EXECUTE, W_WRONGEXT, W_WRONGOAR, W_ECCLASST, W_MISSTOOL, ToolDependency, ToolDependencyRole) + FAILED_EXECUTE, W_WRONGEXT, W_WRONGOAR, W_ECCLASST, W_MISSTOOL, ToolDependency, ToolDependencyRole, + TRY_INSTALL_CHECK) from .registrable import RegOutput, RegDependency from .out_base import BaseOptions from .error import KiPlotConfigurationError @@ -722,6 +723,7 @@ class ReportOptions(BaseOptions): except FileNotFoundError: logger.error("Unable to convert the report, `{}` must be installed.".format(PANDOC)) logger.error(PANDOC_INSTALL) + logger.error(TRY_INSTALL_CHECK) exit(MISSING_TOOL) except CalledProcessError as e: logger.error('{} error: {}'.format(PANDOC, e.returncode)) @@ -803,6 +805,7 @@ class Report(BaseOutput): # noqa: F821 def get_conf_examples(name, layers, templates): if which(PANDOC) is None: logger.warning((W_MISSTOOL+'Missing {} tool, disabling report in PDF format\n'+PANDOC_INSTALL).format(PANDOC)) + logger.warning(W_MISSTOOL+TRY_INSTALL_CHECK) pandoc = False else: pandoc = True diff --git a/kibot/pre_pcb_replace.py b/kibot/pre_pcb_replace.py index 77160070..6a9375bc 100644 --- a/kibot/pre_pcb_replace.py +++ b/kibot/pre_pcb_replace.py @@ -1,14 +1,17 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2021 Salvador E. Tropea -# Copyright (c) 2021 Instituto Nacional de Tecnología Industrial +# Copyright (c) 2021-2022 Salvador E. Tropea +# Copyright (c) 2021-2022 Instituto Nacional de Tecnología Industrial # License: GPL-3.0 # Project: KiBot (formerly KiPlot) from .gs import GS from .pre_any_replace import TagReplaceBase, Base_ReplaceOptions, Base_Replace +from .registrable import RegDependency +from .misc import git_dependency from .macros import macros, document, pre_class # noqa: F401 from . import log logger = log.get_logger() +RegDependency.register(git_dependency('pcb_replace')) class TagReplacePCB(TagReplaceBase): diff --git a/kibot/pre_sch_replace.py b/kibot/pre_sch_replace.py index 36538abd..ee765aa5 100644 --- a/kibot/pre_sch_replace.py +++ b/kibot/pre_sch_replace.py @@ -7,10 +7,13 @@ import os from .gs import GS from .kiplot import load_sch from .pre_any_replace import TagReplaceBase, Base_ReplaceOptions, Base_Replace +from .registrable import RegDependency +from .misc import git_dependency from .macros import macros, document, pre_class # noqa: F401 from . import log logger = log.get_logger() +RegDependency.register(git_dependency('sch_replace')) class TagReplaceSCH(TagReplaceBase): diff --git a/kibot/pre_set_text_variables.py b/kibot/pre_set_text_variables.py index 4cc833d6..26b4cf22 100644 --- a/kibot/pre_set_text_variables.py +++ b/kibot/pre_set_text_variables.py @@ -8,14 +8,16 @@ import sys import json from subprocess import run, PIPE from .error import KiPlotConfigurationError -from .misc import FAILED_EXECUTE, W_EMPTREP +from .misc import FAILED_EXECUTE, W_EMPTREP, git_dependency from .optionable import Optionable from .pre_base import BasePreFlight from .gs import GS +from .registrable import RegDependency from .macros import macros, document, pre_class # noqa: F401 from . import log logger = log.get_logger() +RegDependency.register(git_dependency('set_text_variables')) class KiCadVariable(Optionable): diff --git a/kibot/registrable.py b/kibot/registrable.py index f61a148f..c189f1c3 100644 --- a/kibot/registrable.py +++ b/kibot/registrable.py @@ -170,8 +170,8 @@ class RegDependency(Registrable): # Here we register some global dependencies -RegDependency.register(ToolDependency('global', 'colorama', is_python=True, +RegDependency.register(ToolDependency('global', 'Colorama', is_python=True, roles=ToolDependencyRole(desc='get color messages in a portable way'))) -RegDependency.register(ToolDependency('global', 'distutils', is_python=True)) -RegDependency.register(ToolDependency('global', 'requests', is_python=True)) -RegDependency.register(ToolDependency('global', 'PyYAML', is_python=True, deb='python3-yaml')) +RegDependency.register(ToolDependency('global', 'Distutils', is_python=True)) +RegDependency.register(ToolDependency('global', 'Requests', is_python=True)) +RegDependency.register(ToolDependency('global', 'PyYAML', is_python=True, deb='python3-yaml', module_name='yaml')) diff --git a/setup.py b/setup.py index ae300907..fd80e9eb 100755 --- a/setup.py +++ b/setup.py @@ -18,7 +18,7 @@ setup(name='kibot', url=__url__, # Packages are marked using __init__.py packages=find_packages(), - scripts=['src/kibot', 'src/kiplot'], + scripts=['src/kibot', 'src/kiplot', 'src/kibot-check'], install_requires=__pypi_deps__, include_package_data=True, classifiers=['Development Status :: 5 - Production/Stable', diff --git a/src/kibot-check b/src/kibot-check new file mode 100755 index 00000000..83e33015 --- /dev/null +++ b/src/kibot-check @@ -0,0 +1,1028 @@ +#!/usr/bin/python3 +# -*- 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) +# +# This is the installation checker, should help people to detect installation issues and install needed tools +import os +import re +import sys +import platform +import subprocess +import json +import importlib +from shutil import which +from contextlib import contextmanager + +deps = '{\ + "Colorama": {\ + "command": "colorama",\ + "deb_package": "python3-colorama",\ + "extra_deb": null,\ + "help_option": "--version",\ + "importance": 100,\ + "in_debian": true,\ + "is_kicad_plugin": false,\ + "is_python": true,\ + "module_name": "colorama",\ + "name": "Colorama",\ + "no_cmd_line_version": false,\ + "no_cmd_line_version_old": false,\ + "output": "global",\ + "plugin_dirs": null,\ + "pypi_name": "Colorama",\ + "roles": [\ + {\ + "desc": "get color messages in a portable way",\ + "mandatory": false,\ + "output": "global",\ + "version": null\ + }\ + ],\ + "url": null,\ + "url_down": null\ + },\ + "Distutils": {\ + "command": "distutils",\ + "deb_package": "python3-distutils",\ + "extra_deb": null,\ + "help_option": "--version",\ + "importance": 1000000,\ + "in_debian": true,\ + "is_kicad_plugin": false,\ + "is_python": true,\ + "module_name": "distutils",\ + "name": "Distutils",\ + "no_cmd_line_version": false,\ + "no_cmd_line_version_old": false,\ + "output": "global",\ + "plugin_dirs": null,\ + "pypi_name": "Distutils",\ + "roles": [\ + {\ + "desc": null,\ + "mandatory": true,\ + "output": "global",\ + "version": null\ + }\ + ],\ + "url": null,\ + "url_down": null\ + },\ + "Ghostscript": {\ + "command": "ghostscript",\ + "deb_package": "ghostscript",\ + "extra_deb": null,\ + "help_option": "--version",\ + "importance": 1,\ + "in_debian": true,\ + "is_kicad_plugin": false,\ + "is_python": false,\ + "name": "Ghostscript",\ + "no_cmd_line_version": false,\ + "no_cmd_line_version_old": false,\ + "output": "pcb_print",\ + "plugin_dirs": null,\ + "pypi_name": "Ghostscript",\ + "roles": [\ + {\ + "desc": "Create PS files",\ + "mandatory": false,\ + "output": "pcb_print",\ + "version": null\ + }\ + ],\ + "url": "https://www.ghostscript.com/",\ + "url_down": "https://github.com/ArtifexSoftware/ghostpdl-downloads/releases"\ + },\ + "Git": {\ + "command": "git",\ + "deb_package": "git",\ + "extra_deb": null,\ + "help_option": "--version",\ + "importance": 3,\ + "in_debian": true,\ + "is_kicad_plugin": false,\ + "is_python": false,\ + "name": "Git",\ + "no_cmd_line_version": false,\ + "no_cmd_line_version_old": false,\ + "output": "pcb_replace",\ + "plugin_dirs": null,\ + "pypi_name": "Git",\ + "roles": [\ + {\ + "desc": "Find commit hash and/or date",\ + "mandatory": false,\ + "output": "pcb_replace",\ + "version": null\ + },\ + {\ + "desc": "Find commit hash and/or date",\ + "mandatory": false,\ + "output": "sch_replace",\ + "version": null\ + },\ + {\ + "desc": "Find commit hash and/or date",\ + "mandatory": false,\ + "output": "set_text_variables",\ + "version": null\ + }\ + ],\ + "url": "https://git-scm.com/",\ + "url_down": null\ + },\ + "ImageMagick": {\ + "command": "convert",\ + "deb_package": "imagemagick",\ + "extra_deb": null,\ + "help_option": "--version",\ + "importance": 2,\ + "in_debian": true,\ + "is_kicad_plugin": false,\ + "is_python": false,\ + "name": "ImageMagick",\ + "no_cmd_line_version": false,\ + "no_cmd_line_version_old": false,\ + "output": "pcb_print",\ + "plugin_dirs": null,\ + "pypi_name": "ImageMagick",\ + "roles": [\ + {\ + "desc": "Create monochrome prints",\ + "mandatory": false,\ + "output": "pcb_print",\ + "version": null\ + },\ + {\ + "desc": "Create JPG images",\ + "mandatory": false,\ + "output": "pcbdraw",\ + "version": null\ + }\ + ],\ + "url": "https://imagemagick.org/",\ + "url_down": null\ + },\ + "Interactive HTML BoM": {\ + "command": "generate_interactive_bom.py",\ + "deb_package": "interactive html bom",\ + "extra_deb": null,\ + "help_option": "--version",\ + "importance": 10000,\ + "in_debian": false,\ + "is_kicad_plugin": true,\ + "is_python": false,\ + "name": "Interactive HTML BoM",\ + "no_cmd_line_version": false,\ + "no_cmd_line_version_old": true,\ + "output": "ibom",\ + "plugin_dirs": [\ + "InteractiveHtmlBom",\ + "InteractiveHtmlBom/InteractiveHtmlBom"\ + ],\ + "pypi_name": "Interactive HTML BoM",\ + "roles": [\ + {\ + "desc": null,\ + "mandatory": true,\ + "output": "ibom",\ + "version": [\ + 2,\ + 4,\ + 1,\ + 4\ + ]\ + }\ + ],\ + "url": "https://github.com/INTI-CMNB/InteractiveHtmlBom",\ + "url_down": "https://github.com/INTI-CMNB/InteractiveHtmlBom/releases"\ + },\ + "KiBoM": {\ + "command": "KiBOM_CLI.py",\ + "deb_package": "kibom",\ + "extra_deb": null,\ + "help_option": "--version",\ + "importance": 10000,\ + "in_debian": false,\ + "is_kicad_plugin": false,\ + "is_python": false,\ + "name": "KiBoM",\ + "no_cmd_line_version": false,\ + "no_cmd_line_version_old": false,\ + "output": "kibom",\ + "plugin_dirs": null,\ + "pypi_name": "KiBoM",\ + "roles": [\ + {\ + "desc": null,\ + "mandatory": true,\ + "output": "kibom",\ + "version": [\ + 1,\ + 8,\ + 0\ + ]\ + }\ + ],\ + "url": "https://github.com/INTI-CMNB/KiBoM",\ + "url_down": "https://github.com/INTI-CMNB/KiBoM/releases"\ + },\ + "KiCad Automation tools": {\ + "command": "pcbnew_do",\ + "deb_package": "kicad automation tools",\ + "extra_deb": null,\ + "help_option": "--version",\ + "importance": 110000,\ + "in_debian": false,\ + "is_kicad_plugin": false,\ + "is_python": false,\ + "name": "KiCad Automation tools",\ + "no_cmd_line_version": false,\ + "no_cmd_line_version_old": false,\ + "output": "step",\ + "plugin_dirs": null,\ + "pypi_name": "kiauto",\ + "roles": [\ + {\ + "desc": null,\ + "mandatory": true,\ + "output": "step",\ + "version": [\ + 1,\ + 6,\ + 1\ + ]\ + },\ + {\ + "desc": null,\ + "mandatory": true,\ + "output": "netlist",\ + "version": [\ + 1,\ + 6,\ + 11\ + ]\ + },\ + {\ + "desc": null,\ + "mandatory": true,\ + "output": "render_3d",\ + "version": [\ + 1,\ + 6,\ + 8\ + ]\ + },\ + {\ + "desc": null,\ + "mandatory": true,\ + "output": "svg_sch_print",\ + "version": null\ + },\ + {\ + "desc": null,\ + "mandatory": true,\ + "output": "pdf_pcb_print",\ + "version": [\ + 1,\ + 6,\ + 7\ + ]\ + },\ + {\ + "desc": null,\ + "mandatory": true,\ + "output": "pdf_sch_print",\ + "version": null\ + },\ + {\ + "desc": null,\ + "mandatory": true,\ + "output": "svg_pcb_print",\ + "version": [\ + 1,\ + 6,\ + 7\ + ]\ + },\ + {\ + "desc": null,\ + "mandatory": true,\ + "output": "gencad",\ + "version": [\ + 1,\ + 6,\ + 5\ + ]\ + },\ + {\ + "desc": null,\ + "mandatory": true,\ + "output": "run_erc",\ + "version": null\ + },\ + {\ + "desc": null,\ + "mandatory": true,\ + "output": "run_drc",\ + "version": null\ + },\ + {\ + "desc": null,\ + "mandatory": true,\ + "output": "update_xml",\ + "version": null\ + }\ + ],\ + "url": "https://github.com/INTI-CMNB/KiAuto",\ + "url_down": "https://github.com/INTI-CMNB/KiAuto/releases"\ + },\ + "KiCost": {\ + "command": "kicost",\ + "deb_package": "kicost",\ + "extra_deb": null,\ + "help_option": "--version",\ + "importance": 10001,\ + "in_debian": false,\ + "is_kicad_plugin": false,\ + "is_python": false,\ + "name": "KiCost",\ + "no_cmd_line_version": false,\ + "no_cmd_line_version_old": false,\ + "output": "bom",\ + "plugin_dirs": null,\ + "pypi_name": "KiCost",\ + "roles": [\ + {\ + "desc": "Find components costs and specs",\ + "mandatory": false,\ + "output": "bom",\ + "version": [\ + 1,\ + 1,\ + 8\ + ]\ + },\ + {\ + "desc": null,\ + "mandatory": true,\ + "output": "kicost",\ + "version": [\ + 1,\ + 1,\ + 7\ + ]\ + }\ + ],\ + "url": "https://github.com/INTI-CMNB/KiCost",\ + "url_down": "https://github.com/INTI-CMNB/KiCost/releases"\ + },\ + "LXML": {\ + "command": "lxml",\ + "deb_package": "python3-lxml",\ + "extra_deb": null,\ + "help_option": "--version",\ + "importance": 10000,\ + "in_debian": true,\ + "is_kicad_plugin": false,\ + "is_python": true,\ + "module_name": "lxml",\ + "name": "LXML",\ + "no_cmd_line_version": false,\ + "no_cmd_line_version_old": false,\ + "output": "pcb_print",\ + "plugin_dirs": null,\ + "pypi_name": "LXML",\ + "roles": [\ + {\ + "desc": null,\ + "mandatory": true,\ + "output": "pcb_print",\ + "version": null\ + }\ + ],\ + "url": null,\ + "url_down": null\ + },\ + "Pandoc": {\ + "command": "pandoc",\ + "deb_package": "pandoc",\ + "extra_deb": [\ + "texlive-latex-base",\ + "texlive-latex-recommended"\ + ],\ + "help_option": "--version",\ + "importance": 1,\ + "in_debian": true,\ + "is_kicad_plugin": false,\ + "is_python": false,\ + "name": "Pandoc",\ + "no_cmd_line_version": false,\ + "no_cmd_line_version_old": false,\ + "output": "report",\ + "plugin_dirs": null,\ + "pypi_name": "Pandoc",\ + "roles": [\ + {\ + "desc": "Create PDF/ODF/DOCX files",\ + "mandatory": false,\ + "output": "report",\ + "version": null\ + }\ + ],\ + "url": "https://pandoc.org/",\ + "url_down": "https://github.com/jgm/pandoc/releases"\ + },\ + "PcbDraw": {\ + "command": "pcbdraw",\ + "deb_package": "pcbdraw",\ + "extra_deb": null,\ + "help_option": "--version",\ + "importance": 10000,\ + "in_debian": false,\ + "is_kicad_plugin": false,\ + "is_python": false,\ + "name": "PcbDraw",\ + "no_cmd_line_version": false,\ + "no_cmd_line_version_old": false,\ + "output": "pcbdraw",\ + "plugin_dirs": null,\ + "pypi_name": "PcbDraw",\ + "roles": [\ + {\ + "desc": null,\ + "mandatory": true,\ + "output": "pcbdraw",\ + "version": [\ + 0,\ + 9,\ + 0\ + ]\ + }\ + ],\ + "url": "https://github.com/INTI-CMNB/pcbdraw",\ + "url_down": "https://github.com/INTI-CMNB/pcbdraw/releases"\ + },\ + "PyYAML": {\ + "command": "pyyaml",\ + "deb_package": "python3-yaml",\ + "extra_deb": null,\ + "help_option": "--version",\ + "importance": 1000000,\ + "in_debian": true,\ + "is_kicad_plugin": false,\ + "is_python": true,\ + "module_name": "yaml",\ + "name": "PyYAML",\ + "no_cmd_line_version": false,\ + "no_cmd_line_version_old": false,\ + "output": "global",\ + "plugin_dirs": null,\ + "pypi_name": "PyYAML",\ + "roles": [\ + {\ + "desc": null,\ + "mandatory": true,\ + "output": "global",\ + "version": null\ + }\ + ],\ + "url": null,\ + "url_down": null\ + },\ + "QRCodeGen": {\ + "command": "qrcodegen",\ + "deb_package": "python3-qrcodegen",\ + "extra_deb": null,\ + "help_option": "--version",\ + "importance": 10000,\ + "in_debian": true,\ + "is_kicad_plugin": false,\ + "is_python": true,\ + "module_name": "qrcodegen",\ + "name": "QRCodeGen",\ + "no_cmd_line_version": false,\ + "no_cmd_line_version_old": false,\ + "output": "qr_lib",\ + "plugin_dirs": null,\ + "pypi_name": "QRCodeGen",\ + "roles": [\ + {\ + "desc": null,\ + "mandatory": true,\ + "output": "qr_lib",\ + "version": null\ + }\ + ],\ + "url": null,\ + "url_down": null\ + },\ + "RAR": {\ + "command": "rar",\ + "deb_package": "rar",\ + "extra_deb": null,\ + "help_option": "-?",\ + "importance": 1,\ + "in_debian": true,\ + "is_kicad_plugin": false,\ + "is_python": false,\ + "name": "RAR",\ + "no_cmd_line_version": false,\ + "no_cmd_line_version_old": false,\ + "output": "compress",\ + "plugin_dirs": null,\ + "pypi_name": "RAR",\ + "roles": [\ + {\ + "desc": "Compress in RAR format",\ + "mandatory": false,\ + "output": "compress",\ + "version": null\ + }\ + ],\ + "url": "https://www.rarlab.com/",\ + "url_down": "https://www.rarlab.com/download.htm"\ + },\ + "RSVG tools": {\ + "command": "rsvg-convert",\ + "deb_package": "librsvg2-bin",\ + "extra_deb": null,\ + "help_option": "--version",\ + "importance": 2,\ + "in_debian": true,\ + "is_kicad_plugin": false,\ + "is_python": false,\ + "name": "RSVG tools",\ + "no_cmd_line_version": false,\ + "no_cmd_line_version_old": false,\ + "output": "pcb_print",\ + "plugin_dirs": null,\ + "pypi_name": "RSVG tools",\ + "roles": [\ + {\ + "desc": "Create PDF, PNG, EPS and PS formats",\ + "mandatory": false,\ + "output": "pcb_print",\ + "version": null\ + },\ + {\ + "desc": "Create PNG and JPG images",\ + "mandatory": false,\ + "output": "pcbdraw",\ + "version": null\ + }\ + ],\ + "url": "https://cran.r-project.org/web/packages/rsvg/index.html",\ + "url_down": null\ + },\ + "Requests": {\ + "command": "requests",\ + "deb_package": "python3-requests",\ + "extra_deb": null,\ + "help_option": "--version",\ + "importance": 1000000,\ + "in_debian": true,\ + "is_kicad_plugin": false,\ + "is_python": true,\ + "module_name": "requests",\ + "name": "Requests",\ + "no_cmd_line_version": false,\ + "no_cmd_line_version_old": false,\ + "output": "global",\ + "plugin_dirs": null,\ + "pypi_name": "Requests",\ + "roles": [\ + {\ + "desc": null,\ + "mandatory": true,\ + "output": "global",\ + "version": null\ + }\ + ],\ + "url": null,\ + "url_down": null\ + },\ + "XLSXWriter": {\ + "command": "xlsxwriter",\ + "deb_package": "python3-xlsxwriter",\ + "extra_deb": null,\ + "help_option": "--version",\ + "importance": 1,\ + "in_debian": true,\ + "is_kicad_plugin": false,\ + "is_python": true,\ + "module_name": "xlsxwriter",\ + "name": "XLSXWriter",\ + "no_cmd_line_version": false,\ + "no_cmd_line_version_old": false,\ + "output": "bom",\ + "plugin_dirs": null,\ + "pypi_name": "XLSXWriter",\ + "roles": [\ + {\ + "desc": "Create XLSX files",\ + "mandatory": false,\ + "output": "bom",\ + "version": null\ + }\ + ],\ + "url": null,\ + "url_down": null\ + }\ +}\ +' +# Dirs to look for plugins +kicad_plugins_dirs = [] +NOT_AVAIL = 'Not available' +UNKNOWN = '*UNKNOWN*' +CSI = '\033[' +RED = CSI+str(31)+'m' +GREEN = CSI+str(32)+'m' +YELLOW = CSI+str(33)+'m' +YELLOW2 = CSI+str(93)+'m' +RESET = CSI+str(39)+'m' +BRIGHT = CSI+";1;4"+'m' +NORMAL = CSI+'0'+'m' +last_ok = False +is_x86 = is_64 = is_linux = False +ver_re = re.compile(r'(\d+)\.(\d+)(?:\.(\d+))?(?:[\.-](\d+))?') + + +def run_command(cmd, only_first_line=True, pre_ver_text=None, no_err_2=False): + global last_ok + try: + cmd_output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) + except FileNotFoundError as e: + last_ok = False + return NOT_AVAIL + except subprocess.CalledProcessError as e: + if e.returncode != 2 or not no_err_2: + print('Failed to run %s, error %d' % (cmd[0], e.returncode)) + if e.output: + print('Output from command: '+e.output.decode()) + last_ok = False + return UNKNOWN + res = cmd_output.decode().strip() + if only_first_line: + res = res.split('\n')[0] + pre_vers = (cmd[0]+' version ', cmd[0]+' ', pre_ver_text) + for pre_ver in pre_vers: + if pre_ver and res.startswith(pre_ver): + res = res[len(pre_ver):] + last_ok = True + return res + + +def simple_run_command(cmd): + res = run_command(cmd) + sev, ver = check_version(res, [{'mandatory': True, 'output': 'global', 'version': None}], no_ver=True) + return do_color(res, sev, version=ver) + + +def search_as_plugin(cmd, names): + """ If a command isn't in the path look for it in the KiCad plugins """ + if which(cmd) is not None: + return cmd + for dir in kicad_plugins_dirs: + for name in names: + fname = os.path.join(dir, name, cmd) + if os.path.isfile(fname): + logger.debug('Using `{}` for `{}` ({})'.format(fname, cmd, name)) + return fname + return cmd + + +@contextmanager +def hide_stderr(): + """ Low level stderr suppression, used to hide KiCad bugs. """ + newstderr = os.dup(2) + devnull = os.open('/dev/null', os.O_WRONLY) + os.dup2(devnull, 2) + os.close(devnull) + yield + os.dup2(newstderr, 2) + + +def do_int(v): + return int(v) if v is not None else 0 + + +def check_version(version, roles, no_ver=False): + res = ver_re.search(version) + if res: + ver = list(map(do_int, res.groups())) + else: + ver = [0, 0, 0] + not_avail = version == NOT_AVAIL or version == UNKNOWN + severity = 0 + for r in roles: + mandatory = r['mandatory'] + glb = r['output'] == 'global' + this_sever = 0 + if not_avail or (r['version'] and ver < r['version']): + if mandatory: + this_sever = 4 if glb else 3 + else: + this_sever = 2 if glb else 1 + severity = max(severity, this_sever) + r['sev'] = this_sever + return severity, ver + + +def sev2color(severity): + if severity == 4: + return RED + elif severity == 3: + return YELLOW2 + elif severity: + return YELLOW + else: + return GREEN + + +def do_color(msg, severity, version=None): + if version is not None and version != [0, 0, 0]: + if len(version) == 4 and version[3] == 0: + version = version[:-1] + ver_str = '.'.join(map(str, version)) + if ver_str != msg: + msg = ver_str+' ('+msg+')' + return sev2color(severity)+msg+RESET + + +def error(msg): + print(sev2color(4)+'**> '+msg+RESET) + + +def do_bright(msg): + return BRIGHT+msg+NORMAL + + +def global2human(name): + return '`'+name+'`' if name != 'global' else 'general use' + + +def show_roles(roles): + needed = [] + optional = [] + for r in roles: + if r['mandatory']: + needed.append(r) + else: + optional.append(r) + r['output'] = global2human(r['output']) + if needed: + if len(needed) == 1: + color = sev2color(needed[0]['sev']) + name = needed[0]['output'] + if name == 'general use': + print(color+' - Mandatory') + else: + print(color+' - Mandatory for '+name) + else: + need_s = sorted(needed, key=lambda x: x['output']) + print(RESET+' - Mandatory for: '+', '.join([sev2color(f['sev'])+f['output']+RESET for f in need_s])) + if optional: + if len(optional) == 1: + o = optional[0] + desc = o['desc'][0].lower()+o['desc'][1:] + print(sev2color(o['sev'])+' - Optional to {} for {}'.format(desc, o['output'])) + else: + print(RESET+' - Optional to:') + for o in optional: + ver = '' + if o['version']: + ver = ' (v'+'.'.join(map(str, o['version']))+')' + print(sev2color(o['sev'])+' - {} for {}{}'.format(o['desc'], o['output'], ver)) + + +def python_module(severity, name, deb_package, roles): + if not severity: + return + print(sev2color(severity)+'* Python module `{}` not installed or too old'.format(name)) + if debian_support: + if deb_package is None: + deb_package = 'python3-'+name + print(' Install the `{0}` package, i.e.: `sudo apt-get install {0}`'.format(deb_package)) + elif pip_ok: + print(' run `{} install {}` as root,'.format(pip_command, name)) + print(' or run `{} install --user {}` as a regular user'.format(pip_command, name)) + else: + print(' Install the Package Installer for Python (pip) and run this script again') + show_roles(roles) + print(RESET) + + +def binary_tool(severity, name, url, url_down, deb_package, deb, extra_deb, roles): + if not severity: + return + print(sev2color(severity)+'* {} not installed or too old'.format(name)) + if deb and debian_support: + if deb_package is None: + deb_package = name.lower() + print(' Install the `{0}` package, i.e.: `sudo apt-get install {0}`'.format(deb_package)) + if extra_deb: + print(' You should also install the following packages: '+', '.join(extra_deb)) + else: + print(' Visit: '+url) + if url_down: + print(' Download it from: '+url_down) + show_roles(roles) + print(RESET) + +# ###################################################################################################################### +# Core tools +# ###################################################################################################################### + +print('KiBot installation checker\n') + +print(do_bright('Core:')) +# Operating system +system = platform.system() +if system == 'Linux': + linux_version = simple_run_command(['uname', '-a']) + print('Linux: '+linux_version) + os_ok = True + is_x86 = 'x86' in linux_version + is_64 = ('x86_64' in linux_version) or ('amd64' in linux_version) + is_linux = True +else: + print(system) + os_ok = False +# Python version +if sys.version_info >= (3, 6): + py_ok = True + sev = 0 +else: + py_ok = False + sev = 4 +print('Python: '+do_color(sys.version.replace('\n', ' '), sev)) +# KiCad +try: + import pcbnew + kicad_ok = True + # Fill the plug-in locations + # TODO: Windows? MacOSX? + kicad_share_path = '/usr/share/kicad' + if hasattr(pcbnew, 'GetKicadConfigPath'): + with hide_stderr(): + kicad_conf_path = pcbnew.GetKicadConfigPath() + elif hasattr(pcbnew, 'GetSettingsManager'): + kicad_conf_path = pcbnew.GetSettingsManager().GetUserSettingsPath() + else: + kicad_conf_path = None + # /usr/share/kicad/* + kicad_plugins_dirs.append(os.path.join(kicad_share_path, 'scripting')) + kicad_plugins_dirs.append(os.path.join(kicad_share_path, 'scripting', 'plugins')) + # ~/.config/kicad/* + if kicad_conf_path: + kicad_plugins_dirs.append(os.path.join(kicad_conf_path, 'scripting')) + kicad_plugins_dirs.append(os.path.join(kicad_conf_path, 'scripting', 'plugins')) + # ~/.kicad_plugins and ~/.kicad + if 'HOME' in os.environ: + home = os.environ['HOME'] + kicad_plugins_dirs.append(os.path.join(home, '.kicad_plugins')) + kicad_plugins_dirs.append(os.path.join(home, '.kicad', 'scripting')) + kicad_plugins_dirs.append(os.path.join(home, '.kicad', 'scripting', 'plugins')) +except FileNotFoundError: + kicad_ok = False +kicad_version = (0, 0, 0) +if kicad_ok: + try: + version = pcbnew.GetBuildVersion() + # KiCad version + m = re.search(r'(\d+)\.(\d+)\.(\d+)', version) + if m is None: + error("Unable to detect KiCad version, got: `{}`".format(version)) + else: + kicad_version = (int(m.group(1)), int(m.group(2)), int(m.group(3))) + except: + version = 'Older than 5.1.6' +else: + version = NOT_AVAIL +if kicad_version >= (5, 1, 6) and kicad_version < (6, 99): + sev = 0 +else: + sev = 4 +print('KiCad: '+do_color(version, sev)) +# KiBot version +try: + from kibot.__main__ import __version__ + kibot_ok = True + sev = 0 +except: + __version__ = NOT_AVAIL + kibot_ok = False + sev = 4 +print('Kibot: '+do_color(__version__, sev)) +if kibot_ok and which('kibot') is None: + print(sev2color(4)+'* KiBot is installed but not available in your PATH') + import kibot + if '/lib/' in kibot.__file__: + v = re.sub(r'\/lib\/.*', '/bin/kibot', kibot.__file__) + if os.path.isfile(v): + print(' Try adding `{}` to your PATH'.format(v[:-5])) + print(' I.e.: export PATH=$PATH:'+v[:-5]) + sys.exit(1) + +dependencies = json.loads(deps) +print(do_bright('\nModules:')) +for name, d in dependencies.items(): + if not d['is_python']: + continue + try: + mod = importlib.import_module(d['module_name']) + if hasattr(mod, '__version__'): + version = mod.__version__ + else: + version = 'Ok' + except: + version = NOT_AVAIL + sev, ver = check_version(version, d['roles']) + d['sev'] = sev + print(name+': '+do_color(version, sev, version=ver)) + +print(do_bright('\nTools:')) +for name, d in dependencies.items(): + if d['is_python']: + continue + command = d['command'] + if d['is_kicad_plugin']: + command = search_as_plugin(command, d['plugin_dirs']) + if d['no_cmd_line_version']: + version = 'Ok ({})'.format(command) if which(command) is not None else NOT_AVAIL + else: + version = run_command([command, d['help_option']], no_err_2=d['no_cmd_line_version_old']) + sev, ver = check_version(version, d['roles']) + d['sev'] = sev + print(name+': '+do_color(version, sev, version=ver)) + +# ###################################################################################################################### +# Recommendations +# ###################################################################################################################### + +print() + +debian_support = False +if which('apt-get'): + debian_support = True +pip_ok = False +if which('pip3'): + pip_ok = True + pip_command = 'pip3' +elif which('pip'): + pip_ok = True + pip_command = 'pip' + +if not os_ok: + print(sev2color(4)+'* KiBot is currently tested under Linux') + if system == 'Darwin': + print(' MacOSX should be supported for KiCad 6.x') + elif system == 'Windows': + print(' Windows may work with some limitations for KiCad 6.x') + print(' Consider using a docker image, Windows docker can run Linux images (using virtualization)') + else: + print(' What OS are you using? Is KiCad available for it?') + print(' Please consult: https://github.com/INTI-CMNB/KiBot/issues') + print(RESET) + +if not py_ok: + print(sev2color(4)+'* Install Python 3.6 or newer') + print(RESET) + +if not kicad_ok: + print(sev2color(4)+'* Install KiCad 5.1.6 or newer') + if debian_support: + print(' Try `apt-get install kicad` as root') + else: + print(' Download it from: https://www.kicad.org/download/') + print(RESET) + +if not kibot_ok: + print(sev2color(4)+'* Install KiBot!') + if debian_support: + print(' Follow the instructions here: https://set-soft.github.io/debian/') + elif pip_ok: + print(' run `{} install --no-compile kibot` as root,'.format(pip_command)) + print(' or run `{} install --user --no-compile kibot` as a regular user'.format(pip_command)) + else: + print(' Install the Package Installer for Python (pip) and run this script again') + print(RESET) + +for name, d in dependencies.items(): + if d['is_python']: + python_module(d['sev'], d['pypi_name'], d['deb_package'], d['roles']) + else: + binary_tool(d['sev'], d['name'], d['url'], d['url_down'], d['deb_package'], d['in_debian'], d['extra_deb'], + d['roles']) + +labels = ('ok', 'optional for an output', 'optional for general use', 'mandatory for an output', 'mandatory for general use') +text = ', '.join([sev2color(c)+l+RESET for c, l in enumerate(labels)]) +print(do_bright('\nColor reference:')+' '+text) + +print('\nDid this help? Please consider commenting it on https://github.com/INTI-CMNB/KiBot/issues/200') + diff --git a/src/kibot-check.in b/src/kibot-check.in new file mode 100755 index 00000000..88cd57ef --- /dev/null +++ b/src/kibot-check.in @@ -0,0 +1,410 @@ +#!/usr/bin/python3 +# -*- 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) +# +# This is the installation checker, should help people to detect installation issues and install needed tools +import os +import re +import sys +import platform +import subprocess +import json +import importlib +from shutil import which +from contextlib import contextmanager + +deps = '@json_dep@' +# Dirs to look for plugins +kicad_plugins_dirs = [] +NOT_AVAIL = 'Not available' +UNKNOWN = '*UNKNOWN*' +CSI = '\033[' +RED = CSI+str(31)+'m' +GREEN = CSI+str(32)+'m' +YELLOW = CSI+str(33)+'m' +YELLOW2 = CSI+str(93)+'m' +RESET = CSI+str(39)+'m' +BRIGHT = CSI+";1;4"+'m' +NORMAL = CSI+'0'+'m' +last_ok = False +is_x86 = is_64 = is_linux = False +ver_re = re.compile(r'(\d+)\.(\d+)(?:\.(\d+))?(?:[\.-](\d+))?') + + +def run_command(cmd, only_first_line=True, pre_ver_text=None, no_err_2=False): + global last_ok + try: + cmd_output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) + except FileNotFoundError as e: + last_ok = False + return NOT_AVAIL + except subprocess.CalledProcessError as e: + if e.returncode != 2 or not no_err_2: + print('Failed to run %s, error %d' % (cmd[0], e.returncode)) + if e.output: + print('Output from command: '+e.output.decode()) + last_ok = False + return UNKNOWN + res = cmd_output.decode().strip() + if only_first_line: + res = res.split('\n')[0] + pre_vers = (cmd[0]+' version ', cmd[0]+' ', pre_ver_text) + for pre_ver in pre_vers: + if pre_ver and res.startswith(pre_ver): + res = res[len(pre_ver):] + last_ok = True + return res + + +def simple_run_command(cmd): + res = run_command(cmd) + sev, ver = check_version(res, [{'mandatory': True, 'output': 'global', 'version': None}], no_ver=True) + return do_color(res, sev, version=ver) + + +def search_as_plugin(cmd, names): + """ If a command isn't in the path look for it in the KiCad plugins """ + if which(cmd) is not None: + return cmd + for dir in kicad_plugins_dirs: + for name in names: + fname = os.path.join(dir, name, cmd) + if os.path.isfile(fname): + logger.debug('Using `{}` for `{}` ({})'.format(fname, cmd, name)) + return fname + return cmd + + +@contextmanager +def hide_stderr(): + """ Low level stderr suppression, used to hide KiCad bugs. """ + newstderr = os.dup(2) + devnull = os.open('/dev/null', os.O_WRONLY) + os.dup2(devnull, 2) + os.close(devnull) + yield + os.dup2(newstderr, 2) + + +def do_int(v): + return int(v) if v is not None else 0 + + +def check_version(version, roles, no_ver=False): + res = ver_re.search(version) + if res: + ver = list(map(do_int, res.groups())) + else: + ver = [0, 0, 0] + not_avail = version == NOT_AVAIL or version == UNKNOWN + severity = 0 + for r in roles: + mandatory = r['mandatory'] + glb = r['output'] == 'global' + this_sever = 0 + if not_avail or (r['version'] and ver < r['version']): + if mandatory: + this_sever = 4 if glb else 3 + else: + this_sever = 2 if glb else 1 + severity = max(severity, this_sever) + r['sev'] = this_sever + return severity, ver + + +def sev2color(severity): + if severity == 4: + return RED + elif severity == 3: + return YELLOW2 + elif severity: + return YELLOW + else: + return GREEN + + +def do_color(msg, severity, version=None): + if version is not None and version != [0, 0, 0]: + if len(version) == 4 and version[3] == 0: + version = version[:-1] + ver_str = '.'.join(map(str, version)) + if ver_str != msg: + msg = ver_str+' ('+msg+')' + return sev2color(severity)+msg+RESET + + +def error(msg): + print(sev2color(4)+'**> '+msg+RESET) + + +def do_bright(msg): + return BRIGHT+msg+NORMAL + + +def global2human(name): + return '`'+name+'`' if name != 'global' else 'general use' + + +def show_roles(roles): + needed = [] + optional = [] + for r in roles: + if r['mandatory']: + needed.append(r) + else: + optional.append(r) + r['output'] = global2human(r['output']) + if needed: + if len(needed) == 1: + color = sev2color(needed[0]['sev']) + name = needed[0]['output'] + if name == 'general use': + print(color+' - Mandatory') + else: + print(color+' - Mandatory for '+name) + else: + need_s = sorted(needed, key=lambda x: x['output']) + print(RESET+' - Mandatory for: '+', '.join([sev2color(f['sev'])+f['output']+RESET for f in need_s])) + if optional: + if len(optional) == 1: + o = optional[0] + desc = o['desc'][0].lower()+o['desc'][1:] + print(sev2color(o['sev'])+' - Optional to {} for {}'.format(desc, o['output'])) + else: + print(RESET+' - Optional to:') + for o in optional: + ver = '' + if o['version']: + ver = ' (v'+'.'.join(map(str, o['version']))+')' + print(sev2color(o['sev'])+' - {} for {}{}'.format(o['desc'], o['output'], ver)) + + +def python_module(severity, name, deb_package, roles): + if not severity: + return + print(sev2color(severity)+'* Python module `{}` not installed or too old'.format(name)) + if debian_support: + if deb_package is None: + deb_package = 'python3-'+name + print(' Install the `{0}` package, i.e.: `sudo apt-get install {0}`'.format(deb_package)) + elif pip_ok: + print(' run `{} install {}` as root,'.format(pip_command, name)) + print(' or run `{} install --user {}` as a regular user'.format(pip_command, name)) + else: + print(' Install the Package Installer for Python (pip) and run this script again') + show_roles(roles) + print(RESET) + + +def binary_tool(severity, name, url, url_down, deb_package, deb, extra_deb, roles): + if not severity: + return + print(sev2color(severity)+'* {} not installed or too old'.format(name)) + if deb and debian_support: + if deb_package is None: + deb_package = name.lower() + print(' Install the `{0}` package, i.e.: `sudo apt-get install {0}`'.format(deb_package)) + if extra_deb: + print(' You should also install the following packages: '+', '.join(extra_deb)) + else: + print(' Visit: '+url) + if url_down: + print(' Download it from: '+url_down) + show_roles(roles) + print(RESET) + +# ###################################################################################################################### +# Core tools +# ###################################################################################################################### + +print('KiBot installation checker\n') + +print(do_bright('Core:')) +# Operating system +system = platform.system() +if system == 'Linux': + linux_version = simple_run_command(['uname', '-a']) + print('Linux: '+linux_version) + os_ok = True + is_x86 = 'x86' in linux_version + is_64 = ('x86_64' in linux_version) or ('amd64' in linux_version) + is_linux = True +else: + print(system) + os_ok = False +# Python version +if sys.version_info >= (3, 6): + py_ok = True + sev = 0 +else: + py_ok = False + sev = 4 +print('Python: '+do_color(sys.version.replace('\n', ' '), sev)) +# KiCad +try: + import pcbnew + kicad_ok = True + # Fill the plug-in locations + # TODO: Windows? MacOSX? + kicad_share_path = '/usr/share/kicad' + if hasattr(pcbnew, 'GetKicadConfigPath'): + with hide_stderr(): + kicad_conf_path = pcbnew.GetKicadConfigPath() + elif hasattr(pcbnew, 'GetSettingsManager'): + kicad_conf_path = pcbnew.GetSettingsManager().GetUserSettingsPath() + else: + kicad_conf_path = None + # /usr/share/kicad/* + kicad_plugins_dirs.append(os.path.join(kicad_share_path, 'scripting')) + kicad_plugins_dirs.append(os.path.join(kicad_share_path, 'scripting', 'plugins')) + # ~/.config/kicad/* + if kicad_conf_path: + kicad_plugins_dirs.append(os.path.join(kicad_conf_path, 'scripting')) + kicad_plugins_dirs.append(os.path.join(kicad_conf_path, 'scripting', 'plugins')) + # ~/.kicad_plugins and ~/.kicad + if 'HOME' in os.environ: + home = os.environ['HOME'] + kicad_plugins_dirs.append(os.path.join(home, '.kicad_plugins')) + kicad_plugins_dirs.append(os.path.join(home, '.kicad', 'scripting')) + kicad_plugins_dirs.append(os.path.join(home, '.kicad', 'scripting', 'plugins')) +except FileNotFoundError: + kicad_ok = False +kicad_version = (0, 0, 0) +if kicad_ok: + try: + version = pcbnew.GetBuildVersion() + # KiCad version + m = re.search(r'(\d+)\.(\d+)\.(\d+)', version) + if m is None: + error("Unable to detect KiCad version, got: `{}`".format(version)) + else: + kicad_version = (int(m.group(1)), int(m.group(2)), int(m.group(3))) + except: + version = 'Older than 5.1.6' +else: + version = NOT_AVAIL +if kicad_version >= (5, 1, 6) and kicad_version < (6, 99): + sev = 0 +else: + sev = 4 +print('KiCad: '+do_color(version, sev)) +# KiBot version +try: + from kibot.__main__ import __version__ + kibot_ok = True + sev = 0 +except: + __version__ = NOT_AVAIL + kibot_ok = False + sev = 4 +print('Kibot: '+do_color(__version__, sev)) +if kibot_ok and which('kibot') is None: + print(sev2color(4)+'* KiBot is installed but not available in your PATH') + import kibot + if '/lib/' in kibot.__file__: + v = re.sub(r'\/lib\/.*', '/bin/kibot', kibot.__file__) + if os.path.isfile(v): + print(' Try adding `{}` to your PATH'.format(v[:-5])) + print(' I.e.: export PATH=$PATH:'+v[:-5]) + sys.exit(1) + +dependencies = json.loads(deps) +print(do_bright('\nModules:')) +for name, d in dependencies.items(): + if not d['is_python']: + continue + try: + mod = importlib.import_module(d['module_name']) + if hasattr(mod, '__version__'): + version = mod.__version__ + else: + version = 'Ok' + except: + version = NOT_AVAIL + sev, ver = check_version(version, d['roles']) + d['sev'] = sev + print(name+': '+do_color(version, sev, version=ver)) + +print(do_bright('\nTools:')) +for name, d in dependencies.items(): + if d['is_python']: + continue + command = d['command'] + if d['is_kicad_plugin']: + command = search_as_plugin(command, d['plugin_dirs']) + if d['no_cmd_line_version']: + version = 'Ok ({})'.format(command) if which(command) is not None else NOT_AVAIL + else: + version = run_command([command, d['help_option']], no_err_2=d['no_cmd_line_version_old']) + sev, ver = check_version(version, d['roles']) + d['sev'] = sev + print(name+': '+do_color(version, sev, version=ver)) + +# ###################################################################################################################### +# Recommendations +# ###################################################################################################################### + +print() + +debian_support = False +if which('apt-get'): + debian_support = True +pip_ok = False +if which('pip3'): + pip_ok = True + pip_command = 'pip3' +elif which('pip'): + pip_ok = True + pip_command = 'pip' + +if not os_ok: + print(sev2color(4)+'* KiBot is currently tested under Linux') + if system == 'Darwin': + print(' MacOSX should be supported for KiCad 6.x') + elif system == 'Windows': + print(' Windows may work with some limitations for KiCad 6.x') + print(' Consider using a docker image, Windows docker can run Linux images (using virtualization)') + else: + print(' What OS are you using? Is KiCad available for it?') + print(' Please consult: https://github.com/INTI-CMNB/KiBot/issues') + print(RESET) + +if not py_ok: + print(sev2color(4)+'* Install Python 3.6 or newer') + print(RESET) + +if not kicad_ok: + print(sev2color(4)+'* Install KiCad 5.1.6 or newer') + if debian_support: + print(' Try `apt-get install kicad` as root') + else: + print(' Download it from: https://www.kicad.org/download/') + print(RESET) + +if not kibot_ok: + print(sev2color(4)+'* Install KiBot!') + if debian_support: + print(' Follow the instructions here: https://set-soft.github.io/debian/') + elif pip_ok: + print(' run `{} install --no-compile kibot` as root,'.format(pip_command)) + print(' or run `{} install --user --no-compile kibot` as a regular user'.format(pip_command)) + else: + print(' Install the Package Installer for Python (pip) and run this script again') + print(RESET) + +for name, d in dependencies.items(): + if d['is_python']: + python_module(d['sev'], d['pypi_name'], d['deb_package'], d['roles']) + else: + binary_tool(d['sev'], d['name'], d['url'], d['url_down'], d['deb_package'], d['in_debian'], d['extra_deb'], + d['roles']) + +labels = ('ok', 'optional for an output', 'optional for general use', 'mandatory for an output', 'mandatory for general use') +text = ', '.join([sev2color(c)+l+RESET for c, l in enumerate(labels)]) +print(do_bright('\nColor reference:')+' '+text) + +print('\nDid this help? Please consider commenting it on https://github.com/INTI-CMNB/KiBot/issues/200') +