From 084dfe05b16d720c7a4a9d032de5d28fb3fb8cdc Mon Sep 17 00:00:00 2001 From: Kai-Uwe Hermann Date: Mon, 18 Nov 2024 19:56:47 +0100 Subject: [PATCH 1/2] Add support to ev-cli for custom license headers Signed-off-by: Kai-Uwe Hermann --- ev-dev-tools/src/ev_cli/__init__.py | 2 +- ev-dev-tools/src/ev_cli/ev.py | 40 ++++++++++++++----- ev-dev-tools/src/ev_cli/helpers.py | 37 ++++++++++++++++- .../opensource.org/licenses/Apache-2.0 | 2 + .../src/ev_cli/templates/helper_macros.j2 | 3 ++ .../ev_cli/templates/interface-Impl.cpp.j2 | 4 +- .../ev_cli/templates/interface-Impl.hpp.j2 | 4 +- .../src/ev_cli/templates/module.cpp.j2 | 4 +- .../src/ev_cli/templates/module.hpp.j2 | 4 +- 9 files changed, 80 insertions(+), 20 deletions(-) create mode 100644 ev-dev-tools/src/ev_cli/licenses/opensource.org/licenses/Apache-2.0 diff --git a/ev-dev-tools/src/ev_cli/__init__.py b/ev-dev-tools/src/ev_cli/__init__.py index be4d338e..04bbb7b6 100644 --- a/ev-dev-tools/src/ev_cli/__init__.py +++ b/ev-dev-tools/src/ev_cli/__init__.py @@ -1,2 +1,2 @@ """EVerest command line utility.""" -__version__ = '0.4.2' +__version__ = '0.4.3' diff --git a/ev-dev-tools/src/ev_cli/ev.py b/ev-dev-tools/src/ev_cli/ev.py index 9fe4e09f..4c0117c6 100755 --- a/ev-dev-tools/src/ev_cli/ev.py +++ b/ev-dev-tools/src/ev_cli/ev.py @@ -256,13 +256,24 @@ def generate_module_loader_files(rel_mod_dir, output_dir): return loader_files -def generate_module_files(rel_mod_dir, update_flag): +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' + additional_license_dir = Path(licenses) + license_dirs = [default_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 @@ -362,6 +373,7 @@ def generate_module_files(rel_mod_dir, update_flag): 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({ @@ -370,7 +382,8 @@ def generate_module_files(rel_mod_dir, update_flag): '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 + 'last_mtime': last_mtime, + 'license_header': license_header }) mod_files['interfaces'].append({ @@ -379,11 +392,13 @@ def generate_module_files(rel_mod_dir, update_flag): '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 + '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, @@ -401,7 +416,8 @@ def generate_module_files(rel_mod_dir, update_flag): '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 + 'last_mtime': mod_path.stat().st_mtime, + 'license_header': license_header }) # module.cpp @@ -411,7 +427,8 @@ def generate_module_files(rel_mod_dir, update_flag): '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 + 'last_mtime': mod_path.stat().st_mtime, + 'license_header': license_header }) # doc.rst @@ -521,7 +538,7 @@ def generate_interface_headers(interface, all_interfaces_flag, output_dir): def module_create(args): create_strategy = 'force-create' if args.force else 'create' - mod_files = generate_module_files(args.module, False) + mod_files = generate_module_files(args.module, False, args.licenses) if args.only == 'which': helpers.print_available_mod_files(mod_files) @@ -551,7 +568,7 @@ def module_update(args): 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) + mod_files = generate_module_files(args.module, True, args.licenses) if args.only == 'which': helpers.print_available_mod_files(mod_files) @@ -568,13 +585,13 @@ def module_update(args): 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) + 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) + 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) + helpers.write_content_to_file(file_info, 'update-if-non-existent', args.diff, '', True) def module_genld(args): @@ -734,6 +751,9 @@ def main(): common_parser.add_argument('--schemas-dir', '-sd', type=str, help='everest framework directory containing the schema definitions (default: ../everest-framework/schemas)', default=str(Path.cwd() / '../everest-framework/schemas')) + 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('--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, diff --git a/ev-dev-tools/src/ev_cli/helpers.py b/ev-dev-tools/src/ev_cli/helpers.py index 3bb925fe..20a96735 100644 --- a/ev-dev-tools/src/ev_cli/helpers.py +++ b/ev-dev-tools/src/ev_cli/helpers.py @@ -688,7 +688,7 @@ def is_template_newer(file_info) -> Tuple[bool, str]: return (False, '') -def write_content_to_file(file_info, strategy, only_diff=False, reason = ''): +def write_content_to_file(file_info, strategy, only_diff=False, reason = '', check_license_header=False): # strategy: # update: update only if dest older or not existent # force-update: update, even if dest newer @@ -730,6 +730,23 @@ def write_content_to_file(file_info, strategy, only_diff=False, reason = ''): if not file_dir.exists(): file_dir.mkdir(parents=True, exist_ok=True) + # check if file header is different from license header + if check_license_header: + if 'license_header' in file_info: + original_content = file_path.read_text() + if not original_content.startswith(file_info['license_header']): + # determine likely end of license header + search_terms = ['#ifndef', '#pragma once', '#include'] + original_license_header = '' + for search in search_terms: + index = original_content.find(search) + if index >= 0: + original_license_header = original_content[0:index] + break + print(f'Keeping the existing licence header:\n{original_license_header}') + file_info['content'] = file_info['content'].replace( + file_info['license_header'], original_license_header.strip()) + file_path.write_text(file_info['content']) @@ -740,3 +757,21 @@ def write_content_to_file_and_check_template(file_info, strategy, only_diff=Fals if newer: update_strategy = 'force-update' write_content_to_file(file_info, update_strategy, only_diff, reason) + + +def get_license_header(license_dirs, license_url): + url_schemas = ['http://', 'https://'] + for url_schema in url_schemas: + if license_url.startswith(url_schema): + license_url = license_url.replace(url_schema, '', 1) + license_path = None + for license_dir in license_dirs: + check_license_path = license_dir / license_url + print(f'Checking if license "{check_license_path}" exists...') + if check_license_path.exists(): + license_path = check_license_path + + if not license_path: + return None + with open(license_path, 'r') as custom_license_file: + return custom_license_file.read().strip() diff --git a/ev-dev-tools/src/ev_cli/licenses/opensource.org/licenses/Apache-2.0 b/ev-dev-tools/src/ev_cli/licenses/opensource.org/licenses/Apache-2.0 new file mode 100644 index 00000000..754962c5 --- /dev/null +++ b/ev-dev-tools/src/ev_cli/licenses/opensource.org/licenses/Apache-2.0 @@ -0,0 +1,2 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Pionix GmbH and Contributors to EVerest diff --git a/ev-dev-tools/src/ev_cli/templates/helper_macros.j2 b/ev-dev-tools/src/ev_cli/templates/helper_macros.j2 index 15ce34a2..6dc7a311 100644 --- a/ev-dev-tools/src/ev_cli/templates/helper_macros.j2 +++ b/ev-dev-tools/src/ev_cli/templates/helper_macros.j2 @@ -89,6 +89,9 @@ void publish_{{ var.name }}({% if var.json_type != 'null' %}{{ cpp_type(var) }} // Copyright{% if year_tag %} {{ year_tag }}{% endif %} Pionix GmbH and Contributors to EVerest {%- endmacro %} +{% macro print_license_header(license) %} +{{ license }} +{%- endmacro %} {% macro print_template_info(version, title='DO NOT EDIT!', comment_sep='//') %} {{ comment_sep }} diff --git a/ev-dev-tools/src/ev_cli/templates/interface-Impl.cpp.j2 b/ev-dev-tools/src/ev_cli/templates/interface-Impl.cpp.j2 index f4e58e7f..cf111dff 100644 --- a/ev-dev-tools/src/ev_cli/templates/interface-Impl.cpp.j2 +++ b/ev-dev-tools/src/ev_cli/templates/interface-Impl.cpp.j2 @@ -1,5 +1,5 @@ -{% from "helper_macros.j2" import handle_cmd_signature, print_spdx_line %} -{{ print_spdx_line('Apache-2.0') }} +{% from "helper_macros.j2" import handle_cmd_signature, print_license_header %} +{{ print_license_header(info.license_header) }} #include "{{ info.class_name}}.hpp" diff --git a/ev-dev-tools/src/ev_cli/templates/interface-Impl.hpp.j2 b/ev-dev-tools/src/ev_cli/templates/interface-Impl.hpp.j2 index 0023a8bf..20650e2f 100644 --- a/ev-dev-tools/src/ev_cli/templates/interface-Impl.hpp.j2 +++ b/ev-dev-tools/src/ev_cli/templates/interface-Impl.hpp.j2 @@ -1,5 +1,5 @@ -{% from "helper_macros.j2" import handle_cmd_signature, print_template_info, insert_block, cpp_type, print_spdx_line %} -{{ print_spdx_line('Apache-2.0') }} +{% from "helper_macros.j2" import handle_cmd_signature, print_template_info, insert_block, cpp_type, print_license_header %} +{{ print_license_header(info.license_header) }} #ifndef {{ info.hpp_guard }} #define {{ info.hpp_guard }} diff --git a/ev-dev-tools/src/ev_cli/templates/module.cpp.j2 b/ev-dev-tools/src/ev_cli/templates/module.cpp.j2 index fa6729b2..209c7496 100644 --- a/ev-dev-tools/src/ev_cli/templates/module.cpp.j2 +++ b/ev-dev-tools/src/ev_cli/templates/module.cpp.j2 @@ -1,5 +1,5 @@ -{% from "helper_macros.j2" import print_spdx_line %} -{{ print_spdx_line('Apache-2.0') }} +{% from "helper_macros.j2" import print_license_header %} +{{ print_license_header(info.license_header) }} #include "{{ info.module_header }}" namespace module { diff --git a/ev-dev-tools/src/ev_cli/templates/module.hpp.j2 b/ev-dev-tools/src/ev_cli/templates/module.hpp.j2 index 0d076b6c..f95967e1 100644 --- a/ev-dev-tools/src/ev_cli/templates/module.hpp.j2 +++ b/ev-dev-tools/src/ev_cli/templates/module.hpp.j2 @@ -1,5 +1,5 @@ -{% from "helper_macros.j2" import print_template_info, insert_block, cpp_type, print_spdx_line %} -{{ print_spdx_line('Apache-2.0') }} +{% from "helper_macros.j2" import print_template_info, insert_block, cpp_type, print_license_header %} +{{ print_license_header(info.license_header) }} #ifndef {{ info.hpp_guard }} #define {{ info.hpp_guard }} From 32cf583ed36dd350987514e92edc13966d766f46 Mon Sep 17 00:00:00 2001 From: Kai-Uwe Hermann Date: Sat, 23 Nov 2024 12:37:13 +0100 Subject: [PATCH 2/2] Add support for license dir in current workdir (eg. everest-core/licenses/) Signed-off-by: Kai-Uwe Hermann --- ev-dev-tools/src/ev_cli/ev.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ev-dev-tools/src/ev_cli/ev.py b/ev-dev-tools/src/ev_cli/ev.py index 219c6f43..b75ed138 100755 --- a/ev-dev-tools/src/ev_cli/ev.py +++ b/ev-dev-tools/src/ev_cli/ev.py @@ -264,8 +264,9 @@ def generate_module_files(rel_mod_dir, update_flag, licenses): 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, additional_license_dir] + 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)