Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Config finder make target #1328

Merged
merged 4 commits into from
Mar 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/workflows/chipyard-full-flow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,19 @@ jobs:
export MAKEFLAGS="-j32"
./build-setup.sh -f

run-cfg-finder:
name: run-cfg-finder
needs: [setup-repo]
runs-on: ferry
steps:
- name: Run config finder
run: |
cd ${{ env.REMOTE_WORK_DIR }}
eval "$(conda shell.bash hook)"
source env.sh
cd sims/verilator
make find-config-fragments

run-tutorial:
name: run-tutorial
needs: [setup-repo]
Expand Down
16 changes: 15 additions & 1 deletion common.mk
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ HELP_COMMANDS += \
" run-tests = run all assembly and benchmark tests" \
" launch-sbt = start sbt terminal" \
" {shutdown,start}-sbt-server = shutdown or start sbt server if using ENABLE_SBT_THIN_CLIENT" \
" find-config-fragments = list all config. fragments and their locations (recursive up to CONFIG_FRAG_LEVELS=$(CONFIG_FRAG_LEVELS))"

#########################################################################################
# include additional subproject make fragments
Expand Down Expand Up @@ -375,8 +376,21 @@ start-sbt-server: check-thin-client
cd $(base_dir) && $(SBT) "exit"

#########################################################################################
# print help text
# print help text (and other help)
#########################################################################################
# helper to add newlines (avoid bash argument too long)
define \n


endef

CONFIG_FRAG_LEVELS ?= 3
.PHONY: find-config-fragments
find-config-fragments: $(SCALA_SOURCES)
rm -rf /tmp/scala_files.f
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm afraid of this causing problems for shared installations. Can you make this dump the file within the repo?

@$(foreach file,$(SCALA_SOURCES),echo $(file) >> /tmp/scala_files.f${\n})
$(base_dir)/scripts/config-finder.py -l $(CONFIG_FRAG_LEVELS) /tmp/scala_files.f

.PHONY: help
help:
@for line in $(HELP_LINES); do echo "$$line"; done
Expand Down
6 changes: 6 additions & 0 deletions docs/Customization/Keys-Traits-Configs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,9 @@ We can use this config fragment when composing our configs.

.. note::
Readers who want more information on the configuration system may be interested in reading :ref:`cdes`.

Chipyard Config Fragments
-------------------------

For discoverability, users can run ``make find-config-fragments`` to see a list of config. fragments
(config. fragments that match "class NAME extends CONFIG\n" on a single line and a subset of their children) and their file path in a fully initialized Chipyard repository.
76 changes: 76 additions & 0 deletions scripts/config-finder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/usr/bin/env python3

import argparse
import subprocess
from collections import defaultdict
import re
from copy import deepcopy
import os

cy_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))

# from https://gist.github.com/angstwad/bf22d1822c38a92ec0a9
def deep_merge(a: dict, b: dict) -> dict:
"""Merge two dicts and return a singular dict"""
result = deepcopy(a)
for bk, bv in b.items():
av = result.get(bk)
if isinstance(av, dict) and isinstance(bv, dict):
result[bk] = deep_merge(av, bv)
else:
result[bk] = deepcopy(bv)
return result

if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Pretty print all configs given a filelist of scala files')
parser.add_argument('FILE', type=str, help='Filelist of scala files to search within')
parser.add_argument('-l', '--levels', default=0, type=int, help='Number of levels to recursively look for configs')
args = parser.parse_args()

files = []
with open(args.FILE, 'r') as f:
files = f.read().splitlines()

cmd = ['grep', '-o', r"class \+.* \+extends \+Config"] + files
r = subprocess.run(cmd, check=True, capture_output=True)

base_file_path_dict = defaultdict(list)
for l in r.stdout.decode("UTF-8").splitlines():
match = re.match(r"^(.*):class +([a-zA-Z_$][a-zA-Z\d_$]*).* +extends", l)
if match:
base_file_path_dict[match.group(1)].append(match.group(2))

levels = []
for level in range(args.levels):
if level == 0:
# use the base
dict_to_use = base_file_path_dict
else:
# use the level-1 dict
assert len(levels) > 0
dict_to_use = levels[-1]

file_path_dict = defaultdict(list)

for configs in dict_to_use.values():
for config in configs:
cmd = ['grep', '-o', r"class \+.* \+extends \+" + f"{config}"] + files
r = subprocess.run(cmd, capture_output=True)

for l in r.stdout.decode("UTF-8").splitlines():
match = re.match(r"^(.*):class +([a-zA-Z_$][a-zA-Z\d_$]*).* +extends", l)
if match:
file_path_dict[match.group(1)].append(match.group(2))

levels.append(file_path_dict)

final_dict = base_file_path_dict
for dct in levels:
final_dict = deep_merge(final_dict, dct)

print(f"Finding all one-line config. fragments (up to {args.levels} levels)\n")
for k, v in final_dict.items():
print(f"{k.replace(cy_path, 'chipyard')}:")
for e in v:
print(f" {e}")
print("")