#!/usr/bin/env python3 import shlex import os import sys import argparse import re help_str = """ Tool for building file lists, into formats required by various tools. ".f" files contain four types of command: file (filename) adds a file to the list include (dir) add a directory to include path, if output format supports this wildcard (.extension) (dir) add all files with a given extension in a given directory list (filename) recurse on another filelist file The idea is that each component in a project has a .f file which lists all of the Verilog (e.g.) files for that component. Higher-level .f files will hierarchically include the lower-level ones. In this way, you can build large flat file lists to pass into the various tools, but never have to *write* large flat file lists. It also makes it easier to specify parts of your design hierarchy for a given tool. For example, if you just want to synthesise your CPU, you can run "listfiles cpu.f -f flat" """ def wildcard(dir, extension): prev_dir = os.getcwd() os.chdir(dir) files = [os.path.abspath(f) for f in os.listdir() if os.path.splitext(f)[-1] == extension] os.chdir(prev_dir) return files def read_filelist(fname): files = [] includes = [] f = open(fname) prev_dir = os.getcwd() os.chdir(os.path.dirname(os.path.abspath(fname))) for l in f.readlines(): l = l.split("#")[0].strip() if l == "": continue words = shlex.split(l) if words[0] == "file": assert(len(words) == 2) files.append(os.path.abspath(os.path.expandvars(words[1]))) elif words[0] == "include": assert(len(words) == 2) includes.append(os.path.abspath(os.path.expandvars(words[1]))) elif words[0] == "wildcard": assert(len(words) == 3) files.extend(wildcard(os.path.expandvars(words[2]), words[1])) elif words[0] == "list": assert(len(words) == 2) newfiles, newincludes = read_filelist(os.path.expandvars(words[1])) files.extend(newfiles) includes.extend(newincludes) else: raise Exception("In filelist {}: Invalid command \"{}\"".format(fname, words[0])) os.chdir(prev_dir) return (files, includes) formats = { "isim": lambda f, i: "verilog work {} {}\n".format(" ".join(f), " ".join("-i " + inc for inc in i)), "flat": lambda f, i: " ".join(f) + "\n", "flati": lambda f, i: " ".join(i) + "\n", "make": lambda f, i: "SRCS={}\nINCDIRS={}\n".format(" ".join(f), " ".join(i)) } if __name__ == "__main__": parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter) parser.epilog = help_str parser.add_argument("src", help="File list source file") parser.add_argument("--format", "-f", help="Format to generate output in. Allowed: isim, make, flat (default)") parser.add_argument("--relative", "-r", action="store_true", help="Use relative paths in output file") parser.add_argument("--relativeto", help="Use relative paths, relative to some specified path") parser.add_argument("--output", "-o", help="Output file name") args = parser.parse_args() if args.format is None: args.format = "flat" files, includes = read_filelist(args.src) # Uniquify whilst preserving order func = lambda l: list(dict.fromkeys(l)) if args.relative: func = lambda l, func=func: [os.path.relpath(f) for f in func(l)] elif args.relativeto: func = lambda l, func=func: [os.path.relpath(f, args.relativeto) for f in func(l)] files, includes = map(func, (files, includes)) if args.format not in formats: sys.exit("Unknown format: " + args.format) ofile = sys.stdout if args.output is None else open(args.output, "w") ofile.write(formats[args.format](files, includes))