From ce1e08966b48411a570dbe2824b83b3993f2c50d Mon Sep 17 00:00:00 2001 From: Anders Blomdell <anders.blomdell@control.lth.se> Date: Fri, 8 Jun 2018 13:46:46 +0200 Subject: [PATCH] library merging tool --- tools/merge_libs | 280 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 280 insertions(+) create mode 100755 tools/merge_libs diff --git a/tools/merge_libs b/tools/merge_libs new file mode 100755 index 0000000..db9dce7 --- /dev/null +++ b/tools/merge_libs @@ -0,0 +1,280 @@ +#!/usr/bin/python3 + +import sys +import re +import os + +def assert_equal(a, b, message, path=None): + if a != b: + raise Exception("%s '%s' != '%s'%s" % + (message, a, b, path and " in '%s'" % path or '')) + raise Exception() + +class Lib: + + def __init__(self, path): + assert not os.path.exists(path), "'%s' already exists" % path + self.path = path + self.parsed = set() + self.defined = {} + self.component = {} + self.alias = {} + self.footprint = {} + pass + + def add(self, path, name, component, alias, footprint): + if name in self.defined: + if (component == self.component[name] and + alias == self.alias[name] and + footprint == self.footprint[name]): + print("'%s' in '%s' is duplicate of '%s'" % + (name, path, sorted(self.defined[name])), + file=sys.stderr) + pass + else: + raise Exception("'%s' in '%s' already defined in '%s'" % + (name, path, self.defined[name])) + pass + if not name in self.defined: + self.defined[name] = set() + self.component[name] = component + self.alias[name] = alias + self.footprint[name] = footprint + pass + self.defined[name].add(path) + + + + + def merge(self, path): + if path in self.parsed: + print('Already parsed %s' % path, file=sys.stderr) + return + self.parsed.add(path) + component = [] + fplist = None + footprint = set() + for l in open(path): + m = re.match('^EESchema-(\S+)\s+Version\s+([0-9.]+)\s*$', l) + if m: + assert_equal(m.group(1), 'LIBRARY', 'Unexpected kind', path) + assert_equal(m.group(2), '2.3', "Unexpected version", path) + continue + m = re.match('^#encoding utf-8', l) + if m: + continue + component.append(l) + m = re.match('^DEF\s+(\S+)', l) + if m: + name = m.group(1) + alias = set([name]) + pass + m = re.match('^ALIAS\s+(.*)\s*$', l) + if m: + alias.update(m.group(1).split()) + pass + m = re.match('^\$ENDFPLIST', l) + if m: + footprint = fplist + fplist = None + pass + if fplist != None: + fplist.add(l.strip()) + pass + m = re.match('^\$FPLIST', l) + if m: + fplist = set() + pass + m = re.match('^ENDDEF', l) + if m: + self.add(path, name, component, alias, footprint) + component = [] + fplist = None + footprint = set() + pass + pass + pass + + def commit(self): + with open(self.path, 'w') as f: + f.write('EESchema-LIBRARY Version 2.3\n') + f.write('#encoding utf-8\n') + for c in sorted(self.component): + f.write(''.join(self.component[c])) + pass + pass + f.write('#\n') + f.write('#End Library\n') + pass + +class DocLib: + + def __init__(self, path): + assert not os.path.exists(path), "'%s' already exists" % path + self.path = path + self.parsed = set() + self.defined = {} + self.component = {} + pass + + def add (self, path, name, component): + if name in self.defined: + if component == self.component[name]: + print("'%s' in '%s' is duplicate of '%s'" % + (name, path, sorted(self.defined[name])), + file=sys.stderr) + pass + else: + raise Exception("'%s' in '%s' already defined in '%s'" % + (name, path, self.defined[name])) + pass + if not name in self.defined: + self.defined[name] = set() + self.component[name] = component + pass + self.defined[name].add(path) + + def merge(self, path): + if path in self.parsed: + print('Already parsed %s' % path, file=sys.stderr) + return + self.parsed.add(path) + component = None + for l in open(path): + m = re.match('^EESchema-(\S+)\s+Version\s+([0-9.]+)\s*$', l) + if m: + assert_equal(m.group(1), 'DOCLIB', 'Unexpected kind', path) + assert_equal(m.group(2), '2.0', "Unexpected version", path) + continue + m = re.match('^#encoding utf-8', l) + if m: + continue + m = re.match('^\$CMP\s+(\S+)', l) + if m: + name = m.group(1) + component = [ ] + pass + if component != None: + component.append(l) + m = re.match('^\$ENDCMP', l) + if m: + self.add(path, name, component) + component = None + pass + pass + pass + + def commit(self): + with open(self.path, 'w') as f: + f.write('EESchema-DOCLIB Version 2.0\n') + f.write('#encoding utf-8\n') + for c in sorted(self.component): + f.write(''.join(self.component[c])) + f.write('#\n') + pass + pass + f.write('#End Doc Library\n') + pass + +class FootPrint: + + def __init__(self, path): + assert not os.path.exists(path), "'%s' already exists" % path + self.path = path + self.entries = {} + + def merge(self, path): + for p in os.listdir(path): + m = re.match('^(.*).kicad_mod', p) + if not m: + continue + name = m.group(1) + pp = os.path.join(path, p) + for l in open(pp): + m = re.match('.*\(\s*module\s+(\S+)', l) + if m: + assert_equal(name, m.group(1), 'Wrong module name', pp) + pass + pass + self.entries[name] = pp + pass + + def commit(self): + os.mkdir(self.path) + for k,v in self.entries.items(): + with open(os.path.join(self.path, '%s.kicad_mod' % k), 'w') as f1: + with open(v) as f2: + f1.write(f2.read()) + pass + pass + pass + pass + +if __name__ == '__main__': + dest = sys.argv[1] + destdir = os.path.dirname(dest) + source = sys.argv[2:] + assert os.path.isdir(destdir), "Destination '%s' does not exist" % destdir + lib = Lib('%s.lib' % (dest)) + dcm = DocLib('%s.dcm' % (dest)) + fp = FootPrint('%s.pretty' % (dest)) + for s in source: + for p in os.listdir(s): + if p.endswith('.lib'): + lib.merge(os.path.join(s, p)) + pass + elif p.endswith('.dcm'): + dcm.merge(os.path.join(s, p)) + pass + elif p.endswith('.pretty'): + fp.merge(os.path.join(s, p)) + pass + pass + pass + + # Sanity checks + alias = set() + for k in lib.alias: + for a in lib.alias[k]: + alias.add(a) + if not a in dcm.component: + print("'%s' doesn't have a description" % a, file=sys.stderr) + pass + pass + pass + for k in dcm.component: + if not k in alias: + print("description '%s' isn't used" % k, file=sys.stderr) + pass + pass + + import fnmatch + used = set() + for k in lib.footprint: + for p in lib.footprint[k]: + ok = False + for f in fp.entries: + if fnmatch.fnmatch(f, p): + ok = True + used.add(f) + pass + pass + if not ok: + print("No match for '%s(%s)' in '%s'" % + (k, p, lib.defined[k]), file=sys.stderr) + pass + pass + pass + for u in set(fp.entries.keys()) - used: + print("Unmatched footprint '%s' in '%s'" % + (u, fp.entries[u]), file=sys.stderr) + pass + + # Generate library + lib.commit() + dcm.commit() + fp.commit() + + + + -- GitLab