104 lines
3.5 KiB
Python
Executable File
104 lines
3.5 KiB
Python
Executable File
#!/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))
|