Skip to content

Commit

Permalink
improved inheritance
Browse files Browse the repository at this point in the history
improved inheritance
centralized inheritance calls
remove extra import
fixed lint issues
  • Loading branch information
sifferman committed Sep 26, 2023
1 parent 03910d0 commit 5b68751
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 5 deletions.
36 changes: 36 additions & 0 deletions fusesoc/capi2/inheritance.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copyright FuseSoC contributors
# Licensed under the 2-Clause BSD License, see LICENSE for details.
# SPDX-License-Identifier: BSD-2-Clause

import copy
import re

from fusesoc import utils


class Inheritance:
MERGE_OPERATOR = "<<__FUSESOC_MERGE_OVERLOAD__<<"


def yaml_merge_2_fusesoc_merge(capi):
"""
Replace YAML merge key operator (<<) with FuseSoC merge operator
"""
return re.sub(r"<<(?!<)", Inheritance.MERGE_OPERATOR, capi)


def elaborate_inheritance(capi):
if not isinstance(capi, dict):
return capi

for key, value in capi.items():
if isinstance(value, dict):
capi[key] = Inheritance.elaborate_inheritance(copy.deepcopy(value))

parent = capi.pop(Inheritance.MERGE_OPERATOR, {})
if isinstance(parent, dict):
capi = utils.merge_dict(parent, capi, concat_list_appends_only=True)
else:
raise SyntaxError("Invalid use of inheritance operator")

return capi
17 changes: 12 additions & 5 deletions fusesoc/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
from yaml import SafeDumper as YamlDumper
from yaml import SafeLoader as YamlLoader

from fusesoc.capi2.inheritance import Inheritance

logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -159,15 +161,18 @@ def yaml_fread(filepath, resolve_env_vars=False, remove_preamble=False):
with open(filepath) as f:
if remove_preamble:
f.readline()
return yaml_read(f, resolve_env_vars)
return yaml_read(f.read(), resolve_env_vars)


def yaml_read(data, resolve_env_vars=False):
try:
data = Inheritance.yaml_merge_2_fusesoc_merge(data)
capi_data = {}
if resolve_env_vars:
return yaml.load(os.path.expandvars(data.read()), Loader=YamlLoader)
capi_data = yaml.load(os.path.expandvars(data), Loader=YamlLoader)
else:
return yaml.load(data, Loader=YamlLoader)
capi_data = yaml.load(data, Loader=YamlLoader)
return Inheritance.elaborate_inheritance(capi_data)
except (yaml.parser.ParserError, yaml.scanner.ScannerError) as e:
raise SyntaxError(str(e))

Expand All @@ -176,11 +181,13 @@ def yaml_dump(data):
return yaml.dump(data)


def merge_dict(d1, d2):
def merge_dict(d1, d2, concat_list_appends_only=False):
for key, value in d2.items():
if isinstance(value, dict):
d1[key] = merge_dict(d1.get(key, {}), value)
elif isinstance(value, list):
elif isinstance(value, list) and (
not concat_list_appends_only or key.endswith("_append")
):
d1[key] = d1.get(key, []) + value
else:
d1[key] = value
Expand Down
53 changes: 53 additions & 0 deletions tests/capi2_cores/parser/inheritance.core
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
CAPI=2:
# Copyright FuseSoC contributors
# Licensed under the 2-Clause BSD License, see LICENSE for details.
# SPDX-License-Identifier: BSD-2-Clause

name: ::inheritance:0
filesets:
fileset_a:
files:
- 1.txt
- 2.txt
- 3.txt
fileset_b:
files:
- 4.txt
- 5.txt
- 6.txt
fileset_c:
files:
- 7.txt
- 8.txt
- 9.txt

targets:
default: &default
filesets:
- fileset_a
child: &child
<<: *default
filesets_append:
- fileset_b
grandchild: &grandchild
<<: *child
filesets_append:
- fileset_c
child2: &child2
<<: *default
filesets:
- fileset_b
filesets_append:
- fileset_c

subfield: &subfield
tools:
verilator:
mode: cc
verilator_options:
- --timing
subfield_child:
<<: *subfield
tools:
verilator:
mode: lint-only
46 changes: 46 additions & 0 deletions tests/test_capi2.py
Original file line number Diff line number Diff line change
Expand Up @@ -734,3 +734,49 @@ def test_syntax_error():
with pytest.raises(SyntaxError) as excinfo:
parser.read(core_file)
assert "did not find expected node content" in str(excinfo.value)


def test_inheritance():
import os

from fusesoc.capi2.coreparser import Core2Parser

core_file = os.path.join(tests_dir, "capi2_cores", "parser", "inheritance.core")

parser = Core2Parser()
assert parser.get_version() == 2
assert parser.get_preamble() == "CAPI=2:"

expected = {
"name": "::inheritance:0",
"filesets": {
"fileset_a": {"files": ["1.txt", "2.txt", "3.txt"]},
"fileset_b": {"files": ["4.txt", "5.txt", "6.txt"]},
"fileset_c": {"files": ["7.txt", "8.txt", "9.txt"]},
},
"targets": {
"default": {"filesets": ["fileset_a"]},
"child": {"filesets": ["fileset_a"], "filesets_append": ["fileset_b"]},
"grandchild": {
"filesets": ["fileset_a"],
"filesets_append": ["fileset_b", "fileset_c"],
},
"child2": {"filesets": ["fileset_b"], "filesets_append": ["fileset_c"]},
"subfield": {
"tools": {
"verilator": {"mode": "cc", "verilator_options": ["--timing"]}
}
},
"subfield_child": {
"tools": {
"verilator": {
"mode": "lint-only",
"verilator_options": ["--timing"],
}
}
},
},
}

capi2_data = parser.read(core_file)
assert expected == capi2_data

0 comments on commit 5b68751

Please sign in to comment.