mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-24 22:07:43 -04:00
190 lines
7.1 KiB
Python
Executable file
190 lines
7.1 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
# Copyright lowRISC contributors.
|
|
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
|
|
import argparse
|
|
import logging
|
|
import os
|
|
import re
|
|
import shlex
|
|
import shutil
|
|
import subprocess
|
|
import tempfile
|
|
from typing import List, Pattern, Tuple
|
|
|
|
|
|
def read_file_list(path: str) -> List[str]:
|
|
'''Read in a list of paths from a file, one per line.'''
|
|
ret = []
|
|
with open(path) as handle:
|
|
for line in handle:
|
|
ret.append(line.strip())
|
|
return ret
|
|
|
|
|
|
def transform_one(sv2v: str,
|
|
defines: List[str],
|
|
incdirs: List[str],
|
|
pkg_paths: List[str],
|
|
sv_path: str,
|
|
dst_path: str) -> None:
|
|
'''Run sv2v to edit a file in place'''
|
|
defines_args = ['--define=' + d for d in defines]
|
|
incdirs_args = ['--incdir=' + d for d in incdirs]
|
|
paths = pkg_paths + ([] if sv_path in pkg_paths else [sv_path])
|
|
|
|
cmd = ([sv2v,
|
|
# Pass --exclude=assert to tell sv2v not to strip out assertions.
|
|
# Since the whole point of this flow is to prove assertions, we
|
|
# need to leave them unscathed!
|
|
'--exclude=assert'] +
|
|
defines_args +
|
|
incdirs_args +
|
|
paths)
|
|
logging.info('Running sv2v on {}'.format(sv_path))
|
|
logging.debug('Command: {}'.format(cmd))
|
|
with open(dst_path, 'w') as dst_file:
|
|
proc = subprocess.run(cmd, stdout=dst_file)
|
|
if proc.returncode != 0:
|
|
cmd_str = ' '.join([shlex.quote(a) for a in cmd])
|
|
raise RuntimeError('Failed to run sv2v on {}. '
|
|
'Exit code: {}. Full command: {}'
|
|
.format(sv_path, proc.returncode, cmd_str))
|
|
|
|
|
|
def parse_define_if(arg: str) -> Tuple[Pattern[str], str]:
|
|
'''Handle a --define-if argument'''
|
|
parts = arg.rsplit(':', 1)
|
|
if len(parts) != 2:
|
|
msg = ('The --define-if argument {!r} contains no colon. The correct '
|
|
'syntax is "--define-if regex:define".'
|
|
.format(arg))
|
|
raise argparse.ArgumentTypeError(msg)
|
|
|
|
re_str, define = parts
|
|
try:
|
|
return (re.compile(re_str), define)
|
|
except re.error as err:
|
|
raise argparse.ArgumentTypeError('The regex for the --define-if '
|
|
'argument ({!r}) is malformed: {}.'
|
|
.format(re_str, err))
|
|
|
|
|
|
def transform(sv2v: str,
|
|
defines: List[str],
|
|
defines_if: List[Tuple[Pattern[str], str]],
|
|
incdirs: List[str],
|
|
pkg_paths: List[str],
|
|
sv_paths: List[str]) -> None:
|
|
'''Run sv2v to transform a list of files in-place'''
|
|
with tempfile.TemporaryDirectory() as tmpdir:
|
|
# First write each file to a file in a temporary directory, then copy
|
|
# everything back. We have to do it like this because otherwise we
|
|
# might trash a file that needs to be included by a later one.
|
|
dst_paths = []
|
|
for idx, src_path in enumerate(sv_paths):
|
|
dst_path = os.path.join(tmpdir, str(idx))
|
|
|
|
extra_file_defines = []
|
|
for regex, define in defines_if:
|
|
if regex.search(src_path):
|
|
extra_file_defines.append(define)
|
|
|
|
transform_one(sv2v, defines + extra_file_defines,
|
|
incdirs, pkg_paths, src_path, dst_path)
|
|
dst_paths.append(dst_path)
|
|
|
|
# Now copy everything back, overwriting the original code
|
|
for dst_path, src_path in zip(dst_paths, sv_paths):
|
|
shutil.copy(dst_path, src_path)
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument('file_list',
|
|
help=('File containing a list of '
|
|
'paths on which to work.'))
|
|
parser.add_argument('--verbose', '-v', action='store_true',
|
|
help="Log messages about what we're doing.")
|
|
parser.add_argument('--define', '-D', action='append', dest='defines',
|
|
default=[],
|
|
help='Add a preprocessor define.')
|
|
parser.add_argument('--define-if', action='append',
|
|
dest='defines_if', type=parse_define_if, default=[],
|
|
help=('Add a preprocessor define which applies to '
|
|
'specific files. For example '
|
|
'--define-if=foo:bar would define `bar on any '
|
|
'files whose paths contained a match for the '
|
|
'regex "foo".'))
|
|
parser.add_argument('--incdir', '-I', action='append', dest='incdirs',
|
|
default=[],
|
|
help='Add an include dir for the preprocessor.')
|
|
parser.add_argument('--incdir-list',
|
|
help=('Specify a file containing a list of include '
|
|
'directories (which are appended to any defined '
|
|
'through the --incdir argument).'))
|
|
parser.add_argument('--sv2v',
|
|
default='sv2v',
|
|
help=("Specify the name or path of the sv2v binary. "
|
|
"Defaults to 'sv2v'."))
|
|
|
|
args = parser.parse_args()
|
|
|
|
if args.verbose:
|
|
logging.basicConfig(level=logging.INFO)
|
|
|
|
try:
|
|
logging.info('Reading file list from {!r}.'.format(args.file_list))
|
|
paths = read_file_list(args.file_list)
|
|
except IOError:
|
|
logging.error('Failed to read file list from {!r}'
|
|
.format(args.file_list))
|
|
return 1
|
|
|
|
if args.incdir_list is not None:
|
|
try:
|
|
logging.info('Reading incdir list from {!r}.'
|
|
.format(args.incdir_list))
|
|
args.incdirs += read_file_list(args.incdir_list)
|
|
except IOError:
|
|
logging.error('Failed to read incdir list from {!r}'
|
|
.format(args.file_list))
|
|
return 1
|
|
|
|
# Find all .sv or .svh files, splitting out paths ending in "pkg.sv"
|
|
# specially. We treat these as packages, which are included in each sv2v
|
|
# conversion.
|
|
sv_paths = []
|
|
svh_paths = []
|
|
pkg_paths = []
|
|
for path in paths:
|
|
if os.path.splitext(path)[1] == '.sv':
|
|
sv_paths.append(path)
|
|
if os.path.splitext(path)[1] == '.svh':
|
|
svh_paths.append(path)
|
|
if path.endswith('pkg.sv'):
|
|
pkg_paths.append(path)
|
|
|
|
logging.info('Running sv2v in-place on {} files ({} packages).'
|
|
.format(len(sv_paths), len(pkg_paths)))
|
|
|
|
try:
|
|
transform(args.sv2v, args.defines, args.defines_if, args.incdirs,
|
|
pkg_paths, sv_paths)
|
|
except RuntimeError as err:
|
|
logging.error(err)
|
|
return 1
|
|
|
|
# Empty out any remaining .svh files: they should have been included by
|
|
# this point (sv2v includes a preprocessor).
|
|
logging.info('Splatting contents of {} .svh files.'.format(len(svh_paths)))
|
|
for path in svh_paths:
|
|
with open(path, 'w'):
|
|
pass
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
exit(main())
|