Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter

- 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
This commit is contained in:
Eric F
2026-06-08 00:38:27 -04:00
parent 468cfeaa50
commit d398a6ced2
7326 changed files with 1177561 additions and 7 deletions

View File

@@ -0,0 +1,902 @@
#!/usr/bin/env -S python3 -tt
# -*- coding: utf-8 -*-
#
# SPDX-License-Identifier: Apache-2.0
# Copyright Pionix GmbH and Contributors to EVerest
#
"""
author: aw@pionix.de
FIXME (aw): Module documentation.
"""
from ev_cli import __version__
from ev_cli import helpers
from ev_cli.type_parsing import TypeParser
from ev_cli.error_parsing import ErrorParser
from datetime import datetime
from pathlib import Path
import jinja2 as j2
import argparse
import stringcase
from typing import List
# FIXME (aw): remove these global variables
# global variables
everest_dirs: List[Path] = []
work_dir: Path = None
# jinja template environment and global variable
env = j2.Environment(loader=j2.FileSystemLoader(Path(__file__).parent / 'templates'),
lstrip_blocks=True, trim_blocks=True, undefined=j2.StrictUndefined,
keep_trailing_newline=True)
templates = {}
validators = {}
# Function declarations
def setup_jinja_env():
env.globals['timestamp'] = datetime.utcnow()
# FIXME (aw): which repo to use? everest or everest-framework?
env.filters['snake_case'] = helpers.snake_case
env.filters['create_dummy_result'] = helpers.create_dummy_result
templates.update({
'interface_base': env.get_template('interface-Base.hpp.j2'),
'interface_exports': env.get_template('interface-Exports.hpp.j2'),
'interface_impl.hpp': env.get_template('interface-Impl.hpp.j2'),
'interface_impl.cpp': env.get_template('interface-Impl.cpp.j2'),
'types.hpp': env.get_template('types.hpp.j2'),
'module.hpp': env.get_template('module.hpp.j2'),
'module.cpp': env.get_template('module.cpp.j2'),
'ld-ev.hpp': env.get_template('ld-ev.hpp.j2'),
'ld-ev.cpp': env.get_template('ld-ev.cpp.j2'),
'cmakelists': env.get_template('CMakeLists.txt.j2'),
'index.rst': env.get_template('index.rst.j2'),
})
def generate_tmpl_data_for_if(interface, if_def, type_file):
helpers.parsed_enums.clear()
helpers.parsed_types.clear()
helpers.type_headers.clear()
types = []
enums = []
vars = []
for var, var_info in if_def.get('vars', {}).items():
(type_info, enum_info) = helpers.extended_build_type_info(var, var_info, type_file)
if enum_info and type_file:
enums.append(enum_info)
vars.append(type_info)
cmds = []
for cmd, cmd_info in if_def.get('cmds', {}).items():
args = []
for arg, arg_info in cmd_info.get('arguments', {}).items():
(type_info, enum_info) = helpers.extended_build_type_info(arg, arg_info, type_file)
if enum_info and type_file:
enums.append(enum_info)
args.append(type_info)
result_type_info = None
if 'result' in cmd_info:
result_info = cmd_info['result']
(result_type_info, enum_info) = helpers.extended_build_type_info('result', result_info, type_file)
if enum_info and type_file:
enums.append(enum_info)
cmds.append({'name': cmd, 'args': args, 'result': result_type_info})
if type_file:
for parsed_enum in helpers.parsed_enums:
enum_info = {
'name': parsed_enum['name'],
'description': parsed_enum['description'],
'enum_type': stringcase.capitalcase(parsed_enum['name']),
'enum': parsed_enum['enums']
}
enums.append(enum_info)
if type_file:
for parsed_type in helpers.parsed_types:
parsed_type['name'] = stringcase.capitalcase(parsed_type['name'])
if 'properties' in parsed_type:
for prop in parsed_type['properties']:
if 'type_dict' in prop['info']:
path = Path('generated/types') / \
prop['info']['type_dict']['type_relative_path'].with_suffix('.hpp')
helpers.type_headers.add(path.as_posix())
types.append(parsed_type)
error_lists = if_def.get('errors', [])
# Use a dict to avoid duplicate error definitions
errors_dict = {}
for entry in error_lists:
if not 'reference' in entry:
raise Exception(f'Error definition {entry} does not have a reference.')
for error in ErrorParser.resolve_error_reference(entry['reference']):
if error.namespace not in errors_dict:
errors_dict[error.namespace] = {}
if error.name in errors_dict[error.namespace]:
raise Exception(f'Error definition {error.namespace}/{error.name} already referenced.')
errors_dict[error.namespace][error.name] = error
errors = []
for value in errors_dict.values():
errors.extend(value.values())
tmpl_data = {
'info': {
'base_class_header': f'generated/interfaces/{interface}/Implementation.hpp',
'interface': interface,
'desc': if_def['description'],
'type_headers': sorted(helpers.type_headers)
},
'enums': enums,
'types': types,
'vars': vars,
'cmds': cmds,
'errors': errors,
}
return tmpl_data
def generate_tmpl_data_for_module(module, module_def):
provides = []
for impl, impl_info in module_def.get('provides', {}).items():
config = []
for conf_id, conf_info in impl_info.get('config', {}).items():
type_info = helpers.build_type_info(conf_id, conf_info['type'])
config.append(type_info)
provides.append({
'id': impl,
'type': impl_info['interface'],
'desc': impl_info['description'],
'config': config,
'class_name': f'{impl_info["interface"]}Impl',
'base_class': f'{impl_info["interface"]}ImplBase',
'base_class_header': f'generated/interfaces/{impl_info["interface"]}/Implementation.hpp'
})
requires = []
for requirement_id, req_info in module_def.get('requires', {}).items():
# min_connections=1 and max_connections=1 is the default if not provided otherwise (see manifest meta schema)
is_vector = not (
('min_connections' not in req_info or req_info['min_connections'] == 1) and
('max_connections' not in req_info or req_info['max_connections'] == 1))
requires.append({
'id': requirement_id,
'is_vector': is_vector,
'type': req_info['interface'],
'class_name': f'{req_info["interface"]}Intf',
'exports_header': f'generated/interfaces/{req_info["interface"]}/Interface.hpp'
})
module_config = []
for conf_id, conf_info in module_def.get('config', {}).items():
type_info = helpers.build_type_info(conf_id, conf_info['type'])
module_config.append(type_info)
tmpl_data = {
'info': {
'name': module,
'class_name': module, # FIXME (aw): enforce capital case?
'desc': module_def['description'],
'module_header': f'{module}.hpp',
'module_config': module_config,
'ld_ev_header': 'ld-ev.hpp',
'enable_external_mqtt': module_def.get('enable_external_mqtt', False),
'enable_telemetry': module_def.get('enable_telemetry', False),
'enable_global_errors': module_def.get('enable_global_errors', False)
},
'provides': provides,
'requires': requires,
}
return tmpl_data
def construct_impl_file_paths(impl):
interface = impl['type']
common_part = f'{impl["id"]}/{interface}'
return (f'{common_part}Impl.hpp', f'{common_part}Impl.cpp')
def set_impl_specific_path_vars(tmpl_data, output_path):
"""Set cpp_file_rel_path and class_header vars to implementation template data."""
for impl in tmpl_data['provides']:
(impl['class_header'], impl['cpp_file_rel_path']) = construct_impl_file_paths(impl)
def generate_module_loader_files(rel_mod_dir, output_dir):
loader_files = []
(_, _, mod) = rel_mod_dir.rpartition('/')
mod_path = work_dir / f'modules/{rel_mod_dir}/manifest.yaml'
if not mod_path.exists():
raise Exception(f'Could not find module manifest ({mod_path}')
mod_def = helpers.load_validated_module_def(mod_path, validators['module'])
tmpl_data = generate_tmpl_data_for_module(mod, mod_def)
set_impl_specific_path_vars(tmpl_data, mod_path.parent)
# ld-ev.hpp
tmpl_data['info']['hpp_guard'] = 'LD_EV_HPP'
loader_files.append({
'filename': 'ld-ev.hpp',
'path': output_dir / mod / 'ld-ev.hpp',
'printable_name': f'{mod}/ld-ev.hpp',
'content': templates['ld-ev.hpp'].render(tmpl_data),
'template_path': Path(templates['ld-ev.hpp'].filename),
'last_mtime': mod_path.stat().st_mtime
})
# ld-ev.cpp
loader_files.append({
'filename': 'ld-ev.cpp',
'path': output_dir / mod / 'ld-ev.cpp',
'printable_name': f'{mod}/ld-ev.cpp',
'content': templates['ld-ev.cpp'].render(tmpl_data),
'template_path': Path(templates['ld-ev.cpp'].filename),
'last_mtime': mod_path.stat().st_mtime
})
return loader_files
def generate_module_files(rel_mod_dir, update_flag, licenses):
(_, _, mod) = rel_mod_dir.rpartition('/')
mod_files = {'core': [], 'interfaces': [], 'docs': []}
mod_path = work_dir / f'modules/{rel_mod_dir}/manifest.yaml'
mod_def = helpers.load_validated_module_def(mod_path, validators['module'])
default_license_dir = Path(__file__).parent / 'licenses'
current_license_dir = work_dir / 'licenses'
additional_license_dir = Path(licenses)
license_dirs = [default_license_dir, current_license_dir, additional_license_dir]
license_url = mod_def['metadata']['license']
license_header = helpers.get_license_header(license_dirs, license_url)
if not license_header:
print(f'Could not find license "{license_url}" in {license_dirs}.')
print('Consider providing a additonal custom license directory with --licenses')
exit(1)
tmpl_data = generate_tmpl_data_for_module(mod, mod_def)
output_path = mod_path.parent
# FIXME (aw): we might move the following function into generate_tmp_data_for_module
set_impl_specific_path_vars(tmpl_data, output_path)
cmakelists_blocks = {
'version': 'v1',
'format_str': '# ev@{uuid}:{version}',
'regex_str': '^(?P<indent>\s*)# ev@(?P<uuid>[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}):(?P<version>.*)$',
'definitions': {
'add_general': {
'id': 'bcc62523-e22b-41d7-ba2f-825b493a3c97',
'content': '# insert your custom targets and additional config variables here'
},
'add_other': {
'id': 'c55432ab-152c-45a9-9d2e-7281d50c69c3',
'content': '# insert other things like install cmds etc here'
}
}
}
impl_hpp_blocks = {
'version': 'v1',
'format_str': '// ev@{uuid}:{version}',
'regex_str': '^(?P<indent>\s*)// ev@(?P<uuid>[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}):(?P<version>.*)$',
'definitions': {
'add_headers': {
'id': '75ac1216-19eb-4182-a85c-820f1fc2c091',
'content': '// insert your custom include headers here'
},
'public_defs': {
'id': '8ea32d28-373f-4c90-ae5e-b4fcc74e2a61',
'content': '// insert your public definitions here'
},
'protected_defs': {
'id': 'd2d1847a-7b88-41dd-ad07-92785f06f5c4',
'content': '// insert your protected definitions here'
},
'private_defs': {
'id': '3370e4dd-95f4-47a9-aaec-ea76f34a66c9',
'content': '// insert your private definitions here'
},
'after_class': {
'id': '3d7da0ad-02c2-493d-9920-0bbbd56b9876',
'content': '// insert other definitions here'
}
}
}
mod_hpp_blocks = {
'version': 'v1',
'format_str': '// ev@{uuid}:{version}',
'regex_str': '^(?P<indent>\s*)// ev@(?P<uuid>[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}):(?P<version>.*)$',
'definitions': {
'add_headers': {
'id': '4bf81b14-a215-475c-a1d3-0a484ae48918',
'content': '// insert your custom include headers here'
},
'public_defs': {
'id': '1fce4c5e-0ab8-41bb-90f7-14277703d2ac',
'content': '// insert your public definitions here'
},
'protected_defs': {
'id': '4714b2ab-a24f-4b95-ab81-36439e1478de',
'content': '// insert your protected definitions here'
},
'private_defs': {
'id': '211cfdbe-f69a-4cd6-a4ec-f8aaa3d1b6c8',
'content': '// insert your private definitions here'
},
'after_class': {
'id': '087e516b-124c-48df-94fb-109508c7cda9',
'content': '// insert other definitions here'
}
}
}
# provided interface implementations (impl cpp & hpp)
for impl in tmpl_data['provides']:
interface = impl['type']
(impl_hpp_file, impl_cpp_file) = construct_impl_file_paths(impl)
# load template data for interface
if_def, last_mtime = load_interface_definition(interface)
if_tmpl_data = generate_tmpl_data_for_if(interface, if_def, False)
if_tmpl_data['info'].update({
'hpp_guard': helpers.snake_case(f'{impl["id"]}_{interface}').upper() + '_IMPL_HPP',
'config': impl['config'],
'class_name': interface + 'Impl',
'class_parent': interface + 'ImplBase',
'module_header': f'../{mod}.hpp',
'module_class': mod,
'interface_implementation_id': impl['id']
})
if_tmpl_data['info']['blocks'] = helpers.load_tmpl_blocks(
impl_hpp_blocks, output_path / impl_hpp_file, update_flag)
if_tmpl_data['info']['license_header'] = license_header
# FIXME (aw): time stamp should include parent interfaces modification dates
mod_files['interfaces'].append({
'abbr': f'{impl["id"]}.hpp',
'path': output_path / impl_hpp_file,
'printable_name': impl_hpp_file,
'content': templates['interface_impl.hpp'].render(if_tmpl_data),
'template_path': Path(templates['interface_impl.hpp'].filename),
'last_mtime': last_mtime,
'license_header': license_header
})
mod_files['interfaces'].append({
'abbr': f'{impl["id"]}.cpp',
'path': output_path / impl_cpp_file,
'printable_name': impl_cpp_file,
'content': templates['interface_impl.cpp'].render(if_tmpl_data),
'template_path': Path(templates['interface_impl.cpp'].filename),
'last_mtime': last_mtime,
'license_header': license_header
})
cmakelists_file = output_path / 'CMakeLists.txt'
tmpl_data['info']['blocks'] = helpers.load_tmpl_blocks(cmakelists_blocks, cmakelists_file, update_flag)
tmpl_data['info']['license_header'] = license_header
mod_files['core'].append({
'abbr': 'cmakelists',
'path': cmakelists_file,
'content': templates['cmakelists'].render(tmpl_data),
'template_path': Path(templates['cmakelists'].filename),
'last_mtime': mod_path.stat().st_mtime
})
# module.hpp
tmpl_data['info']['hpp_guard'] = helpers.snake_case(mod).upper() + '_HPP'
mod_hpp_file = output_path / f'{mod}.hpp'
tmpl_data['info']['blocks'] = helpers.load_tmpl_blocks(mod_hpp_blocks, mod_hpp_file, update_flag)
mod_files['core'].append({
'abbr': 'module.hpp',
'path': mod_hpp_file,
'content': templates['module.hpp'].render(tmpl_data),
'template_path': Path(templates['module.hpp'].filename),
'last_mtime': mod_path.stat().st_mtime,
'license_header': license_header
})
# module.cpp
mod_cpp_file = output_path / f'{mod}.cpp'
mod_files['core'].append({
'abbr': 'module.cpp',
'path': mod_cpp_file,
'content': templates['module.cpp'].render(tmpl_data),
'template_path': Path(templates['module.cpp'].filename),
'last_mtime': mod_path.stat().st_mtime,
'license_header': license_header
})
# docs/index.rst
mod_files['docs'].append({
'abbr': 'index.rst',
'path': output_path / 'docs' / 'index.rst',
'content': templates['index.rst'].render(tmpl_data),
'template_path': Path(templates['index.rst'].filename),
'last_mtime': mod_path.stat().st_mtime
})
for file_info in [*mod_files['core'], *mod_files['interfaces'], *mod_files['docs']]:
file_info['printable_name'] = file_info['path'].relative_to(output_path)
return mod_files
def load_interface_definition(interface):
if_path = helpers.resolve_everest_dir_path(f'interfaces/{interface}.yaml')
if_def = helpers.load_validated_interface_def(if_path, validators['interface'])
if 'vars' not in if_def:
if_def['vars'] = {}
if 'cmds' not in if_def:
if_def['cmds'] = {}
last_mtime = if_path.stat().st_mtime
return if_def, last_mtime
def generate_interface_headers(interface, all_interfaces_flag, output_dir):
if_parts = {'base': None, 'exports': None, 'types': None}
try:
if_def, last_mtime = load_interface_definition(interface)
except Exception as e:
if not all_interfaces_flag:
raise
else:
# FIXME (aw): should we really silently ignore that?
print(f'Ignoring interface {interface} with reason: {e}')
return
tmpl_data = generate_tmpl_data_for_if(interface, if_def, False)
output_path = output_dir / interface
output_path.mkdir(parents=True, exist_ok=True)
tmpl_data['info']['interface_name'] = f'{interface}'
tmpl_data['info']['namespace'] = [f'{interface}']
# generate Base file (providers view)
tmpl_data['info']['hpp_guard'] = helpers.snake_case(interface).upper() + '_IMPLEMENTATION_HPP'
tmpl_data['info']['class_name'] = f'{interface}ImplBase'
base_file = output_path / 'Implementation.hpp'
if_parts['base'] = {
'path': base_file,
'content': templates['interface_base'].render(tmpl_data),
'template_path': Path(templates['interface_base'].filename),
'last_mtime': last_mtime,
'printable_name': base_file.relative_to(output_path.parent)
}
# generate Exports file (users view)
tmpl_data['info']['hpp_guard'] = helpers.snake_case(interface).upper() + '_INTERFACE_HPP'
tmpl_data['info']['class_name'] = f'{interface}Intf'
exports_file = output_path / 'Interface.hpp'
if_parts['exports'] = {
'path': exports_file,
'content': templates['interface_exports'].render(tmpl_data),
'template_path': Path(templates['interface_exports'].filename),
'last_mtime': last_mtime,
'printable_name': exports_file.relative_to(output_path.parent)
}
# generate Types file
tmpl_data['info']['hpp_guard'] = helpers.snake_case(interface).upper() + '_TYPES_HPP'
types_file = output_path / 'Types.hpp'
if_parts['types'] = {
'path': types_file,
'content': templates['types.hpp'].render(tmpl_data),
'template_path': Path(templates['types.hpp'].filename),
'last_mtime': last_mtime,
'printable_name': types_file.relative_to(output_path.parent)
}
return if_parts
def module_create(args):
create_strategy = 'force-create' if args.force else 'create'
detected_projects = helpers.detect_everest_projects(args.everest_projects, args.build_dir)
if detected_projects:
helpers.everest_dirs.extend(detected_projects)
mod_files = generate_module_files(args.module, False, args.licenses)
if args.only == 'which':
helpers.print_available_mod_files(mod_files)
return
else:
try:
helpers.filter_mod_files(args.only, mod_files)
except Exception as err:
print(err)
return
for file_info in mod_files['core'] + mod_files['interfaces'] + mod_files['docs']:
if not args.disable_clang_format:
helpers.clang_format(args.clang_format_file, file_info)
helpers.write_content_to_file(file_info, create_strategy, args.diff)
def module_update(args):
detected_projects = helpers.detect_everest_projects(args.everest_projects, args.build_dir)
if detected_projects:
helpers.everest_dirs.extend(detected_projects)
# Always generate type info before updating module
for type_with_namespace in list_types_with_namespace():
_tmpl_data, _last_mtime = TypeParser.generate_type_info(type_with_namespace, all_types=True)
primary_update_strategy = 'force-update' if args.force else 'update'
update_strategy = {'module.cpp': 'update-if-non-existent'}
for file_name in ['cmakelists', 'module.hpp']:
update_strategy[file_name] = primary_update_strategy
# FIXME (aw): refactor out this only handling and rename it properly
mod_files = generate_module_files(args.module, True, args.licenses)
if args.only == 'which':
helpers.print_available_mod_files(mod_files)
return
else:
try:
helpers.filter_mod_files(args.only, mod_files)
except Exception as err:
print(err)
return
if not args.disable_clang_format:
for file_info in mod_files['core'] + mod_files['interfaces']:
helpers.clang_format(args.clang_format_file, file_info)
for file_info in mod_files['core']:
helpers.write_content_to_file(file_info, update_strategy[file_info['abbr']], args.diff, '', True)
for file_info in mod_files['interfaces']:
if file_info['abbr'].endswith('.hpp'):
helpers.write_content_to_file(file_info, primary_update_strategy, args.diff, '', True)
else:
helpers.write_content_to_file(file_info, 'update-if-non-existent', args.diff, '', True)
def module_genld(args):
output_dir = Path(args.output_dir).resolve() if args.output_dir else work_dir / \
'build/generated/generated/modules'
primary_update_strategy = 'force-update' if args.force else 'update'
loader_files = generate_module_loader_files(args.module, output_dir)
if not args.disable_clang_format:
for file_info in loader_files:
helpers.clang_format(args.clang_format_file, file_info)
for file_info in loader_files:
helpers.write_content_to_file_and_check_template(file_info, primary_update_strategy)
def module_get_templates(args):
interface_files = args.separator.join(
[templates['ld-ev.hpp'].filename,
templates['ld-ev.cpp'].filename])
print(f'{interface_files}')
def interface_genhdr(args):
# Always generate type info before generating interfaces
for type_with_namespace in list_types_with_namespace():
_tmpl_data, _last_mtime = TypeParser.generate_type_info(type_with_namespace, all_types=True)
output_dir = Path(args.output_dir).resolve() if args.output_dir else work_dir / \
'build/generated/include/generated/interfaces'
primary_update_strategy = 'force-update' if args.force else 'update'
interfaces = args.interfaces
all_interfaces = False
if not interfaces:
all_interfaces = True
interfaces = []
for everest_dir in everest_dirs:
if_dir = everest_dir / 'interfaces'
interfaces += [if_path.stem for if_path in if_dir.iterdir() if (if_path.is_file()
and if_path.suffix == '.yaml')]
for interface in interfaces:
if_parts = generate_interface_headers(interface, all_interfaces, output_dir)
if not args.disable_clang_format:
# FIXME (aw): this broken, because in case all_interfaces is true, if_parts might be none for invalid interface files
helpers.clang_format(args.clang_format_file, if_parts['base'])
helpers.clang_format(args.clang_format_file, if_parts['exports'])
helpers.clang_format(args.clang_format_file, if_parts['types'])
helpers.write_content_to_file_and_check_template(if_parts['base'], primary_update_strategy, args.diff)
helpers.write_content_to_file_and_check_template(if_parts['exports'], primary_update_strategy, args.diff)
helpers.write_content_to_file_and_check_template(if_parts['types'], primary_update_strategy, args.diff)
def interface_get_templates(args):
interface_files = args.separator.join(
[templates['interface_base'].filename,
templates['interface_exports'].filename])
print(f'{interface_files}')
def helpers_genuuids(args):
if (args.count <= 0):
raise Exception(f'Invalid number ("{args.count}") of uuids to generate')
helpers.generate_some_uuids(args.count)
def helpers_yaml2json(args):
helpers.yaml2json(Path(args.input).resolve(), Path(args.output).resolve())
def helpers_json2yaml(args):
helpers.json2yaml(Path(args.input).resolve(), Path(args.output).resolve())
def list_types_with_namespace(types=None) -> List:
if not types:
types = []
for everest_dir in everest_dirs:
types_dir = everest_dir / 'types'
types += list(types_dir.glob('**/*.yaml'))
types_with_namespace = []
for type_path in types:
relative_path = None
for everest_dir in everest_dirs:
types_dir = everest_dir / 'types'
try:
relative_path = type_path.relative_to(types_dir).with_suffix('')
break
except ValueError:
pass
uppercase_path = []
for part in relative_path.parts:
uppercase_path.append(stringcase.capitalcase(part))
namespace = '::'.join(relative_path.parts)
type_with_namespace = {
'path': type_path,
'relative_path': relative_path,
'namespace': namespace,
'uppercase_path': uppercase_path,
}
types_with_namespace.append(type_with_namespace)
return types_with_namespace
def types_genhdr(args):
print('Generating global type headers.')
output_dir = Path(args.output_dir).resolve() if args.output_dir else work_dir / \
'build/generated/generated/types'
primary_update_strategy = 'force-update' if args.force else 'update'
types = None
all_types = False
if 'types' not in args:
all_types = True
else:
types = args.types
types_with_namespace = list_types_with_namespace(types=types)
for type_with_namespace in types_with_namespace:
type_parts = TypeParser.generate_type_headers(type_with_namespace, all_types, output_dir)
if not args.disable_clang_format:
helpers.clang_format(args.clang_format_file, type_parts['types'])
helpers.write_content_to_file_and_check_template(type_parts['types'], primary_update_strategy, args.diff)
def types_get_templates(args):
interface_files = templates['types.hpp'].filename
print(f'{interface_files}')
def main():
global validators, everest_dirs, work_dir
everest_dir = Path(__file__).parent.parent.parent.parent.parent.parent
everest_framework_dir = everest_dir / 'lib' / 'everest' / 'framework'
schemas_dir = everest_framework_dir / 'schemas'
everest_dir_default = [str(everest_dir), str(Path.cwd().parent / 'EVerest')]
parser = argparse.ArgumentParser(description='Everest command line tool')
parser.add_argument('--version', action='version', version=f'%(prog)s {__version__}')
common_parser = argparse.ArgumentParser(add_help=False)
common_parser.add_argument('--work-dir', '-wd', type=str,
help='work directory containing the manifest definitions (default: .)', default=str(Path.cwd()))
common_parser.add_argument('--everest-dir', '-ed', nargs='*',
help=f'everest directory containing the interface definitions (default: {everest_dir_default})',
default=everest_dir_default)
common_parser.add_argument('--everest-projects', '-ep', nargs='*',
help='everest project names. used in auto detection of their directories to get eg. interface defintions (default: EVerest)',
default=['EVerest'])
common_parser.add_argument('--schemas-dir', '-sd', type=str,
help=f'everest framework directory containing the schema definitions (default: {schemas_dir})',
default=str(schemas_dir))
common_parser.add_argument('--licenses', '-lc', type=str,
help='license directory from which ev-cli will attempt to parse custom license texts (default ../licenses)',
default=str(Path.cwd() / '../licenses'))
common_parser.add_argument('--build-dir', '-bd', type=str,
help='everest build directory from which ev-cli will attempt to parse the everest framework schema definitions (default ./build)',
default=str(Path.cwd() / 'build'))
common_parser.add_argument('--clang-format-file', type=str, default=str(Path.cwd()),
help='Path to the directory, containing the .clang-format file (default: .)')
common_parser.add_argument('--disable-clang-format', action='store_true', default=False,
help='Set this flag to disable clang-format')
subparsers = parser.add_subparsers(metavar='<command>', help='available commands', required=True)
parser_mod = subparsers.add_parser('module', aliases=['mod'], help='module related actions')
parser_if = subparsers.add_parser('interface', aliases=['if'], help='interface related actions')
parser_hlp = subparsers.add_parser('helpers', aliases=['hlp'], help='helper actions')
parser_types = subparsers.add_parser('types', aliases=['ty'], help='type related actions')
mod_actions = parser_mod.add_subparsers(metavar='<action>', help='available actions', required=True)
mod_create_parser = mod_actions.add_parser('create', aliases=['c'], parents=[
common_parser], help='create module(s)')
mod_create_parser.add_argument('module', type=str, help='name of the module, that should be created')
mod_create_parser.add_argument('-f', '--force', action='store_true', help='force overwriting - use with care!')
mod_create_parser.add_argument('-d', '--diff', '--dry-run', action='store_true',
help='show resulting diff on create or overwrite')
mod_create_parser.add_argument('--only', type=str,
help='Comma separated filter list of module files, that should be created. '
'For a list of available files use "--only which".')
mod_create_parser.set_defaults(action_handler=module_create)
mod_update_parser = mod_actions.add_parser('update', aliases=['u'], parents=[
common_parser], help='update module(s)')
mod_update_parser.add_argument('module', type=str, help='name of the module, that should be updated')
mod_update_parser.add_argument('-f', '--force', action='store_true', help='force overwriting')
mod_update_parser.add_argument('-d', '--diff', '--dry-run', action='store_true', help='show resulting diff')
mod_update_parser.add_argument('--only', type=str,
help='Comma separated filter list of module files, that should be updated. '
'For a list of available files use "--only which".')
mod_update_parser.set_defaults(action_handler=module_update)
mod_genld_parser = mod_actions.add_parser(
'generate-loader', aliases=['gl'], parents=[common_parser], help='generate everest loader')
mod_genld_parser.add_argument(
'module', type=str, help='name of the module, for which the loader should be generated')
mod_genld_parser.add_argument('-f', '--force', action='store_true', help='force overwriting')
mod_genld_parser.add_argument('-o', '--output-dir', type=str, help='Output directory for generated loader '
'files (default: {everest-dir}/build/generated/generated/modules)')
mod_genld_parser.set_defaults(action_handler=module_genld)
if_actions = parser_if.add_subparsers(metavar='<action>', help='available actions', required=True)
if_genhdr_parser = if_actions.add_parser(
'generate-headers', aliases=['gh'], parents=[common_parser], help='generate headers')
if_genhdr_parser.add_argument('-f', '--force', action='store_true', help='force overwriting')
if_genhdr_parser.add_argument('-o', '--output-dir', type=str, help='Output directory for generated interface '
'headers (default: {everest-dir}/build/generated/generated/interfaces)')
if_genhdr_parser.add_argument('-d', '--diff', '--dry-run', action='store_true', help='show resulting diff')
if_genhdr_parser.add_argument('interfaces', nargs='*', help='a list of interfaces, for which header files should '
'be generated - if no interface is given, all will be processed and non-processable '
'will be skipped')
if_genhdr_parser.set_defaults(action_handler=interface_genhdr)
hlp_actions = parser_hlp.add_subparsers(metavar='<action>', help='available actions', required=True)
hlp_genuuid_parser = hlp_actions.add_parser('generate-uuids', help='generate uuids')
hlp_genuuid_parser.add_argument('count', type=int, default=3)
hlp_genuuid_parser.set_defaults(action_handler=helpers_genuuids)
hlp_yaml2json_parser = hlp_actions.add_parser('yaml2json', help='convert yaml into json')
hlp_yaml2json_parser.add_argument('input', type=str, help='path to yaml input file')
hlp_yaml2json_parser.add_argument('output', type=str, help='path to json output file')
hlp_yaml2json_parser.set_defaults(action_handler=helpers_yaml2json)
hlp_json2yaml_parser = hlp_actions.add_parser('json2yaml', help='convert json into yaml')
hlp_json2yaml_parser.add_argument('input', type=str, help='path to json input file')
hlp_json2yaml_parser.add_argument('output', type=str, help='path to yaml output file')
hlp_json2yaml_parser.set_defaults(action_handler=helpers_json2yaml)
types_actions = parser_types.add_subparsers(metavar='<action>', help='available actions', required=True)
types_genhdr_parser = types_actions.add_parser(
'generate-headers', aliases=['gh'], parents=[common_parser], help='generate type headers')
types_genhdr_parser.add_argument('-f', '--force', action='store_true', help='force overwriting')
types_genhdr_parser.add_argument('-o', '--output-dir', type=str, help='Output directory for generated type '
'headers (default: {everest-dir}/build/generated/generated/types)')
types_genhdr_parser.add_argument('-d', '--diff', '--dry-run', action='store_true', help='show resulting diff')
types_genhdr_parser.add_argument('types', nargs='*', help='a list of types, for which header files should '
'be generated - if no type is given, all will be processed and non-processable '
'will be skipped')
types_genhdr_parser.set_defaults(action_handler=types_genhdr)
for sub_parser, get_template_function in [
(mod_actions, module_get_templates),
(if_actions, interface_get_templates),
(types_actions, types_get_templates)
]:
get_templates_parser = sub_parser.add_parser(
'get-templates', aliases=['gt'], parents=[common_parser], help='get paths to template files')
get_templates_parser.add_argument(
'-s', '--separator', type=str, default='\n', help='separator between template files')
get_templates_parser.set_defaults(action_handler=get_template_function)
args = parser.parse_args()
if 'everest_dir' in args:
# FIXME (aw): the helper commands do not set everest_dir, work_dir and schema_dirs, but the following common
# code has to run for all other commands - we need some better check here than just checking for
# 'everest_dir' in args!
for entry in args.everest_dir:
everest_dir = Path(entry).resolve()
everest_dirs.append(everest_dir)
helpers.everest_dirs = everest_dirs
work_dir = Path(args.work_dir).resolve()
setup_jinja_env()
schemas_dir = Path(args.schemas_dir).resolve()
if not schemas_dir.exists():
print(f'The default ("{schemas_dir}") xor supplied (via --schemas-dir) schemas directory'
' doesn\'t exist.\n'
f'dir: {schemas_dir}')
cmake_cache_path = Path(args.build_dir) / 'CMakeCache.txt'
found_dir = helpers.get_path_from_cmake_cache('everest-framework', cmake_cache_path, '--schemas-dir')
if not found_dir:
exit(1)
schemas_dir = found_dir / 'schemas'
if not schemas_dir.exists():
exit(1)
validators = helpers.load_validators(schemas_dir)
TypeParser.validators = validators
TypeParser.templates = templates
ErrorParser.validators = validators
args.action_handler(args)
if __name__ == '__main__':
try:
main()
except helpers.EVerestParsingException as e:
raise SystemExit(e)