Various modifications to accommodate the somewhat complicated
management of include statements in verilog files in order to
build the compiled libraries.
diff --git a/VERSION b/VERSION
index 42de374..7cb055c 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.0.40
+1.0.41
diff --git a/common/create_gds_library.py b/common/create_gds_library.py
index 85380fe..34424fb 100755
--- a/common/create_gds_library.py
+++ b/common/create_gds_library.py
@@ -38,7 +38,10 @@
 
 def create_gds_library(destlibdir, destlib, startup_script, do_compile_only=False, excludelist=[], keep=False):
 
-    alllibname = destlibdir + '/' + destlib + '.gds'
+    # destlib should not have a file extension
+    destlibroot = os.path.splitext(destlib)[0]
+
+    alllibname = destlibdir + '/' + destlibroot + '.gds'
     if os.path.isfile(alllibname):
         os.remove(alllibname)
 
@@ -86,8 +89,8 @@
             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 "Creating cell ' + destlibroot + '"', file=ofile)
+            print('load ' + destlibroot, file=ofile)
             print('puts stdout "Adding cells to library"', file=ofile)
             print('box values 0 0 0 0', file=ofile)
             for gdsfile in glist:
@@ -97,9 +100,9 @@
                 # 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('puts stdout "Writing GDS library ' + destlibroot + '"', file=ofile)
             print('gds library true', file=ofile)
-            print('gds write ' + destlib, file=ofile)
+            print('gds write ' + destlibroot, file=ofile)
             print('puts stdout "Done."', file=ofile)
             print('quit -noprompt', file=ofile)
 
diff --git a/common/create_lef_library.py b/common/create_lef_library.py
index 4b8ecf3..8a922c8 100755
--- a/common/create_lef_library.py
+++ b/common/create_lef_library.py
@@ -35,11 +35,14 @@
 
 def create_lef_library(destlibdir, destlib, do_compile_only=False, excludelist=[]):
 
-    alllibname = destlibdir + '/' + destlib + '.lef'
+    # destlib should not have a file extension
+    destlibroot = os.path.splitext(destlib)[0]
+
+    alllibname = destlibdir + '/' + destlibroot + '.lef'
     if os.path.isfile(alllibname):
         os.remove(alllibname)
 
-    print('Diagnostic:  Creating consolidated LEF library ' + destlib + '.lef')
+    print('Diagnostic:  Creating consolidated LEF library ' + destlibroot + '.lef')
     llist = glob.glob(destlibdir + '/*.lef')
     if alllibname in llist:
         llist.remove(alllibname)
diff --git a/common/create_lib_library.py b/common/create_lib_library.py
index 7ee350c..bfa773d 100755
--- a/common/create_lib_library.py
+++ b/common/create_lib_library.py
@@ -42,11 +42,14 @@
 
 def create_lib_library(destlibdir, destlib, do_compile_only=False, excludelist=[]):
 
-    alllibname = destlibdir + '/' + destlib + '.lib'
+    # destlib should not have a file extension
+    destlibrooot = os.path.splitext(destlib)[0]
+
+    alllibname = destlibdir + '/' + destlibroot + '.lib'
     if os.path.isfile(alllibname):
         os.remove(alllibname)
 
-    print('Diagnostic:  Creating consolidated liberty library ' + destlib + '.lib')
+    print('Diagnostic:  Creating consolidated liberty library ' + destlibroot + '.lib')
 
     # Create exclude list with glob-style matching using fnmatch
     if len(llist) > 0:
diff --git a/common/create_spice_library.py b/common/create_spice_library.py
index a25ac34..ca95fa6 100755
--- a/common/create_spice_library.py
+++ b/common/create_spice_library.py
@@ -38,10 +38,13 @@
 
 def create_spice_library(destlibdir, destlib, spiext, do_compile_only=False, do_stub=False, excludelist=[]):
 
+    # destlib should not have a file extension
+    destlibroot = os.path.splitext(destlib)[0]
+
     fformat = 'CDL' if spiext == '.cdl' else 'SPICE'
 
     allstubname = destlibdir + '/stub' + spiext
-    alllibname = destlibdir + '/' + destlib + spiext
+    alllibname = destlibdir + '/' + destlibroot + spiext
     if do_stub:
         outputname = allstubname
     else:
diff --git a/common/create_verilog_library.py b/common/create_verilog_library.py
index fb530ab..32ce2c5 100755
--- a/common/create_verilog_library.py
+++ b/common/create_verilog_library.py
@@ -37,11 +37,14 @@
 
 def create_verilog_library(destlibdir, destlib, do_compile_only=False, do_stub=False, excludelist=[]):
 
