siliconcompiler 0.33.2__py3-none-any.whl → 0.34.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. siliconcompiler/__init__.py +2 -0
  2. siliconcompiler/_metadata.py +1 -1
  3. siliconcompiler/apps/sc_issue.py +5 -3
  4. siliconcompiler/apps/sc_remote.py +0 -17
  5. siliconcompiler/checklist.py +1 -1
  6. siliconcompiler/core.py +34 -47
  7. siliconcompiler/dependencyschema.py +392 -0
  8. siliconcompiler/design.py +664 -0
  9. siliconcompiler/flowgraph.py +32 -1
  10. siliconcompiler/package/__init__.py +383 -223
  11. siliconcompiler/package/git.py +75 -77
  12. siliconcompiler/package/github.py +70 -97
  13. siliconcompiler/package/https.py +77 -93
  14. siliconcompiler/packageschema.py +260 -0
  15. siliconcompiler/pdk.py +2 -2
  16. siliconcompiler/remote/client.py +15 -3
  17. siliconcompiler/report/dashboard/cli/board.py +1 -1
  18. siliconcompiler/scheduler/__init__.py +3 -1382
  19. siliconcompiler/scheduler/docker.py +268 -0
  20. siliconcompiler/scheduler/run_node.py +10 -16
  21. siliconcompiler/scheduler/scheduler.py +308 -0
  22. siliconcompiler/scheduler/schedulernode.py +934 -0
  23. siliconcompiler/scheduler/slurm.py +147 -163
  24. siliconcompiler/scheduler/taskscheduler.py +39 -52
  25. siliconcompiler/schema/__init__.py +3 -3
  26. siliconcompiler/schema/baseschema.py +234 -10
  27. siliconcompiler/schema/editableschema.py +4 -0
  28. siliconcompiler/schema/journal.py +210 -0
  29. siliconcompiler/schema/namedschema.py +31 -2
  30. siliconcompiler/schema/parameter.py +14 -1
  31. siliconcompiler/schema/parametervalue.py +1 -34
  32. siliconcompiler/schema/schema_cfg.py +210 -349
  33. siliconcompiler/tool.py +61 -20
  34. siliconcompiler/tools/builtin/concatenate.py +2 -2
  35. siliconcompiler/tools/builtin/verify.py +1 -2
  36. siliconcompiler/tools/openroad/scripts/common/procs.tcl +27 -25
  37. siliconcompiler/tools/vpr/route.py +69 -0
  38. siliconcompiler/toolscripts/_tools.json +4 -4
  39. siliconcompiler/utils/__init__.py +2 -23
  40. siliconcompiler/utils/flowgraph.py +5 -5
  41. siliconcompiler/utils/logging.py +2 -1
  42. {siliconcompiler-0.33.2.dist-info → siliconcompiler-0.34.0.dist-info}/METADATA +4 -3
  43. {siliconcompiler-0.33.2.dist-info → siliconcompiler-0.34.0.dist-info}/RECORD +47 -42
  44. siliconcompiler/scheduler/docker_runner.py +0 -254
  45. siliconcompiler/schema/journalingschema.py +0 -242
  46. {siliconcompiler-0.33.2.dist-info → siliconcompiler-0.34.0.dist-info}/WHEEL +0 -0
  47. {siliconcompiler-0.33.2.dist-info → siliconcompiler-0.34.0.dist-info}/entry_points.txt +0 -0
  48. {siliconcompiler-0.33.2.dist-info → siliconcompiler-0.34.0.dist-info}/licenses/LICENSE +0 -0
  49. {siliconcompiler-0.33.2.dist-info → siliconcompiler-0.34.0.dist-info}/top_level.txt +0 -0
@@ -3,6 +3,7 @@ from siliconcompiler._common import NodeStatus, SiliconCompilerError
3
3
  from siliconcompiler.utils import sc_open
4
4
  from siliconcompiler.schema_obj import SchemaTmp as Schema
5
5
 
6
+ from siliconcompiler.design import DesignSchema
6
7
  from siliconcompiler.record import RecordSchema
7
8
  from siliconcompiler.metric import MetricSchema
8
9
  from siliconcompiler.pdk import PDKSchema
