dv-flow-mgr 0.0.1.12664275031a1__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.
- dv_flow_mgr/__init__.py +6 -0
- dv_flow_mgr/__main__.py +21 -0
- dv_flow_mgr/cmds/cmd_run.py +28 -0
- dv_flow_mgr/fileset.py +31 -0
- dv_flow_mgr/flow.py +59 -0
- dv_flow_mgr/fragment_def.py +46 -0
- dv_flow_mgr/package.py +79 -0
- dv_flow_mgr/package_def.py +97 -0
- dv_flow_mgr/parameters.py +27 -0
- dv_flow_mgr/session.py +247 -0
- dv_flow_mgr/share/flow.json +172 -0
- dv_flow_mgr/task.py +225 -0
- dv_flow_mgr/task_data.py +77 -0
- dv_flow_mgr/task_def.py +45 -0
- dv_flow_mgr/task_memento.py +34 -0
- dv_flow_mgr/tasklib/hdl/sim/mti_pkg.py +11 -0
- dv_flow_mgr/tasklib/hdl/sim/mti_task_sim_image.py +69 -0
- dv_flow_mgr/tasklib/hdl/sim/mti_task_sim_run.py +47 -0
- dv_flow_mgr/tasklib/hdl/sim/pkg_hdl_sim.py +8 -0
- dv_flow_mgr/tasklib/hdl/sim/task_sim_image.py +16 -0
- dv_flow_mgr/tasklib/hdl/sim/vl_task_sim_image.py +72 -0
- dv_flow_mgr/tasklib/hdl/sim/vlt_pkg.py +14 -0
- dv_flow_mgr/tasklib/hdl/sim/vlt_task_sim_image.py +50 -0
- dv_flow_mgr/tasklib/hdl/sim/vlt_task_sim_run.py +45 -0
- dv_flow_mgr/tasklib/std/fileset.py +5 -0
- dv_flow_mgr/tasklib/std/pkg_std.py +15 -0
- dv_flow_mgr/tasklib/std/std.dfs +7 -0
- dv_flow_mgr/tasklib/std/task_fileset.py +89 -0
- dv_flow_mgr/tasklib/std/task_null.py +26 -0
- dv_flow_mgr-0.0.1.12664275031a1.dist-info/LICENSE +201 -0
- dv_flow_mgr-0.0.1.12664275031a1.dist-info/METADATA +211 -0
- dv_flow_mgr-0.0.1.12664275031a1.dist-info/RECORD +35 -0
- dv_flow_mgr-0.0.1.12664275031a1.dist-info/WHEEL +5 -0
- dv_flow_mgr-0.0.1.12664275031a1.dist-info/entry_points.txt +2 -0
- dv_flow_mgr-0.0.1.12664275031a1.dist-info/top_level.txt +1 -0
dv_flow_mgr/__init__.py
ADDED
dv_flow_mgr/__main__.py
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
|
2
|
+
import argparse
|
3
|
+
from .cmds.cmd_run import CmdRun
|
4
|
+
|
5
|
+
def get_parser():
|
6
|
+
parser = argparse.ArgumentParser(description='dv_flow_mgr')
|
7
|
+
subparsers = parser.add_subparsers(required=True)
|
8
|
+
|
9
|
+
run_parser = subparsers.add_parser('run', help='run a flow')
|
10
|
+
run_parser.add_argument("tasks", nargs='+', help="tasks to run")
|
11
|
+
run_parser.set_defaults(func=CmdRun())
|
12
|
+
|
13
|
+
return parser
|
14
|
+
|
15
|
+
def main():
|
16
|
+
parser = get_parser()
|
17
|
+
args = parser.parse_args()
|
18
|
+
args.func(args)
|
19
|
+
|
20
|
+
if __name__ == "__main__":
|
21
|
+
main()
|
@@ -0,0 +1,28 @@
|
|
1
|
+
import asyncio
|
2
|
+
import os
|
3
|
+
from ..session import Session
|
4
|
+
|
5
|
+
class CmdRun(object):
|
6
|
+
|
7
|
+
def __call__(self, args):
|
8
|
+
srcdir = os.getcwd()
|
9
|
+
rundir = os.path.join(srcdir, "rundir")
|
10
|
+
|
11
|
+
session = Session(srcdir, rundir)
|
12
|
+
|
13
|
+
package = session.load(srcdir)
|
14
|
+
|
15
|
+
graphs = []
|
16
|
+
for task in args.tasks:
|
17
|
+
if task.find(".") == -1:
|
18
|
+
task = package.name + "." + task
|
19
|
+
subgraph = session.mkTaskGraph(task)
|
20
|
+
graphs.append(subgraph)
|
21
|
+
|
22
|
+
awaitables = [subgraph.do_run() for subgraph in graphs]
|
23
|
+
print("%d awaitables" % len(awaitables))
|
24
|
+
|
25
|
+
out = asyncio.get_event_loop().run_until_complete(asyncio.gather(*awaitables))
|
26
|
+
|
27
|
+
print("out: %s" % str(out))
|
28
|
+
|
dv_flow_mgr/fileset.py
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
#****************************************************************************
|
2
|
+
#* fileset.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 pydantic.dataclasses as dc
|
23
|
+
from pydantic import BaseModel
|
24
|
+
from typing import Any, Dict, List, Tuple
|
25
|
+
|
26
|
+
class FileSet(BaseModel):
|
27
|
+
src : str
|
28
|
+
type : str
|
29
|
+
basedir : str
|
30
|
+
files : List[str] = dc.Field(default_factory=list)
|
31
|
+
params : Dict[str,str] = dc.Field(default_factory=dict)
|
dv_flow_mgr/flow.py
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
#****************************************************************************
|
2
|
+
#* flow.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
|
+
from pydantic import BaseModel, Field
|
23
|
+
from typing import ClassVar
|
24
|
+
#from .task import Task
|
25
|
+
|
26
|
+
class Flow(BaseModel):
|
27
|
+
# - Parameters are user-facing
|
28
|
+
# - Any implementation data must be stored elsewhere, such that it isn't
|
29
|
+
# checked for equality...
|
30
|
+
name : str
|
31
|
+
description : str = Field(None)
|
32
|
+
|
33
|
+
|
34
|
+
@classmethod
|
35
|
+
def mk(cls, *args, **kwargs):
|
36
|
+
pass
|
37
|
+
|
38
|
+
async def my_method(self):
|
39
|
+
return Task(a,b,c)(self, input)
|
40
|
+
|
41
|
+
#@extend(target)
|
42
|
+
#class FlowExt(object):
|
43
|
+
# pass
|
44
|
+
|
45
|
+
|
46
|
+
class Flow2(Flow):
|
47
|
+
description : str = "abc"
|
48
|
+
|
49
|
+
async def my_method(self):
|
50
|
+
super().my_method()
|
51
|
+
|
52
|
+
f = Flow2(name="foo")
|
53
|
+
|
54
|
+
#for d in dir(f):
|
55
|
+
# if not d.startswith("_"):
|
56
|
+
# print("%s: %s" % (d, str(getattr(f, d))))
|
57
|
+
|
58
|
+
|
59
|
+
|
@@ -0,0 +1,46 @@
|
|
1
|
+
#****************************************************************************
|
2
|
+
#* fragment_def.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 pydantic.dataclasses as dc
|
23
|
+
import json
|
24
|
+
from pydantic import BaseModel
|
25
|
+
from typing import Any, Dict, List
|
26
|
+
from .flow import Flow
|
27
|
+
from .package import Package
|
28
|
+
from .package_def import PackageImportSpec
|
29
|
+
from .task import TaskParamCtor
|
30
|
+
from .task_def import TaskDef, TaskSpec
|
31
|
+
|
32
|
+
class FragmentDef(BaseModel):
|
33
|
+
tasks : List[TaskDef] = dc.Field(default_factory=list)
|
34
|
+
imports : List[(str|PackageImportSpec)] = dc.Field(default_factory=list, alias="imports")
|
35
|
+
fragments: List[str] = dc.Field(default_factory=list)
|
36
|
+
|
37
|
+
basedir : str = None
|
38
|
+
|
39
|
+
def getTask(self, name : str) -> 'TaskDef':
|
40
|
+
for t in self.tasks:
|
41
|
+
if t.name == name:
|
42
|
+
return t
|
43
|
+
|
44
|
+
def apply(self, session, pkg : Package):
|
45
|
+
pass
|
46
|
+
|
dv_flow_mgr/package.py
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
#****************************************************************************
|
2
|
+
#* package.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 dataclasses as dc
|
23
|
+
import json
|
24
|
+
from pydantic import BaseModel
|
25
|
+
from typing import Any, Callable, Dict, List, Tuple
|
26
|
+
from .flow import Flow
|
27
|
+
from .task import TaskParams, TaskCtor
|
28
|
+
from .task_def import TaskDef
|
29
|
+
|
30
|
+
class PackageAcc(object):
|
31
|
+
pkg_spec : 'PackageSpec'
|
32
|
+
session : 'Session'
|
33
|
+
pkg : 'Package' = None
|
34
|
+
|
35
|
+
def getPackage(self) -> 'Package':
|
36
|
+
if self.pkg is None:
|
37
|
+
self.pkg = self.session.getPackage(self.pkg_spec)
|
38
|
+
return self.pkg
|
39
|
+
|
40
|
+
@dc.dataclass
|
41
|
+
class Package(object):
|
42
|
+
name : str
|
43
|
+
params : Dict[str,Any] = dc.field(default_factory=dict)
|
44
|
+
# Package holds constructors for tasks
|
45
|
+
# - Dict holds the default parameters for the task
|
46
|
+
tasks : Dict[str,TaskCtor] = dc.field(default_factory=dict)
|
47
|
+
imports : List['PackageAcc'] = dc.field(default_factory=list)
|
48
|
+
|
49
|
+
def getPackage(self, name : str) -> 'Package':
|
50
|
+
for p in self.imports:
|
51
|
+
if p.name == name:
|
52
|
+
return p.getPackage()
|
53
|
+
|
54
|
+
def getTaskCtor(self, name : str) -> TaskCtor:
|
55
|
+
return self.tasks[name]
|
56
|
+
|
57
|
+
def mkTaskParams(self, name : str) -> TaskParams:
|
58
|
+
if name not in self.tasks:
|
59
|
+
raise Exception("Task " + name + " not found")
|
60
|
+
return self.tasks[name].mkTaskParams()
|
61
|
+
|
62
|
+
def setTaskParams(self, name : str, params : TaskParams, pvals : Dict[str,Any]):
|
63
|
+
if name not in self.tasks:
|
64
|
+
raise Exception("Task " + name + " not found")
|
65
|
+
self.tasks[name].setTaskParams(params, pvals)
|
66
|
+
|
67
|
+
def mkTask(self,
|
68
|
+
name : str,
|
69
|
+
task_id : int,
|
70
|
+
session : 'Session',
|
71
|
+
params : TaskParams,
|
72
|
+
depends : List['Task']) -> 'Task':
|
73
|
+
# TODO: combine parameters to create the full taskname
|
74
|
+
task = self.tasks[name].mkTask(name, task_id, session, params, depends)
|
75
|
+
return task
|
76
|
+
|
77
|
+
def __hash__(self):
|
78
|
+
return hash(self.fullname())
|
79
|
+
|
@@ -0,0 +1,97 @@
|
|
1
|
+
#****************************************************************************
|
2
|
+
#* package_def.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 pydantic.dataclasses as dc
|
23
|
+
import json
|
24
|
+
from pydantic import BaseModel
|
25
|
+
from typing import Any, Dict, List
|
26
|
+
from .flow import Flow
|
27
|
+
from .package import Package
|
28
|
+
from .task import TaskParamCtor
|
29
|
+
from .task_def import TaskDef, TaskSpec
|
30
|
+
|
31
|
+
@dc.dataclass
|
32
|
+
class PackageSpec(object):
|
33
|
+
name : str
|
34
|
+
params : Dict[str,Any] = dc.Field(default_factory=dict)
|
35
|
+
_fullname : str = None
|
36
|
+
|
37
|
+
def get_fullname(self) -> str:
|
38
|
+
if self._fullname is None:
|
39
|
+
if len(self.params) != 0:
|
40
|
+
self._fullname = "%s%s}" % (
|
41
|
+
self.name,
|
42
|
+
json.dumps(self.params, separators=(',', ':')))
|
43
|
+
else:
|
44
|
+
self._fullname = self.name
|
45
|
+
return self._fullname
|
46
|
+
|
47
|
+
def __hash__(self):
|
48
|
+
return hash(self.get_fullname())
|
49
|
+
|
50
|
+
def __eq__(self, value):
|
51
|
+
return isinstance(value, PackageSpec) and value.get_fullname() == self.get_fullname()
|
52
|
+
|
53
|
+
@dc.dataclass
|
54
|
+
class PackageImportSpec(PackageSpec):
|
55
|
+
path : str = dc.Field(default=None, alias="from")
|
56
|
+
alias : str = dc.Field(default=None, alias="as")
|
57
|
+
|
58
|
+
class PackageDef(BaseModel):
|
59
|
+
name : str
|
60
|
+
params : Dict[str,Any] = dc.Field(default_factory=dict)
|
61
|
+
type : List[PackageSpec] = dc.Field(default_factory=list)
|
62
|
+
tasks : List[TaskDef] = dc.Field(default_factory=list)
|
63
|
+
imports : List[PackageImportSpec] = dc.Field(default_factory=list)
|
64
|
+
fragments: List[str] = dc.Field(default_factory=list)
|
65
|
+
|
66
|
+
# import_m : Dict['PackageSpec','Package'] = dc.Field(default_factory=dict)
|
67
|
+
|
68
|
+
basedir : str = None
|
69
|
+
|
70
|
+
def getTask(self, name : str) -> 'TaskDef':
|
71
|
+
for t in self.tasks:
|
72
|
+
if t.name == name:
|
73
|
+
return t
|
74
|
+
|
75
|
+
def mkPackage(self, session, params : Dict[str,Any] = None) -> 'Package':
|
76
|
+
ret = Package(self.name)
|
77
|
+
|
78
|
+
for task in self.tasks:
|
79
|
+
if task.type is not None:
|
80
|
+
# Find package (not package_def) that implements this task
|
81
|
+
# Insert an indirect reference to that tasks's constructor
|
82
|
+
|
83
|
+
# Only call getTaskCtor if the task is in a different package
|
84
|
+
task_t = task.type if isinstance(task.type, TaskSpec) else TaskSpec(task.type)
|
85
|
+
ctor_t = session.getTaskCtor(task_t, self)
|
86
|
+
|
87
|
+
ctor_t = TaskParamCtor(
|
88
|
+
base=ctor_t,
|
89
|
+
params=task.params,
|
90
|
+
basedir=self.basedir,
|
91
|
+
depend_refs=task.depends)
|
92
|
+
else:
|
93
|
+
raise Exception("")
|
94
|
+
ret.tasks[task.name] = ctor_t
|
95
|
+
|
96
|
+
return ret
|
97
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
#****************************************************************************
|
2
|
+
#* parameters.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
|
+
|
23
|
+
class Parameters(object):
|
24
|
+
|
25
|
+
def __init__(self):
|
26
|
+
pass
|
27
|
+
|
dv_flow_mgr/session.py
ADDED
@@ -0,0 +1,247 @@
|
|
1
|
+
#****************************************************************************
|
2
|
+
#* session.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 asyncio
|
23
|
+
import os
|
24
|
+
import yaml
|
25
|
+
import dataclasses as dc
|
26
|
+
from typing import Any, Callable, Dict, List
|
27
|
+
from .package import Package
|
28
|
+
from .package_def import PackageDef, PackageSpec
|
29
|
+
from .task import Task,TaskSpec
|
30
|
+
|
31
|
+
@dc.dataclass
|
32
|
+
class Session(object):
|
33
|
+
"""Manages the running of a flow"""
|
34
|
+
|
35
|
+
srcdir : str
|
36
|
+
rundir : str
|
37
|
+
|
38
|
+
# Search path for .dfs files
|
39
|
+
package_path : List[str] = dc.field(default_factory=list)
|
40
|
+
package : PackageDef = None
|
41
|
+
create_subprocess : Callable = asyncio.create_subprocess_exec
|
42
|
+
_root_dir : str = None
|
43
|
+
_pkg_s : List[Package] = dc.field(default_factory=list)
|
44
|
+
_pkg_m : Dict[PackageSpec,Package] = dc.field(default_factory=dict)
|
45
|
+
_pkg_spec_s : List[PackageDef] = dc.field(default_factory=list)
|
46
|
+
_pkg_def_m : Dict[str,PackageDef] = dc.field(default_factory=dict)
|
47
|
+
_task_list : List[Task] = dc.field(default_factory=list)
|
48
|
+
_task_m : Dict[TaskSpec,Task] = dc.field(default_factory=dict)
|
49
|
+
_task_id : int = 0
|
50
|
+
_rundir_s : List[str] = dc.field(default_factory=list)
|
51
|
+
|
52
|
+
def __post_init__(self):
|
53
|
+
from .tasklib.std.pkg_std import PackageStd
|
54
|
+
from .tasklib.hdl.sim.vlt_pkg import VltPackage
|
55
|
+
from .tasklib.hdl.sim.mti_pkg import MtiPackage
|
56
|
+
self._pkg_m[PackageSpec("std")] = PackageStd("std")
|
57
|
+
self._pkg_m[PackageSpec("hdl.sim.mti")] = MtiPackage("hdl.sim.mti")
|
58
|
+
self._pkg_m[PackageSpec("hdl.sim.vlt")] = VltPackage("hdl.sim.vlt")
|
59
|
+
|
60
|
+
def load(self, root : str):
|
61
|
+
if not os.path.isdir(root):
|
62
|
+
raise Exception("Root directory %s does not exist" % root)
|
63
|
+
|
64
|
+
if not os.path.isfile(os.path.join(root, "flow.yaml")):
|
65
|
+
raise Exception("No root flow file")
|
66
|
+
|
67
|
+
self._root_dir = os.path.dirname(root)
|
68
|
+
self.package = self._load_package(os.path.join(root, "flow.yaml"), [])
|
69
|
+
|
70
|
+
return self.package
|
71
|
+
|
72
|
+
def mkTaskGraph(self, task : str) -> Task:
|
73
|
+
self._pkg_s.clear()
|
74
|
+
self._task_m.clear()
|
75
|
+
|
76
|
+
self._rundir_s.clear()
|
77
|
+
self._rundir_s.append(self.rundir)
|
78
|
+
|
79
|
+
return self._mkTaskGraph(task)
|
80
|
+
|
81
|
+
def _mkTaskGraph(self, task : str, params : dict = None) -> Task:
|
82
|
+
|
83
|
+
elems = task.split(".")
|
84
|
+
|
85
|
+
pkg_name = ".".join(elems[0:-1])
|
86
|
+
task_name = elems[-1]
|
87
|
+
|
88
|
+
self._rundir_s.append(os.path.join(self._rundir_s[-1], pkg_name, task_name))
|
89
|
+
|
90
|
+
if pkg_name == "":
|
91
|
+
if len(self._pkg_spec_s) == 0:
|
92
|
+
raise Exception("No package context for %s" % task)
|
93
|
+
pkg_spec = self._pkg_spec_s[-1]
|
94
|
+
else:
|
95
|
+
pkg_spec = PackageSpec(pkg_name)
|
96
|
+
|
97
|
+
self._pkg_spec_s.append(pkg_spec)
|
98
|
+
pkg = self.getPackage(pkg_spec)
|
99
|
+
|
100
|
+
self._pkg_s.append(pkg)
|
101
|
+
|
102
|
+
#task_def = pkg.getTask(task_name)
|
103
|
+
|
104
|
+
depends = []
|
105
|
+
|
106
|
+
params = pkg.mkTaskParams(task_name)
|
107
|
+
|
108
|
+
task_id = self.mkTaskId(None)
|
109
|
+
# task_name = "%s.%s" % (pkg.name, task_def.name)
|
110
|
+
|
111
|
+
# The returned task should have all param references resolved
|
112
|
+
task = pkg.mkTask(
|
113
|
+
task_name,
|
114
|
+
task_id,
|
115
|
+
self,
|
116
|
+
params,
|
117
|
+
depends)
|
118
|
+
task.rundir = self._rundir_s[-1]
|
119
|
+
|
120
|
+
for i,d in enumerate(task.depend_refs):
|
121
|
+
if d in self._task_m.keys():
|
122
|
+
task.depends.append(self._task_m[d])
|
123
|
+
else:
|
124
|
+
print("mkTaskGraph: %s" % d)
|
125
|
+
task.depends.append(self._mkTaskGraph(d))
|
126
|
+
|
127
|
+
self._task_m[task.name] = task
|
128
|
+
|
129
|
+
self._pkg_s.pop()
|
130
|
+
self._pkg_spec_s.pop()
|
131
|
+
self._rundir_s.pop()
|
132
|
+
|
133
|
+
return task
|
134
|
+
|
135
|
+
def mkTaskId(self, task : 'Task') -> int:
|
136
|
+
self._task_id += 1
|
137
|
+
# TODO: save task <-> id map for later?
|
138
|
+
return self._task_id
|
139
|
+
|
140
|
+
async def run(self, task : str) -> 'TaskData':
|
141
|
+
impl = self.mkTaskGraph(task)
|
142
|
+
return await impl.do_run()
|
143
|
+
|
144
|
+
def _load_package(self, root : str, file_s : List[str]) -> PackageDef:
|
145
|
+
if root in file_s:
|
146
|
+
raise Exception("Recursive file processing @ %s: %s" % (root, ",".join(self._file_s)))
|
147
|
+
file_s.append(root)
|
148
|
+
ret = None
|
149
|
+
with open(root, "r") as fp:
|
150
|
+
doc = yaml.load(fp, Loader=yaml.FullLoader)
|
151
|
+
if "package" not in doc.keys():
|
152
|
+
raise Exception("Missing 'package' key in %s" % root)
|
153
|
+
pkg = PackageDef(**(doc["package"]))
|
154
|
+
pkg.basedir = os.path.dirname(root)
|
155
|
+
|
156
|
+
# for t in pkg.tasks:
|
157
|
+
# t.basedir = os.path.dirname(root)
|
158
|
+
|
159
|
+
if not len(self._pkg_spec_s):
|
160
|
+
self._pkg_spec_s.append(PackageSpec(pkg.name))
|
161
|
+
self._pkg_def_m[PackageSpec(pkg.name)] = pkg
|
162
|
+
else:
|
163
|
+
if self._pkg_spec_s[0].name != pkg.name:
|
164
|
+
raise Exception("Package name mismatch: %s != %s" % (self._pkg_m[0].name, pkg.name))
|
165
|
+
else:
|
166
|
+
# TODO: merge content
|
167
|
+
self._pkg_spec_s.append(PackageSpec(pkg.name))
|
168
|
+
|
169
|
+
print("pkg: %s" % str(pkg))
|
170
|
+
|
171
|
+
# TODO: read in fragments
|
172
|
+
|
173
|
+
self._pkg_spec_s.pop()
|
174
|
+
file_s.pop()
|
175
|
+
|
176
|
+
return pkg
|
177
|
+
|
178
|
+
def getPackage(self, spec : PackageSpec) -> Package:
|
179
|
+
pkg_spec = self._pkg_spec_s[-1]
|
180
|
+
pkg_def = self._pkg_def_m[pkg_spec]
|
181
|
+
|
182
|
+
# Need a stack to track which package we are currently in
|
183
|
+
# Need a map to get a concrete package from a name with parameterization
|
184
|
+
|
185
|
+
# Note: _pkg_m needs to be context specific, such that imports from
|
186
|
+
# one package don't end up visible in another
|
187
|
+
if spec in self._pkg_m.keys():
|
188
|
+
pkg = self._pkg_m[spec]
|
189
|
+
elif spec in self._pkg_def_m.keys():
|
190
|
+
pkg = self._pkg_def_m[spec].mkPackage(self)
|
191
|
+
self._pkg_m[spec] = pkg
|
192
|
+
else:
|
193
|
+
pkg = None
|
194
|
+
print("imports: %s" % str(pkg_def.imports))
|
195
|
+
for imp in pkg_def.imports:
|
196
|
+
print("imp: %s" % str(imp))
|
197
|
+
if imp.alias is not None and imp.alias == spec.name:
|
198
|
+
# Found the alias name. Just need to get an instance of this package
|
199
|
+
tgt_pkg_spec = PackageSpec(imp.name)
|
200
|
+
if tgt_pkg_spec in self._pkg_m.keys():
|
201
|
+
pkg = self._pkg_m[tgt_pkg_spec]
|
202
|
+
elif tgt_pkg_spec in self._pkg_def_m.keys():
|
203
|
+
base = self._pkg_def_m[tgt_pkg_spec]
|
204
|
+
pkg = base.mkPackage(self, spec.params)
|
205
|
+
self._pkg_m[spec] = pkg
|
206
|
+
elif imp.path is not None:
|
207
|
+
# See if we can load the package
|
208
|
+
print("TODO: load referenced package")
|
209
|
+
else:
|
210
|
+
raise Exception("Import alias %s not found" % imp.name)
|
211
|
+
break
|
212
|
+
else:
|
213
|
+
# Need to compare the spec with the full import spec
|
214
|
+
imp_spec = PackageSpec(imp.name)
|
215
|
+
# TODO: set parameters
|
216
|
+
if imp_spec == spec:
|
217
|
+
base = self._pkg_def_m[PackageSpec(spec.name)]
|
218
|
+
pkg = base.mkPackage(self, spec.params)
|
219
|
+
self._pkg_m[spec] = pkg
|
220
|
+
break
|
221
|
+
|
222
|
+
if pkg is None:
|
223
|
+
raise Exception("Failed to find package %s from package %s" % (
|
224
|
+
spec.name, pkg_def.name))
|
225
|
+
|
226
|
+
# base_spec = PackageSpec(spec.name)
|
227
|
+
# if not base_spec in self._pkg_def_m.keys():
|
228
|
+
# # Template is not present. Go find it...
|
229
|
+
#
|
230
|
+
# # If not found...
|
231
|
+
# raise Exception("Package %s not found" % spec.name)
|
232
|
+
|
233
|
+
return pkg
|
234
|
+
|
235
|
+
def getTaskCtor(self, spec : TaskSpec, pkg : PackageDef) -> 'TaskCtor':
|
236
|
+
spec_e = spec.name.split(".")
|
237
|
+
task_name = spec_e[-1]
|
238
|
+
pkg_name = ".".join(spec_e[0:-1])
|
239
|
+
|
240
|
+
try:
|
241
|
+
pkg = self.getPackage(PackageSpec(pkg_name))
|
242
|
+
except Exception as e:
|
243
|
+
print("Failed to find package %s while looking for task %s" % (pkg_name, spec.name))
|
244
|
+
raise e
|
245
|
+
|
246
|
+
return pkg.getTaskCtor(task_name)
|
247
|
+
|