Added a script to do the final composition from the top level design,
the seal ring, and the fill generation.
diff --git a/scripts/compositor.py b/scripts/compositor.py
index 3d96ca4..00b96b8 100644
--- a/scripts/compositor.py
+++ b/scripts/compositor.py
@@ -1,5 +1,118 @@
 #!/bin/env python3
 #
-# compository.py ---
+# compositor.py ---
 #
-#    Final composition.  Combine the caravel top level, seal ring, and fill.
+#    Compose the final GDS for caravel from the caravel GDS, seal ring
+#    GDS, and fill GDS.
+#
+
+import sys
+import os
+import re
+import subprocess
+
+def usage():
+    print("compositor.py [layout_name] [-keep]")
+    return 0
+
+if __name__ == '__main__':
+
+    if len(sys.argv) == 1:
+        usage()
+        sys.exit(0)
+
+    optionlist = []
+    arguments = []
+
+    debugmode = False
+    keepmode = False
+
+    for option in sys.argv[1:]:
+        if option.find('-', 0) == 0:
+            optionlist.append(option)
+        else:
+            arguments.append(option)
+
+    if len(arguments) > 1:
+        print("Wrong number of arguments given to compositor.py.")
+        usage()
+        sys.exit(0)
+
+    if len(arguments) == 1:
+        project = arguments[0]
+    else:
+        project = 'caravel'
+
+    if '-debug' in optionlist:
+        debugmode = True
+    if '-keep' in optionlist:
+        keepmode = True
+
+    magdir = '../mag'
+    rcfile = magdir + '/.magicrc'
+
+    with open(magdir + '/compose_final.tcl', 'w') as ofile:
+        print('#!/bin/env wish', file=ofile)
+        print('drc off', file=ofile)
+
+        print('load ' + project + ' -dereference', file=ofile)
+        print('select top cell', file=ofile)
+
+        # Ceate a cell to represent the generated fill.  There are
+        # no magic layers corresponding to the fill shape data, and
+        # it's gigabytes anyway, so we don't want to deal with any
+        # actual data.  So it's just a placeholder.
+
+        print('set bbox [box values]', file=ofile)
+        print('load ' + project + '_fill_pattern', file=ofile)
+        print('snap internal', file=ofile)
+        print('box values {*}$bbox', file=ofile)
+        print('paint comment', file=ofile)
+        print('property GDS_FILE ../gds/' + project + '_fill_pattern.gds', file=ofile)
+        print('property GDS_START 0', file=ofile)
+        print('property FIXED_BBOX "$bbox"', file=ofile)
+
+        # Now go back to the project top level and place the fill cell.
+        print('load ' + project, file=ofile)
+	print('select top cell', file=ofile)	
+        print('getcell ' + project + '_fill_pattern child 0 0', file=ofile)
+
+        # Move existing origin to (6um, 6um) for seal ring placement
+        print('move origin -6um -6um', file=ofile)
+
+        # Read in abstract view of seal ring
+        print('box position 0 0', file=ofile)
+        print('getcell advSeal_6um_gen', file=ofile)
+
+        # Generate final GDS
+        print('puts stdout "Writing final GDS. . . "', file=ofile)
+        print('flush stdout', file=ofile)
+        print('gds write ../gds/' + project + '_final.gds', file=ofile)
+        print('quit -noprompt', file=ofile)
+
+    myenv = os.environ.copy()
+    # Abstract views are appropriate for final composition
+    myenv['MAGTYPE'] = 'maglef'
+
+    mproc = subprocess.run(['magic', '-dnull', '-noconsole',
+		'-rcfile', rcfile, magdir + '/compose_final.tcl'],
+		stdin = subprocess.DEVNULL,
+		stdout = subprocess.PIPE,
+		stderr = subprocess.PIPE,
+		cwd = magdir,
+		env = myenv,
+		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 not keepmode:
+        os.remove(magdir + '/compose_final.tcl')
+
+    exit(0)