@@ -30,6 +31,7 @@ __all__ = [
30
31
  "Checklist",
31
32
  "Schema",
32
33
  "sc_open",
34
+ "DesignSchema",
33
35
  "RecordSchema",
34
36
  "MetricSchema",
35
37
  "PDKSchema",
@@ -1,5 +1,5 @@
1
1
  # Version number following semver standard.
2
- version = '0.33.2'
2
+ version = '0.34.0'
3
3
 
4
4
  # Default server address for remote runs, if unspecified.
5
5
  default_server = 'https://server.siliconcompiler.com'
@@ -4,7 +4,7 @@ import os
4
4
  import siliconcompiler
5
5
  import tarfile
6
6
  import json
7
- from siliconcompiler.scheduler import _runtask, _executenode
7
+ from siliconcompiler.scheduler.schedulernode import SchedulerNode
8
8
  from siliconcompiler.utils.issue import generate_testcase
9
9
  from siliconcompiler.tools._common import get_tool_task
10
10
 
@@ -153,8 +153,10 @@ To run a testcase, use:
153
153
  # Run task
154
154
  # Rerun setup task, assumed to be running in its own thread so
155
155
  # multiprocess is not needed
156
- flow = chip.get('option', 'flow')
157
- _runtask(chip, flow, step, index, _executenode, replay=True)
156
+ SchedulerNode(chip,
157
+ step,
158
+ index,
159
+ replay=True).run()
158
160
 
159
161
  return 0
160
162
 
@@ -1,12 +1,9 @@
1
1
  # Copyright 2023 Silicon Compiler Authors. All Rights Reserved.
2
- import os
3
2
  import sys
4
3
 
5
4
  from siliconcompiler import Chip
6
5
  from siliconcompiler import SiliconCompilerError
7
6
  from siliconcompiler.remote.client import Client, ConfigureClient
8
- from siliconcompiler.scheduler import _finalize_run
9
- from siliconcompiler.flowgraph import RuntimeFlowgraph
10
7
 
11
8
 
12
9
  def main():
@@ -169,20 +166,6 @@ To delete a job, use:
169
166
  chip.logger.error(f'{e}')
170
167
  return 1
171
168
 
172
- # Wrap up run
173
- runtime = RuntimeFlowgraph(
174
- chip.schema.get("flowgraph", flow, field='schema'),
175
- from_steps=chip.get('option', 'from'),
176
- to_steps=chip.get('option', 'to'),
177
- prune_nodes=chip.get('option', 'prune'))
178
- for step, index in runtime.get_nodes():
179
- manifest = os.path.join(chip.getworkdir(step=step, index=index),
180
- 'outputs',
181
- f'{chip.design}.pkg.json')
182
- if os.path.exists(manifest):
183
- chip.schema.read_journal(manifest)
184
- _finalize_run(chip)
185
-
186
169
  # Summarize the run.
187
170
  chip.summary()
188
171
 
@@ -4,7 +4,7 @@ from siliconcompiler.schema.utils import trim
4
4
 
5
5
 
6
6
  class ChecklistSchema(NamedSchema):
7
- def __init__(self, name=None):
7
+ def __init__(self, name):
8
8
  super().__init__(name=name)
9
9
 
10
10
  schema_checklist(self)
siliconcompiler/core.py CHANGED
@@ -18,9 +18,9 @@ import csv
18
18
  import yaml
19
19
  from inspect import getfullargspec
20
20
  from siliconcompiler import Schema
21
- from siliconcompiler.schema import SCHEMA_VERSION, PerNode, JournalingSchema, EditableSchema
21
+ from siliconcompiler.schema import SCHEMA_VERSION, PerNode, Journal, EditableSchema
22
22
  from siliconcompiler.schema.parametertype import NodeType
23
- from siliconcompiler.schema.parametervalue import FileNodeValue, PathNodeValue
23
+ from siliconcompiler.schema.parametervalue import FileNodeValue
24
24
  from siliconcompiler.schema import utils as schema_utils
25
25
  from siliconcompiler import utils
26
26
  from siliconcompiler.utils.logging import SCColorLoggerFormatter, \
@@ -34,9 +34,8 @@ from siliconcompiler.report import _generate_summary_image, _open_summary_image
34
34
  from siliconcompiler.report.dashboard.web import WebDashboard
35
35
  from siliconcompiler.report.dashboard.cli import CliDashboard
36
36
  from siliconcompiler.report.dashboard import DashboardType
37
- from siliconcompiler import package as sc_package
38
37
  import glob
39
- from siliconcompiler.scheduler import run as sc_runner
38
+ from siliconcompiler.scheduler.scheduler import Scheduler
40
39
  from siliconcompiler.utils.flowgraph import _check_flowgraph_io, _get_flowgraph_information
41
40
  from siliconcompiler.tools._common import get_tool_task
42
41
  from types import FunctionType, ModuleType
@@ -1102,7 +1101,8 @@ class Chip:
1102
1101
  '''
1103
1102
 
1104
1103
  if package:
1105
- filename = os.path.join(sc_package.path(self, package), filename)
1104
+ resolvers = self.get("package", field="schema").get_resolvers()
1105
+ filename = os.path.join(resolvers[package](), filename)
1106
1106
 
1107
1107
  if not os.path.isfile(filename):
1108
1108
  raise FileNotFoundError(filename)
@@ -1110,10 +1110,16 @@ class Chip:
1110
1110
  package_name = f'flist-{os.path.basename(filename)}'
1111
1111
  package_dir = os.path.dirname(os.path.abspath(filename))
1112
1112
 
1113
- env_vars = utils.get_env_vars(self, None, None)
1114
-
1115
1113
  def __make_path(rel, path):
1116
- path = PathNodeValue.resolve_env_vars(path, envvars=env_vars)
1114
+ env_save = os.environ.copy()
1115
+ schema_env = {}
1116
+ for env in self.getkeys('option', 'env'):
1117
+ schema_env[env] = self.get('option', 'env', env)
1118
+ os.environ.update(schema_env)
1119
+ path = os.path.expandvars(path)
1120
+ path = os.path.expanduser(path)
1121
+ os.environ.clear()
1122
+ os.environ.update(env_save)
1117
1123
  if os.path.isabs(path):
1118
1124
  if path.startswith(rel):
1119
1125
  return os.path.relpath(path, rel), package_name
@@ -1380,18 +1386,17 @@ class Chip:
1380
1386
  else:
1381
1387
  search_paths = [self.cwd]
1382
1388
 
1383
- env_vars = utils.get_env_vars(self, step, index)
1389
+ resolvers = self.get("package", field="schema").get_resolvers()
1384
1390
  for (dependency, path) in zip(dependencies, paths):
1385
1391
  faux_param = FileNodeValue()
1386
1392
  faux_param.set(path)
1387
1393
  try:
1388
1394
  if dependency:
1389
1395
  faux_param.set(dependency, field='package')
1390
- faux_search = [os.path.abspath(os.path.join(sc_package.path(self, dependency)))]
1396
+ faux_search = [resolvers[dependency]()]
1391
1397
  else:
1392
1398
  faux_search = search_paths
1393
1399
  resolved = faux_param.resolve_path(
1394
- envvars=env_vars,
1395
1400
  search=faux_search,
1396
1401
  collection_dir=collection_dir)
1397
1402
  except FileNotFoundError:
@@ -1547,39 +1552,19 @@ class Chip:
1547
1552
  True if all file paths are valid, otherwise False.
1548
1553
  '''
1549
1554
 
1550
- allkeys = self.allkeys()
1551
- error = False
1552
- for keypath in allkeys:
1553
- paramtype = self.get(*keypath, field='type')
1554
- is_file = 'file' in paramtype
1555
- is_dir = 'dir' in paramtype
1556
- is_list = paramtype.startswith('[')
1557
-
1558
- if is_file or is_dir:
1559
- if keypath[-2:] == ('option', 'builddir'):
1560
- # Skip ['option', 'builddir'] since it will get created by run() if it doesn't
1561
- # exist
1562
- continue
1563
-
1564
- for check_files, step, index in self.schema.get(*keypath, field=None).getvalues():
1565
- if not check_files:
1566
- continue
1555
+ ignore_keys = []
1556
+ for keypath in self.allkeys():
1557
+ if keypath[-2:] == ('option', 'builddir'):
1558
+ ignore_keys.append(keypath)
1567
1559
 
1568
- if not is_list:
1569
- check_files = [check_files]
1570
-
1571
- for idx, check_file in enumerate(check_files):
1572
- found_file = self.__find_files(*keypath,
1573
- missing_ok=True,
1574
- step=step, index=index,
1575
- list_index=idx)
1576
- if is_list:
1577
- found_file = found_file[0]
1578
- if not found_file:
1579
- self.logger.error(f"Parameter {keypath} path {check_file} is invalid")
1580
- error = True
1560
+ package_map = self.get("package", field="schema").get_resolvers()
1581
1561
 
1582
- return not error
1562
+ return self.schema.check_filepaths(
1563
+ ignore_keys=ignore_keys,
1564
+ logger=self.logger,
1565
+ packages=package_map,
1566
+ collection_dir=self._getcollectdir(),
1567
+ cwd=self.cwd)
1583
1568
 
1584
1569
  ###########################################################################
1585
1570
  def check_manifest(self):
@@ -1796,10 +1781,7 @@ class Chip:
1796
1781
  schema.write_manifest(filepath)
1797
1782
  return
1798
1783
 
1799
- tcl_record = False
1800
- if isinstance(schema, JournalingSchema):
1801
- tcl_record = "get" in schema.get_journaling_types()
1802
- schema = schema.get_base_schema()
1784
+ tcl_record = "get" in Journal.access(schema).get_types()
1803
1785
 
1804
1786
  is_csv = re.search(r'(\.csv)(\.gz)*$', filepath)
1805
1787
 
@@ -3201,9 +3183,14 @@ class Chip:
3201
3183
  >>> run()
3202
3184
  Runs the execution flow defined by the flowgraph dictionary.
3203
3185
  '''
3186
+ from siliconcompiler.remote.client import ClientScheduler
3204
3187
 
3205
3188
  try:
3206
- sc_runner(self)
3189
+ if self.get('option', 'remote'):
3190
+ scheduler = ClientScheduler(self)
3191
+ else:
3192
+ scheduler = Scheduler(self)
3193
+ scheduler.run()
3207
3194
  except Exception as e:
3208
3195
  if raise_exception:
3209
3196
  raise e
@@ -0,0 +1,392 @@
1
+ import os.path
2
+
3
+ from siliconcompiler.schema.baseschema import BaseSchema
4
+ from siliconcompiler.schema.editableschema import EditableSchema
5
+ from siliconcompiler.schema.parameter import Parameter, Scope
6
+ from siliconcompiler.schema.namedschema import NamedSchema
7
+ from siliconcompiler.schema.utils import trim
8
+
9
+ from siliconcompiler.package import Resolver
10
+
11
+
12
+ class DependencySchema(BaseSchema):
13
+ '''
14
+ Schema extension to add :meth:`.add_dep` capability to a schema section.
15
+ '''
16
+
17
+ def __init__(self):
18
+ super().__init__()
19
+
20
+ self.__deps = {}
21
+
22
+ schema = EditableSchema(self)
23
+ schema.insert(
24
+ "deps",
25
+ Parameter(
26
+ '[str]',
27
+ scope=Scope.GLOBAL,
28
+ lock=True,
29
+ shorthelp="List of dependencies",
30
+ help="List of named object dependencies included via add_dep()."))
31
+
32
+ schema.insert(
33
+ 'package', 'default', 'root',
34
+ Parameter(
35
+ 'str',
36
+ scope=Scope.GLOBAL,
37
+ shorthelp="Package: package root",
38
+ example=[
39
+ "api: chip.set('source', "
40
+ "'freepdk45_data', 'path', 'ssh://git@github.com/siliconcompiler/freepdk45/')"],
41
+ help=trim("""
42
+ Package root path, this points the location where the package data can be
43
+ retrieved or accessed.
44
+ Allowed roots:
45
+
46
+ * /path/on/network/drive
47
+ * file:///path/on/network/drive
48
+ * git+https://github.com/xyz/xyz
49
+ * git://github.com/xyz/xyz
50
+ * git+ssh://github.com/xyz/xyz
51
+ * ssh://github.com/xyz/xyz
52
+ * https://github.com/xyz/xyz/archive
53
+ * https://zeroasic.com/xyz.tar.gz
54
+ * github://siliconcompiler/lambdapdk/v1.0/asap7.tar.gz
55
+ * python://siliconcompiler
56
+ """)))
57
+
58
+ schema.insert(
59
+ 'package', 'default', 'tag',
60
+ Parameter(
61
+ 'str',
62
+ scope=Scope.GLOBAL,
63
+ shorthelp="Package: package tag/version",
64
+ example=[
65
+ "api: chip.set('source', 'freepdk45_data', 'ref', '07ec4aa')"],
66
+ help=trim("""
67
+ Package reference tag. The meaning of the this tag depends on the context of
68
+ the root.
69
+ For git, this can be a tag, branch, or commit id. For https this is the version
70
+ of the file that will be downloaded.
71
+ """)))
72
+
73
+ def _from_dict(self, manifest, keypath, version=None):
74
+ self.set("deps", False, field="lock")
75
+ ret = super()._from_dict(manifest, keypath, version)
76
+ self.set("deps", True, field="lock")
77
+ return ret
78
+
79
+ def add_dep(self, obj: NamedSchema, clobber: bool = True) -> bool:
80
+ """
81
+ Adds a module to this object.
82
+
83
+ Args:
84
+ obj (:class:`NamedSchema`): Module to add
85
+ clobber (bool): If true will insert the object and overwrite any
86
+ existing with the same name
87
+
88
+ Returns:
89
+ True if object was imported, otherwise false.
90
+ """
91
+
92
+ if not isinstance(obj, NamedSchema):
93
+ raise TypeError(f"Cannot add an object of type: {type(obj)}")
94
+
95
+ if not clobber and obj.name() in self.__deps:
96
+ return False
97
+
98
+ if obj.name() not in self.__deps:
99
+ self.set("deps", False, field="lock")
100
+ self.add("deps", obj.name())
101
+ self.set("deps", True, field="lock")
102
+
103
+ self.__deps[obj.name()] = obj
104
+ obj._reset()
105
+
106
+ return True
107
+
108
+ def write_depgraph(self, filename: str,
109
+ fontcolor: str = '#000000',
110
+ background: str = 'transparent',
111
+ fontsize: str = '14',
112
+ border: bool = True, landscape: bool = False) -> None:
113
+ r'''
114
+ Renders and saves the dependency graph to a file.
115
+
116
+ Args:
117
+ filename (filepath): Output filepath
118
+ fontcolor (str): Node font RGB color hex value
119
+ background (str): Background color
120
+ fontsize (str): Node text font size
121
+ border (bool): Enables node border if True
122
+ landscape (bool): Renders graph in landscape layout if True
123
+
124
+ Examples:
125
+ >>> schema.write_depgraph('mydump.png')
126
+ Renders the object dependency graph and writes the result to a png file.
127
+ '''
128
+ import graphviz
129
+
130
+ filepath = os.path.abspath(filename)
131
+ fileroot, ext = os.path.splitext(filepath)
132
+ fileformat = ext[1:]
133
+
134
+ # controlling border width
135
+ if border:
136
+ penwidth = '1'
137
+ else:
138
+ penwidth = '0'
139
+
140
+ # controlling graph direction
141
+ if landscape:
142
+ rankdir = 'LR'
143
+ else:
144
+ rankdir = 'TB'
145
+
146
+ dot = graphviz.Digraph(format=fileformat)
147
+ dot.graph_attr['rankdir'] = rankdir
148
+ dot.attr(bgcolor=background)
149
+
150
+ def make_label(dep):
151
+ return f"lib-{dep.name()}"
152
+
153
+ nodes = {
154
+ self.name(): {
155
+ "text": self.name(),
156
+ "shape": "box",
157
+ "color": background,
158
+ "connects_to": set([make_label(subdep) for subdep in self.__deps.values()])
159
+ }
160
+ }
161
+ for dep in self.get_dep():
162
+ nodes[make_label(dep)] = {
163
+ "text": dep.name(),
164
+ "shape": "oval",
165
+ "color": background,
166
+ "connects_to": set([make_label(subdep) for subdep in dep.__deps.values()])
167
+ }
168
+
169
+ for label, info in nodes.items():
170
+ dot.node(label, label=info['text'], bordercolor=fontcolor, style='filled',
171
+ fontcolor=fontcolor, fontsize=fontsize, ordering="in",
172
+ penwidth=penwidth, fillcolor=info["color"], shape=info['shape'])
173
+
174
+ for conn in info['connects_to']:
175
+ dot.edge(label, conn, dir='back')
176
+
177
+ try:
178
+ dot.render(filename=fileroot, cleanup=True)
179
+ except graphviz.ExecutableNotFound as e:
180
+ raise RuntimeError(f'Unable to save flowgraph: {e}')
181
+
182
+ def __get_all_deps(self, seen: set) -> list:
183
+ '''
184
+ Loop through the dependency tree and generate a flat list
185
+ '''
186
+ deps = []
187
+
188
+ for obj in self.__deps.values():
189
+ if obj.name() in seen:
190
+ continue
191
+
192
+ deps.append(obj)
193
+ seen.add(obj.name())
194
+
195
+ if not isinstance(obj, DependencySchema):
196
+ # nothing to iterate over
197
+ continue
198
+
199
+ for obj_dep in obj.__get_all_deps(seen):
200
+ deps.append(obj_dep)
201
+ seen.add(obj_dep.name())
202
+
203
+ return deps
204
+
205
+ def get_dep(self, name: str = None, hierarchy: bool = True) -> list:
206
+ '''
207
+ Returns all dependencies associated with this object or a specific one if requested.
208
+
209
+ Raises:
210
+ KeyError: if the module specific module is requested but not found
211
+
212
+ Args:
213
+ name (str): name of the module
214
+ hierarchy (bool): if True, will return all modules including children
215
+ otherwise only this objects modules are returned.
216
+ '''
217
+
218
+ if name:
219
+ if name not in self.__deps:
220
+ raise KeyError(f"{name} is not an imported module")
221
+
222
+ return self.__deps[name]
223
+
224
+ if hierarchy:
225
+ return self.__get_all_deps(set())
226
+ return list(self.__deps.values())
227
+
228
+ def remove_dep(self, name: str) -> bool:
229
+ '''
230
+ Removes a previously registered module.
231
+
232
+ Args:
233
+ name (str): name of the module
234
+
235
+ Returns:
236
+ True if the module was removed, False is not found.
237
+ '''
238
+ if name not in self.__deps:
239
+ return False
240
+
241
+ del self.__deps[name]
242
+
243
+ # Remove from dependency list
244
+ dependencies = self.get("deps")
245
+ dependencies.remove(name)
246
+
247
+ self.set("deps", False, field="lock")
248
+ self.set("deps", dependencies)
249
+ self.set("deps", True, field="lock")
250
+
251
+ return True
252
+
253
+ def _populate_deps(self, module_map: dict):
254
+ if self.__deps:
255
+ return
256
+
257
+ for module in self.get("deps"):
258
+ if module not in module_map:
259
+ raise ValueError(f"{module} not available in map")
260
+ self.__deps[module] = module_map[module]
261
+
262
+ if isinstance(self.__deps[module], DependencySchema):
263
+ self.__deps[module]._populate_deps(module_map)
264
+
265
+ def register_package(self, name: str, root: str, tag: str = None):
266
+ """
267
+ Registers a package by its name with the root and associated tag.
268
+
269
+ Args:
270
+ name (str): Package name
271
+ root (str): Path to the root, can be directory, git url, or archive url
272
+ tag (str): Reference of the sources, can be commitid, branch name, tag
273
+
274
+ Examples:
275
+ >>> schema.register_package('siliconcompiler_data',
276
+ 'git+https://github.com/siliconcompiler/siliconcompiler',
277
+ 'v1.0.0')
278
+ """
279
+
280
+ if os.path.isfile(root):
281
+ root = os.path.dirname(os.path.abspath(root))
282
+
283
+ self.set("package", name, "root", root)
284
+ if tag:
285
+ self.set("package", name, "tag", tag)
286
+
287
+ def find_package(self, name: str):
288
+ """
289
+ Returns absolute path to the package root.
290
+
291
+ Raises:
292
+ ValueError: is package is not found
293
+
294
+ Args:
295
+ name (str): name of the package to find.
296
+
297
+ Returns:
298
+ Path to the package directory root.
299
+
300
+ Examples:
301
+ >>> schema.find_package('siliconcompiler')
302
+ Returns the path to the root of the siliconcompiler package.
303
+ """
304
+
305
+ if not self.valid("package", name):
306
+ raise ValueError(f"{name} is not a recognized source")
307
+
308
+ root = self.get("package", name, "root")
309
+ tag = self.get("package", name, "tag")
310
+
311
+ resolver = Resolver.find_resolver(root)
312
+ return resolver(name, self._parent(root=True), root, tag).get_path()
313
+
314
+ def __get_resolver_map(self):
315
+ """
316
+ Generate the resolver map got package handling for find_files and check_filepaths
317
+ """
318
+ schema_root = self._parent(root=True)
319
+ resolver_map = {}
320
+ for package in self.getkeys("package"):
321
+ root = self.get("package", package, "root")
322
+ tag = self.get("package", package, "tag")
323
+ resolver = Resolver.find_resolver(root)
324
+ resolver_map[package] = resolver(package, schema_root, root, tag).get_path
325
+ return resolver_map
326
+
327
+ def find_files(self, *keypath,
328
+ missing_ok=False,
329
+ step=None, index=None):
330
+ """
331
+ Returns absolute paths to files or directories based on the keypath
332
+ provided.
333
+
334
+ The keypath provided must point to a schema parameter of type file, dir,
335
+ or lists of either. Otherwise, it will trigger an error.
336
+
337
+ Args:
338
+ keypath (list of str): Variable length schema key list.
339
+ missing_ok (bool): If True, silently return None when files aren't
340
+ found. If False, print an error and set the error flag.
341
+ step (str): Step name to access for parameters that may be specified
342
+ on a per-node basis.
343
+ index (str): Index name to access for parameters that may be specified
344
+ on a per-node basis.
345
+
346
+ Returns:
347
+ If keys points to a scalar entry, returns an absolute path to that
348
+ file/directory, or None if not found. It keys points to a list
349
+ entry, returns a list of either the absolute paths or None for each
350
+ entry, depending on whether it is found.
351
+
352
+ Examples:
353
+ >>> schema.find_files('input', 'verilog')
354
+ Returns a list of absolute paths to source files, as specified in
355
+ the schema.
356
+ """
357
+ schema_root = self._parent(root=True)
358
+ cwd = getattr(schema_root, "cwd", os.getcwd())
359
+ collection_dir = getattr(schema_root, "collection_dir", None)
360
+ if collection_dir:
361
+ collection_dir = collection_dir()
362
+
363
+ return super().find_files(*keypath,
364
+ missing_ok=missing_ok,
365
+ step=step, index=index,
366
+ packages=self.__get_resolver_map(),
367
+ collection_dir=collection_dir,
368
+ cwd=cwd)
369
+
370
+ def check_filepaths(self, ignore_keys=None):
371
+ '''
372
+ Verifies that paths to all files in manifest are valid.
373
+
374
+ Args:
375
+ ignore_keys (list of keypaths): list of keypaths to ignore while checking
376
+
377
+ Returns:
378
+ True if all file paths are valid, otherwise False.
379
+ '''
380
+ schema_root = self._parent(root=True)
381
+ cwd = getattr(schema_root, "cwd", os.getcwd())
382
+ logger = getattr(schema_root, "logger", None)
383
+ collection_dir = getattr(schema_root, "collection_dir", None)
384
+ if collection_dir:
385
+ collection_dir = collection_dir()
386
+
387
+ return super().check_filepaths(
388
+ ignore_keys=ignore_keys,
389
+ logger=logger,
390
+ packages=self.__get_resolver_map(),
391
+ collection_dir=collection_dir,
392
+ cwd=cwd)