#!/usr/bin/env python # Copyright (C) 2007 RADLogic # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; version 2 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # See http://www.fsf.org/licensing/licenses/gpl.txt for full license text. """Generate single concatenated spice with expanded inlude/lib statements.""" __author__ = 'Tim Wegener ' __date__ = '$Date: 2007/03/27 04:20:19 $' __version__ = '$Revision: 0.4 $' import sys import time import optparse def process_file(filename, lib_entry=None, f_out=None, f_err=None, add_comments=True, lib_mode=0, _include_stack=[]): """Process a spice file and expand .include, .inc and .lib statements. Everything other than .include, .inc, .lib and .endlib is passed through unprocessed. Arguments: filename -- Spice input file (Use '-' for stdin) lib_entry -- library entryname (used for .lib calls) f_out -- file object for output (default: stdout) f_err -- file object for messages (default: stderr) add_comments -- If set then add informative comments about inclusions. (default: on) lib_mode -- corresponds to syntax_form in SmartSpice docs: 0 is HSpice style (default) 1 is PSpice style _include_stack -- internal use only (for infinite call cycle checks) """ if filename == '-': f = sys.stdin else: f = open(filename, 'r') if f_out is None: f_out = sys.stdout if f_err is None: f_err = sys.stderr # Keep track of files that are included to prevent infinite cycles. _include_stack.append(filename) line_no = 0 print_it = (lib_entry is None) lib_section = None while 1: l = f.readline() if not l: break line_no += 1 if l.startswith('.'): # . Command ll = l.split() command = ll[0].upper() # insensitive? if command in ['.INC', '.INCLUDE', '.LIB']: if command == '.LIB' and len(ll) == 2 and lib_mode == 0: # Entry in a .lib file. lib_section = ll[1] if lib_section == lib_entry: print_it = True else: print_it = False else: if lib_entry and lib_section != lib_entry: continue include_file = ll[1] if command == '.LIB' and len(ll) == 3 and lib_mode == 0: sub_lib_entry = ll[2] else: sub_lib_entry = None # Deal with quotes. if include_file[0] == include_file[-1] in ["'", '"']: include_file = include_file[1:-1] if include_file not in _include_stack: if add_comments: f_out.write("* Included file: '%s'" % include_file) if sub_lib_entry: f_out.write(" entry: '%s'" % sub_lib_entry) f_out.write("\n") try: process_file(include_file, lib_entry=sub_lib_entry, lib_mode=lib_mode) except IOError, message: f_err.write("Error (%s:%d): %s" % (filename, line_no, message)) sys.exit(1) if add_comments: f_out.write("* End of included file: '%s'" % include_file) if sub_lib_entry: f_out.write(" entry: '%s'" % sub_lib_entry) f_out.write("\n") else: f_err.write("Error (%s:%d): " "Include cycle detected" " when trying to call '%s'\n" % (filename, line_no, include_file)) sys.exit(1) elif command == '.ENDL': lib_section = None # Top-level scope of lib files should not be included # when lib_entry used. print_it = (lib_entry is None) else: # Non-include related . command if print_it: f_out.write(l) else: # Normal spice statement. if print_it: f_out.write(l) f.close() del _include_stack[-1] def main(): """Command-line front-end.""" usage = 'usage: %prog [options] spice_file spice_file2 ... spice_fileN' version = ('spice_cat ' + __version__.split()[1] + '\n' 'Copyright (C) 2004 RADLogic Pty. Ltd.\n' 'All rights reserved.') parser = optparse.OptionParser(usage=usage, version=version) parser.add_option('--alt-lib-mode', action='store_true', help='use alternate .lib syntax (.lib includefile)') options, args = parser.parse_args() if args: filenames = args else: filenames = ['-'] if options.alt_lib_mode: lib_mode = 1 else: lib_mode = 0 f_out = sys.stdout f_out.write('* Processed by spice_cat on %s\n' % time.asctime()) for filename in filenames: process_file(filename, f_out=f_out, lib_mode=lib_mode) f_out.close() if __name__ == '__main__': main()