Skip to content

Commit b58d34a

Browse files
rw1nklerQuantamHD
authored andcommitted
Improve python dependency handling in cocotb_test rule
The changes made in this commit include improvements that allow the `py_library` targets to be properly passed to the `cocotb_test` rule via the `deps` attribute. Now all transitive imports are properly added to `PYTHONPATH`. The `test_module` attribute, on the other hand, is intended to contain only python source files. Internal-tag: [#46586] Signed-off-by: Robert Winkler <[email protected]>
1 parent da175e9 commit b58d34a

File tree

1 file changed

+66
-33
lines changed

1 file changed

+66
-33
lines changed

cocotb/cocotb.bzl

Lines changed: 66 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
load("//verilog:providers.bzl", "VerilogInfo")
1818
load("@rules_python//python:defs.bzl", "PyInfo")
1919

20+
## Helpers for parsing arguments
21+
2022
def _list_to_argstring(data, argname, attr = None, operation = None):
2123
result = " --{}".format(argname) if data else ""
2224
for value in data:
@@ -45,6 +47,8 @@ def _remove_duplicates_from_list(data):
4547
result.append(e)
4648
return result
4749

50+
# Helpers for collecting information from context
51+
4852
def _collect_verilog_files(ctx):
4953
transitive_srcs_list = [
5054
dep
@@ -59,21 +63,58 @@ def _collect_verilog_files(ctx):
5963
verilog_info_struct.srcs
6064
for verilog_info_struct in transitive_srcs_depset.to_list()
6165
]
62-
verilog_files = depset(
66+
67+
return depset(
6368
[src for sub_tuple in verilog_srcs for src in sub_tuple] +
6469
ctx.files.verilog_sources,
6570
)
66-
return verilog_files.to_list()
6771

6872
def _collect_vhdl_files(ctx):
69-
return ctx.files.vhdl_sources
73+
return depset(direct = ctx.files.vhdl_sources)
7074

71-
def _cocotb_test_impl(ctx):
72-
# prepare arguments for the test command
73-
vhdl_files = _collect_vhdl_files(ctx)
74-
vhdl_sources_args = _files_to_argstring(vhdl_files, "vhdl_sources")
75+
def _collect_python_transitive_imports(ctx):
76+
return depset(transitive = [
77+
dep[PyInfo].imports
78+
for dep in ctx.attr.deps
79+
if PyInfo in dep
80+
])
81+
82+
def _collect_python_direct_imports(ctx):
83+
return depset(direct = [module.dirname for module in ctx.files.test_module])
84+
85+
def _collect_transitive_files(ctx):
86+
py_toolchain = ctx.toolchains["@bazel_tools//tools/python:toolchain_type"].py3_runtime
87+
return depset(
88+
direct = [py_toolchain.interpreter],
89+
transitive = [dep[PyInfo].transitive_sources for dep in ctx.attr.deps] +
90+
[ctx.attr.cocotb_wrapper[PyInfo].transitive_sources] +
91+
[py_toolchain.files],
92+
)
93+
94+
def _collect_transitive_runfiles(ctx):
95+
return ctx.runfiles().merge_all(
96+
[dep.default_runfiles for dep in ctx.attr.deps] +
97+
[dep.default_runfiles for dep in ctx.attr.sim],
98+
)
99+
100+
# Helpers for preparing test script and its environment
101+
102+
def _get_pythonpath_to_set(ctx):
103+
direct_imports = _collect_python_direct_imports(ctx).to_list()
104+
transitive_imports = [
105+
"../" + path
106+
for path in _collect_python_transitive_imports(ctx).to_list()
107+
]
108+
imports = _remove_duplicates_from_list(transitive_imports + direct_imports)
109+
return ":".join(imports)
110+
111+
def _get_path_to_set(ctx):
112+
sim_paths = _remove_duplicates_from_list([dep.label.workspace_root for dep in ctx.attr.sim])
113+
path = ":".join(["$PWD/" + str(p) for p in sim_paths])
114+
return path
75115

76-
verilog_files = _collect_verilog_files(ctx)
116+
def _get_test_command(ctx, verilog_files, vhdl_files):
117+
vhdl_sources_args = _files_to_argstring(vhdl_files, "vhdl_sources")
77118
verilog_sources_args = _files_to_argstring(verilog_files, "verilog_sources")
78119

79120
includes_args = _list_to_argstring(ctx.attr.includes, "includes")
@@ -86,21 +127,14 @@ def _cocotb_test_impl(ctx):
86127

87128
defines_args = _dict_to_argstring(ctx.attr.defines, "defines")
88129
parameters_args = _dict_to_argstring(ctx.attr.parameters, "parameters")
89-
90130
verbose_args = " --verbose" if ctx.attr.verbose else ""
91131
waves_args = " --waves" if ctx.attr.waves else ""
92132
seed_args = " --seed {}".format(ctx.attr.seed) if ctx.attr.seed != "" else ""
93133

94134
test_module_args = _pymodules_to_argstring(ctx.files.test_module, "test_module")
95135

96-
# define a script and a command
97-
runner_script = ctx.actions.declare_file("cocotb_runner.sh")
98-
99-
sim_paths = _remove_duplicates_from_list([dep.label.workspace_root for dep in ctx.attr.sim])
100-
path = ":".join(["$PWD/" + str(p) for p in sim_paths])
101-
102136
command = (
103-
"PATH={}:$PATH ".format(path) +
137+
"PATH={}:$PATH ".format(_get_path_to_set(ctx)) +
104138
"python {}".format(ctx.executable.cocotb_wrapper.short_path) +
105139
" --sim {}".format(ctx.attr.sim_name) +
106140
" --hdl_library {}".format(ctx.attr.hdl_library) +
@@ -123,33 +157,32 @@ def _cocotb_test_impl(ctx):
123157
test_module_args
124158
)
125159

126-
ctx.actions.write(output = runner_script, content = command)
160+
return command
127161

128-
# specify dependencies for the script
129-
py_toolchain = ctx.toolchains["@bazel_tools//tools/python:toolchain_type"].py3_runtime
130-
transitive_files = depset(
131-
direct = [py_toolchain.interpreter],
132-
transitive = [dep[PyInfo].transitive_sources for dep in ctx.attr.deps] +
133-
[ctx.attr.cocotb_wrapper[PyInfo].transitive_sources] +
134-
[py_toolchain.files],
162+
def _cocotb_test_impl(ctx):
163+
verilog_files = _collect_verilog_files(ctx).to_list()
164+
vhdl_files = _collect_vhdl_files(ctx).to_list()
165+
166+
# create test script
167+
runner_script = ctx.actions.declare_file("cocotb_runner.sh")
168+
ctx.actions.write(
169+
output = runner_script,
170+
content = _get_test_command(ctx, verilog_files, vhdl_files),
135171
)
136172

173+
# specify dependencies for the script
137174
runfiles = ctx.runfiles(
138175
files = ctx.files.cocotb_wrapper +
139176
verilog_files +
140177
vhdl_files +
141178
ctx.files.test_module,
142-
transitive_files = transitive_files,
143-
).merge_all(
144-
[dep.default_runfiles for dep in ctx.attr.deps] +
145-
[dep.default_runfiles for dep in ctx.attr.test_module] +
146-
[dep.default_runfiles for dep in ctx.attr.sim],
179+
transitive_files = _collect_transitive_files(ctx),
180+
).merge(
181+
_collect_transitive_runfiles(ctx),
147182
)
148183

149-
# specify pythonpath for the script
150-
test_module_paths = _remove_duplicates_from_list([module.dirname for module in ctx.files.test_module])
151-
pypath = ":".join([str(p) for p in test_module_paths])
152-
env = {"PYTHONPATH": pypath}
184+
# specify PYTHONPATH for the script
185+
env = {"PYTHONPATH": _get_pythonpath_to_set(ctx)}
153186

154187
# return the information about testing script and its dependencies
155188
return [

0 commit comments

Comments
 (0)