-    alllibname = destlibdir + '/' + destlib + '.v'
+    # 'destlib' should not have an extension, because one will be generated.
+    destlibroot = os.path.splitext(destlib)[0]
+
+    alllibname = destlibdir + '/' + destlibroot + '.v'
     if os.path.isfile(alllibname):
         os.remove(alllibname)
 
-    print('Diagnostic:  Creating consolidated verilog library ' + destlib + '.v')
+    print('Diagnostic:  Creating consolidated verilog library ' + destlibroot + '.v')
     vlist = glob.glob(destlibdir + '/*.v')
     if alllibname in vlist:
         vlist.remove(alllibname)
diff --git a/common/foundry_install.py b/common/foundry_install.py
index 40782e0..7028956 100755
--- a/common/foundry_install.py
+++ b/common/foundry_install.py
@@ -805,7 +805,23 @@
                 destpathcomp.reverse()
                 destpath = ''.join(destpathcomp)
 
+                if option[0] == 'verilog':
+                    fileext = '.v'
+                elif option[0] == 'gds':
+                    fileext = '.gds'
+                elif option[0] == 'liberty' or option[0] == 'lib':
+                    fileext = '.lib'
+                elif option[0] == 'spice' or option[0] == 'spi':
+                    fileext = '.spice' if not ef_format else '.spi'
+                elif option[0] == 'cdl':
+                    fileext = '.cdl'
+                elif option[0] == 'lef':
+                    fileext = '.lef'
+
                 if newname:
+                    if os.path.splitext(newname)[1] == '':
+                        newname = newname + fileext
+
                     if len(liblist) == 1:
                         destfile = newname
                     else:
@@ -864,7 +880,7 @@
             if do_compile == True or do_compile_only == True:
                 # NOTE:  The purpose of "rename" is to put a destlib-named
                 # library elsewhere so that it can be merged with another
-                # library into a compiled <destlib>.<ext>
+                # library into a compiled <destlib>.<ext> on another pass.
 
                 compname = destlib
                     
@@ -877,11 +893,6 @@
 
                     create_verilog_library(destlibdir, compname, do_compile_only, do_stub, excludelist)
 
-                    if do_compile_only == True:
-                        if newname:
-                            if os.path.isfile(newname):
-                                os.remove(newname)
-
                 elif option[0] == 'gds' and have_mag_8_2:
                     # If there is not a single file with all GDS cells in it,
                     # then compile one.
@@ -892,11 +903,6 @@
                         startup_script = targetdir + mag_current + pdkname + '.magicrc'
                     create_gds_library(destlibdir, compname, startup_script, do_compile_only, excludelist)
 
-                    if do_compile_only == True:
-                        if newname:
-                            if os.path.isfile(newname):
-                                os.remove(newname)
-
                 elif option[0] == 'liberty' or option[0] == 'lib':
                     # If there is not a single file with all liberty cells in it,
                     # then compile one, because one does not want to have to have
@@ -904,11 +910,6 @@
 
                     create_lib_library(destlibdir, compname, do_compile_only, excludelist)
 
-                    if do_compile_only == True:
-                        if newname:
-                            if os.path.isfile(newname):
-                                os.remove(newname)
-
                 elif option[0] == 'spice' or option[0] == 'spi':
                     # If there is not a single file with all SPICE subcircuits in it,
                     # then compile one, because one does not want to have to have
@@ -916,10 +917,6 @@
 
                     spiext = '.spice' if not ef_format else '.spi'
                     create_spice_library(destlibdir, compname, spiext, do_compile_only, do_stub, excludelist)
-                    if do_compile_only == True:
-                        if newname:
-                            if os.path.isfile(newname):
-                                os.remove(newname)
 
                 elif option[0] == 'cdl':
                     # If there is not a single file with all CDL subcircuits in it,
@@ -927,10 +924,6 @@
                     # an include line for every single cell used in a design.
 
                     create_spice_library(destlibdir, compname, '.cdl', do_compile_only, do_stub, excludelist)
-                    if do_compile_only == True:
-                        if newname:
-                            if os.path.isfile(newname):
-                                os.remove(newname)
 
                 elif option[0] == 'lef':
                     # If there is not a single file with all LEF cells in it,
@@ -939,10 +932,20 @@
 
                     create_lef_library(destlibdir, compname, do_compile_only, excludelist)
 
-                    if do_compile_only == True:
-                        if newname:
-                            if os.path.isfile(newname):
-                                os.remove(newname)
+                if do_compile_only == True:
+                    if newname:
+                        if os.path.isfile(targname):
+                            os.remove(targname)
+
+                # "rename" with "compile" or "compile-only":  Change the name
+                # of the compiled file.
+
+                if newname:
+                    print('   Renaming ' + compname + fileext + ' to ' + newname)
+                    origname = destlibdir + '/' + compname + fileext
+                    targrename = destlibdir + destpath + '/' + newname
+                    if os.path.isfile(origname):
+                        os.rename(origname, targrename)
 
         # Find any libraries/options marked as "privileged" (or "private") and
         # move the files from libs.tech or libs.ref to libs.priv, leaving a
