dv-flow-mgr 0.0.1.12761553329a1__tar.gz → 0.0.1.12822558956a1__tar.gz

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 (81) hide show
  1. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/PKG-INFO +1 -1
  2. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/docs/Roadmap.md +5 -0
  3. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/pyproject.toml +1 -1
  4. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/fileset.py +2 -1
  5. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/task.py +11 -13
  6. dv_flow_mgr-0.0.1.12822558956a1/src/dv_flow_mgr/task_data.py +269 -0
  7. dv_flow_mgr-0.0.1.12822558956a1/src/dv_flow_mgr/tasklib/builtin_pkg.py +61 -0
  8. dv_flow_mgr-0.0.1.12822558956a1/src/dv_flow_mgr/tasklib/std/flow.dv +12 -0
  9. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr.egg-info/PKG-INFO +1 -1
  10. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr.egg-info/SOURCES.txt +5 -0
  11. dv_flow_mgr-0.0.1.12822558956a1/tests/unit/__init__.py +1 -0
  12. dv_flow_mgr-0.0.1.12822558956a1/tests/unit/test_data_merge.py +121 -0
  13. dv_flow_mgr-0.0.1.12822558956a1/tests/unit/test_pyclass.py +29 -0
  14. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/tests/unit/test_smoke.py +1 -1
  15. dv_flow_mgr-0.0.1.12761553329a1/src/dv_flow_mgr/task_data.py +0 -94
  16. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/.github/workflows/ci.yml +0 -0
  17. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/.gitignore +0 -0
  18. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/.vscode/settings.json +0 -0
  19. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/LICENSE +0 -0
  20. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/README.md +0 -0
  21. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/docs/Makefile +0 -0
  22. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/docs/Notes.md +0 -0
  23. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/docs/Stages.md +0 -0
  24. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/docs/TypesAndDefs.md +0 -0
  25. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/docs/conf.py +0 -0
  26. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/docs/index.rst +0 -0
  27. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/docs/intro.rst +0 -0
  28. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/docs/quickstart.rst +0 -0
  29. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/docs/reference.rst +0 -0
  30. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/ivpm.yaml +0 -0
  31. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/setup.cfg +0 -0
  32. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/__init__.py +0 -0
  33. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/__main__.py +0 -0
  34. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/cmds/cmd_run.py +0 -0
  35. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/flow.py +0 -0
  36. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/fragment_def.py +0 -0
  37. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/package.py +0 -0
  38. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/package_def.py +0 -0
  39. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/package_import_spec.py +0 -0
  40. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/parameters.py +0 -0
  41. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/session.py +0 -0
  42. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/share/flow.json +0 -0
  43. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/task_def.py +0 -0
  44. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/task_memento.py +0 -0
  45. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/tasklib/hdl/sim/mti_pkg.py +0 -0
  46. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/tasklib/hdl/sim/mti_task_sim_image.py +0 -0
  47. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/tasklib/hdl/sim/mti_task_sim_run.py +0 -0
  48. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/tasklib/hdl/sim/pkg_hdl_sim.py +0 -0
  49. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/tasklib/hdl/sim/task_sim_image.py +0 -0
  50. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/tasklib/hdl/sim/vcs_pkg.py +0 -0
  51. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/tasklib/hdl/sim/vcs_task_sim_image.py +0 -0
  52. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/tasklib/hdl/sim/vcs_task_sim_run.py +0 -0
  53. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/tasklib/hdl/sim/vl_task_sim_image.py +0 -0
  54. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/tasklib/hdl/sim/vlt_pkg.py +0 -0
  55. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/tasklib/hdl/sim/vlt_task_sim_image.py +0 -0
  56. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/tasklib/hdl/sim/vlt_task_sim_run.py +0 -0
  57. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/tasklib/std/fileset.py +0 -0
  58. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/tasklib/std/pkg_std.py +0 -0
  59. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/tasklib/std/std.dfs +0 -0
  60. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/tasklib/std/task_fileset.py +0 -0
  61. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr/tasklib/std/task_null.py +0 -0
  62. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr.egg-info/dependency_links.txt +0 -0
  63. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr.egg-info/entry_points.txt +0 -0
  64. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr.egg-info/requires.txt +0 -0
  65. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/src/dv_flow_mgr.egg-info/top_level.txt +0 -0
  66. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/tests/examples/example1/example1.flow +0 -0
  67. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/tests/unit/data/fileset/test1/files1/file1_1.sv +0 -0
  68. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/tests/unit/data/fileset/test1/files1/file1_2.sv +0 -0
  69. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/tests/unit/data/fileset/test1/files2/file2_1.sv +0 -0
  70. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/tests/unit/data/fileset/test1/files2/file2_2.sv +0 -0
  71. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/tests/unit/data/fileset/test1/test1.dfs +0 -0
  72. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/tests/unit/data/fileset/test1 copy/files1/file1_1.sv +0 -0
  73. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/tests/unit/data/fileset/test1 copy/files1/file1_2.sv +0 -0
  74. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/tests/unit/data/fileset/test1 copy/files2/file2_1.sv +0 -0
  75. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/tests/unit/data/fileset/test1 copy/files2/file2_2.sv +0 -0
  76. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/tests/unit/data/fileset/test1 copy/test1.dfs +0 -0
  77. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/tests/unit/data/proj1/proj1.dfs +0 -0
  78. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/tests/unit/data/proj2/proj2.dfs +0 -0
  79. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/tests/unit/data/proj3/proj3.dfs +0 -0
  80. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/tests/unit/test_fileset.py +0 -0
  81. {dv_flow_mgr-0.0.1.12761553329a1 → dv_flow_mgr-0.0.1.12822558956a1}/tests/unit/test_smoke copy.sav +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: dv-flow-mgr
