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