#!/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.4\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('#\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 footprint 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()