├── .github ├── logToCs.py └── workflows │ ├── pre-commit.yml │ ├── python-publish.yml │ ├── pythonapp.yml │ └── release.yml ├── .gitignore ├── .pre-commit-config.yaml ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.md ├── debian ├── README.Debian ├── changelog ├── compat ├── control ├── copyright ├── docs ├── rules ├── source │ ├── format │ └── local-options └── watch ├── kicad-diff-init.py ├── kicad-diff.py ├── kicad-git-diff.py ├── setup.cfg ├── setup.py └── tests ├── cases ├── 1 │ ├── a │ │ ├── 1.kicad_pcb │ │ └── 1.kicad_sch │ ├── b │ │ ├── 1.kicad_pcb │ │ └── 1.kicad_sch │ ├── ref_pcb │ │ ├── diff-00.png │ │ ├── diff-10.png │ │ ├── diff-20.png │ │ ├── diff-310.png │ │ ├── diff-320.png │ │ ├── diff-330.png │ │ ├── diff-340.png │ │ ├── diff-350.png │ │ ├── diff-360.png │ │ ├── diff-370.png │ │ ├── diff-380.png │ │ ├── diff-390.png │ │ ├── diff-400.png │ │ ├── diff-410.png │ │ ├── diff-420.png │ │ ├── diff-430.png │ │ ├── diff-440.png │ │ ├── diff-450.png │ │ ├── diff-460.png │ │ ├── diff-470.png │ │ ├── diff-480.png │ │ └── diff-490.png │ └── ref_sch │ │ └── diff-Schematic0.png ├── 2 │ ├── a │ │ ├── 2.kicad_sch │ │ ├── deeper.kicad_sch │ │ └── sub-sheet.kicad_sch │ ├── b │ │ ├── 2.kicad_sch │ │ ├── deeper.kicad_sch │ │ └── sub-sheet.kicad_sch │ └── ref_sch │ │ └── diff.pdf ├── 3 │ ├── a │ │ └── 3.kicad_pcb │ └── b │ │ └── 3.kicad_pcb ├── 4 │ ├── a │ │ └── 4.kicad_pcb │ ├── b │ │ └── 4.kicad_pcb │ ├── exclude.lst │ ├── include.lst │ ├── include_names.lst │ ├── ref_pcb │ │ ├── diff-00.png │ │ ├── diff-10.png │ │ ├── diff-20.png │ │ ├── diff-310.png │ │ └── diff-360.png │ └── ref_pcb2 │ │ ├── diff-00.png │ │ ├── diff-10.png │ │ ├── diff-20.png │ │ ├── diff-310.png │ │ └── diff-360.png ├── 5 │ ├── a │ │ ├── 5.kicad_sch │ │ ├── deeper.kicad_sch │ │ └── sub-sheet.kicad_sch │ ├── b │ │ ├── 5.kicad_sch │ │ ├── deeper.kicad_sch │ │ └── sub-sheet.kicad_sch │ ├── ref_sch │ │ ├── diff-Schematic_root-Sub Sheet 2-Deeper test0.png │ │ ├── diff-Schematic_root-Sub Sheet 20.png │ │ ├── diff-Schematic_root-Sub Sheet-Deeper test0.png │ │ ├── diff-Schematic_root-Sub Sheet0.png │ │ └── diff-Schematic_root0.png │ └── ref_sch2 │ │ ├── diff-Schematic_root-Sub Sheet 2-Deeper test0.png │ │ ├── diff-Schematic_root-Sub Sheet 20.png │ │ ├── diff-Schematic_root-Sub Sheet-Deeper test0.png │ │ ├── diff-Schematic_root-Sub Sheet0.png │ │ └── diff-Schematic_root0.png ├── 6 │ ├── a │ │ └── 6.kicad_sch │ ├── b │ │ └── 6.kicad_sch │ └── ref_sch │ │ └── diff-Schematic0.png └── exclude.lst ├── conftest.py ├── test_pcb_diff.py ├── test_sch_diff.py └── utils └── context.py /.github/logToCs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # pylint: disable=invalid-name 3 | """ 4 | Convert a log to CheckStyle format. 5 | 6 | Url: https://github.com/mdeweerd/LogToCheckStyle 7 | 8 | The log can then be used for generating annotations in a github action. 9 | 10 | Note: this script is very young and "quick and dirty". 11 | Patterns can be added to "PATTERNS" to match more messages. 12 | 13 | # Examples 14 | 15 | Assumes that logToCs.py is available as .github/logToCs.py. 16 | 17 | ## Example 1: 18 | 19 | 20 | ```yaml 21 | - run: | 22 | pre-commit run -all-files | tee pre-commit.log 23 | .github/logToCs.py pre-commit.log pre-commit.xml 24 | - uses: staabm/annotate-pull-request-from-checkstyle-action@v1 25 | with: 26 | files: pre-commit.xml 27 | notices-as-warnings: true # optional 28 | ``` 29 | 30 | ## Example 2: 31 | 32 | 33 | ```yaml 34 | - run: | 35 | pre-commit run --all-files | tee pre-commit.log 36 | - name: Add results to PR 37 | if: ${{ always() }} 38 | run: | 39 | .github/logToCs.py pre-commit.log | cs2pr 40 | ``` 41 | 42 | Author(s): 43 | - https://github.com/mdeweerd 44 | 45 | License: MIT License 46 | 47 | """ 48 | 49 | import argparse 50 | import os 51 | import re 52 | import sys 53 | import xml.etree.ElementTree as ET # nosec 54 | 55 | 56 | def convert_to_checkstyle(messages, root_path=None): 57 | """ 58 | Convert provided message to CheckStyle format. 59 | """ 60 | root = ET.Element("checkstyle") 61 | for message in messages: 62 | fields = parse_message(message) 63 | if fields: 64 | add_error_entry(root, **fields, root_path=root_path) 65 | return ET.tostring(root, encoding="utf-8").decode("utf-8") 66 | 67 | 68 | def convert_text_to_checkstyle(text, root_path=None): 69 | """ 70 | Convert provided message to CheckStyle format. 71 | """ 72 | root = ET.Element("checkstyle") 73 | for fields in parse_file(text): 74 | if fields: 75 | add_error_entry(root, **fields, root_path=root_path) 76 | return ET.tostring(root, encoding="utf-8").decode("utf-8") 77 | 78 | 79 | ANY_REGEX = r".*?" 80 | FILE_REGEX = r"\s*(?P\S.*?)\s*?" 81 | EOL_REGEX = r"[\r\n]" 82 | LINE_REGEX = r"\s*(?P\d+?)\s*?" 83 | COLUMN_REGEX = r"\s*(?P\d+?)\s*?" 84 | SEVERITY_REGEX = r"\s*(?Perror|warning|notice|style|info)\s*?" 85 | MSG_REGEX = r"\s*(?P.+?)\s*?" 86 | MULTILINE_MSG_REGEX = r"\s*(?P(?:.|.[\r\n])+)" 87 | # cpplint confidence index 88 | CONFIDENCE_REGEX = r"\s*\[(?P\d+)\]\s*?" 89 | 90 | 91 | # List of message patterns, add more specific patterns earlier in the list 92 | # Creating patterns by using constants makes them easier to define and read. 93 | PATTERNS = [ 94 | # beautysh 95 | # File ftp.sh: error: "esac" before "case" in line 90. 96 | re.compile( 97 | f"^File {FILE_REGEX}:{SEVERITY_REGEX}:" 98 | f" {MSG_REGEX} in line {LINE_REGEX}.$" 99 | ), 100 | # beautysh 101 | # File socks4echo.sh: error: indent/outdent mismatch: -2. 102 | re.compile(f"^File {FILE_REGEX}:{SEVERITY_REGEX}: {MSG_REGEX}$"), 103 | # ESLint (JavaScript Linter), RoboCop, shellcheck 104 | # path/to/file.js:10:2: Some linting issue 105 | # path/to/file.rb:10:5: Style/Indentation: Incorrect indentation detected 106 | # path/to/script.sh:10:1: SC2034: Some shell script issue 107 | re.compile(f"^{FILE_REGEX}:{LINE_REGEX}:{COLUMN_REGEX}: {MSG_REGEX}$"), 108 | # Cpplint default output: 109 | # '%s:%s: %s [%s] [%d]\n' 110 | # % (filename, linenum, message, category, confidence) 111 | re.compile(f"^{FILE_REGEX}:{LINE_REGEX}:{MSG_REGEX}{CONFIDENCE_REGEX}$"), 112 | # MSVC 113 | # file.cpp(10): error C1234: Some error message 114 | re.compile( 115 | f"^{FILE_REGEX}\\({LINE_REGEX}\\):{SEVERITY_REGEX}{MSG_REGEX}$" 116 | ), 117 | # Java compiler 118 | # File.java:10: error: Some error message 119 | re.compile(f"^{FILE_REGEX}:{LINE_REGEX}:{SEVERITY_REGEX}:{MSG_REGEX}$"), 120 | # Python 121 | # File ".../logToCs.py", line 90 (note: code line follows) 122 | re.compile(f'^File "{FILE_REGEX}", line {LINE_REGEX}$'), 123 | # Pylint, others 124 | # path/to/file.py:10: [C0111] Missing docstring 125 | # others 126 | re.compile(f"^{FILE_REGEX}:{LINE_REGEX}: {MSG_REGEX}$"), 127 | # Shellcheck: 128 | # In script.sh line 76: 129 | re.compile( 130 | f"^In {FILE_REGEX} line {LINE_REGEX}:{EOL_REGEX}?" 131 | f"({MULTILINE_MSG_REGEX})?{EOL_REGEX}{EOL_REGEX}" 132 | ), 133 | # eslint: 134 | # /path/to/filename 135 | # 14:5 error Unexpected trailing comma comma-dangle 136 | re.compile( 137 | f"^{FILE_REGEX}{EOL_REGEX}" 138 | rf"\s+{LINE_REGEX}:{COLUMN_REGEX}\s+{SEVERITY_REGEX}\s+{MSG_REGEX}$" 139 | ), 140 | ] 141 | 142 | # Severities available in CodeSniffer report format 143 | SEVERITY_NOTICE = "notice" 144 | SEVERITY_WARNING = "warning" 145 | SEVERITY_ERROR = "error" 146 | 147 | 148 | def strip_ansi(text: str): 149 | """ 150 | Strip ANSI escape sequences from string (colors, etc) 151 | """ 152 | return re.sub(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])", "", text) 153 | 154 | 155 | def parse_file(text): 156 | """ 157 | Parse all messages in a file 158 | 159 | Returns the fields in a dict. 160 | """ 161 | # regex required to allow same group names 162 | import regex # pylint: disable=import-outside-toplevel 163 | 164 | patterns = [pattern.pattern for pattern in PATTERNS] 165 | # patterns = [PATTERNS[0].pattern] 166 | 167 | full_regex = "(?:(?:" + (")|(?:".join(patterns)) + "))" 168 | results = [] 169 | 170 | for fields in regex.finditer( 171 | full_regex, strip_ansi(text), regex.MULTILINE 172 | ): 173 | if not fields: 174 | continue 175 | result = fields.groupdict() 176 | 177 | if len(result) == 0: 178 | continue 179 | severity = result.get("severity", None) 180 | confidence = result.pop("confidence", None) 181 | 182 | if confidence is not None: 183 | # Convert confidence level of cpplint 184 | # to warning, etc. 185 | confidence = int(confidence) 186 | 187 | if confidence <= 1: 188 | severity = SEVERITY_NOTICE 189 | elif confidence >= 5: 190 | severity = SEVERITY_ERROR 191 | else: 192 | severity = SEVERITY_WARNING 193 | 194 | if severity is None: 195 | severity = SEVERITY_ERROR 196 | else: 197 | severity = severity.lower() 198 | 199 | if severity in ["info", "style"]: 200 | severity = SEVERITY_NOTICE 201 | 202 | result["severity"] = severity 203 | 204 | results.append(result) 205 | 206 | return results 207 | 208 | 209 | def parse_message(message): 210 | """ 211 | Parse message until it matches a pattern. 212 | 213 | Returns the fields in a dict. 214 | """ 215 | for pattern in PATTERNS: 216 | fields = pattern.match(message) 217 | if not fields: 218 | continue 219 | result = fields.groupdict() 220 | if len(result) == 0: 221 | continue 222 | 223 | if "confidence" in result: 224 | # Convert confidence level of cpplint 225 | # to warning, etc. 226 | confidence = int(result["confidence"]) 227 | del result["confidence"] 228 | 229 | if confidence <= 1: 230 | severity = SEVERITY_NOTICE 231 | elif confidence >= 5: 232 | severity = SEVERITY_ERROR 233 | else: 234 | severity = SEVERITY_WARNING 235 | result["severity"] = severity 236 | 237 | if "severity" not in result: 238 | result["severity"] = SEVERITY_ERROR 239 | else: 240 | result["severity"] = result["severity"].lower() 241 | 242 | if result["severity"] in ["info", "style"]: 243 | result["severity"] = SEVERITY_NOTICE 244 | 245 | return result 246 | 247 | # Nothing matched 248 | return None 249 | 250 | 251 | def add_error_entry( # pylint: disable=too-many-arguments 252 | root, 253 | severity, 254 | file_name, 255 | line=None, 256 | column=None, 257 | message=None, 258 | source=None, 259 | root_path=None, 260 | ): 261 | """ 262 | Add error information to the CheckStyle output being created. 263 | """ 264 | file_element = find_or_create_file_element( 265 | root, file_name, root_path=root_path 266 | ) 267 | error_element = ET.SubElement(file_element, "error") 268 | error_element.set("severity", severity) 269 | if line: 270 | error_element.set("line", line) 271 | if column: 272 | error_element.set("column", column) 273 | if message: 274 | error_element.set("message", message) 275 | if source: 276 | # To verify if this is a valid attribute 277 | error_element.set("source", source) 278 | 279 | 280 | def find_or_create_file_element(root, file_name: str, root_path=None): 281 | """ 282 | Find/create file element in XML document tree. 283 | """ 284 | 285 | if root_path is not None: 286 | file_name = file_name.removeprefix(root_path) 287 | for file_element in root.findall("file"): 288 | if file_element.get("name") == file_name: 289 | return file_element 290 | file_element = ET.SubElement(root, "file") 291 | file_element.set("name", file_name) 292 | return file_element 293 | 294 | 295 | def main(): 296 | """ 297 | Parse the script arguments and get the conversion done. 298 | """ 299 | parser = argparse.ArgumentParser( 300 | description="Convert messages to Checkstyle XML format." 301 | ) 302 | parser.add_argument( 303 | "input", help="Input file. Use '-' for stdin.", nargs="?", default="-" 304 | ) 305 | parser.add_argument( 306 | "output", 307 | help="Output file. Use '-' for stdout.", 308 | nargs="?", 309 | default="-", 310 | ) 311 | parser.add_argument( 312 | "-i", 313 | "--in", 314 | dest="input_named", 315 | help="Input filename. Overrides positional input.", 316 | ) 317 | parser.add_argument( 318 | "-o", 319 | "--out", 320 | dest="output_named", 321 | help="Output filename. Overrides positional output.", 322 | ) 323 | parser.add_argument( 324 | "--root", 325 | metavar="ROOT_PATH", 326 | help="Root directory to remove from file paths." 327 | " Defaults to working directory.", 328 | default=os.getcwd(), 329 | ) 330 | 331 | args = parser.parse_args() 332 | 333 | if args.input == "-" and args.input_named: 334 | with open(args.input_named, encoding="utf_8") as input_file: 335 | text = input_file.read() 336 | elif args.input != "-": 337 | with open(args.input, encoding="utf_8") as input_file: 338 | text = input_file.read() 339 | else: 340 | text = sys.stdin.read() 341 | 342 | root_path = os.path.join(args.root, "") 343 | 344 | try: 345 | checkstyle_xml = convert_text_to_checkstyle(text, root_path=root_path) 346 | except ImportError: 347 | checkstyle_xml = convert_to_checkstyle( 348 | re.split(r"[\r\n]+", text), root_path=root_path 349 | ) 350 | 351 | if args.output == "-" and args.output_named: 352 | with open(args.output_named, "w", encoding="utf_8") as output_file: 353 | output_file.write(checkstyle_xml) 354 | elif args.output != "-": 355 | with open(args.output, "w", encoding="utf_8") as output_file: 356 | output_file.write(checkstyle_xml) 357 | else: 358 | print(checkstyle_xml) 359 | 360 | 361 | if __name__ == "__main__": 362 | main() 363 | -------------------------------------------------------------------------------- /.github/workflows/pre-commit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: pre-commit 3 | on: 4 | pull_request: 5 | push: 6 | jobs: 7 | pre-commit: 8 | runs-on: ubuntu-latest 9 | env: 10 | LOG_TO_CS: .github/logToCs.py 11 | RAW_LOG: pre-commit.log 12 | CS_XML: pre-commit.xml 13 | steps: 14 | - name: Install required tools 15 | run: sudo apt-get update && sudo apt-get install cppcheck 16 | if: false 17 | - uses: actions/checkout@v4 18 | - name: Create requirements.txt if no requirements.txt or pyproject.toml 19 | run: |- 20 | [ -r requirements.txt ] || [ -r pyproject.toml ] || touch requirements.txt 21 | - uses: actions/setup-python@v4 22 | with: 23 | cache: pip 24 | python-version: '3.11' 25 | - run: python -m pip install pre-commit regex 26 | - uses: actions/cache/restore@v3 27 | with: 28 | path: ~/.cache/pre-commit/ 29 | key: pre-commit-4|${{ env.pythonLocation }}|${{ hashFiles('.pre-commit-config.yaml') 30 | }} 31 | - name: Run pre-commit hooks 32 | run: | 33 | set -o pipefail 34 | pre-commit gc 35 | pre-commit run --show-diff-on-failure --color=always --all-files | tee ${RAW_LOG} 36 | - name: Convert Raw Log to CheckStyle format 37 | if: ${{ failure() }} 38 | run: | 39 | python ${LOG_TO_CS} ${RAW_LOG} ${CS_XML} 40 | - name: Annotate Source Code with Messages 41 | uses: staabm/annotate-pull-request-from-checkstyle-action@v1 42 | if: ${{ failure() }} 43 | with: 44 | files: ${{ env.CS_XML }} 45 | notices-as-warnings: true # optional 46 | prepend-filename: true # optional 47 | - uses: actions/cache/save@v3 48 | if: ${{ always() }} 49 | with: 50 | path: ~/.cache/pre-commit/ 51 | key: pre-commit-4|${{ env.pythonLocation }}|${{ hashFiles('.pre-commit-config.yaml') 52 | }} 53 | - name: Provide log as artifact 54 | uses: actions/upload-artifact@v4 55 | if: ${{ always() }} 56 | with: 57 | name: precommit-logs 58 | path: | 59 | ${{ env.RAW_LOG }} 60 | ${{ env.CS_XML }} 61 | retention-days: 2 62 | -------------------------------------------------------------------------------- /.github/workflows/python-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflows will upload a Python Package using Twine when a release is created 2 | # For more information see: https://help.github.com/en/actions/language-and-framework-guides/using-python-with-github-actions#publishing-to-package-registries 3 | 4 | name: Upload Python Package 5 | 6 | on: 7 | push: 8 | tags: 9 | - "v*" 10 | 11 | jobs: 12 | deploy: 13 | 14 | runs-on: ubuntu-latest 15 | container: setsoft/kicad_pybuild:latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | 20 | - name: Build and publish 21 | env: 22 | TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} 23 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} 24 | run: | 25 | python3 setup.py sdist bdist_wheel 26 | twine upload dist/* 27 | -------------------------------------------------------------------------------- /.github/workflows/pythonapp.yml: -------------------------------------------------------------------------------- 1 | name: Regression tests 2 | 3 | on: 4 | push: 5 | paths: 6 | - '**.py' 7 | - 'tests/**' 8 | - '.github/workflows/pythonapp.yml' 9 | pull_request: 10 | paths: 11 | - '**.py' 12 | - 'tests/**' 13 | - '.github/workflows/pythonapp.yml' 14 | 15 | jobs: 16 | test: 17 | runs-on: ubuntu-latest 18 | container: setsoft/kicad_auto_test:ki7 19 | 20 | steps: 21 | - uses: actions/checkout@v3 22 | - name: Lint with flake8 23 | run: | 24 | # stop the build if there are Python syntax errors or undefined names 25 | flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 26 | # exit-zero treats all errors as warnings. 27 | flake8 . --count --exit-zero --statistics 28 | - name: Test KiCad 7 29 | shell: bash 30 | run: | 31 | make test_server 32 | - name: Store results 33 | if: ${{ always() }} 34 | uses: actions/upload-artifact@v4 35 | with: 36 | name: Test_Output_ki6 37 | path: output 38 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | 8 | jobs: 9 | check: 10 | runs-on: ubuntu-latest 11 | outputs: 12 | is_tag: ${{ steps.check.outputs.is_tag }} 13 | 14 | steps: 15 | - name: Check if it's a tagged release 16 | id: check 17 | run: | 18 | REF=${{ github.ref }} 19 | URL="https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/tags/${REF##*/}" 20 | StatusCode=$(curl -o /dev/null -I -L -s -w "%{http_code}" -X GET -G $URL) 21 | echo $StatusCode 22 | if [[ "$StatusCode" == 200 ]]; then 23 | echo "This is tagged release!" 24 | echo "IS_TAG=no" >> $GITHUB_OUTPUT 25 | else 26 | echo "This is a tag not release!" 27 | echo "IS_TAG=yes" >> $GITHUB_OUTPUT 28 | fi 29 | 30 | build: 31 | runs-on: ubuntu-latest 32 | container: setsoft/kicad_pybuild:latest 33 | needs: check 34 | if: ${{ needs.check.outputs.IS_TAG == 'yes' }} 35 | outputs: 36 | DEB_NAME: ${{ steps.mdeb.outputs.DEB_NAME }} 37 | 38 | steps: 39 | - name: Checkout 40 | uses: actions/checkout@v1 41 | 42 | - name: Build 43 | id: mdeb 44 | run: | 45 | make deb 46 | mv ../*.deb . 47 | echo "DEB_NAME="`ls *.deb` >> $GITHUB_OUTPUT 48 | mkdir output 49 | mv *.deb output 50 | 51 | - name: Store 52 | uses: actions/upload-artifact@v4 53 | with: 54 | name: package 55 | path: output 56 | 57 | release: 58 | needs: build 59 | runs-on: ubuntu-latest 60 | steps: 61 | - name: Checkout 62 | uses: actions/checkout@v1 63 | 64 | - name: Retrieve 65 | uses: actions/download-artifact@v4 66 | with: 67 | name: package 68 | 69 | - name: Release 70 | uses: docker://antonyurchenko/git-release:latest 71 | env: 72 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 73 | DRAFT_RELEASE: "false" 74 | PRE_RELEASE: "false" 75 | CHANGELOG_FILE: "CHANGELOG.md" 76 | with: 77 | args: | 78 | package/${{needs.build.outputs.DEB_NAME}} 79 | 80 | - name: Trigger kicad_auto image build 81 | run: | 82 | curl -X POST -u "${{secrets.PAT_USERNAME}}:${{secrets.PAT_TOKEN}}" -H "Accept: application/vnd.github.v3+json" -H "Content-Type: application/json" https://api.github.com/repos/INTI-CMNB/kicad_auto/dispatches --data '{"event_type": "build_k5"}' 83 | curl -X POST -u "${{secrets.PAT_USERNAME}}:${{secrets.PAT_TOKEN}}" -H "Accept: application/vnd.github.v3+json" -H "Content-Type: application/json" https://api.github.com/repos/INTI-CMNB/kicad_auto/dispatches --data '{"event_type": "build_k6"}' 84 | 85 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | cache/ 2 | diff.epr 3 | output/ 4 | test/ 5 | __pycache__/ 6 | .coverage 7 | pp 8 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | files: ^(.*\.(py|json|md|sh|yaml|cfg|txt))$ 3 | exclude: ^(\.[^/]*cache/.*|.*/_user.py)$ 4 | repos: 5 | - repo: https://github.com/executablebooks/mdformat 6 | # Do this before other tools "fixing" the line endings 7 | rev: 0.7.17 8 | hooks: 9 | - id: mdformat 10 | name: Format Markdown 11 | entry: mdformat # Executable to run, with fixed options 12 | language: python 13 | exclude: CHANGELOG.md 14 | types: [markdown] 15 | args: [--wrap, '78', --number] 16 | additional_dependencies: 17 | - mdformat-toc 18 | - mdformat-beautysh 19 | # -mdformat-shfmt 20 | # -mdformat-tables 21 | - mdformat-config 22 | - mdformat-black 23 | - mdformat-web 24 | - mdformat-gfm 25 | - setuptools 26 | - repo: https://github.com/pre-commit/pre-commit-hooks 27 | rev: v4.5.0 28 | hooks: 29 | - id: no-commit-to-branch 30 | args: [--branch, main] 31 | - id: check-yaml 32 | args: [--unsafe] 33 | - id: debug-statements 34 | - id: end-of-file-fixer 35 | - id: trailing-whitespace 36 | - id: check-json 37 | - id: mixed-line-ending 38 | - id: check-builtin-literals 39 | - id: check-ast 40 | - id: check-merge-conflict 41 | - id: check-executables-have-shebangs 42 | - id: check-shebang-scripts-are-executable 43 | - id: check-docstring-first 44 | - id: fix-byte-order-marker 45 | - id: check-case-conflict 46 | # - id: check-toml 47 | - repo: https://github.com/adrienverge/yamllint.git 48 | rev: v1.33.0 49 | hooks: 50 | - id: yamllint 51 | args: 52 | - --no-warnings 53 | - -d 54 | - '{extends: relaxed, rules: {line-length: {max: 90}}}' 55 | - repo: https://github.com/lovesegfault/beautysh.git 56 | rev: v6.2.1 57 | hooks: 58 | - id: beautysh 59 | additional_dependencies: 60 | - setuptools 61 | # - repo: https://github.com/asottile/pyupgrade 62 | # rev: v3.15.0 63 | # hooks: 64 | # - id: pyupgrade 65 | # - repo: https://github.com/psf/black 66 | # rev: 22.6.0 67 | # hooks: 68 | # - id: black 69 | # - repo: https://github.com/Lucas-C/pre-commit-hooks-bandit 70 | # rev: v1.0.6 71 | # hooks: 72 | # - id: python-bandit-vulnerability-check 73 | # args: [--skip, 'B101,B311', --recursive, .] 74 | # 75 | # - repo: https://github.com/fsouza/autoflake8 76 | # rev: v0.3.2 77 | # hooks: 78 | # - id: autoflake8 79 | # args: 80 | # - -i 81 | # - -r 82 | # - --expand-star-imports 83 | # - . 84 | # - repo: https://github.com/PyCQA/flake8 85 | # rev: 4.0.1 86 | # hooks: 87 | # - id: flake8 88 | # additional_dependencies: 89 | # - pyproject-flake8==0.0.1a2 90 | # - flake8-bugbear==22.1.11 91 | # - flake8-comprehensions==3.8.0 92 | # - flake8_2020==1.6.1 93 | # - mccabe==0.6.1 94 | # - pycodestyle==2.8.0 95 | # - pyflakes==2.4.0 96 | # - repo: https://github.com/PyCQA/isort 97 | # rev: 5.10.1 98 | # hooks: 99 | # - id: isort 100 | - repo: https://github.com/codespell-project/codespell 101 | rev: v2.2.6 102 | hooks: 103 | - id: codespell 104 | args: 105 | # - --builtin=clear,rare,informal,usage,code,names,en-GB_to_en-US 106 | - --builtin=clear,rare,informal,usage,code,names 107 | - --ignore-words-list=hass,master 108 | - --skip="./.*" 109 | - --quiet-level=2 110 | # - repo: https://github.com/pylint-dev/pylint 111 | # rev: v3.0.3 112 | # hooks: 113 | # - id: pylint 114 | # args: 115 | # - --reports=no 116 | # - --py-version=3.7 117 | # #exclude: ^$ 118 | # - repo: https://github.com/pre-commit/mirrors-mypy 119 | # rev: v0.961 120 | # hooks: 121 | # - id: mypy 122 | # args: 123 | # - --ignore-missing-imports 124 | # - --install-types 125 | # - --non-interactive 126 | # - --check-untyped-defs 127 | # - --show-error-codes 128 | # - --show-error-context 129 | # additional_dependencies: 130 | # - zigpy==0.43.0 131 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | 8 | ## [2.5.8] - 2025-04-03 9 | ### Added 10 | * Support for broken KiCad 9.0.1 (which changed various details in the API) 11 | * Debug verbosity is now passed to children (#17) 12 | 13 | ### Changed 14 | * Now the git plug-in defaults to compare all the schematic pages. 15 | Helps to workaround bugs in KiCad 9 and IMHO is better. 16 | 17 | ## [2.5.7] - 2025-02-11 18 | ### Added 19 | * KiCad 9 experimental support fixes (WIP) 20 | 21 | ## [2.5.6] - 2025-01-08 22 | ### Added 23 | * KiCad 9 experimental support (WIP) 24 | 25 | ## [2.5.5] - 2024-09-12 26 | ### Added 27 | * Workaround for KiCad 8.0.4 new feature: draw black drill marks on technical layers 28 | 29 | ## [2.5.4] - 2024-05-03 30 | ### Added 31 | * Workaround for KiCad 8.0.2 computing hidden text when the GUI enabled it 32 | 33 | ## [2.5.3] - 2024-01-10 34 | ### Added 35 | * New mode "2color" where you can control the added/removed colors 36 | 37 | ## [2.5.2] - 2024-01-09 38 | ### Added 39 | * Smarter cache: changing KiCad or --zones option invalidates the cache 40 | 41 | ### Fixed 42 | * KiRi mode for KiCad 5: 43 | - Plotting the worksheet makes KiCad crash, disabled 44 | 45 | ## [2.5.1] - 2024-01-03 46 | ### Added 47 | * Option to un/fill zones 48 | 49 | 50 | ## [2.5.0] - 2023-12-20 51 | ### Added 52 | * Support to generate KiRi SVGs *Experimental* (--kiri_mode) 53 | 54 | ### Fixed 55 | * PNGs not removed when no diff and --only_different was specified 56 | 57 | 58 | ## [2.4.7] - 2023-03-29 59 | ### Fixed 60 | * The message about different page size for the red_green mode 61 | 62 | ## [2.4.6] - 2023-03-29 63 | ### Fixed 64 | - Problems when comparing two PCB/SCH with different page size using the 65 | red_green mode. (#5) 66 | 67 | ## [2.4.5] - 2023-03-07 68 | ### Changed 69 | - When the bounding box of the PCB changes we make the diff using a 1:1 plot 70 | (not using autoscale). (#4) 71 | 72 | 73 | ## [2.4.4] - 2023-02-13 74 | ### Added 75 | - KiCad 7 support 76 | 77 | 78 | ## [2.4.3] - 2022-10-14 79 | ### Added 80 | - Option to skip the test for input files. 81 | Useful to compare from cache. 82 | - Allow to compare two multi-page schematics even when their base name is 83 | different. 84 | 85 | ### Changed 86 | - When comparing multiple sheets using the SVG mode the displayed name of the 87 | sheet is like the sheet path displayed by KiCad 88 | 89 | 90 | ## [2.4.2] - 2022-10-05 91 | ### Added 92 | - Option to skip pages without diff 93 | 94 | ### Fixed 95 | - Problems when using the plug-in and comparing uncommitted stuff. 96 | 97 | 98 | ## [2.4.1] - 2022-08-31 99 | ### Added 100 | - Support for user defined layer names. 101 | 102 | ### Fixed 103 | - The 'only in XXX' message worked only the first time, after caching the 104 | files it wasn't informed anymore. 105 | 106 | ### Changed 107 | - Now PCB layers are cached by layer ID, not name. 108 | - Lines in ex/include lists that begin with # are just ignored. 109 | Before we let them fail to match because the names weren't valid. 110 | This avoids confusing warnings. 111 | 112 | ## [2.4.0] - Unreleased 113 | ### Added 114 | - Support for adding/removing SCH sheets 115 | 116 | ## [2.3.1] - 2022-08-25 117 | ### Fixed 118 | - KiCad 5 problems with inner layers (undefined names) 119 | 120 | ## [2.3.0] - 2022-08-25 121 | ### Added 122 | - Support for adding/removing PCB layers 123 | - Option to specify the layers to use, instead of excluded 124 | 125 | ### Changed 126 | - The red/green colors to match text mode diff tools 127 | 128 | ## [2.2.0] - 2022-08-24 129 | ### Added 130 | - Multi-page schematic support. 131 | - More control for the output name and generated files. 132 | - Option to just populate the cache. 133 | - Support for layer numbers in the exclude file. 134 | 135 | ### Changed 136 | - Error codes for old/new file invalid. 137 | The old ones are the reserved by Python. 138 | 139 | ## [2.1.0] - 2022-08-15 140 | ### Added 141 | - Stats diff mode 142 | - Alternative support for Ghostscript 143 | 144 | ## [2.0.0] - 2022-08-14 145 | ### Added 146 | - Support for schematics. 147 | 148 | ## [1.2.0] - 2022-08-13 149 | ### Added 150 | - Support for KiCad 6. 151 | 152 | ## [1.1.0] - 2020-03-03 153 | ### Added 154 | - Script to init the repo. 155 | 156 | ## [1.0.0] - 2020-03-03 157 | ### Added 158 | - Initial release 159 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make 2 | PYTEST?=pytest-3 3 | prefix?=/usr/local 4 | 5 | all: 6 | 7 | deb: 8 | DEB_BUILD_OPTIONS=nocheck fakeroot dpkg-buildpackage -uc -b 9 | 10 | deb_clean: 11 | fakeroot debian/rules clean 12 | 13 | install: 14 | install -D kicad-diff.py $(DESTDIR)$(prefix)/bin/kicad-diff.py 15 | install -D kicad-git-diff.py $(DESTDIR)$(prefix)/bin/kicad-git-diff.py 16 | install -D kicad-diff-init.py $(DESTDIR)$(prefix)/bin/kicad-diff-init.py 17 | 18 | test: 19 | rm -rf output 20 | $(PYTEST) --test_dir output 21 | 22 | t1: 23 | rm -rf pp 24 | -$(PYTEST) --log-cli-level debug -k "$(SINGLE_TEST)" --test_dir pp 25 | @echo "********************" Output 26 | @cat pp/*/output.txt 27 | @echo "********************" Error 28 | @tail -n 30 pp/*/error.txt 29 | 30 | test_server: 31 | pytest-3 --test_dir output 32 | 33 | clean: 34 | 35 | distclean: clean 36 | 37 | uninstall: 38 | -rm -f $(DESTDIR)$(prefix)/bin/kicad-diff.py 39 | -rm -f $(DESTDIR)$(prefix)/bin/kicad-git-diff.py 40 | -rm -f $(DESTDIR)$(prefix)/bin/kicad-diff-init.py 41 | 42 | .PHONY: all install clean distclean uninstall deb deb_clean 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # KiDiff (kicad-diff) 2 | 3 | This program generates a PDF file showing the changes between two KiCad PCB or 4 | SCH files. 5 | 6 | PCBs are plotted into PDF files, one for each layer. Then ImageMagick is used 7 | to compare the layers and PNG files are generated. The PNGs are finally 8 | assembled into one PDF file and the default PDF reader is invoked. 9 | 10 | All the intermediate files are generated in the system temporal directory and 11 | removed as soon as the job is finished. You can cache the layers PDFs 12 | specifying a cache directory and keep the resulting diff images specifying an 13 | output directory. 14 | 15 | For SCHs the process is similar, but using KiAuto. Note that one schematic is 16 | compared at a time. The `--all_pages` option allows comparing multiple pages. 17 | In this case adding or removing pages is only supported when the 18 | `rsvg-convert` tool is available. Also note that this can't be done by the git 19 | plug-in because git extracts only the file you want to compare. Consult the 20 | advanced use explanation. 21 | 22 | The default resolution for the images is 150 DPI. It can be increased for 23 | better images, at the cost of (exponentially) longer execution times. You can 24 | provide a smaller resolution for faster processing. For high resolution you 25 | could need to configure the ImageMagick limits. Consult the 'identify -list 26 | resource' command. 27 | 28 | # Installation 29 | 30 | ## Dependencies 31 | 32 | In order to run the scripts you need: 33 | 34 | - Python 3.5 or newer 35 | - KiCad 5.1 or newer 36 | - Python3 wxWidgets (i.e. python3-wxgtk4.0). This is usually installed with 37 | KiCad. 38 | - ImageMagick tools (i.e. imagemagick Debian package). Used to manipulate 39 | images and create PDF files. 40 | - pdftoppm tool (i.e. poppler-utils Debian package). Used to decode PDF files. 41 | - Alternative: Ghostscript (slower and worst results) 42 | - rsvg-convert tool (i.e. librsvg2-bin Debian package). Needed to compare 43 | schematics with multiple pages. Converts SVG files to PNGs. 44 | - xdg-open tool (i.e. xdg-utils Debian package). Used to open the PDF viewer. 45 | - [KiAuto](https://github.com/INTI-CMNB/KiAuto/). Used to print the schematic 46 | in PDF format. 47 | 48 | In a Debian/Ubuntu system you'll first need to add this 49 | [repo](https://set-soft.github.io/debian/) and then use: 50 | 51 | ```shell 52 | $ sudo apt-get install kidiff 53 | ``` 54 | 55 | Arch Linux can install 56 | [kicad-pcb-diff](https://aur.archlinux.org/packages/kidiff) using the AUR 57 | repository: 58 | 59 | ```bash 60 | $ yay -S kidiff 61 | ``` 62 | 63 | Note: if you are using Debian, or some derived distro like Ubuntu, you can 64 | find a Debian package in the releases section. But using the repo is simpler. 65 | 66 | ## Standalone use 67 | 68 | 1. As root run: 69 | 70 | ```shell 71 | # make install 72 | ``` 73 | 74 | The scripts will be copied to */usr/local/bin*. If you want to install the 75 | scripts in */usr/bin* run 76 | 77 | ```shell 78 | # make prefix=/usr install 79 | ``` 80 | 81 | Note: if you are using Debian, or some derived distro like Ubuntu, you can 82 | find a Debian package in the releases section. 83 | 84 | ## Docker image 85 | 86 | You can find a docker image containing KiDiff and other KiCad tools 87 | [here](https://github.com/INTI-CMNB/kicad_auto). 88 | 89 | ## Git plug-in 90 | 91 | 1. Install the scripts 92 | 2. To initialize a repo just run the *kicad-diff-init.py* script from the root 93 | of the repo.\ 94 | This will configure the repo to read extra configuration from 95 | the *.gitconfig* file.\ 96 | It will also associate the *kicad_pcb* file 97 | extension with the *kicad-git-diff.py* script. 98 | 3. The initialization script will create a list of layers to be excluded in 99 | the *.kicad-git-diff* file.\ 100 | Review this file and adjust it to your needs. 101 | Lines starting with *#* will be ignored. 102 | 103 | Once configured the tool will be used every time you do a diff using *git*. 104 | 105 | ### Temporarily disabling the git plug-in 106 | 107 | Sometimes the graphics diff is not what you want. To disable it just invoke 108 | `git diff` using the `--no-ext-diff` option. It will force the text mode 109 | original diff behavior. 110 | 111 | ### Disabling the pager 112 | 113 | Git will use a pager to allow scrolling back and forth. This could be annoying 114 | because you must exit it, the git option `--no-pager` disables it. Example: 115 | 116 | ```shell 117 | $ git --no-pager diff XXXXXX.kicad_pcb 118 | ``` 119 | 120 | ## PDF conversion policy 121 | 122 | On some systems (i.e. Debian) ImageMagick disables PDF manipulation in its 123 | *policy.xml* configuration file. Comment or remove lines like this: 124 | 125 | ``` 126 | 127 | ``` 128 | 129 | On Debian 11/12 this applies to the */etc/ImageMagick-6/policy.xml* and/or 130 | */etc/ImageMagick-7/policy.xml* file. 131 | 132 | # Usage 133 | 134 | The *kicad-git-diff.py* is a plug-in for *git* so you just need to configure 135 | *git* and then it becomes transparent. If you need to create a diff between 136 | two files outside *git* you can use the *kicad-diff.py* script. 137 | 138 | You have to provide the name of the two PCB/SCHs to be compared. The first is 139 | the old one, the reference, and the second is the new, the one you want to 140 | compare. The additional command line options are: 141 | 142 | ## --help 143 | 144 | Shows a detailed list of the available options. 145 | 146 | ## --all_pages 147 | 148 | Compare all pages for a schematic. Note that the tool doesn't currently 149 | support adding or removing sheets, both documents must have the same amount of 150 | pages. 151 | 152 | ## --cache_dir 153 | 154 | The PCB/SCH files are plotted to PDF files. One PDF file for layer. To avoid 155 | plotting them over and over you can specify a cache directory to store the 156 | PDFs. 157 | 158 | ## --diff_mode 159 | 160 | Selects the mechanism used to represent the differences: 161 | 162 | - **red_green** this is the default mode. Here we try to mimic the colored 163 | text mode diff tools. Things added to the *old* file are represented in 164 | green and things removed in red. 165 | - **stats** in this mode all difference are represented in red. We compute the 166 | amount of pixels that are different, you can use this value to determine if 167 | the changes are significant. See the `--fuzz` and `--threshold` options. 168 | 169 | ## --exclude 170 | 171 | Specifies the name of a file containing a list of layers to be excluded. Each 172 | line of the file is interpreted as a layer name. An example for this file 173 | could be: 174 | 175 | ```raw 176 | B.Adhes 177 | F.Adhes 178 | Cmts.User 179 | Eco1.User 180 | Eco2.User 181 | Edge.Cuts 182 | Margin 183 | ``` 184 | 185 | You can also use the KiCad internal layer ID number. This will avoid warnings 186 | about undefined layer names. 187 | 188 | Note that when using the *git* plug-in the script looks for a file named 189 | *.kicad-git-diff* at the root of the repo. 190 | 191 | Using this you can reduce the time wasted computing diffs for empty or useless 192 | layers. 193 | 194 | Note that lines starting with # are skipped. 195 | 196 | See also `--layers` 197 | 198 | ## --force_gs 199 | 200 | KiDiff uses poppler utils if they are available. When they aren't KiDiff can 201 | use Ghostscript. The results using poppler are better and the process is 202 | faster, but you can choose to use Ghostscript using this option. 203 | 204 | ## --fuzz 205 | 206 | When comparing using the *stats* mode (see `--diff_mode`) this option controls 207 | how strict is the color comparison. The default is to tolerate 5 % of error in 208 | the colors. Enlarge it if you want to ignore bigger differences in the colors. 209 | 210 | ## --keep_pngs 211 | 212 | Don't remove the individual PNGs. Complements `--output_dir`. They are usually 213 | removed as soon as we get the output PDF. 214 | 215 | ## --layers 216 | 217 | Specifies the name of a file containing a list of layers to be included. This 218 | option works similar to `--exclude` but we process only the layers indicated 219 | here. 220 | 221 | Important: when this list is only composed by layer numbers KiDiff skips any 222 | check and uses this list of layers, even if they aren't defined in any of the 223 | specified PCBs. Otherwise you can get warnings about names not defined. 224 | 225 | `--layers` and `--exclude` are mutually exclusive. 226 | 227 | ## --new_file_hash 228 | 229 | This is the equivalent of the *--old_file_hash* option used for the new 230 | PCB/SCH file. 231 | 232 | ## --no_reader 233 | 234 | Use it to avoid invoking the default PDF viewer. Note that you should also 235 | provide an output directory using *--output_dir*. 236 | 237 | The default PDF reader is invoked using *xdg-open* 238 | 239 | ## --old_file_hash 240 | 241 | The plotted PDF files for each layer are stored in the cache directory using a 242 | SHA1 of the PCB/SCH file as name for the directory. You can specify another 243 | hash here to identify the old PCB/SCH file. 244 | 245 | The *git* plug-in uses the hash provided by *git* instead of the SHA1 for the 246 | file. 247 | 248 | ## --output_dir 249 | 250 | Five seconds after invoking the PDF viewer the output files are removed. If 251 | you want to keep them for later review, or five seconds isn't enough for your 252 | system, you can specify a directory to store the generated files. 253 | 254 | Note: individual PNGs are always removed, consult `--keep_pngs` 255 | 256 | ## --output_name 257 | 258 | Used to complement `--output_dir`. The default name is `diff.pdf` 259 | 260 | ## --resolution 261 | 262 | The PDF files are converted to bitmaps to be compared. The default resolution 263 | for these bitmaps is 150 DPIs. This is a compromise between speed and 264 | legibility. For faster compares you can use a smaller resolution. For detailed 265 | comparison you can use a higher resolution. Be careful because the time is 266 | increased exponentially. You can also run out of resources. In particular 267 | ImageMagick defines some limits to the disk used for operations. These limits 268 | can be very low for default installations. You can consult the limits using 269 | the following command: 270 | 271 | `identify -list resource` 272 | 273 | Consult ImageMagick documentation in order to increase them. 274 | 275 | ## --threshold 276 | 277 | In the *stats* mode this option can make KiDiff to return an error value if 278 | the difference is bigger than the specified threshold. Indicating 0 means that 279 | we don't look for errors, KiDiff always returns 0. 280 | 281 | ## -v/--verbose 282 | 283 | Increases the level of verbosity. The default is a quite mode, specifying one 284 | level (*-v*) you'll get information messages about what's going on. If you 285 | increase the level to two (*-vv*) you'll get very detailed information, most 286 | probably useful only to debug problems. 287 | 288 | ## --version 289 | 290 | Print the script version, copyright and license. 291 | 292 | # Advanced use 293 | 294 | `kicad-diff.py` can be run using the `--only_cache` option. In this mode the 295 | *new_file* argument can be the same as the *old_file* argument. This operation 296 | is used to store the images, but skip the comparison. In order to work you 297 | must specify a valid `cache_dir`. You should also take note of the hash used 298 | by `kicad-diff.py`, or just provide one using the `--old_file_hash`. 299 | 300 | You can then compare two files using the cached images, you just need to 301 | provide the hashes you want to compare. The *old_file* and *new_file* won't be 302 | used. 303 | 304 | # Similar tools 305 | 306 | ## KiCad-Diff 307 | 308 | [KiCad-Diff](https://github.com/Gasman2014/KiCad-Diff) is a diff tool for 309 | PCBs, is more oriented to GUI use. It has support not only for `git` but also 310 | for `fossil` and `svn`. You can navigate the diffs and get information about 311 | added or removed items, all using a browser. And is fast (all is handled using 312 | SVGs). 313 | 314 | It doesn't support schematics, can't be directly used as a git plug-in 315 | (perhaps using some wrapper), can't measure the ammout of difference and 316 | doesn't generate a file to see the differences (only browsable). 317 | 318 | # Credits and notes 319 | 320 | - This script is strongly based on Jesse Vincent 321 | [work](https://github.com/obra/kicad-tools). 322 | - I borrowed the command to compare two images from Brecht Machiels. In 323 | particular from his 324 | [diffpdf.sh](https://gist.github.com/brechtm/891de9f72516c1b2cbc1) tool. 325 | - I'm not a Python programmer, stackoverflow helps me ... 326 | -------------------------------------------------------------------------------- /debian/README.Debian: -------------------------------------------------------------------------------- 1 | kicad-pcb-diff for Debian 2 | 3 | This is a simple package for the KiCad PCB diff tool. 4 | 5 | -- Salvador Eduardo Tropea Tue, 03 Mar 2020 07:17:31 -0300 6 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | kidiff (2.5.8-1) stable; urgency=medium 2 | 3 | * The plug-in now compares all schematic pages 4 | * Added support for broken API in KiCad 9.0.1 5 | 6 | -- Salvador Eduardo Tropea Thu, 20 Mar 2025 09:25:54 -0300 7 | 8 | kidiff (2.5.7-1) stable; urgency=medium 9 | 10 | * Fix no copper layer detection, needed for KiCad 9 11 | 12 | -- Salvador Eduardo Tropea Tue, 11 Feb 2025 10:05:11 -0300 13 | 14 | kidiff (2.5.6-1) stable; urgency=medium 15 | 16 | * KiCad 9 experimental support (WIP) 17 | 18 | -- Salvador Eduardo Tropea Wed, 08 Jan 2025 07:40:24 -0300 19 | 20 | kidiff (2.5.5-1) stable; urgency=medium 21 | 22 | * Visible drill marks issue with KiCad 8.0.4 23 | 24 | -- Salvador Eduardo Tropea Thu, 12 Sep 2024 10:13:07 -0300 25 | 26 | kidiff (2.5.4-1) stable; urgency=medium 27 | 28 | * Hidden text workaround for KiCad 8.0.2 29 | 30 | -- Salvador Eduardo Tropea Fri, 03 May 2024 12:54:59 -0300 31 | 32 | kidiff (2.5.3-1) stable; urgency=medium 33 | 34 | * Added new mode "2color" where you can control the added/removed colors 35 | 36 | -- Salvador Eduardo Tropea Wed, 10 Jan 2024 09:54:13 -0300 37 | 38 | kidiff (2.5.2-1) stable; urgency=medium 39 | 40 | * Added Smarter cache: changing KiCad or --zones option invalidates the cache 41 | * Fixed KiRi mode for KiCad 5: Plotting the worksheet makes KiCad crash, 42 | disabled 43 | 44 | -- Salvador Eduardo Tropea Thu, 09 Jan 2024 10:41:43 -0300 45 | 46 | kidiff (2.5.1-1) stable; urgency=medium 47 | 48 | * Added option to un/fill zones 49 | 50 | -- Salvador Eduardo Tropea Fri, 22 Dec 2023 08:25:14 -0300 51 | 52 | kidiff (2.5.0-1) stable; urgency=medium 53 | 54 | * Added support to generate KiRi SVGs 55 | 56 | -- Salvador Eduardo Tropea Tue, 28 Nov 2023 08:21:05 -0300 57 | 58 | kidiff (2.4.7-1) stable; urgency=medium 59 | 60 | * Fixed the message about different page size for the red_green mode 61 | 62 | -- Salvador Eduardo Tropea Wed, 29 Mar 2023 09:20:14 -0300 63 | 64 | kidiff (2.4.6-1) stable; urgency=medium 65 | 66 | * Better behavior when the page sizes doesn't match 67 | 68 | -- Salvador Eduardo Tropea Wed, 29 Mar 2023 08:52:50 -0300 69 | 70 | kidiff (2.4.5-1) stable; urgency=medium 71 | 72 | * Added mechanism to better compare PCBs with changes in the bounding box 73 | 74 | -- Salvador Eduardo Tropea Tue, 07 Mar 2023 11:04:21 -0300 75 | 76 | kidiff (2.4.4-1) stable; urgency=medium 77 | 78 | * KiCad 7 support 79 | 80 | -- Salvador Eduardo Tropea Mon, 13 Feb 2023 12:39:49 -0300 81 | 82 | kidiff (2.4.3-1) stable; urgency=medium 83 | 84 | * Added option to skip the test for input files. 85 | 86 | -- Salvador Eduardo Tropea Wed, 14 Oct 2022 10:18:19 -0300 87 | 88 | kidiff (2.4.2-1) stable; urgency=medium 89 | 90 | * Added option to skip pages without diff 91 | * Fixed problems when using the plug-in and comparing uncommited stuff. 92 | 93 | -- Salvador Eduardo Tropea Wed, 31 Aug 2022 10:25:15 -0300 94 | 95 | kidiff (2.4.1-1) stable; urgency=medium 96 | 97 | * Added support for user defined layer names. 98 | * Fixed the 'only in XXX' message worked only the first time, after caching 99 | the files it wasn't informed anymore. 100 | * Changed: Now PCB layers are cached by layer ID, not name. 101 | * Lines in ex/include lists that begin with # are just ignored. 102 | Before we let them fail to match because the names weren't valid. 103 | This avoids confusing warnings. 104 | 105 | -- Salvador Eduardo Tropea Tue, 30 Aug 2022 13:20:52 -0300 106 | 107 | kidiff (2.4.0-1) stable; urgency=medium 108 | 109 | * Added support for adding/removing SCH sheets 110 | 111 | -- Salvador Eduardo Tropea Fri, 26 Aug 2022 13:05:49 -0300 112 | 113 | kidiff (2.3.1-1) stable; urgency=medium 114 | 115 | * Fixed KiCad 5 problems with inner layers (undefined names) 116 | 117 | -- Salvador Eduardo Tropea Thu, 25 Aug 2022 13:36:23 -0300 118 | 119 | kidiff (2.3.0-1) stable; urgency=medium 120 | 121 | * Added support for adding/removing PCB layers 122 | * Added option to specify the layers to use, instead of excluded 123 | * Changed the red/green colors to match text mode diff tools 124 | 125 | -- Salvador Eduardo Tropea Thu, 25 Aug 2022 08:33:51 -0300 126 | 127 | kidiff (2.2.0-1) stable; urgency=medium 128 | 129 | * Added multi-page schematic support 130 | * Added more control for the output name and generated files 131 | 132 | -- Salvador Eduardo Tropea Mon, 24 Aug 2022 11:05:43 -0300 133 | 134 | kicad-pcb-diff (2.1.0-1) stable; urgency=medium 135 | 136 | * Added stats diff mode 137 | * Added alternative support for Ghostscript 138 | 139 | -- Salvador Eduardo Tropea Mon, 15 Aug 2022 18:27:50 -0300 140 | 141 | kicad-pcb-diff (2.0.0-1) stable; urgency=medium 142 | 143 | * Added SCH support 144 | 145 | -- Salvador Eduardo Tropea Sun, 14 Aug 2022 13:23:10 -0300 146 | 147 | kicad-pcb-diff (1.2.0-1) stable; urgency=medium 148 | 149 | * Added KiCad 6 support 150 | 151 | -- Salvador Eduardo Tropea Sat, 13 Aug 2022 20:40:56 -0300 152 | 153 | kicad-pcb-diff (1.1.0-1) testing; urgency=medium 154 | 155 | * Added script to init the repo. 156 | 157 | -- Salvador Eduardo Tropea Tue, 03 Mar 2020 09:17:18 -0300 158 | 159 | kicad-pcb-diff (1.0.0-1) unstable; urgency=low 160 | 161 | * Initial release. 162 | 163 | -- Salvador Eduardo Tropea Tue, 03 Mar 2020 07:17:31 -0300 164 | -------------------------------------------------------------------------------- /debian/compat: -------------------------------------------------------------------------------- 1 | 11 2 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: kidiff 2 | Section: electronics 3 | Priority: optional 4 | Maintainer: Salvador Eduardo Tropea 5 | Build-Depends: debhelper (>=11~), dh-python, python3-all 6 | Standards-Version: 4.1.4 7 | Homepage: https://github.com/INTI-CMNB/kicad_pcb-diff 8 | X-Python3-Version: >= 3.2 9 | 10 | Package: kidiff 11 | Architecture: all 12 | Multi-Arch: foreign 13 | Depends: ${misc:Depends}, ${python3:Depends}, kicad (>= 5.1.0) | kicad-nightly (>= 5.1.0), imagemagick, poppler-utils | ghostscript, xdg-utils, kiauto, librsvg2-bin 14 | Conflicts: kicad-pcb-diff 15 | Recommends: git, scour 16 | Description: KiCad PCB/SCH diff tool 17 | This package provides a tool to compute the difference between two 18 | PCB/SCH files created with KiCad. 19 | . 20 | It generates a PDF file with images showing the added/removed stuff. 21 | . 22 | Is oriented for command line usage and isn't interactive, i.e. you 23 | must indicate the name of the files. 24 | . 25 | It also includes a plug-in for git. 26 | 27 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: kicad-pcb-diff 3 | Source: 4 | # 5 | # Uncomment the following 2 lines to enable uscan to exclude non-DFSG components 6 | # Files-Excluded: command/non-dfsg.exe 7 | # docs/source/javascripts/jquery-1.7.1.min.js 8 | # 9 | # This is a autogenerated template for debian/copyright. 10 | # 11 | # Edit this accordinng to the "Machine-readable debian/copyright file" as 12 | # http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ . 13 | # 14 | # Generate updated license templates with the "debmake -cc" to STDOUT 15 | # and merge them into debian/copyright as needed. 16 | # 17 | # Please avoid to pick license terms that are more restrictive than the 18 | # packaged work, as it may make Debian's contributions unacceptable upstream. 19 | # 20 | # Please double check copyright with the licensecheck(1) command. 21 | 22 | Files: kicad_pcb-diff.py 23 | kicad_pcb-git-diff.py 24 | Copyright: Copyright 2020, INTI/Salvador E. Tropea 25 | License: GPL 2.0 26 | 27 | #---------------------------------------------------------------------------- 28 | # License file: LICENSE 29 | GNU GENERAL PUBLIC LICENSE 30 | Version 2, June 1991 31 | . 32 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 33 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 34 | Everyone is permitted to copy and distribute verbatim copies 35 | of this license document, but changing it is not allowed. 36 | . 37 | Preamble 38 | . 39 | The licenses for most software are designed to take away your 40 | freedom to share and change it. By contrast, the GNU General Public 41 | License is intended to guarantee your freedom to share and change free 42 | software--to make sure the software is free for all its users. This 43 | General Public License applies to most of the Free Software 44 | Foundation's software and to any other program whose authors commit to 45 | using it. (Some other Free Software Foundation software is covered by 46 | the GNU Lesser General Public License instead.) You can apply it to 47 | your programs, too. 48 | . 49 | When we speak of free software, we are referring to freedom, not 50 | price. Our General Public Licenses are designed to make sure that you 51 | have the freedom to distribute copies of free software (and charge for 52 | this service if you wish), that you receive source code or can get it 53 | if you want it, that you can change the software or use pieces of it 54 | in new free programs; and that you know you can do these things. 55 | . 56 | To protect your rights, we need to make restrictions that forbid 57 | anyone to deny you these rights or to ask you to surrender the rights. 58 | These restrictions translate to certain responsibilities for you if you 59 | distribute copies of the software, or if you modify it. 60 | . 61 | For example, if you distribute copies of such a program, whether 62 | gratis or for a fee, you must give the recipients all the rights that 63 | you have. You must make sure that they, too, receive or can get the 64 | source code. And you must show them these terms so they know their 65 | rights. 66 | . 67 | We protect your rights with two steps: (1) copyright the software, and 68 | (2) offer you this license which gives you legal permission to copy, 69 | distribute and/or modify the software. 70 | . 71 | Also, for each author's protection and ours, we want to make certain 72 | that everyone understands that there is no warranty for this free 73 | software. If the software is modified by someone else and passed on, we 74 | want its recipients to know that what they have is not the original, so 75 | that any problems introduced by others will not reflect on the original 76 | authors' reputations. 77 | . 78 | Finally, any free program is threatened constantly by software 79 | patents. We wish to avoid the danger that redistributors of a free 80 | program will individually obtain patent licenses, in effect making the 81 | program proprietary. To prevent this, we have made it clear that any 82 | patent must be licensed for everyone's free use or not licensed at all. 83 | . 84 | The precise terms and conditions for copying, distribution and 85 | modification follow. 86 | . 87 | GNU GENERAL PUBLIC LICENSE 88 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 89 | . 90 | 0. This License applies to any program or other work which contains 91 | a notice placed by the copyright holder saying it may be distributed 92 | under the terms of this General Public License. The "Program", below, 93 | refers to any such program or work, and a "work based on the Program" 94 | means either the Program or any derivative work under copyright law: 95 | that is to say, a work containing the Program or a portion of it, 96 | either verbatim or with modifications and/or translated into another 97 | language. (Hereinafter, translation is included without limitation in 98 | the term "modification".) Each licensee is addressed as "you". 99 | . 100 | Activities other than copying, distribution and modification are not 101 | covered by this License; they are outside its scope. The act of 102 | running the Program is not restricted, and the output from the Program 103 | is covered only if its contents constitute a work based on the 104 | Program (independent of having been made by running the Program). 105 | Whether that is true depends on what the Program does. 106 | . 107 | 1. You may copy and distribute verbatim copies of the Program's 108 | source code as you receive it, in any medium, provided that you 109 | conspicuously and appropriately publish on each copy an appropriate 110 | copyright notice and disclaimer of warranty; keep intact all the 111 | notices that refer to this License and to the absence of any warranty; 112 | and give any other recipients of the Program a copy of this License 113 | along with the Program. 114 | . 115 | You may charge a fee for the physical act of transferring a copy, and 116 | you may at your option offer warranty protection in exchange for a fee. 117 | . 118 | 2. You may modify your copy or copies of the Program or any portion 119 | of it, thus forming a work based on the Program, and copy and 120 | distribute such modifications or work under the terms of Section 1 121 | above, provided that you also meet all of these conditions: 122 | . 123 | a) You must cause the modified files to carry prominent notices 124 | stating that you changed the files and the date of any change. 125 | . 126 | b) You must cause any work that you distribute or publish, that in 127 | whole or in part contains or is derived from the Program or any 128 | part thereof, to be licensed as a whole at no charge to all third 129 | parties under the terms of this License. 130 | . 131 | c) If the modified program normally reads commands interactively 132 | when run, you must cause it, when started running for such 133 | interactive use in the most ordinary way, to print or display an 134 | announcement including an appropriate copyright notice and a 135 | notice that there is no warranty (or else, saying that you provide 136 | a warranty) and that users may redistribute the program under 137 | these conditions, and telling the user how to view a copy of this 138 | License. (Exception: if the Program itself is interactive but 139 | does not normally print such an announcement, your work based on 140 | the Program is not required to print an announcement.) 141 | . 142 | These requirements apply to the modified work as a whole. If 143 | identifiable sections of that work are not derived from the Program, 144 | and can be reasonably considered independent and separate works in 145 | themselves, then this License, and its terms, do not apply to those 146 | sections when you distribute them as separate works. But when you 147 | distribute the same sections as part of a whole which is a work based 148 | on the Program, the distribution of the whole must be on the terms of 149 | this License, whose permissions for other licensees extend to the 150 | entire whole, and thus to each and every part regardless of who wrote it. 151 | . 152 | Thus, it is not the intent of this section to claim rights or contest 153 | your rights to work written entirely by you; rather, the intent is to 154 | exercise the right to control the distribution of derivative or 155 | collective works based on the Program. 156 | . 157 | In addition, mere aggregation of another work not based on the Program 158 | with the Program (or with a work based on the Program) on a volume of 159 | a storage or distribution medium does not bring the other work under 160 | the scope of this License. 161 | . 162 | 3. You may copy and distribute the Program (or a work based on it, 163 | under Section 2) in object code or executable form under the terms of 164 | Sections 1 and 2 above provided that you also do one of the following: 165 | . 166 | a) Accompany it with the complete corresponding machine-readable 167 | source code, which must be distributed under the terms of Sections 168 | 1 and 2 above on a medium customarily used for software interchange; or, 169 | . 170 | b) Accompany it with a written offer, valid for at least three 171 | years, to give any third party, for a charge no more than your 172 | cost of physically performing source distribution, a complete 173 | machine-readable copy of the corresponding source code, to be 174 | distributed under the terms of Sections 1 and 2 above on a medium 175 | customarily used for software interchange; or, 176 | . 177 | c) Accompany it with the information you received as to the offer 178 | to distribute corresponding source code. (This alternative is 179 | allowed only for noncommercial distribution and only if you 180 | received the program in object code or executable form with such 181 | an offer, in accord with Subsection b above.) 182 | . 183 | The source code for a work means the preferred form of the work for 184 | making modifications to it. For an executable work, complete source 185 | code means all the source code for all modules it contains, plus any 186 | associated interface definition files, plus the scripts used to 187 | control compilation and installation of the executable. However, as a 188 | special exception, the source code distributed need not include 189 | anything that is normally distributed (in either source or binary 190 | form) with the major components (compiler, kernel, and so on) of the 191 | operating system on which the executable runs, unless that component 192 | itself accompanies the executable. 193 | . 194 | If distribution of executable or object code is made by offering 195 | access to copy from a designated place, then offering equivalent 196 | access to copy the source code from the same place counts as 197 | distribution of the source code, even though third parties are not 198 | compelled to copy the source along with the object code. 199 | . 200 | 4. You may not copy, modify, sublicense, or distribute the Program 201 | except as expressly provided under this License. Any attempt 202 | otherwise to copy, modify, sublicense or distribute the Program is 203 | void, and will automatically terminate your rights under this License. 204 | However, parties who have received copies, or rights, from you under 205 | this License will not have their licenses terminated so long as such 206 | parties remain in full compliance. 207 | . 208 | 5. You are not required to accept this License, since you have not 209 | signed it. However, nothing else grants you permission to modify or 210 | distribute the Program or its derivative works. These actions are 211 | prohibited by law if you do not accept this License. Therefore, by 212 | modifying or distributing the Program (or any work based on the 213 | Program), you indicate your acceptance of this License to do so, and 214 | all its terms and conditions for copying, distributing or modifying 215 | the Program or works based on it. 216 | . 217 | 6. Each time you redistribute the Program (or any work based on the 218 | Program), the recipient automatically receives a license from the 219 | original licensor to copy, distribute or modify the Program subject to 220 | these terms and conditions. You may not impose any further 221 | restrictions on the recipients' exercise of the rights granted herein. 222 | You are not responsible for enforcing compliance by third parties to 223 | this License. 224 | . 225 | 7. If, as a consequence of a court judgment or allegation of patent 226 | infringement or for any other reason (not limited to patent issues), 227 | conditions are imposed on you (whether by court order, agreement or 228 | otherwise) that contradict the conditions of this License, they do not 229 | excuse you from the conditions of this License. If you cannot 230 | distribute so as to satisfy simultaneously your obligations under this 231 | License and any other pertinent obligations, then as a consequence you 232 | may not distribute the Program at all. For example, if a patent 233 | license would not permit royalty-free redistribution of the Program by 234 | all those who receive copies directly or indirectly through you, then 235 | the only way you could satisfy both it and this License would be to 236 | refrain entirely from distribution of the Program. 237 | . 238 | If any portion of this section is held invalid or unenforceable under 239 | any particular circumstance, the balance of the section is intended to 240 | apply and the section as a whole is intended to apply in other 241 | circumstances. 242 | . 243 | It is not the purpose of this section to induce you to infringe any 244 | patents or other property right claims or to contest validity of any 245 | such claims; this section has the sole purpose of protecting the 246 | integrity of the free software distribution system, which is 247 | implemented by public license practices. Many people have made 248 | generous contributions to the wide range of software distributed 249 | through that system in reliance on consistent application of that 250 | system; it is up to the author/donor to decide if he or she is willing 251 | to distribute software through any other system and a licensee cannot 252 | impose that choice. 253 | . 254 | This section is intended to make thoroughly clear what is believed to 255 | be a consequence of the rest of this License. 256 | . 257 | 8. If the distribution and/or use of the Program is restricted in 258 | certain countries either by patents or by copyrighted interfaces, the 259 | original copyright holder who places the Program under this License 260 | may add an explicit geographical distribution limitation excluding 261 | those countries, so that distribution is permitted only in or among 262 | countries not thus excluded. In such case, this License incorporates 263 | the limitation as if written in the body of this License. 264 | . 265 | 9. The Free Software Foundation may publish revised and/or new versions 266 | of the General Public License from time to time. Such new versions will 267 | be similar in spirit to the present version, but may differ in detail to 268 | address new problems or concerns. 269 | . 270 | Each version is given a distinguishing version number. If the Program 271 | specifies a version number of this License which applies to it and "any 272 | later version", you have the option of following the terms and conditions 273 | either of that version or of any later version published by the Free 274 | Software Foundation. If the Program does not specify a version number of 275 | this License, you may choose any version ever published by the Free Software 276 | Foundation. 277 | . 278 | 10. If you wish to incorporate parts of the Program into other free 279 | programs whose distribution conditions are different, write to the author 280 | to ask for permission. For software which is copyrighted by the Free 281 | Software Foundation, write to the Free Software Foundation; we sometimes 282 | make exceptions for this. Our decision will be guided by the two goals 283 | of preserving the free status of all derivatives of our free software and 284 | of promoting the sharing and reuse of software generally. 285 | . 286 | NO WARRANTY 287 | . 288 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 289 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 290 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 291 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 292 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 293 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 294 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 295 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 296 | REPAIR OR CORRECTION. 297 | . 298 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 299 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 300 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 301 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 302 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 303 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 304 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 305 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 306 | POSSIBILITY OF SUCH DAMAGES. 307 | . 308 | END OF TERMS AND CONDITIONS 309 | . 310 | How to Apply These Terms to Your New Programs 311 | . 312 | If you develop a new program, and you want it to be of the greatest 313 | possible use to the public, the best way to achieve this is to make it 314 | free software which everyone can redistribute and change under these terms. 315 | . 316 | To do so, attach the following notices to the program. It is safest 317 | to attach them to the start of each source file to most effectively 318 | convey the exclusion of warranty; and each file should have at least 319 | the "copyright" line and a pointer to where the full notice is found. 320 | . 321 | 322 | Copyright (C) 323 | . 324 | This program is free software; you can redistribute it and/or modify 325 | it under the terms of the GNU General Public License as published by 326 | the Free Software Foundation; either version 2 of the License, or 327 | (at your option) any later version. 328 | . 329 | This program is distributed in the hope that it will be useful, 330 | but WITHOUT ANY WARRANTY; without even the implied warranty of 331 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 332 | GNU General Public License for more details. 333 | . 334 | You should have received a copy of the GNU General Public License along 335 | with this program; if not, write to the Free Software Foundation, Inc., 336 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 337 | . 338 | Also add information on how to contact you by electronic and paper mail. 339 | . 340 | If the program is interactive, make it output a short notice like this 341 | when it starts in an interactive mode: 342 | . 343 | Gnomovision version 69, Copyright (C) year name of author 344 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 345 | This is free software, and you are welcome to redistribute it 346 | under certain conditions; type `show c' for details. 347 | . 348 | The hypothetical commands `show w' and `show c' should show the appropriate 349 | parts of the General Public License. Of course, the commands you use may 350 | be called something other than `show w' and `show c'; they could even be 351 | mouse-clicks or menu items--whatever suits your program. 352 | . 353 | You should also get your employer (if you work as a programmer) or your 354 | school, if any, to sign a "copyright disclaimer" for the program, if 355 | necessary. Here is a sample; alter the names: 356 | . 357 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 358 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 359 | . 360 | , 1 April 1989 361 | Ty Coon, President of Vice 362 | . 363 | This General Public License does not permit incorporating your program into 364 | proprietary programs. If your program is a subroutine library, you may 365 | consider it more useful to permit linking proprietary applications with the 366 | library. If this is what you want to do, use the GNU Lesser General 367 | Public License instead of this License. 368 | -------------------------------------------------------------------------------- /debian/docs: -------------------------------------------------------------------------------- 1 | README.md 2 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | %: 4 | dh $@ --with python3 5 | 6 | override_dh_auto_install: 7 | dh_auto_install -- prefix=/usr 8 | 9 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /debian/source/local-options: -------------------------------------------------------------------------------- 1 | # Uncomment to active options. See dpkg-source(1) 2 | #abort-on-upstream-changes 3 | #unapply-patches 4 | -------------------------------------------------------------------------------- /debian/watch: -------------------------------------------------------------------------------- 1 | version=3 2 | https://github.com/INTI-CMNB/kicad_pcb-diff/tags .*/(\d[\d\.]*)\.(?:tar.gz|tar.bz2|tar.xz) 3 | 4 | -------------------------------------------------------------------------------- /kicad-diff-init.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright (c) 2020-2022 Salvador E. Tropea 3 | # Copyright (c) 2020-2022 Instituto Nacional de Tecnología Industrial 4 | # License: GPL-2.0 5 | # Project: KiCad Diff 6 | """ 7 | KiCad diff tool 8 | 9 | This program initializes a repo to use the kicad-git-diff plug-in. 10 | """ 11 | __author__ = 'Salvador E. Tropea' 12 | __copyright__ = 'Copyright 2020, INTI' 13 | __credits__ = ['Salvador E. Tropea'] 14 | __license__ = 'GPL 2.0' 15 | __version__ = '2.5.8' 16 | __email__ = 'stopea@inti.gob.ar' 17 | __status__ = 'beta' 18 | __url__ = 'https://github.com/INTI-CMNB/KiDiff/' 19 | 20 | import argparse 21 | import logging 22 | from os.path import isfile, isdir, basename, sep 23 | from os import getcwd 24 | import re 25 | from shutil import which 26 | from subprocess import call 27 | from sys import exit 28 | 29 | # Exit error codes 30 | NO_GIT_ROOT = 1 31 | MISSING_GIT = 2 32 | MISSING_SCRIPTS = 3 33 | 34 | git_attributes = '.gitattributes' 35 | git_config = '.gitconfig' 36 | layers_file = '.kicad-git-diff' 37 | 38 | 39 | def CheckAttributes(): 40 | if not isfile(git_attributes): 41 | logger.debug('No '+git_attributes) 42 | return False 43 | with open(git_attributes) as attr_file: 44 | for line in attr_file: 45 | if re.match(r'^\*.kicad_pcb\s+diff', line): 46 | attr_file.close() 47 | return True 48 | return False 49 | 50 | 51 | def CheckCommand(): 52 | if not isfile(git_config): 53 | logger.debug('No '+git_config) 54 | return False 55 | with open(git_config) as cfg_file: 56 | for line in cfg_file: 57 | if re.match(r'^\[diff\s+\"kicad_diff\"', line): 58 | cfg_file.close() 59 | return True 60 | return False 61 | 62 | 63 | if __name__ == '__main__': 64 | parser = argparse.ArgumentParser(description='KiCad diff GIT repo initialization') 65 | 66 | parser.add_argument('--verbose', '-v', action='count', default=0) 67 | parser.add_argument('--version', '-V', action='version', version='%(prog)s '+__version__+' - ' + 68 | __copyright__+' - License: '+__license__) 69 | 70 | args = parser.parse_args() 71 | 72 | # Create a logger with the specified verbosity 73 | if args.verbose >= 2: 74 | log_level = logging.DEBUG 75 | verb = '-vv' 76 | elif args.verbose == 1: 77 | log_level = logging.INFO 78 | verb = '-v' 79 | else: 80 | verb = None 81 | log_level = logging.WARNING 82 | logging.basicConfig(level=log_level) 83 | logger = logging.getLogger(basename(__file__)) 84 | 85 | # Check the environment 86 | if which('git') is None: 87 | logger.error('No git command, install it') 88 | exit(MISSING_GIT) 89 | if which('kicad-git-diff.py') is None: 90 | logger.error('Please install the diff scripts first') 91 | exit(MISSING_SCRIPTS) 92 | 93 | # The script must be invoked from the root of the repo 94 | dir_git = getcwd()+sep+'.git' 95 | if not isdir(dir_git): 96 | logger.error('Run this script from the root of your repo (no .git/ here)') 97 | exit(NO_GIT_ROOT) 98 | 99 | # Configure the repo to use a local .gitconfig file 100 | logger.info('Configuring git to use ".gitconfig" as a configuration file') 101 | command = ['git', 'config', '--local', 'include.path', '../'+git_config] 102 | logger.debug(command) 103 | call(command) 104 | 105 | # Add an attribute for *.kicad_pcb files 106 | if CheckAttributes(): 107 | logger.info('KiCad PCB files already has a diff tool associated') 108 | else: 109 | logger.info('Associating the KiCad PCB extension to a diff plug-in') 110 | with open(git_attributes, "a+") as attr_file: 111 | attr_file.write("*.kicad_pcb diff=kicad_diff\n") 112 | attr_file.write("*.kicad_sch diff=kicad_diff\n") 113 | attr_file.write("*.sch diff=kicad_diff\n") 114 | 115 | # Add a command to the new attribute 116 | if CheckCommand(): 117 | logger.info('Command already configured') 118 | else: 119 | logger.info("Defining a command to compute a diff between KiCad PCB/SCH files") 120 | with open(git_config, "a+") as cfg_file: 121 | cfg_file.write('[diff "kicad_diff"]\n') 122 | cfg_file.write("\tcommand="+which('kicad-git-diff.py')+" -v\n") 123 | 124 | # Add a list of layers to be excluded 125 | if isfile(layers_file): 126 | logger.info('Layer exclusion file already present') 127 | else: 128 | logger.info('Generating a list of layers to be excluded') 129 | with open(layers_file, "w+") as layer_file: 130 | layer_file.write("B.Adhes\n") 131 | layer_file.write("F.Adhes\n") 132 | layer_file.write("#B.Paste\n") 133 | layer_file.write("#F.Paste\n") 134 | layer_file.write("#B.SilkS\n") 135 | layer_file.write("#F.SilkS\n") 136 | layer_file.write("#B.Mask\n") 137 | layer_file.write("#F.Mask\n") 138 | layer_file.write("#Dwgs.User\n") 139 | layer_file.write("Cmts.User\n") 140 | layer_file.write("Eco1.User\n") 141 | layer_file.write("Eco2.User\n") 142 | layer_file.write("Edge.Cuts\n") 143 | layer_file.write("Margin\n") 144 | layer_file.write("#B.CrtYd\n") 145 | layer_file.write("#F.CrtYd\n") 146 | layer_file.write("#B.Fab\n") 147 | layer_file.write("#F.Fab\n") 148 | -------------------------------------------------------------------------------- /kicad-git-diff.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # Copyright (c) 2020-2022 Salvador E. Tropea 3 | # Copyright (c) 2020-2022 Instituto Nacional de Tecnologïa Industrial 4 | # License: GPL-2.0 5 | # Project: KiCad Diff 6 | # Adapted from: https://github.com/obra/kicad-tools 7 | """ 8 | KiCad diff tool 9 | 10 | This program is a git plug-in to generate diffs between two KiCad PCBs. 11 | It relies on kicad-diff.py script (where the real work is done). 12 | """ 13 | __author__ = 'Salvador E. Tropea' 14 | __copyright__ = 'Copyright 2020, INTI' 15 | __credits__ = ['Salvador E. Tropea', 'Jesse Vincent'] 16 | __license__ = 'GPL 2.0' 17 | __version__ = '2.5.8' 18 | __email__ = 'salvador@inti.gob.ar' 19 | __status__ = 'beta' 20 | __url__ = 'https://github.com/INTI-CMNB/KiDiff/' 21 | 22 | import argparse 23 | import logging 24 | from os.path import isfile, isdir, basename, sep, dirname, realpath 25 | from os import getcwd, mkdir 26 | from subprocess import call 27 | from sys import exit 28 | 29 | # Exit error codes 30 | OLD_PCB_INVALID = 1 31 | NEW_PCB_INVALID = 2 32 | 33 | if __name__ == '__main__': 34 | parser = argparse.ArgumentParser(description='KiCad diff wrapper for Git') 35 | 36 | parser.add_argument('old_file_name', help='PCB/SCH name') 37 | parser.add_argument('old_file_file', help='Original PCB/SCH file') 38 | parser.add_argument('old_file_hash', help='Original PCB/SCH hash') 39 | parser.add_argument('old_file_perm', help='Original PCB/SCH perms') 40 | parser.add_argument('new_file_file', help='New PCB/SCH file') 41 | parser.add_argument('new_file_hash', help='New PCB/SCH hash') 42 | parser.add_argument('new_file_perm', help='New PCB/SCH perms') 43 | 44 | parser.add_argument('--resolution', nargs=1, help='Image resolution in DPIs [150]', default=['150']) 45 | parser.add_argument('--verbose', '-v', action='count', default=0) 46 | parser.add_argument('--version', action='version', version='%(prog)s '+__version__+' - ' + 47 | __copyright__+' - License: '+__license__) 48 | 49 | args = parser.parse_args() 50 | 51 | # Create a logger with the specified verbosity 52 | if args.verbose >= 2: 53 | log_level = logging.DEBUG 54 | verb = "-" + ("v" * args.verbose) 55 | elif args.verbose == 1: 56 | log_level = logging.INFO 57 | verb = '-v' 58 | else: 59 | verb = None 60 | log_level = logging.WARNING 61 | logging.basicConfig(level=log_level) 62 | logger = logging.getLogger(basename(__file__)) 63 | 64 | # Check the arguments 65 | old_file = args.old_file_file 66 | if not isfile(old_file): 67 | logger.error('%s isn\'t a valid file name' % old_file) 68 | exit(OLD_PCB_INVALID) 69 | 70 | new_file = args.new_file_file 71 | if not isfile(new_file): 72 | logger.error('%s isn\'t a valid file name' % new_file) 73 | exit(NEW_PCB_INVALID) 74 | 75 | resolution = int(args.resolution[0]) 76 | 77 | # The script is invoked by git from the root of the repo 78 | dir_git = getcwd()+sep+'.git' 79 | dir_cache = None 80 | if not isdir(dir_git): 81 | logger.error('can\'t find the git repo (no '+dir_git+')') 82 | else: 83 | dir_cache = dir_git+sep+'kicad-git-cache' 84 | if not isdir(dir_cache): 85 | try: 86 | mkdir(dir_cache) 87 | except OSError: 88 | logger.error('can\'t create cache dir ('+dir_cache+')') 89 | dir_cache = None 90 | 91 | command = [dirname(realpath(__file__))+sep+'kicad-diff.py', '--all_pages', '--resolution', str(resolution), 92 | '--old_file_hash', args.old_file_hash] 93 | if int(args.new_file_hash, 16): 94 | # When we compare to the currently modified file git uses 0 as hash 95 | # If we use it all files that we didn't yet commit become the same 96 | command += ['--new_file_hash', args.new_file_hash] 97 | if verb is not None: 98 | command.append(verb) 99 | if dir_cache is not None: 100 | command.append('--cache_dir') 101 | command.append(dir_cache) 102 | if isfile('.kicad-git-diff'): 103 | command.append('--exclude') 104 | command.append('.kicad-git-diff') 105 | command.append(old_file) 106 | command.append(new_file) 107 | logger.debug(command) 108 | call(command) 109 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [no-commit-to-branch] 2 | branch = main 3 | 4 | [check-yaml] 5 | unsafe = True 6 | 7 | [pyupgrade] 8 | py36-plus = True 9 | 10 | [black] 11 | safe = True 12 | quiet = True 13 | line-length = 127 14 | 15 | [isort] 16 | profile = black 17 | line_length = 127 18 | 19 | [autoflake8] 20 | in-place = True 21 | recursive = True 22 | expand-star-imports = True 23 | 24 | [codespell] 25 | ignore-words-list=kibot,tht 26 | quiet-level = 2 27 | 28 | [flake8] 29 | ignore = E402, E226, E126, W504 30 | # docstrings - Missing 31 | D1 32 | # docstrings - whitespace 33 | D2 34 | # docstrings - text style (imperative, dot) 35 | D4 36 | max-line-length = 127 37 | max-complexity = 30 38 | exclude = experiments/kicad/v6/ 39 | experiments/JLC/ 40 | kibot/mcpyrate/ 41 | kibot/PyPDF2/ 42 | submodules/ 43 | pp/ 44 | output/ 45 | 46 | [mypy] 47 | exclude = experiments build 48 | ignore_missing_imports = True 49 | # install_types = True 50 | # non_interactive = True 51 | check_untyped_defs = True 52 | show_error_codes = True 53 | show_error_context = True 54 | # additional_dependencies = module==version 55 | 56 | [pylint.MESSAGES CONTROL] 57 | disable = invalid-name, unused-argument, broad-except, missing-docstring, fixme, 58 | consider-using-f-string, 59 | too-many-branches, too-many-statements, too-many-arguments, protected-access, 60 | import-error, too-many-locals, import-outside-toplevel, 61 | logging-fstring-interpolation, line-too-long, 62 | duplicate-code, 63 | logging-not-lazy, wrong-import-order, 64 | 65 | [pylint.FORMAT] 66 | max-line-length = 127 67 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import re 3 | from setuptools import setup, find_packages 4 | 5 | 6 | with open("kicad-diff.py", 'rt') as f: 7 | txt = f.read() 8 | version = re.search(r"__version__ = '(.*)'", txt).group(1) 9 | author = re.search(r"__author__ = '(.*)'", txt).group(1) 10 | email = re.search(r"__email__ = '(.*)'", txt).group(1) 11 | url = re.search(r"__url__ = '(.*)'", txt).group(1) 12 | 13 | # Use the README.md as a long description. 14 | # Note this is also included in the MANIFEST.in 15 | with open('README.md', encoding='utf-8') as f: 16 | long_description = '\n' + f.read() 17 | 18 | setup(name='kidiff', 19 | version=version, 20 | description='KiCad PCB/SCH Diff', 21 | long_description=long_description, 22 | long_description_content_type='text/markdown', 23 | author=author, 24 | author_email=email, 25 | url=url, 26 | # Packages are marked using __init__.py 27 | packages=find_packages(), 28 | scripts=['kicad-diff-init.py', 'kicad-diff.py', 'kicad-git-diff.py'], 29 | install_requires=['kiauto'], 30 | include_package_data=True, 31 | classifiers=['Development Status :: 5 - Production/Stable', 32 | 'Environment :: Console', 33 | 'Intended Audience :: Developers', 34 | 'License :: OSI Approved :: GNU General Public License v2 (GPLv2)', 35 | 'Natural Language :: English', 36 | 'Operating System :: POSIX :: Linux', 37 | 'Programming Language :: Python :: 3', 38 | 'Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)', 39 | ], 40 | platforms='POSIX', 41 | license='GPL-2', 42 | python_requires='>=3.4', 43 | ) 44 | -------------------------------------------------------------------------------- /tests/cases/1/ref_pcb/diff-00.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/1/ref_pcb/diff-00.png -------------------------------------------------------------------------------- /tests/cases/1/ref_pcb/diff-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/1/ref_pcb/diff-10.png -------------------------------------------------------------------------------- /tests/cases/1/ref_pcb/diff-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/1/ref_pcb/diff-20.png -------------------------------------------------------------------------------- /tests/cases/1/ref_pcb/diff-310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/1/ref_pcb/diff-310.png -------------------------------------------------------------------------------- /tests/cases/1/ref_pcb/diff-320.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/1/ref_pcb/diff-320.png -------------------------------------------------------------------------------- /tests/cases/1/ref_pcb/diff-330.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/1/ref_pcb/diff-330.png -------------------------------------------------------------------------------- /tests/cases/1/ref_pcb/diff-340.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/1/ref_pcb/diff-340.png -------------------------------------------------------------------------------- /tests/cases/1/ref_pcb/diff-350.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/1/ref_pcb/diff-350.png -------------------------------------------------------------------------------- /tests/cases/1/ref_pcb/diff-360.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/1/ref_pcb/diff-360.png -------------------------------------------------------------------------------- /tests/cases/1/ref_pcb/diff-370.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/1/ref_pcb/diff-370.png -------------------------------------------------------------------------------- /tests/cases/1/ref_pcb/diff-380.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/1/ref_pcb/diff-380.png -------------------------------------------------------------------------------- /tests/cases/1/ref_pcb/diff-390.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/1/ref_pcb/diff-390.png -------------------------------------------------------------------------------- /tests/cases/1/ref_pcb/diff-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/1/ref_pcb/diff-400.png -------------------------------------------------------------------------------- /tests/cases/1/ref_pcb/diff-410.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/1/ref_pcb/diff-410.png -------------------------------------------------------------------------------- /tests/cases/1/ref_pcb/diff-420.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/1/ref_pcb/diff-420.png -------------------------------------------------------------------------------- /tests/cases/1/ref_pcb/diff-430.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/1/ref_pcb/diff-430.png -------------------------------------------------------------------------------- /tests/cases/1/ref_pcb/diff-440.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/1/ref_pcb/diff-440.png -------------------------------------------------------------------------------- /tests/cases/1/ref_pcb/diff-450.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/1/ref_pcb/diff-450.png -------------------------------------------------------------------------------- /tests/cases/1/ref_pcb/diff-460.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/1/ref_pcb/diff-460.png -------------------------------------------------------------------------------- /tests/cases/1/ref_pcb/diff-470.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/1/ref_pcb/diff-470.png -------------------------------------------------------------------------------- /tests/cases/1/ref_pcb/diff-480.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/1/ref_pcb/diff-480.png -------------------------------------------------------------------------------- /tests/cases/1/ref_pcb/diff-490.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/1/ref_pcb/diff-490.png -------------------------------------------------------------------------------- /tests/cases/1/ref_sch/diff-Schematic0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/1/ref_sch/diff-Schematic0.png -------------------------------------------------------------------------------- /tests/cases/2/a/deeper.kicad_sch: -------------------------------------------------------------------------------- 1 | (kicad_sch (version 20211123) (generator eeschema) 2 | 3 | (uuid 3b838d52-596d-4e4d-a6ac-e4c8e7621137) 4 | 5 | (paper "A4" portrait) 6 | 7 | (title_block 8 | (date " ") 9 | ) 10 | 11 | (lib_symbols 12 | (symbol "Device:R" (pin_numbers hide) (pin_names (offset 0)) (in_bom yes) (on_board yes) 13 | (property "Reference" "R" (id 0) (at 2.032 0 90) 14 | (effects (font (size 1.27 1.27))) 15 | ) 16 | (property "Value" "R" (id 1) (at 0 0 90) 17 | (effects (font (size 1.27 1.27))) 18 | ) 19 | (property "Footprint" "" (id 2) (at -1.778 0 90) 20 | (effects (font (size 1.27 1.27)) hide) 21 | ) 22 | (property "Datasheet" "~" (id 3) (at 0 0 0) 23 | (effects (font (size 1.27 1.27)) hide) 24 | ) 25 | (property "ki_keywords" "R res resistor" (id 4) (at 0 0 0) 26 | (effects (font (size 1.27 1.27)) hide) 27 | ) 28 | (property "ki_description" "Resistor" (id 5) (at 0 0 0) 29 | (effects (font (size 1.27 1.27)) hide) 30 | ) 31 | (property "ki_fp_filters" "R_*" (id 6) (at 0 0 0) 32 | (effects (font (size 1.27 1.27)) hide) 33 | ) 34 | (symbol "R_0_1" 35 | (rectangle (start -1.016 -2.54) (end 1.016 2.54) 36 | (stroke (width 0.254) (type default) (color 0 0 0 0)) 37 | (fill (type none)) 38 | ) 39 | ) 40 | (symbol "R_1_1" 41 | (pin passive line (at 0 3.81 270) (length 1.27) 42 | (name "~" (effects (font (size 1.27 1.27)))) 43 | (number "1" (effects (font (size 1.27 1.27)))) 44 | ) 45 | (pin passive line (at 0 -3.81 90) (length 1.27) 46 | (name "~" (effects (font (size 1.27 1.27)))) 47 | (number "2" (effects (font (size 1.27 1.27)))) 48 | ) 49 | ) 50 | ) 51 | ) 52 | 53 | 54 | (symbol (lib_id "Device:R") (at 139.7 80.01 0) (unit 1) 55 | (in_bom yes) (on_board yes) 56 | (uuid 00000000-0000-0000-0000-00005f3bbcff) 57 | (property "Reference" "R3" (id 0) (at 141.478 78.8416 0) 58 | (effects (font (size 1.27 1.27)) (justify left)) 59 | ) 60 | (property "Value" "1m" (id 1) (at 141.478 81.153 0) 61 | (effects (font (size 1.27 1.27)) (justify left)) 62 | ) 63 | (property "Footprint" "" (id 2) (at 137.922 80.01 90) 64 | (effects (font (size 1.27 1.27)) hide) 65 | ) 66 | (property "Datasheet" "~" (id 3) (at 139.7 80.01 0) 67 | (effects (font (size 1.27 1.27)) hide) 68 | ) 69 | (pin "1" (uuid c8dca1a6-f09a-4305-804d-2295b5448f55)) 70 | (pin "2" (uuid 4dec140a-3f2f-4b17-b6f0-89a0fc1cf30b)) 71 | ) 72 | ) 73 | -------------------------------------------------------------------------------- /tests/cases/2/a/sub-sheet.kicad_sch: -------------------------------------------------------------------------------- 1 | (kicad_sch (version 20211123) (generator eeschema) 2 | 3 | (uuid 5b2b5c7d-f943-4634-9f0a-e9561705c49d) 4 | 5 | (paper "User" 297.002 210.007) 6 | 7 | (title_block 8 | (date " ") 9 | ) 10 | 11 | (lib_symbols 12 | (symbol "74xx:74LS04" (in_bom yes) (on_board yes) 13 | (property "Reference" "U" (id 0) (at 0 1.27 0) 14 | (effects (font (size 1.27 1.27))) 15 | ) 16 | (property "Value" "74LS04" (id 1) (at 0 -1.27 0) 17 | (effects (font (size 1.27 1.27))) 18 | ) 19 | (property "Footprint" "" (id 2) (at 0 0 0) 20 | (effects (font (size 1.27 1.27)) hide) 21 | ) 22 | (property "Datasheet" "http://www.ti.com/lit/gpn/sn74LS04" (id 3) (at 0 0 0) 23 | (effects (font (size 1.27 1.27)) hide) 24 | ) 25 | (property "ki_locked" "" (id 4) (at 0 0 0) 26 | (effects (font (size 1.27 1.27))) 27 | ) 28 | (property "ki_keywords" "TTL not inv" (id 5) (at 0 0 0) 29 | (effects (font (size 1.27 1.27)) hide) 30 | ) 31 | (property "ki_description" "Hex Inverter" (id 6) (at 0 0 0) 32 | (effects (font (size 1.27 1.27)) hide) 33 | ) 34 | (property "ki_fp_filters" "DIP*W7.62mm* SSOP?14* TSSOP?14*" (id 7) (at 0 0 0) 35 | (effects (font (size 1.27 1.27)) hide) 36 | ) 37 | (symbol "74LS04_1_0" 38 | (polyline 39 | (pts 40 | (xy -3.81 3.81) 41 | (xy -3.81 -3.81) 42 | (xy 3.81 0) 43 | (xy -3.81 3.81) 44 | ) 45 | (stroke (width 0.254) (type default) (color 0 0 0 0)) 46 | (fill (type background)) 47 | ) 48 | (pin input line (at -7.62 0 0) (length 3.81) 49 | (name "~" (effects (font (size 1.27 1.27)))) 50 | (number "1" (effects (font (size 1.27 1.27)))) 51 | ) 52 | (pin output inverted (at 7.62 0 180) (length 3.81) 53 | (name "~" (effects (font (size 1.27 1.27)))) 54 | (number "2" (effects (font (size 1.27 1.27)))) 55 | ) 56 | ) 57 | (symbol "74LS04_2_0" 58 | (polyline 59 | (pts 60 | (xy -3.81 3.81) 61 | (xy -3.81 -3.81) 62 | (xy 3.81 0) 63 | (xy -3.81 3.81) 64 | ) 65 | (stroke (width 0.254) (type default) (color 0 0 0 0)) 66 | (fill (type background)) 67 | ) 68 | (pin input line (at -7.62 0 0) (length 3.81) 69 | (name "~" (effects (font (size 1.27 1.27)))) 70 | (number "3" (effects (font (size 1.27 1.27)))) 71 | ) 72 | (pin output inverted (at 7.62 0 180) (length 3.81) 73 | (name "~" (effects (font (size 1.27 1.27)))) 74 | (number "4" (effects (font (size 1.27 1.27)))) 75 | ) 76 | ) 77 | (symbol "74LS04_3_0" 78 | (polyline 79 | (pts 80 | (xy -3.81 3.81) 81 | (xy -3.81 -3.81) 82 | (xy 3.81 0) 83 | (xy -3.81 3.81) 84 | ) 85 | (stroke (width 0.254) (type default) (color 0 0 0 0)) 86 | (fill (type background)) 87 | ) 88 | (pin input line (at -7.62 0 0) (length 3.81) 89 | (name "~" (effects (font (size 1.27 1.27)))) 90 | (number "5" (effects (font (size 1.27 1.27)))) 91 | ) 92 | (pin output inverted (at 7.62 0 180) (length 3.81) 93 | (name "~" (effects (font (size 1.27 1.27)))) 94 | (number "6" (effects (font (size 1.27 1.27)))) 95 | ) 96 | ) 97 | (symbol "74LS04_4_0" 98 | (polyline 99 | (pts 100 | (xy -3.81 3.81) 101 | (xy -3.81 -3.81) 102 | (xy 3.81 0) 103 | (xy -3.81 3.81) 104 | ) 105 | (stroke (width 0.254) (type default) (color 0 0 0 0)) 106 | (fill (type background)) 107 | ) 108 | (pin output inverted (at 7.62 0 180) (length 3.81) 109 | (name "~" (effects (font (size 1.27 1.27)))) 110 | (number "8" (effects (font (size 1.27 1.27)))) 111 | ) 112 | (pin input line (at -7.62 0 0) (length 3.81) 113 | (name "~" (effects (font (size 1.27 1.27)))) 114 | (number "9" (effects (font (size 1.27 1.27)))) 115 | ) 116 | ) 117 | (symbol "74LS04_5_0" 118 | (polyline 119 | (pts 120 | (xy -3.81 3.81) 121 | (xy -3.81 -3.81) 122 | (xy 3.81 0) 123 | (xy -3.81 3.81) 124 | ) 125 | (stroke (width 0.254) (type default) (color 0 0 0 0)) 126 | (fill (type background)) 127 | ) 128 | (pin output inverted (at 7.62 0 180) (length 3.81) 129 | (name "~" (effects (font (size 1.27 1.27)))) 130 | (number "10" (effects (font (size 1.27 1.27)))) 131 | ) 132 | (pin input line (at -7.62 0 0) (length 3.81) 133 | (name "~" (effects (font (size 1.27 1.27)))) 134 | (number "11" (effects (font (size 1.27 1.27)))) 135 | ) 136 | ) 137 | (symbol "74LS04_6_0" 138 | (polyline 139 | (pts 140 | (xy -3.81 3.81) 141 | (xy -3.81 -3.81) 142 | (xy 3.81 0) 143 | (xy -3.81 3.81) 144 | ) 145 | (stroke (width 0.254) (type default) (color 0 0 0 0)) 146 | (fill (type background)) 147 | ) 148 | (pin output inverted (at 7.62 0 180) (length 3.81) 149 | (name "~" (effects (font (size 1.27 1.27)))) 150 | (number "12" (effects (font (size 1.27 1.27)))) 151 | ) 152 | (pin input line (at -7.62 0 0) (length 3.81) 153 | (name "~" (effects (font (size 1.27 1.27)))) 154 | (number "13" (effects (font (size 1.27 1.27)))) 155 | ) 156 | ) 157 | (symbol "74LS04_7_0" 158 | (pin power_in line (at 0 12.7 270) (length 5.08) 159 | (name "VCC" (effects (font (size 1.27 1.27)))) 160 | (number "14" (effects (font (size 1.27 1.27)))) 161 | ) 162 | (pin power_in line (at 0 -12.7 90) (length 5.08) 163 | (name "GND" (effects (font (size 1.27 1.27)))) 164 | (number "7" (effects (font (size 1.27 1.27)))) 165 | ) 166 | ) 167 | (symbol "74LS04_7_1" 168 | (rectangle (start -5.08 7.62) (end 5.08 -7.62) 169 | (stroke (width 0.254) (type default) (color 0 0 0 0)) 170 | (fill (type background)) 171 | ) 172 | ) 173 | ) 174 | (symbol "power:GND" (power) (pin_names (offset 0)) (in_bom yes) (on_board yes) 175 | (property "Reference" "#PWR" (id 0) (at 0 -6.35 0) 176 | (effects (font (size 1.27 1.27)) hide) 177 | ) 178 | (property "Value" "GND" (id 1) (at 0 -3.81 0) 179 | (effects (font (size 1.27 1.27))) 180 | ) 181 | (property "Footprint" "" (id 2) (at 0 0 0) 182 | (effects (font (size 1.27 1.27)) hide) 183 | ) 184 | (property "Datasheet" "" (id 3) (at 0 0 0) 185 | (effects (font (size 1.27 1.27)) hide) 186 | ) 187 | (property "ki_keywords" "power-flag" (id 4) (at 0 0 0) 188 | (effects (font (size 1.27 1.27)) hide) 189 | ) 190 | (property "ki_description" "Power symbol creates a global label with name \"GND\" , ground" (id 5) (at 0 0 0) 191 | (effects (font (size 1.27 1.27)) hide) 192 | ) 193 | (symbol "GND_0_1" 194 | (polyline 195 | (pts 196 | (xy 0 0) 197 | (xy 0 -1.27) 198 | (xy 1.27 -1.27) 199 | (xy 0 -2.54) 200 | (xy -1.27 -1.27) 201 | (xy 0 -1.27) 202 | ) 203 | (stroke (width 0) (type default) (color 0 0 0 0)) 204 | (fill (type none)) 205 | ) 206 | ) 207 | (symbol "GND_1_1" 208 | (pin power_in line (at 0 0 270) (length 0) hide 209 | (name "GND" (effects (font (size 1.27 1.27)))) 210 | (number "1" (effects (font (size 1.27 1.27)))) 211 | ) 212 | ) 213 | ) 214 | (symbol "power:VCC" (power) (pin_names (offset 0)) (in_bom yes) (on_board yes) 215 | (property "Reference" "#PWR" (id 0) (at 0 -3.81 0) 216 | (effects (font (size 1.27 1.27)) hide) 217 | ) 218 | (property "Value" "VCC" (id 1) (at 0 3.81 0) 219 | (effects (font (size 1.27 1.27))) 220 | ) 221 | (property "Footprint" "" (id 2) (at 0 0 0) 222 | (effects (font (size 1.27 1.27)) hide) 223 | ) 224 | (property "Datasheet" "" (id 3) (at 0 0 0) 225 | (effects (font (size 1.27 1.27)) hide) 226 | ) 227 | (property "ki_keywords" "power-flag" (id 4) (at 0 0 0) 228 | (effects (font (size 1.27 1.27)) hide) 229 | ) 230 | (property "ki_description" "Power symbol creates a global label with name \"VCC\"" (id 5) (at 0 0 0) 231 | (effects (font (size 1.27 1.27)) hide) 232 | ) 233 | (symbol "VCC_0_1" 234 | (polyline 235 | (pts 236 | (xy -0.762 1.27) 237 | (xy 0 2.54) 238 | ) 239 | (stroke (width 0) (type default) (color 0 0 0 0)) 240 | (fill (type none)) 241 | ) 242 | (polyline 243 | (pts 244 | (xy 0 0) 245 | (xy 0 2.54) 246 | ) 247 | (stroke (width 0) (type default) (color 0 0 0 0)) 248 | (fill (type none)) 249 | ) 250 | (polyline 251 | (pts 252 | (xy 0 2.54) 253 | (xy 0.762 1.27) 254 | ) 255 | (stroke (width 0) (type default) (color 0 0 0 0)) 256 | (fill (type none)) 257 | ) 258 | ) 259 | (symbol "VCC_1_1" 260 | (pin power_in line (at 0 0 90) (length 0) hide 261 | (name "VCC" (effects (font (size 1.27 1.27)))) 262 | (number "1" (effects (font (size 1.27 1.27)))) 263 | ) 264 | ) 265 | ) 266 | ) 267 | 268 | 269 | (wire (pts (xy 104.14 63.5) (xy 106.68 63.5)) 270 | (stroke (width 0) (type default) (color 0 0 0 0)) 271 | (uuid 10109f84-4940-47f8-8640-91f185ac9bc1) 272 | ) 273 | (wire (pts (xy 175.26 63.5) (xy 177.8 63.5)) 274 | (stroke (width 0) (type default) (color 0 0 0 0)) 275 | (uuid 47baf4b1-0938-497d-88f9-671136aa8be7) 276 | ) 277 | (wire (pts (xy 121.92 63.5) (xy 124.46 63.5)) 278 | (stroke (width 0) (type default) (color 0 0 0 0)) 279 | (uuid 55e740a3-0735-4744-896e-2bf5437093b9) 280 | ) 281 | (wire (pts (xy 157.48 63.5) (xy 160.02 63.5)) 282 | (stroke (width 0) (type default) (color 0 0 0 0)) 283 | (uuid c022004a-c968-410e-b59e-fbab0e561e9d) 284 | ) 285 | (wire (pts (xy 193.04 63.5) (xy 195.58 63.5)) 286 | (stroke (width 0) (type default) (color 0 0 0 0)) 287 | (uuid e615f7aa-337e-474d-9615-2ad82b1c44ca) 288 | ) 289 | (wire (pts (xy 85.09 63.5) (xy 88.9 63.5)) 290 | (stroke (width 0) (type default) (color 0 0 0 0)) 291 | (uuid ef8fe2ac-6a7f-4682-9418-b801a1b10a3b) 292 | ) 293 | (wire (pts (xy 139.7 63.5) (xy 142.24 63.5)) 294 | (stroke (width 0) (type default) (color 0 0 0 0)) 295 | (uuid f4f99e3d-7269-4f6a-a759-16ad2a258779) 296 | ) 297 | 298 | (hierarchical_label "IN" (shape input) (at 85.09 63.5 180) 299 | (effects (font (size 1.27 1.27)) (justify right)) 300 | (uuid 4fb02e58-160a-4a39-9f22-d0c75e82ee72) 301 | ) 302 | (hierarchical_label "OUT" (shape output) (at 195.58 63.5 0) 303 | (effects (font (size 1.27 1.27)) (justify left)) 304 | (uuid 77ed3941-d133-4aef-a9af-5a39322d14eb) 305 | ) 306 | 307 | (symbol (lib_id "74xx:74LS04") (at 96.52 63.5 0) (unit 1) 308 | (in_bom yes) (on_board yes) 309 | (uuid 00000000-0000-0000-0000-00005f34307a) 310 | (property "Reference" "U1" (id 0) (at 96.52 55.4482 0)) 311 | (property "Value" "74LS04" (id 1) (at 96.52 57.7596 0)) 312 | (property "Footprint" "" (id 2) (at 96.52 63.5 0) 313 | (effects (font (size 1.27 1.27)) hide) 314 | ) 315 | (property "Datasheet" "http://www.ti.com/lit/gpn/sn74LS04" (id 3) (at 96.52 63.5 0) 316 | (effects (font (size 1.27 1.27)) hide) 317 | ) 318 | (pin "1" (uuid f1e9ac99-f8d4-4d05-beb3-290dccb8d277)) 319 | (pin "2" (uuid 014c98b2-ac35-4831-825d-74c6fa5ddc67)) 320 | (pin "3" (uuid 2282aa17-25e5-4b18-b436-0d5d8b80c271)) 321 | (pin "4" (uuid 779d408a-048e-4117-b37b-52b0f8855fe0)) 322 | (pin "5" (uuid 0209c2aa-e6bb-4fb6-ad76-6e4b9b1b16f7)) 323 | (pin "6" (uuid 867bfffe-ae91-4651-94e5-196c3c600344)) 324 | (pin "8" (uuid 46124aee-812d-4b49-8b07-54040860b90b)) 325 | (pin "9" (uuid bfb61142-9bc3-4e95-89e6-4ced14b533f1)) 326 | (pin "10" (uuid c37c1333-9b31-4605-8469-7903812fede5)) 327 | (pin "11" (uuid f638fbb1-32ba-4d92-b985-ace474e20207)) 328 | (pin "12" (uuid 0105483f-ed6d-4621-a345-4f843c0072bf)) 329 | (pin "13" (uuid 052e0faa-99fa-4dba-8771-a6a810a9615f)) 330 | (pin "14" (uuid cad197d5-2063-47e9-a3da-c0da8b26e6ec)) 331 | (pin "7" (uuid a5eca4b9-9690-4686-9caf-8a9265e35104)) 332 | ) 333 | 334 | (symbol (lib_id "74xx:74LS04") (at 114.3 63.5 0) (unit 2) 335 | (in_bom yes) (on_board yes) 336 | (uuid 00000000-0000-0000-0000-00005f344a0e) 337 | (property "Reference" "U1" (id 0) (at 114.3 55.4482 0)) 338 | (property "Value" "74LS04" (id 1) (at 114.3 57.7596 0)) 339 | (property "Footprint" "" (id 2) (at 114.3 63.5 0) 340 | (effects (font (size 1.27 1.27)) hide) 341 | ) 342 | (property "Datasheet" "http://www.ti.com/lit/gpn/sn74LS04" (id 3) (at 114.3 63.5 0) 343 | (effects (font (size 1.27 1.27)) hide) 344 | ) 345 | (pin "1" (uuid 7eb5089c-0f47-4b87-beab-8ff80b20792f)) 346 | (pin "2" (uuid 60102715-3c93-4a15-8674-20c8b7c57be4)) 347 | (pin "3" (uuid c1630947-26ec-4031-96dd-f1cdf939d3a7)) 348 | (pin "4" (uuid e2858b6c-d9d5-4bfd-a26d-d7110c9ff81c)) 349 | (pin "5" (uuid 8e8bdbab-9511-4475-9eff-1510f6b5ad23)) 350 | (pin "6" (uuid 6ae8ec3f-43b9-4112-bfd5-128204c7759e)) 351 | (pin "8" (uuid cf6c2bfe-fd20-4797-a562-aefcd563a7af)) 352 | (pin "9" (uuid 99a6c8b8-68c2-479d-ad69-5f387013f1da)) 353 | (pin "10" (uuid 349db570-c21f-46f9-80d4-b68f3473ae15)) 354 | (pin "11" (uuid 76d634a3-0256-4a17-8718-470b7e39e4ac)) 355 | (pin "12" (uuid 72b533b9-dc1e-46c5-a29d-c8b49a52ce5d)) 356 | (pin "13" (uuid 132268c6-c19f-48f6-8da5-42a0ba313cd0)) 357 | (pin "14" (uuid 5cd18a01-4203-4bf0-961c-09af1ded6848)) 358 | (pin "7" (uuid 612772b8-33ef-4e45-abaa-4fba74ec3e0b)) 359 | ) 360 | 361 | (symbol (lib_id "74xx:74LS04") (at 132.08 63.5 0) (unit 3) 362 | (in_bom yes) (on_board yes) 363 | (uuid 00000000-0000-0000-0000-00005f345e39) 364 | (property "Reference" "U1" (id 0) (at 132.08 55.4482 0)) 365 | (property "Value" "74LS04" (id 1) (at 132.08 57.7596 0)) 366 | (property "Footprint" "" (id 2) (at 132.08 63.5 0) 367 | (effects (font (size 1.27 1.27)) hide) 368 | ) 369 | (property "Datasheet" "http://www.ti.com/lit/gpn/sn74LS04" (id 3) (at 132.08 63.5 0) 370 | (effects (font (size 1.27 1.27)) hide) 371 | ) 372 | (pin "1" (uuid bbac1062-acc9-427d-9486-84613968193d)) 373 | (pin "2" (uuid aa6fbc5d-962a-421b-b0f8-5cc0634fa940)) 374 | (pin "3" (uuid ee86faf1-b781-415c-a773-7ffd42b4ccac)) 375 | (pin "4" (uuid f60b38a1-6bcf-4425-85f9-40dd840f097e)) 376 | (pin "5" (uuid 9a609661-5895-4db4-a600-14bffdacc16e)) 377 | (pin "6" (uuid f6c777f1-b4a6-4529-9b29-67dfedcb1744)) 378 | (pin "8" (uuid d6ee852a-2125-4cbb-b4c0-2c1f7b3da0fd)) 379 | (pin "9" (uuid 66b18884-be07-4547-81e4-598e8796b478)) 380 | (pin "10" (uuid 23b24c2d-06bc-4f64-bbba-4a1836993bd3)) 381 | (pin "11" (uuid ab204973-c25f-4438-a480-b6256c3846e5)) 382 | (pin "12" (uuid b7d46009-86c1-4915-8882-efed73321a1b)) 383 | (pin "13" (uuid ae6c4a1d-7821-456a-9178-e99999733573)) 384 | (pin "14" (uuid bb4b6efa-8944-40e5-8681-42c8086dfb79)) 385 | (pin "7" (uuid 7b67e898-9526-4fba-8f77-59962db970c7)) 386 | ) 387 | 388 | (symbol (lib_id "74xx:74LS04") (at 149.86 63.5 0) (unit 4) 389 | (in_bom yes) (on_board yes) 390 | (uuid 00000000-0000-0000-0000-00005f346e8b) 391 | (property "Reference" "U1" (id 0) (at 149.86 55.4482 0)) 392 | (property "Value" "74LS04" (id 1) (at 149.86 57.7596 0)) 393 | (property "Footprint" "" (id 2) (at 149.86 63.5 0) 394 | (effects (font (size 1.27 1.27)) hide) 395 | ) 396 | (property "Datasheet" "http://www.ti.com/lit/gpn/sn74LS04" (id 3) (at 149.86 63.5 0) 397 | (effects (font (size 1.27 1.27)) hide) 398 | ) 399 | (pin "1" (uuid 2905dd4f-e9df-4c8d-9d14-37e5474058d3)) 400 | (pin "2" (uuid 909aa50b-4012-4e97-bcca-8dbd688e9e99)) 401 | (pin "3" (uuid 8520760e-f968-483e-8989-07d96ff7c0bd)) 402 | (pin "4" (uuid 472927d7-713d-4434-aae2-cab12a79506c)) 403 | (pin "5" (uuid 5c2e0337-2447-4117-8a69-8ab7a6736b16)) 404 | (pin "6" (uuid 44dcea25-62da-41dc-8e4e-72c45198747e)) 405 | (pin "8" (uuid 47d49bdd-bacb-468f-bcfb-647fd9ccff38)) 406 | (pin "9" (uuid 71c84a4a-5d81-46df-a82f-84ff433708b7)) 407 | (pin "10" (uuid ccbabbc8-3805-4494-9f79-0e7c58491280)) 408 | (pin "11" (uuid 5f478e4e-1c37-417f-802a-dcd64da6ec69)) 409 | (pin "12" (uuid f4169419-5cd3-4db1-860a-b37e557bc728)) 410 | (pin "13" (uuid 0eb475d7-7c32-41bc-a947-f6e04adbfa7d)) 411 | (pin "14" (uuid c3d6c577-12ce-4f2f-84bd-dda13a8fe40f)) 412 | (pin "7" (uuid 2843c226-58ef-4dc8-ad8f-3fcb74143c53)) 413 | ) 414 | 415 | (symbol (lib_id "74xx:74LS04") (at 167.64 63.5 0) (unit 5) 416 | (in_bom yes) (on_board yes) 417 | (uuid 00000000-0000-0000-0000-00005f348270) 418 | (property "Reference" "U1" (id 0) (at 167.64 55.4482 0)) 419 | (property "Value" "74LS04" (id 1) (at 167.64 57.7596 0)) 420 | (property "Footprint" "" (id 2) (at 167.64 63.5 0) 421 | (effects (font (size 1.27 1.27)) hide) 422 | ) 423 | (property "Datasheet" "http://www.ti.com/lit/gpn/sn74LS04" (id 3) (at 167.64 63.5 0) 424 | (effects (font (size 1.27 1.27)) hide) 425 | ) 426 | (pin "1" (uuid 50c06431-98b4-4782-9bc2-1f48dd66012e)) 427 | (pin "2" (uuid 237913c0-554e-4f92-9454-74fca47a4ff0)) 428 | (pin "3" (uuid 22591fce-1b7c-432b-9f65-ca786ad38164)) 429 | (pin "4" (uuid cf7394f4-fb02-417b-8fcd-f7a746f2f953)) 430 | (pin "5" (uuid 1c6ed6bd-b27d-4107-a50a-c5fa1ca815a8)) 431 | (pin "6" (uuid c9913a2d-9fdc-477b-99fb-40ae623b387f)) 432 | (pin "8" (uuid 1dd5c978-2ace-45f7-9c90-88b9fc1a4ffc)) 433 | (pin "9" (uuid 9a40e5da-1346-4d0c-af28-5a2601f44304)) 434 | (pin "10" (uuid 01fec0dc-78cf-4a7d-851f-e24a9edd4a86)) 435 | (pin "11" (uuid 92e30d01-6115-49a3-bead-4b8b74d533c9)) 436 | (pin "12" (uuid 7092ba6d-f06a-417d-9814-317dd365bfc4)) 437 | (pin "13" (uuid bce2b649-7e95-435b-ad4a-9467fe595946)) 438 | (pin "14" (uuid cee262bb-8369-447b-8bc6-8fc7e99304ba)) 439 | (pin "7" (uuid d69f10fe-7d7e-437f-89c6-5d4591a60c24)) 440 | ) 441 | 442 | (symbol (lib_id "74xx:74LS04") (at 185.42 63.5 0) (unit 6) 443 | (in_bom yes) (on_board yes) 444 | (uuid 00000000-0000-0000-0000-00005f348d95) 445 | (property "Reference" "U1" (id 0) (at 185.42 55.4482 0)) 446 | (property "Value" "74LS04" (id 1) (at 185.42 57.7596 0)) 447 | (property "Footprint" "" (id 2) (at 185.42 63.5 0) 448 | (effects (font (size 1.27 1.27)) hide) 449 | ) 450 | (property "Datasheet" "http://www.ti.com/lit/gpn/sn74LS04" (id 3) (at 185.42 63.5 0) 451 | (effects (font (size 1.27 1.27)) hide) 452 | ) 453 | (pin "1" (uuid c1de8072-85af-41fb-bbae-891234fe6286)) 454 | (pin "2" (uuid 92eba779-7a6c-4f2f-bf8a-9d01b4559540)) 455 | (pin "3" (uuid 87aa64d0-4063-42a0-8b26-e6afc8ae74de)) 456 | (pin "4" (uuid 57d78745-19ee-4265-be9f-6570894e3e61)) 457 | (pin "5" (uuid 924bfea8-9b40-4840-882f-6d467382007a)) 458 | (pin "6" (uuid 6353b9ee-662c-49d5-8069-f7d044044c97)) 459 | (pin "8" (uuid 9711c471-9fef-4c47-a8bb-dbb7861fc7f5)) 460 | (pin "9" (uuid 4875c4e7-8ec2-47f1-a20e-42413b9c1e4a)) 461 | (pin "10" (uuid 0d8c1296-05ab-46c9-b16d-99728b6eacd6)) 462 | (pin "11" (uuid 41727653-a496-48b3-ac11-0e25bdbe5198)) 463 | (pin "12" (uuid b9a8ff4c-342a-4f44-bfb4-1b1895e9679b)) 464 | (pin "13" (uuid 29941c34-ddd5-4a9c-bdef-5bf81398f604)) 465 | (pin "14" (uuid c589a3b6-28de-47c0-87df-1950ce799f45)) 466 | (pin "7" (uuid f0358a25-a450-4144-94fb-900372ab12a7)) 467 | ) 468 | 469 | (symbol (lib_id "74xx:74LS04") (at 139.7 88.9 0) (unit 7) 470 | (in_bom yes) (on_board yes) 471 | (uuid 00000000-0000-0000-0000-00005f34a12f) 472 | (property "Reference" "U1" (id 0) (at 145.542 87.7316 0) 473 | (effects (font (size 1.27 1.27)) (justify left)) 474 | ) 475 | (property "Value" "74LS04" (id 1) (at 145.542 90.043 0) 476 | (effects (font (size 1.27 1.27)) (justify left)) 477 | ) 478 | (property "Footprint" "" (id 2) (at 139.7 88.9 0) 479 | (effects (font (size 1.27 1.27)) hide) 480 | ) 481 | (property "Datasheet" "http://www.ti.com/lit/gpn/sn74LS04" (id 3) (at 139.7 88.9 0) 482 | (effects (font (size 1.27 1.27)) hide) 483 | ) 484 | (pin "1" (uuid 6202c74f-4d05-4660-b905-0e5ca6889128)) 485 | (pin "2" (uuid 011a7ac5-7a54-4c9a-97b1-3c71ef0bed96)) 486 | (pin "3" (uuid 98b08e85-9973-4c19-9269-2dcea15b1681)) 487 | (pin "4" (uuid f023d9a1-53db-47ae-bc98-ce0c0852432d)) 488 | (pin "5" (uuid a1064e68-0e5f-40e9-9c19-682469444ee6)) 489 | (pin "6" (uuid 819e8bc3-d0a5-4ab9-ad3b-83fcfff57d5f)) 490 | (pin "8" (uuid ff2b7b52-4097-4d64-8511-d7ac105c5a57)) 491 | (pin "9" (uuid f85ce822-c4cd-4bc6-8c31-cfad683d10ae)) 492 | (pin "10" (uuid a4bb7bae-f982-4b25-a9eb-bc556b618638)) 493 | (pin "11" (uuid 84529ab6-9a65-4c87-b095-7a53fb755e2c)) 494 | (pin "12" (uuid 3d1ca20e-431d-45c0-9443-c5f597b34af3)) 495 | (pin "13" (uuid 2bd0a9eb-83dd-424f-92d0-bd4297676cbf)) 496 | (pin "14" (uuid bf0862f0-7b4d-4da4-a5e8-91a9bdbee56f)) 497 | (pin "7" (uuid 0639e567-d45a-4c84-98fa-a167b211ae20)) 498 | ) 499 | 500 | (symbol (lib_id "power:VCC") (at 139.7 76.2 0) (unit 1) 501 | (in_bom yes) (on_board yes) 502 | (uuid 00000000-0000-0000-0000-00005f34bc05) 503 | (property "Reference" "#PWR01" (id 0) (at 139.7 80.01 0) 504 | (effects (font (size 1.27 1.27)) hide) 505 | ) 506 | (property "Value" "VCC" (id 1) (at 140.081 71.8058 0)) 507 | (property "Footprint" "" (id 2) (at 139.7 76.2 0) 508 | (effects (font (size 1.27 1.27)) hide) 509 | ) 510 | (property "Datasheet" "" (id 3) (at 139.7 76.2 0) 511 | (effects (font (size 1.27 1.27)) hide) 512 | ) 513 | (pin "1" (uuid 0030af45-9cbd-4108-9b0f-2e3885996ada)) 514 | ) 515 | 516 | (symbol (lib_id "power:GND") (at 139.7 101.6 0) (unit 1) 517 | (in_bom yes) (on_board yes) 518 | (uuid 00000000-0000-0000-0000-00005f34c535) 519 | (property "Reference" "#PWR02" (id 0) (at 139.7 107.95 0) 520 | (effects (font (size 1.27 1.27)) hide) 521 | ) 522 | (property "Value" "GND" (id 1) (at 139.827 105.9942 0)) 523 | (property "Footprint" "" (id 2) (at 139.7 101.6 0) 524 | (effects (font (size 1.27 1.27)) hide) 525 | ) 526 | (property "Datasheet" "" (id 3) (at 139.7 101.6 0) 527 | (effects (font (size 1.27 1.27)) hide) 528 | ) 529 | (pin "1" (uuid 03d3f3dc-57cc-4a5d-b588-5d7b6cd2d907)) 530 | ) 531 | 532 | (sheet (at 114.3 127) (size 25.4 12.7) (fields_autoplaced) 533 | (stroke (width 0) (type solid) (color 0 0 0 0)) 534 | (fill (color 0 0 0 0.0000)) 535 | (uuid 00000000-0000-0000-0000-00005f3bb8bb) 536 | (property "Sheet name" "Deeper test" (id 0) (at 114.3 126.2884 0) 537 | (effects (font (size 1.27 1.27)) (justify left bottom)) 538 | ) 539 | (property "Sheet file" "deeper.kicad_sch" (id 1) (at 114.3 140.2846 0) 540 | (effects (font (size 1.27 1.27)) (justify left top)) 541 | ) 542 | ) 543 | ) 544 | -------------------------------------------------------------------------------- /tests/cases/2/b/deeper.kicad_sch: -------------------------------------------------------------------------------- 1 | (kicad_sch (version 20211123) (generator eeschema) 2 | 3 | (uuid 3b838d52-596d-4e4d-a6ac-e4c8e7621137) 4 | 5 | (paper "A4" portrait) 6 | 7 | (title_block 8 | (date " ") 9 | ) 10 | 11 | (lib_symbols 12 | (symbol "Device:R" (pin_numbers hide) (pin_names (offset 0)) (in_bom yes) (on_board yes) 13 | (property "Reference" "R" (id 0) (at 2.032 0 90) 14 | (effects (font (size 1.27 1.27))) 15 | ) 16 | (property "Value" "R" (id 1) (at 0 0 90) 17 | (effects (font (size 1.27 1.27))) 18 | ) 19 | (property "Footprint" "" (id 2) (at -1.778 0 90) 20 | (effects (font (size 1.27 1.27)) hide) 21 | ) 22 | (property "Datasheet" "~" (id 3) (at 0 0 0) 23 | (effects (font (size 1.27 1.27)) hide) 24 | ) 25 | (property "ki_keywords" "R res resistor" (id 4) (at 0 0 0) 26 | (effects (font (size 1.27 1.27)) hide) 27 | ) 28 | (property "ki_description" "Resistor" (id 5) (at 0 0 0) 29 | (effects (font (size 1.27 1.27)) hide) 30 | ) 31 | (property "ki_fp_filters" "R_*" (id 6) (at 0 0 0) 32 | (effects (font (size 1.27 1.27)) hide) 33 | ) 34 | (symbol "R_0_1" 35 | (rectangle (start -1.016 -2.54) (end 1.016 2.54) 36 | (stroke (width 0.254) (type default) (color 0 0 0 0)) 37 | (fill (type none)) 38 | ) 39 | ) 40 | (symbol "R_1_1" 41 | (pin passive line (at 0 3.81 270) (length 1.27) 42 | (name "~" (effects (font (size 1.27 1.27)))) 43 | (number "1" (effects (font (size 1.27 1.27)))) 44 | ) 45 | (pin passive line (at 0 -3.81 90) (length 1.27) 46 | (name "~" (effects (font (size 1.27 1.27)))) 47 | (number "2" (effects (font (size 1.27 1.27)))) 48 | ) 49 | ) 50 | ) 51 | ) 52 | 53 | 54 | (symbol (lib_id "Device:R") (at 139.7 80.01 0) (unit 1) 55 | (in_bom yes) (on_board yes) 56 | (uuid 00000000-0000-0000-0000-00005f3bbcff) 57 | (property "Reference" "R3" (id 0) (at 141.478 78.8416 0) 58 | (effects (font (size 1.27 1.27)) (justify left)) 59 | ) 60 | (property "Value" "1m" (id 1) (at 141.478 81.153 0) 61 | (effects (font (size 1.27 1.27)) (justify left)) 62 | ) 63 | (property "Footprint" "" (id 2) (at 137.922 80.01 90) 64 | (effects (font (size 1.27 1.27)) hide) 65 | ) 66 | (property "Datasheet" "~" (id 3) (at 139.7 80.01 0) 67 | (effects (font (size 1.27 1.27)) hide) 68 | ) 69 | (pin "1" (uuid c8dca1a6-f09a-4305-804d-2295b5448f55)) 70 | (pin "2" (uuid 4dec140a-3f2f-4b17-b6f0-89a0fc1cf30b)) 71 | ) 72 | ) 73 | -------------------------------------------------------------------------------- /tests/cases/2/b/sub-sheet.kicad_sch: -------------------------------------------------------------------------------- 1 | (kicad_sch (version 20211123) (generator eeschema) 2 | 3 | (uuid 5b2b5c7d-f943-4634-9f0a-e9561705c49d) 4 | 5 | (paper "User" 297.002 210.007) 6 | 7 | (title_block 8 | (date " ") 9 | ) 10 | 11 | (lib_symbols 12 | (symbol "74xx:74LS04" (in_bom yes) (on_board yes) 13 | (property "Reference" "U" (id 0) (at 0 1.27 0) 14 | (effects (font (size 1.27 1.27))) 15 | ) 16 | (property "Value" "74LS04" (id 1) (at 0 -1.27 0) 17 | (effects (font (size 1.27 1.27))) 18 | ) 19 | (property "Footprint" "" (id 2) (at 0 0 0) 20 | (effects (font (size 1.27 1.27)) hide) 21 | ) 22 | (property "Datasheet" "http://www.ti.com/lit/gpn/sn74LS04" (id 3) (at 0 0 0) 23 | (effects (font (size 1.27 1.27)) hide) 24 | ) 25 | (property "ki_locked" "" (id 4) (at 0 0 0) 26 | (effects (font (size 1.27 1.27))) 27 | ) 28 | (property "ki_keywords" "TTL not inv" (id 5) (at 0 0 0) 29 | (effects (font (size 1.27 1.27)) hide) 30 | ) 31 | (property "ki_description" "Hex Inverter" (id 6) (at 0 0 0) 32 | (effects (font (size 1.27 1.27)) hide) 33 | ) 34 | (property "ki_fp_filters" "DIP*W7.62mm* SSOP?14* TSSOP?14*" (id 7) (at 0 0 0) 35 | (effects (font (size 1.27 1.27)) hide) 36 | ) 37 | (symbol "74LS04_1_0" 38 | (polyline 39 | (pts 40 | (xy -3.81 3.81) 41 | (xy -3.81 -3.81) 42 | (xy 3.81 0) 43 | (xy -3.81 3.81) 44 | ) 45 | (stroke (width 0.254) (type default) (color 0 0 0 0)) 46 | (fill (type background)) 47 | ) 48 | (pin input line (at -7.62 0 0) (length 3.81) 49 | (name "~" (effects (font (size 1.27 1.27)))) 50 | (number "1" (effects (font (size 1.27 1.27)))) 51 | ) 52 | (pin output inverted (at 7.62 0 180) (length 3.81) 53 | (name "~" (effects (font (size 1.27 1.27)))) 54 | (number "2" (effects (font (size 1.27 1.27)))) 55 | ) 56 | ) 57 | (symbol "74LS04_2_0" 58 | (polyline 59 | (pts 60 | (xy -3.81 3.81) 61 | (xy -3.81 -3.81) 62 | (xy 3.81 0) 63 | (xy -3.81 3.81) 64 | ) 65 | (stroke (width 0.254) (type default) (color 0 0 0 0)) 66 | (fill (type background)) 67 | ) 68 | (pin input line (at -7.62 0 0) (length 3.81) 69 | (name "~" (effects (font (size 1.27 1.27)))) 70 | (number "3" (effects (font (size 1.27 1.27)))) 71 | ) 72 | (pin output inverted (at 7.62 0 180) (length 3.81) 73 | (name "~" (effects (font (size 1.27 1.27)))) 74 | (number "4" (effects (font (size 1.27 1.27)))) 75 | ) 76 | ) 77 | (symbol "74LS04_3_0" 78 | (polyline 79 | (pts 80 | (xy -3.81 3.81) 81 | (xy -3.81 -3.81) 82 | (xy 3.81 0) 83 | (xy -3.81 3.81) 84 | ) 85 | (stroke (width 0.254) (type default) (color 0 0 0 0)) 86 | (fill (type background)) 87 | ) 88 | (pin input line (at -7.62 0 0) (length 3.81) 89 | (name "~" (effects (font (size 1.27 1.27)))) 90 | (number "5" (effects (font (size 1.27 1.27)))) 91 | ) 92 | (pin output inverted (at 7.62 0 180) (length 3.81) 93 | (name "~" (effects (font (size 1.27 1.27)))) 94 | (number "6" (effects (font (size 1.27 1.27)))) 95 | ) 96 | ) 97 | (symbol "74LS04_4_0" 98 | (polyline 99 | (pts 100 | (xy -3.81 3.81) 101 | (xy -3.81 -3.81) 102 | (xy 3.81 0) 103 | (xy -3.81 3.81) 104 | ) 105 | (stroke (width 0.254) (type default) (color 0 0 0 0)) 106 | (fill (type background)) 107 | ) 108 | (pin output inverted (at 7.62 0 180) (length 3.81) 109 | (name "~" (effects (font (size 1.27 1.27)))) 110 | (number "8" (effects (font (size 1.27 1.27)))) 111 | ) 112 | (pin input line (at -7.62 0 0) (length 3.81) 113 | (name "~" (effects (font (size 1.27 1.27)))) 114 | (number "9" (effects (font (size 1.27 1.27)))) 115 | ) 116 | ) 117 | (symbol "74LS04_5_0" 118 | (polyline 119 | (pts 120 | (xy -3.81 3.81) 121 | (xy -3.81 -3.81) 122 | (xy 3.81 0) 123 | (xy -3.81 3.81) 124 | ) 125 | (stroke (width 0.254) (type default) (color 0 0 0 0)) 126 | (fill (type background)) 127 | ) 128 | (pin output inverted (at 7.62 0 180) (length 3.81) 129 | (name "~" (effects (font (size 1.27 1.27)))) 130 | (number "10" (effects (font (size 1.27 1.27)))) 131 | ) 132 | (pin input line (at -7.62 0 0) (length 3.81) 133 | (name "~" (effects (font (size 1.27 1.27)))) 134 | (number "11" (effects (font (size 1.27 1.27)))) 135 | ) 136 | ) 137 | (symbol "74LS04_6_0" 138 | (polyline 139 | (pts 140 | (xy -3.81 3.81) 141 | (xy -3.81 -3.81) 142 | (xy 3.81 0) 143 | (xy -3.81 3.81) 144 | ) 145 | (stroke (width 0.254) (type default) (color 0 0 0 0)) 146 | (fill (type background)) 147 | ) 148 | (pin output inverted (at 7.62 0 180) (length 3.81) 149 | (name "~" (effects (font (size 1.27 1.27)))) 150 | (number "12" (effects (font (size 1.27 1.27)))) 151 | ) 152 | (pin input line (at -7.62 0 0) (length 3.81) 153 | (name "~" (effects (font (size 1.27 1.27)))) 154 | (number "13" (effects (font (size 1.27 1.27)))) 155 | ) 156 | ) 157 | (symbol "74LS04_7_0" 158 | (pin power_in line (at 0 12.7 270) (length 5.08) 159 | (name "VCC" (effects (font (size 1.27 1.27)))) 160 | (number "14" (effects (font (size 1.27 1.27)))) 161 | ) 162 | (pin power_in line (at 0 -12.7 90) (length 5.08) 163 | (name "GND" (effects (font (size 1.27 1.27)))) 164 | (number "7" (effects (font (size 1.27 1.27)))) 165 | ) 166 | ) 167 | (symbol "74LS04_7_1" 168 | (rectangle (start -5.08 7.62) (end 5.08 -7.62) 169 | (stroke (width 0.254) (type default) (color 0 0 0 0)) 170 | (fill (type background)) 171 | ) 172 | ) 173 | ) 174 | (symbol "power:GND" (power) (pin_names (offset 0)) (in_bom yes) (on_board yes) 175 | (property "Reference" "#PWR" (id 0) (at 0 -6.35 0) 176 | (effects (font (size 1.27 1.27)) hide) 177 | ) 178 | (property "Value" "GND" (id 1) (at 0 -3.81 0) 179 | (effects (font (size 1.27 1.27))) 180 | ) 181 | (property "Footprint" "" (id 2) (at 0 0 0) 182 | (effects (font (size 1.27 1.27)) hide) 183 | ) 184 | (property "Datasheet" "" (id 3) (at 0 0 0) 185 | (effects (font (size 1.27 1.27)) hide) 186 | ) 187 | (property "ki_keywords" "power-flag" (id 4) (at 0 0 0) 188 | (effects (font (size 1.27 1.27)) hide) 189 | ) 190 | (property "ki_description" "Power symbol creates a global label with name \"GND\" , ground" (id 5) (at 0 0 0) 191 | (effects (font (size 1.27 1.27)) hide) 192 | ) 193 | (symbol "GND_0_1" 194 | (polyline 195 | (pts 196 | (xy 0 0) 197 | (xy 0 -1.27) 198 | (xy 1.27 -1.27) 199 | (xy 0 -2.54) 200 | (xy -1.27 -1.27) 201 | (xy 0 -1.27) 202 | ) 203 | (stroke (width 0) (type default) (color 0 0 0 0)) 204 | (fill (type none)) 205 | ) 206 | ) 207 | (symbol "GND_1_1" 208 | (pin power_in line (at 0 0 270) (length 0) hide 209 | (name "GND" (effects (font (size 1.27 1.27)))) 210 | (number "1" (effects (font (size 1.27 1.27)))) 211 | ) 212 | ) 213 | ) 214 | (symbol "power:VCC" (power) (pin_names (offset 0)) (in_bom yes) (on_board yes) 215 | (property "Reference" "#PWR" (id 0) (at 0 -3.81 0) 216 | (effects (font (size 1.27 1.27)) hide) 217 | ) 218 | (property "Value" "VCC" (id 1) (at 0 3.81 0) 219 | (effects (font (size 1.27 1.27))) 220 | ) 221 | (property "Footprint" "" (id 2) (at 0 0 0) 222 | (effects (font (size 1.27 1.27)) hide) 223 | ) 224 | (property "Datasheet" "" (id 3) (at 0 0 0) 225 | (effects (font (size 1.27 1.27)) hide) 226 | ) 227 | (property "ki_keywords" "power-flag" (id 4) (at 0 0 0) 228 | (effects (font (size 1.27 1.27)) hide) 229 | ) 230 | (property "ki_description" "Power symbol creates a global label with name \"VCC\"" (id 5) (at 0 0 0) 231 | (effects (font (size 1.27 1.27)) hide) 232 | ) 233 | (symbol "VCC_0_1" 234 | (polyline 235 | (pts 236 | (xy -0.762 1.27) 237 | (xy 0 2.54) 238 | ) 239 | (stroke (width 0) (type default) (color 0 0 0 0)) 240 | (fill (type none)) 241 | ) 242 | (polyline 243 | (pts 244 | (xy 0 0) 245 | (xy 0 2.54) 246 | ) 247 | (stroke (width 0) (type default) (color 0 0 0 0)) 248 | (fill (type none)) 249 | ) 250 | (polyline 251 | (pts 252 | (xy 0 2.54) 253 | (xy 0.762 1.27) 254 | ) 255 | (stroke (width 0) (type default) (color 0 0 0 0)) 256 | (fill (type none)) 257 | ) 258 | ) 259 | (symbol "VCC_1_1" 260 | (pin power_in line (at 0 0 90) (length 0) hide 261 | (name "VCC" (effects (font (size 1.27 1.27)))) 262 | (number "1" (effects (font (size 1.27 1.27)))) 263 | ) 264 | ) 265 | ) 266 | ) 267 | 268 | 269 | (wire (pts (xy 104.14 63.5) (xy 106.68 63.5)) 270 | (stroke (width 0) (type default) (color 0 0 0 0)) 271 | (uuid 10109f84-4940-47f8-8640-91f185ac9bc1) 272 | ) 273 | (wire (pts (xy 175.26 63.5) (xy 177.8 63.5)) 274 | (stroke (width 0) (type default) (color 0 0 0 0)) 275 | (uuid 47baf4b1-0938-497d-88f9-671136aa8be7) 276 | ) 277 | (wire (pts (xy 121.92 63.5) (xy 124.46 63.5)) 278 | (stroke (width 0) (type default) (color 0 0 0 0)) 279 | (uuid 55e740a3-0735-4744-896e-2bf5437093b9) 280 | ) 281 | (wire (pts (xy 139.7 104.14) (xy 139.7 101.6)) 282 | (stroke (width 0) (type default) (color 0 0 0 0)) 283 | (uuid 9dbcc283-a9aa-46fd-91d6-3ebd238d1195) 284 | ) 285 | (wire (pts (xy 157.48 63.5) (xy 160.02 63.5)) 286 | (stroke (width 0) (type default) (color 0 0 0 0)) 287 | (uuid c022004a-c968-410e-b59e-fbab0e561e9d) 288 | ) 289 | (wire (pts (xy 193.04 63.5) (xy 195.58 63.5)) 290 | (stroke (width 0) (type default) (color 0 0 0 0)) 291 | (uuid e615f7aa-337e-474d-9615-2ad82b1c44ca) 292 | ) 293 | (wire (pts (xy 85.09 63.5) (xy 88.9 63.5)) 294 | (stroke (width 0) (type default) (color 0 0 0 0)) 295 | (uuid ef8fe2ac-6a7f-4682-9418-b801a1b10a3b) 296 | ) 297 | (wire (pts (xy 139.7 63.5) (xy 142.24 63.5)) 298 | (stroke (width 0) (type default) (color 0 0 0 0)) 299 | (uuid f4f99e3d-7269-4f6a-a759-16ad2a258779) 300 | ) 301 | 302 | (hierarchical_label "IN" (shape input) (at 85.09 63.5 180) 303 | (effects (font (size 1.27 1.27)) (justify right)) 304 | (uuid 4fb02e58-160a-4a39-9f22-d0c75e82ee72) 305 | ) 306 | (hierarchical_label "OUT" (shape output) (at 195.58 63.5 0) 307 | (effects (font (size 1.27 1.27)) (justify left)) 308 | (uuid 77ed3941-d133-4aef-a9af-5a39322d14eb) 309 | ) 310 | 311 | (symbol (lib_id "74xx:74LS04") (at 96.52 63.5 0) (unit 1) 312 | (in_bom yes) (on_board yes) 313 | (uuid 00000000-0000-0000-0000-00005f34307a) 314 | (property "Reference" "U1" (id 0) (at 96.52 55.4482 0)) 315 | (property "Value" "74LS04" (id 1) (at 96.52 57.7596 0)) 316 | (property "Footprint" "" (id 2) (at 96.52 63.5 0) 317 | (effects (font (size 1.27 1.27)) hide) 318 | ) 319 | (property "Datasheet" "http://www.ti.com/lit/gpn/sn74LS04" (id 3) (at 96.52 63.5 0) 320 | (effects (font (size 1.27 1.27)) hide) 321 | ) 322 | (pin "1" (uuid f1e9ac99-f8d4-4d05-beb3-290dccb8d277)) 323 | (pin "2" (uuid 014c98b2-ac35-4831-825d-74c6fa5ddc67)) 324 | (pin "3" (uuid 2282aa17-25e5-4b18-b436-0d5d8b80c271)) 325 | (pin "4" (uuid 779d408a-048e-4117-b37b-52b0f8855fe0)) 326 | (pin "5" (uuid 0209c2aa-e6bb-4fb6-ad76-6e4b9b1b16f7)) 327 | (pin "6" (uuid 867bfffe-ae91-4651-94e5-196c3c600344)) 328 | (pin "8" (uuid 46124aee-812d-4b49-8b07-54040860b90b)) 329 | (pin "9" (uuid bfb61142-9bc3-4e95-89e6-4ced14b533f1)) 330 | (pin "10" (uuid c37c1333-9b31-4605-8469-7903812fede5)) 331 | (pin "11" (uuid f638fbb1-32ba-4d92-b985-ace474e20207)) 332 | (pin "12" (uuid 0105483f-ed6d-4621-a345-4f843c0072bf)) 333 | (pin "13" (uuid 052e0faa-99fa-4dba-8771-a6a810a9615f)) 334 | (pin "14" (uuid cad197d5-2063-47e9-a3da-c0da8b26e6ec)) 335 | (pin "7" (uuid a5eca4b9-9690-4686-9caf-8a9265e35104)) 336 | ) 337 | 338 | (symbol (lib_id "74xx:74LS04") (at 114.3 63.5 0) (unit 2) 339 | (in_bom yes) (on_board yes) 340 | (uuid 00000000-0000-0000-0000-00005f344a0e) 341 | (property "Reference" "U1" (id 0) (at 114.3 55.4482 0)) 342 | (property "Value" "74LS04" (id 1) (at 114.3 57.7596 0)) 343 | (property "Footprint" "" (id 2) (at 114.3 63.5 0) 344 | (effects (font (size 1.27 1.27)) hide) 345 | ) 346 | (property "Datasheet" "http://www.ti.com/lit/gpn/sn74LS04" (id 3) (at 114.3 63.5 0) 347 | (effects (font (size 1.27 1.27)) hide) 348 | ) 349 | (pin "1" (uuid 7eb5089c-0f47-4b87-beab-8ff80b20792f)) 350 | (pin "2" (uuid 60102715-3c93-4a15-8674-20c8b7c57be4)) 351 | (pin "3" (uuid c1630947-26ec-4031-96dd-f1cdf939d3a7)) 352 | (pin "4" (uuid e2858b6c-d9d5-4bfd-a26d-d7110c9ff81c)) 353 | (pin "5" (uuid 8e8bdbab-9511-4475-9eff-1510f6b5ad23)) 354 | (pin "6" (uuid 6ae8ec3f-43b9-4112-bfd5-128204c7759e)) 355 | (pin "8" (uuid cf6c2bfe-fd20-4797-a562-aefcd563a7af)) 356 | (pin "9" (uuid 99a6c8b8-68c2-479d-ad69-5f387013f1da)) 357 | (pin "10" (uuid 349db570-c21f-46f9-80d4-b68f3473ae15)) 358 | (pin "11" (uuid 76d634a3-0256-4a17-8718-470b7e39e4ac)) 359 | (pin "12" (uuid 72b533b9-dc1e-46c5-a29d-c8b49a52ce5d)) 360 | (pin "13" (uuid 132268c6-c19f-48f6-8da5-42a0ba313cd0)) 361 | (pin "14" (uuid 5cd18a01-4203-4bf0-961c-09af1ded6848)) 362 | (pin "7" (uuid 612772b8-33ef-4e45-abaa-4fba74ec3e0b)) 363 | ) 364 | 365 | (symbol (lib_id "74xx:74LS04") (at 132.08 63.5 0) (unit 3) 366 | (in_bom yes) (on_board yes) 367 | (uuid 00000000-0000-0000-0000-00005f345e39) 368 | (property "Reference" "U1" (id 0) (at 132.08 55.4482 0)) 369 | (property "Value" "74LS04" (id 1) (at 132.08 57.7596 0)) 370 | (property "Footprint" "" (id 2) (at 132.08 63.5 0) 371 | (effects (font (size 1.27 1.27)) hide) 372 | ) 373 | (property "Datasheet" "http://www.ti.com/lit/gpn/sn74LS04" (id 3) (at 132.08 63.5 0) 374 | (effects (font (size 1.27 1.27)) hide) 375 | ) 376 | (pin "1" (uuid bbac1062-acc9-427d-9486-84613968193d)) 377 | (pin "2" (uuid aa6fbc5d-962a-421b-b0f8-5cc0634fa940)) 378 | (pin "3" (uuid ee86faf1-b781-415c-a773-7ffd42b4ccac)) 379 | (pin "4" (uuid f60b38a1-6bcf-4425-85f9-40dd840f097e)) 380 | (pin "5" (uuid 9a609661-5895-4db4-a600-14bffdacc16e)) 381 | (pin "6" (uuid f6c777f1-b4a6-4529-9b29-67dfedcb1744)) 382 | (pin "8" (uuid d6ee852a-2125-4cbb-b4c0-2c1f7b3da0fd)) 383 | (pin "9" (uuid 66b18884-be07-4547-81e4-598e8796b478)) 384 | (pin "10" (uuid 23b24c2d-06bc-4f64-bbba-4a1836993bd3)) 385 | (pin "11" (uuid ab204973-c25f-4438-a480-b6256c3846e5)) 386 | (pin "12" (uuid b7d46009-86c1-4915-8882-efed73321a1b)) 387 | (pin "13" (uuid ae6c4a1d-7821-456a-9178-e99999733573)) 388 | (pin "14" (uuid bb4b6efa-8944-40e5-8681-42c8086dfb79)) 389 | (pin "7" (uuid 7b67e898-9526-4fba-8f77-59962db970c7)) 390 | ) 391 | 392 | (symbol (lib_id "74xx:74LS04") (at 149.86 63.5 0) (unit 4) 393 | (in_bom yes) (on_board yes) 394 | (uuid 00000000-0000-0000-0000-00005f346e8b) 395 | (property "Reference" "U1" (id 0) (at 149.86 55.4482 0)) 396 | (property "Value" "74LS04" (id 1) (at 149.86 57.7596 0)) 397 | (property "Footprint" "" (id 2) (at 149.86 63.5 0) 398 | (effects (font (size 1.27 1.27)) hide) 399 | ) 400 | (property "Datasheet" "http://www.ti.com/lit/gpn/sn74LS04" (id 3) (at 149.86 63.5 0) 401 | (effects (font (size 1.27 1.27)) hide) 402 | ) 403 | (pin "1" (uuid 2905dd4f-e9df-4c8d-9d14-37e5474058d3)) 404 | (pin "2" (uuid 909aa50b-4012-4e97-bcca-8dbd688e9e99)) 405 | (pin "3" (uuid 8520760e-f968-483e-8989-07d96ff7c0bd)) 406 | (pin "4" (uuid 472927d7-713d-4434-aae2-cab12a79506c)) 407 | (pin "5" (uuid 5c2e0337-2447-4117-8a69-8ab7a6736b16)) 408 | (pin "6" (uuid 44dcea25-62da-41dc-8e4e-72c45198747e)) 409 | (pin "8" (uuid 47d49bdd-bacb-468f-bcfb-647fd9ccff38)) 410 | (pin "9" (uuid 71c84a4a-5d81-46df-a82f-84ff433708b7)) 411 | (pin "10" (uuid ccbabbc8-3805-4494-9f79-0e7c58491280)) 412 | (pin "11" (uuid 5f478e4e-1c37-417f-802a-dcd64da6ec69)) 413 | (pin "12" (uuid f4169419-5cd3-4db1-860a-b37e557bc728)) 414 | (pin "13" (uuid 0eb475d7-7c32-41bc-a947-f6e04adbfa7d)) 415 | (pin "14" (uuid c3d6c577-12ce-4f2f-84bd-dda13a8fe40f)) 416 | (pin "7" (uuid 2843c226-58ef-4dc8-ad8f-3fcb74143c53)) 417 | ) 418 | 419 | (symbol (lib_id "74xx:74LS04") (at 167.64 63.5 0) (unit 5) 420 | (in_bom yes) (on_board yes) 421 | (uuid 00000000-0000-0000-0000-00005f348270) 422 | (property "Reference" "U1" (id 0) (at 167.64 55.4482 0)) 423 | (property "Value" "74LS04" (id 1) (at 167.64 57.7596 0)) 424 | (property "Footprint" "" (id 2) (at 167.64 63.5 0) 425 | (effects (font (size 1.27 1.27)) hide) 426 | ) 427 | (property "Datasheet" "http://www.ti.com/lit/gpn/sn74LS04" (id 3) (at 167.64 63.5 0) 428 | (effects (font (size 1.27 1.27)) hide) 429 | ) 430 | (pin "1" (uuid 50c06431-98b4-4782-9bc2-1f48dd66012e)) 431 | (pin "2" (uuid 237913c0-554e-4f92-9454-74fca47a4ff0)) 432 | (pin "3" (uuid 22591fce-1b7c-432b-9f65-ca786ad38164)) 433 | (pin "4" (uuid cf7394f4-fb02-417b-8fcd-f7a746f2f953)) 434 | (pin "5" (uuid 1c6ed6bd-b27d-4107-a50a-c5fa1ca815a8)) 435 | (pin "6" (uuid c9913a2d-9fdc-477b-99fb-40ae623b387f)) 436 | (pin "8" (uuid 1dd5c978-2ace-45f7-9c90-88b9fc1a4ffc)) 437 | (pin "9" (uuid 9a40e5da-1346-4d0c-af28-5a2601f44304)) 438 | (pin "10" (uuid 01fec0dc-78cf-4a7d-851f-e24a9edd4a86)) 439 | (pin "11" (uuid 92e30d01-6115-49a3-bead-4b8b74d533c9)) 440 | (pin "12" (uuid 7092ba6d-f06a-417d-9814-317dd365bfc4)) 441 | (pin "13" (uuid bce2b649-7e95-435b-ad4a-9467fe595946)) 442 | (pin "14" (uuid cee262bb-8369-447b-8bc6-8fc7e99304ba)) 443 | (pin "7" (uuid d69f10fe-7d7e-437f-89c6-5d4591a60c24)) 444 | ) 445 | 446 | (symbol (lib_id "74xx:74LS04") (at 185.42 63.5 0) (unit 6) 447 | (in_bom yes) (on_board yes) 448 | (uuid 00000000-0000-0000-0000-00005f348d95) 449 | (property "Reference" "U1" (id 0) (at 185.42 55.4482 0)) 450 | (property "Value" "74LS04" (id 1) (at 185.42 57.7596 0)) 451 | (property "Footprint" "" (id 2) (at 185.42 63.5 0) 452 | (effects (font (size 1.27 1.27)) hide) 453 | ) 454 | (property "Datasheet" "http://www.ti.com/lit/gpn/sn74LS04" (id 3) (at 185.42 63.5 0) 455 | (effects (font (size 1.27 1.27)) hide) 456 | ) 457 | (pin "1" (uuid c1de8072-85af-41fb-bbae-891234fe6286)) 458 | (pin "2" (uuid 92eba779-7a6c-4f2f-bf8a-9d01b4559540)) 459 | (pin "3" (uuid 87aa64d0-4063-42a0-8b26-e6afc8ae74de)) 460 | (pin "4" (uuid 57d78745-19ee-4265-be9f-6570894e3e61)) 461 | (pin "5" (uuid 924bfea8-9b40-4840-882f-6d467382007a)) 462 | (pin "6" (uuid 6353b9ee-662c-49d5-8069-f7d044044c97)) 463 | (pin "8" (uuid 9711c471-9fef-4c47-a8bb-dbb7861fc7f5)) 464 | (pin "9" (uuid 4875c4e7-8ec2-47f1-a20e-42413b9c1e4a)) 465 | (pin "10" (uuid 0d8c1296-05ab-46c9-b16d-99728b6eacd6)) 466 | (pin "11" (uuid 41727653-a496-48b3-ac11-0e25bdbe5198)) 467 | (pin "12" (uuid b9a8ff4c-342a-4f44-bfb4-1b1895e9679b)) 468 | (pin "13" (uuid 29941c34-ddd5-4a9c-bdef-5bf81398f604)) 469 | (pin "14" (uuid c589a3b6-28de-47c0-87df-1950ce799f45)) 470 | (pin "7" (uuid f0358a25-a450-4144-94fb-900372ab12a7)) 471 | ) 472 | 473 | (symbol (lib_id "74xx:74LS04") (at 139.7 88.9 0) (unit 7) 474 | (in_bom yes) (on_board yes) 475 | (uuid 00000000-0000-0000-0000-00005f34a12f) 476 | (property "Reference" "U1" (id 0) (at 145.542 87.7316 0) 477 | (effects (font (size 1.27 1.27)) (justify left)) 478 | ) 479 | (property "Value" "74LS04" (id 1) (at 145.542 90.043 0) 480 | (effects (font (size 1.27 1.27)) (justify left)) 481 | ) 482 | (property "Footprint" "" (id 2) (at 139.7 88.9 0) 483 | (effects (font (size 1.27 1.27)) hide) 484 | ) 485 | (property "Datasheet" "http://www.ti.com/lit/gpn/sn74LS04" (id 3) (at 139.7 88.9 0) 486 | (effects (font (size 1.27 1.27)) hide) 487 | ) 488 | (pin "1" (uuid 6202c74f-4d05-4660-b905-0e5ca6889128)) 489 | (pin "2" (uuid 011a7ac5-7a54-4c9a-97b1-3c71ef0bed96)) 490 | (pin "3" (uuid 98b08e85-9973-4c19-9269-2dcea15b1681)) 491 | (pin "4" (uuid f023d9a1-53db-47ae-bc98-ce0c0852432d)) 492 | (pin "5" (uuid a1064e68-0e5f-40e9-9c19-682469444ee6)) 493 | (pin "6" (uuid 819e8bc3-d0a5-4ab9-ad3b-83fcfff57d5f)) 494 | (pin "8" (uuid ff2b7b52-4097-4d64-8511-d7ac105c5a57)) 495 | (pin "9" (uuid f85ce822-c4cd-4bc6-8c31-cfad683d10ae)) 496 | (pin "10" (uuid a4bb7bae-f982-4b25-a9eb-bc556b618638)) 497 | (pin "11" (uuid 84529ab6-9a65-4c87-b095-7a53fb755e2c)) 498 | (pin "12" (uuid 3d1ca20e-431d-45c0-9443-c5f597b34af3)) 499 | (pin "13" (uuid 2bd0a9eb-83dd-424f-92d0-bd4297676cbf)) 500 | (pin "14" (uuid bf0862f0-7b4d-4da4-a5e8-91a9bdbee56f)) 501 | (pin "7" (uuid 0639e567-d45a-4c84-98fa-a167b211ae20)) 502 | ) 503 | 504 | (symbol (lib_id "power:VCC") (at 139.7 76.2 0) (unit 1) 505 | (in_bom yes) (on_board yes) 506 | (uuid 00000000-0000-0000-0000-00005f34bc05) 507 | (property "Reference" "#PWR01" (id 0) (at 139.7 80.01 0) 508 | (effects (font (size 1.27 1.27)) hide) 509 | ) 510 | (property "Value" "VCC" (id 1) (at 140.081 71.8058 0)) 511 | (property "Footprint" "" (id 2) (at 139.7 76.2 0) 512 | (effects (font (size 1.27 1.27)) hide) 513 | ) 514 | (property "Datasheet" "" (id 3) (at 139.7 76.2 0) 515 | (effects (font (size 1.27 1.27)) hide) 516 | ) 517 | (pin "1" (uuid 0030af45-9cbd-4108-9b0f-2e3885996ada)) 518 | ) 519 | 520 | (symbol (lib_id "power:GND") (at 139.7 104.14 0) (unit 1) 521 | (in_bom yes) (on_board yes) 522 | (uuid 00000000-0000-0000-0000-00005f34c535) 523 | (property "Reference" "#PWR02" (id 0) (at 139.7 110.49 0) 524 | (effects (font (size 1.27 1.27)) hide) 525 | ) 526 | (property "Value" "GND" (id 1) (at 139.827 108.5342 0)) 527 | (property "Footprint" "" (id 2) (at 139.7 104.14 0) 528 | (effects (font (size 1.27 1.27)) hide) 529 | ) 530 | (property "Datasheet" "" (id 3) (at 139.7 104.14 0) 531 | (effects (font (size 1.27 1.27)) hide) 532 | ) 533 | (pin "1" (uuid 03d3f3dc-57cc-4a5d-b588-5d7b6cd2d907)) 534 | ) 535 | 536 | (sheet (at 114.3 127) (size 25.4 12.7) (fields_autoplaced) 537 | (stroke (width 0) (type solid) (color 0 0 0 0)) 538 | (fill (color 0 0 0 0.0000)) 539 | (uuid 00000000-0000-0000-0000-00005f3bb8bb) 540 | (property "Sheet name" "Deeper test" (id 0) (at 114.3 126.2884 0) 541 | (effects (font (size 1.27 1.27)) (justify left bottom)) 542 | ) 543 | (property "Sheet file" "deeper.kicad_sch" (id 1) (at 114.3 140.2846 0) 544 | (effects (font (size 1.27 1.27)) (justify left top)) 545 | ) 546 | ) 547 | ) 548 | -------------------------------------------------------------------------------- /tests/cases/2/ref_sch/diff.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/2/ref_sch/diff.pdf -------------------------------------------------------------------------------- /tests/cases/4/exclude.lst: -------------------------------------------------------------------------------- 1 | 32 2 | 33 3 | 34 4 | 35 5 | 37 6 | 38 7 | 39 8 | 40 9 | 41 10 | 42 11 | 43 12 | 44 13 | 45 14 | 46 15 | 47 16 | 48 17 | 49 18 | -------------------------------------------------------------------------------- /tests/cases/4/include.lst: -------------------------------------------------------------------------------- 1 | 0 2 | 1 3 | 2 4 | 31 5 | 36 6 | -------------------------------------------------------------------------------- /tests/cases/4/include_names.lst: -------------------------------------------------------------------------------- 1 | F.Cu 2 | In1.Cu 3 | In2.Cu 4 | B.Cu 5 | B.Silkscreen 6 | -------------------------------------------------------------------------------- /tests/cases/4/ref_pcb/diff-00.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/4/ref_pcb/diff-00.png -------------------------------------------------------------------------------- /tests/cases/4/ref_pcb/diff-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/4/ref_pcb/diff-10.png -------------------------------------------------------------------------------- /tests/cases/4/ref_pcb/diff-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/4/ref_pcb/diff-20.png -------------------------------------------------------------------------------- /tests/cases/4/ref_pcb/diff-310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/4/ref_pcb/diff-310.png -------------------------------------------------------------------------------- /tests/cases/4/ref_pcb/diff-360.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/4/ref_pcb/diff-360.png -------------------------------------------------------------------------------- /tests/cases/4/ref_pcb2/diff-00.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/4/ref_pcb2/diff-00.png -------------------------------------------------------------------------------- /tests/cases/4/ref_pcb2/diff-10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/4/ref_pcb2/diff-10.png -------------------------------------------------------------------------------- /tests/cases/4/ref_pcb2/diff-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/4/ref_pcb2/diff-20.png -------------------------------------------------------------------------------- /tests/cases/4/ref_pcb2/diff-310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/4/ref_pcb2/diff-310.png -------------------------------------------------------------------------------- /tests/cases/4/ref_pcb2/diff-360.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/4/ref_pcb2/diff-360.png -------------------------------------------------------------------------------- /tests/cases/5/a/deeper.kicad_sch: -------------------------------------------------------------------------------- 1 | (kicad_sch (version 20211123) (generator eeschema) 2 | 3 | (uuid 3b838d52-596d-4e4d-a6ac-e4c8e7621137) 4 | 5 | (paper "A4" portrait) 6 | 7 | (title_block 8 | (date " ") 9 | ) 10 | 11 | (lib_symbols 12 | (symbol "Device:R" (pin_numbers hide) (pin_names (offset 0)) (in_bom yes) (on_board yes) 13 | (property "Reference" "R" (id 0) (at 2.032 0 90) 14 | (effects (font (size 1.27 1.27))) 15 | ) 16 | (property "Value" "R" (id 1) (at 0 0 90) 17 | (effects (font (size 1.27 1.27))) 18 | ) 19 | (property "Footprint" "" (id 2) (at -1.778 0 90) 20 | (effects (font (size 1.27 1.27)) hide) 21 | ) 22 | (property "Datasheet" "~" (id 3) (at 0 0 0) 23 | (effects (font (size 1.27 1.27)) hide) 24 | ) 25 | (property "ki_keywords" "R res resistor" (id 4) (at 0 0 0) 26 | (effects (font (size 1.27 1.27)) hide) 27 | ) 28 | (property "ki_description" "Resistor" (id 5) (at 0 0 0) 29 | (effects (font (size 1.27 1.27)) hide) 30 | ) 31 | (property "ki_fp_filters" "R_*" (id 6) (at 0 0 0) 32 | (effects (font (size 1.27 1.27)) hide) 33 | ) 34 | (symbol "R_0_1" 35 | (rectangle (start -1.016 -2.54) (end 1.016 2.54) 36 | (stroke (width 0.254) (type default) (color 0 0 0 0)) 37 | (fill (type none)) 38 | ) 39 | ) 40 | (symbol "R_1_1" 41 | (pin passive line (at 0 3.81 270) (length 1.27) 42 | (name "~" (effects (font (size 1.27 1.27)))) 43 | (number "1" (effects (font (size 1.27 1.27)))) 44 | ) 45 | (pin passive line (at 0 -3.81 90) (length 1.27) 46 | (name "~" (effects (font (size 1.27 1.27)))) 47 | (number "2" (effects (font (size 1.27 1.27)))) 48 | ) 49 | ) 50 | ) 51 | ) 52 | 53 | 54 | (symbol (lib_id "Device:R") (at 139.7 80.01 0) (unit 1) 55 | (in_bom yes) (on_board yes) 56 | (uuid 00000000-0000-0000-0000-00005f3bbcff) 57 | (property "Reference" "R3" (id 0) (at 141.478 78.8416 0) 58 | (effects (font (size 1.27 1.27)) (justify left)) 59 | ) 60 | (property "Value" "1m" (id 1) (at 141.478 81.153 0) 61 | (effects (font (size 1.27 1.27)) (justify left)) 62 | ) 63 | (property "Footprint" "" (id 2) (at 137.922 80.01 90) 64 | (effects (font (size 1.27 1.27)) hide) 65 | ) 66 | (property "Datasheet" "~" (id 3) (at 139.7 80.01 0) 67 | (effects (font (size 1.27 1.27)) hide) 68 | ) 69 | (pin "1" (uuid c8dca1a6-f09a-4305-804d-2295b5448f55)) 70 | (pin "2" (uuid 4dec140a-3f2f-4b17-b6f0-89a0fc1cf30b)) 71 | ) 72 | ) 73 | -------------------------------------------------------------------------------- /tests/cases/5/a/sub-sheet.kicad_sch: -------------------------------------------------------------------------------- 1 | (kicad_sch (version 20211123) (generator eeschema) 2 | 3 | (uuid 5b2b5c7d-f943-4634-9f0a-e9561705c49d) 4 | 5 | (paper "User" 297.002 210.007) 6 | 7 | (title_block 8 | (date " ") 9 | ) 10 | 11 | (lib_symbols 12 | (symbol "74xx:74LS04" (in_bom yes) (on_board yes) 13 | (property "Reference" "U" (id 0) (at 0 1.27 0) 14 | (effects (font (size 1.27 1.27))) 15 | ) 16 | (property "Value" "74LS04" (id 1) (at 0 -1.27 0) 17 | (effects (font (size 1.27 1.27))) 18 | ) 19 | (property "Footprint" "" (id 2) (at 0 0 0) 20 | (effects (font (size 1.27 1.27)) hide) 21 | ) 22 | (property "Datasheet" "http://www.ti.com/lit/gpn/sn74LS04" (id 3) (at 0 0 0) 23 | (effects (font (size 1.27 1.27)) hide) 24 | ) 25 | (property "ki_locked" "" (id 4) (at 0 0 0) 26 | (effects (font (size 1.27 1.27))) 27 | ) 28 | (property "ki_keywords" "TTL not inv" (id 5) (at 0 0 0) 29 | (effects (font (size 1.27 1.27)) hide) 30 | ) 31 | (property "ki_description" "Hex Inverter" (id 6) (at 0 0 0) 32 | (effects (font (size 1.27 1.27)) hide) 33 | ) 34 | (property "ki_fp_filters" "DIP*W7.62mm* SSOP?14* TSSOP?14*" (id 7) (at 0 0 0) 35 | (effects (font (size 1.27 1.27)) hide) 36 | ) 37 | (symbol "74LS04_1_0" 38 | (polyline 39 | (pts 40 | (xy -3.81 3.81) 41 | (xy -3.81 -3.81) 42 | (xy 3.81 0) 43 | (xy -3.81 3.81) 44 | ) 45 | (stroke (width 0.254) (type default) (color 0 0 0 0)) 46 | (fill (type background)) 47 | ) 48 | (pin input line (at -7.62 0 0) (length 3.81) 49 | (name "~" (effects (font (size 1.27 1.27)))) 50 | (number "1" (effects (font (size 1.27 1.27)))) 51 | ) 52 | (pin output inverted (at 7.62 0 180) (length 3.81) 53 | (name "~" (effects (font (size 1.27 1.27)))) 54 | (number "2" (effects (font (size 1.27 1.27)))) 55 | ) 56 | ) 57 | (symbol "74LS04_2_0" 58 | (polyline 59 | (pts 60 | (xy -3.81 3.81) 61 | (xy -3.81 -3.81) 62 | (xy 3.81 0) 63 | (xy -3.81 3.81) 64 | ) 65 | (stroke (width 0.254) (type default) (color 0 0 0 0)) 66 | (fill (type background)) 67 | ) 68 | (pin input line (at -7.62 0 0) (length 3.81) 69 | (name "~" (effects (font (size 1.27 1.27)))) 70 | (number "3" (effects (font (size 1.27 1.27)))) 71 | ) 72 | (pin output inverted (at 7.62 0 180) (length 3.81) 73 | (name "~" (effects (font (size 1.27 1.27)))) 74 | (number "4" (effects (font (size 1.27 1.27)))) 75 | ) 76 | ) 77 | (symbol "74LS04_3_0" 78 | (polyline 79 | (pts 80 | (xy -3.81 3.81) 81 | (xy -3.81 -3.81) 82 | (xy 3.81 0) 83 | (xy -3.81 3.81) 84 | ) 85 | (stroke (width 0.254) (type default) (color 0 0 0 0)) 86 | (fill (type background)) 87 | ) 88 | (pin input line (at -7.62 0 0) (length 3.81) 89 | (name "~" (effects (font (size 1.27 1.27)))) 90 | (number "5" (effects (font (size 1.27 1.27)))) 91 | ) 92 | (pin output inverted (at 7.62 0 180) (length 3.81) 93 | (name "~" (effects (font (size 1.27 1.27)))) 94 | (number "6" (effects (font (size 1.27 1.27)))) 95 | ) 96 | ) 97 | (symbol "74LS04_4_0" 98 | (polyline 99 | (pts 100 | (xy -3.81 3.81) 101 | (xy -3.81 -3.81) 102 | (xy 3.81 0) 103 | (xy -3.81 3.81) 104 | ) 105 | (stroke (width 0.254) (type default) (color 0 0 0 0)) 106 | (fill (type background)) 107 | ) 108 | (pin output inverted (at 7.62 0 180) (length 3.81) 109 | (name "~" (effects (font (size 1.27 1.27)))) 110 | (number "8" (effects (font (size 1.27 1.27)))) 111 | ) 112 | (pin input line (at -7.62 0 0) (length 3.81) 113 | (name "~" (effects (font (size 1.27 1.27)))) 114 | (number "9" (effects (font (size 1.27 1.27)))) 115 | ) 116 | ) 117 | (symbol "74LS04_5_0" 118 | (polyline 119 | (pts 120 | (xy -3.81 3.81) 121 | (xy -3.81 -3.81) 122 | (xy 3.81 0) 123 | (xy -3.81 3.81) 124 | ) 125 | (stroke (width 0.254) (type default) (color 0 0 0 0)) 126 | (fill (type background)) 127 | ) 128 | (pin output inverted (at 7.62 0 180) (length 3.81) 129 | (name "~" (effects (font (size 1.27 1.27)))) 130 | (number "10" (effects (font (size 1.27 1.27)))) 131 | ) 132 | (pin input line (at -7.62 0 0) (length 3.81) 133 | (name "~" (effects (font (size 1.27 1.27)))) 134 | (number "11" (effects (font (size 1.27 1.27)))) 135 | ) 136 | ) 137 | (symbol "74LS04_6_0" 138 | (polyline 139 | (pts 140 | (xy -3.81 3.81) 141 | (xy -3.81 -3.81) 142 | (xy 3.81 0) 143 | (xy -3.81 3.81) 144 | ) 145 | (stroke (width 0.254) (type default) (color 0 0 0 0)) 146 | (fill (type background)) 147 | ) 148 | (pin output inverted (at 7.62 0 180) (length 3.81) 149 | (name "~" (effects (font (size 1.27 1.27)))) 150 | (number "12" (effects (font (size 1.27 1.27)))) 151 | ) 152 | (pin input line (at -7.62 0 0) (length 3.81) 153 | (name "~" (effects (font (size 1.27 1.27)))) 154 | (number "13" (effects (font (size 1.27 1.27)))) 155 | ) 156 | ) 157 | (symbol "74LS04_7_0" 158 | (pin power_in line (at 0 12.7 270) (length 5.08) 159 | (name "VCC" (effects (font (size 1.27 1.27)))) 160 | (number "14" (effects (font (size 1.27 1.27)))) 161 | ) 162 | (pin power_in line (at 0 -12.7 90) (length 5.08) 163 | (name "GND" (effects (font (size 1.27 1.27)))) 164 | (number "7" (effects (font (size 1.27 1.27)))) 165 | ) 166 | ) 167 | (symbol "74LS04_7_1" 168 | (rectangle (start -5.08 7.62) (end 5.08 -7.62) 169 | (stroke (width 0.254) (type default) (color 0 0 0 0)) 170 | (fill (type background)) 171 | ) 172 | ) 173 | ) 174 | (symbol "power:GND" (power) (pin_names (offset 0)) (in_bom yes) (on_board yes) 175 | (property "Reference" "#PWR" (id 0) (at 0 -6.35 0) 176 | (effects (font (size 1.27 1.27)) hide) 177 | ) 178 | (property "Value" "GND" (id 1) (at 0 -3.81 0) 179 | (effects (font (size 1.27 1.27))) 180 | ) 181 | (property "Footprint" "" (id 2) (at 0 0 0) 182 | (effects (font (size 1.27 1.27)) hide) 183 | ) 184 | (property "Datasheet" "" (id 3) (at 0 0 0) 185 | (effects (font (size 1.27 1.27)) hide) 186 | ) 187 | (property "ki_keywords" "power-flag" (id 4) (at 0 0 0) 188 | (effects (font (size 1.27 1.27)) hide) 189 | ) 190 | (property "ki_description" "Power symbol creates a global label with name \"GND\" , ground" (id 5) (at 0 0 0) 191 | (effects (font (size 1.27 1.27)) hide) 192 | ) 193 | (symbol "GND_0_1" 194 | (polyline 195 | (pts 196 | (xy 0 0) 197 | (xy 0 -1.27) 198 | (xy 1.27 -1.27) 199 | (xy 0 -2.54) 200 | (xy -1.27 -1.27) 201 | (xy 0 -1.27) 202 | ) 203 | (stroke (width 0) (type default) (color 0 0 0 0)) 204 | (fill (type none)) 205 | ) 206 | ) 207 | (symbol "GND_1_1" 208 | (pin power_in line (at 0 0 270) (length 0) hide 209 | (name "GND" (effects (font (size 1.27 1.27)))) 210 | (number "1" (effects (font (size 1.27 1.27)))) 211 | ) 212 | ) 213 | ) 214 | (symbol "power:VCC" (power) (pin_names (offset 0)) (in_bom yes) (on_board yes) 215 | (property "Reference" "#PWR" (id 0) (at 0 -3.81 0) 216 | (effects (font (size 1.27 1.27)) hide) 217 | ) 218 | (property "Value" "VCC" (id 1) (at 0 3.81 0) 219 | (effects (font (size 1.27 1.27))) 220 | ) 221 | (property "Footprint" "" (id 2) (at 0 0 0) 222 | (effects (font (size 1.27 1.27)) hide) 223 | ) 224 | (property "Datasheet" "" (id 3) (at 0 0 0) 225 | (effects (font (size 1.27 1.27)) hide) 226 | ) 227 | (property "ki_keywords" "power-flag" (id 4) (at 0 0 0) 228 | (effects (font (size 1.27 1.27)) hide) 229 | ) 230 | (property "ki_description" "Power symbol creates a global label with name \"VCC\"" (id 5) (at 0 0 0) 231 | (effects (font (size 1.27 1.27)) hide) 232 | ) 233 | (symbol "VCC_0_1" 234 | (polyline 235 | (pts 236 | (xy -0.762 1.27) 237 | (xy 0 2.54) 238 | ) 239 | (stroke (width 0) (type default) (color 0 0 0 0)) 240 | (fill (type none)) 241 | ) 242 | (polyline 243 | (pts 244 | (xy 0 0) 245 | (xy 0 2.54) 246 | ) 247 | (stroke (width 0) (type default) (color 0 0 0 0)) 248 | (fill (type none)) 249 | ) 250 | (polyline 251 | (pts 252 | (xy 0 2.54) 253 | (xy 0.762 1.27) 254 | ) 255 | (stroke (width 0) (type default) (color 0 0 0 0)) 256 | (fill (type none)) 257 | ) 258 | ) 259 | (symbol "VCC_1_1" 260 | (pin power_in line (at 0 0 90) (length 0) hide 261 | (name "VCC" (effects (font (size 1.27 1.27)))) 262 | (number "1" (effects (font (size 1.27 1.27)))) 263 | ) 264 | ) 265 | ) 266 | ) 267 | 268 | 269 | (wire (pts (xy 104.14 63.5) (xy 106.68 63.5)) 270 | (stroke (width 0) (type default) (color 0 0 0 0)) 271 | (uuid 10109f84-4940-47f8-8640-91f185ac9bc1) 272 | ) 273 | (wire (pts (xy 175.26 63.5) (xy 177.8 63.5)) 274 | (stroke (width 0) (type default) (color 0 0 0 0)) 275 | (uuid 47baf4b1-0938-497d-88f9-671136aa8be7) 276 | ) 277 | (wire (pts (xy 121.92 63.5) (xy 124.46 63.5)) 278 | (stroke (width 0) (type default) (color 0 0 0 0)) 279 | (uuid 55e740a3-0735-4744-896e-2bf5437093b9) 280 | ) 281 | (wire (pts (xy 157.48 63.5) (xy 160.02 63.5)) 282 | (stroke (width 0) (type default) (color 0 0 0 0)) 283 | (uuid c022004a-c968-410e-b59e-fbab0e561e9d) 284 | ) 285 | (wire (pts (xy 193.04 63.5) (xy 195.58 63.5)) 286 | (stroke (width 0) (type default) (color 0 0 0 0)) 287 | (uuid e615f7aa-337e-474d-9615-2ad82b1c44ca) 288 | ) 289 | (wire (pts (xy 85.09 63.5) (xy 88.9 63.5)) 290 | (stroke (width 0) (type default) (color 0 0 0 0)) 291 | (uuid ef8fe2ac-6a7f-4682-9418-b801a1b10a3b) 292 | ) 293 | (wire (pts (xy 139.7 63.5) (xy 142.24 63.5)) 294 | (stroke (width 0) (type default) (color 0 0 0 0)) 295 | (uuid f4f99e3d-7269-4f6a-a759-16ad2a258779) 296 | ) 297 | 298 | (hierarchical_label "IN" (shape input) (at 85.09 63.5 180) 299 | (effects (font (size 1.27 1.27)) (justify right)) 300 | (uuid 4fb02e58-160a-4a39-9f22-d0c75e82ee72) 301 | ) 302 | (hierarchical_label "OUT" (shape output) (at 195.58 63.5 0) 303 | (effects (font (size 1.27 1.27)) (justify left)) 304 | (uuid 77ed3941-d133-4aef-a9af-5a39322d14eb) 305 | ) 306 | 307 | (symbol (lib_id "74xx:74LS04") (at 96.52 63.5 0) (unit 1) 308 | (in_bom yes) (on_board yes) 309 | (uuid 00000000-0000-0000-0000-00005f34307a) 310 | (property "Reference" "U1" (id 0) (at 96.52 55.4482 0)) 311 | (property "Value" "74LS04" (id 1) (at 96.52 57.7596 0)) 312 | (property "Footprint" "" (id 2) (at 96.52 63.5 0) 313 | (effects (font (size 1.27 1.27)) hide) 314 | ) 315 | (property "Datasheet" "http://www.ti.com/lit/gpn/sn74LS04" (id 3) (at 96.52 63.5 0) 316 | (effects (font (size 1.27 1.27)) hide) 317 | ) 318 | (pin "1" (uuid f1e9ac99-f8d4-4d05-beb3-290dccb8d277)) 319 | (pin "2" (uuid 014c98b2-ac35-4831-825d-74c6fa5ddc67)) 320 | (pin "3" (uuid 2282aa17-25e5-4b18-b436-0d5d8b80c271)) 321 | (pin "4" (uuid 779d408a-048e-4117-b37b-52b0f8855fe0)) 322 | (pin "5" (uuid 0209c2aa-e6bb-4fb6-ad76-6e4b9b1b16f7)) 323 | (pin "6" (uuid 867bfffe-ae91-4651-94e5-196c3c600344)) 324 | (pin "8" (uuid 46124aee-812d-4b49-8b07-54040860b90b)) 325 | (pin "9" (uuid bfb61142-9bc3-4e95-89e6-4ced14b533f1)) 326 | (pin "10" (uuid c37c1333-9b31-4605-8469-7903812fede5)) 327 | (pin "11" (uuid f638fbb1-32ba-4d92-b985-ace474e20207)) 328 | (pin "12" (uuid 0105483f-ed6d-4621-a345-4f843c0072bf)) 329 | (pin "13" (uuid 052e0faa-99fa-4dba-8771-a6a810a9615f)) 330 | (pin "14" (uuid cad197d5-2063-47e9-a3da-c0da8b26e6ec)) 331 | (pin "7" (uuid a5eca4b9-9690-4686-9caf-8a9265e35104)) 332 | ) 333 | 334 | (symbol (lib_id "74xx:74LS04") (at 114.3 63.5 0) (unit 2) 335 | (in_bom yes) (on_board yes) 336 | (uuid 00000000-0000-0000-0000-00005f344a0e) 337 | (property "Reference" "U1" (id 0) (at 114.3 55.4482 0)) 338 | (property "Value" "74LS04" (id 1) (at 114.3 57.7596 0)) 339 | (property "Footprint" "" (id 2) (at 114.3 63.5 0) 340 | (effects (font (size 1.27 1.27)) hide) 341 | ) 342 | (property "Datasheet" "http://www.ti.com/lit/gpn/sn74LS04" (id 3) (at 114.3 63.5 0) 343 | (effects (font (size 1.27 1.27)) hide) 344 | ) 345 | (pin "1" (uuid 7eb5089c-0f47-4b87-beab-8ff80b20792f)) 346 | (pin "2" (uuid 60102715-3c93-4a15-8674-20c8b7c57be4)) 347 | (pin "3" (uuid c1630947-26ec-4031-96dd-f1cdf939d3a7)) 348 | (pin "4" (uuid e2858b6c-d9d5-4bfd-a26d-d7110c9ff81c)) 349 | (pin "5" (uuid 8e8bdbab-9511-4475-9eff-1510f6b5ad23)) 350 | (pin "6" (uuid 6ae8ec3f-43b9-4112-bfd5-128204c7759e)) 351 | (pin "8" (uuid cf6c2bfe-fd20-4797-a562-aefcd563a7af)) 352 | (pin "9" (uuid 99a6c8b8-68c2-479d-ad69-5f387013f1da)) 353 | (pin "10" (uuid 349db570-c21f-46f9-80d4-b68f3473ae15)) 354 | (pin "11" (uuid 76d634a3-0256-4a17-8718-470b7e39e4ac)) 355 | (pin "12" (uuid 72b533b9-dc1e-46c5-a29d-c8b49a52ce5d)) 356 | (pin "13" (uuid 132268c6-c19f-48f6-8da5-42a0ba313cd0)) 357 | (pin "14" (uuid 5cd18a01-4203-4bf0-961c-09af1ded6848)) 358 | (pin "7" (uuid 612772b8-33ef-4e45-abaa-4fba74ec3e0b)) 359 | ) 360 | 361 | (symbol (lib_id "74xx:74LS04") (at 132.08 63.5 0) (unit 3) 362 | (in_bom yes) (on_board yes) 363 | (uuid 00000000-0000-0000-0000-00005f345e39) 364 | (property "Reference" "U1" (id 0) (at 132.08 55.4482 0)) 365 | (property "Value" "74LS04" (id 1) (at 132.08 57.7596 0)) 366 | (property "Footprint" "" (id 2) (at 132.08 63.5 0) 367 | (effects (font (size 1.27 1.27)) hide) 368 | ) 369 | (property "Datasheet" "http://www.ti.com/lit/gpn/sn74LS04" (id 3) (at 132.08 63.5 0) 370 | (effects (font (size 1.27 1.27)) hide) 371 | ) 372 | (pin "1" (uuid bbac1062-acc9-427d-9486-84613968193d)) 373 | (pin "2" (uuid aa6fbc5d-962a-421b-b0f8-5cc0634fa940)) 374 | (pin "3" (uuid ee86faf1-b781-415c-a773-7ffd42b4ccac)) 375 | (pin "4" (uuid f60b38a1-6bcf-4425-85f9-40dd840f097e)) 376 | (pin "5" (uuid 9a609661-5895-4db4-a600-14bffdacc16e)) 377 | (pin "6" (uuid f6c777f1-b4a6-4529-9b29-67dfedcb1744)) 378 | (pin "8" (uuid d6ee852a-2125-4cbb-b4c0-2c1f7b3da0fd)) 379 | (pin "9" (uuid 66b18884-be07-4547-81e4-598e8796b478)) 380 | (pin "10" (uuid 23b24c2d-06bc-4f64-bbba-4a1836993bd3)) 381 | (pin "11" (uuid ab204973-c25f-4438-a480-b6256c3846e5)) 382 | (pin "12" (uuid b7d46009-86c1-4915-8882-efed73321a1b)) 383 | (pin "13" (uuid ae6c4a1d-7821-456a-9178-e99999733573)) 384 | (pin "14" (uuid bb4b6efa-8944-40e5-8681-42c8086dfb79)) 385 | (pin "7" (uuid 7b67e898-9526-4fba-8f77-59962db970c7)) 386 | ) 387 | 388 | (symbol (lib_id "74xx:74LS04") (at 149.86 63.5 0) (unit 4) 389 | (in_bom yes) (on_board yes) 390 | (uuid 00000000-0000-0000-0000-00005f346e8b) 391 | (property "Reference" "U1" (id 0) (at 149.86 55.4482 0)) 392 | (property "Value" "74LS04" (id 1) (at 149.86 57.7596 0)) 393 | (property "Footprint" "" (id 2) (at 149.86 63.5 0) 394 | (effects (font (size 1.27 1.27)) hide) 395 | ) 396 | (property "Datasheet" "http://www.ti.com/lit/gpn/sn74LS04" (id 3) (at 149.86 63.5 0) 397 | (effects (font (size 1.27 1.27)) hide) 398 | ) 399 | (pin "1" (uuid 2905dd4f-e9df-4c8d-9d14-37e5474058d3)) 400 | (pin "2" (uuid 909aa50b-4012-4e97-bcca-8dbd688e9e99)) 401 | (pin "3" (uuid 8520760e-f968-483e-8989-07d96ff7c0bd)) 402 | (pin "4" (uuid 472927d7-713d-4434-aae2-cab12a79506c)) 403 | (pin "5" (uuid 5c2e0337-2447-4117-8a69-8ab7a6736b16)) 404 | (pin "6" (uuid 44dcea25-62da-41dc-8e4e-72c45198747e)) 405 | (pin "8" (uuid 47d49bdd-bacb-468f-bcfb-647fd9ccff38)) 406 | (pin "9" (uuid 71c84a4a-5d81-46df-a82f-84ff433708b7)) 407 | (pin "10" (uuid ccbabbc8-3805-4494-9f79-0e7c58491280)) 408 | (pin "11" (uuid 5f478e4e-1c37-417f-802a-dcd64da6ec69)) 409 | (pin "12" (uuid f4169419-5cd3-4db1-860a-b37e557bc728)) 410 | (pin "13" (uuid 0eb475d7-7c32-41bc-a947-f6e04adbfa7d)) 411 | (pin "14" (uuid c3d6c577-12ce-4f2f-84bd-dda13a8fe40f)) 412 | (pin "7" (uuid 2843c226-58ef-4dc8-ad8f-3fcb74143c53)) 413 | ) 414 | 415 | (symbol (lib_id "74xx:74LS04") (at 167.64 63.5 0) (unit 5) 416 | (in_bom yes) (on_board yes) 417 | (uuid 00000000-0000-0000-0000-00005f348270) 418 | (property "Reference" "U1" (id 0) (at 167.64 55.4482 0)) 419 | (property "Value" "74LS04" (id 1) (at 167.64 57.7596 0)) 420 | (property "Footprint" "" (id 2) (at 167.64 63.5 0) 421 | (effects (font (size 1.27 1.27)) hide) 422 | ) 423 | (property "Datasheet" "http://www.ti.com/lit/gpn/sn74LS04" (id 3) (at 167.64 63.5 0) 424 | (effects (font (size 1.27 1.27)) hide) 425 | ) 426 | (pin "1" (uuid 50c06431-98b4-4782-9bc2-1f48dd66012e)) 427 | (pin "2" (uuid 237913c0-554e-4f92-9454-74fca47a4ff0)) 428 | (pin "3" (uuid 22591fce-1b7c-432b-9f65-ca786ad38164)) 429 | (pin "4" (uuid cf7394f4-fb02-417b-8fcd-f7a746f2f953)) 430 | (pin "5" (uuid 1c6ed6bd-b27d-4107-a50a-c5fa1ca815a8)) 431 | (pin "6" (uuid c9913a2d-9fdc-477b-99fb-40ae623b387f)) 432 | (pin "8" (uuid 1dd5c978-2ace-45f7-9c90-88b9fc1a4ffc)) 433 | (pin "9" (uuid 9a40e5da-1346-4d0c-af28-5a2601f44304)) 434 | (pin "10" (uuid 01fec0dc-78cf-4a7d-851f-e24a9edd4a86)) 435 | (pin "11" (uuid 92e30d01-6115-49a3-bead-4b8b74d533c9)) 436 | (pin "12" (uuid 7092ba6d-f06a-417d-9814-317dd365bfc4)) 437 | (pin "13" (uuid bce2b649-7e95-435b-ad4a-9467fe595946)) 438 | (pin "14" (uuid cee262bb-8369-447b-8bc6-8fc7e99304ba)) 439 | (pin "7" (uuid d69f10fe-7d7e-437f-89c6-5d4591a60c24)) 440 | ) 441 | 442 | (symbol (lib_id "74xx:74LS04") (at 185.42 63.5 0) (unit 6) 443 | (in_bom yes) (on_board yes) 444 | (uuid 00000000-0000-0000-0000-00005f348d95) 445 | (property "Reference" "U1" (id 0) (at 185.42 55.4482 0)) 446 | (property "Value" "74LS04" (id 1) (at 185.42 57.7596 0)) 447 | (property "Footprint" "" (id 2) (at 185.42 63.5 0) 448 | (effects (font (size 1.27 1.27)) hide) 449 | ) 450 | (property "Datasheet" "http://www.ti.com/lit/gpn/sn74LS04" (id 3) (at 185.42 63.5 0) 451 | (effects (font (size 1.27 1.27)) hide) 452 | ) 453 | (pin "1" (uuid c1de8072-85af-41fb-bbae-891234fe6286)) 454 | (pin "2" (uuid 92eba779-7a6c-4f2f-bf8a-9d01b4559540)) 455 | (pin "3" (uuid 87aa64d0-4063-42a0-8b26-e6afc8ae74de)) 456 | (pin "4" (uuid 57d78745-19ee-4265-be9f-6570894e3e61)) 457 | (pin "5" (uuid 924bfea8-9b40-4840-882f-6d467382007a)) 458 | (pin "6" (uuid 6353b9ee-662c-49d5-8069-f7d044044c97)) 459 | (pin "8" (uuid 9711c471-9fef-4c47-a8bb-dbb7861fc7f5)) 460 | (pin "9" (uuid 4875c4e7-8ec2-47f1-a20e-42413b9c1e4a)) 461 | (pin "10" (uuid 0d8c1296-05ab-46c9-b16d-99728b6eacd6)) 462 | (pin "11" (uuid 41727653-a496-48b3-ac11-0e25bdbe5198)) 463 | (pin "12" (uuid b9a8ff4c-342a-4f44-bfb4-1b1895e9679b)) 464 | (pin "13" (uuid 29941c34-ddd5-4a9c-bdef-5bf81398f604)) 465 | (pin "14" (uuid c589a3b6-28de-47c0-87df-1950ce799f45)) 466 | (pin "7" (uuid f0358a25-a450-4144-94fb-900372ab12a7)) 467 | ) 468 | 469 | (symbol (lib_id "74xx:74LS04") (at 139.7 88.9 0) (unit 7) 470 | (in_bom yes) (on_board yes) 471 | (uuid 00000000-0000-0000-0000-00005f34a12f) 472 | (property "Reference" "U1" (id 0) (at 145.542 87.7316 0) 473 | (effects (font (size 1.27 1.27)) (justify left)) 474 | ) 475 | (property "Value" "74LS04" (id 1) (at 145.542 90.043 0) 476 | (effects (font (size 1.27 1.27)) (justify left)) 477 | ) 478 | (property "Footprint" "" (id 2) (at 139.7 88.9 0) 479 | (effects (font (size 1.27 1.27)) hide) 480 | ) 481 | (property "Datasheet" "http://www.ti.com/lit/gpn/sn74LS04" (id 3) (at 139.7 88.9 0) 482 | (effects (font (size 1.27 1.27)) hide) 483 | ) 484 | (pin "1" (uuid 6202c74f-4d05-4660-b905-0e5ca6889128)) 485 | (pin "2" (uuid 011a7ac5-7a54-4c9a-97b1-3c71ef0bed96)) 486 | (pin "3" (uuid 98b08e85-9973-4c19-9269-2dcea15b1681)) 487 | (pin "4" (uuid f023d9a1-53db-47ae-bc98-ce0c0852432d)) 488 | (pin "5" (uuid a1064e68-0e5f-40e9-9c19-682469444ee6)) 489 | (pin "6" (uuid 819e8bc3-d0a5-4ab9-ad3b-83fcfff57d5f)) 490 | (pin "8" (uuid ff2b7b52-4097-4d64-8511-d7ac105c5a57)) 491 | (pin "9" (uuid f85ce822-c4cd-4bc6-8c31-cfad683d10ae)) 492 | (pin "10" (uuid a4bb7bae-f982-4b25-a9eb-bc556b618638)) 493 | (pin "11" (uuid 84529ab6-9a65-4c87-b095-7a53fb755e2c)) 494 | (pin "12" (uuid 3d1ca20e-431d-45c0-9443-c5f597b34af3)) 495 | (pin "13" (uuid 2bd0a9eb-83dd-424f-92d0-bd4297676cbf)) 496 | (pin "14" (uuid bf0862f0-7b4d-4da4-a5e8-91a9bdbee56f)) 497 | (pin "7" (uuid 0639e567-d45a-4c84-98fa-a167b211ae20)) 498 | ) 499 | 500 | (symbol (lib_id "power:VCC") (at 139.7 76.2 0) (unit 1) 501 | (in_bom yes) (on_board yes) 502 | (uuid 00000000-0000-0000-0000-00005f34bc05) 503 | (property "Reference" "#PWR01" (id 0) (at 139.7 80.01 0) 504 | (effects (font (size 1.27 1.27)) hide) 505 | ) 506 | (property "Value" "VCC" (id 1) (at 140.081 71.8058 0)) 507 | (property "Footprint" "" (id 2) (at 139.7 76.2 0) 508 | (effects (font (size 1.27 1.27)) hide) 509 | ) 510 | (property "Datasheet" "" (id 3) (at 139.7 76.2 0) 511 | (effects (font (size 1.27 1.27)) hide) 512 | ) 513 | (pin "1" (uuid 0030af45-9cbd-4108-9b0f-2e3885996ada)) 514 | ) 515 | 516 | (symbol (lib_id "power:GND") (at 139.7 101.6 0) (unit 1) 517 | (in_bom yes) (on_board yes) 518 | (uuid 00000000-0000-0000-0000-00005f34c535) 519 | (property "Reference" "#PWR02" (id 0) (at 139.7 107.95 0) 520 | (effects (font (size 1.27 1.27)) hide) 521 | ) 522 | (property "Value" "GND" (id 1) (at 139.827 105.9942 0)) 523 | (property "Footprint" "" (id 2) (at 139.7 101.6 0) 524 | (effects (font (size 1.27 1.27)) hide) 525 | ) 526 | (property "Datasheet" "" (id 3) (at 139.7 101.6 0) 527 | (effects (font (size 1.27 1.27)) hide) 528 | ) 529 | (pin "1" (uuid 03d3f3dc-57cc-4a5d-b588-5d7b6cd2d907)) 530 | ) 531 | 532 | (sheet (at 114.3 127) (size 25.4 12.7) (fields_autoplaced) 533 | (stroke (width 0) (type solid) (color 0 0 0 0)) 534 | (fill (color 0 0 0 0.0000)) 535 | (uuid 00000000-0000-0000-0000-00005f3bb8bb) 536 | (property "Sheet name" "Deeper test" (id 0) (at 114.3 126.2884 0) 537 | (effects (font (size 1.27 1.27)) (justify left bottom)) 538 | ) 539 | (property "Sheet file" "deeper.kicad_sch" (id 1) (at 114.3 140.2846 0) 540 | (effects (font (size 1.27 1.27)) (justify left top)) 541 | ) 542 | ) 543 | ) 544 | -------------------------------------------------------------------------------- /tests/cases/5/b/deeper.kicad_sch: -------------------------------------------------------------------------------- 1 | (kicad_sch (version 20211123) (generator eeschema) 2 | 3 | (uuid 3b838d52-596d-4e4d-a6ac-e4c8e7621137) 4 | 5 | (paper "A4" portrait) 6 | 7 | (title_block 8 | (date " ") 9 | ) 10 | 11 | (lib_symbols 12 | (symbol "Device:R" (pin_numbers hide) (pin_names (offset 0)) (in_bom yes) (on_board yes) 13 | (property "Reference" "R" (id 0) (at 2.032 0 90) 14 | (effects (font (size 1.27 1.27))) 15 | ) 16 | (property "Value" "R" (id 1) (at 0 0 90) 17 | (effects (font (size 1.27 1.27))) 18 | ) 19 | (property "Footprint" "" (id 2) (at -1.778 0 90) 20 | (effects (font (size 1.27 1.27)) hide) 21 | ) 22 | (property "Datasheet" "~" (id 3) (at 0 0 0) 23 | (effects (font (size 1.27 1.27)) hide) 24 | ) 25 | (property "ki_keywords" "R res resistor" (id 4) (at 0 0 0) 26 | (effects (font (size 1.27 1.27)) hide) 27 | ) 28 | (property "ki_description" "Resistor" (id 5) (at 0 0 0) 29 | (effects (font (size 1.27 1.27)) hide) 30 | ) 31 | (property "ki_fp_filters" "R_*" (id 6) (at 0 0 0) 32 | (effects (font (size 1.27 1.27)) hide) 33 | ) 34 | (symbol "R_0_1" 35 | (rectangle (start -1.016 -2.54) (end 1.016 2.54) 36 | (stroke (width 0.254) (type default) (color 0 0 0 0)) 37 | (fill (type none)) 38 | ) 39 | ) 40 | (symbol "R_1_1" 41 | (pin passive line (at 0 3.81 270) (length 1.27) 42 | (name "~" (effects (font (size 1.27 1.27)))) 43 | (number "1" (effects (font (size 1.27 1.27)))) 44 | ) 45 | (pin passive line (at 0 -3.81 90) (length 1.27) 46 | (name "~" (effects (font (size 1.27 1.27)))) 47 | (number "2" (effects (font (size 1.27 1.27)))) 48 | ) 49 | ) 50 | ) 51 | ) 52 | 53 | 54 | (symbol (lib_id "Device:R") (at 139.7 80.01 0) (unit 1) 55 | (in_bom yes) (on_board yes) 56 | (uuid 00000000-0000-0000-0000-00005f3bbcff) 57 | (property "Reference" "R3" (id 0) (at 141.478 78.8416 0) 58 | (effects (font (size 1.27 1.27)) (justify left)) 59 | ) 60 | (property "Value" "1m" (id 1) (at 141.478 81.153 0) 61 | (effects (font (size 1.27 1.27)) (justify left)) 62 | ) 63 | (property "Footprint" "" (id 2) (at 137.922 80.01 90) 64 | (effects (font (size 1.27 1.27)) hide) 65 | ) 66 | (property "Datasheet" "~" (id 3) (at 139.7 80.01 0) 67 | (effects (font (size 1.27 1.27)) hide) 68 | ) 69 | (pin "1" (uuid c8dca1a6-f09a-4305-804d-2295b5448f55)) 70 | (pin "2" (uuid 4dec140a-3f2f-4b17-b6f0-89a0fc1cf30b)) 71 | ) 72 | ) 73 | -------------------------------------------------------------------------------- /tests/cases/5/ref_sch/diff-Schematic_root-Sub Sheet 2-Deeper test0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/5/ref_sch/diff-Schematic_root-Sub Sheet 2-Deeper test0.png -------------------------------------------------------------------------------- /tests/cases/5/ref_sch/diff-Schematic_root-Sub Sheet 20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/5/ref_sch/diff-Schematic_root-Sub Sheet 20.png -------------------------------------------------------------------------------- /tests/cases/5/ref_sch/diff-Schematic_root-Sub Sheet-Deeper test0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/5/ref_sch/diff-Schematic_root-Sub Sheet-Deeper test0.png -------------------------------------------------------------------------------- /tests/cases/5/ref_sch/diff-Schematic_root-Sub Sheet0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/5/ref_sch/diff-Schematic_root-Sub Sheet0.png -------------------------------------------------------------------------------- /tests/cases/5/ref_sch/diff-Schematic_root0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/5/ref_sch/diff-Schematic_root0.png -------------------------------------------------------------------------------- /tests/cases/5/ref_sch2/diff-Schematic_root-Sub Sheet 2-Deeper test0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/5/ref_sch2/diff-Schematic_root-Sub Sheet 2-Deeper test0.png -------------------------------------------------------------------------------- /tests/cases/5/ref_sch2/diff-Schematic_root-Sub Sheet 20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/5/ref_sch2/diff-Schematic_root-Sub Sheet 20.png -------------------------------------------------------------------------------- /tests/cases/5/ref_sch2/diff-Schematic_root-Sub Sheet-Deeper test0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/5/ref_sch2/diff-Schematic_root-Sub Sheet-Deeper test0.png -------------------------------------------------------------------------------- /tests/cases/5/ref_sch2/diff-Schematic_root-Sub Sheet0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/5/ref_sch2/diff-Schematic_root-Sub Sheet0.png -------------------------------------------------------------------------------- /tests/cases/5/ref_sch2/diff-Schematic_root0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/5/ref_sch2/diff-Schematic_root0.png -------------------------------------------------------------------------------- /tests/cases/6/a/6.kicad_sch: -------------------------------------------------------------------------------- 1 | (kicad_sch (version 20211123) (generator eeschema) 2 | 3 | (uuid 44ba6fc2-2aff-4fba-b7f6-346e269eb0f1) 4 | 5 | (paper "A4") 6 | 7 | (lib_symbols 8 | ) 9 | 10 | 11 | (text "Different page size" (at 87.63 82.55 0) 12 | (effects (font (size 5 5)) (justify left bottom)) 13 | (uuid dd26a243-2105-4229-b371-2bc5006e18ac) 14 | ) 15 | 16 | (sheet_instances 17 | (path "/" (page "1")) 18 | ) 19 | ) 20 | -------------------------------------------------------------------------------- /tests/cases/6/b/6.kicad_sch: -------------------------------------------------------------------------------- 1 | (kicad_sch (version 20211123) (generator eeschema) 2 | 3 | (uuid 44ba6fc2-2aff-4fba-b7f6-346e269eb0f1) 4 | 5 | (paper "A3") 6 | 7 | (lib_symbols 8 | ) 9 | 10 | 11 | (text "Different page size" (at 87.63 82.55 0) 12 | (effects (font (size 5 5)) (justify left bottom)) 13 | (uuid dd26a243-2105-4229-b371-2bc5006e18ac) 14 | ) 15 | 16 | (sheet_instances 17 | (path "/" (page "1")) 18 | ) 19 | ) 20 | -------------------------------------------------------------------------------- /tests/cases/6/ref_sch/diff-Schematic0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/INTI-CMNB/KiDiff/ab30e3b566557d8b0a9924616dcc6902bc2574bf/tests/cases/6/ref_sch/diff-Schematic0.png -------------------------------------------------------------------------------- /tests/cases/exclude.lst: -------------------------------------------------------------------------------- 1 | 32 2 | 33 3 | 34 4 | 35 5 | 36 6 | 37 7 | 38 8 | 39 9 | 40 10 | 41 11 | 42 12 | 43 13 | 44 14 | 45 15 | 46 16 | 47 17 | 48 18 | 49 19 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | """ 2 | Test configuration 3 | """ 4 | import pytest 5 | 6 | 7 | def pytest_addoption(parser): 8 | parser.addoption("--test_dir", action="store", default=None, 9 | help="the test output dir to use (omit to use a temp dir). " 10 | "If given, outputs will _not_ be cleared after testing.") 11 | 12 | 13 | @pytest.fixture 14 | def test_dir(request): 15 | return request.config.getoption("--test_dir") 16 | -------------------------------------------------------------------------------- /tests/test_pcb_diff.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022 Salvador E. Tropea 2 | # Copyright (c) 2022 Instituto Nacional de Tecnologïa Industrial 3 | # License: GPL-2.0 4 | # Project: KiCad Diff (KiDiff) 5 | """ 6 | Tests for PCB diffs 7 | 8 | For debug information use: 9 | pytest-3 --log-cli-level debug 10 | 11 | """ 12 | 13 | import os 14 | import sys 15 | # Look for the 'utils' module from where the script is running 16 | script_dir = os.path.dirname(os.path.abspath(__file__)) 17 | sys.path.insert(0, os.path.dirname(script_dir)) 18 | # Utils import 19 | from utils import context 20 | 21 | 22 | def test_pcb_simple_1(test_dir): 23 | ctx = context.TestContext(test_dir, 1) 24 | ctx.run(ops=['--only_different']) 25 | ctx.compare_out_pngs() 26 | ctx.clean_up() 27 | 28 | 29 | def test_pcb_diff_layers_1(test_dir): 30 | ctx = context.TestContext(test_dir, 4) 31 | ctx.run(layers=True) 32 | ctx.compare_out_pngs() 33 | ctx.invert() 34 | ctx.set_ref_dir(extra='2') 35 | ctx.run(layers=True) 36 | ctx.compare_out_pngs() 37 | ctx.clean_up() 38 | 39 | 40 | def test_pcb_diff_layers_2(test_dir): 41 | """ Using names instead of numbers in the include.lst """ 42 | ctx = context.TestContext(test_dir, 4) 43 | ctx.include_lst = 'include_names.lst' 44 | ctx.run(layers=True) 45 | ctx.compare_out_pngs() 46 | ctx.clean_up() 47 | -------------------------------------------------------------------------------- /tests/test_sch_diff.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022 Salvador E. Tropea 2 | # Copyright (c) 2022 Instituto Nacional de Tecnologïa Industrial 3 | # License: GPL-2.0 4 | # Project: KiCad Diff (KiDiff) 5 | """ 6 | Tests for PCB diffs 7 | 8 | For debug information use: 9 | pytest-3 --log-cli-level debug --test_dir=pp -k TEST 10 | 11 | """ 12 | 13 | import os 14 | import sys 15 | # Look for the 'utils' module from where the script is running 16 | script_dir = os.path.dirname(os.path.abspath(__file__)) 17 | sys.path.insert(0, os.path.dirname(script_dir)) 18 | # Utils import 19 | from utils import context 20 | 21 | 22 | def test_sch_simple_1(test_dir): 23 | ctx = context.TestContextSCH(test_dir, 1) 24 | ctx.run() 25 | ctx.compare_out_pngs() 26 | ctx.clean_up() 27 | 28 | 29 | def test_sch_multi_1(test_dir): 30 | ctx = context.TestContextSCH(test_dir, 2) 31 | ctx.run(['--all_pages']) 32 | ctx.compare_out_file() 33 | ctx.clean_up() 34 | 35 | 36 | def test_sch_diff_sheets_1(test_dir): 37 | ctx = context.TestContextSCH(test_dir, 5) 38 | ctx.run(['--all_pages']) 39 | ctx.compare_out_pngs() 40 | ctx.invert() 41 | ctx.set_ref_dir(extra='2') 42 | ctx.run(['--all_pages']) 43 | ctx.compare_out_pngs() 44 | ctx.clean_up() 45 | 46 | 47 | def test_sch_diff_size_1(test_dir): 48 | ctx = context.TestContextSCH(test_dir, 6) 49 | ctx.run() 50 | ctx.compare_out_pngs() 51 | ctx.clean_up() 52 | -------------------------------------------------------------------------------- /tests/utils/context.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | import subprocess 4 | import re 5 | import configparser 6 | from glob import glob 7 | from pty import spawn 8 | from contextlib import contextmanager 9 | from psutil import pid_exists 10 | import sys 11 | # Look for the 'kiauto' module from where the script is running 12 | script_dir = os.path.dirname(os.path.abspath(__file__)) 13 | sys.path.insert(0, os.path.dirname(os.path.dirname(script_dir))) 14 | from kiauto.ui_automation import recorded_xvfb, PopenContext 15 | 16 | COVERAGE_SCRIPT = 'python3-coverage' 17 | KICAD_PCB_EXT = '.kicad_pcb' 18 | MODE_SCH = 1 19 | MODE_PCB = 0 20 | 21 | ng_ver = os.environ.get('KIAUS_USE_NIGHTLY') 22 | if ng_ver: 23 | # Path to the Python module 24 | sys.path.insert(0, '/usr/lib/kicad-nightly/lib/python3/dist-packages') 25 | import pcbnew 26 | # Detect version 27 | m = re.search(r'(\d+)\.(\d+)\.(\d+)', pcbnew.GetBuildVersion()) 28 | major = int(m.group(1)) 29 | minor = int(m.group(2)) 30 | patch = int(m.group(3)) 31 | kicad_version = major*1000000+minor*1000+patch 32 | logging.debug('Detected KiCad v{}.{}.{} ({})'.format(major, minor, patch, kicad_version)) 33 | ki5 = kicad_version < 5099000 34 | ki6 = not ki5 35 | 36 | 37 | def usable_cmd(cmd): 38 | return ' '.join(cmd) 39 | 40 | 41 | def get_config_vars_ini(file): 42 | if not os.path.isfile(file): 43 | return None 44 | config = configparser.ConfigParser() 45 | with open(file, "rt") as f: 46 | data = f.read() 47 | config.read_string('[Various]\n'+data) 48 | if 'EnvironmentVariables' in config: 49 | return config['EnvironmentVariables'] 50 | return None 51 | 52 | 53 | class TestContext(object): 54 | pty_data = None 55 | 56 | def __init__(self, test_dir, case_num, test_name=None, mode=MODE_PCB): 57 | if test_name is None: 58 | test_name = sys._getframe(1).f_code.co_name 59 | if test_name.startswith('test_'): 60 | test_name = test_name[5:] 61 | self.board_dir = os.path.join('..', 'cases') 62 | if ki5: 63 | self.kicad_cfg_dir = pcbnew.GetKicadConfigPath() 64 | self.kicad_conf = os.path.join(self.kicad_cfg_dir, 'kicad_common') 65 | env = get_config_vars_ini(self.kicad_conf) 66 | if env and 'kicad_config_home' in env: 67 | self.kicad_cfg_dir = env['kicad_config_home'] 68 | self.sch_ext = '.sch' 69 | self.pro_ext = '.pro' 70 | self.pcbnew_conf = os.path.join(self.kicad_cfg_dir, 'pcbnew') 71 | self.eeschema_conf = os.path.join(self.kicad_cfg_dir, 'eeschema') 72 | else: 73 | # self.kicad_cfg_dir = pcbnew.SETTINGS_MANAGER.GetUserSettingsPath().replace('/kicad/', '/kicadnightly/') 74 | self.kicad_cfg_dir = pcbnew.SETTINGS_MANAGER.GetUserSettingsPath() 75 | self.sch_ext = '.kicad_sch' 76 | self.pro_ext = '.kicad_pro' 77 | self.pcbnew_conf = os.path.join(self.kicad_cfg_dir, 'pcbnew.json') 78 | self.eeschema_conf = os.path.join(self.kicad_cfg_dir, 'eeschema.json') 79 | self.kicad_conf = os.path.join(self.kicad_cfg_dir, 'kicad_common.json') 80 | # We are using PCBs 81 | self.mode = mode 82 | self.kind = 'Schematic' if mode == MODE_SCH else 'PCB' 83 | # The name used for the test output dirs and other logging 84 | self.test_name = test_name 85 | # The name of the PCB board file 86 | self.case_num = str(case_num) 87 | # The actual board file that will be loaded 88 | self._get_work_file_name() 89 | # The actual output dir for this run 90 | self._set_up_output_dir(test_dir) 91 | # stdout and stderr from the run 92 | self.out = None 93 | self.err = None 94 | self.proc = None 95 | # Where is the reference output for this test 96 | self.set_ref_dir() 97 | self.include_lst = 'include.lst' 98 | 99 | def set_ref_dir(self, extra=''): 100 | self.ref_dir = os.path.join('tests', 'cases', str(self.case_num), 101 | 'ref_'+('pcb' if self.mode == MODE_PCB else 'sch')+extra) 102 | 103 | def _get_work_files_dir(self): 104 | this_dir = os.path.dirname(os.path.realpath(__file__)) 105 | return os.path.abspath(os.path.join(this_dir, self.board_dir)) 106 | 107 | def _get_work_file_name_x(self, x): 108 | return os.path.join(self._get_work_files_dir(), self.case_num, x, self.case_num + 109 | (KICAD_PCB_EXT if self.mode == MODE_PCB else self.sch_ext)) 110 | 111 | def _get_work_file_name(self): 112 | self.work_file_a = self._get_work_file_name_x('a') 113 | self.work_file_b = self._get_work_file_name_x('b') 114 | logging.info('{} files: {} / {}'.format(self.kind, self.work_file_a, self.work_file_b)) 115 | assert os.path.isfile(self.work_file_a), self.work_file_a 116 | assert os.path.isfile(self.work_file_b), self.work_file_b 117 | 118 | def invert(self): 119 | """ Do the diff in the reverse order """ 120 | aux = self.work_file_a 121 | self.work_file_a = self.work_file_b 122 | self.work_file_b = aux 123 | 124 | def _set_up_output_dir(self, test_dir): 125 | self.output_dir = os.path.join(test_dir, self.test_name) 126 | os.makedirs(self.output_dir, exist_ok=True) 127 | logging.info('Output dir: '+self.output_dir) 128 | 129 | def clean_up(self): 130 | logging.debug('Clean-up') 131 | 132 | def get_out_path(self, filename): 133 | return os.path.join(self.output_dir, filename) 134 | 135 | def expect_out_file(self, filename): 136 | file = self.get_out_path(filename) 137 | assert os.path.isfile(file) 138 | assert os.path.getsize(file) > 0 139 | return file 140 | 141 | def dont_expect_out_file(self, filename): 142 | file = self.get_out_path(filename) 143 | assert not os.path.isfile(file) 144 | 145 | def create_dummy_out_file(self, filename): 146 | file = self.get_out_path(filename) 147 | with open(file, 'w') as f: 148 | f.write('Dummy file\n') 149 | 150 | def get_pro_filename(self): 151 | return os.path.join(self._get_work_files_dir(), self.case_num, self.case_num+self.pro_ext) 152 | 153 | def get_prl_filename(self): 154 | return os.path.join(self._get_work_files_dir(), self.case_num, self.case_num+'.kicad_prl') 155 | 156 | def get_prodir_filename(self, file): 157 | return os.path.join(self._get_work_files_dir(), self.case_num, file) 158 | 159 | def get_pro_mtime(self): 160 | return os.path.getmtime(self.get_pro_filename()) 161 | 162 | def get_prl_mtime(self): 163 | if ki5: 164 | return os.path.getmtime(self.get_pro_filename()) 165 | return os.path.getmtime(self.get_prl_filename()) 166 | 167 | def get_sub_sheet_name(self, sub, ext): 168 | if ki5: 169 | return sub.lower()+'-'+sub+'.'+ext 170 | return self.case_num+'-'+sub+'.'+ext 171 | 172 | @staticmethod 173 | def read(fd): 174 | data = os.read(fd, 1024) 175 | TestContext.pty_data += data 176 | return data 177 | 178 | def run(self, ops=None, ret_val=None, extra=None, use_a_tty=False, filename=None, ignore_ret=False, no_dir=False, 179 | layers=False): 180 | logging.debug('Running '+self.test_name) 181 | # Change the command to be local and add the board and output arguments 182 | cmd = [COVERAGE_SCRIPT, 'run', '-a'] 183 | cmd += ['kicad-diff.py', '-vvv', '--cache', 'cache', '--output_dir', self.output_dir, '--no_reader', '--keep_pngs'] 184 | if layers: 185 | cmd += ['--layers', os.path.join(self._get_work_files_dir(), self.case_num, self.include_lst)] 186 | if ops: 187 | cmd += ops 188 | cmd += [self.work_file_a, self.work_file_b] 189 | logging.debug(usable_cmd(cmd)) 190 | 191 | out_filename = self.get_out_path('output.txt') 192 | err_filename = self.get_out_path('error.txt') 193 | exp_ret = 0 if ret_val is None else ret_val 194 | 195 | retry = 2 196 | while retry: 197 | if use_a_tty: 198 | # This is used to test the coloured logs, we need stderr to be a TTY 199 | TestContext.pty_data = b'' 200 | ret_code = spawn(cmd, self.read) 201 | self.err = TestContext.pty_data.decode() 202 | self.out = self.err 203 | else: 204 | # Redirect stdout and stderr to files 205 | f_out = os.open(out_filename, os.O_RDWR | os.O_CREAT) 206 | f_err = os.open(err_filename, os.O_RDWR | os.O_CREAT) 207 | # Run the process 208 | process = subprocess.Popen(cmd, stdout=f_out, stderr=f_err) 209 | ret_code = process.wait() 210 | logging.debug('ret_code '+str(ret_code)) 211 | if not ((ret_code == 9 or ret_code == 10) and exp_ret != 9 and exp_ret != 10): 212 | break 213 | retry -= 1 214 | if retry: 215 | logging.debug('Retrying ...') 216 | os.rename(self.output_dir, self.output_dir+'_RETRY_'+str(retry)) 217 | os.makedirs(self.output_dir, exist_ok=True) 218 | if not ignore_ret: 219 | assert ret_code == exp_ret, "got {} when {} expected".format(ret_code, exp_ret) 220 | if use_a_tty: 221 | with open(out_filename, 'w') as f: 222 | f.write(self.out) 223 | with open(err_filename, 'w') as f: 224 | f.write(self.out) 225 | else: 226 | # Read stdout 227 | os.lseek(f_out, 0, os.SEEK_SET) 228 | self.out = os.read(f_out, 1000000) 229 | os.close(f_out) 230 | self.out = self.out.decode() 231 | # Read stderr 232 | os.lseek(f_err, 0, os.SEEK_SET) 233 | self.err = os.read(f_err, 1000000) 234 | os.close(f_err) 235 | self.err = self.err.decode() 236 | 237 | def search_out(self, text): 238 | m = re.search(text, self.out, re.MULTILINE) 239 | return m 240 | 241 | def search_err(self, text): 242 | m = re.search(text, self.err, re.MULTILINE) 243 | return m 244 | 245 | def search_in_file(self, file, texts): 246 | logging.debug('Searching in "'+file+'" output') 247 | with open(self.get_out_path(file)) as f: 248 | txt = f.read() 249 | for t in texts: 250 | logging.debug('- r"'+t+'"') 251 | m = re.search(t, txt, re.MULTILINE) 252 | assert m 253 | 254 | @staticmethod 255 | def cmd_compare(img, ref, diff, fuzz): 256 | return ['compare', 257 | # Tolerate 30/50 % error in color 258 | '-fuzz', fuzz, 259 | # Count how many pixels differ 260 | '-metric', 'AE', 261 | # Create a 720p image 262 | '-size', 'x720', 263 | img, ref, 264 | # Avoid the part where KiCad version and title are printed 265 | # Also avoid the upper numbers. KiCad 5.1.7 changed the place for "1" 266 | # '-crop', '100%x80%+0+36', 267 | # Remove the area outside the image 268 | '+repage', 269 | '-colorspace', 'RGB', 270 | diff] 271 | 272 | @staticmethod 273 | def _compare_image(img, ref, diff, fuzz='30%'): 274 | exact = int(fuzz[:-1]) <= 30 275 | cmd = TestContext.cmd_compare(img, ref, diff, fuzz) 276 | logging.debug('Comparing images with: '+usable_cmd(cmd)) 277 | res = subprocess.run(cmd, stderr=subprocess.PIPE) 278 | assert res.returncode == 0 or not exact, "Compare failed for {} vs {}".format(ref, img) 279 | # m = re.match(r'([\d\.e-]+) \(([\d\.e-]+)\)', res.decode()) 280 | # assert m 281 | # logging.debug('MSE={} ({})'.format(m.group(1), m.group(2))) 282 | ae = int(res.stderr.decode()) 283 | logging.debug('AE=%d' % ae) 284 | assert ae == 0 if exact else ae < 100, ae 285 | 286 | def compare_image(self, image, reference=None, diff='diff.png', fuzz='30%'): 287 | """ For images and single page PDFs """ 288 | if reference is None: 289 | reference = image 290 | self._compare_image(self.get_out_path(image), os.path.join(self.ref_dir, reference), self.get_out_path(diff), fuzz) 291 | 292 | def svg_to_png(self, svg): 293 | png = os.path.splitext(svg)[0]+'.png' 294 | logging.debug('Converting '+svg+' to '+png) 295 | # cmd = ['convert', '-density', '150', svg, png] 296 | cmd = ['rsvg-convert', '-d', '150', '-p', '150', '-o', png, svg] 297 | subprocess.check_call(cmd) 298 | return os.path.basename(png) 299 | 300 | def compare_svg(self, image, reference=None, diff='diff.png'): 301 | """ For SVGs, rendering to PNG """ 302 | if reference is None: 303 | reference = image 304 | image_png = self.svg_to_png(self.get_out_path(image)) 305 | reference_png = self.svg_to_png(os.path.join(self.ref_dir, reference)) 306 | self.compare_image(image_png, reference_png, diff) 307 | os.remove(os.path.join(self.ref_dir, reference_png)) 308 | 309 | def ps_to_png(self, ps): 310 | png = os.path.splitext(ps)[0]+'.png' 311 | logging.debug('Converting '+ps+' to '+png) 312 | cmd = ['convert', '-density', '150', ps, '-rotate', '90', png] 313 | subprocess.check_call(cmd) 314 | return os.path.basename(png) 315 | 316 | def compare_ps(self, image, reference=None, diff='diff.png'): 317 | """ For PSs, rendering to PNG """ 318 | if reference is None: 319 | reference = image 320 | image_png = self.ps_to_png(self.get_out_path(image)) 321 | reference_png = self.ps_to_png(os.path.join(self.ref_dir, reference)) 322 | self.compare_image(image_png, reference_png, diff) 323 | os.remove(os.path.join(self.ref_dir, reference_png)) 324 | 325 | def compare_out_file(self, out_file='diff.pdf'): 326 | self.compare_pdf(out_file) 327 | 328 | def compare_out_pngs(self, out_file='diff.pdf'): 329 | out_file = os.path.splitext(out_file)[0] 330 | refs = os.path.join(self.ref_dir, out_file+'-*.png') 331 | pngs = glob(refs) 332 | assert pngs, "Nothing at "+refs 333 | for png in pngs: 334 | name = os.path.basename(png) 335 | diff = os.path.splitext(name)[0]+'-diff.png' 336 | self.compare_image(name, name, diff=diff, fuzz='30%') 337 | 338 | def compare_pdf(self, gen, reference=None, diff='diff-{}.png'): 339 | """ For multi-page PDFs """ 340 | if reference is None: 341 | reference = gen 342 | logging.debug('Comparing PDFs: '+gen+' vs '+reference+' (reference)') 343 | # Split the reference 344 | logging.debug('Splitting '+reference) 345 | cmd = ['convert', '-density', '150', 346 | os.path.join(self.ref_dir, reference), 347 | self.get_out_path('ref-%d.png')] 348 | subprocess.check_call(cmd) 349 | # Split the generated 350 | logging.debug('Splitting '+gen) 351 | cmd = ['convert', '-density', '150', 352 | self.get_out_path(gen), 353 | self.get_out_path('gen-%d.png')] 354 | subprocess.check_call(cmd) 355 | # Check number of pages 356 | ref_pages = glob(self.get_out_path('ref-*.png')) 357 | gen_pages = glob(self.get_out_path('gen-*.png')) 358 | logging.debug('Pages {} vs {}'.format(len(gen_pages), len(ref_pages))) 359 | assert len(ref_pages) == len(gen_pages) 360 | # Compare each page 361 | for page in range(len(ref_pages)): 362 | self._compare_image(self.get_out_path('ref-'+str(page)+'.png'), 363 | self.get_out_path('gen-'+str(page)+'.png'), 364 | self.get_out_path(diff.format(page))) 365 | 366 | def compare_txt(self, text, reference=None, diff='diff.txt'): 367 | if reference is None: 368 | reference = text 369 | cmd = ['/bin/sh', '-c', 'diff -ub '+os.path.join(self.ref_dir, reference)+' ' + 370 | self.get_out_path(text)+' > '+self.get_out_path(diff)] 371 | logging.debug('Comparing texts with: '+usable_cmd(cmd)) 372 | res = subprocess.call(cmd) 373 | assert res == 0 374 | 375 | def filter_txt(self, file, pattern, repl): 376 | fname = self.get_out_path(file) 377 | with open(fname) as f: 378 | txt = f.read() 379 | with open(fname, 'w') as f: 380 | f.write(re.sub(pattern, repl, txt)) 381 | 382 | @contextmanager 383 | def start_kicad(self, cmd, cfg): 384 | """ Context manager to run a command under a virtual X server. 385 | Use like this: with context.start_kicad('command'): """ 386 | with recorded_xvfb(cfg): 387 | if isinstance(cmd, str): 388 | cmd = [cmd] 389 | with PopenContext(cmd, stderr=subprocess.DEVNULL, close_fds=True) as self.proc: 390 | logging.debug('Started `'+str(cmd)+'` with PID: '+str(self.proc.pid)) 391 | assert pid_exists(self.proc.pid) 392 | yield 393 | logging.debug('Ending KiCad context') 394 | 395 | def stop_kicad(self): 396 | if self.proc: 397 | logging.debug('Stopping KiCad') 398 | assert pid_exists(self.proc.pid) 399 | self.proc.terminate() 400 | self.proc = None 401 | 402 | 403 | class TestContextSCH(TestContext): 404 | 405 | def __init__(self, test_dir, case_num, test_name=None, old_sch=False): 406 | if test_name is None: 407 | test_name = sys._getframe(1).f_code.co_name 408 | if test_name.startswith('test_'): 409 | test_name = test_name[5:] 410 | super().__init__(test_dir, case_num, test_name, mode=MODE_SCH) 411 | if old_sch: 412 | self.sch_ext = '.sch' 413 | self._get_work_file_name() 414 | --------------------------------------------------------------------------------