diff --git a/requirements.txt b/requirements.txt index f16b079..6104395 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ pytest-cov>=2.6.1 pytest-xdist>=1.25.0 coveralls>=1.5.1 antlr4-python3-runtime>=4.9.1 +dataclasses diff --git a/setup.py b/setup.py index bb162e5..5e9528e 100755 --- a/setup.py +++ b/setup.py @@ -3,6 +3,4 @@ setup(name="SigasiProjectCreator", description="Generate a Sigasi Project from your own project specifications", url='https://github.com/sigasi/SigasiProjectCreator', - packages=['SigasiProjectCreator'], - package_dir={'': 'src'}, ) diff --git a/src/SigasiProjectCreator/ArgsAndFileParser.py b/src/SigasiProjectCreator/ArgsAndFileParser.py deleted file mode 100644 index 26b7ec0..0000000 --- a/src/SigasiProjectCreator/ArgsAndFileParser.py +++ /dev/null @@ -1,63 +0,0 @@ -# -*- coding: utf-8 -*- -""" - :copyright: (c) 2008-2023 Sigasi - :license: BSD, see LICENSE for more details. -""" -import os -import argparse -import pathlib - - -class ArgsAndFileParser: - options = None - - def __init__(self, usage): - self.parser = argparse.ArgumentParser(prog='SigasiProjectCreator') - self.parser.add_argument('project_name', help='Project name') - self.parser.add_argument('input_file', help='Input file or comma-separated list of input files') - self.parser.add_argument('destination_folder', help='Root folder of created project', type=pathlib.Path, - nargs='?') - self.parser.add_argument('-l', '--layout', action='store', dest='layout', - choices=['default', 'simulator'], default='default', - help='Project layout: default (in place) or simulator (one folder per library with linked files)') - self.parser.add_argument('--uvm', help='Add UVM to the project, using UVM from the given install path', - dest='uvm', type=pathlib.Path) - self.parser.add_argument('--use-uvm-home', help='Add UVM to the project. Sigasi Studio will use the UVM_HOME environment variable to find your UVM installation', - dest='uvmhome', action='store_true') - self.parser.add_argument('--uvmlib', help='Library in which to compile the UVM package (default `work`)', - dest='uvmlib', default='work') - - def parse_args(self): - args = self.parser.parse_args() - if not os.path.isfile(args.input_file): - self.parser.error('Input file does not exist') - if args.destination_folder is not None: - if not args.destination_folder.is_dir(): - self.parser.error('destination folder has to be a folder') - if args.uvmhome: - if args.uvm is not None: - self.parser.error('Conflicting options --uvm and --use-uvm-home used') - args.uvm = 'ENV-UVM_HOME' - elif args.uvm is not None: - if not os.path.isdir(args.uvm): - self.parser.error(f'UVM home \'{args.uvm}\' must be a folder') - if not os.path.isfile(os.path.join(args.uvm, 'src/uvm_macros.svh')): - self.parser.error(f'Could not find uvm_macros.svh in \'{args.uvm}/src\'') - if not os.path.isfile(os.path.join(args.uvm, 'src/uvm_pkg.sv')): - self.parser.error(f'Could not find uvm_pkg.sv in \'{args.uvm}/src\'') - return args - - def parse_args_and_file(self, parse_file): - args = self.parse_args() - ArgsAndFileParser.options = args - destination = args.destination_folder if args.destination_folder is not None else os.getcwd() - entries = parse_file(args.input_file) - return args.project_name, args.input_file, destination, entries - - @staticmethod - def get_layout_option(): - return ArgsAndFileParser.options.layout - - @staticmethod - def get_uvm_option(): - return ArgsAndFileParser.options.uvm, ArgsAndFileParser.options.uvmlib diff --git a/src/SigasiProjectCreator/ConverterHelper.py b/src/SigasiProjectCreator/ConverterHelper.py deleted file mode 100644 index 41e0258..0000000 --- a/src/SigasiProjectCreator/ConverterHelper.py +++ /dev/null @@ -1,152 +0,0 @@ -# -*- coding: utf-8 -*- -""" - :copyright: (c) 2008-2023 Sigasi - :license: BSD, see LICENSE for more details. -""" -import os -import platform -import subprocess - -from SigasiProjectCreator import absnormpath, posixpath -from SigasiProjectCreator.Creator import SigasiProjectCreator -from SigasiProjectCreator.ArgsAndFileParser import ArgsAndFileParser - - -def get_parts(pth): - parts = [] - while True: - pth, last = os.path.split(pth) - if not last: - break - parts.append(last) - return parts - - -def running_in_cyg_win(): - return platform.system().startswith("CYGWIN") - - -def convert_cygwin_path(cygwin_path): - cygwin_process = subprocess.Popen(['/usr/bin/cygpath', '--windows', cygwin_path], stdout=subprocess.PIPE) - cygwin_location = cygwin_process.communicate()[0].rstrip() - return posixpath(cygwin_location) - - -def parse_and_create_project(usage, parse_file): - parser = ArgsAndFileParser(usage) - (project_name, _, destination, parser_output) = parser.parse_args_and_file(parse_file) - - verilog_includes = None - verilog_defines = None - linked_files = None - if not isinstance(parser_output, dict): - verilog_includes = parser_output.includes - verilog_defines = parser_output.defines - entries = parser_output.library_mapping - if verilog_includes is not None and len(verilog_includes) > 0: - print("Includes: " + str(verilog_includes)) - if verilog_defines is not None and len(verilog_defines) > 0: - print("Defines: " + str(verilog_defines)) - linked_files = parser_output.linked_file_mapping - if linked_files is not None and len(linked_files) > 0: - print("Linked files: " + str(linked_files)) - else: - entries = parser_output - print("Library mapping: " + str(entries)) - - sigasi_project_file_creator = SigasiProjectCreator(project_name) - sigasi_project_file_creator.unmap("/") - - forceVHDL = False - forceVerilog = False - forceVUnit = False - - linked_folders = dict() - abs_destination = absnormpath(destination) - for path, library in entries.items(): - abs_path = absnormpath(path) - relative_path = os.path.relpath(abs_path, abs_destination) - if (not forceVerilog) and (relative_path.endswith('.v') or relative_path.endswith('.sv')): - forceVerilog = True - if (not forceVHDL) and (relative_path.endswith('.vhd') or relative_path.endswith('.vhdl')): - forceVHDL = True - if not relative_path.startswith(".."): - sigasi_project_file_creator.add_mapping(relative_path, library) - else: - common_prefix = os.path.dirname(os.path.commonpath([p + os.path.sep for p in [abs_path, abs_destination]])) - eclipse_path = os.path.relpath(abs_path, common_prefix) - directory_name = get_parts(eclipse_path)[-1] - if str(ArgsAndFileParser.get_layout_option()) == 'default': - target = os.path.join(common_prefix, directory_name) - else: - target = 'virtual:/virtual' - - linked_folders[directory_name] = target - - sigasi_project_file_creator.add_mapping(eclipse_path, library) - print("Linked folders: " + str(linked_folders)) - - # Update verilog includes: if they are in a linked folder, use the link name - if verilog_includes is not None and linked_folders: - new_verilog_includes = [] - for include_folder in verilog_includes: - match_found = False - for linked_folder, dest_folder in linked_folders.items(): - abs_dest_folder = absnormpath(dest_folder) - abs_incl_folder = absnormpath(include_folder) - common_prefix = os.path.commonpath([abs_dest_folder, abs_incl_folder]) - if len(str(common_prefix)) > 0 and os.path.samefile(dest_folder, common_prefix): - prefixlen = len(str(common_prefix)) - include_subpath = abs_incl_folder[prefixlen:] - new_inlcude_path = os.path.join(linked_folder, include_subpath.lstrip('/\\')) - new_verilog_includes.append(new_inlcude_path) - match_found = True - if not match_found: - new_verilog_includes.append(include_folder) - verilog_includes = new_verilog_includes - print("Includes (updated): " + str(verilog_includes)) - - # Adding custom items to libraries. - # sigasi_project_file_creator.add_unisim("C:/xilinx/14.5/ISE_DS/ISE/vhdl/src/unisims") - # sigasi_project_file_creator.add_unimacro("C:/xilinx/14.5/ISE_DS/ISE/vhdl/src/unimacro") - - for folder, location in linked_folders.items(): - if running_in_cyg_win() and not location.startswith('virtual'): - location = convert_cygwin_path(location) - sigasi_project_file_creator.add_link(folder, location, True) - - if linked_files is not None: - for file, location in linked_files.items(): - abs_location = absnormpath(location) - relative_location_path = make_project_location_path(os.path.relpath(abs_location, abs_destination)) - if running_in_cyg_win(): - relative_location_path = convert_cygwin_path(relative_location_path) - - if str(ArgsAndFileParser.get_layout_option()) == 'default': - abs_file = absnormpath(file) - relative_file_path = os.path.relpath(abs_file, abs_destination) - if running_in_cyg_win(): - relative_file_path = convert_cygwin_path(relative_file_path) - else: - relative_file_path = file - - sigasi_project_file_creator.add_link(relative_file_path, relative_location_path) - - # For the time being, we assume that absolute paths are used here (e.g. in a simulator install tree) - # TODO support relative paths (should be part of a more general path handling overhaul?) - uvm_location, uvm_library = ArgsAndFileParser.get_uvm_option() - if uvm_location is not None: - sigasi_project_file_creator.add_uvm(uvm_location, uvm_library) - - sigasi_project_file_creator.write(destination, forceVHDL, forceVerilog, verilog_includes, verilog_defines, - forceVUnit) - - -def make_project_location_path(rel_path): - parent_level = 0 - while rel_path.startswith('..'): - parent_level += 1 - rel_path = rel_path[3::] - if parent_level == 0: - return 'PROJECT_LOC/' + rel_path - return 'PARENT-' + str(parent_level) + '-PROJECT_LOC/' + rel_path \ No newline at end of file diff --git a/src/SigasiProjectCreator/CsvParser.py b/src/SigasiProjectCreator/CsvParser.py index 5b09bbd..408eeba 100644 --- a/src/SigasiProjectCreator/CsvParser.py +++ b/src/SigasiProjectCreator/CsvParser.py @@ -4,21 +4,30 @@ :license: BSD, see LICENSE for more details. """ import csv +import pathlib -usage = """usage: %prog project-name csv-file [destination] +from SigasiProjectCreator.ProjectFileParser import ProjectFileParser, project_file_parser, ProjectFileParserResult -destination is the current directory by default -example: %prog MyProjectName filelist.csv -""" +@project_file_parser('csv') +class CsvParser(ProjectFileParser): + """CSV file""" + def __init__(self): + super().__init__() -def parse_file(csv_file): - entries = dict() - with open(csv_file, 'r') as f: - reader = csv.reader(f, skipinitialspace=True) - for row in reader: - if row: - library = row[0].strip() - path = row[1].strip() - entries[path] = library - return entries + def parse_file(self, csv_file, options=None): + library_mapping = dict() + with open(csv_file, 'r') as f: + reader = csv.reader(f, skipinitialspace=True) + for row in reader: + if row: + library = row[0].strip() + path = pathlib.Path(row[1].strip()).absolute().resolve() + if path in library_mapping: + if isinstance(library_mapping[path], list): + library_mapping[path].append(library) + else: + library_mapping[path] = [library_mapping[path], library] + else: + library_mapping[path] = library + return ProjectFileParserResult(library_mapping, None, None) diff --git a/src/SigasiProjectCreator/DotF/DotFLexer.py b/src/SigasiProjectCreator/DotF/DotFLexer.py index b72990e..9d28115 100644 --- a/src/SigasiProjectCreator/DotF/DotFLexer.py +++ b/src/SigasiProjectCreator/DotF/DotFLexer.py @@ -1,4 +1,4 @@ -# Generated from DotF.g4 by ANTLR 4.13.0 +# Generated from DotF.g4 by ANTLR 4.13.1 from antlr4 import * from io import StringIO import sys @@ -100,7 +100,7 @@ class DotFLexer(Lexer): def __init__(self, input=None, output:TextIO = sys.stdout): super().__init__(input, output) - self.checkVersion("4.13.0") + self.checkVersion("4.13.1") self._interp = LexerATNSimulator(self, self.atn, self.decisionsToDFA, PredictionContextCache()) self._actions = None self._predicates = None diff --git a/src/SigasiProjectCreator/DotF/DotFListener.py b/src/SigasiProjectCreator/DotF/DotFListener.py index 0558d43..16a93d5 100644 --- a/src/SigasiProjectCreator/DotF/DotFListener.py +++ b/src/SigasiProjectCreator/DotF/DotFListener.py @@ -1,4 +1,4 @@ -# Generated from DotF.g4 by ANTLR 4.13.0 +# Generated from DotF.g4 by ANTLR 4.13.1 from antlr4 import * if "." in __name__: from .DotFParser import DotFParser diff --git a/src/SigasiProjectCreator/DotF/DotFParser.py b/src/SigasiProjectCreator/DotF/DotFParser.py index e097397..bac93b3 100644 --- a/src/SigasiProjectCreator/DotF/DotFParser.py +++ b/src/SigasiProjectCreator/DotF/DotFParser.py @@ -1,4 +1,4 @@ -# Generated from DotF.g4 by ANTLR 4.13.0 +# Generated from DotF.g4 by ANTLR 4.13.1 # encoding: utf-8 from antlr4 import * from io import StringIO @@ -74,7 +74,7 @@ class DotFParser ( Parser ): def __init__(self, input:TokenStream, output:TextIO = sys.stdout): super().__init__(input, output) - self.checkVersion("4.13.0") + self.checkVersion("4.13.1") self._interp = ParserATNSimulator(self, self.atn, self.decisionsToDFA, self.sharedContextCache) self._predicates = None diff --git a/src/SigasiProjectCreator/DotF/DotFfileParser.py b/src/SigasiProjectCreator/DotF/DotFfileParser.py index e706950..387869b 100644 --- a/src/SigasiProjectCreator/DotF/DotFfileParser.py +++ b/src/SigasiProjectCreator/DotF/DotFfileParser.py @@ -7,72 +7,84 @@ # 3 : input file not found import os -import sys import glob +import pathlib import re -from SigasiProjectCreator.ArgsAndFileParser import ArgsAndFileParser from .parseFile import parse_dotf -from ..convertDotFtoCsv import rebase_file +from .. import abort_if_false +from ..ProjectFileParser import ProjectFileParser, project_file_parser, ProjectFileParserResult -def abspath(path): +def is_absolute_path(path): + # Check for an absolute pth on Linux or Windows, or a path which starts with an environment variable s_path = str(path) - if s_path.startswith('\\') or s_path.startswith('/') or s_path[1] == ':' or s_path.startswith('$'): - # this is an absolute path in Linux or Windows + return s_path.startswith('\\') or s_path.startswith('/') or s_path[1] == ':' or s_path.startswith('$') + + +def absolute_path(path): + if is_absolute_path(path): + return path + return pathlib.Path(path).absolute() + + +def resolve_path(path: pathlib.Path): + s_path = str(path) + # Don't resolve if it's an absolute Windows path and we're not on Windows + # or if the path starts with a variable + if (os.name != 'nt' and s_path[1] == ':') or s_path.startswith('$'): return path - return os.path.abspath(path) + return path.resolve() -def expandvars_plus(s): - return os.path.expandvars(re.sub(r'\$\((.*)\)', r'${\1}', s)) +def expandvars_plus(s) -> pathlib.Path: + return pathlib.Path(os.path.expandvars(re.sub(r'\$\((.*)\)', r'${\1}', str(s)))) -class DotFfileParser: +class SingleDotFfileParser: - def __init__(self, filename): + def __init__(self, filename, options): self.library_mapping = dict() self.includes = set() self.defines = [] - self.filename = "" self.dotfdir = "" - self.dotfname = "" - self.csvfname = "" - self.filecontent = [] + self.file_content = [] self.linked_file_mapping = dict() - self.filename = filename - if not os.path.isfile(filename): - print("*ERROR* File " + filename + " does not exist") - sys.exit(1) - if os.path.isabs(filename): - filename = os.path.relpath(filename) + abort_if_false(pathlib.Path(filename).is_file(), f'*ERROR* File {filename} does not exist') + input_file = absolute_path(pathlib.Path(expandvars_plus(filename))).resolve() + self.dotfdir = input_file.parent - self.dotfdir = os.path.dirname(filename) - self.dotfname = os.path.basename(filename) - self.csvfname = str(os.path.splitext(self.dotfname)[0]) + ".csv" - - self.filecontent = parse_dotf(filename) + self.file_content = parse_dotf(input_file) parser_expect_library = False parser_expect_dot_f = False - newlib = 'work' - for option in self.filecontent: + + default_work_library = options.work_lib + new_library = default_work_library + for option in self.file_content: if isinstance(option, list): if option[0] == "+incdir": - for fn in option[1:]: - self.includes.add(rebase_file(fn[1:], self.dotfdir)) + for include_folder in option[1:]: + while include_folder.startswith('+'): + include_folder = include_folder[1:] + include_folder_path = pathlib.Path(include_folder) + include_folder_path = expandvars_plus(include_folder_path) + if not is_absolute_path(include_folder_path): + # self.dotfdir is an absolute path + include_folder_path = self.dotfdir.joinpath(include_folder_path) + self.includes.add(resolve_path(include_folder_path)) elif option[0] == "+define": for df in option[1:]: self.defines.append(df[1:].strip()) else: - print('Unknown multiline option (ignored) : ' + option[0]) + print(f'*.f parse* Unknown multiline option (ignored) : {option[0]}') else: bare_option = str(option).strip('"') if bare_option == "-makelib" or bare_option == "-work": parser_expect_library = True elif bare_option == "-endlib": - newlib = 'work' + new_library = default_work_library elif bare_option == "-f": parser_expect_dot_f = True elif bare_option.startswith("+") or bare_option.startswith("-"): @@ -80,90 +92,85 @@ def __init__(self, filename): elif parser_expect_dot_f: parser_expect_dot_f = False # Parse included .f file - subfile = expandvars_plus(bare_option) - if not os.path.isabs(subfile): - subfile = os.path.join(self.dotfdir, subfile) - subparser = DotFfileParser(subfile) + sub_file = expandvars_plus(bare_option) + if not is_absolute_path(sub_file): + sub_file = self.dotfdir.joinpath(sub_file) + subparser = SingleDotFfileParser(sub_file, options) self.library_mapping.update(subparser.library_mapping) self.includes |= subparser.includes self.defines.extend(subparser.defines) elif parser_expect_library: # new library name parser_expect_library = False - newlib = bare_option.split('/')[-1] + new_library = bare_option.split('/')[-1] else: # Design file: add to library mapping - if str(os.path.splitext(bare_option)[1]).lower() in ['.vhd', '.vhdl', '.v', '.sv']: - self.add_to_library_mapping(bare_option, newlib) + design_file = pathlib.Path(bare_option) + if design_file.suffix.lower() in ['.vhd', '.vhdl', '.v', '.sv']: + self.add_to_library_mapping(design_file, new_library) else: print(f'*.f parse* skipping {bare_option}') - def add_to_library_mapping(self, file, library): - # For now, we'll return a dict of abs path => library - # TODO Problem (to be re-solved): files may be mapped to more than one library, in which case this - # approach won't work - # Layout moves to ConverterHelper - - # We need to expand environment variables here, before expanding wildcards - expanded_path = os.path.expandvars(file) - if "*" in expanded_path: - expanded_option = glob.glob(rebase_file(expanded_path, self.dotfdir), recursive=True) - if not expanded_option: - print(f'**warning** wildcard expression {expanded_option} does not match anything') - self.library_mapping[abspath(rebase_file(expanded_path, self.dotfdir))] = library - for f in expanded_option: - self.library_mapping[abspath(f)] = library + def add_to_library_mapping(self, file: pathlib.Path, library): + # Note: we used to handle project layout ("standard in-place" and "simulator" layout) here + # Now we make the library mapping "just" a list of files and libraries, and we'll handle the project + # layout later. + + # File paths in a .f file seem to be relative to the location of the .f file. + # Projects may contain multiple .f files in different locations. + # We make all paths absolute here. At a later stage, relative paths to the project root will be introduced + file = expandvars_plus(file) + if not is_absolute_path(file): + # self.dotfdir is an absolute path + file = self.dotfdir.joinpath(file) + if "*" in str(file): + expanded_file = glob.glob(str(file), recursive=True) + if not expanded_file: + print(f'*.f parse* **warning** wildcard expression {file} does not match anything, skipping') + return + for f in expanded_file: + self.add_file_to_library_mapping(pathlib.Path(f), library) + return + self.add_file_to_library_mapping(file, library) + + def add_file_to_library_mapping(self, file: pathlib.Path, library): + file = resolve_path(file) + if file in self.library_mapping: + if not isinstance(self.library_mapping[file], list): + # Check against duplicates + if library != self.library_mapping[file]: + # Case: file mapped a second time + self.library_mapping[file] = [self.library_mapping[file], library] + else: + # Check against duplicates + if library not in self.library_mapping[file]: + # Case: file mapped a third time (or more) + self.library_mapping[file].append(library) else: - self.library_mapping[abspath(rebase_file(expanded_path, self.dotfdir))] = library - - # self.library_mapping[os.path.abspath(file)] = library - # - # - # - # if str(ArgsAndFileParser.get_layout_option()) == 'default': - # if file in self.library_mapping: - # file_base, file_ext = os.path.splitext(file) - # newfile = file_base + '_' + library + file_ext - # if newfile in self.library_mapping: - # print('File already mapped to library: ' + file + ' => ' + library) - # else: - # self.library_mapping[newfile] = library - # self.linked_file_mapping[newfile] = file - # else: - # self.library_mapping[file] = library - # else: - # # non-default library mapping - # if library not in self.library_mapping: - # self.library_mapping[library] = library - # file_path, file_name = os.path.split(file) - # self.linked_file_mapping[library + '/' + file_name] = file - - -def parse_file(filename): - parser = None - if ',' in filename: - filenames = filename.split(',') - parser = DotFfileParser(filenames[0]) - for fn in filenames[1:]: - subparser = DotFfileParser(fn) - parser.library_mapping.update(subparser.library_mapping) - parser.includes |= subparser.includes - parser.defines.extend(subparser.defines) - else: - parser = DotFfileParser(filename) - - return parser - - -usage = """usage: %prog [--layout=default|simulator] project-name dot-f-file [destination] - -destination is the current directory by default -example: %prog MyProjectName filelist.f -use a relative path to the .f file -multiple .f files can be specified as a comma-separated list - -project layout: default : files are referenced in their current location. - HDL files must reside in the destination folder or a sub-folder thereof. - simulator: project consists of a virtual folder per library, into which HDL files are linked. - Destination folder must be empty for 'simulator' project layout. -""" + # General case: file mapped once + self.library_mapping[file] = library + + +@project_file_parser('dotf') +class DotFfileParser(ProjectFileParser): + """.f file""" + def __init__(self): + super().__init__() + + def parse_file(self, filename, options): + library_mapping = dict() + verilog_includes = set() + verilog_defines = [] + + if isinstance(filename, list): + for this_file in filename: + parsed_result = self.parse_file(this_file, options) + library_mapping.update(parsed_result.library_mapping) + verilog_includes |= parsed_result.verilog_includes + verilog_defines.extend(parsed_result.verilog_defines) + else: + parser = SingleDotFfileParser(filename, options) + library_mapping = parser.library_mapping + verilog_includes = parser.includes + verilog_defines = parser.defines + return ProjectFileParserResult(library_mapping, verilog_includes, verilog_defines) diff --git a/src/SigasiProjectCreator/DotF/parseFile.py b/src/SigasiProjectCreator/DotF/parseFile.py index 35215a2..36dbfcf 100644 --- a/src/SigasiProjectCreator/DotF/parseFile.py +++ b/src/SigasiProjectCreator/DotF/parseFile.py @@ -51,7 +51,7 @@ def enterFilecont(self, ctx: DotFParser.FilecontContext): def parse_dotf(filename): - print('\nParsing file: ' + filename) + print(f'\nParsing file: {filename}') input_stream = FileStream(filename) lexer = DotFLexer(input_stream) stream = CommonTokenStream(lexer) diff --git a/src/SigasiProjectCreator/HdpProjectParser.py b/src/SigasiProjectCreator/HdpProjectParser.py new file mode 100644 index 0000000..1ad4df4 --- /dev/null +++ b/src/SigasiProjectCreator/HdpProjectParser.py @@ -0,0 +1,23 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" + :copyright: (c) 2008-2023 Sigasi + :license: BSD, see LICENSE for more details. +""" +from configparser import ConfigParser + +from SigasiProjectCreator.ProjectFileParser import ProjectFileParser, project_file_parser, ProjectFileParserResult + + +@project_file_parser('hdp') +class HdpParser(ProjectFileParser): + """HDP project""" + def __init__(self): + super().__init__() + + def parse_file(self, hdp_file, options=None): + config = ConfigParser() + config.read(hdp_file) + entries = config.items("hdl") + library_mapping = {lib: path for path, lib in entries} # TODO HUH? isn't that the other way around? + return ProjectFileParserResult(library_mapping, None, None) diff --git a/src/SigasiProjectCreator/ProjectCreator.py b/src/SigasiProjectCreator/ProjectCreator.py new file mode 100644 index 0000000..9591d4c --- /dev/null +++ b/src/SigasiProjectCreator/ProjectCreator.py @@ -0,0 +1,460 @@ +# -*- coding: utf-8 -*- +""" + :copyright: (c) 2008-2023 Sigasi + :license: BSD, see LICENSE for more details. +""" +import os +import pathlib + +from SigasiProjectCreator import abort_if_false +from SigasiProjectCreator.ProjectFileParser import ProjectFileParser, get_parser_for_type, ProjectFileParserResult +from SigasiProjectCreator.SigasiProject import SigasiProject +from pathlib import Path + + +project_creators = {} + + +def project_creator(key): + def register(cls): + project_creators[key] = cls + return cls + + return register + + +# def running_in_cyg_win(): +# return platform.system().startswith("CYGWIN") + + +# def convert_cygwin_path(cygwin_path): +# cygwin_process = subprocess.Popen(['/usr/bin/cygpath', '--windows', cygwin_path], stdout=subprocess.PIPE) +# cygwin_location = cygwin_process.communicate()[0].rstrip() +# return posixpath(cygwin_location) + +class ProjectCreator: + def __init__(self, options): + self.options = options + self.virtual_folders = [pathlib.Path('Common Libraries')] + self.linked_paths_simulator_layout = [] + self.project_root = pathlib.Path(options.destination_folder).absolute() + + self.sigasi_project = SigasiProject(options) + self.sigasi_project.unmap("/") + + def check_and_create_virtual_folder(self, file_name: pathlib.Path): + if not isinstance(file_name, pathlib.Path): + raise TypeError + filepath = file_name.parent + if filepath not in self.virtual_folders: + new_folders = [] + while filepath and (str(filepath) != '.') and (filepath not in self.virtual_folders): + new_folders.insert(0, filepath) + filepath = filepath.parent + for new_path in new_folders: + self.virtual_folders.append(new_path) + self.sigasi_project.add_link(new_path, None, True) + + def check_and_create_linked_folder(self, folder_name: pathlib.Path, folder_path: pathlib.Path): + if not (isinstance(folder_name, pathlib.Path) and isinstance(folder_path, pathlib.Path)): + raise TypeError + # TODO future work: map some folders into Common Libraries + # if folder_name.startswith('dependencies'): + # virtual_path_name = posixpath(os.path.join('Common Libraries', folder_name)) + self.check_and_create_virtual_folder(folder_name) + self.sigasi_project.add_link(folder_name, + get_rel_or_abs_path(folder_path, self.project_root, self.options), True) + + def parse_input_file(self) -> ProjectFileParserResult: + parser = get_parser_for_type(self.options.input_format)() + return parser.parse_file(self.options.input_file, self.options) + + def create_project(self): + parser_output = self.parse_input_file() + + verilog_includes = parser_output.verilog_includes + verilog_defines = parser_output.verilog_defines + entries = parser_output.library_mapping + if verilog_includes is not None and len(verilog_includes) > 0: + verilog_includes = [pathlib.Path(include_path) for include_path in verilog_includes] + if self.options.verbose: + print("Include folders: " + str(verilog_includes)) + if (verilog_defines is not None) and (len(verilog_defines) > 0) and self.options.verbose: + print("Preprocessor definitions: " + str(verilog_defines)) + + new_entries = dict() + for path, library in entries.items(): + new_entries[pathlib.Path(path)] = library + entries = new_entries + if self.options.verbose: + print("Library mapping: " + str(entries)) + + if not self.options.skip_check_exists: + for file in entries.keys(): + abort_if_false(file.is_file(), f'*ERROR* file {file} does not exist') + + (has_vhdl, has_verilog) = self.create_project_layout(entries) + + # Update verilog includes + # Incoming are absolute paths + # If the path is a sub-path of the project folder, use the relative path + # If not: + # * make an `include_folders` folder + # * in it, make a link to the include folder, making sure to not have name clashes + # * use the project path + if verilog_includes is not None: + has_includes_folder = False + linked_include_folders = [] + for include_folder in verilog_includes: + abort_if_false(self.options.skip_check_exists or include_folder.is_dir(), + f'*ERROR* include folder does not exist: {include_folder} ') + local_include_folder = None + if not include_folder.is_relative_to(self.project_root): + if not has_includes_folder: + self.sigasi_project.add_link('include_folders', None, True) + has_includes_folder = True + local_path = Path(include_folder.name) + local_path = get_unique_name(local_path, linked_include_folders) + linked_include_folders.append(local_path) + local_include_folder = Path('include_folders').joinpath(local_path) + self.sigasi_project.add_link(local_include_folder, + get_rel_or_abs_path(include_folder, self.project_root, self.options), + True) + else: + local_include_folder = pathlib.Path(os.path.relpath(include_folder, self.project_root)) + self.sigasi_project.add_verilog_include(local_include_folder) + + if self.options.uvm is not None: + self.sigasi_project.add_uvm(self.options.uvm, self.options.uvm_lib) + + self.sigasi_project.set_languages(has_vhdl, has_verilog) + self.sigasi_project.write(self.project_root, None, verilog_defines, self.options.enable_vunit) + + return self.sigasi_project, verilog_defines + + def create_project_simulator_add_single(self, my_library, libraries, my_file): + if my_library not in libraries: + self.sigasi_project.add_link(my_library, None, True) # virtual folder named after the library + self.sigasi_project.add_mapping(my_library, my_library) + libraries.append(my_library) + linked_path = get_unique_name(pathlib.Path(my_library).joinpath(my_file.name), + self.linked_paths_simulator_layout) + self.linked_paths_simulator_layout.append(linked_path) + self.sigasi_project.add_link(linked_path, get_rel_or_abs_path(my_file, self.project_root, self.options), False) + + def create_library_mapping_folders(self, entries, file_to_project_map): + # design_folders is a list of folders with actual design files in them + design_folders = get_design_folders(entries) + design_root = get_design_root_folder(design_folders) + for design_folder in design_folders: + folder_library = None + folder_list = os.listdir(design_folder) + for folder_item in folder_list: + # In each design folder = folder with design files: + # * Unmap all sub-folders. If a sub-folder contains design files, it will be handled later + # * Map this folder to the library of the first design file. + # * If any design files need to be mapped to a different library, do so on a file by file basis + # * If any design files need to not be mapped to a library, do so on a file by file basis + folder_item_with_path = design_folder.joinpath(folder_item) + if folder_item_with_path.is_dir(): + local_folder = pathlib.Path(os.path.relpath(folder_item_with_path, design_root)) + self.sigasi_project.unmap(local_folder) + elif folder_item_with_path.is_file() and folder_item_with_path.suffix in ['.vhd', '.vhdl', '.v', '.sv']: + file_with_path = design_folder.joinpath(folder_item) + file_with_path_relpath = pathlib.Path(os.path.relpath(file_with_path, design_root)) + if file_with_path in entries: + my_lib = entries[file_with_path] + if isinstance(my_lib, list): + folder_library = self.map_file_to_multiple_libraries(file_with_path, my_lib, design_folder, + folder_library, design_root) + else: + if folder_library is None: + folder_library = self.map_folder_to_library(design_folder, my_lib, design_root) + elif folder_library != my_lib: + self.sigasi_project.add_mapping(file_with_path_relpath, my_lib) + elif self.options.layout != 'linked-files-tree': + self.sigasi_project.unmap(file_with_path_relpath) + + def map_folder_to_library(self, design_folder, library, design_root): + # if parent folder is mapped to this library, clear the mapping of the current folder + parent_lib = self.sigasi_project.get_mapping(design_folder.parent) + design_folder_relpath = pathlib.Path(os.path.relpath(design_folder, design_root)) + if parent_lib is None or parent_lib != library: + self.sigasi_project.add_mapping(design_folder_relpath, library) + else: + self.sigasi_project.remove_mapping(design_folder_relpath) + return library + + def map_file_to_multiple_libraries(self, file_with_path, my_lib, design_folder, folder_library, + design_root): + file_is_mapped = (folder_library is not None) and (folder_library in my_lib) + for single_lib in my_lib: + if folder_library is None: + folder_library = self.map_folder_to_library(design_folder, single_lib, design_root) + file_is_mapped = True + elif single_lib != folder_library: + file_with_path_relpath = pathlib.Path(os.path.relpath(file_with_path, design_root)) + if file_is_mapped: + new_file = file_with_path_relpath.parent.joinpath( + f'{file_with_path_relpath.stem}_{single_lib}' + f'{file_with_path_relpath.suffix}') + self.sigasi_project.add_link(new_file, + get_rel_or_abs_path(file_with_path, + self.project_root, + self.options)) + self.sigasi_project.add_mapping(new_file, single_lib) + else: + self.sigasi_project.add_mapping(file_with_path_relpath, single_lib) + file_is_mapped = True + return folder_library + + def create_library_mapping_per_file(self, mapping_entries, filesystem_to_project_mapping): + for item, library in mapping_entries.items(): + self.add_library_mapping(filesystem_to_project_mapping[item], library, item) + + def add_library_mapping(self, project_file: pathlib.Path, libraries, filesystem_file): + if isinstance(libraries, list): + first_library = True + for library in libraries: + if first_library: + self.sigasi_project.add_mapping(project_file, library) + first_library = False + else: + new_file = project_file.parent.joinpath(f'{project_file.stem}_{library}{project_file.suffix}') + self.sigasi_project.add_link(new_file, + get_rel_or_abs_path(filesystem_file, self.project_root, self.options)) + self.sigasi_project.add_mapping(new_file, library) + else: + self.sigasi_project.add_mapping(project_file, libraries) + + +@project_creator('in-place') +class ProjectCreatorInPlace(ProjectCreator): + """default""" + + def __init__(self, options): + super().__init__(options) + + def create_project_layout(self, entries): + # In place means that we assume that the design files are in the "destination" tree. + # TODO future work: handle files not in the destination tree: link in some way (out of scope of ticket #23) + mapping_style = self.options.mapping + map_folders = (mapping_style == 'folder') + destination_folder = self.options.destination_folder + + has_vhdl = False + has_verilog = False + for my_file, my_library in entries.items(): + if not my_file.is_relative_to(destination_folder): + raise ValueError(f'*In-place layout* file {my_file} is not in destination folder {destination_folder}') + file_ext = my_file.suffix.lower() + if file_ext in ['.vhd', '.vhdl']: + has_vhdl = True + elif file_ext in ['.v', '.sv']: + has_verilog = True + local_file = my_file.relative_to(destination_folder) + if not map_folders: + self.add_library_mapping(local_file, my_library, local_file) + + if map_folders: + self.create_library_mapping_folders(entries, None) + + return has_vhdl, has_verilog + + +@project_creator('simulator') +class ProjectCreatorSimulator(ProjectCreator): + """one folder per library with linked files""" + + def __init__(self, options): + super().__init__(options) + + def create_project_layout(self, entries): + # In this layout, the project contains one virtual folder per HDL library, which in turn contains + # links to each relevant file. Virtual folders are mapped to libraries. + libraries = [] + has_vhdl = False + has_verilog = False + for my_file, my_library in entries.items(): + file_ext = my_file.suffix.lower() + if file_ext in ['.vhd', '.vhdl']: + has_vhdl = True + elif file_ext in ['.v', '.sv']: + has_verilog = True + ref_path = None + if isinstance(my_library, list): + for my_lib in my_library: + self.create_project_simulator_add_single(my_lib, libraries, my_file) + else: + self.create_project_simulator_add_single(my_library, libraries, my_file) + return has_vhdl, has_verilog + + +@project_creator('linked-files-flat') +class ProjectCreatorLinkedFilesFlat(ProjectCreator): + """one folder with links to all files""" + + def __init__(self, options): + super().__init__(options) + + def create_project_layout(self, entries): + has_vhdl = False + has_verilog = False + linked_paths = [] + for path, library in entries.items(): + abort_if_false(not pathlib.Path(path).is_relative_to(self.project_root), + f'*ERROR* linked project: destination folder {self.project_root} may not contain design file {path}') + file_ext = path.suffix.lower() + if file_ext in ['.vhd', '.vhdl']: + has_vhdl = True + elif file_ext in ['.v', '.sv']: + has_verilog = True + if isinstance(library, list): + for my_library in library: + linked_path = get_unique_name(pathlib.Path(path.name), linked_paths) + linked_paths.append(linked_path) + self.sigasi_project.add_link(linked_path, + get_rel_or_abs_path(path, self.project_root, self.options), False) + self.sigasi_project.add_mapping(linked_path, my_library) + else: + linked_path = get_unique_name(pathlib.Path(path.name), linked_paths) + linked_paths.append(linked_path) + self.sigasi_project.add_link(linked_path, + get_rel_or_abs_path(path, self.project_root, self.options), False) + self.sigasi_project.add_mapping(linked_path, library) + return has_vhdl, has_verilog + + +@project_creator('linked-files-tree') +class ProjectCreatorLinkedFilesTree(ProjectCreator): + """virtual folders like the source tree, with links to files""" + + def __init__(self, options): + super().__init__(options) + + def create_project_layout(self, entries): + has_vhdl = False + has_verilog = False + + design_folders = get_design_folders(entries) + design_root = get_design_root_folder(design_folders) + abs_to_rel_file = {} + + for path, library in entries.items(): + abort_if_false(not pathlib.Path(path).is_relative_to(self.project_root), + f'*ERROR* linked project: destination folder {self.project_root} may not contain design file {path}') + file_ext = path.suffix.lower() + if file_ext in ['.vhd', '.vhdl']: + has_vhdl = True + elif file_ext in ['.v', '.sv']: + has_verilog = True + + rel_path = path.relative_to(design_root) + self.check_and_create_virtual_folder(rel_path) + self.sigasi_project.add_link(rel_path, get_rel_or_abs_path(path, self.project_root, self.options), False) + abs_to_rel_file[path] = rel_path + + if self.options.mapping == 'file': + self.create_library_mapping_per_file(entries, abs_to_rel_file) + else: + self.create_library_mapping_folders(entries, abs_to_rel_file) + + return has_vhdl, has_verilog + + +@project_creator('linked-folders') +class ProjectCreatorLinkedFolders(ProjectCreator): + """mix of virtual and linked folders""" + + def __init__(self, options): + # super(ProjectCreatorLinkedFolders, self).__init__(options) + super().__init__(options) + + def create_project_layout(self, entries): + has_vhdl = False + has_verilog = False + + # design_folders is a list of folders with actual design files + design_folders = get_design_folders(entries) + design_root = get_design_root_folder(design_folders) + design_subtrees = get_design_subtrees(design_folders) + for subtree in design_subtrees: + self.check_and_create_linked_folder(pathlib.Path(os.path.relpath(subtree, design_root)), subtree) + + abs_to_rel_file = {} + for path, library in entries.items(): + abort_if_false(not path.is_relative_to(self.project_root), + f'*ERROR* linked project: destination folder {self.project_root} may not contain design file {path}') + file_ext = path.suffix.lower() + if file_ext in ['.vhd', '.vhdl']: + has_vhdl = True + elif file_ext in ['.v', '.sv']: + has_verilog = True + # abs_to_rel_file[path] = pathlib.Path(os.path.relpath(path)) + for subtree in design_subtrees: + if path.is_relative_to(subtree): + abs_to_rel_file[path] = path.relative_to(subtree.parent) + + if self.options.mapping == 'file': + self.create_library_mapping_per_file(entries, abs_to_rel_file) + else: + self.create_library_mapping_folders(entries, abs_to_rel_file) + + return has_vhdl, has_verilog + + +def get_project_creator(options): + abort_if_false(options.layout in project_creators.keys(), f'Invalid layout option: {options.layout}') + return project_creators[options.layout](options) + + +def get_unique_name(path: pathlib.Path, existing_path_list): + if (not existing_path_list) or (path not in existing_path_list): + return path + seq = 1 + new_path = path + path_base = path.parent.joinpath(path.stem) + path_ext = path.suffix + while new_path in existing_path_list and seq < 1000: + new_path = Path(f'{path_base}_{seq}{path_ext}') + seq += 1 + abort_if_false(new_path not in existing_path_list, f'*ERROR* Cannot find a unique name for {path}') + return new_path + + +def get_design_folders(entries) -> list[pathlib.Path]: + folders = [] + for path, library in entries.items(): + folder = path.parent + if folder not in folders: + folders.append(folder) + folders.sort() + return folders + + +def get_design_root_folder(folder_list) -> pathlib.Path: + return pathlib.Path(os.path.commonpath(folder_list)) + + +def get_design_subtrees(folder_list): + # Design subtrees are folders which contain design files, while none of their parent folders contain design files + folder_list.sort() + design_root_folders = [] + current_folder = None + for my_folder in folder_list: + if (current_folder is None) or (not my_folder.is_relative_to(current_folder)): + design_root_folders.append(my_folder) + current_folder = my_folder + return design_root_folders + + +def get_rel_or_abs_path(my_path: pathlib.Path, project_root, options): + # If a relative path is given at this point, it is returned unchanged. + if not my_path.is_absolute(): + return my_path + # If an absolute path is given and a relative path is expected, the relative path is returned. + destination_path = pathlib.Path(project_root).absolute() + if my_path.is_relative_to(destination_path) or options.use_relative_path(my_path): + # return input_path.relative_to(destination_path) + return Path(os.path.relpath(my_path, project_root)) + return my_path.absolute() diff --git a/src/SigasiProjectCreator/ProjectFileParser.py b/src/SigasiProjectCreator/ProjectFileParser.py new file mode 100644 index 0000000..cb2b323 --- /dev/null +++ b/src/SigasiProjectCreator/ProjectFileParser.py @@ -0,0 +1,40 @@ +import pathlib + +from SigasiProjectCreator import abort_if_false +from dataclasses import dataclass + +project_file_parsers = {} + + +@dataclass +class ProjectFileParserResult: + library_mapping: dict[pathlib.Path, str] + verilog_includes: set[str] + verilog_defines: list[str] + + +def project_file_parser(key): + def register(cls): + project_file_parsers[key] = cls + return cls + + return register + + +@project_file_parser('filelist') +class ProjectFileParser: + """file list""" + + def __init__(self): + pass + + def parse_file(self, filename, options): + # Default parser is no parser: the filename(s) are the HDL files + library_mapping = {pathlib.Path(entry).absolute().resolve(): options.worklib + for entry in filename} + return ProjectFileParserResult(library_mapping, None, None) + + +def get_parser_for_type(input_type) -> ProjectFileParser: + abort_if_false(input_type in project_file_parsers.keys(), f'Invalid input type: {input_type}') + return project_file_parsers[input_type] diff --git a/src/SigasiProjectCreator/ProjectOptions.py b/src/SigasiProjectCreator/ProjectOptions.py new file mode 100644 index 0000000..328f30a --- /dev/null +++ b/src/SigasiProjectCreator/ProjectOptions.py @@ -0,0 +1,151 @@ +# -*- coding: utf-8 -*- +""" + :copyright: (c) 2008-2023 Sigasi + :license: BSD, see LICENSE for more details. +""" +import argparse +import pathlib + +from SigasiProjectCreator import VerilogVersion, VhdlVersion, ProjectCreator, ProjectFileParser + + +class ProjectOptions: + def __init__(self, args_list=None): + parser = argparse.ArgumentParser(prog='SigasiProjectCreator') + parser.add_argument('project_name', help='Project name') + parser.add_argument('input_file', help='Input file or comma-separated list of input files', nargs='+') + parser.add_argument('-d', '--destination', action='store', dest='destination_folder', + help='Root folder of created project', type=pathlib.Path, default=pathlib.Path.cwd()) + parser.add_argument('-l', '--layout', choices=ProjectCreator.project_creators.keys(), default='in-place', + help=('Any of the following layouts: ' + ', '.join( + f'{key} ({cls.__doc__})' for key, cls in ProjectCreator.project_creators.items()))) + parser.add_argument('--uvm', help='Add UVM to the project, using UVM from the given install path', + dest='uvm', type=pathlib.Path) + parser.add_argument('--use-uvm-home', help='Add UVM to the project. Sigasi Studio will use the UVM_HOME ' + 'environment variable to find your UVM installation', + dest='uvmhome', action='store_true') + parser.add_argument('--uvmlib', help='Library in which to compile the UVM package (default: the library ' + 'set with `--work`, or `work`)', + dest='uvmlib', default=None) + parser.add_argument('--format', action='store', dest='format', + choices=ProjectFileParser.project_file_parsers.keys(), default=None, + help='Force input format (ignore file extension): ' + ', '.join( + f'{key} ({cls.__doc__})' for key, cls in ProjectFileParser.project_file_parsers.items())) + parser.add_argument('--mapping', action='store', dest='mapping', + choices=['file', 'folder'], default='file', + help='Library mapping style: `folder` = map folders where possible, `file` = map ' + 'individual files (default). Option `folder` requires that files are actually ' + 'available. Only relevant with `default`, `linked-files-tree` and ' + '`linked-folders` project layouts') + parser.add_argument('--enable-vhdl', action='store_true', dest='enable_vhdl', + help='Force VHDL support (regardless of VHDL file presence)') + parser.add_argument('--vhdl-version', action='store', dest='vhdl_version', + choices=VhdlVersion.get_str_enums(), default=str(VhdlVersion.TWENTY_O_EIGHT), + help='Set VHDL version (default VHDL-2008)') + parser.add_argument('--enable-verilog', action='store_true', dest='enable_verilog', + help='Force (System)Verilog support (regardless of (System)Verilog file presence)') + parser.add_argument('--verilog-as-sv', action='store_true', dest='system_verilog', + help='Treat .v files as SystemVerilog') + parser.add_argument('--enable-vunit', action='store_true', dest='enable_vunit', + help='Enable VUnit support') + parser.add_argument('-w', '--work', help='Main HDL library name (default `work`)', + dest='worklib', default='work') + parser.add_argument('--skip-check-exists', action='store_true', dest='skip_check_exists', + help='Skip checking whether files and folders exist') + parser.add_argument('--encoding', action='store', dest='encoding', + default='UTF-8', help='Set unicode character encoding (default: UTF-8)') + parser.add_argument('-f', '--force', action='store_true', dest='force_write', + help='Overwrite existing project files') + parser.add_argument('--rel-path', action='store', dest='rel_path_root', + nargs='*', type=pathlib.Path, + help='Use relative paths for links to files in this folder and its sub-folders') + parser.add_argument('-v', '--verbose', action='store_true', dest='verbose', + help='Verbose output') + + # Run the command line parser + args = parser.parse_args(args_list) + + # Transfer parsed arguments to attributes + self.project_name = args.project_name + self.input_file = args.input_file + self.input_format = args.format + + for infile in self.input_file: + if not pathlib.Path(infile).is_file(): + parser.exit(1, f'Input file \'{infile}\' does not exist or is not a file\n') + if args.format is None: + # Only check the file type if it's not overridden + if self.input_format is None: + self.input_format = get_file_type(infile) + else: + if self.input_format != get_file_type(infile): + parser.error('Mixed input file types are not supported') + if len(self.input_file) == 1: + self.input_file = self.input_file[0] + + if args.destination_folder.exists(): + if not args.destination_folder.is_dir(): + parser.exit(1, f'Cannot create project folder {args.destination_folder}: a file with that name ' + f'exists\n') + else: + if not args.destination_folder.parent.is_dir(): + parser.exit(1, f'Cannot create project folder {args.destination_folder.name} in ' + f'{args.destination_folder.parent}\n') + args.destination_folder.mkdir() + self.destination_folder = args.destination_folder.absolute().resolve() + + self.uvm = None + if args.uvmhome: + if args.uvm is not None: + parser.error('Conflicting options --uvm and --use-uvm-home used') + self.uvm = pathlib.Path('ENV-UVM_HOME') + elif args.uvm is not None: + uvm_path = pathlib.Path(args.uvm) + if not uvm_path.is_dir(): + parser.exit(1, f'UVM home \'{args.uvm}\' must be a folder\n') + if not (uvm_path.joinpath('src/uvm_macros.svh').is_file() + and uvm_path.joinpath('src/uvm_pkg.sv').is_file()): + parser.exit(1, f'UVM folder \'{args.uvm}/src\' must contain uvm_macros.svh and uvm_pkg.sv\n') + self.uvm = uvm_path + + if args.uvmlib is None: + self.uvm_lib = args.worklib + else: + self.uvm_lib = args.uvmlib + + self.rel_path_root = None + if args.rel_path_root: + self.rel_path_root = [pathlib.Path(folder).absolute().resolve() for folder in args.rel_path_root] + + self.layout = args.layout + self.mapping = args.mapping + self.enable_vhdl = args.enable_vhdl + self.enable_verilog = args.enable_verilog + self.enable_vunit = args.enable_vunit + self.work_lib = args.worklib + self.encoding = args.encoding + self.vhdl_version = int(args.vhdl_version) + self.verilog_version = VerilogVersion.TWENTY_TWELVE if args.system_verilog else VerilogVersion.TWENTY_O_FIVE + self.force_overwrite = args.force_write + self.skip_check_exists = args.skip_check_exists + self.verbose = args.verbose + + def use_relative_path(self, my_path): + # TODO figure out path/purepath mess (windows related??) + pure_path = pathlib.PurePath(pathlib.Path(my_path).absolute()) + if self.rel_path_root: + for path_root in self.rel_path_root: + if pure_path.is_relative_to(path_root): + return True + return False + + +def get_file_type(filename): + file_ext = str(pathlib.Path(filename).suffix).lower() + if file_ext.startswith('.'): + file_ext = file_ext[1:] + if file_ext == 'f': + return 'dotf' + if file_ext in ['csv', 'hdp']: + return file_ext + return 'filelist' diff --git a/src/SigasiProjectCreator/SettingsFileWriter.py b/src/SigasiProjectCreator/SettingsFileWriter.py index 0ce0e01..e727552 100644 --- a/src/SigasiProjectCreator/SettingsFileWriter.py +++ b/src/SigasiProjectCreator/SettingsFileWriter.py @@ -1,7 +1,12 @@ -import os +import pathlib +from SigasiProjectCreator import abort_if_false -def write(destination, name, content): - library_mapping_file = os.path.abspath(os.path.join(destination, name)) - with open(library_mapping_file, "wb") as f: + +def write(destination, name, content, force_overwrite): + settings_file = pathlib.Path(destination).joinpath(name) + abort_if_false((force_overwrite or not settings_file.exists()), + f'*ERROR* project file {settings_file} exists, ' \ + 'won\'t overwrite (use `-f` or `--force` to overwrite)') + with open(settings_file, "wb") as f: f.write(content.encode()) diff --git a/src/SigasiProjectCreator/Creator.py b/src/SigasiProjectCreator/SigasiProject.py similarity index 57% rename from src/SigasiProjectCreator/Creator.py rename to src/SigasiProjectCreator/SigasiProject.py index 1693010..6b042b9 100644 --- a/src/SigasiProjectCreator/Creator.py +++ b/src/SigasiProjectCreator/SigasiProject.py @@ -3,12 +3,10 @@ :copyright: (c) 2008-2023 Sigasi :license: BSD, see LICENSE for more details. """ -import os -import re import pathlib from string import Template -from SigasiProjectCreator import absnormpath, posixpath +from SigasiProjectCreator import posixpath, abort_if_false from SigasiProjectCreator import VhdlVersion from SigasiProjectCreator import VerilogVersion from SigasiProjectCreator import SettingsFileWriter @@ -32,6 +30,22 @@ def check_hdl_versions(vhdl_version, verilog_version): raise ValueError("\n".join(filter(None, [vhdl_error, verilog_error]))) +def get_settings_folder(destination: pathlib.Path, suffix=None): + if suffix: + settings_folder = destination.joinpath(suffix) + else: + settings_folder = destination + if settings_folder.exists(): + abort_if_false(settings_folder.is_dir(), f'*ERROR* Settings folder {settings_folder} ' \ + 'exists but is not a folder') + return settings_folder + abort_if_false(settings_folder.parent.is_dir(), + f'*ERROR* Cannot create settings folder {settings_folder}, parent is not' \ + 'an existing folder') + settings_folder.mkdir() + return settings_folder + + class LibraryMappingFileCreator: """A Library Mapping File Creator helps you to easily create a Sigasi Library Mapping file. @@ -57,7 +71,6 @@ class LibraryMappingFileCreator: __MAPPING_TEMPLATE = Template(' \n') __DEFAULT_VERILOG_MAPPINGS = { - "": "not mapped" } __DEFAULT_VHDL_MAPPINGS = { @@ -65,44 +78,57 @@ class LibraryMappingFileCreator: "Common Libraries/IEEE Synopsys": "ieee", "Common Libraries": "not mapped", "Common Libraries/STD": "std", - "": "not mapped" } - def __init__(self, vhdl_version=VhdlVersion.NINETY_THREE, verilog_version=None): + def __init__(self): self.__entries = dict() - self.__add_default_mappings() + self.__vhdl_version = None + self.__verilog_version = None - check_hdl_versions(vhdl_version, verilog_version) + def set_languages(self, vhdl_version, verilog_version): self.__vhdl_version = vhdl_version self.__verilog_version = verilog_version + self.__add_default_mappings() def __add_default_mappings(self): - if VhdlVersion is not None: + # Default value + self.add_mapping("", "not mapped") + if self.__vhdl_version is not None: for path, library in self.__DEFAULT_VHDL_MAPPINGS.items(): self.add_mapping(path, library) - if VerilogVersion is not None: + if self.__verilog_version is not None: for path, library in self.__DEFAULT_VERILOG_MAPPINGS.items(): self.add_mapping(path, library) - if VhdlVersion is None and VerilogVersion is None: - # Default value - self.add_mapping("", "not mapped") def __str__(self): mappings = "" for (path, library) in sorted(self.__entries.items()): mappings += self.__MAPPING_TEMPLATE.substitute( - path=path, - library=library) + path=path, + library=library) return self.__LIBRARIES_TEMPLATE.substitute(mappings=mappings) - def add_mapping(self, path, library): + def add_mapping(self, path, library=None): + if library is None: + self.__entries[path] = 'not mapped' self.__entries[path] = library + def get_mapping(self, path): + result = None + if path in self.__entries: + result = self.__entries[path] + if result == 'not mapped': + result = None + return result + def unmap(self, path): self.__entries[path] = "not mapped" - def write(self, destination): - SettingsFileWriter.write(destination, ".library_mapping.xml", str(self)) + def remove_mapping(self, path): + del self.__entries[path] + + def write(self, destination, force_overwrite): + SettingsFileWriter.write(destination, ".library_mapping.xml", str(self), force_overwrite) class ProjectFileCreator: @@ -121,7 +147,7 @@ class ProjectFileCreator: """ __LINK_TEMPLATE = Template( -'''\t\t + '''\t\t \t\t\t${name} \t\t\t${link_type} \t\t\t<${loc_type}>${location} @@ -140,7 +166,7 @@ class ProjectFileCreator: \t\t\n''' __PROJECT_FILE_TEMPLATE = Template( -''' + ''' \t${project_name} \t @@ -168,34 +194,19 @@ class ProjectFileCreator: ["Common Libraries/STD", Template("sigasiresource:/vhdl/${version}/STD")], ] - force_vhdl = None - force_verilog = None - force_vunit = None - - def __init__(self, project_name, vhdl_version=VhdlVersion.NINETY_THREE, verilog_version=None): - check_hdl_versions(vhdl_version, verilog_version) + def __init__(self, project_name): self.__project_name = project_name - self.__version = vhdl_version self.__links = [] self.__project_references = [] - self.__add_default_links() - - def is_verilog(self): - # Workaround for VHDL/Verilog/mixed detection, see below - if self.force_verilog is not None: - return self.force_verilog - # TODO you can't check for a Verilog nature like this, files in the library mapping are ignored - vl_ext = re.compile(r"\.sv[hi]?$|\.v[h]?$", re.IGNORECASE) - return any([vl_ext.search(l[1]) for l in self.__links]) - - def is_vhdl(self): - # Workaround for VHDL/Verilog/mixed detection, see below - if self.force_vhdl is not None: - return self.force_vhdl - vhdl_ext = re.compile(r"\.vhd[l]?$", re.IGNORECASE) - # VHDL is the default - # TODO you can't check for a Mixed VHDL/Verilog nature like this, files in the library mapping are ignored - return not self.is_verilog() or any([vhdl_ext.search(l[1]) for l in self.__links]) + self.vhdl_version = None + self.verilog_version = None + self.force_vunit = None + + def set_languages(self, vhdl_version, verilog_version): + self.vhdl_version = vhdl_version + self.verilog_version = verilog_version + if vhdl_version is not None: + self.__add_default_links() def is_vunit(self): if self.force_vunit is not None: @@ -204,26 +215,25 @@ def is_vunit(self): def __add_default_links(self): for name, template in self.__DEFAULT_LINKS: - self.__links.append([name, template.substitute(version=self.__version), True, False]) + self.__links.append([name, template.substitute(version=self.vhdl_version), True, False]) def __str__(self): links = "" project_references = "" buildspecs = "" natures = "" - # TODO git rid of VHDL common libraries for non-VHDL projects for [name, location, folder, is_path] in self.__links: location_type = "location" if (is_path and not str(location).startswith('virtual')) else "locationURI" links += self.__LINK_TEMPLATE.substitute( - name=name, - link_type=2 if folder else 1, - loc_type=location_type, - location=location) + name=name, + link_type=2 if folder else 1, + loc_type=location_type, + location=location) - if self.is_verilog(): + if self.verilog_version is not None: natures += self.__VERILOG_NATURE - if self.is_vhdl(): + if self.vhdl_version is not None: natures += self.__VHDL_NATURE if self.is_vunit(): @@ -243,18 +253,19 @@ def __str__(self): ) def add_link(self, name, location, folder=False): - if name.startswith(".."): - raise ValueError('invalid name "' + name + '", a name can not start with dots') - self.__links.append([name, location, folder, True]) + if str(location).startswith('virtual'): + self.__links.append([name, location, folder, False]) + else: + # if name.startswith(".."): + # raise ValueError('invalid name "' + name + '", a name can not start with dots') + self.__links.append([name, project_location_path(location), folder, True]) def add_project_reference(self, name): self.__project_references.append(name) - def write(self, destination, force_vhdl=None, force_verilog=None, force_vunit=None): - self.force_vhdl = force_vhdl - self.force_verilog = force_verilog - self.force_vunit = force_vunit - SettingsFileWriter.write(destination, ".project", str(self)) + def write(self, destination, force_vunit, force_overwrite): + self.force_vunit = force_vunit + SettingsFileWriter.write(destination, ".project", str(self), force_overwrite) class ProjectVersionCreator: @@ -273,21 +284,27 @@ def __init__(self, version=VhdlVersion.NINETY_THREE): self.version = version self.lang = "vhdl" if self.version in VhdlVersion.get_enums() else "verilog" - def write(self, destination): - self.write_version(destination) + def write(self, destination, force_overwrite): + self.write_version(destination, force_overwrite) + + def get_vhdl_version(self): + if self.lang == 'vhdl': + return self.version + return None + + def get_verilog_version(self): + if self.lang == 'verilog': + return self.version + return None def __str__(self): return "={0}".format(self.version) - def write_version(self, destination): - settings_dir = os.path.join(destination, ".settings") - # Create .settings dir if it doesn't yet exist - if not os.path.exists(settings_dir): - os.makedirs(settings_dir) + def write_version(self, destination, force_overwrite): + settings_dir = get_settings_folder(destination, '.settings') version_file_path = "com.sigasi.hdt.{0}.version.prefs".format(self.lang) - version_file = os.path.join(settings_dir, version_file_path) - if self.version is not None and not os.path.exists(version_file): - SettingsFileWriter.write(settings_dir, version_file_path, str(self)) + if self.version is not None: + SettingsFileWriter.write(settings_dir, version_file_path, str(self), force_overwrite) class ProjectPreferencesCreator: @@ -297,75 +314,74 @@ class ProjectPreferencesCreator: Limitation: only Verilog supported atm """ + def __init__(self, language, verilog_includes, verilog_defines): self.verilog_includes = verilog_includes - self.verilog_defines = verilog_defines + self.verilog_defines = verilog_defines self.lang = language - def write(self, destination): - settings_dir = os.path.join(destination, ".settings") - # Create .settings dir if it doesn't yet exist - if not os.path.exists(settings_dir): - os.makedirs(settings_dir) + def write(self, destination, force_overwrite): + settings_dir = get_settings_folder(destination, '.settings') prefs_file_path = "com.sigasi.hdt.{0}.{1}.prefs".format(self.lang, str(self.lang).title()) - prefs_file = os.path.join(settings_dir, prefs_file_path) - # TODO why not overwrite? - if not os.path.exists(prefs_file): - rel_verilog_includes = [] - abs_destination = absnormpath(destination) - # TODO improve PATH handling - for path in self.verilog_includes: - if str(path).startswith('Common Libraries'): - rel_verilog_includes.append(path) - else: - # TODO why is this line here? # abs_path = absnormpath(path) - relative_path = os.path.relpath(path, abs_destination) - rel_verilog_includes.append(posixpath(relative_path)) - self.verilog_includes = rel_verilog_includes - SettingsFileWriter.write(settings_dir, prefs_file_path, str(self)) + SettingsFileWriter.write(settings_dir, prefs_file_path, str(self), force_overwrite) def __str__(self): - incstr = "" - defstr = "" + includes_string = "" + defines_string = "" if self.lang == 'verilog' and self.verilog_includes is not None and len(self.verilog_includes) > 0: if isinstance(self.verilog_includes, list) and list: - incstr = "includePath=" + includes_string = "includePath=" first = True - for incfile in self.verilog_includes: + for include_folder in self.verilog_includes: if not first: - incstr += ";" + includes_string += ";" first = False - incstr += incfile - incstr += "\n" + includes_string += posixpath(include_folder) + includes_string += "\n" if self.lang == 'verilog' and self.verilog_defines is not None and len(self.verilog_defines) > 0: - defstr = "propertiesDefine=" + defines_string = "propertiesDefine=" for definition in self.verilog_defines: - defstr += "`define " + definition.replace('=',' ') + "\\r\\n" - defstr += "\n" - return "eclipse.preferences.version=1\n" + incstr + defstr + defines_string += "`define " + definition.replace('=', ' ') + "\\r\\n" + defines_string += "\n" + return "eclipse.preferences.version=1\n" + includes_string + defines_string + + +class ProjectEncodingCreator: + """ + Create /.settings/com.sigasi.hdt.vhdl.version.prefs + with file encoding settings (default UTF-8) + """ + + def __init__(self, encoding='UTF-8'): + self.encoding = encoding + + def write(self, destination, force_overwrite): + settings_dir = get_settings_folder(destination, '.settings') + prefs_file_path = "org.eclipse.core.resources.prefs" + SettingsFileWriter.write(settings_dir, prefs_file_path, str(self), force_overwrite) + + def __str__(self): + return f'eclipse.preferences.version=1\nencoding/={self.encoding}\n' + class VUnitPreferencesCreator: """ Help to write a .settings file for the VUnit script (run.py) location """ - def __init__(self, VUnitScript="run.py"): - self.script = VUnitScript - - def write(self, destination): - settings_dir = os.path.join(destination, ".settings") - # Create .settings dir if it doesn't yet exist - if not os.path.exists(settings_dir): - os.makedirs(settings_dir) + + def __init__(self, vunit_script="run.py"): + self.script = vunit_script + + def write(self, destination, force_overwrite): + settings_dir = get_settings_folder(destination, '.settings') prefs_file_path = "com.sigasi.hdt.toolchains.vunit.prefs" - prefs_file = os.path.join(settings_dir, prefs_file_path) - if not os.path.exists(prefs_file): - SettingsFileWriter.write(settings_dir, prefs_file_path, str(self)) - + SettingsFileWriter.write(settings_dir, prefs_file_path, str(self), force_overwrite) + def __str__(self): - scriptsstr = "VUnitScriptLocation=" + self.script - return scriptsstr + "\n" + "eclipse.preferences.version=1\n" + script_string = "VUnitScriptLocation=" + self.script + return script_string + "\n" + "eclipse.preferences.version=1\n" -class SigasiProjectCreator: +class SigasiProject: """This class helps you to easily create a Sigasi project (".project") and library mapping (".library_mapping.xml") file. It will also create a .settings folder if it doesn't yet exist, see ProjectVersionCreator. @@ -374,49 +390,74 @@ class SigasiProjectCreator: creator = SigasiProjectCreator(project_name, VhdlVersion.NINETY_THREE) creator.add_link("test.vhd", "/home/heeckhau/shared/test.vhd") creator.add_mapping("test.vhd", "myLib") + creator.set_languages(True, False) # for a VHDL only project creator.write("/home/heeckhau/test/") """ - # TODO Verilog, mixed language or other VHDL versions are never detected at this point - # Change such that VHDL or Verilog presence may be set from library mapping and options. - def __init__(self, project_name, vhdl_version=VhdlVersion.NINETY_THREE, verilog_version=None): - check_hdl_versions(vhdl_version, verilog_version) - self.__libraryMappingFileCreator = LibraryMappingFileCreator(vhdl_version, verilog_version) - self.__projectFileCreator = ProjectFileCreator(project_name, vhdl_version, verilog_version) - self.__projectVersionCreators = [] - if vhdl_version is not None: - self.__projectVersionCreators.append(ProjectVersionCreator(vhdl_version)) - if verilog_version is not None: - self.__projectVersionCreators.append(ProjectVersionCreator(verilog_version)) - else: - # Version file shouldn't hurt anyone - self.__projectVersionCreators.append(ProjectVersionCreator(VerilogVersion.TWENTY_O_FIVE)) + def __init__(self, options): + self.options = options + self.__libraryMappingFileCreator = LibraryMappingFileCreator() + self.__projectFileCreator = ProjectFileCreator(options.project_name) self.verilog_includes = [] + self.vhdl_version = None + self.verilog_version = None + self.languages_initialized = False + + def set_languages(self, has_vhdl, has_verilog): + has_vhdl = has_vhdl or self.options.enable_vhdl + has_verilog = has_verilog or self.options.enable_verilog + if has_vhdl: + self.vhdl_version = self.options.vhdl_version + if has_verilog: + self.verilog_version = self.options.verilog_version + check_hdl_versions(self.vhdl_version, self.verilog_version) + self.__projectFileCreator.set_languages(self.vhdl_version, self.verilog_version) + self.__libraryMappingFileCreator.set_languages(self.vhdl_version, self.verilog_version) + self.languages_initialized = True def add_link(self, name, location, folder=False): - self.__projectFileCreator.add_link(name, posixpath(location), folder) + if folder and (location is None): + # virtual folder + self.__projectFileCreator.add_link(name, 'virtual:/virtual', folder) + else: + self.__projectFileCreator.add_link(name, posixpath(location), folder) def add_mapping(self, path, library): self.__libraryMappingFileCreator.add_mapping(posixpath(path), library) + def remove_mapping(self, path): + self.__libraryMappingFileCreator.remove_mapping(posixpath(path)) + def unmap(self, path): self.__libraryMappingFileCreator.unmap(posixpath(path)) + def get_mapping(self, path): + return self.__libraryMappingFileCreator.get_mapping(path) + def add_verilog_include(self, path): self.verilog_includes.append(path) - def write(self, destination, force_vhdl=None, force_verilog=None, verilog_includes=None, verilog_defines=None, force_vunit=None): - self.__projectFileCreator.write(destination, force_vhdl, force_verilog, force_vunit) - self.__libraryMappingFileCreator.write(destination) - for projectVersionCreator in self.__projectVersionCreators: - projectVersionCreator.write(destination) + def write(self, destination, verilog_includes=None, verilog_defines=None, force_vunit=None): + abort_if_false(self.languages_initialized, "HDL languages must be set before writing the project") + + if not isinstance(destination, pathlib.Path): + destination = pathlib.Path(destination) + + self.__projectFileCreator.write(destination, force_vunit, self.options.force_overwrite) + self.__libraryMappingFileCreator.write(destination, self.options.force_overwrite) + if self.vhdl_version is not None: + ProjectVersionCreator(self.options.vhdl_version).write(destination, self.options.force_overwrite) + if self.verilog_version is not None: + ProjectVersionCreator(self.options.verilog_version).write(destination, self.options.force_overwrite) self.verilog_includes.extend(verilog_includes or []) if self.verilog_includes or verilog_defines: verilog_prefs = ProjectPreferencesCreator('verilog', self.verilog_includes, verilog_defines) - verilog_prefs.write(destination) + verilog_prefs.write(destination, self.options.force_overwrite) if force_vunit: vunit_prefs = VUnitPreferencesCreator() - vunit_prefs.write(destination) + vunit_prefs.write(destination, self.options.force_overwrite) + encoding_prefs = ProjectEncodingCreator(self.options.encoding) + encoding_prefs.write(destination, self.options.force_overwrite) def add_unisim(self, unisim_location): self.add_link("Common Libraries/unisim", unisim_location, True) @@ -434,9 +475,22 @@ def add_unimacro(self, unimacro_location): def add_project_reference(self, name): self.__projectFileCreator.add_project_reference(name) - def add_uvm(self, uvm_location, uvm_library): + def add_uvm(self, uvm_location: pathlib.Path, uvm_library): if uvm_location is not None: - # TODO improve path handling - self.add_link('Common Libraries/uvm', os.path.join(uvm_location, 'src'), True) - self.add_mapping('Common Libraries/uvm/uvm_pkg.sv', uvm_library) - self.add_verilog_include('Common Libraries/uvm') + self.add_link(pathlib.Path('Common Libraries/uvm'), uvm_location.joinpath('src'), True) + self.add_mapping(pathlib.Path('Common Libraries/uvm/uvm_pkg.sv'), uvm_library) + self.add_verilog_include(pathlib.Path('Common Libraries/uvm')) + + +def project_location_path(my_path): + assert (not (str(my_path).startswith('PROJECT') or str(my_path).startswith('PARENT-'))), \ + f'*OOPS* not expecting {my_path} here' + if pathlib.Path(my_path).is_absolute(): + return my_path + parent_level = 0 + while my_path.startswith('..'): + parent_level += 1 + my_path = my_path[3::] + if parent_level == 0: + return my_path + return 'PARENT-' + str(parent_level) + '-PROJECT_LOC/' + my_path diff --git a/src/SigasiProjectCreator/VerilogVersion.py b/src/SigasiProjectCreator/VerilogVersion.py index 1dd9add..f2b8e1b 100644 --- a/src/SigasiProjectCreator/VerilogVersion.py +++ b/src/SigasiProjectCreator/VerilogVersion.py @@ -1,5 +1,6 @@ TWENTY_O_FIVE = "v2005" +TWENTY_TWELVE = "sv2012" def get_enums(): - return [TWENTY_O_FIVE] + return [TWENTY_O_FIVE, TWENTY_TWELVE] diff --git a/src/SigasiProjectCreator/VhdlVersion.py b/src/SigasiProjectCreator/VhdlVersion.py index 02e2689..0c2b061 100644 --- a/src/SigasiProjectCreator/VhdlVersion.py +++ b/src/SigasiProjectCreator/VhdlVersion.py @@ -3,7 +3,12 @@ NINETY_THREE = 93 TWENTY_O_TWO = 2002 TWENTY_O_EIGHT = 2008 +TWENTY_NINETEEN = 2019 def get_enums(): - return [NINETY_THREE, TWENTY_O_TWO, TWENTY_O_EIGHT] + return [NINETY_THREE, TWENTY_O_TWO, TWENTY_O_EIGHT, TWENTY_NINETEEN] + + +def get_str_enums(): + return [str(version) for version in get_enums()] diff --git a/src/SigasiProjectCreator/XilinxProjectParser.py b/src/SigasiProjectCreator/XilinxProjectParser.py new file mode 100644 index 0000000..f1e9886 --- /dev/null +++ b/src/SigasiProjectCreator/XilinxProjectParser.py @@ -0,0 +1,33 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" + :copyright: (c) 2008-2023 Sigasi + :license: BSD, see LICENSE for more details. +""" +import xml.etree.ElementTree as eT +import os + +from SigasiProjectCreator.ProjectFileParser import ProjectFileParser, project_file_parser, ProjectFileParserResult + + +@project_file_parser('xise') +class XilinxProjectParser(ProjectFileParser): + """Xilinx ISE project""" + def __init__(self): + super().__init__() + + def parse_file(self, xilinx_file, options=None): + tree = eT.parse(xilinx_file) + root = tree.getroot() + schema = '{http://www.xilinx.com/XMLSchema}' + library_mapping = dict() + + for f in root.findall('*/' + schema + 'file'): + if schema + 'type' in f.attrib: + schema_type = f.attrib[schema + 'type'] + if schema_type == 'FILE_VHDL': # TODO VHDL only? + name = os.path.realpath(os.path.abspath(f.attrib[schema + 'name'])) + lib = f.find(schema + 'library') + library = lib.attrib[schema + 'name'] if (lib is not None) else "work" + library_mapping[name] = library + return ProjectFileParserResult(library_mapping, None, None) diff --git a/src/SigasiProjectCreator/__init__.py b/src/SigasiProjectCreator/__init__.py index 8e13419..a70ae1b 100644 --- a/src/SigasiProjectCreator/__init__.py +++ b/src/SigasiProjectCreator/__init__.py @@ -1,6 +1,8 @@ +import sys from os.path import abspath, normcase, splitdrive, join from pathlib import PurePath + def absnormpath(p): """ Get a normalized absolute version of given path. @@ -10,6 +12,14 @@ def absnormpath(p): drive, tail = splitdrive(abspath(p)) return join(normcase(drive), tail) + def posixpath(p): - "Convert a path to POSIX style, also on Windows." + """Convert a path to POSIX style, also on Windows.""" return PurePath(p).as_posix() + + +def abort_if_false(condition, message, code=5): + """User-facing equivalent of assert""" + if not condition: + print(message) + sys.exit(code) diff --git a/src/SigasiProjectCreator/convertCsvFileToLinks.py b/src/SigasiProjectCreator/convertCsvFileToLinks.py deleted file mode 100755 index 88ab587..0000000 --- a/src/SigasiProjectCreator/convertCsvFileToLinks.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -""" - :copyright: (c) 2008-2023 Sigasi - :license: BSD, see LICENSE for more details. -""" -import os - -from SigasiProjectCreator.Creator import SigasiProjectCreator -from SigasiProjectCreator.ArgsAndFileParser import ArgsAndFileParser -from SigasiProjectCreator import CsvParser -from SigasiProjectCreator import VhdlVersion - - -def get_file_name(entry): - (folder, filename) = os.path.split(os.path.abspath(entry)) - return filename - - -def main(): - parser = ArgsAndFileParser(CsvParser.usage) - (project_name, _, destination, entries) = parser.parse_args_and_file(CsvParser.parse_file) - - creator = SigasiProjectCreator(project_name, VhdlVersion.NINETY_THREE) - - for path, library in entries.items(): - file_name = get_file_name(path) - link_type = os.path.isdir(path) - creator.add_link(file_name, os.path.abspath(path), link_type) - creator.add_mapping(file_name, library) - - creator.write(destination, force_vunit=True) - - -if __name__ == '__main__': - main() diff --git a/src/SigasiProjectCreator/convertCsvFileToTree.py b/src/SigasiProjectCreator/convertCsvFileToTree.py deleted file mode 100755 index 02ee4b6..0000000 --- a/src/SigasiProjectCreator/convertCsvFileToTree.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -""" - :copyright: (c) 2008-2023 Sigasi - :license: BSD, see LICENSE for more details. -""" -from SigasiProjectCreator import CsvParser -from SigasiProjectCreator import ConverterHelper - - -def main(): - ConverterHelper.parse_and_create_project(CsvParser.usage, CsvParser.parse_file) - - -if __name__ == '__main__': - main() diff --git a/src/SigasiProjectCreator/convertDotFtoCsv.py b/src/SigasiProjectCreator/convertDotFtoCsv.py deleted file mode 100644 index 741d4df..0000000 --- a/src/SigasiProjectCreator/convertDotFtoCsv.py +++ /dev/null @@ -1,76 +0,0 @@ -""" - :copyright: (c) 2008-2023 Sigasi - :license: BSD, see LICENSE for more details. -""" - -# Exit codes: -# 1 : file not found -# 2 : relative path expected, but absolute path given - -from SigasiProjectCreator.DotF.parseFile import parse_dotf -import sys -import os -import csv - -def isabspath(path): - s_path = str(path) - if s_path.startswith('\\') or s_path.startswith('/') or s_path[1] == ':' or s_path.startswith('$'): - return True - return os.path.isabs(path) - - -def rebase_file(filename, dotfdir): - hdlpath = os.path.expandvars(filename) - if not isabspath(hdlpath): - hdlpath = os.path.join(dotfdir, hdlpath) - hdlpath = os.path.normpath(hdlpath) - return hdlpath - - -def convertDotFtoCsv(filename): - if not os.path.isfile(filename): - print("*ERROR* File " + filename + " does not exist") - sys.exit(1) - if os.path.isabs(filename): - print("*ERROR* must use a relative path, but " + filename + " is absolute") - sys.exit(2) - - dotfdir = os.path.dirname(filename) - dotfname = os.path.basename(filename) - csvfname = str(os.path.splitext(dotfname)[0]) + ".csv" - - include_path = [] - filecontent = parse_dotf(filename) - - print("Writing CSV: " + csvfname) - - with open(csvfname, "w", newline='') as csvfile: - csvwriter = csv.writer(csvfile) - for option in filecontent: - if isinstance(option, list): - if option[0].startswith("-makelib"): - newlib = option[0].split(' ')[1].split('/')[-1] - print('Library ' + newlib) - for fn in option: - if not (fn.startswith("+") or fn.startswith("-")): - csvwriter.writerow([newlib, rebase_file(str(fn).strip('"'), dotfdir)]) - else: - print('Unexpected multiline option: ' + option[0]) - else: - bare_option = str(option).strip('"') - if bare_option.startswith("+incdir"): - print("*include path* " + rebase_file(bare_option[8:], dotfdir)) - include_path.append(bare_option[8:]) - elif bare_option.startswith("+") or bare_option.startswith("-"): - print("*unknown option* " + bare_option) - else: - # print("*file* " + bare_option + " => " + rebase_file(bare_option, dotfdir)) - csvwriter.writerow(['work', rebase_file(bare_option, dotfdir)]) - -if __name__ == '__main__': - print(sys.argv) - if len(sys.argv) > 1: - convertDotFtoCsv(sys.argv[1]) - else: - print('Usage: convertDotFtoCsv.py ') - exit(1) diff --git a/src/SigasiProjectCreator/convertHdpProjectToSigasiProject.py b/src/SigasiProjectCreator/convertHdpProjectToSigasiProject.py deleted file mode 100644 index fd5ff9c..0000000 --- a/src/SigasiProjectCreator/convertHdpProjectToSigasiProject.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -""" - :copyright: (c) 2008-2023 Sigasi - :license: BSD, see LICENSE for more details. -""" -from configparser import ConfigParser - -from SigasiProjectCreator import ConverterHelper - -usage = """usage: %prog project-name hdp-file [destination] - -destination is the current directory by default -example: %prog MyProjectName myproject.hdp -""" - - -def parse_hdp_file(hdp_file): - config = ConfigParser() - config.read(hdp_file) - entries = config.items("hdl") - return {lib: path for path, lib in entries} - - -def main(): - ConverterHelper.parse_and_create_project(usage, parse_hdp_file) - - -if __name__ == '__main__': - main() diff --git a/src/SigasiProjectCreator/convertXilinxISEToSigasiProject.py b/src/SigasiProjectCreator/convertXilinxISEToSigasiProject.py deleted file mode 100644 index 2245780..0000000 --- a/src/SigasiProjectCreator/convertXilinxISEToSigasiProject.py +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -""" - :copyright: (c) 2008-2023 Sigasi - :license: BSD, see LICENSE for more details. -""" -import xml.etree.ElementTree as eT - -from SigasiProjectCreator import ConverterHelper - -usage = """usage: %prog project-name Xilinx-file [destination] - -destination is the current directory by default -example: %prog MyProjectName project.xise -""" - - -def parse_xilinx_file(xilinx_file): - entries = dict() - tree = eT.parse(xilinx_file) - root = tree.getroot() - schema = '{http://www.xilinx.com/XMLSchema}' - - for f in root.findall('*/' + schema + 'file'): - if schema + 'type' in f.attrib: - schema_type = f.attrib[schema + 'type'] - if schema_type == 'FILE_VHDL': - name = f.attrib[schema + 'name'] - lib = f.find(schema + 'library') - library = lib.attrib[schema + 'name'] if (lib is not None) else "work" - entries[name] = library - - return entries - - -def main(): - ConverterHelper.parse_and_create_project(usage, parse_xilinx_file) - - -if __name__ == '__main__': - main() diff --git a/src/SigasiProjectCreator/createSigasiProjectFromListOfFiles.py b/src/SigasiProjectCreator/createSigasiProjectFromListOfFiles.py deleted file mode 100755 index 259291b..0000000 --- a/src/SigasiProjectCreator/createSigasiProjectFromListOfFiles.py +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- -""" - :copyright: (c) 2008-2023 Sigasi - :license: BSD, see LICENSE for more details. -""" -import os - -from SigasiProjectCreator.ArgsAndFileParser import ArgsAndFileParser -from SigasiProjectCreator.Creator import SigasiProjectCreator -from SigasiProjectCreator import VhdlVersion -import argparse - -usage = """usage: %prog project-name hdl-file hdl-file... - - this script creates a Sigasi project in the current working directory: - * adds one linked folder to the project that points to the common - folder of all listed hdl-files - * unmaps all hdl-files in the common folder, except the listed files. - These files are mapped to the 'work' library -example: %prog MyProjectName foo.vhdl bar.sv -""" - - -def main(): - parser = argparse.ArgumentParser(prog='SigasiProjectCreator') - parser.add_argument('project_name', help='project name') - parser.add_argument('input_files', help='input files', nargs='+') - args = parser.parse_args() - project_name = args.project_name - hdl_files = args.input_files - destination = os.getcwd() - - # Find common directory of the hdl files - abs_paths = [os.path.abspath(x) for x in hdl_files] - folder = os.path.dirname(os.path.commonprefix([p + os.path.sep for p in abs_paths])) - - sigasi_project_file_creator = SigasiProjectCreator(project_name, VhdlVersion.NINETY_THREE) - # Create Project File and add a link the common source folder - folder_name = os.path.basename(os.path.normpath(folder)) - sigasi_project_file_creator.add_link(folder_name, folder, True) - - # Create Library Mapping File - # Unmap everything except the list of files (map those to work) - sigasi_project_file_creator.unmap("/") - for path in abs_paths: - relative_file_path = os.path.relpath(path, folder) - sigasi_project_file_creator.add_mapping(folder_name + "/" + relative_file_path, "work") - - sigasi_project_file_creator.write(destination) - - -if __name__ == '__main__': - main() diff --git a/src/convertDotFtoSigasiProject.py b/src/convertDotFtoSigasiProject.py deleted file mode 100644 index 2f02c67..0000000 --- a/src/convertDotFtoSigasiProject.py +++ /dev/null @@ -1,14 +0,0 @@ -""" - :copyright: (c) 2008-2023 Sigasi - :license: BSD, see LICENSE for more details. -""" -from SigasiProjectCreator import ConverterHelper -from SigasiProjectCreator.DotF import DotFfileParser - - -def main(): - ConverterHelper.parse_and_create_project(DotFfileParser.usage, DotFfileParser.parse_file) - - -if __name__ == '__main__': - main() diff --git a/src/createSigasiProject.py b/src/createSigasiProject.py new file mode 100644 index 0000000..bce925d --- /dev/null +++ b/src/createSigasiProject.py @@ -0,0 +1,20 @@ +""" + :copyright: (c) 2008-2023 Sigasi + :license: BSD, see LICENSE for more details. +""" +import sys + +from SigasiProjectCreator.ProjectCreator import get_project_creator +from SigasiProjectCreator.ProjectOptions import ProjectOptions +from SigasiProjectCreator import CsvParser +from SigasiProjectCreator.DotF import DotFfileParser +from SigasiProjectCreator.HdpProjectParser import HdpParser +from SigasiProjectCreator.XilinxProjectParser import XilinxProjectParser + + +def main(args): + get_project_creator(ProjectOptions(args)).create_project() + + +if __name__ == '__main__': + main(sys.argv[1:]) diff --git a/tests/ConvertCsvFileToLinksTest.py b/tests/ConvertCsvFileToLinksTest.py deleted file mode 100644 index 2c9b863..0000000 --- a/tests/ConvertCsvFileToLinksTest.py +++ /dev/null @@ -1,29 +0,0 @@ -# -*- coding: utf-8 -*- -""" - :copyright: (c) 2008-2023 Sigasi - :license: BSD, see LICENSE for more details. -""" -import unittest -import sys -import tempfile -import shutil -import os -import filecmp -import SigasiProjectCreator.convertCsvFileToLinks as csvConverter - - -class ConvertCsvFileToLinksTest(unittest.TestCase): - def setUp(self): - self.temp_dir = tempfile.mkdtemp() - - def tearDown(self): - shutil.rmtree(self.temp_dir) - - def test_main(self): - wd = os.path.dirname(os.path.realpath(__file__)) - links_dir = os.path.join(wd, "test-files/links") - csv_file = os.path.join(links_dir, "compilation_order.csv") - sys.argv = [sys.argv[0], "tutorial", csv_file, links_dir] - csvConverter.main() - result = filecmp.dircmp(links_dir, self.temp_dir) - self.assertTrue(not result.report()) diff --git a/tests/ConvertCsvFileToTreeTest.py b/tests/ConvertCsvFileToTreeTest.py deleted file mode 100644 index 2642216..0000000 --- a/tests/ConvertCsvFileToTreeTest.py +++ /dev/null @@ -1,29 +0,0 @@ -# -*- coding: utf-8 -*- -""" - :copyright: (c) 2008-2023 Sigasi - :license: BSD, see LICENSE for more details. -""" -import unittest -import sys -import tempfile -import shutil -import os -import filecmp -import SigasiProjectCreator.convertCsvFileToTree as csvConverter - - -class ConvertCsvFileToTreeTest(unittest.TestCase): - def setUp(self): - self.temp_dir = tempfile.mkdtemp() - - def tearDown(self): - shutil.rmtree(self.temp_dir) - - def test_main(self): - wd = os.path.dirname(os.path.realpath(__file__)) - tree_dir = os.path.join(wd, "test-files/tree") - csv_file = os.path.join(tree_dir, "compilation_order.csv") - sys.argv = [sys.argv[0], "tutorial", csv_file, self.temp_dir] - csvConverter.main() - result = filecmp.dircmp(tree_dir, self.temp_dir) - self.assertTrue(not result.report()) diff --git a/tests/CreateSigasiProjectFromListOfFilesTest.py b/tests/CreateSigasiProjectFromListOfFilesTest.py deleted file mode 100644 index 03a25aa..0000000 --- a/tests/CreateSigasiProjectFromListOfFilesTest.py +++ /dev/null @@ -1,37 +0,0 @@ -# -*- coding: utf-8 -*- -""" - :copyright: (c) 2008-2023 Sigasi - :license: BSD, see LICENSE for more details. -""" -import unittest -import sys -import tempfile -import shutil -import os -import filecmp -import SigasiProjectCreator.createSigasiProjectFromListOfFiles as sigasiProjectCreator - - -class CreateSigasiProjectFromListOfFilesTest(unittest.TestCase): - def setUp(self): - self.temp_dir = tempfile.mkdtemp() - self.old_path = os.getcwd() - - def tearDown(self): - shutil.rmtree(self.temp_dir) - os.chdir(self.old_path) - - def test_main(self): - wd = os.path.dirname(os.path.realpath(__file__)) - list_dir = os.path.join(wd, "test-files/list") - # Be sure to cd back to the original dir later on - os.chdir(self.temp_dir) - files = ["testbench.vhd", "dut.vhd", "clock_generator.vhd", "foo/foo.vhd"] - file_paths = [os.path.join(wd, "tutorial" + f) for f in files] - sys.argv = [sys.argv[0], "list"] - for path in file_paths: - sys.argv.append(path) - sigasiProjectCreator.main() - result = filecmp.dircmp(list_dir, self.temp_dir) - self.assertTrue(not result.report()) - os.chdir(wd) diff --git a/tests/DotFfileParserTest.py b/tests/DotFfileParserTest.py index 8f8a3f1..fcfd83d 100644 --- a/tests/DotFfileParserTest.py +++ b/tests/DotFfileParserTest.py @@ -1,127 +1,131 @@ -import os.path +import pathlib import unittest -from SigasiProjectCreator.DotF.DotFfileParser import parse_file + +from SigasiProjectCreator.ProjectOptions import ProjectOptions +from SigasiProjectCreator.DotF.DotFfileParser import DotFfileParser + class DotFfileParserTest(unittest.TestCase): + def setUp(self): + command_line_options = ['the_project', 'tests/test-files/tree/compilation_order.csv'] + self.options = ProjectOptions(command_line_options) + def test_basic(self): self.maxDiff = None input_file = "tests/test-files/dotFparser/test1.f" - result = parse_file(input_file) - base_path = os.path.abspath(os.path.join(os.path.dirname(input_file), '../../../..')) + result = DotFfileParser().parse_file(input_file, self.options) expected_library_mapping = { - f'{base_path}/bench/verilog/stainlesssteel/ram_d1.sv': 'work', - f'{base_path}/bench/verilog/stainlesssteel/ram_d2.sv': 'work', - f'{base_path}/bench/verilog/stainlesssteel/ram_dp.sv': 'work', - f'{base_path}/bench/verilog/stainlesssteel/ram_p2.sv': 'work', - f'{base_path}/bench/verilog/stainlesssteel/ram_sp.sv': 'work', - f'{base_path}/bench/verilog/stainlesssteel/glbl.sv': 'work', - f'{base_path}/bench/verilog/stainlesssteel/m_debug.sv': 'work', - f'{base_path}/bench/verilog/stainlesssteel/m_testbench.sv': 'work', - f'{base_path}/rtl/verilog/soc/m_soc.sv': 'work', - f'{base_path}/rtl/verilog/soc/m_io_cell.sv': 'work' + pathlib.Path('../bench/verilog/stainlesssteel/ram_d1.sv').absolute().resolve(): 'work', + pathlib.Path('../bench/verilog/stainlesssteel/ram_d2.sv').absolute().resolve(): 'work', + pathlib.Path('../bench/verilog/stainlesssteel/ram_dp.sv').absolute().resolve(): 'work', + pathlib.Path('../bench/verilog/stainlesssteel/ram_p2.sv').absolute().resolve(): 'work', + pathlib.Path('../bench/verilog/stainlesssteel/ram_sp.sv').absolute().resolve(): 'work', + pathlib.Path('../bench/verilog/stainlesssteel/glbl.sv').absolute().resolve(): 'work', + pathlib.Path('../bench/verilog/stainlesssteel/m_debug.sv').absolute().resolve(): 'work', + pathlib.Path('../bench/verilog/stainlesssteel/m_testbench.sv').absolute().resolve(): 'work', + pathlib.Path('../rtl/verilog/soc/m_soc.sv').absolute().resolve(): 'work', + pathlib.Path('../rtl/verilog/soc/m_io_cell.sv').absolute().resolve(): 'work' } expected_includes = { - '../rtl/verilog/pkg', - '../bench/verilog/stainlesssteel' + pathlib.Path('../rtl/verilog/pkg').absolute().resolve(), + pathlib.Path('../bench/verilog/stainlesssteel').absolute().resolve() } expected_defines = [] self.assertDictEqual(result.library_mapping, expected_library_mapping, 'Library mapping mismatch') - self.assertSetEqual(result.includes, expected_includes, 'Includes list mismatch') - self.assertListEqual(result.defines, expected_defines, 'Defines list mismatch') + self.assertSetEqual(result.verilog_includes, expected_includes, 'Includes list mismatch') + self.assertListEqual(result.verilog_defines, expected_defines, 'Defines list mismatch') def test_continuation(self): self.maxDiff = None input_file = "tests/test-files/dotFparser/continuation.f" - result = parse_file(input_file) + result = DotFfileParser().parse_file(input_file, self.options) expected_library_mapping = { - "D:/Vendor/Tool/2018.2/data/vendor_vip/hdl/axi4stream_vip_axi4streampc.sv": 'vendor_vip', - "D:/Vendor/Tool/2018.2/data/vendor_vip/hdl/axi_vip_axi4pc.sv": 'vendor_vip', - "D:/Vendor/Tool/2018.2/data/vendor_vip/hdl/xil_common_vip_pkg.sv": 'vendor_vip', - "D:/Vendor/Tool/2018.2/data/vendor_vip/hdl/axi4stream_vip_pkg.sv": 'vendor_vip', - "D:/Vendor/Tool/2018.2/data/vendor_vip/hdl/axi_vip_pkg.sv": 'vendor_vip', - "D:/Vendor/Tool/2018.2/data/vendor_vip/hdl/axi4stream_vip_if.sv": 'vendor_vip', - "D:/Vendor/Tool/2018.2/data/vendor_vip/hdl/axi_vip_if.sv": 'vendor_vip', - "D:/Vendor/Tool/2018.2/data/vendor_vip/hdl/clk_vip_if.sv": 'vendor_vip', - "D:/Vendor/Tool/2018.2/data/vendor_vip/hdl/rst_vip_if.sv": 'vendor_vip', - "D:/Vendor/Tool/2018.2/data/ip/xpm/xpm_cdc/hdl/xpm_cdc.sv": 'defaultlib', - "D:/Vendor/Tool/2018.2/data/ip/xpm/xpm_fifo/hdl/xpm_fifo.sv": 'defaultlib', - "D:/Vendor/Tool/2018.2/data/ip/xpm/xpm_memory/hdl/xpm_memory.sv": 'defaultlib', - "D:/Vendor/Tool/2018.2/data/ip/xpm/xpm_VCOMP.vhd": 'xpm' + pathlib.Path("D:/Vendor/Tool/2018.2/data/vendor_vip/hdl/axi4stream_vip_axi4streampc.sv"): 'vendor_vip', + pathlib.Path("D:/Vendor/Tool/2018.2/data/vendor_vip/hdl/axi_vip_axi4pc.sv"): 'vendor_vip', + pathlib.Path("D:/Vendor/Tool/2018.2/data/vendor_vip/hdl/xil_common_vip_pkg.sv"): 'vendor_vip', + pathlib.Path("D:/Vendor/Tool/2018.2/data/vendor_vip/hdl/axi4stream_vip_pkg.sv"): 'vendor_vip', + pathlib.Path("D:/Vendor/Tool/2018.2/data/vendor_vip/hdl/axi_vip_pkg.sv"): 'vendor_vip', + pathlib.Path("D:/Vendor/Tool/2018.2/data/vendor_vip/hdl/axi4stream_vip_if.sv"): 'vendor_vip', + pathlib.Path("D:/Vendor/Tool/2018.2/data/vendor_vip/hdl/axi_vip_if.sv"): 'vendor_vip', + pathlib.Path("D:/Vendor/Tool/2018.2/data/vendor_vip/hdl/clk_vip_if.sv"): 'vendor_vip', + pathlib.Path("D:/Vendor/Tool/2018.2/data/vendor_vip/hdl/rst_vip_if.sv"): 'vendor_vip', + pathlib.Path("D:/Vendor/Tool/2018.2/data/ip/xpm/xpm_cdc/hdl/xpm_cdc.sv"): 'defaultlib', + pathlib.Path("D:/Vendor/Tool/2018.2/data/ip/xpm/xpm_fifo/hdl/xpm_fifo.sv"): 'defaultlib', + pathlib.Path("D:/Vendor/Tool/2018.2/data/ip/xpm/xpm_memory/hdl/xpm_memory.sv"): 'defaultlib', + pathlib.Path("D:/Vendor/Tool/2018.2/data/ip/xpm/xpm_VCOMP.vhd"): 'xpm' } expected_includes = set() expected_defines = [] self.assertDictEqual(result.library_mapping, expected_library_mapping, 'Library mapping mismatch') - self.assertSetEqual(result.includes, expected_includes, 'Includes list mismatch') - self.assertListEqual(result.defines, expected_defines, 'Defines list mismatch') + self.assertSetEqual(result.verilog_includes, expected_includes, 'Includes list mismatch') + self.assertListEqual(result.verilog_defines, expected_defines, 'Defines list mismatch') def test_filelist(self): self.maxDiff = None input_file = "tests/test-files/dotFparser/filelist.f" - result = parse_file(input_file) - base_path = os.path.abspath(os.path.join(os.path.dirname(input_file), '../../..')) + result = DotFfileParser().parse_file(input_file, self.options) expected_library_mapping = { - f'{base_path}/ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_conv.v': 'work', - f'{base_path}/ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_lite_conv.v': 'work', - f'{base_path}/ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_r_conv.v': 'work', - f'{base_path}/ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_w_conv.v': 'work', - f'{base_path}/ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_b_downsizer.v': 'work', - f'{base_path}/ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_decerr_slave.v': 'work', - f'{base_path}/ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_b2s_simple_fifo.v': 'work', - f'{base_path}/ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_b2s_wrap_cmd.v': 'work', - f'{base_path}/ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_b2s_incr_cmd.v': 'work', - f'{base_path}/ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_b2s_wr_cmd_fsm.v': 'work', - f'{base_path}/ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_b2s_rd_cmd_fsm.v': 'work', - f'{base_path}/ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_b2s_cmd_translator.v': 'work', - f'{base_path}/ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_b2s_b_channel.v': 'work', - f'{base_path}/ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_b2s_r_channel.v': 'work', - f'{base_path}/ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_b2s_aw_channel.v': 'work', - f'{base_path}/ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_b2s_ar_channel.v': 'work', - f'{base_path}/ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_b2s.v': 'work', - f'{base_path}/ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_some_protocol_converter.v': 'work', - f'{base_path}/bd/design_1/ip/design_1_auto_pc_0/sim/design_1_auto_pc_0.v': 'work', - f'{base_path}/bd/design_1/hdl/design_1.vhd': 'work', - f'{base_path}/tests/test-files/dotFparser/glbl.v': 'work' + pathlib.Path('ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_conv.v').absolute().resolve(): 'work', + pathlib.Path('ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_lite_conv.v').absolute().resolve(): 'work', + pathlib.Path('ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_r_conv.v').absolute().resolve(): 'work', + pathlib.Path('ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_w_conv.v').absolute().resolve(): 'work', + pathlib.Path('ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_b_downsizer.v').absolute().resolve(): 'work', + pathlib.Path('ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_decerr_slave.v').absolute().resolve(): 'work', + pathlib.Path('ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_b2s_simple_fifo.v').absolute().resolve(): 'work', + pathlib.Path('ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_b2s_wrap_cmd.v').absolute().resolve(): 'work', + pathlib.Path('ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_b2s_incr_cmd.v').absolute().resolve(): 'work', + pathlib.Path('ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_b2s_wr_cmd_fsm.v').absolute().resolve(): 'work', + pathlib.Path('ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_b2s_rd_cmd_fsm.v').absolute().resolve(): 'work', + pathlib.Path('ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_b2s_cmd_translator.v').absolute().resolve(): 'work', + pathlib.Path('ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_b2s_b_channel.v').absolute().resolve(): 'work', + pathlib.Path('ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_b2s_r_channel.v').absolute().resolve(): 'work', + pathlib.Path('ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_b2s_aw_channel.v').absolute().resolve(): 'work', + pathlib.Path('ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_b2s_ar_channel.v').absolute().resolve(): 'work', + pathlib.Path('ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_b2s.v').absolute().resolve(): 'work', + pathlib.Path('ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_some_protocol_converter.v').absolute().resolve(): 'work', + pathlib.Path('bd/design_1/ip/design_1_auto_pc_0/sim/design_1_auto_pc_0.v').absolute().resolve(): 'work', + pathlib.Path('bd/design_1/hdl/design_1.vhd').absolute().resolve(): 'work', + pathlib.Path('tests/test-files/dotFparser/glbl.v').absolute().resolve(): 'work' } expected_includes = set() expected_defines = [] self.assertDictEqual(result.library_mapping, expected_library_mapping, 'Library mapping mismatch') - self.assertSetEqual(result.includes, expected_includes, 'Includes list mismatch') - self.assertListEqual(result.defines, expected_defines, 'Defines list mismatch') + self.assertSetEqual(result.verilog_includes, expected_includes, 'Includes list mismatch') + self.assertListEqual(result.verilog_defines, expected_defines, 'Defines list mismatch') def test_variable(self): self.maxDiff = None - input_file = "tests/test-files/dotFparser/variable.f" - result = parse_file(input_file) - base_path = str(os.path.abspath(os.path.join(os.path.dirname(input_file), '.'))) + input_file = pathlib.Path("tests/test-files/dotFparser/variable.f") + result = DotFfileParser().parse_file(input_file, self.options) expected_library_mapping = { - '${FUBAR_HOME}/src/fubar_pkg.sv': 'work', - f'{base_path}/vw_wd_g2u_if.sv': 'work', - f'{base_path}/vw_wd_g2u_test.sv': 'work', - f'{base_path}/vw_wd_g2u_top.sv': 'work', + pathlib.Path('${FUBAR_HOME}/src/fubar_pkg.sv'): 'work', + pathlib.Path('tests/test-files/dotFparser/vw_wd_g2u_if.sv').absolute().resolve(): 'work', + pathlib.Path('tests/test-files/dotFparser/vw_wd_g2u_test.sv').absolute().resolve(): 'work', + pathlib.Path('tests/test-files/dotFparser/vw_wd_g2u_top.sv').absolute().resolve(): 'work', } - expected_includes = {'$FUBAR_HOME/src'} + expected_includes = {pathlib.Path('$FUBAR_HOME/src')} expected_defines = [] self.assertDictEqual(result.library_mapping, expected_library_mapping, 'Library mapping mismatch') - self.assertSetEqual(result.includes, expected_includes, 'Includes list mismatch') - self.assertListEqual(result.defines, expected_defines, 'Defines list mismatch') + self.assertSetEqual(result.verilog_includes, expected_includes, 'Includes list mismatch') + self.assertListEqual(result.verilog_defines, expected_defines, 'Defines list mismatch') def test_wildcard(self): self.maxDiff = None input_file = "tests/test-files/dotFparser/wildcard.f" - base_path1 = str(os.path.abspath(os.path.join(os.path.dirname(input_file), '..'))) - base_path2 = str(os.path.abspath(os.path.join(os.path.dirname(input_file), '../..'))) - result = parse_file(input_file) + result = DotFfileParser().parse_file(input_file, self.options) expected_library_mapping = { - '/usr/eda/dk/vendor/tech/verilog/*.v': 'work', - f'{base_path2}/synthesis/image_average.v': 'work', - f'{base_path1}/tb/tb_image.vhd': 'work', - f'{base_path2}/rtl/image_ram.vhd': 'work' + pathlib.Path('tests/test-files/dotFparser/../tutorial/clock_generator.vhd').absolute().resolve(): 'work', + pathlib.Path('tests/test-files/dotFparser/../tutorial/dut.vhd').absolute().resolve(): 'work', + pathlib.Path('tests/test-files/dotFparser/../tutorial/testbench.vhd').absolute().resolve(): 'work', + pathlib.Path('/absolute/path/synthesis/image_average.v'): 'work', + pathlib.Path('tests/test-files/dotFparser/../tb/tb_image.vhd').absolute().resolve(): 'work', + pathlib.Path('tests/test-files/dotFparser/../../rtl/image_ram.vhd').absolute().resolve(): 'work' } expected_includes = set() expected_defines = [] self.assertDictEqual(result.library_mapping, expected_library_mapping, 'Library mapping mismatch') - self.assertSetEqual(result.includes, expected_includes, 'Includes list mismatch') - self.assertListEqual(result.defines, expected_defines, 'Defines list mismatch') + self.assertSetEqual(result.verilog_includes, expected_includes, 'Includes list mismatch') + self.assertListEqual(result.verilog_defines, expected_defines, 'Defines list mismatch') if __name__ == '__main__': diff --git a/tests/LibraryMappingFileCreatorTest.py b/tests/LibraryMappingFileCreatorTest.py index 1a64664..6d1c889 100644 --- a/tests/LibraryMappingFileCreatorTest.py +++ b/tests/LibraryMappingFileCreatorTest.py @@ -5,7 +5,8 @@ """ import unittest -from SigasiProjectCreator.Creator import LibraryMappingFileCreator +from SigasiProjectCreator import VhdlVersion, VerilogVersion +from SigasiProjectCreator.SigasiProject import LibraryMappingFileCreator from string import Template @@ -28,6 +29,7 @@ def setUp(self): def test_empty_file(self): expected = mappings_template + self.creator.set_languages(VhdlVersion.TWENTY_O_EIGHT, None) self.assertEqual(expected.substitute(after=""), str(self.creator)) def test_simple_mapping(self): @@ -38,6 +40,7 @@ def test_simple_mapping(self): self.creator.add_mapping(loc, lib) self.creator.add_mapping("", "not mapped") + self.creator.set_languages(VhdlVersion.TWENTY_O_EIGHT, VerilogVersion.TWENTY_O_FIVE) self.assertEqual(expected, str(self.creator)) def test_duplicate_mapping(self): @@ -48,4 +51,5 @@ def test_duplicate_mapping(self): self.creator.add_mapping(loc, "test") self.creator.add_mapping(loc, lib) + self.creator.set_languages(VhdlVersion.TWENTY_O_EIGHT, None) self.assertEqual(expected, str(self.creator)) diff --git a/tests/ProjectCreatorTest.py b/tests/ProjectCreatorTest.py new file mode 100644 index 0000000..0c4e40f --- /dev/null +++ b/tests/ProjectCreatorTest.py @@ -0,0 +1,488 @@ +import pathlib +import shutil +import unittest +import os + +from SigasiProjectCreator import CsvParser +from SigasiProjectCreator.ProjectCreator import ProjectCreator, get_parser_for_type, get_design_root_folder, \ + get_design_folders, get_design_subtrees, get_unique_name, get_rel_or_abs_path, ProjectCreatorSimulator, \ + ProjectCreatorLinkedFilesFlat, ProjectCreatorLinkedFilesTree, ProjectCreatorLinkedFolders, ProjectCreatorInPlace +from SigasiProjectCreator.ProjectFileParser import ProjectFileParser +from SigasiProjectCreator.ProjectOptions import ProjectOptions +from SigasiProjectCreator.DotF import DotFfileParser +from SigasiProjectCreator.HdpProjectParser import HdpParser +from SigasiProjectCreator.XilinxProjectParser import XilinxProjectParser + + +class ProjectCreatorTest(unittest.TestCase): + def setUp(self): + command_line_options = ['the_project', 'tests/test-files/tree/compilation_order.csv'] + self.options = ProjectOptions(command_line_options) + self.project_creator = ProjectCreator(self.options) + + def test_abs_or_rel_path_rel_in(self): + destination_folder = pathlib.Path('foo').absolute().as_posix() + command_line_options = ['the_project', 'tests/test-files/tree/compilation_order.csv', '-d', destination_folder] + self.options = ProjectOptions(command_line_options) + design_path = pathlib.Path('tests/test-files/tutorial/testbench.vhd') + result = get_rel_or_abs_path(design_path, self.project_creator.project_root, self.options) + self.assertEqual(result, design_path) + + def test_abs_or_rel_path_abs(self): + destination_folder = pathlib.Path('foo').absolute().as_posix() + command_line_options = ['the_project', 'tests/test-files/tree/compilation_order.csv', '-d', destination_folder] + self.options = ProjectOptions(command_line_options) + self.project_creator = ProjectCreator(self.options) + design_path = pathlib.Path('tests/test-files/tutorial/testbench.vhd').absolute() + result = get_rel_or_abs_path(design_path, self.project_creator.project_root, self.options) + self.assertEqual(result, design_path.absolute()) + + def test_abs_or_rel_path_rel(self): + destination_folder = pathlib.Path('foo').absolute() + command_line_options = ['the_project', 'tests/test-files/tree/compilation_order.csv', + '-d', destination_folder.as_posix(), + '--rel-path', 'fooh', + '--rel-path', 'tests/test-files'] + self.options = ProjectOptions(command_line_options) + self.project_creator = ProjectCreator(self.options) + design_path = pathlib.Path('tests/test-files/tutorial/testbench.vhd').absolute() + self.assertTrue(self.options.use_relative_path(design_path)) + result = get_rel_or_abs_path(design_path, self.project_creator.project_root, self.options) + self.assertEqual(result, pathlib.Path(os.path.relpath(design_path, destination_folder))) + + def test_check_and_create_virtual_folder(self): + result = self.project_creator.sigasi_project._SigasiProject__projectFileCreator._ProjectFileCreator__links + self.assertEqual(result, []) + self.project_creator.check_and_create_virtual_folder(pathlib.Path('foo/bar/file.vhd')) + result = self.project_creator.sigasi_project._SigasiProject__projectFileCreator._ProjectFileCreator__links + expected = [[pathlib.Path('foo'), 'virtual:/virtual', True, False], + [pathlib.Path('foo/bar'), 'virtual:/virtual', True, False]] + self.assertEqual(result, expected) + + def test_check_and_create_linked_folder(self): + # set_project_root('project_folder') + self.project_creator.check_and_create_linked_folder(pathlib.Path('foo/bar/file.vhd'), + pathlib.Path('/here/there')) + result = self.project_creator.sigasi_project._SigasiProject__projectFileCreator._ProjectFileCreator__links + expected = [[pathlib.Path('foo'), 'virtual:/virtual', True, False], + [pathlib.Path('foo/bar'), 'virtual:/virtual', True, False], + [pathlib.Path('foo/bar/file.vhd'), '/here/there', True, True]] + self.assertEqual(result, expected) + + def test_uniquify_project_path_none(self): + result = get_unique_name(pathlib.Path('/my/path/foo/bar.vhd'), None) + self.assertEqual(result, pathlib.Path('/my/path/foo/bar.vhd')) + + def test_uniquify_project_path_empty_list(self): + result = get_unique_name(pathlib.Path('/my/path/foo/bar.vhd'), []) + self.assertEqual(result, pathlib.Path('/my/path/foo/bar.vhd')) + + def test_uniquify_project_path_not_in_list(self): + path_list = [ + pathlib.Path('/my/path/foo/bar.v'), + pathlib.Path('/my/path/foo/bar.vhdl') + ] + result = get_unique_name(pathlib.Path('/my/path/foo/bar.vhd'), path_list) + self.assertEqual(result, pathlib.Path('/my/path/foo/bar.vhd')) + + def test_uniquify_project_path_in_list(self): + path_list = [ + pathlib.Path('/my/path/foo/bar.v'), + pathlib.Path('/my/path/foo/bar.vhd'), + pathlib.Path('/my/path/foo/bar.vhdl') + ] + result = get_unique_name(pathlib.Path('/my/path/foo/bar.vhd'), path_list) + self.assertEqual(result, pathlib.Path('/my/path/foo/bar_1.vhd')) + + def test_uniquify_project_path_in_list_multi(self): + path_list = [ + pathlib.Path('/my/path/foo/bar.v'), + pathlib.Path('/my/path/foo/bar.vhd'), + pathlib.Path('/my/path/foo/bar_1.vhd'), + pathlib.Path('/my/path/foo/bar_2.vhd'), + pathlib.Path('/my/path/foo/bar_3.vhd'), + pathlib.Path('/my/path/foo/bar.vhdl') + ] + result = get_unique_name(pathlib.Path('/my/path/foo/bar.vhd'), path_list) + self.assertEqual(result, pathlib.Path('/my/path/foo/bar_4.vhd')) + + def test_create_project_simulator(self): + self.maxDiff = None + self.project_creator = ProjectCreatorSimulator(self.options) + entries = { + pathlib.Path('/my/path/foo/bar.v'): 'work', + pathlib.Path('/my/path/foo/bar.vhd'): 'work', + pathlib.Path('/my/path/foo1/bar.vhd'): 'work', + pathlib.Path('/my/path/foo2/bar.vhd'): 'labor', + pathlib.Path('/my/path/foo3/bar.vhd'): 'work', + pathlib.Path('/my/path/foo/bar.vhdl'): 'work', + pathlib.Path('/my/path/foo/bahr.vhdl'): ['work', 'travail'] + } + has_vhdl, has_verilog = self.project_creator.create_project_layout(entries) + self.assertTrue(has_vhdl) + self.assertTrue(has_verilog) + file_mapping = self.project_creator.sigasi_project._SigasiProject__projectFileCreator._ProjectFileCreator__links + expected = [['work', 'virtual:/virtual', True, False], + [pathlib.Path('work/bar.v'), '/my/path/foo/bar.v', False, True], + [pathlib.Path('work/bar.vhd'), '/my/path/foo/bar.vhd', False, True], + [pathlib.Path('work/bar_1.vhd'), '/my/path/foo1/bar.vhd', False, True], + ['labor', 'virtual:/virtual', True, False], + [pathlib.Path('labor/bar.vhd'), '/my/path/foo2/bar.vhd', False, True], + [pathlib.Path('work/bar_2.vhd'), '/my/path/foo3/bar.vhd', False, True], + [pathlib.Path('work/bar.vhdl'), '/my/path/foo/bar.vhdl', False, True], + [pathlib.Path('work/bahr.vhdl'), '/my/path/foo/bahr.vhdl', False, True], + ['travail', 'virtual:/virtual', True, False], + [pathlib.Path('travail/bahr.vhdl'), '/my/path/foo/bahr.vhdl', False, True] + ] + self.assertEqual(file_mapping, expected) + lib_mapping = self.project_creator.sigasi_project._SigasiProject__libraryMappingFileCreator._LibraryMappingFileCreator__entries + lib_expected = { + '/': 'not mapped', + 'work': 'work', + 'labor': 'labor', + 'travail': 'travail' + } + self.assertEqual(lib_mapping, lib_expected) + + def test_create_project_links_flat(self): + self.maxDiff = None + self.project_creator = ProjectCreatorLinkedFilesFlat(self.options) + entries = { + pathlib.Path('/my/path/foo/bar.v'): 'work', + pathlib.Path('/my/path/foo/bar.vhd'): 'work', + pathlib.Path('/my/path/foo1/bar.vhd'): 'work', + pathlib.Path('/my/path/foo2/bar.vhd'): 'labor', + pathlib.Path('/my/path/foo3/bar.vhd'): 'work', + pathlib.Path('/my/path/foo/bar.vhdl'): 'work', + pathlib.Path('/my/path/foo/bahr.vhdl'): ['work', 'travail'] + } + has_vhdl, has_verilog = self.project_creator.create_project_layout(entries) + self.assertTrue(has_vhdl) + self.assertTrue(has_verilog) + file_mapping = self.project_creator.sigasi_project._SigasiProject__projectFileCreator._ProjectFileCreator__links + expected = [[pathlib.Path('bar.v'), '/my/path/foo/bar.v', False, True], + [pathlib.Path('bar.vhd'), '/my/path/foo/bar.vhd', False, True], + [pathlib.Path('bar_1.vhd'), '/my/path/foo1/bar.vhd', False, True], + [pathlib.Path('bar_2.vhd'), '/my/path/foo2/bar.vhd', False, True], + [pathlib.Path('bar_3.vhd'), '/my/path/foo3/bar.vhd', False, True], + [pathlib.Path('bar.vhdl'), '/my/path/foo/bar.vhdl', False, True], + [pathlib.Path('bahr.vhdl'), '/my/path/foo/bahr.vhdl', False, True], + [pathlib.Path('bahr_1.vhdl'), '/my/path/foo/bahr.vhdl', False, True] + ] + self.assertEqual(file_mapping, expected) + lib_mapping = self.project_creator.sigasi_project._SigasiProject__libraryMappingFileCreator._LibraryMappingFileCreator__entries + lib_expected = { + '/': 'not mapped', + 'bahr.vhdl': 'work', + 'bahr_1.vhdl': 'travail', + 'bar.v': 'work', + 'bar.vhd': 'work', + 'bar.vhdl': 'work', + 'bar_1.vhd': 'work', + 'bar_2.vhd': 'labor', + 'bar_3.vhd': 'work' + } + self.assertEqual(lib_mapping, lib_expected) + + def test_get_design_folders(self): + self.maxDiff = None + entries = { + pathlib.Path('/my/path/foo/bar.v'): 'work', + pathlib.Path('/my/path/foo/bar.vhd'): 'work', + pathlib.Path('/my/path/foo1/bar.vhd'): 'work', + pathlib.Path('/my/path/foo2/bar.vhd'): 'labor', + pathlib.Path('/my/path/foo3/bar.vhd'): 'work', + pathlib.Path('/my/path/foo/bar.vhdl'): 'work', + pathlib.Path('/my/path/foo/bahr.vhdl'): ['work', 'travail'] + } + design_folders = get_design_folders(entries) + expected_folders = [ + pathlib.Path('/my/path/foo'), + pathlib.Path('/my/path/foo1'), + pathlib.Path('/my/path/foo2'), + pathlib.Path('/my/path/foo3') + ] + self.assertEqual(design_folders, expected_folders) + + def test_get_design_root_folder(self): + design_folders = [ + pathlib.Path('/my/path/foo'), + pathlib.Path('/my/path/brol/foo1'), + pathlib.Path('/my/path/foo2'), + pathlib.Path('/my/path/some/deeper/path/foo3') + ] + self.assertEqual(get_design_root_folder(design_folders), pathlib.Path('/my/path')) + + def test_get_design_subtrees(self): + # Note: folder lists must be sorted! + design_folders = [ + pathlib.Path('/my/path/brol/foo1'), + pathlib.Path('/my/path/brol/foo1/foo2'), + pathlib.Path('/my/path/foo'), + pathlib.Path('/my/path/foo2'), + pathlib.Path('/my/path/some/deeper/path/foo3') + ] + design_subtrees = get_design_subtrees(design_folders) + expected_folders = [ + pathlib.Path('/my/path/brol/foo1'), + pathlib.Path('/my/path/foo'), + pathlib.Path('/my/path/foo2'), + pathlib.Path('/my/path/some/deeper/path/foo3') + ] + self.assertEqual(design_subtrees, expected_folders) + + def test_create_project_links_tree(self): + self.maxDiff = None + self.project_creator = ProjectCreatorLinkedFilesTree(self.options) + entries = { + pathlib.Path('/my/path/foo/bar.v'): 'work', + pathlib.Path('/my/path/foo/bar.vhd'): 'work', + pathlib.Path('/my/path/foo/one/bar.vhd'): 'work', + pathlib.Path('/my/path/foo/one/bars.vhd'): 'labor', + pathlib.Path('/my/path/foo2/bar.vhd'): 'work', + pathlib.Path('/my/path/foo/one/two/bar.vhdl'): 'work', + pathlib.Path('/my/path/foo/bahr.vhdl'): ['work', 'travail'] + } + has_vhdl, has_verilog = self.project_creator.create_project_layout(entries) + self.assertTrue(has_vhdl) + self.assertTrue(has_verilog) + file_mapping = self.project_creator.sigasi_project._SigasiProject__projectFileCreator._ProjectFileCreator__links + print(f'**file mapping** {file_mapping}') + expected = [[pathlib.Path('foo'), 'virtual:/virtual', True, False], + [pathlib.Path('foo/bar.v'), '/my/path/foo/bar.v', False, True], + [pathlib.Path('foo/bar.vhd'), '/my/path/foo/bar.vhd', False, True], + [pathlib.Path('foo/one'), 'virtual:/virtual', True, False], + [pathlib.Path('foo/one/bar.vhd'), '/my/path/foo/one/bar.vhd', False, True], + [pathlib.Path('foo/one/bars.vhd'), '/my/path/foo/one/bars.vhd', False, True], + [pathlib.Path('foo2'), 'virtual:/virtual', True, False], + [pathlib.Path('foo2/bar.vhd'), '/my/path/foo2/bar.vhd', False, True], + [pathlib.Path('foo/one/two'), 'virtual:/virtual', True, False], + [pathlib.Path('foo/one/two/bar.vhdl'), '/my/path/foo/one/two/bar.vhdl', False, True], + [pathlib.Path('foo/bahr.vhdl'), '/my/path/foo/bahr.vhdl', False, True], + [pathlib.Path('foo/bahr_travail.vhdl'), '/my/path/foo/bahr.vhdl', False, True] + ] + print(f'##file mapping## {expected}') + self.assertEqual(file_mapping, expected) + lib_mapping = self.project_creator.sigasi_project._SigasiProject__libraryMappingFileCreator._LibraryMappingFileCreator__entries + lib_expected = { + '/': 'not mapped', + 'foo/bahr.vhdl': 'work', + 'foo/bahr_travail.vhdl': 'travail', + 'foo/bar.v': 'work', + 'foo/bar.vhd': 'work', + 'foo/one/two/bar.vhdl': 'work', + 'foo/one/bar.vhd': 'work', + 'foo/one/bars.vhd': 'labor', + 'foo2/bar.vhd': 'work' + } + self.assertEqual(lib_mapping, lib_expected) + + def test_create_project_links_folders(self): + self.maxDiff = None + base_path = pathlib.Path('test_create_project_in_folders') + command_line_options = ['the_project', 'tests/test-files/tree/compilation_order.csv', '-d', + base_path.as_posix(), '--skip-check-exists'] + self.options = ProjectOptions(command_line_options) + self.project_creator = ProjectCreatorLinkedFolders(self.options) + entries = { + pathlib.Path('/my/path/foo/bar.v'): 'work', + pathlib.Path('/my/path/foo/bar.vhd'): 'work', + pathlib.Path('/my/path/foo/one/bar.vhd'): 'work', + pathlib.Path('/my/path/foo/one/bars.vhd'): 'labor', + pathlib.Path('/my/path/foo2/bar.vhd'): 'work', + pathlib.Path('/my/path/foo/one/two/bar.vhdl'): 'work', + pathlib.Path('/my/path/foo/bahr.vhdl'): ['work', 'travail'] + } + has_vhdl, has_verilog = self.project_creator.create_project_layout(entries) + self.assertTrue(has_vhdl) + self.assertTrue(has_verilog) + file_mapping = self.project_creator.sigasi_project._SigasiProject__projectFileCreator._ProjectFileCreator__links + print(f'**file mapping** {file_mapping}') + expected = [[pathlib.Path('foo'), '/my/path/foo', True, True], + [pathlib.Path('foo2'), '/my/path/foo2', True, True], + [pathlib.Path('foo/bahr_travail.vhdl'), '/my/path/foo/bahr.vhdl', False, True] + ] + print(f'##file mapping## {expected}') + self.assertEqual(file_mapping, expected) + lib_mapping = self.project_creator.sigasi_project._SigasiProject__libraryMappingFileCreator._LibraryMappingFileCreator__entries + lib_expected = { + '/': 'not mapped', + 'foo/bahr.vhdl': 'work', + 'foo/bahr_travail.vhdl': 'travail', + 'foo/bar.v': 'work', + 'foo/bar.vhd': 'work', + 'foo/one/two/bar.vhdl': 'work', + 'foo/one/bar.vhd': 'work', + 'foo/one/bars.vhd': 'labor', + 'foo2/bar.vhd': 'work' + } + self.assertEqual(lib_mapping, lib_expected) + + # @unittest.skip # Path handling not OK! + def test_create_project_in_place(self): + self.maxDiff = None + base_path = pathlib.Path.cwd().joinpath('test_create_project_in_place') + command_line_options = ['the_project', 'tests/test-files/tree/compilation_order.csv', '-d', + base_path.as_posix(), '--skip-check-exists'] + self.options = ProjectOptions(command_line_options) + self.project_creator = ProjectCreatorInPlace(self.options) + + entries = { + base_path.joinpath('foo/bar.v'): 'work', + base_path.joinpath('foo/bar.vhd'): 'work', + base_path.joinpath('foo/one/bar.vhd'): 'work', + base_path.joinpath('foo/one/bars.vhd'): 'labor', + base_path.joinpath('foo2/bar.vhd'): 'work', + base_path.joinpath('foo/one/two/bar.vhdl'): 'work', + base_path.joinpath('foo/bahr.vhdl'): ['work', 'travail'] + } + has_vhdl, has_verilog = self.project_creator.create_project_layout(entries) + self.assertTrue(has_vhdl) + self.assertTrue(has_verilog) + file_mapping = self.project_creator.sigasi_project._SigasiProject__projectFileCreator._ProjectFileCreator__links + print(f'**file mapping** {file_mapping}') + expected = [ + [pathlib.Path('foo/bahr_travail.vhdl'), pathlib.Path('foo/bahr.vhdl').as_posix(), False, True] + ] + print(f'##file mapping## {expected}') + self.assertEqual(file_mapping, expected) + lib_mapping = self.project_creator.sigasi_project._SigasiProject__libraryMappingFileCreator._LibraryMappingFileCreator__entries + lib_expected = { + '/': 'not mapped', + 'foo/bahr.vhdl': 'work', + 'foo/bahr_travail.vhdl': 'travail', + 'foo/bar.v': 'work', + 'foo/bar.vhd': 'work', + 'foo/one/two/bar.vhdl': 'work', + 'foo/one/bar.vhd': 'work', + 'foo/one/bars.vhd': 'labor', + 'foo2/bar.vhd': 'work' + } + self.assertEqual(lib_mapping, lib_expected) + + def test_get_parser_for_type(self): + self.assertEqual(get_parser_for_type('dotf'), DotFfileParser.DotFfileParser) + self.assertEqual(get_parser_for_type('csv'), CsvParser.CsvParser) + self.assertEqual(get_parser_for_type('hdp'), HdpParser) + self.assertEqual(get_parser_for_type('xise'), XilinxProjectParser) + self.assertEqual(get_parser_for_type('filelist'), ProjectFileParser) + + @staticmethod + def set_up_simple_project(base_path: pathlib.Path): + base_path.mkdir() + base_path.joinpath('foo').mkdir() + base_path.joinpath('bar').mkdir() + base_path.joinpath('foo/one').mkdir() + base_path.joinpath('foo/one/two').mkdir() + base_path.joinpath('foo/bhar.v').touch() + base_path.joinpath('foo/bhar.vhd').touch() + base_path.joinpath('foo/bhar.v').touch() + base_path.joinpath('foo/barh.v').touch() + base_path.joinpath('foo/one/bar.v').touch() + base_path.joinpath('foo/one/two/bar.v').touch() + base_path.joinpath('bar/foo.vhdl').touch() + + def test_create_library_mapping_folders(self): + base_path = pathlib.Path.cwd().joinpath('test_create_library_mapping_folders') + shutil.rmtree(base_path, True) + self.set_up_simple_project(base_path) + entries = { + base_path.joinpath('foo/bhar.v'): 'main', + base_path.joinpath('foo/bhar.vhd'): 'main', + base_path.joinpath('foo/barh.v'): 'other', + base_path.joinpath('foo/one/two/bar.v'): 'main', + base_path.joinpath('bar/foo.vhdl'): ['main', 'some'] + } + self.project_creator.create_library_mapping_folders(entries, None) + lib_mapping = self.project_creator.sigasi_project._SigasiProject__libraryMappingFileCreator._LibraryMappingFileCreator__entries + lib_expected = { + '/': 'not mapped', + 'foo': 'main', + 'foo/barh.v': 'other', + 'foo/one': 'not mapped', + 'foo/one/two': 'main', + 'bar': 'main', + 'bar/foo_some.vhdl': 'some' + } + self.assertEqual(lib_mapping, lib_expected) + + @staticmethod + def setup_fake_uvm_folder(uvm_path: pathlib.Path): + if uvm_path.exists(): + return + uvm_path.mkdir() + uvm_path.joinpath('src').mkdir() + uvm_path.joinpath('src/uvm_pkg.sv').touch() + uvm_path.joinpath('src/uvm_macros.svh').touch() + + def test_parse_and_create_project(self): + self.maxDiff = None + base_path = pathlib.Path.cwd() + uvm_path = base_path.joinpath('uvm').absolute() + self.setup_fake_uvm_folder(uvm_path) + command_line_options = ['the_project', 'tests/test-files/dotFparser/features.f', '-d', base_path.as_posix(), + '--skip-check-exists', '--layout', 'in-place', '-f', '--uvm', str(uvm_path), + '--uvmlib', 'uvm'] + self.options = ProjectOptions(command_line_options) + self.project_creator = ProjectCreatorInPlace(self.options) + os.environ['SIM'] = 'simulator' + os.environ['FUBAR_HOME'] = 'phoo/barh/ome' + (the_project, verilog_defines) = self.project_creator.create_project() + del os.environ['SIM'] + del os.environ['FUBAR_HOME'] + file_mapping = \ + self.project_creator.sigasi_project._SigasiProject__projectFileCreator._ProjectFileCreator__links + expected = [ + ['include_folders', 'virtual:/virtual', True, False], + [pathlib.Path('include_folders/verilog'), '/somelib/verilog', True, True], + [pathlib.Path('Common Libraries/uvm'), '/home/wmeeus/git/SigasiProjectCreator_github/uvm/src', True, True], + ['Common Libraries', 'virtual:/virtual', True, False], + ['Common Libraries/IEEE', 'sigasiresource:/vhdl/2008/IEEE', True, False], + ['Common Libraries/IEEE Synopsys', 'sigasiresource:/vhdl/2008/IEEE%20Synopsys', True, False], + ['Common Libraries/STD', 'sigasiresource:/vhdl/2008/STD', True, False] + ] + self.assertEqual(file_mapping, expected) + lib_mapping = \ + self.project_creator.sigasi_project._SigasiProject__libraryMappingFileCreator._LibraryMappingFileCreator__entries + lib_expected = { + '/': 'not mapped', + '': 'not mapped', + 'Common Libraries': 'not mapped', + 'Common Libraries/IEEE': 'ieee', + 'Common Libraries/IEEE Synopsys': 'ieee', + 'Common Libraries/STD': 'std', + 'Common Libraries/uvm/uvm_pkg.sv': 'uvm', + 'ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_conv.v': 'foolib', + 'ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_b_downsizer.v': 'foolib', + 'ipstatic/some_protocol_converter_v2_1/hdl/verilog/foo_b2s.v': 'b2s', + 'ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_some_protocol_converter.v': 'work', + 'bd/design_1/ip/design_1_auto_pc_0/simulator/design_1_auto_pc_0.v': 'work', + 'bd/design_1/hdl/design_all.vhd': 'work', + 'tests/test-files/dotFparser/glbl.v': 'work' + } + self.assertEqual(lib_mapping, lib_expected) + self.assertTrue(base_path.joinpath('.project').is_file()) + self.assertTrue(base_path.joinpath('.library_mapping.xml').is_file()) + self.assertTrue(base_path.joinpath('.settings').is_dir()) + expected_includes = [ + pathlib.Path('tests/rtl/phoo/barh/ome/pkg'), + pathlib.Path('tests/bench/verilog'), + pathlib.Path('tests/verilog'), + pathlib.Path('include_folders/verilog'), + pathlib.Path('Common Libraries/uvm') + ] + expected_includes.sort() + actual_includes = the_project.verilog_includes + actual_includes.sort() + print(f'**includes** {expected_includes}') + print(f'##includes## {actual_includes}') + self.assertEqual(actual_includes, expected_includes) + expected_defines = [ + 'SIGASI="yes"', + 'FOOOOOOOH' + ] + self.assertEqual(verilog_defines, expected_defines) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/ProjectFileCreatorTest.py b/tests/ProjectFileCreatorTest.py index 163d9e2..e7b3279 100644 --- a/tests/ProjectFileCreatorTest.py +++ b/tests/ProjectFileCreatorTest.py @@ -5,7 +5,8 @@ """ import unittest -from SigasiProjectCreator.Creator import ProjectFileCreator +from SigasiProjectCreator import VhdlVersion, VerilogVersion +from SigasiProjectCreator.SigasiProject import ProjectFileCreator, project_location_path from string import Template test_template = Template(''' @@ -25,7 +26,11 @@ ${natures}\t\torg.eclipse.xtext.ui.shared.xtextNature \t \t -\t\t +${vhdl_links}${extra_links}\t + +''') + +vhdl_linked_resources = '''\t\t \t\t\tCommon Libraries \t\t\t2 \t\t\tvirtual:/virtual @@ -45,9 +50,7 @@ \t\t\t2 \t\t\tsigasiresource:/vhdl/93/STD \t\t -${extra_links}\t - -''') +''' link_template = Template('''\t\t \t\t\t${name} @@ -69,8 +72,9 @@ def setUp(self): def test_tutorial_project(self): # Vhdl nature is the default - self.assertEqual(test_template.substitute(extra_links="", project_references="", natures=vhdl_nature), - str(self.creator)) + self.creator.set_languages(VhdlVersion.NINETY_THREE, None) + self.assertEqual(test_template.substitute(extra_links="", project_references="", natures=vhdl_nature, + vhdl_links = vhdl_linked_resources), str(self.creator)) def check_links(self, links, natures): extra_links = "" @@ -78,21 +82,36 @@ def check_links(self, links, natures): location = "foobar/" + link self.creator.add_link(link, location) extra_links += link_template.substitute(name=link, file_type=1, location=location) - expected = test_template.substitute(extra_links=extra_links, project_references="", natures=natures) + if vhdl_nature in natures: + linked_resources = vhdl_linked_resources + else: + linked_resources = '' + expected = test_template.substitute(extra_links=extra_links, project_references="", natures=natures, + vhdl_links=linked_resources) self.assertEqual(expected, str(self.creator)) def test_one_verilog_link(self): + self.creator.set_languages(None, VerilogVersion.TWENTY_O_FIVE) self.check_links(["test.sv"], verilog_nature) def test_one_vhdl_link(self): + self.creator.set_languages(VhdlVersion.NINETY_THREE, None) self.check_links(["test.vhdl"], vhdl_nature) def test_mixed_links(self): + self.creator.set_languages(VhdlVersion.NINETY_THREE, VerilogVersion.TWENTY_O_FIVE) self.check_links(["test.vhdl", "test.sv"], verilog_nature + vhdl_nature) + @unittest.skip # project reference is obsolete def test_one_project_reference(self): self.creator.add_project_reference('other_tutorial') project_reference = '''\t\tother_tutorial\n''' expected = test_template.substitute(extra_links="", project_references=project_reference, natures=vhdl_nature) self.assertEqual(expected, str(self.creator)) + + def test_project_location_path(self): + self.assertEqual(project_location_path('/absolute/path'), '/absolute/path') + self.assertEqual(project_location_path('local/relative/path'), 'local/relative/path') + self.assertEqual(project_location_path('../relative/path'), 'PARENT-1-PROJECT_LOC/relative/path') + self.assertEqual(project_location_path('../../../../relative/path'), 'PARENT-4-PROJECT_LOC/relative/path') diff --git a/tests/ProjectOptionsTest.py b/tests/ProjectOptionsTest.py new file mode 100644 index 0000000..fcd2b1c --- /dev/null +++ b/tests/ProjectOptionsTest.py @@ -0,0 +1,141 @@ +import pathlib +import unittest + +from SigasiProjectCreator import VhdlVersion, VerilogVersion, CsvParser +from SigasiProjectCreator.ProjectOptions import ProjectOptions, get_file_type + + +class TestProjectOptions(unittest.TestCase): + def test_dotf_file(self): + self.assertEqual(get_file_type('foo/bahr/hello.f'), 'dotf') + + def test_csv_file(self): + self.assertEqual(get_file_type('foo/rab/hello.csV'), 'csv') + + def test_hdp_file(self): + self.assertEqual(get_file_type('foo/bahr/hello-oh.hdp'), 'hdp') + + def test_file_list(self): + self.assertEqual(get_file_type('foo/bahr/hello.v'), 'filelist') + + def test_constructor(self): + args_parser = ProjectOptions(['foo', 'tests/test-files/compilation_order.csv']) + self.assertTrue(isinstance(args_parser, ProjectOptions)) + + def test_parser_minimal(self): + options = ProjectOptions(['my_project', 'tests/test-files/compilation_order.csv']) + self.assertEqual(options.input_format, 'csv') + self.assertEqual(options.layout, 'in-place') + self.assertEqual(options.destination_folder, pathlib.Path.cwd()) + self.assertFalse(options.enable_vhdl) + self.assertFalse(options.enable_verilog) + self.assertFalse(options.enable_vunit) + self.assertEqual(options.encoding, 'UTF-8') + self.assertEqual(options.vhdl_version, VhdlVersion.TWENTY_O_EIGHT) + self.assertEqual(options.verilog_version, str(VerilogVersion.TWENTY_O_FIVE)) + self.assertFalse(options.force_overwrite) + self.assertFalse(options.skip_check_exists) + self.assertFalse(options.use_relative_path(pathlib.Path.cwd())) + + def test_parser_many_options(self): + command_line_options = ['the_project', 'tests/test-files/tree/compilation_order.csv', '-d', 'tests', + '--layout', 'simulator', + # '--uvm', 'src', + '--use-uvm-home', + '--uvmlib', 'uvm_lib', + '--format', 'csv', + '--mapping', 'folder', + '--enable-vhdl', + '--vhdl-version', '2002', + '--enable-verilog', + '--verilog-as-sv', + '--enable-vunit', + '--work', 'worklib', + '--skip-check-exists', + '--encoding', 'UTF-9', + '--force', + '--rel-path', 'src' + ] + options = ProjectOptions(command_line_options) + self.assertEqual(options.input_format, 'csv') + self.assertEqual(options.layout, 'simulator') + self.assertEqual(options.destination_folder, pathlib.Path('tests').absolute()) + self.assertTrue(options.enable_vhdl) + self.assertTrue(options.enable_verilog) + self.assertTrue(options.enable_vunit) + self.assertEqual(options.encoding, 'UTF-9') + self.assertEqual(options.vhdl_version, VhdlVersion.TWENTY_O_TWO) + self.assertEqual(options.verilog_version, str(VerilogVersion.TWENTY_TWELVE)) + self.assertTrue(options.force_overwrite) + self.assertTrue(options.skip_check_exists) + self.assertFalse(options.use_relative_path(pathlib.Path.cwd())) + self.assertTrue(options.use_relative_path(pathlib.Path('src').absolute())) + self.assertEqual(options.uvm, pathlib.Path('ENV-UVM_HOME')) + self.assertEqual(options.uvm_lib, 'uvm_lib') + self.assertEqual(options.mapping, 'folder') + + def test_parser_multiple_input_files(self): + command_line_options = ['the_project', + 'tests/test-files/dotFparser/continuation.f', 'tests/test-files/dotFparser/filelist.f'] + options = ProjectOptions(command_line_options) + self.assertEqual(options.input_format, 'dotf') + + def test_parser_nonexistent_single_input_file(self): + command_line_options = ['the_project', + 'notexist.f'] + with self.assertRaises(SystemExit) as context: + options = ProjectOptions(command_line_options) + self.assertEqual('1', str(context.exception)) # exit code 1 from argparse error handling + + def test_parser_nonexistent_multiple_input_files(self): + command_line_options = ['the_project', + 'continuation.f', 'tests/test-files/dotFparser/filelist.f'] + with self.assertRaises(SystemExit) as context: + options = ProjectOptions(command_line_options) + self.assertEqual('1', str(context.exception)) # exit code 1 from argparse error handling + + def test_parser_mixed_input_types(self): + command_line_options = ['the_project', + 'tests/test-files/tree/compilation_order.csv', 'tests/test-files/dotFparser/filelist.f'] + with self.assertRaises(SystemExit) as context: + options = ProjectOptions(command_line_options) + self.assertEqual('2', str(context.exception)) # exit code 2 from argparse error handling + + def test_parser_cant_create_destination_folder(self): + command_line_options = ['the_project', 'tests/test-files/tree/compilation_order.csv', + '--destination', 'phoo/bahr'] + with self.assertRaises(SystemExit) as context: + options = ProjectOptions(command_line_options) + print(f'*ERROR* {context.exception}') + self.assertEqual('1', str(context.exception)) # exit code 1 from argparse error handling + + def test_parser_do_create_destination_folder(self): + command_line_options = ['the_project', 'tests/test-files/tree/compilation_order.csv', '-d', 'phoobahr'] + options = ProjectOptions(command_line_options) + destination_folder = pathlib.Path('phoobahr') + self.assertTrue(destination_folder.is_dir()) + destination_folder.rmdir() + + # def test_parser_parse_file(self): + # command_line_options = ['the_project', 'tests/test-files/tree/compilation_order.csv'] + # options = ProjectOptions(command_line_options) + # options.parse_args(command_line_options) + # project_name, input_file, dest_folder, project_entries = options.parse_input_file(CsvParser.parse_file) + # self.assertEqual(project_name, 'the_project') + # self.assertEqual(input_file, 'tests/test-files/tree/compilation_order.csv') + # self.assertEqual(dest_folder, pathlib.Path.cwd().absolute()) + # self.assertTrue(isinstance(project_entries, dict)) + # + # def test_parser_parse_list(self): + # command_line_options = ['the_project', 'tests/test-files/tutorial/clock_generator.vhd,tests/test-files/tutorial/dut.vhd'] + # args_parser = ProjectOptions() + # args_parser.parse_args(command_line_options) + # project_name, input_file, dest_folder, project_entries = args_parser.parse_input_file(None) + # self.assertEqual(project_name, 'the_project') + # self.assertTrue(input_file is None) + # self.assertEqual(dest_folder, pathlib.Path.cwd().absolute()) + # self.assertTrue(isinstance(project_entries, dict)) + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/SettingsFileWriterTest.py b/tests/SettingsFileWriterTest.py index 2ff382b..bd18dc9 100644 --- a/tests/SettingsFileWriterTest.py +++ b/tests/SettingsFileWriterTest.py @@ -24,24 +24,22 @@ def tearDown(self): def test_simple_write(self): self.path = tempfile.mktemp() content = "some content" - write(".", self.path, content) + write(".", self.path, content, True) self.assertCorrect(self.path, content) def test_xml_write(self): self.path = tempfile.mktemp(suffix=".xml") content = "test\ntest2\n" - write(".", self.path, content) + write(".", self.path, content, True) self.assertCorrect(self.path, content) - @unittest.expectedFailure def test_non_existent_parent_write(self): self.path = self.prefix + "/" + self.prefix content = "some content" - try: - write(".", self.path, content) - except IOError: - self.path = None - raise AssertionError("Path doesn't exist") + with self.assertRaises(IOError) as context: + write(".", self.path, content, True) + print(f'Exception: {str(context.exception)}') + self.assertTrue('No such file or directory' in str(context.exception)) def test_existent_parent_write(self): tempdir = None @@ -49,7 +47,7 @@ def test_existent_parent_write(self): tempdir = tempfile.mkdtemp() self.path = tempfile.mktemp(dir=tempdir) content = "some content" - write(".", self.path, content) + write(".", self.path, content, True) self.assertCorrect(self.path, content) finally: # Teardown diff --git a/tests/SigasiProjectCreatorTest.py b/tests/SigasiProjectTest.py similarity index 60% rename from tests/SigasiProjectCreatorTest.py rename to tests/SigasiProjectTest.py index b34b926..eef1463 100644 --- a/tests/SigasiProjectCreatorTest.py +++ b/tests/SigasiProjectTest.py @@ -5,23 +5,26 @@ """ import unittest -import SigasiProjectCreator.Creator as sPC +import SigasiProjectCreator.SigasiProject as sPC import SigasiProjectCreator.VerilogVersion as VerilogVersion import SigasiProjectCreator.VhdlVersion as VhdlVersion -from SigasiProjectCreator.Creator import SigasiProjectCreator +from SigasiProjectCreator.ProjectOptions import ProjectOptions +from SigasiProjectCreator.SigasiProject import SigasiProject -class SigasiProjectCreatorTest(unittest.TestCase): +class SigasiProjectTest(unittest.TestCase): # No teardown needed, a new creator is created every instance def setUp(self): - self.creator = SigasiProjectCreator('tutorial') + command_line_options = ['the_project', 'tests/test-files/tree/compilation_order.csv'] + self.options = ProjectOptions(command_line_options) + self.creator = SigasiProject(self.options) def test_check_hdl_versions_both_none(self): with self.assertRaises(ValueError) as exc: sPC.check_hdl_versions(None, None) self.assertTrue( - '''Only 93, 2002, 2008 is/are allowed as VHDL version number. -Only v2005 is/are allowed as Verilog version number.''' == str(exc.exception)) + '''Only 93, 2002, 2008, 2019 is/are allowed as VHDL version number. +Only v2005, sv2012 is/are allowed as Verilog version number.''' == str(exc.exception)) def test_check_hdl_versions_vhdl_none(self): sPC.check_hdl_versions(None, VerilogVersion.TWENTY_O_FIVE) @@ -35,16 +38,16 @@ def test_check_hdl_versions_both_correct(self): def test_check_hdl_versions_vhdl_wrong(self): with self.assertRaises(ValueError) as exc: sPC.check_hdl_versions(VerilogVersion.TWENTY_O_FIVE, VerilogVersion.TWENTY_O_FIVE) - self.assertTrue("Only 93, 2002, 2008 is/are allowed as VHDL version number." == str(exc.exception)) + self.assertTrue("Only 93, 2002, 2008, 2019 is/are allowed as VHDL version number." == str(exc.exception)) def test_check_hdl_versions_verilog_wrong(self): with self.assertRaises(ValueError) as exc: sPC.check_hdl_versions(VhdlVersion.NINETY_THREE, VhdlVersion.NINETY_THREE) - self.assertTrue("Only v2005 is/are allowed as Verilog version number." == str(exc.exception)) + self.assertTrue("Only v2005, sv2012 is/are allowed as Verilog version number." == str(exc.exception)) def test_check_hdl_versions_both_wrong(self): with self.assertRaises(ValueError) as exc: sPC.check_hdl_versions(VerilogVersion.TWENTY_O_FIVE, VhdlVersion.NINETY_THREE) self.assertTrue( - '''Only 93, 2002, 2008 is/are allowed as VHDL version number. -Only v2005 is/are allowed as Verilog version number.''' == str(exc.exception)) + '''Only 93, 2002, 2008, 2019 is/are allowed as VHDL version number. +Only v2005, sv2012 is/are allowed as Verilog version number.''' == str(exc.exception)) diff --git a/tests/parseFileTest.py b/tests/parseFileTest.py index b384405..4869b2e 100644 --- a/tests/parseFileTest.py +++ b/tests/parseFileTest.py @@ -107,8 +107,8 @@ def test_wildcard(self): '-maxdelays', '-sdf_cmd_file', './sdf_cmd.cmd', - '/usr/eda/dk/vendor/tech/verilog/*.v', - '../../synthesis/image_average.v', + '../tutorial/*.vhd', + '/absolute/path/synthesis/image_average.v', '../tb/tb_image.vhd', '../../rtl/image_ram.vhd' ] diff --git a/tests/test-files/dotFparser/features.f b/tests/test-files/dotFparser/features.f new file mode 100644 index 0000000..c031559 --- /dev/null +++ b/tests/test-files/dotFparser/features.f @@ -0,0 +1,33 @@ +//////////////////////////////////////////////////////////////////////////////// +// Comments // +//////////////////////////////////////////////////////////////////////////////// + +-makelib foolib -sv \ + ../../../ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_conv.v + ../../../ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_b_downsizer.v +-endlib +-makelib ies_lib/b2s -sv \ + ../../../ipstatic/some_protocol_converter_v2_1/hdl/verilog/foo_b2s.v +-endlib +../../../ipstatic/some_protocol_converter_v2_1/hdl/verilog/some_protocol_converter_v2_1_some_protocol_converter.v +../../../bd/design_1/ip/design_1_auto_pc_0/$(SIM)/design_1_auto_pc_0.v +../../../bd/design_1/hdl/design_all.vhd + +-smartorder -work work -V93 -top tb_image -gui -access +rw -maxdelays -sdf_cmd_file ./sdf_cmd.cmd +glbl.v +/* + * Block comments + * More comments + */ + ++incdir+../../rtl/$FUBAR_HOME/pkg/ + +//============================================================================= +// Intermediate comments +//============================================================================= + ++incdir+../../bench/verilog/ ++incdir+../../verilog/ ++incdir+/somelib/verilog/ ++define+SIGASI="yes" ++define+FOOOOOOOH diff --git a/tests/test-files/dotFparser/wildcard.f b/tests/test-files/dotFparser/wildcard.f index ac6f4de..06723cd 100644 --- a/tests/test-files/dotFparser/wildcard.f +++ b/tests/test-files/dotFparser/wildcard.f @@ -1,5 +1,5 @@ -smartorder -work work -V93 -top tb_image -gui -access +rw -maxdelays -sdf_cmd_file ./sdf_cmd.cmd -/usr/eda/dk/vendor/tech/verilog/*.v -../../synthesis/image_average.v +../tutorial/*.vhd +/absolute/path/synthesis/image_average.v ../tb/tb_image.vhd ../../rtl/image_ram.vhd