diff --git a/sky130/Makefile.in b/sky130/Makefile.in
index daa0808..2cfa21e 100644
--- a/sky130/Makefile.in
+++ b/sky130/Makefile.in
@@ -385,7 +385,8 @@
 		-doc %l/latest/cells/*/*.pdf \
 		-lib %l/latest/timing/*.lib \
 		-gds %l/latest/cells/*/*.gds compile-only \
-		-verilog %l/latest/cells/*/*.v compile-only \
+		-verilog %l/latest/models/*/*.v exclude *.*.v compile-only rename test \
+		-verilog %l/latest/cells/*/*.v exclude *.*.v,test,test.v compile-only \
 		-library digital sky130_fd_sc_hd \
 		-library digital sky130_fd_sc_hdll \
 		-library digital sky130_fd_sc_hvl \
diff --git a/sky130/custom/scripts/inc_verilog.py b/sky130/custom/scripts/inc_verilog.py
new file mode 100755
index 0000000..d34129d
--- /dev/null
+++ b/sky130/custom/scripts/inc_verilog.py
@@ -0,0 +1,110 @@
+#!/bin/env python3
+#
+# inc_verilog ---
+#
+# This script handles the verilog sources by removing `include statements
+# for files that are already being added to the single consolidated
+# verilog library file, and in-lining any other verilog files (namely
+# the functional and behavioral sources).
+#
+# This script is a filter to be run by setting the name of this script as
+# the value to "filter=" for the model install in the sky130 Makefile.
+
+import re
+import os
+import sys
+
+def filter(inname, outname):
+
+    # Read input
+    try:
+        with open(inname, 'r') as inFile:
+            vtext = inFile.read()
+            vlines = vtext.splitlines()
+    except:
+        print('inc_verilog.py: failed to open ' + fnmIn + ' for reading.', file=sys.stderr)
+        return 1
+
+    # Process input with regexp
+
+    fixedlines = []
+    modified = False
+    increx = re.compile('[ \t]*`include[ \t]+"?([^ \t\n"]+)"?')
+    ddotrex = re.compile('[^\.]+\.[^\.]+\.v')
+    tdotrex = re.compile('[^\.]+\.[^\.]+\.[^\.]+\.v')
+    inpath = os.path.split(inname)[0]
+
+    for line in vlines:
+
+        # Check includes
+        imatch = increx.match(line)
+        if imatch:
+            incfilename = imatch.group(1)
+            dmatch = ddotrex.match(incfilename)
+            tmatch = tdotrex.match(incfilename)
+            if dmatch or tmatch:
+                # double-dot or triple-dot:  Include this file in-line
+                # NOTE:  These files are assumed not to need in-line
+                # includes, but includes of primitives need to be ignored.
+
+                with open(inpath + '/' + incfilename, 'r') as incfile:
+                    v2text = incfile.read()
+                    v2lines = v2text.splitlines()
+                    for line2 in v2lines:
+                        i2match = imatch.match(line2)
+                        if not i2match:
+                            fixedlines.append(line2)
+            else:
+                # single-dot:  Ignore this line
+                pass
+            modified = True
+        else:
+            fixedlines.append(line)
+
+    # Write output
+    if outname == None:
+        for i in fixedlines:
+            print(i)
+    else:
+        # If the output is a symbolic link but no modifications have been made,
+        # then leave it alone.  If it was modified, then remove the symbolic
+        # link before writing.
+        if os.path.islink(outname):
+            if not modified:
+                return 0
+            else:
+                os.unlink(outname)
+        try:
+            with open(outname, 'w') as outFile:
+                for i in fixedlines:
+                    print(i, file=outFile)
+        except:
+            print('inc_verilog.py: failed to open ' + outname + ' for writing.', file=sys.stderr)
+            return 1
+
+
+if __name__ == '__main__':
+
+    # This script expects to get one or two arguments.  One argument is
+    # mandatory and is the input file.  The other argument is optional and
+    # is the output file.  The output file and input file may be the same
+    # name, in which case the original input is overwritten.
+
+    options = []
+    arguments = []
+    for item in sys.argv[1:]:
+        if item.find('-', 0) == 0:
+            options.append(item[1:])
+        else:
+            arguments.append(item)
+
+    if len(arguments) > 0:
+        infilename = arguments[0]
+
+    if len(arguments) > 1:
+        outfilename = arguments[1]
+    else:
+        outfilename = None
+
+    result = filter(infilename, outfilename)
+    sys.exit(result)