Removed all of the routines that generate compiled libraries from
individual files for various file formats (GDS, LEF, SPICE/CDL,
and verilog (liberty is unfinished and probably never will be), and
put the in individual files that can be either run separately from
the command line or included into the foundry_install.py script and
run internally.
diff --git a/common/foundry_install.py b/common/foundry_install.py
index 149c21f..267342b 100755
--- a/common/foundry_install.py
+++ b/common/foundry_install.py
@@ -116,6 +116,13 @@
 import fnmatch
 import subprocess
 
+# Import local routines
+from create_gds_library import create_gds_library
+from create_spice_library import create_spice_library
+from create_lef_library import create_lef_library
+from create_lib_library import create_lib_library
+from create_verilog_library import create_verilog_library
+
 def usage():
     print("foundry_install.py [options...]")
     print("   -copy             Copy files from source to target (default)")
@@ -327,438 +334,6 @@
                         print(line)
 
 #----------------------------------------------------------------------------
-# Given a destination directory holding individual verilog files of a number
-# of modules, create a single verilog library file named <alllibname> and place
-# it in the same directory.  This is done for the option "compile" if specified
-# for the "-verilog" install.
-#----------------------------------------------------------------------------
-
-def create_verilog_library(destlibdir, destlib, do_compile_only, do_stub, excludelist):
-
-    alllibname = destlibdir + '/' + destlib + '.v'
-    if os.path.isfile(alllibname):
-        os.remove(alllibname)
-
-    print('Diagnostic:  Creating consolidated verilog library ' + destlib + '.v')
-    vlist = glob.glob(destlibdir + '/*.v')
-    if alllibname in vlist:
-        vlist.remove(alllibname)
-
-    # Create exclude list with glob-style matching using fnmatch
-    if len(vlist) > 0:
-        vlistnames = list(os.path.split(item)[1] for item in vlist)
-        notvlist = []
-        for exclude in excludelist:
-            notvlist.extend(fnmatch.filter(vlistnames, exclude))
-
-        # Apply exclude list
-        if len(notvlist) > 0:
-            for file in vlist[:]:
-                if os.path.split(file)[1] in notvlist:
-                    vlist.remove(file)
-
-    if len(vlist) > 1:
-        print('New file is:  ' + alllibname)
-        with open(alllibname, 'w') as ofile:
-            allmodules = []
-            for vfile in vlist:
-                with open(vfile, 'r') as ifile:
-                    # print('Adding ' + vfile + ' to library.')
-                    vtext = ifile.read()
-                    modules = re.findall(r'[ \t\n]module[ \t]+([^ \t\n\(]+)', vtext)
-                    mseen = list(item for item in modules if item in allmodules)
-                    allmodules.extend(list(item for item in modules if item not in allmodules))
-                    vfilter = remove_redundant_modules(vtext, allmodules, mseen)
-                    # NOTE:  The following workaround resolves an issue with iverilog,
-                    # which does not properly parse specify timing paths that are not in
-                    # parentheses.  Easy to work around
-                    vlines = re.sub(r'\)[ \t]*=[ \t]*([01]:[01]:[01])[ \t]*;', r') = ( \1 ) ;', vfilter)
-                    print(vlines, file=ofile)
-                print('\n//--------EOF---------\n', file=ofile)
-
-        if do_compile_only == True:
-            print('Compile-only:  Removing individual verilog files')
-            for vfile in vlist:
-                if os.path.isfile(vfile):
-                    os.remove(vfile)
-                elif os.path.islink(vfile):
-                    os.unlink(vfile)
-    else:
-        print('Only one file (' + str(vlist) + ');  ignoring "compile" option.')
-
-#----------------------------------------------------------------------------
-# Remove redundant module entries from a verilog file.  "m2list" is a list of
-# module names gleaned from all previously read files using re.findall().
-# "mlist" is a list of all module names including those in "ntext".
-# The reason for doing this is that some verilog files may includes modules used
-# by all the files, and if included more than once, then iverilog complains.
-#----------------------------------------------------------------------------
-
-def remove_redundant_modules(ntext, mlist, m2list):
-    updated = ntext
-    for module in mlist:
-        # Determine the number of times the module appears in the text
-        if module in m2list:
-            # This module seen before outside of ntext, so remove all occurrances in ntext
-            new = re.sub(r'[ \t\n]+module[ \t]+' + module + '[ \t\n\(]+.*[ \t\n]endmodule', '\n', updated, flags=re.DOTALL)
-            updated = new
-
-        else:
-            n = len(re.findall(r'[ \t\n]module[ \t]+' + module + '[ \t\n\(]+.*[ \t\n]endmodule', updated, flags=re.DOTALL))
-            # This module defined more than once inside ntext, so remove all but one
-            # Optimization:  Just keep original text if n < 2
-            if n < 2:
-                continue
-
-            # Remove all but one
-            updated = re.sub(r'[ \t\n]+module[ \t]+' + module + '[ \t\n]+.*[ \t\n]endmodule', '\n', n - 1, updated, flags=re.IGNORECASE | re.DOTALL)
-    return updated
-
-#----------------------------------------------------------------------------
-# Given a destination directory holding individual LEF files of a number
-# of cells, create a single LEF library file named <alllibname> and place
-# it in the same directory.  This is done for the option "compile" if specified
-# for the "-lef" install.
-#----------------------------------------------------------------------------
-
-def create_lef_library(destlibdir, destlib, do_compile_only, excludelist):
-
-    alllibname = destlibdir + '/' + destlib + '.lef'
-    if os.path.isfile(alllibname):
-        os.remove(alllibname)
-
-    print('Diagnostic:  Creating consolidated LEF library ' + destlib + '.lef')
-    llist = glob.glob(destlibdir + '/*.lef')
-    if alllibname in llist:
-        llist.remove(alllibname)
-
-    # Create exclude list with glob-style matching using fnmatch
-    if len(llist) > 0:
-        llistnames = list(os.path.split(item)[1] for item in llist)
-        notllist = []
-        for exclude in excludelist:
-            notllist.extend(fnmatch.filter(llistnames, exclude))
-
-        # Apply exclude list
-        if len(notllist) > 0:
-            for file in llist[:]:
-                if os.path.split(file)[1] in notllist:
-                    llist.remove(file)
-
-    if len(llist) > 1:
-        print('New file is:  ' + alllibname)
-        with open(alllibname, 'w') as ofile:
-            headerdone = False
-            for lfile in llist:
-                with open(lfile, 'r') as ifile:
-                    # print('Adding ' + lfile + ' to library.')
-                    ltext = ifile.read()
-                    llines = ltext.splitlines()
-                    headerseen = False
-                    for lline in llines:
-                        if headerdone:
-                            if not headerseen:
-                                if not lline.startswith('MACRO'):
-                                    continue
-                                else:
-                                    headerseen = True
-                        print(lline, file=ofile)
-                    headerdone = True
-                print('#--------EOF---------\n', file=ofile)
-
-        if do_compile_only == True:
-            print('Compile-only:  Removing individual LEF files')
-            for lfile in llist:
-                if os.path.isfile(lfile):
-                    os.remove(lfile)
-            if newname:
-                if os.path.isfile(newname):
-                    os.remove(newname)
-    else:
-        print('Only one file (' + str(llist) + ');  ignoring "compile" option.')
-
-#----------------------------------------------------------------------------
-# Given a destination directory holding individual liberty files of a number
-# of cells, create a single liberty library file named <alllibname> and place
-# it in the same directory.  This is done for the option "compile" if specified
-# for the "-lib" install.
-#----------------------------------------------------------------------------
-
-# Warning:  This script is unfinished.  Needs to parse the library header
-# in each cell and generate a new library header combining the contents of
-# all cell headers.  Also:  The library name in the header needs to be
-# changed to the full library name.  Also:  There is no mechanism for
-# collecting all files belonging to a single process corner/temperature/
-# voltage.
-
-def create_lib_library(destlibdir, destlib, do_compile_only, excludelist):
-
-    alllibname = destlibdir + '/' + destlib + '.lib'
-    if os.path.isfile(alllibname):
-        os.remove(alllibname)
-
-    print('Diagnostic:  Creating consolidated liberty library ' + destlib + '.lib')
-
-    # Create exclude list with glob-style matching using fnmatch
-    if len(llist) > 0:
-        llistnames = list(os.path.split(item)[1] for item in llist)
-        notllist = []
-        for exclude in excludelist:
-            notllist.extend(fnmatch.filter(llistnames, exclude))
-
-        # Apply exclude list
-        if len(notllist) > 0:
-            for file in llist[:]:
-                if os.path.split(file)[1] in notllist:
-                    llist.remove(file)
-
-    if len(llist) > 1:
-        print('New file is:  ' + alllibname)
-        with open(alllibname, 'w') as ofile:
-            headerdone = False
-            for lfile in llist:
-                with open(lfile, 'r') as ifile:
-                    # print('Adding ' + lfile + ' to library.')
-                    ltext = ifile.read()
-                    llines = ltext.splitlines()
-                    headerseen = False
-                    for lline in llines:
-                        if headerdone:
-                            if not headerseen:
-                                if not lline.split()[0] == 'cell':
-                                    continue
-                                else:
-                                    headerseen = True
-                        print(lline, file=ofile)
-                    headerdone = True
-                print('/*--------EOF---------*/\n', file=ofile)
-
-        if do_compile_only == True:
-            print('Compile-only:  Removing individual LEF files')
-            for lfile in llist:
-                if os.path.isfile(lfile):
-                    os.remove(lfile)
-            if newname:
-                if os.path.isfile(newname):
-                    os.remove(newname)
-    else:
-        print('Only one file (' + str(llist) + ');  ignoring "compile" option.')
-
-#----------------------------------------------------------------------------
-# Given a destination directory holding individual GDS files of a number
-# of cells, create a single GDL library file named <alllibname> and place
-# it in the same directory.  This is done for the option "compile" if specified
-# for the "-gds" install.
-#----------------------------------------------------------------------------
-
-def create_gds_library(destlibdir, destlib, startup_script, do_compile_only, excludelist):
-
-    alllibname = destlibdir + '/' + destlib + '.gds'
-    if os.path.isfile(alllibname):
-        os.remove(alllibname)
-
-    print('Diagnostic:  Creating consolidated GDS library ' + destlib + '.gds')
-    glist = glob.glob(destlibdir + '/*.gds')
-    glist.extend(glob.glob(destlibdir + '/*.gdsii'))
-    glist.extend(glob.glob(destlibdir + '/*.gds2'))
-    if alllibname in glist:
-        glist.remove(alllibname)
-
-    # Create exclude list with glob-style matching using fnmatch
-    if len(glist) > 0:
-        glistnames = list(os.path.split(item)[1] for item in glist)
-        notglist = []
-        for exclude in excludelist:
-            notglist.extend(fnmatch.filter(glistnames, exclude))
-
-        # Apply exclude list
-        if len(notglist) > 0:
-            for file in glist[:]:
-                if os.path.split(file)[1] in notglist:
-                    glist.remove(file)
-
-    if len(glist) > 1:
-        print('New file is:  ' + alllibname)
-
-        if os.path.isfile(startup_script):
-            # If the symbolic link exists, remove it.
-            if os.path.isfile(destlibdir + '/.magicrc'):
-                os.remove(destlibdir + '/.magicrc')
-            os.symlink(startup_script, destlibdir + '/.magicrc')
-
-        # A GDS library is binary and requires handling in Magic
-        print('Creating magic generation script to generate GDS library.') 
-        with open(destlibdir + '/generate_magic.tcl', 'w') as ofile:
-            print('#!/usr/bin/env wish', file=ofile)
-            print('#--------------------------------------------', file=ofile)
-            print('# Script to generate .gds library from files   ', file=ofile)
-            print('#--------------------------------------------', file=ofile)
-            print('drc off', file=ofile)
-            print('gds readonly true', file=ofile)
-            print('gds flatten true', file=ofile)
-            print('gds rescale false', file=ofile)
-            print('tech unlock *', file=ofile)
-
-            for gdsfile in glist:
-                print('gds read ' + gdsfile, file=ofile)
-
-            print('puts stdout "Creating cell ' + destlib + '"', file=ofile)
-            print('load ' + destlib, file=ofile)
-            print('puts stdout "Adding cells to library"', file=ofile)
-            print('box values 0 0 0 0', file=ofile)
-            for gdsfile in glist:
-                gdsroot = os.path.split(gdsfile)[1]
-                gdsname = os.path.splitext(gdsroot)[0]
-                print('getcell ' + gdsname, file=ofile)
-                # Could properly make space for the cell here. . . 
-                print('box move e 200', file=ofile)
-                                
-            print('puts stdout "Writing GDS library ' + destlib + '"', file=ofile)
-            print('gds library', file=ofile)
-            print('gds write ' + destlib, file=ofile)
-            print('puts stdout "Done."', file=ofile)
-            print('quit -noprompt', file=ofile)
-
-        # Run magic to read in the individual GDS files and
-        # write out the consolidated GDS library
-
-        print('Running magic to create GDS library.')
-        sys.stdout.flush()
-
-        mproc = subprocess.run(['magic', '-dnull', '-noconsole',
-			destlibdir + '/generate_magic.tcl'],
-			stdin = subprocess.DEVNULL,
-			stdout = subprocess.PIPE,
-			stderr = subprocess.PIPE, cwd = destlibdir,
-			universal_newlines = True)
-
-        if mproc.stdout:
-            for line in mproc.stdout.splitlines():
-                print(line)
-        if mproc.stderr:
-            print('Error message output from magic:')
-            for line in mproc.stderr.splitlines():
-                print(line)
-        if mproc.returncode != 0:
-            print('ERROR:  Magic exited with status ' + str(mproc.returncode))
-        if do_compile_only == True:
-            print('Compile-only:  Removing individual GDS files')
-            for gfile in glist:
-                if os.path.isfile(gfile):
-                    os.remove(gfile)
-            if newname:
-                if os.path.isfile(newname):
-                    os.remove(newname)
-    else:
-        print('Only one file (' + str(glist) + ');  ignoring "compile" option.')
-
-#----------------------------------------------------------------------------
-# Given a destination directory holding individual SPICE netlists of a number
-# of cells, create a single SPICE library file named <alllibname> and place
-# it in the same directory.  This is done for the option "compile" if specified
-# for the "-spice" install.
-#----------------------------------------------------------------------------
-
-def create_spice_library(destlibdir, destlib, spiext, do_compile_only, do_stub, excludelist):
-
-    fformat = 'CDL' if spiext == '.cdl' else 'SPICE'
-
-    allstubname = destlibdir + '/stub' + spiext
-    alllibname = destlibdir + '/' + destlib + spiext
-    if do_stub:
-        outputname = allstubname
-    else:
-        outputname = alllibname
-
-    print('Diagnostic:  Creating consolidated ' + fformat + ' library ' + outputname)
-
-    if os.path.isfile(outputname):
-        os.remove(outputname)
-
-    if fformat == 'CDL':
-        slist = glob.glob(destlibdir + '/*.cdl')
-    else:
-        # Sadly, there is no consensus on what a SPICE file extension should be.
-        slist = glob.glob(destlibdir + '/*.spc')
-        slist.extend(glob.glob(destlibdir + '/*.spice'))
-        slist.extend(glob.glob(destlibdir + '/*.spi'))
-        slist.extend(glob.glob(destlibdir + '/*.ckt'))
-
-    if alllibname in slist:
-        slist.remove(alllibname)
-
-    if allstubname in slist:
-        slist.remove(allstubname)
-
-    # Create exclude list with glob-style matching using fnmatch
-    if len(slist) > 0:
-        slistnames = list(os.path.split(item)[1] for item in slist)
-        notslist = []
-        for exclude in excludelist:
-            notslist.extend(fnmatch.filter(slistnames, exclude))
-
-        # Apply exclude list
-        if len(notslist) > 0:
-            for file in slist[:]:
-                if os.path.split(file)[1] in notslist:
-                    slist.remove(file)
-
-    if len(slist) > 1:
-        with open(outputname, 'w') as ofile:
-            allsubckts = []
-            for sfile in slist:
-                with open(sfile, 'r') as ifile:
-                    # print('Adding ' + sfile + ' to library.')
-                    stext = ifile.read()
-                    subckts = re.findall(r'\.subckt[ \t]+([^ \t\n]+)', stext, flags=re.IGNORECASE)
-                    sseen = list(item for item in subckts if item in allsubckts)
-                    allsubckts.extend(list(item for item in subckts if item not in allsubckts))
-                    sfilter = remove_redundant_subckts(stext, allsubckts, sseen)
-                    print(sfilter, file=ofile)
-                print('\n******* EOF\n', file=ofile)
-
-        if do_compile_only == True:
-            print('Compile-only:  Removing individual SPICE files')
-            for sfile in slist:
-                if os.path.isfile(sfile):
-                    os.remove(sfile)
-                elif os.path.islink(sfile):
-                    os.unlink(sfile)
-    else:
-        print('Only one file (' + str(slist) + ');  ignoring "compile" option.')
-
-#----------------------------------------------------------------------------
-# Remove redundant subcircuit entries from a SPICE or CDL netlist file.  "sseen"
-# is a list of subcircuit names gleaned from all previously read files using
-# re.findall(). "slist" is a list of subcircuits including those in "ntext".
-# If a subcircuit is defined outside of "ntext", then remove all occurrences in
-# "ntext".  Otherwise, if a subcircuit is defined more than once in "ntext",
-# remove all but one copy.  The reason for doing this is that some netlists will
-# include primitive device definitions used by all the standard cell subcircuits.
-#
-# It may be necessary to remove redundant .include statements and redundant .model
-# and/or .option statements as well.
-#----------------------------------------------------------------------------
-
-def remove_redundant_subckts(ntext, slist, sseen):
-    updated = ntext
-    for subckt in slist:
-        if subckt in sseen:
-            # Remove all occurrences of subckt
-            updated = re.sub(r'\n\.subckt[ \t]+' + subckt + '[ \t\n]+.*\n\.ends[ \t\n]+', '\n', updated, flags=re.IGNORECASE | re.DOTALL)
-
-        else:
-            # Determine the number of times the subcircuit appears in the text
-            n = len(re.findall(r'\n\.subckt[ \t]+' + subckt + '[ \t\n]+.*\n\.ends[ \t\n]+', updated, flags=re.IGNORECASE | re.DOTALL))
-            # Optimization:  Just keep original text if n < 2
-            if n < 2:
-                continue
-
-            # Remove all but one
-            updated = re.sub(r'\n\.subckt[ \t]+' + subckt + '[ \t\n]+.*\n\.ends[ \t\n]+', '\n', n - 1, updated, flags=re.IGNORECASE | re.DOTALL)
-    return updated
-
-#----------------------------------------------------------------------------
 # This is the main entry point for the foundry install script.
 #----------------------------------------------------------------------------