3
- Version: 0.0.1.12761553329a1
3
+ Version: 0.0.1.12822558956a1
4
4
  Summary: DV Flow Manager is a build system for silicon design
5
5
  Author-email: Matthew Ballance <matt.ballance@gmail.com>
6
6
  License: Apache License
@@ -29,6 +29,11 @@ are evaluated.
29
29
  - JQ-based data extraction
30
30
  - YAML task templates / expansions
31
31
  - Support for annotating job requirements
32
+ - Mark tasks as producing and accepting certain data
33
+ - FileSet task `produces` fileset of `type`
34
+ - SimImage task `accepts` systemVerilogSource, verilogSource, verilogPreCompLib, etc
35
+ => Mostly useful for checking and suggestion
36
+ => As more are marked, can treat as more binding
32
37
 
33
38
  # Library
34
39
 
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
 
6
6
  [project]
7
7
  name = "dv-flow-mgr"
8
- version = "0.0.1.12761553329a1"
8
+ version = "0.0.1.12822558956a1"
9
9
  dependencies = [
10
10
  "pyyaml",
11
11
  "toposort",
@@ -24,8 +24,9 @@ from pydantic import BaseModel
24
24
  from typing import Any, Dict, List, Tuple
25
25
 
26
26
  class FileSet(BaseModel):
27
- src : str
28
27
  type : str
29
28
  basedir : str
29
+ name : str = ""
30
+ src : str = None
30
31
  files : List[str] = dc.Field(default_factory=list)
31
32
  params : Dict[str,str] = dc.Field(default_factory=dict)
@@ -155,14 +155,6 @@ class Task(object):
155
155
  # Merge filesets. A fileset with the same
156
156
  print("deps_o: %s" % str(deps_o))
157
157
 
158
- # First, merge the dep maps of all the inputs
159
- deps_m = self.depends[0].output.deps.copy()
160
- for deps in map(lambda d: d.deps, self.depends[1:]):
161
- for k,v in deps.items():
162
- if k in deps_m:
163
- deps_m[k].add(v)
164
- else:
165
- deps_m[k] = set(v)
166
158
 
167
159
  print("deps_m: %s" % str(deps_m))
168
160
 
@@ -182,8 +174,7 @@ class Task(object):
182
174
  # Mark the source of this data as being this task
183
175
  input.src = self.name
184
176
 
185
- if not os.path.isdir(self.rundir):
186
- os.makedirs(self.rundir)
177
+ self.init_rundir()
187
178
 
188
179
  result = await self.run(input)
189
180
 
@@ -202,9 +193,7 @@ class Task(object):
202
193
  result = self.getOutput()
203
194
 
204
195
  # Write-back the memento, if specified
205
- if self.memento is not None:
206
- with open(os.path.join(self.rundir, "memento.json"), "w") as fp:
207
- fp.write(self.memento.model_dump_json(indent=2))
196
+ self.save_memento()
208
197
 
209
198
  self.running = False
210
199
 
@@ -214,6 +203,15 @@ class Task(object):
214
203
  async def run(self, input : TaskData) -> TaskData:
215
204
  raise NotImplementedError("TaskImpl.run() not implemented")
216
205
 
206
+ def init_rundir(self):
207
+ if not os.path.isdir(self.rundir):
208
+ os.makedirs(self.rundir)
209
+
210
+ def save_memento(self):
211
+ if self.memento is not None:
212
+ with open(os.path.join(self.rundir, "memento.json"), "w") as fp:
213
+ fp.write(self.memento.model_dump_json(indent=2))
214
+
217
215
  def setOutput(self, output : TaskData):
218
216
  self.output_set = True
219
217
  output.src = self.name
@@ -0,0 +1,269 @@
1
+ #****************************************************************************
2
+ #* task_data.py
3
+ #*
4
+ #* Copyright 2023 Matthew Ballance and Contributors
5
+ #*
6
+ #* Licensed under the Apache License, Version 2.0 (the "License"); you may
7
+ #* not use this file except in compliance with the License.
8
+ #* You may obtain a copy of the License at:
9
+ #*
10
+ #* http://www.apache.org/licenses/LICENSE-2.0
11
+ #*
12
+ #* Unless required by applicable law or agreed to in writing, software
13
+ #* distributed under the License is distributed on an "AS IS" BASIS,
14
+ #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ #* See the License for the specific language governing permissions and
16
+ #* limitations under the License.
17
+ #*
18
+ #* Created on:
19
+ #* Author:
20
+ #*
21
+ #****************************************************************************
22
+ import enum
23
+ import pydantic.dataclasses as dc
24
+ from pydantic import BaseModel
25
+ from typing import Any, Dict, Set, List, Tuple
26
+ from .fileset import FileSet
27
+ from toposort import toposort
28
+
29
+ class TaskDataParamOpE(enum.Enum):
30
+ Set = enum.auto()
31
+ Append = enum.auto()
32
+ Prepend = enum.auto()
33
+ PathAppend = enum.auto()
34
+ PathPrepend = enum.auto()
35
+
36
+ class TaskDataParamKindE(enum.Enum):
37
+ String = enum.auto()
38
+ FilePath = enum.auto()
39
+ SearchPath = enum.auto()
40
+ List = enum.auto()
41
+
42
+ class TaskDataParamOp(BaseModel):
43
+ op : TaskDataParamOpE
44
+ value : Any
45
+
46
+ class TaskDataParam(BaseModel):
47
+ kind : TaskDataParamKindE
48
+ ops : List[TaskDataParamOp] = dc.Field(default_factory=list)
49
+
50
+ class TaskData(BaseModel):
51
+ src : str = None
52
+ params : Dict[str,TaskDataParam] = dc.Field(default_factory=dict)
53
+ deps : Dict[str,Set[str]] = dc.Field(default_factory=dict)
54
+ filesets : List[FileSet] = dc.Field(default_factory=list)
55
+ changed : bool = False
56
+
57
+ def hasParam(self, name: str) -> bool:
58
+ return name in self.params
59
+
60
+ def getParam(self, name: str) -> Any:
61
+ return self.params[name]
62
+
63
+ def setParam(self, name: str, value: Any):
64
+ self.params[name] = value
65
+
66
+ def addFileSet(self, fs : FileSet):
67
+ self.filesets.append(fs)
68
+
69
+ def getFileSets(self, type=None, order=True) -> List[FileSet]:
70
+ ret = []
71
+
72
+ if order:
73
+ # The deps map specifies task dependencies
74
+
75
+ candidate_fs = []
76
+ for fs in self.filesets:
77
+ if type is None or fs.type in type:
78
+ candidate_fs.append(fs)
79
+
80
+ order = toposort(self.deps)
81
+
82
+ for order_s in order:
83
+ for fs in candidate_fs:
84
+ if fs.src in order_s:
85
+ ret.append(fs)
86
+ else:
87
+ for fs in self.filesets:
88
+ if type is None or fs.type in type:
89
+ ret.append(fs)
90
+
91
+ return ret
92
+
93
+ def copy(self) -> 'TaskData':
94
+ ret = TaskData()
95
+ ret.src = self.src
96
+ ret.params = self.params.copy()
97
+ for d in self.deps:
98
+ ret.deps.append(d.clone())
99
+ ret.changed = self.changed
100
+ return ret
101
+
102
+ def setParamVal(self, name: str, kind : TaskDataParamKindE, value: Any):
103
+ if name not in self.params:
104
+ self.params[name] = TaskDataParam(kind=kind)
105
+ self.params[name].ops.append(TaskDataParamOp(op=TaskDataParamOpE.Set, value=value))
106
+
107
+ def getParamVal(self, name: str) -> Any:
108
+ if name not in self.params.keys():
109
+ raise Exception("No such parameter: %s" % name)
110
+ param = self.params[name]
111
+ value = param.ops[0].value
112
+
113
+ if len(param.ops) > 1:
114
+ for op in param.ops[1:]:
115
+ if op.op == TaskDataParamOpE.Append:
116
+ if isinstance(value, list):
117
+ value.extend(op.value)
118
+ else:
119
+ value += op.value
120
+ elif op.op == TaskDataParamOpE.Prepend:
121
+ if isinstance(value, list):
122
+ for nv in op.value:
123
+ value.insert(0, nv)
124
+ else:
125
+ value = op.value + value
126
+ elif op.op == TaskDataParamOpE.PathAppend:
127
+ if isinstance(value, list):
128
+ value = ":".join(value)
129
+ value = value + ":" + op.value
130
+ elif op.op == TaskDataParamOpE.PathPrepend:
131
+ if isinstance(value, list):
132
+ value = ":".join(value)
133
+ value = op.value + ":" + value
134
+
135
+ return value
136
+
137
+ @staticmethod
138
+ def merge(incoming : List['TaskData'], local : 'TaskData' = None) -> 'TaskData':
139
+ """Merges incoming data with local settings and produces an output"""
140
+
141
+ # Deal with the dependency trees first
142
+ output = TaskData()
143
+
144
+ # First, merge the dep maps of all the inputs
145
+ output.deps = incoming[0].deps.copy()
146
+ for deps in map(lambda i: i.deps, incoming[1:]):
147
+ for k,v in deps.items():
148
+ if k not in output.deps:
149
+ output.deps[k] = []
150
+ for vi in v:
151
+ if vi not in output.deps[k]:
152
+ output.deps[k].append(v)
153
+
154
+ # Process filesets
155
+ for inp in incoming:
156
+ for fs in inp.filesets:
157
+ exists = False
158
+ for fs_o in output.filesets:
159
+ if fs_o.name == fs.name and fs_o.src == fs.src:
160
+ exists = True
161
+ break
162
+ if not exists:
163
+ output.addFileSet(fs.model_copy())
164
+
165
+ # Now, deal with parameters
166
+ # Find collisions first
167
+ colliding_keys = set()
168
+ passthrough_keys = set()
169
+
170
+ for i in incoming:
171
+ for k in i.params.keys():
172
+ if k in passthrough_keys:
173
+ colliding_keys.add(k)
174
+ else:
175
+ passthrough_keys.add(k)
176
+
177
+ # Now, removes those that are locally set
178
+ local_set_params = set()
179
+ if local is not None:
180
+ for k,v in local.params.items():
181
+ if len(v.ops) == 1 and v.ops[0].op == TaskDataParamOpE.Set:
182
+ local_set_params.add(k)
183
+ # If are setting locally, it's not passthrough
184
+ passthrough_keys.remove(k)
185
+ if k in colliding_keys:
186
+ colliding_keys.remove(k)
187
+
188
+ # Construct the passthrough set by removing
189
+ # colliding entries and those that we will set locally
190
+ for k in colliding_keys:
191
+ if k in passthrough_keys:
192
+ passthrough_keys.remove(k)
193
+
194
+ # For the remaining keys, check for conflicts by
195
+ # confirming that the last 'set' in each incoming parameter
196
+ # are equal
197
+ for k in colliding_keys:
198
+ value = None
199
+ for i,inp in enumerate(incoming):
200
+ value_i = None
201
+ param = inp.params[k]
202
+ if len(param.ops) == 1:
203
+ value_i = param.ops[0].value
204
+ else:
205
+ # Iterate in reverse over the operations
206
+ for op in param.ops[::-1]:
207
+ if op.op == TaskDataParamOpE.Set:
208
+ value_i = op.value
209
+ break
210
+ if not i:
211
+ value = value_i
212
+ else:
213
+ if value != value_i:
214
+ raise Exception("Parameter %s has conflicting values (%s %s)" % (
215
+ k,
216
+ str(value),
217
+ value(value_i)))
218
+
219
+
220
+ # Now, we need to construct the result
221
+ # - copy over passthrough parameters
222
+ # - add locally-set parameters
223
+ # - for others
224
+ # - Apply full list for first input
225
+ # - Apply all beyond the last 'set' operation for others
226
+ for k in passthrough_keys:
227
+ # Find an input that has the parameter
228
+ for inp in incoming:
229
+ if k in inp.params:
230
+ break
231
+ # Find the value of the param
232
+ param = inp.params[k]
233
+
234
+ if len(param.ops) == 1:
235
+ output.params[k] = TaskDataParam(kind=param.kind)
236
+ output.params[k].ops.append(param.ops[0])
237
+ else:
238
+ for op in param.ops[::-1]:
239
+ if op.op == TaskDataParamOpE.Set:
240
+ output.params[k] = TaskDataParam(kind=param.kind)
241
+ output.params[k].ops.append(op)
242
+ break
243
+ for k in local_set_params:
244
+ output.params[k] = local.params[k].model_copy()
245
+
246
+ for k in colliding_keys:
247
+ value = None
248
+ for i,inp in enumerate(incoming):
249
+ # Find the last location that performs a 'set'
250
+ last_set_i = -1
251
+ param = inp.params[k]
252
+ if len(param.ops) == 1:
253
+ last_set_i = 0
254
+ else:
255
+ # Iterate in reverse over the operations
256
+ for j,op in enumerate(param.ops[::-1]):
257
+ if op.op == TaskDataParamOpE.Set:
258
+ last_set_i = j
259
+ break
260
+
261
+ if not i:
262
+ # Copy the full list, including the last 'set'
263
+ output.params[k].ops = param.param[last_set_i:].copy()
264
+ else:
265
+ # append any additional directives
266
+ if last_set_i+1 < len(param.ops):
267
+ output.params[k].extend(param.ops[last_set_i+1:])
268
+
269
+ return output
@@ -0,0 +1,61 @@
1
+ import os
2
+ import sys
3
+ import glob
4
+ import fnmatch
5
+ import importlib
6
+ import pydantic.dataclasses as dc
7
+ from ..package import TaskCtor
8
+ from ..task import Task, TaskParams, TaskCtorT
9
+ from ..task_data import TaskData
10
+ from ..task_memento import TaskMemento
11
+ from typing import List, Tuple
12
+ import dataclasses as dc
13
+ from ..package_def import Package
14
+
15
+ class TaskPyClass(Task):
16
+
17
+ async def run(self, input : TaskData) -> TaskData:
18
+
19
+ if self.srcdir not in sys.path:
20
+ sys.path.insert(0, self.srcdir)
21
+
22
+ print("sys.path: %s" % str(sys.path), flush=True)
23
+ idx = self.params.pyclass.rfind('.')
24
+ modname = self.params.pyclass[:idx]
25
+ clsname = self.params.pyclass[idx+1:]
26
+
27
+ if os.path.isfile(os.path.join(self.basedir, "my_module.py")):
28
+ print("my_module.py exists", flush=True)
29
+ else:
30
+ print("my_module.py does not exist", flush=True)
31
+
32
+ try:
33
+ print("modname=%s" % modname, flush=True)
34
+ module = importlib.import_module(modname)
35
+ except ModuleNotFoundError as e:
36
+ print("Module not found: %s syspath=%s" % (str(e), str(sys.path)), flush=True)
37
+ raise e
38
+
39
+ cls = getattr(module, clsname)
40
+
41
+ obj = cls(self.name, self.task_id, self.session, self.basedir, srcdir=self.srcdir)
42
+
43
+ return await obj.run(input)
44
+
45
+
46
+ class TaskPyClassParams(TaskParams):
47
+ pyclass : str
48
+
49
+ class TaskPyClassMemento(TaskMemento):
50
+ pass
51
+
52
+ class TaskPyClassCtor(TaskCtorT):
53
+ def __init__(self):
54
+ super().__init__(TaskPyClassParams, TaskPyClass)
55
+
56
+ @dc.dataclass
57
+ class PackageBuiltin(Package):
58
+
59
+ def __post_init__(self):
60
+ print("PackageBuiltin::__post_init__", flush=True)
61
+ self.tasks["PyClass"] = TaskPyClass()
@@ -0,0 +1,12 @@
1
+
2
+ package:
3
+ name: std
4
+
5
+ tasks:
6
+ - name: Exec
7
+ uses: builtin.PyClass
8
+ params:
9
+ - name: cmd
10
+ type: string
11
+ - name: pyclass
12
+ type: string
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: dv-flow-mgr
3
- Version: 0.0.1.12761553329a1
3
+ Version: 0.0.1.12822558956a1
4
4
  Summary: DV Flow Manager is a build system for silicon design
5
5
  Author-email: Matthew Ballance <matt.ballance@gmail.com>
6
6
  License: Apache License
@@ -37,6 +37,7 @@ src/dv_flow_mgr.egg-info/requires.txt
37
37
  src/dv_flow_mgr.egg-info/top_level.txt
38
38
  src/dv_flow_mgr/cmds/cmd_run.py
39
39
  src/dv_flow_mgr/share/flow.json
40
+ src/dv_flow_mgr/tasklib/builtin_pkg.py
40
41
  src/dv_flow_mgr/tasklib/hdl/sim/mti_pkg.py
41
42
  src/dv_flow_mgr/tasklib/hdl/sim/mti_task_sim_image.py
42
43
  src/dv_flow_mgr/tasklib/hdl/sim/mti_task_sim_run.py
@@ -50,12 +51,16 @@ src/dv_flow_mgr/tasklib/hdl/sim/vlt_pkg.py
50
51
  src/dv_flow_mgr/tasklib/hdl/sim/vlt_task_sim_image.py
51
52
  src/dv_flow_mgr/tasklib/hdl/sim/vlt_task_sim_run.py
52
53
  src/dv_flow_mgr/tasklib/std/fileset.py
54
+ src/dv_flow_mgr/tasklib/std/flow.dv
53
55
  src/dv_flow_mgr/tasklib/std/pkg_std.py
54
56
  src/dv_flow_mgr/tasklib/std/std.dfs
55
57
  src/dv_flow_mgr/tasklib/std/task_fileset.py
56
58
  src/dv_flow_mgr/tasklib/std/task_null.py
57
59
  tests/examples/example1/example1.flow
60
+ tests/unit/__init__.py
61
+ tests/unit/test_data_merge.py
58
62
  tests/unit/test_fileset.py
63
+ tests/unit/test_pyclass.py
59
64
  tests/unit/test_smoke copy.sav
60
65
  tests/unit/test_smoke.py
61
66
  tests/unit/data/fileset/test1/test1.dfs
@@ -0,0 +1,121 @@
1
+ import asyncio
2
+ import io
3
+ import os
4
+ import dataclasses as dc
5
+ import pytest
6
+ from typing import List
7
+ import yaml
8
+ from dv_flow_mgr import TaskData, FileSet, Session, TaskData, TaskDataParamKindE
9
+ from pydantic import BaseModel
10
+ from shutil import copytree
11
+ from .tasklib import TaskNull
12
+
13
+ def test_empty_in():
14
+
15
+ in1 = TaskData()
16
+ in2 = TaskData()
17
+
18
+ out = TaskData.merge([in1, in2])
19
+
20
+ assert len(out.params) == 0
21
+
22
+ def test_empty_combine_nonoverlap_in():
23
+
24
+ in1 = TaskData()
25
+ in1.setParamVal("v1", "1")
26
+ in2 = TaskData()
27
+ in2.setParamVal("v2", "2")
28
+
29
+ out = TaskData.merge([in1, in2])
30
+
31
+ assert len(out.params) != 0
32
+ assert "v1" in out.params.keys()
33
+ assert out.getParamVal("v1") == "1"
34
+ assert "v2" in out.params.keys()
35
+ assert out.getParamVal("v2") == "2"
36
+
37
+ def test_empty_combine_nonoverlap_in():
38
+
39
+ in1 = TaskData()
40
+ in1.setParamVal("v1", TaskDataParamKindE.String, "1")
41
+ in2 = TaskData()
42
+ in2.setParamVal("v2", TaskDataParamKindE.String, "2")
43
+
44
+ out = TaskData.merge([in1, in2])
45
+
46
+ assert len(out.params) != 0
47
+ assert "v1" in out.params.keys()
48
+ assert out.getParamVal("v1") == "1"
49
+ assert "v2" in out.params.keys()
50
+ assert out.getParamVal("v2") == "2"
51
+
52
+ def test_conflict_1():
53
+
54
+ in1 = TaskData()
55
+ in1.setParamVal("v1", TaskDataParamKindE.String, "1")
56
+ in2 = TaskData()
57
+ in2.setParamVal("v1", TaskDataParamKindE.String, "2")
58
+
59
+ with pytest.raises(Exception):
60
+ out = TaskData.merge([in1, in2])
61
+
62
+ def test_fileset_merge_1():
63
+ in1 = TaskData(src="in1")
64
+ in1.addFileSet(FileSet(
65
+ src="in1",
66
+ type="systemVerilogSource",
67
+ basedir="."))
68
+
69
+ in2 = TaskData(src="in2")
70
+ in2.addFileSet(FileSet(
71
+ src="in2",
72
+ type="systemVerilogSource",
73
+ basedir="."))
74
+
75
+ out = TaskData.merge([in1, in2])
76
+
77
+ assert len(out.filesets) == 2
78
+
79
+ def test_fileset_merge_common_dep_1():
80
+ in1 = TaskData(src="in1")
81
+ in1.addFileSet(FileSet(
82
+ src="in1",
83
+ type="systemVerilogSource",
84
+ basedir="."))
85
+ in1.addFileSet(FileSet(
86
+ src="in0",
87
+ type="systemVerilogSource",
88
+ basedir="."))
89
+ in1.deps = {
90
+ "in1": ["in0"]
91
+ }
92
+
93
+ in2 = TaskData(src="in2")
94
+ in2.addFileSet(FileSet(
95
+ src="in1",
96
+ type="systemVerilogSource",
97
+ basedir="."))
98
+ in2.addFileSet(FileSet(
99
+ src="in2",
100
+ type="systemVerilogSource",
101
+ basedir="."))
102
+ in2.addFileSet(FileSet(
103
+ src="in0",
104
+ type="systemVerilogSource",
105
+ basedir="."))
106
+ in2.deps = {
107
+ "in1": ["in0"],
108
+ "in2": ["in1"]
109
+ }
110
+
111
+ out = TaskData.merge([in2, in1])
112
+
113
+ assert len(out.filesets) == 3
114
+ fs = out.getFileSets(type=["systemVerilogSource"], order=True)
115
+ assert len(fs) == 3
116
+
117
+ assert fs[0].src == "in0"
118
+ assert fs[1].src == "in1"
119
+ assert fs[2].src == "in2"
120
+
121
+
@@ -0,0 +1,29 @@
1
+
2
+ import os
3
+ import asyncio
4
+ import pytest
5
+ from dv_flow_mgr import TaskData
6
+ from dv_flow_mgr.tasklib.builtin_pkg import TaskPyClass, TaskPyClassParams
7
+
8
+ def test_smoke(tmpdir):
9
+ module = """
10
+ from dv_flow_mgr import Task, TaskData
11
+
12
+ class foo(Task):
13
+
14
+ async def run(self, input : TaskData) -> TaskData:
15
+ print("foo::run", flush=True)
16
+ return input
17
+ """
18
+ print("test_smoke")
19
+
20
+ with open(os.path.join(tmpdir, "my_module.py"), "w") as f:
21
+ f.write(module)
22
+
23
+ params = TaskPyClassParams(pyclass="my_module.foo")
24
+ basedir = os.path.join(tmpdir)
25
+ task = TaskPyClass("t1", -1, None, params, basedir, srcdir=basedir)
26
+
27
+ in_data = TaskData()
28
+ asyncio.run(task.run(in_data))
29
+ pass
@@ -5,7 +5,7 @@ import dataclasses as dc
5
5
  import pytest
6
6
  from typing import List
7
7
  import yaml
8
- from dv_flow_mgr import FileSet, PackageDef, Session, TaskData, TaskImpl
8
+ from dv_flow_mgr import FileSet, PackageDef, Session, TaskData
9
9
  from pydantic import BaseModel
10
10
  from shutil import copytree
11
11
 
@@ -1,94 +0,0 @@
1
- #****************************************************************************
2
- #* task_data.py
3
- #*
4
- #* Copyright 2023 Matthew Ballance and Contributors
5
- #*
6
- #* Licensed under the Apache License, Version 2.0 (the "License"); you may
7
- #* not use this file except in compliance with the License.
8
- #* You may obtain a copy of the License at:
9
- #*
10
- #* http://www.apache.org/licenses/LICENSE-2.0
11
- #*
12
- #* Unless required by applicable law or agreed to in writing, software
13
- #* distributed under the License is distributed on an "AS IS" BASIS,
14
- #* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
- #* See the License for the specific language governing permissions and
16
- #* limitations under the License.
17
- #*
18
- #* Created on:
19
- #* Author:
20
- #*
21
- #****************************************************************************
22
- import enum
23
- import pydantic.dataclasses as dc
24
- from pydantic import BaseModel
25
- from typing import Any, Dict, Set, List, Tuple
26
- from .fileset import FileSet
27
-
28
- class TaskDataParamOpE(enum.Enum):
29
- Set = enum.auto()
30
- Append = enum.auto()
31
- Prepend = enum.auto()
32
- PathAppend = enum.auto()
33
- PathPrepend = enum.auto()
34
-
35
- class TaskDataParamOp(BaseModel):
36
- op : TaskDataParamOpE
37
- value : Any
38
-
39
- class TaskDataParam(BaseModel):
40
- value : Any
41
- ops : List[TaskDataParamOp] = dc.Field(default_factory=list)
42
-
43
- class TaskData(BaseModel):
44
- src : str = None
45
- params : Dict[str,Any] = dc.Field(default_factory=dict)
46
- deps : Dict[str,Set[str]] = dc.Field(default_factory=dict)
47
- changed : bool = False
48
-
49
- def hasParam(self, name: str) -> bool:
50
- return name in self.params
51
-
52
- def getParam(self, name: str) -> Any:
53
- return self.params[name]
54
-
55
- def setParam(self, name: str, value: Any):
56
- self.params[name] = value
57
-
58
- def addFileSet(self, fs : FileSet):
59
- fs.src = self.src
60
- if "filesets" not in self.params:
61
- self.params["filesets"] = []
62
- self.params["filesets"].append(fs)
63
-
64
- def getFileSets(self, type=None) -> List[FileSet]:
65
- ret = []
66
-
67
- if "filesets" in self.params:
68
- for fs in self.params["filesets"]:
69
- if type is None or fs.type in type:
70
- ret.append(fs)
71
-
72
- return ret
73
-
74
- def copy(self) -> 'TaskData':
75
- ret = TaskData()
76
- ret.src = self.src
77
- ret.params = self.params.copy()
78
- for d in self.deps:
79
- ret.deps.append(d.clone())
80
- ret.changed = self.changed
81
- return ret
82
-
83
- def merge(self, other):
84
- for k,v in other.params.items():
85
- if k not in self.params:
86
- if hasattr(v, "copy"):
87
- self.params[k] = v.copy()
88
- else:
89
- self.params[k] = v
90
- elif hasattr(self.params[k], "merge"):
91
- self.params[k].merge(v)
92
- elif self.params[k] != v:
93
- raise Exception("Parameter %s has conflicting values" % k)
94
-