- CitrineOS core extracted (CSMS OCPP 2.0.1) - OpenOCPP extracted (firmware OCPP 1.6J/2.0.1) - ShapeShifter library installed (pip install -e) - ShapeShifter specification extracted - EVerest extracted TODO updated with progress
308 lines
9.6 KiB
Python
308 lines
9.6 KiB
Python
load("@rules_python//python:defs.bzl", "PyInfo")
|
|
load("//third-party/bazel/toolchains:defs.bzl", "CROSS_PYTHON_INCOMPATIBLE")
|
|
|
|
def _everest_env(ctx):
|
|
"""Everest Root rule
|
|
|
|
Rule creates a everest root from provided modules and config file.
|
|
"""
|
|
|
|
# Validate the input - we make sure that the set of provided modules matches
|
|
# the set of modules declared in config.yaml
|
|
validation_output = ctx.actions.declare_file(ctx.attr.name + ".validation")
|
|
ctx.actions.run(
|
|
inputs = ctx.attr.config_file[DefaultInfo].files,
|
|
outputs = [validation_output],
|
|
executable = ctx.executable._validation_tool,
|
|
arguments = [
|
|
"--output",
|
|
validation_output.path,
|
|
"--config",
|
|
ctx.attr.config_file[DefaultInfo].files.to_list()[0].path,
|
|
"--",
|
|
] +
|
|
[mod.label.name for mod in ctx.attr.modules],
|
|
)
|
|
|
|
symlinks = {}
|
|
files = []
|
|
py_toolchain = ctx.toolchains["@bazel_tools//tools/python:toolchain_type"]
|
|
py_interpreter = py_toolchain.py3_runtime.interpreter.dirname.removeprefix("external/")
|
|
py_imports = []
|
|
py_transitive_sources = []
|
|
|
|
# Python modules get a special handling.
|
|
for mod in ctx.attr.modules + ctx.attr.test_modules:
|
|
if PyInfo in mod:
|
|
py_imports.extend(mod[PyInfo].imports.to_list())
|
|
py_transitive_sources.extend(mod[PyInfo].transitive_sources.to_list())
|
|
|
|
for mod in ctx.attr.modules + ctx.attr.test_modules:
|
|
# Find the manifest in the data_runfiles and use its path as prefix.
|
|
manifest = [
|
|
file
|
|
for file in mod[DefaultInfo].data_runfiles.files.to_list()
|
|
if file.basename in ["manifest.yaml", "manifest.yml"]
|
|
][0]
|
|
prefix = manifest.dirname
|
|
|
|
symlinks.update(
|
|
{
|
|
"libexec/everest/modules/{0}{1}".format(
|
|
mod.label.name,
|
|
file.path.removeprefix(prefix),
|
|
): file
|
|
for file in mod[DefaultInfo].data_runfiles.files.to_list()
|
|
if file.path.startswith(prefix)
|
|
},
|
|
)
|
|
[
|
|
files.append(file)
|
|
for file in mod[DefaultInfo].default_runfiles.files.to_list()
|
|
if not file.path.startswith(prefix)
|
|
]
|
|
|
|
config_file = ctx.attr.config_file[DefaultInfo].files.to_list()[0]
|
|
config_path = "etc/everest/{0}".format(config_file.basename)
|
|
symlinks.update({"bin/manager_impl": ctx.attr.manager[DefaultInfo].files.to_list()[0]})
|
|
symlinks.update(
|
|
{
|
|
config_path: config_file,
|
|
},
|
|
)
|
|
symlinks.update(
|
|
{
|
|
"etc/everest/default_logging.cfg": ctx.attr.default_logging_file[DefaultInfo].files.to_list()[0],
|
|
},
|
|
)
|
|
|
|
# EVerest expects that there is a `share/everest/www` directory but does
|
|
# not care about the content... We just symlink the config.yaml into it.
|
|
symlinks.update(
|
|
{
|
|
"share/everest/www/config.yaml": ctx.attr.config_file[DefaultInfo].files.to_list()[0],
|
|
},
|
|
)
|
|
symlinks.update(
|
|
{
|
|
"share/everest/schemas/{0}".format(file.basename): file
|
|
for file in ctx.attr.schemas[DefaultInfo].files.to_list()
|
|
},
|
|
)
|
|
symlinks.update(
|
|
{
|
|
"share/everest/interfaces/{0}".format(file.basename): file
|
|
for interfaces in ctx.attr.interfaces
|
|
for file in interfaces[DefaultInfo].files.to_list()
|
|
},
|
|
)
|
|
symlinks.update(
|
|
{
|
|
"share/everest/types/{0}".format(file.basename): file
|
|
for types in ctx.attr.types
|
|
for file in types[DefaultInfo].files.to_list()
|
|
},
|
|
)
|
|
symlinks.update(
|
|
{
|
|
"share/everest/errors/{0}".format(file.basename): file
|
|
for errors in ctx.attr.errors
|
|
for file in errors[DefaultInfo].files.to_list()
|
|
},
|
|
)
|
|
|
|
# For the executable we need to export the python specific variables by
|
|
# hand.
|
|
script = ctx.actions.declare_file("manager_wrapper.{}".format(ctx.label.name))
|
|
script_content = """\
|
|
#!/bin/sh
|
|
set -eu
|
|
SCRIPT_DIR=$(cd "$(dirname "$0")/.." && pwd)
|
|
export PATH="$SCRIPT_DIR/{py_interpreter}:$PATH"
|
|
{pythonpath_lines}
|
|
""".format(
|
|
py_interpreter = py_interpreter.removeprefix("external/"),
|
|
pythonpath_lines = "\n".join([
|
|
'export PYTHONPATH="$SCRIPT_DIR/{0}:$PYTHONPATH"'.format(imp)
|
|
for imp in py_imports
|
|
]),
|
|
)
|
|
|
|
if ctx.attr._is_test:
|
|
script_content += """
|
|
exec bin/manager_impl --prefix . --config {0} --check
|
|
""".format(config_path)
|
|
else:
|
|
script_content += """
|
|
if [ $# -gt 0 ]; then
|
|
exec bin/manager_impl "$@"
|
|
else
|
|
exec bin/manager_impl --prefix . --config {0}
|
|
fi
|
|
""".format(config_path)
|
|
ctx.actions.write(script, script_content, is_executable = True)
|
|
symlinks.update({"bin/manager": script})
|
|
|
|
runfiles = ctx.runfiles(
|
|
symlinks = symlinks,
|
|
files = files + [script],
|
|
)
|
|
|
|
return [
|
|
DefaultInfo(
|
|
executable = script,
|
|
runfiles = runfiles,
|
|
),
|
|
OutputGroupInfo(_validation = depset([validation_output])),
|
|
PyInfo(
|
|
imports = depset(py_imports),
|
|
transitive_sources = depset(py_transitive_sources),
|
|
),
|
|
]
|
|
|
|
ATTRS = {
|
|
"config_file": attr.label(
|
|
doc = """
|
|
The EVerest configuration file. It will be linked to
|
|
`/etc/everest/<basename>`""",
|
|
allow_single_file = True,
|
|
),
|
|
"manager": attr.label(
|
|
doc = "The EVerest manager.",
|
|
default = Label("//lib/everest/framework:manager"),
|
|
allow_single_file = True,
|
|
executable = True,
|
|
cfg = "target",
|
|
),
|
|
"schemas": attr.label(
|
|
doc = "The target with the EVerest schemas.",
|
|
default = Label("//lib/everest/framework/schemas"),
|
|
),
|
|
"interfaces": attr.label_list(
|
|
doc = "A list of targets with EVerest interfaces.",
|
|
default = [
|
|
Label("//lib/everest/framework/everestrs/tests/interfaces"),
|
|
],
|
|
),
|
|
"types": attr.label_list(
|
|
doc = "A list of targets with EVerest types.",
|
|
default = [
|
|
Label("//lib/everest/framework/everestrs/tests/types"),
|
|
],
|
|
),
|
|
"errors": attr.label_list(
|
|
doc = "A list of targets with EVerest errors.",
|
|
default = [
|
|
Label("//lib/everest/framework/everestrs/tests/errors"),
|
|
],
|
|
),
|
|
"default_logging_file": attr.label(
|
|
doc = "The target with the EVerest logging.ini file.",
|
|
default = Label("//lib/everest/framework/everestrs/tests:logging.ini"),
|
|
allow_single_file = True,
|
|
),
|
|
"modules": attr.label_list(
|
|
doc = """
|
|
The list of targets with the EVerest modules under test.
|
|
|
|
The rule validates that the set of provided modules matches the set of modules
|
|
defined in the given `config_file`.""",
|
|
allow_files = False,
|
|
),
|
|
"test_modules": attr.label_list(
|
|
doc = """
|
|
The list of targets with EVerest modules which are only enabled by the
|
|
`everest.testing` framework.
|
|
|
|
The rule will not enforce that these modules are defined in the given
|
|
`config_file`.
|
|
""",
|
|
allow_files = False,
|
|
),
|
|
"_validation_tool": attr.label(
|
|
default = Label("//lib/everest/framework/bazel/validate"),
|
|
executable = True,
|
|
cfg = "exec",
|
|
),
|
|
"_is_test": attr.bool(
|
|
default = False,
|
|
doc = "Indicates if target is test target to validate config",
|
|
),
|
|
}
|
|
|
|
everest_impl_env = rule(
|
|
implementation = _everest_env,
|
|
attrs = ATTRS,
|
|
executable = True,
|
|
toolchains = ["@bazel_tools//tools/python:toolchain_type"],
|
|
)
|
|
|
|
_everest_impl_test = rule(
|
|
implementation = _everest_env,
|
|
attrs = dict(ATTRS, _is_test = attr.bool(default = True)),
|
|
toolchains = ["@bazel_tools//tools/python:toolchain_type"],
|
|
doc = """
|
|
Creates an EVerest Test.
|
|
|
|
Example:
|
|
|
|
Suppose you have the EVerest modules `ModuleFoo` and `ModuleBar` and the
|
|
EVerest config `my_config.yaml` which uses both modules. The test will launch
|
|
the modules and return when the manager process returns.
|
|
|
|
Then you can create an environment by writing:
|
|
|
|
```
|
|
everest_test(
|
|
name = "my_everest_env",
|
|
modules = [":ModuleFoo", ":ModuleBar"],
|
|
config_file = ":my_config.yaml",
|
|
test_script=":my_test_script",
|
|
)
|
|
|
|
```
|
|
|
|
You can run it with `bazel test`.
|
|
""",
|
|
test = True,
|
|
)
|
|
|
|
def everest_test(name, target_compatible_with = [], **kwargs):
|
|
"""Wrapper around the everest_test rule that marks the target as
|
|
incompatible with cross-compilation platforms (no Python toolchain)."""
|
|
_everest_impl_test(
|
|
name = name,
|
|
target_compatible_with = CROSS_PYTHON_INCOMPATIBLE + target_compatible_with,
|
|
**kwargs
|
|
)
|
|
|
|
def everest_env(name, target_compatible_with = [], **kwargs):
|
|
"""
|
|
Creates an EVerest environment.
|
|
|
|
Example:
|
|
|
|
Suppose you have the EVerest modules `ModuleFoo` and `ModuleBar` and the
|
|
EVerest config `my_config.yaml` which uses both modules.
|
|
|
|
Then you can create an environment by writing:
|
|
|
|
```
|
|
everest_env(
|
|
name = "my_everest_env",
|
|
modules = [":ModuleFoo", ":ModuleBar"],
|
|
config_file = ":my_config.yaml",
|
|
)
|
|
|
|
```
|
|
|
|
You can either run this target with `bazel run` or pass it for example to a (py)
|
|
test which will run your tests against the environment.
|
|
"""
|
|
everest_impl_env(
|
|
name = name,
|
|
target_compatible_with = CROSS_PYTHON_INCOMPATIBLE + target_compatible_with,
|
|
**kwargs
|
|
)
|
|
everest_test(name = name + "__manager_test", tags = ["exclusive"], target_compatible_with = target_compatible_with, **kwargs)
|