diff --git a/verification/.devcontainer/devcontainer.json b/.devcontainer/c_cpp/devcontainer.json similarity index 85% rename from verification/.devcontainer/devcontainer.json rename to .devcontainer/c_cpp/devcontainer.json index 3d6d5d8d..b8a65f3c 100644 --- a/verification/.devcontainer/devcontainer.json +++ b/.devcontainer/c_cpp/devcontainer.json @@ -1,8 +1,8 @@ { "name": "C/C++ verification environment", "image": "ghcr.io/opencyphal/toolshed:ts22.4.10", - "workspaceFolder": "/workspace", - "workspaceMount": "source=${localWorkspaceFolder}/..,target=/workspace,type=bind,consistency=delegated", + "workspaceFolder": "/repo", + "workspaceMount": "source=${localWorkspaceFolder},target=/repo,type=bind,consistency=delegated", "customizations": { "vscode": { "extensions": [ diff --git a/.devcontainer/devcontainer.json b/.devcontainer/python/devcontainer.json similarity index 100% rename from .devcontainer/devcontainer.json rename to .devcontainer/python/devcontainer.json diff --git a/.github/verify.py b/.github/verify.py deleted file mode 100755 index 0abf2f08..00000000 --- a/.github/verify.py +++ /dev/null @@ -1,633 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright (C) OpenCyphal Development Team -# Copyright Amazon.com Inc. or its affiliates. -# SPDX-License-Identifier: MIT -# -""" - Command-line helper for running verification builds. -""" - -import argparse -import configparser -import functools -import logging -import os -import pathlib -import shutil -import subprocess -import sys -import textwrap -import typing - - -def _make_parser() -> argparse.ArgumentParser: - - script = pathlib.Path(__file__).relative_to(pathlib.Path.cwd()) - - epilog = textwrap.dedent( - f""" - - **Example Usage**:: - - {script} -l c - - """ - ) - - parser = argparse.ArgumentParser( - description="CMake command-line helper for running verification builds.", - epilog=epilog, - formatter_class=argparse.RawTextHelpFormatter, - ) - - parser.add_argument( - "--override", - action="append", - nargs="*", - help=textwrap.dedent( - """ - Optional configuration files to provide override values for verification arguments. - Use this to configure common CI options without creating long CI command lines. Multiple - overrides can be specified with the order being an ascending priority (i.e. the next override - will overwrite any previous overrides). - - This file uses the python configparser syntax and expects a single section called 'overrides'. - See https://docs.python.org/3/library/configparser.html for more information. - - Example ini file: - - [overrides] - verbose: True - remove-first: True - force: True - endianness: any - language: cpp - platform: native32 - toolchain-family: gcc - - """[1:]), - ) - - build_args = parser.add_argument_group( - title="build options", - description=textwrap.dedent( - """ - Arguments that can be used in parallel builds. Each of these will change - the name of the build directory created for the build. - """[1:]), - ) - - build_args.add_argument( - "--version-only", - action="store_true", - help=textwrap.dedent( - f""" - Print out the version number (stored in src/nunavut/_version.py) only and exit. This number - will be the only output to stdout allowing build scripts to extract this string value for - use in the build environment. For example: - - export NUNAVUT_FULL_VERSION=$({script} --version-only) - - """[1:]), - ) - - build_args.add_argument( - "--major-minor-version-only", - action="store_true", - help=textwrap.dedent( - f""" - Print out the major and minor version number (stored in src/nunavut/_version.py) only and exit. - This number will be the only output to stdout allowing build scripts to extract this string - value for use in the build environment. For example: - - export NUNAVUT_MAJOR_MINOR_VERSION=$({script} --major-minor-version-only) - - """[1:]), - ) - - build_args.add_argument( - "--version-check-only", - help=textwrap.dedent( - f""" - Compares a given semantic version number with the current Nunavut version - (stored in src/nunavut/_version.py) and returns 0 if it matches else returns 1. - - if $({script} --version-check-only 1.0.2); then echo "match"; fi - - """[1:]), - ) - - build_args.add_argument("-l", "--language", default="c", help="Value for NUNAVUT_VERIFICATION_LANG (defaults to c)") - - build_args.add_argument("-std", "--language-standard", default="", help="Language standard") - - build_args.add_argument("--build-type", help="Value for CMAKE_BUILD_TYPE") - - build_args.add_argument("--endianness", help="Value for NUNAVUT_VERIFICATION_TARGET_ENDIANNESS") - - build_args.add_argument("--platform", help="Value for NUNAVUT_VERIFICATION_TARGET_PLATFORM") - - build_args.add_argument( - "--disable-asserts", action="store_true", help="Set NUNAVUT_VERIFICATION_SER_ASSERT=OFF (default is ON)" - ) - - build_args.add_argument( - "--disable-fp", action="store_true", help="Set NUNAVUT_VERIFICATION_SER_FP_DISABLE=ON (default is OFF)" - ) - - build_args.add_argument( - "--enable-ovr-var-array", - action="store_true", - help="Set NUNAVUT_VERIFICATION_OVR_VAR_ARRAY_ENABLE=ON (default is OFF)", - ) - - build_args.add_argument( - "--toolchain-family", - choices=["gcc", "clang", "none"], - default="gcc", - help=textwrap.dedent( - """ - Select the toolchain family to use. Use "none" to get the toolchain - from the environment (i.e. set CC and CXX environment variables). - """[1:]), - ) - - build_args.add_argument( - "--none", - action="store_true", - help=textwrap.dedent( - """ - Dummy argument used to support matrix builds where an argument present - in other builds is not provided in the current build. - """[1:]), - ) - - action_args = parser.add_argument_group( - title="behavioral options", - description=textwrap.dedent( - """ - Arguments that change the actions taken by the build. - """[1:]), - ) - - action_args.add_argument("-v", "--verbose", action="count", default=0, help="Set output verbosity.") - - action_args.add_argument( - "-f", - "--force", - action="store_true", - help=textwrap.dedent( - """ - Force recreation of verification directory if it already exists. - - ** WARNING ** This will delete the cmake build directory! - - """[1:]), - ) - - action_args.add_argument("-c", "--configure-only", action="store_true", help="Configure but do not build.") - - action_args.add_argument( - "-b", "--build-only", action="store_true", help="Try to build without configuring. Do not try to run tests." - ) - - action_args.add_argument( - "-t", "--test-only", action="store_true", help="Only try to run tests. Don't configure or build." - ) - - action_args.add_argument( - "--dry-run", - action="store_true", - help=textwrap.dedent( - """ - Don't actually do anything. Just log what this script would have done. - Combine with --verbose to ensure you actually see the script's log output. - """[1:]), - ) - - action_args.add_argument( - "-x", - "--no-coverage", - action="store_true", - help="Deprecated. Use compiler_flag_set instead.", - ) - - action_args.add_argument( - "-cfs", - "--compiler-flag-set", - default="native", - type=pathlib.Path, - help=textwrap.dedent( - """ - Select the compiler flag set to use. This will select the appropriate compiler flags - for the build. The default is 'native' which is the default compiler flags for the - build environment. Use 'native_w_cov' to enable coverage flags. - See cmake/compiler_flag_sets for available options. - """[1:]), - ) - - action_args.add_argument( - "-rm", - "--remove-first", - action="store_true", - help=textwrap.dedent( - """ - If specified, any existing build directory will be deleted first. Use - -f to skip the user prompt. - - Note: This only applies to the configure step. If you do a build-only this - argument has no effect. - """[1:]), - ) - - other_options = parser.add_argument_group( - title="extra build options", - description=textwrap.dedent( - """ - Additional arguments for modifying how the build runs but which are used less frequently. - """[1:]), - ) - - other_options.add_argument( - "-j", - "--jobs", - type=int, - help="The number of concurrent build jobs to request. " - "Defaults to the number of logical CPUs on the local machine.", - ) - - other_options.add_argument("--verification-dir", default="verification", help="Path to the verification directory.") - - other_options.add_argument( - "--use-default-generator", - action="store_true", - help=textwrap.dedent( - """ - We use Ninja by default. Set this flag to omit the explicit generator override - and use whatever the default is for cmake (i.e. normally make) - """[1:]), - ) - - return parser - - -def _apply_overrides(args: argparse.Namespace) -> argparse.Namespace: - if args.override is None: - return args - - for override_list in args.override: - for override in override_list: - if not pathlib.Path(override).exists(): - raise RuntimeError(f'ini file "{override}" does not exist.') - print( - textwrap.dedent( - f""" - ***************************************************************** - About to apply override file : {override} - ***************************************************************** - """ - ) - ) - - overrides = configparser.ConfigParser(interpolation=configparser.ExtendedInterpolation()) - overrides.read(override) - if "overrides" not in overrides: - raise RuntimeError(f'ini file "{override}" did not contain an overrides section.') - for key, value in overrides["overrides"].items(): - corrected_key = key.replace("-", "_") - if value.lower() == "true" or value.lower() == "false": - setattr(args, corrected_key, bool(value)) - else: - try: - setattr(args, corrected_key, int(value)) - except ValueError: - setattr(args, corrected_key, value) - - return args - - -def _cmake_run( - cmake_args: typing.List[str], - cmake_dir: pathlib.Path, - verbose: int, - dry_run: bool, - env: typing.Optional[typing.Dict] = None, -) -> int: - """ - Simple wrapper around cmake execution logic - """ - logging.debug( - textwrap.dedent( - """ - - ***************************************************************** - About to run command: {} - in directory : {} - ***************************************************************** - - """ - ).format(" ".join(cmake_args), str(cmake_dir)) - ) - - copy_of_env: typing.Dict = {} - copy_of_env.update(os.environ) - if env is not None: - copy_of_env.update(env) - - if verbose > 1: - logging.debug(" *****************************************************************") - logging.debug(" Using Environment:") - for key, value in copy_of_env.items(): - overridden = key in env if env is not None else False - logging.debug(" %s = %s%s", key, value, (" (override)" if overridden else "")) - logging.debug(" *****************************************************************\n") - - if not dry_run: - return subprocess.run(cmake_args, cwd=cmake_dir, env=copy_of_env, check=True).returncode - else: - return 0 - - -def _handle_build_dir(args: argparse.Namespace, cmake_dir: pathlib.Path) -> None: - """ - Handle all the logic, user input, logging, and file-system operations needed to - manage the cmake build directory ahead of invoking cmake. - """ - if args.remove_first and cmake_dir.exists(): - okay_to_remove = False - if not args.force: - response = input(f"Are you sure you want to delete {cmake_dir}? [y/N]:") - if (len(response) == 1 and response.lower() == "y") or (len(response) == 3 and response.lower() == "yes"): - okay_to_remove = True - else: - okay_to_remove = True - - if okay_to_remove: - if not args.dry_run: - logging.info("Removing directory %s", cmake_dir) - shutil.rmtree(cmake_dir) - else: - logging.info("Is dry-run. Would have removed directory %s", cmake_dir) - else: - raise RuntimeError( - """ - Build directory {} already exists, -rm or --remove-first was specified, - and permission was not granted to delete it. We cannot continue. Either - allow re-use of this build directory or allow deletion. (use -f flag to - skip user prompts).""".lstrip().format( - cmake_dir - ) - ) - - if not cmake_dir.exists(): - if not args.dry_run: - logging.info("Creating build directory at %s", cmake_dir) - cmake_dir.mkdir() - else: - logging.info("Dry run: Would have created build directory at %s", cmake_dir) - else: - logging.info("Using existing build directory at %s", cmake_dir) - - -def _cmake_configure(args: argparse.Namespace, cmake_args: typing.List[str], cmake_dir: pathlib.Path) -> int: - """ - Format and execute cmake configure command. This also include the cmake build directory (re)creation - logic. - """ - - if args.build_only or args.test_only: - return 0 - - cmake_logging_level = "NOTICE" - - if args.verbose == 1: - cmake_logging_level = "STATUS" - elif args.verbose == 2: - cmake_logging_level = "VERBOSE" - elif args.verbose == 3: - cmake_logging_level = "DEBUG" - elif args.verbose > 3: - cmake_logging_level = "TRACE" - - _handle_build_dir(args, cmake_dir) - - cmake_configure_args = cmake_args.copy() - - cmake_configure_args.append(f"--log-level={cmake_logging_level}") - cmake_configure_args.append(f"-DNUNAVUT_VERIFICATION_LANG={args.language}") - cmake_configure_args.append(f"-DCMAKE_PREFIX_PATH=..") - - if args.language_standard is not None: - cmake_configure_args.append(f"-DNUNAVUT_VERIFICATION_LANG_STANDARD={args.language_standard}") - - if args.build_type is not None: - cmake_configure_args.append(f"-DCMAKE_BUILD_TYPE={args.build_type}") - - if args.endianness is not None: - cmake_configure_args.append(f"-DNUNAVUT_VERIFICATION_TARGET_ENDIANNESS={args.endianness}") - - if args.platform is not None: - cmake_configure_args.append(f"-DNUNAVUT_VERIFICATION_TARGET_PLATFORM={args.platform}") - - if args.disable_asserts: - cmake_configure_args.append("-DNUNAVUT_VERIFICATION_SER_ASSERT:BOOL=OFF") - - if args.disable_fp: - cmake_configure_args.append("-DNUNAVUT_VERIFICATION_SER_FP_DISABLE:BOOL=ON") - - if args.enable_ovr_var_array: - cmake_configure_args.append("-DNUNAVUT_VERIFICATION_OVR_VAR_ARRAY_ENABLE:BOOL=ON") - - if args.verbose > 0: - cmake_configure_args.append("-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON") - - flag_set_dir = pathlib.Path("cmake") / pathlib.Path("compiler_flag_sets") - flagset_file = (flag_set_dir / args.compiler_flag_set).with_suffix(".cmake") - compiler_flag_set = (pathlib.Path(args.verification_dir) / flagset_file).resolve() - if not compiler_flag_set.exists(): - raise RuntimeError( - f"Compiler flag set file {str(compiler_flag_set)} does not exist in the verification directory." - ) - - cmake_configure_args.append(f"-DNUNAVUT_FLAGSET={str(compiler_flag_set)}") - - if args.toolchain_family != "none": - toolchain_dir = pathlib.Path("cmake") / pathlib.Path("toolchains") - if args.toolchain_family == "clang": - toolchain_file = toolchain_dir / pathlib.Path("clang-native").with_suffix(".cmake") - else: - toolchain_file = toolchain_dir / pathlib.Path("gcc-native").with_suffix(".cmake") - - cmake_configure_args.append(f"-DCMAKE_TOOLCHAIN_FILE={str(toolchain_file)}") - - if not args.use_default_generator: - cmake_configure_args.append("-DCMAKE_GENERATOR=Ninja") - - cmake_configure_args.append("..") - - return _cmake_run(cmake_configure_args, cmake_dir, args.verbose, args.dry_run) - - -def _cmake_build(args: argparse.Namespace, cmake_args: typing.List[str], cmake_dir: pathlib.Path) -> int: - """ - Format and execute cmake build command. This method assumes that the cmake_dir is already properly - configured. - """ - if not args.configure_only and not args.test_only: - cmake_build_args = cmake_args.copy() - - cmake_build_args += ["--build", ".", "--target", "all"] - - if args.jobs is not None and args.jobs > 0: - cmake_build_args += ["--", f"-j{args.jobs}"] - - return _cmake_run(cmake_build_args, cmake_dir, args.verbose, args.dry_run) - - return 0 - - -def _cmake_test(args: argparse.Namespace, cmake_args: typing.List[str], cmake_dir: pathlib.Path) -> int: - """ - Format and execute cmake test command. This method assumes that the cmake_dir is already properly - configured. - """ - if not args.configure_only and not args.build_only: - cmake_test_args = cmake_args.copy() - - cmake_test_args += ["--build", ".", "--target"] - - if args.compiler_flag_set.stem == "native_w_cov": - cmake_test_args.append("cov_all_archive") - else: - cmake_test_args.append("test_all") - - return _cmake_run(cmake_test_args, cmake_dir, args.verbose, args.dry_run) - - return 0 - - -def _create_build_dir_name(args: argparse.Namespace) -> str: - name = f"build_{args.language}" - - if args.language_standard is not None: - name += f"_{args.language_standard}" - - name += f"_{args.toolchain_family}" - - if args.platform is not None: - name += f"_{args.platform}" - - if args.build_type is not None: - name += f"_{args.build_type}" - - if args.endianness is not None: - name += f"_{args.endianness}" - - if args.disable_asserts: - name += "_noassert" - - if args.disable_fp: - name += "_nofp" - - if args.enable_ovr_var_array: - name += "_wovervararray" - - return name - - -@functools.lru_cache(maxsize=None) -def _get_version_string() -> typing.Tuple[str, str, str, str]: - version: typing.Dict[str, str] = {} - nunavut_version_file = pathlib.Path("src/nunavut/_version.py") - - with nunavut_version_file.open("r", encoding="UTF-8") as version_py: - exec(version_py.read(), version) # pylint: disable=exec-used - - version_string = version["__version__"] - version_array = version_string.split(".") - if len(version_array) not in (3, 4): - raise RuntimeError(f"Invalid version string: {version_string}") - if len(version_array) == 3: - return (version_array[0], version_array[1], version_array[2], "") - else: - return (version_array[0], version_array[1], version_array[2], version_array[3]) - - -def main() -> int: - """ - Main method to execute when this package/script is invoked as a command. - """ - args = _apply_overrides(_make_parser().parse_args()) - - if args.version_only: - sys.stdout.write(".".join(_get_version_string())) - sys.stdout.flush() - return 0 - - if args.major_minor_version_only: - version = _get_version_string() - sys.stdout.write(f"{version[0]}.{version[1]}") - sys.stdout.flush() - return 0 - - logging_level = logging.WARN - - if args.verbose == 1: - logging_level = logging.INFO - elif args.verbose > 1: - logging_level = logging.DEBUG - - logging.basicConfig(format="%(levelname)s: %(message)s", level=logging_level) - - if args.version_check_only is not None: - version_as_string = ".".join(_get_version_string()) - logging.debug( - "Comparing nunavut version %s to provided version %s (%s)", - version_as_string, - args.version_check_only, - "matches" if (version_as_string == args.version_check_only) else "no-match") - return 0 if (version_as_string == args.version_check_only) else 1 - - logging.debug( - textwrap.dedent( - """ - - ***************************************************************** - Commandline Arguments to {}: - - {} - - For Nunavut version {} - ***************************************************************** - - """ - ).format(os.path.basename(__file__), str(args), _get_version_string()) - ) - - verification_dir = pathlib.Path.cwd() / pathlib.Path(args.verification_dir) - cmake_dir = verification_dir / pathlib.Path(_create_build_dir_name(args)) - cmake_args = ["cmake"] - - configure_result = _cmake_configure(args, cmake_args, cmake_dir) - - if configure_result != 0: - return configure_result - elif args.configure_only: - return 0 - - build_result = _cmake_build(args, cmake_args, cmake_dir) - - if build_result != 0: - return build_result - elif args.build_only: - return 0 - - if not args.configure_only and not args.build_only: - return _cmake_test(args, cmake_args, cmake_dir) - - raise RuntimeError("Internal logic error: only_do_x flags resulted in no action.") - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 21e226fb..41982cd6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,7 +19,7 @@ jobs: - name: version-check # Fails the release if the release-tag doesn't match the Nunavut version at that tag. run: | - $(./.github/verify.py -vv --version-check-only "2.3.4.dev0") + $(./version_check_nunavut.py -vv --version-check "2.3.4.dev0") - name: lint run: tox -e lint - name: test-nnvg diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5e61748b..cff6fd6b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -66,7 +66,7 @@ jobs: path: .tox/py311-test/tmp/ - name: set-environment run: | - echo NUNAVUT_MAJOR_MINOR_VERSION=$(./.github/verify.py --major-minor-version-only) >> $GITHUB_ENV + echo NUNAVUT_MAJOR_MINOR_VERSION=$(./version_check_nunavut --major-minor-version-only) >> $GITHUB_ENV - name: verify tox artifacts run: ls -R working-directory: .tox @@ -139,125 +139,61 @@ jobs: container: ghcr.io/opencyphal/toolshed:ts22.4.10 strategy: matrix: - build_type: [Debug, Release, MinSizeRel] - architecture: [native32, native64] + architecture: [native32, native] compiler: [gcc, clang] - endianness: [any, little] - flag: [--none, --enable-ovr-var-array, --disable-asserts] exclude: - - build_type: Debug - flag: --disable-asserts - - build_type: Release - flag: --disable-asserts + - architecture: native32 + compiler: clang steps: - uses: actions/checkout@v4 with: submodules: true - name: verify + # We're using Ninja multi-config so we only have to configure once for all build types + working-directory: verification run: | - .github/verify.py --override .github/verify_global.ini \ - --build-type ${{ matrix.build_type }} \ - --language c \ - --platform ${{ matrix.architecture }} \ - --toolchain-family ${{ matrix.compiler }} \ - --endianness ${{ matrix.endianness }} \ - ${{ matrix.flag }} + cmake -DNUNAVUT_EXTRA_GENERATOR_ARGS=--embed-auditing-info --preset config-${{ matrix.compiler }}-${{ matrix.architecture }}-c-11 + cmake --build --preset build-${{ matrix.compiler }}-${{ matrix.architecture }}-c-11-release + cmake --build --preset build-${{ matrix.compiler }}-${{ matrix.architecture }}-c-11-debugasan - language-verification-cpp-14: + language-verification-cpp: runs-on: ubuntu-latest needs: test container: ghcr.io/opencyphal/toolshed:ts22.4.10 strategy: matrix: - build_type: [Debug, Release, MinSizeRel] - architecture: [native32, native64] + architecture: [native32, native] compiler: [gcc, clang] - endianness: [any, little] - flag: [--none, --enable-ovr-var-array, --disable-asserts] + language: [cpp-14, cpp-17, cpp-20] exclude: - - build_type: Debug - flag: --disable-asserts - - build_type: Release - flag: --disable-asserts - + - architecture: native32 + compiler: clang steps: - uses: actions/checkout@v4 with: submodules: true - name: verify + working-directory: verification run: | - .github/verify.py --override .github/verify_global.ini \ - --build-type ${{ matrix.build_type }} \ - --language-standard c++14 \ - --language cpp \ - --platform ${{ matrix.architecture }} \ - --toolchain-family ${{ matrix.compiler }} \ - --endianness ${{ matrix.endianness }} \ - ${{ matrix.flag }} + cmake -DNUNAVUT_EXTRA_GENERATOR_ARGS=--embed-auditing-info --preset config-${{ matrix.compiler }}-${{ matrix.architecture }}-${{ matrix.language }} + cmake --build --preset build-${{ matrix.compiler }}-${{ matrix.architecture }}-${{ matrix.language }}-debugasan + cmake --build --preset build-${{ matrix.compiler }}-${{ matrix.architecture }}-${{ matrix.language }}-release - language-verification-cpp-17: + language-verification-c-cpp-clang-native-extra: runs-on: ubuntu-latest needs: test container: ghcr.io/opencyphal/toolshed:ts22.4.10 - strategy: - matrix: - build_type: [Debug, Release, MinSizeRel] - architecture: [native32, native64] - compiler: [gcc, clang] - endianness: [any, little] - flag: [--none, --enable-ovr-var-array, --disable-asserts] - exclude: - - build_type: Debug - flag: --disable-asserts - - build_type: Release - flag: --disable-asserts - - steps: - - uses: actions/checkout@v4 - with: - submodules: true - - name: verify - run: | - .github/verify.py --override .github/verify_global.ini \ - --build-type ${{ matrix.build_type }} \ - --language-standard c++17 \ - --language cpp \ - --platform ${{ matrix.architecture }} \ - --toolchain-family ${{ matrix.compiler }} \ - --endianness ${{ matrix.endianness }} \ - ${{ matrix.flag }} - - language-verification-cpp-20: - runs-on: ubuntu-latest - needs: test - container: ghcr.io/opencyphal/toolshed:ts22.4.10 - strategy: - matrix: - build_type: [Debug, Release, MinSizeRel] - architecture: [native32, native64] - compiler: [gcc, clang] - endianness: [any, little] - flag: [--none, --enable-ovr-var-array, --disable-asserts] - exclude: - - build_type: Debug - flag: --disable-asserts - - build_type: Release - flag: --disable-asserts - steps: - uses: actions/checkout@v4 with: submodules: true - name: verify + working-directory: verification run: | - .github/verify.py --override .github/verify_global.ini \ - --build-type ${{ matrix.build_type }} \ - --language-standard c++20 \ - --language cpp \ - --platform ${{ matrix.architecture }} \ - --toolchain-family ${{ matrix.compiler }} \ - --endianness ${{ matrix.endianness }} \ - ${{ matrix.flag }} + cmake -DNUNAVUT_EXTRA_GENERATOR_ARGS="--enable-override-variable-array-capacity;--embed-auditing-info" --preset config-clang-native-cpp-20 + cmake --build --preset build-clang-native-cpp-20-debugcov + cmake -DNUNAVUT_EXTRA_GENERATOR_ARGS="--enable-override-variable-array-capacity;--embed-auditing-info" --preset config-clang-native-c-11 + cmake --build --preset build-clang-native-c-11-debugcov language-verification-python: runs-on: ubuntu-latest diff --git a/.vscode/launch.json b/.vscode/launch.json index bb65b638..75529b4f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,17 +4,29 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { + "name": "Python Debugger: Current File", + "type": "debugpy", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal", + "cwd": "${workspaceFolder}/verification" + }, { "name": "Python: nnvg c++", "type": "debugpy", "request": "launch", "module": "nunavut", "cwd": "${workspaceFolder}/src", - "args": ["--experimental-languages", - "--verbose", - "--outdir", "${workspaceFolder}/nunavut_out", - "-l", "cpp", - "${workspaceFolder}/submodules/public_regulated_data_types/uavcan"] + "args": [ + "--experimental-languages", + "--verbose", + "--outdir", + "${workspaceFolder}/nunavut_out", + "-l", + "cpp", + "${workspaceFolder}/submodules/public_regulated_data_types/uavcan" + ] }, { "name": "Pytest: current test", @@ -84,7 +96,9 @@ "name": "lldb: cmake.launchTargetPath", "args": [], "cwd": "${workspaceFolder}", - "initCommands": ["settings set target.process.thread.step-avoid-regexp \"\""], + "initCommands": [ + "settings set target.process.thread.step-avoid-regexp \"\"" + ], "env": { "PATH": "$PATH:${command:cmake.launchTargetDirectory}", }, diff --git a/.vscode/settings.json b/.vscode/settings.json index 92c5c203..bbf67685 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -60,5 +60,6 @@ "description": "Words used in in the Nunavut codebase to add to spell checker dictionaries.", "addWords": true } - } + }, + "cmake.sourceDirectory": "${workspaceFolder}/verification" } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 69b4f37b..9ebc7b5a 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -36,23 +36,6 @@ "kind": "build", }, "problemMatcher": [] - }, - { - "label": "verify c native32", - "type": "shell", - "command": "${workspaceFolder}/.github/verify.py", - "args": [ - "--verbose", - "--force", - "--language", "c", - "--endianness", "any", - "--platform", "native32", - "--build-type", "Debug" - ], - "group": "build", - "problemMatcher": [ - "$gcc" - ] } ] } diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..2bb1e1f3 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,13 @@ +# +# Copyright (C) OpenCyphal Development Team +# Copyright Amazon.com Inc. or its affiliates. +# SPDX-License-Identifier: MIT +# +# This top-level CMakeLists.txt is added for symmetry with the python environment. All C/C++ build +# for testing Nunavut is located under the verification folder. + +cmake_minimum_required(VERSION 3.20) + +project(nunavut) + +add_subdirectory("verification") diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index df50898d..fa0ff906 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -82,13 +82,20 @@ To run the language verification build you'll need to use a different docker con docker pull ghcr.io/opencyphal/toolshed:ts22.4.10 docker run --rm -it -v $PWD:/workspace ghcr.io/opencyphal/toolshed:ts22.4.10 - cd /workspace - ./.github/verify.py -l c - ./.github/verify.py -l cpp + cd /workspace/verification + cmake --list-presets -The verify.py script is a simple commandline generator for our cmake scripts. Use help for details:: +Choose one of the presets. For example:: - ./.github/verify.py --help + cmake --preset config-clang-native-c-11 + +To build replace the prefix ``config`` with ``build`` and suffix with one of the configurations listed in the presets +file as ``CMAKE_CONFIGURATION_TYPES`` but in all lower-case. For example:: + + cmake --build --preset build-clang-native-c-11-debug + +The verification cmake uses Ninja Multi-Config so you can run any ``build-clang-native-c-11-`` build flavor without +re-configuring in this example. If you get a "denied" response from ghcr your ip might be getting rate-limited. While these are public containers you'll have to login to get around any rate-limiting for your local site. See [the github docs](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry) @@ -254,88 +261,33 @@ TLDR:: git submodule update --init --recursive docker run --rm -it -v $PWD:/repo ghcr.io/opencyphal/toolshed:ts22.4.10 - export NUNAVUT_VERIFICATION_LANG=c cd verification - mkdir "build_$NUNAVUT_VERIFICATION_LANG" - cd "build_$NUNAVUT_VERIFICATION_LANG" - cmake -DNUNAVUT_FLAGSET=/repo/verification/cmake/compiler_flag_sets/native.cmake -DCMAKE_TOOLCHAIN_FILE=cmake/toolchains/gcc-native.cmake -DCMAKE_GENERATOR=Ninja .. - cmake --build . --target help - -Try running a test which will first compile the test. For example, in the C language build :: - - cmake --build . --target test_all - -To run the C++ test use the same steps shown in the TLDR above but set :code:`NUNAVUT_VERIFICATION_LANG` to -"cpp" first. - -In the list of targets that the :code:`cmake --build . --target help` command lists the targets that build tests -will be prefixed with :code:`test_` and the psedo-target that also executes the test will be prefixed with -:code:`run_test_`. You should avoid the :code:`_with_lcov` when you are manually building tests. - -To obtain coverage information for the verification suite (not the Python code), -build the `cov_all` target and inspect the output under the `coverage` directory. - -cmake build options ------------------------------------------------- - -The following options are supported when configuring your build. These can be specified by using :code:`-D` arguments -to cmake. For example :: - - cmake -DNUNAVUT_VERIFICATION_LANG=c -DNUNAVUT_VERIFICATION_TARGET_ENDIANNESS=any .. - -+-----------------------------------------+---------+----------+------------------------------------+------------------------------------------------------------------+ -| Option | Type | Default | Values | Description | -+=========================================+=========+==========+====================================+==================================================================+ -|| CMAKE_BUILD_TYPE || string || release || Debug, Release, MinSizeRel || Compiler optimizations are set based | -|| || || || || on the CMake build type. | -+-----------------------------------------+---------+----------+------------------------------------+------------------------------------------------------------------+ -|| NUNAVUT_VERIFICATION_LANG || string || c, cpp || Specifies the language for source || | -|| || || || || code generated by nnvg. | -+-----------------------------------------+---------+----------+------------------------------------+------------------------------------------------------------------+ -|| NUNAVUT_VERIFICATION_TARGET_ENDIANNESS || string || any || little, big, any || Modifies generated serialization code | -|| || || || || and support code to support various | -|| || || || || CPU architectures. Other than | -|| || || || || endianess, Nunavut serialization and | -|| || || || || support code should be generic. | -+-----------------------------------------+---------+----------+------------------------------------+------------------------------------------------------------------+ -|| NUNAVUT_VERIFICATION_TARGET_PLATFORM || string || (unset) || native32, native64 || The target platform to compile for. | -|| || || || || In future releases we hope to support | -|| || || || || ppc (Big), AVR8, RISCV, ARM. | -+-----------------------------------------+---------+----------+------------------------------------+------------------------------------------------------------------+ -|| NUNAVUT_VERIFICATION_SER_ASSERT || bool || ON || ON, OFF || Enable or disable asserts in | -|| || || || || generated serialization and support | -|| || || || || code. | -+-----------------------------------------+---------+----------+------------------------------------+------------------------------------------------------------------+ -|| NUNAVUT_VERIFICATION_SER_FP_DISABLE || bool || OFF || ON, OFF || Enable to omit floating-point | -|| || || || || serialization routines. | -+-----------------------------------------+---------+----------+------------------------------------+------------------------------------------------------------------+ -| NUNAVUT_VERIFICATION_LANG_STANDARD | string | (empty) | c++17, c99 (etc) | override value for the -std compiler flag of the target language | -+-----------------------------------------+---------+----------+------------------------------------+------------------------------------------------------------------+ + cmake --preset config-clang-native-c-11 + cmake --build --preset build-clang-native-c-11-debug +To see all presets available do:: + cmake --list-presets + cmake --build --list-presets -\* *Because this option has no default, a value must be provided by the user.* +After configuring you can also use Ninja directly:: -VSCode Remote Container Development of Verification Tests -==================================================================================== + cd build + ninja -t targets -To write and debug verification tests using `VSCode Remote Containers`_ you'll need to use the -"Open Folder in Container..." option: - -.. image:: /docs/static/images/vscode_open_in_container.png - -Open the "verification" folder: +To obtain coverage information for the verification suite (not the Python code), +build the `cov_all` target and inspect the output under the `coverage` directory. -.. image:: /docs/static/images/vscode_folder_verification.png +While we strongly encourage you to use the cmake presets, the CMakeLists.txt for the verification suite is driven by +three variables you can set in your environment or pass into cmake if using cmake directly: -We play a little trick here where we dump you back into the Nunvut repo root when you reopen in -the container. This lets you also work with the Python source. If you "reopen locally" while in -this state, however, you'll find yourself back in the verification folder which can be a little -disorienting. Write to Microsoft asking them to allow multiple images in the .devcontainer -json and we can get rid of this ugly hack. Sorry. + - ``NUNAVUT_VERIFICATION_LANG`` - By default this will be 'c'. Set to 'c' or 'cpp' + - ``NUNAVUT_VERIFICATION_LANG_STANDARD`` - See the supported options for ``--language-standard`` (see ``nnvg -h``) + - ``NUNAVUT_VERIFICATION_TARGET_PLATFORM`` - 'native' by default. 'native32' for cross-compiling for a 32-bit version of the native platform. +All other options set when generating code are provided by setting ``NUNAVUT_EXTRA_GENERATOR_ARGS`` in your environment. .. _`read the docs`: https://readthedocs.org/ .. _`tox`: https://tox.readthedocs.io/en/latest/ diff --git a/NunavutConfig.cmake b/NunavutConfig.cmake index 42edc5b5..0a237a46 100644 --- a/NunavutConfig.cmake +++ b/NunavutConfig.cmake @@ -37,14 +37,14 @@ set_and_check(NUNAVUT_SOURCE_DIR "${PACKAGE_PREFIX_DIR}/src") check_required_components(Nunavut Python3) execute_process( - COMMAND ${Python3_EXECUTABLE} ${PACKAGE_PREFIX_DIR}/.github/verify.py --major-minor-version-only + COMMAND ${Python3_EXECUTABLE} ${PACKAGE_PREFIX_DIR}/version_check_nunavut.py --major-minor-version-only OUTPUT_VARIABLE NUNAVUT_VERSION_MAJOR_MINOR OUTPUT_STRIP_TRAILING_WHITESPACE WORKING_DIRECTORY "${PACKAGE_PREFIX_DIR}" ) execute_process( - COMMAND ${Python3_EXECUTABLE} ${PACKAGE_PREFIX_DIR}/.github/verify.py --version-only + COMMAND ${Python3_EXECUTABLE} ${PACKAGE_PREFIX_DIR}/version_check_nunavut.py --version-only OUTPUT_VARIABLE NUNAVUT_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE WORKING_DIRECTORY "${PACKAGE_PREFIX_DIR}" @@ -63,6 +63,85 @@ endif() # ################################################################################### # HELPER MACROS FOR INTERNAL FUNCTIONS. YOU CAN IGNORE THESE. +list(APPEND NUNAVUT_CONFIGURE_OPTIONS + ALLOW_EXPERIMENTAL_LANGUAGES + CONSOLE_DEBUG + SUPPORT_ONLY + NO_SUPPORT + OMIT_PUBLIC_REGULATED_NAMESPACE + OMIT_DEPENDENCIES +) +list(APPEND NUNAVUT_CONFIGURE_SINGLE_VALUE_ARGS + LANGUAGE + OUTPUT_DIR + LANGUAGE_STANDARD + PYDSDL_PATH + WORKING_DIRECTORY + FILE_EXTENSION +) +list(APPEND NUNAVUT_CONFIGURE_MULTI_VALUE_ARGS + CONFIGURATION + DSDL_FILES + DSDL_NAMESPACES +) + +# Forward option arguments to an inner function +macro(_forward_option arg_list arg_key) + if (NOT DEFINED ${arg_list}) + set(${arg_list}) + endif() + if (NUNV_ARG_${arg_key}) + list(APPEND ${arg_list} ${arg_key}) + endif() +endmacro() + +# Forward single-value arguments to an inner function. Compliant with CMP0174. +macro(_forward_single_value_arg arg_list arg_key) + if (NOT DEFINED ${arg_list}) + set(${arg_list}) + endif() + if (NUNV_ARG_${arg_key}) + list(APPEND ${arg_list} ${arg_key} "${NUNV_ARG_${arg_key}}") + endif() +endmacro() + +# Helper for populating a list of key[i]=value[i+1] pairs to ensure only one key/value +# pair exists. This also raises errors if a duplicate key insertion is attempted with a +# different value but has no effect if a duplicate key insertion with the same value is +# attempted (idempotence). +macro(_add_single_value_once arg_list arg_key arg_value) + if (NOT ${arg_list}) + set(${arg_list}) + endif() + list(FIND ${arg_list} ${arg_key} arg_list_index) + if (arg_list_index GREATER -1) + list(LENGTH ${arg_list} arg_list_length) + math(EXPR arg_list_index "${arg_list_index} + 1") + if (arg_list_length GREATER arg_list_index) + list(GET ${arg_list} ${arg_list_index} arg_list_value_at_index) + if (NOT ${arg_list_value_at_index} STREQUAL ${arg_value}) + message(FATAL_ERROR "Attempted to set ${arg_key}=${arg_value} when ${arg_key}=${arg_list_value_at_index} was already set?") + endif() + else() + message(FATAL_ERROR "${arg_key} was set as an option but it is supposed to have a single value?") + endif() + else() + list(APPEND ${arg_list} ${arg_key} ${arg_value}) + endif() +endmacro() + +# Helper for populating a list without creating duplicate values. +macro(_add_option_once arg_list arg_key) + if (NOT ${arg_list}) + set(${arg_list}) + endif() + list(FIND ${arg_list} ${arg_key} arg_list_index) + if (arg_list_index EQUAL -1) + list(APPEND ${arg_list} ${arg_key}) + endif() +endmacro() + + # transform a JSON array into a CMAKE list macro(_nunavut_json_array_to_list _json_array _list) string(JSON _json_array_type ERROR_VARIABLE _json_error TYPE ${${_json_array}}) @@ -97,165 +176,131 @@ macro(_nunavut_json_array_to_list _json_array _list) set(${_list} ${_local_list}) endmacro() -# used internally to unify argument handling for standards nunavut cli arguments across all cmake functions -# Note: all options are repeated as "LOCAL_ARG_[option name]" to support forwarding. -macro(_nunavut_config_args has_name options singleValueArgs multiValueArgs usageLines) - list(APPEND ${options} - ALLOW_EXPERIMENTAL_LANGUAGES - CONSOLE_DEBUG - SUPPORT_ONLY - NO_SUPPORT - OMIT_PUBLIC_REGULATED_NAMESPACE - OMIT_DEPENDENCIES - ) - list(APPEND ${singleValueArgs} - NAME - LANGUAGE - OUTPUT_DIR - LANGUAGE_STANDARD - PYDSDL_PATH - WORKING_DIRECTORY - FILE_EXTENSION - ) - list(APPEND ${multiValueArgs} CONFIGURATION DSDL_FILES DSDL_NAMESPACES) +# Used internally to unify argument handling for standards nunavut cli arguments across all cmake functions. +# Defines the following, local variables: +# NUNV_LOCAL_PYTHON_PATH - The pythonpath to use when invoking Nunavut from source. +# NUNV_LOCAL_LOOKUP_DIRS - list if --lookup-dir arguments +# NUNV_LOCAL_DYNAMIC_ARGS - all other arguments to pass to Nunavut +# NUNV_LOCAL_DEBUG_COMMAND_OPTIONS - Additional options to use when setting up the custom command. +macro(_nunavut_config_args options singleValueArgs multiValueArgs usageLines) + set(NUNV_LOCAL_DYNAMIC_ARGS "") + set(NUNV_LOCAL_LOOKUP_DIRS "") + + list(APPEND ${options} ${NUNAVUT_CONFIGURE_OPTIONS}) + list(APPEND ${singleValueArgs} ${NUNAVUT_CONFIGURE_SINGLE_VALUE_ARGS}) + list(APPEND ${multiValueArgs} ${NUNAVUT_CONFIGURE_MULTI_VALUE_ARGS}) list(INSERT ${usageLines} 0 "USAGE:" - " ${CMAKE_CURRENT_FUNCTION}") - - if(${has_name}) - list(INSERT ${usageLines} 2 " NAME LANGUAGE DSDL_FILES [DSDL_NAMESPACES ]") - else() - list(INSERT ${usageLines} 2 " LANGUAGE DSDL_FILES [DSDL_NAMESPACES ]") - endif() - - list(INSERT ${usageLines} 3 + " ${CMAKE_CURRENT_FUNCTION}" + " LANGUAGE DSDL_FILES [DSDL_NAMESPACES ]" " [LANGUAGE_STANDARD ] [OUTPUT_DIR ] [CONFIGURATION ]" " [WORKING_DIRECTORY ] [PYDSDL_PATH ] [FILE_EXTENSION ]" " [ALLOW_EXPERIMENTAL_LANGUAGES] [CONSOLE_DEBUG] [SUPPORT_ONLY|NO_SUPPORT] [OMIT_DEPENDENCIES]" ) - cmake_parse_arguments(PARSE_ARGV 0 ARG "${${options}}" "${${singleValueArgs}}" "${${multiValueArgs}}") - - if(${has_name} AND NOT ARG_NAME) - message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}: NAME is required.") - endif() + cmake_parse_arguments(PARSE_ARGV 0 NUNV_ARG "${${options}}" "${${singleValueArgs}}" "${${multiValueArgs}}") - if(NOT ARG_LANGUAGE) + if(NOT NUNV_ARG_LANGUAGE) message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}: LANGUAGE is required.") endif() - if(${ARG_LANGUAGE} STREQUAL "cpp") - if(NOT ARG_ALLOW_EXPERIMENTAL_LANGUAGES) - message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}: C++ support is experimental and must be enabled by setting the ALLOW_EXPERIMENTAL_LANGUAGES option.") + if(${NUNV_ARG_LANGUAGE} STREQUAL "cpp") + if (NOT NUNV_ARG_ALLOW_EXPERIMENTAL_LANGUAGES) + list(FIND NUNV_LOCAL_DYNAMIC_ARGS "--include-experimental-languages" NUNV_LOCAL_DYNAMIC_ARG_INDEX) + if(NUNV_LOCAL_DYNAMIC_ARG_INDEX EQUAL -1) + message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}: C++ support is experimental and must be enabled by setting the ALLOW_EXPERIMENTAL_LANGUAGES option.") + endif() endif() - elseif(NOT ${ARG_LANGUAGE} STREQUAL "c") + elseif(NOT ${NUNV_ARG_LANGUAGE} STREQUAL "c") message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}: LANGUAGE must be 'c' or 'cpp'.") endif() - if(NOT ARG_OUTPUT_DIR) - set(ARG_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated") + if(NOT NUNV_ARG_OUTPUT_DIR) + set(NUNV_ARG_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated") endif() - if(NOT ARG_WORKING_DIRECTORY) - set(ARG_WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") + if(NOT NUNV_ARG_WORKING_DIRECTORY) + set(NUNV_ARG_WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}") endif() - if(ARG_SUPPORT_ONLY AND ARG_NO_SUPPORT) + if(NUNV_ARG_SUPPORT_ONLY AND NUNV_ARG_NO_SUPPORT) message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}: SUPPORT_ONLY and NO_SUPPORT are mutually exclusive.") endif() - if(NOT ARG_DSDL_FILES AND NOT ARG_SUPPORT_ONLY) + if(NOT NUNV_ARG_DSDL_FILES AND NOT NUNV_ARG_SUPPORT_ONLY) message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}: DSDL_FILES is required.") endif() - if(ARG_UNPARSED_ARGUMENTS) - list(INSERT ${usageLines} 0 "Unknown arguments found: ${ARG_UNPARSED_ARGUMENTS}") - string(JOIN "\n" LOCAL_USAGE_MESSAGE ${${usageLines}}) - message(FATAL_ERROR "${LOCAL_USAGE_MESSAGE}\n") + if(NUNV_ARG_UNPARSED_ARGUMENTS) + list(INSERT ${usageLines} 0 "Unknown arguments found: ${NUNV_ARG_UNPARSED_ARGUMENTS}") + string(JOIN "\n" NUNV_LOCAL_USAGE_MESSAGE ${${usageLines}}) + message(FATAL_ERROR "${NUNV_LOCAL_USAGE_MESSAGE}\n") endif() -endmacro() -macro(_nunavut_local_args) - # handle forming arguments for the nunavut tool based on arguments passed into this function. - if (NOT LOCAL_DYNAMIC_ARGS) - set(LOCAL_DYNAMIC_ARGS "") + if(NUNV_ARG_DSDL_NAMESPACES) + foreach(NUNV_LOCAL_DSDL_NAMESPACE IN LISTS NUNV_ARG_DSDL_NAMESPACES) + list(APPEND NUNV_LOCAL_LOOKUP_DIRS "--lookup-dir" "${NUNV_LOCAL_DSDL_NAMESPACE}") + endforeach() endif() - if(ARG_DSDL_NAMESPACES) - foreach(LOCAL_DSDL_NAMESPACE IN LISTS ARG_DSDL_NAMESPACES) - list(APPEND LOCAL_DYNAMIC_ARGS "--lookup-dir" "${LOCAL_DSDL_NAMESPACE}") - endforeach() + if(NOT NUNV_ARG_OMIT_PUBLIC_REGULATED_NAMESPACE) + list(APPEND NUNV_LOCAL_LOOKUP_DIRS "--lookup-dir" "${NUNAVUT_SUBMODULES_DIR}/public_regulated_data_types/uavcan") endif() - if(NOT ARG_OMIT_PUBLIC_REGULATED_NAMESPACE) - list(APPEND LOCAL_DYNAMIC_ARGS "--lookup-dir" "${NUNAVUT_SUBMODULES_DIR}/public_regulated_data_types/uavcan") + if(NUNV_ARG_LANGUAGE) + _add_single_value_once(NUNV_LOCAL_DYNAMIC_ARGS "--target-language" "${NUNV_ARG_LANGUAGE}") endif() - if(ARG_LANGUAGE_STANDARD) - list(APPEND LOCAL_DYNAMIC_ARGS "--language-standard" "${ARG_LANGUAGE_STANDARD}") + if(NUNV_ARG_OUTPUT_DIR) + _add_single_value_once(NUNV_LOCAL_DYNAMIC_ARGS "--outdir" "${NUNV_ARG_OUTPUT_DIR}") endif() - if(ARG_CONFIGURATION) - foreach(LOCAL_CONFIGURATION IN LISTS ARG_CONFIGURATION) - list(APPEND LOCAL_DYNAMIC_ARGS "--configuration" "${LOCAL_CONFIGURATION}") + if(NUNV_ARG_LANGUAGE_STANDARD) + _add_single_value_once(NUNV_LOCAL_DYNAMIC_ARGS "--language-standard" "${NUNV_ARG_LANGUAGE_STANDARD}") + endif() + + if(NUNV_ARG_CONFIGURATION) + foreach(NUNV_LOCAL_CONFIGURATION IN LISTS NUNV_ARG_CONFIGURATION) + list(APPEND NUNV_LOCAL_DYNAMIC_ARGS "--configuration" "${NUNV_LOCAL_CONFIGURATION}") endforeach() endif() - if(ARG_ALLOW_EXPERIMENTAL_LANGUAGES) - set(LOCAL_ARG_ALLOW_EXPERIMENTAL_LANGUAGES "ALLOW_EXPERIMENTAL_LANGUAGES") - list(APPEND LOCAL_DYNAMIC_ARGS "--include-experimental-languages") - else() - set(LOCAL_ARG_ALLOW_EXPERIMENTAL_LANGUAGES) + if(NUNV_ARG_ALLOW_EXPERIMENTAL_LANGUAGES) + _add_option_once(NUNV_LOCAL_DYNAMIC_ARGS "--include-experimental-languages") endif() - if(ARG_SUPPORT_ONLY) - set(LOCAL_ARG_SUPPORT_ONLY "SUPPORT_ONLY") - set(LOCAL_ARG_NO_SUPPORT "") - list(APPEND LOCAL_DYNAMIC_ARGS "--generate-support" "only") - elseif(ARG_NO_SUPPORT) - set(LOCAL_ARG_SUPPORT_ONLY "") - set(LOCAL_ARG_NO_SUPPORT "NO_SUPPORT") - list(APPEND LOCAL_DYNAMIC_ARGS "--generate-support" "never") - else() - set(LOCAL_ARG_SUPPORT_ONLY "") - set(LOCAL_ARG_NO_SUPPORT "") + if(NUNV_ARG_SUPPORT_ONLY) + _add_single_value_once(NUNV_LOCAL_DYNAMIC_ARGS "--generate-support" "only") + endif() + + if(NUNV_ARG_NO_SUPPORT) + _add_single_value_once(NUNV_LOCAL_DYNAMIC_ARGS "--generate-support" "never") endif() - if(ARG_FILE_EXTENSION) - list(APPEND LOCAL_DYNAMIC_ARGS "--output-extension" "${ARG_FILE_EXTENSION}") + if(NUNV_ARG_FILE_EXTENSION) + _add_single_value_once(NUNV_LOCAL_DYNAMIC_ARGS "--output-extension" ${NUNV_ARG_FILE_EXTENSION}) endif() # Setup running nunavut and pydsdl from source - set(LOCAL_PYTHON_PATH "${NUNAVUT_SOURCE_DIR}") + set(NUNV_LOCAL_PYTHON_PATH "${NUNAVUT_SOURCE_DIR}") - if(ARG_PYDSDL_PATH) - set(LOCAL_PYTHON_PATH "${LOCAL_PYTHON_PATH}${NUNAVUT_PATH_LIST_SEP}${ARG_PYDSDL_PATH}") + if(NUNV_ARG_PYDSDL_PATH) + set(NUNV_LOCAL_PYTHON_PATH "${NUNV_LOCAL_PYTHON_PATH}${NUNAVUT_PATH_LIST_SEP}${NUNV_ARG_PYDSDL_PATH}") else() - set(LOCAL_PYTHON_PATH "${LOCAL_PYTHON_PATH}${NUNAVUT_PATH_LIST_SEP}${NUNAVUT_SUBMODULES_DIR}/pydsdl") + set(NUNV_LOCAL_PYTHON_PATH "${NUNV_LOCAL_PYTHON_PATH}${NUNAVUT_PATH_LIST_SEP}${NUNAVUT_SUBMODULES_DIR}/pydsdl") endif() - set(ENV{PYTHONPATH} ${LOCAL_PYTHON_PATH}) + set(ENV{PYTHONPATH} ${NUNV_LOCAL_PYTHON_PATH}) # Setup additional debug options if requested. - set(LOCAL_DEBUG_COMMAND_OPTIONS "") + set(NUNV_LOCAL_DEBUG_COMMAND_OPTIONS "") - if(ARG_CONSOLE_DEBUG) - set(LOCAL_ARG_CONSOLE_DEBUG "CONSOLE_DEBUG") - list(APPEND LOCAL_DEBUG_COMMAND_OPTIONS "COMMAND_ECHO" "STDOUT" "ECHO_OUTPUT_VARIABLE") - else() - set(LOCAL_ARG_CONSOLE_DEBUG "") + if(NUNV_ARG_CONSOLE_DEBUG) + list(APPEND NUNV_LOCAL_DEBUG_COMMAND_OPTIONS "COMMAND_ECHO" "STDOUT" "ECHO_OUTPUT_VARIABLE") endif() - if(ARG_EXPORT_MANIFEST) - set(LOCAL_JSON_FORMAT "json-pretty") - set(LOCAL_LIST_CONFIGURATION "--list-configuration") - else() - set(LOCAL_JSON_FORMAT "json") - set(LOCAL_LIST_CONFIGURATION "") - endif() - - if(ARG_OMIT_DEPENDENCIES) - list(APPEND LOCAL_DYNAMIC_ARGS "--omit-dependencies") + if(NUNV_ARG_OMIT_DEPENDENCIES) + _add_option_once(NUNV_LOCAL_DYNAMIC_ARGS "--omit-dependencies") endif() endmacro() @@ -264,135 +309,30 @@ endmacro() #[==[.rst: .. cmake:variable:: NUNAVUT_SUBMODULES_DIR - Path to the submodules folder in the nunavut repository. + Set by the Nunavut package, this is the path to the submodules folder in the nunavut repository. #]==] set(NUNAVUT_SUBMODULES_DIR ${CMAKE_CURRENT_LIST_DIR}/submodules) #[==[.rst: + .. cmake:envvar:: NUNAVUT_EXTRA_GENERATOR_ARGS - .. cmake:command:: export_nunavut_manifest - - Generate a json file listing the inputs to a code gen rule and the outputs generated by the rule. This is - useful for complex builds where discovering the inputs and outputs is time consuming. By generating this file - and checking it into source control, the build can use the manifest to avoid dynamic discovery for each new - configuration step. - - - **param** ``LANGUAGE`` **str**: - - The language to generate code for. Supported types are ``c`` and ``cpp``. - - - **param** ``DSDL_FILES`` **list[path]**: - - A list of DSDL files to generate code for. - - - **param** ``DSDL_NAMESPACES`` **optional list[path]**: - - A list of namespaces to search for dependencies in. Unless ``OMIT_PUBLIC_REGULATED_NAMESPACE`` is set, this - will always include ``${NUNAVUT_SUBMODULES_DIR}/public_regulated_data_types/uavcan`` - - - **param** ``LANGUAGE_STANDARD`` **optional str**: - - The language standard to use. - - - **param** ``OUTPUT_DIR`` **optional path**: - - The directory to write generated code to. If omitted then ``${CMAKE_CURRENT_BINARY_DIR}/generated`` is used. - - - **param** ``CONFIGURATION`` **optional list[path]**: - - A list of configuration files to pass into nunavut. See the nunavut documentation for more information about - configuration files. - - - **param** ``WORKING_DIRECTORY`` **optional path**: - - The working directory to use when invoking the Nunavut tool. If omitted then ``${CMAKE_CURRENT_SOURCE_DIR}`` - is used. - - - **param** ``PYDSDL_PATH`` **optional path**: - - The path to the PyDSDL tool. If omitted then this is set to ${NUNAVUT_SUBMODULES_DIR}/pydsdl - which is the root of the pydsdl submodule in the Nunavut repo. - - - **param** ``FILE_EXTENSION`` **optional str**: - - The file extension to use for generated files. If omitted then the default for the language is used. - - - **option** ``ALLOW_EXPERIMENTAL_LANGUAGES``: - - If set then unsupported languages will be allowed. - - - **option** ``CONSOLE_DEBUG``: - - If set then verbose output will be enabled. - - - **option** ``SUPPORT_ONLY``: - - If set then the library created will contain only support code needed to use the code generated for - ``DSDL_FILES``. This allows different cyphal libraries to share a single set of support headers and avoids - duplicate target rules. This option is mutually exclusive with ``NO_SUPPORT``. - - - **option** ``NO_SUPPORT``: - - If set then the library created will not contain support code needed to use the code generated for - ``DSDL_FILES``. This is a mutually exclusive option with ``SUPPORT_ONLY``. - - - **option** ``OMIT_PUBLIC_REGULATED_NAMESPACE``: - - By default, ``${NUNAVUT_SUBMODULES_DIR}/public_regulated_data_types/uavcan`` is added to the list of - ``DSDL_NAMESPACES`` even if this variable is not set. This option disables this behaviour so only explicitly - listed ``DSDL_NAMESPACES`` values will be used. - - - **option** ``OMIT_DEPENDENCIES``: - - Disables the generation of dependent types. This is useful when setting up build rules for a project where - the dependent types are generated separately. - - - **param** ``OUT_MANIFEST_PATH``: - - If set then this method write a variable named ``${OUT_MANIFEST_PATH}`` with the path to the manifest file - in the calling scope. + If defined, this environment variable is used as additional command-line arguments which are passed + to Nunavut when generating code. This can also be specified as a cache variable (e.g. + ``cmake -DNUNAVUT_EXTRA_GENERATOR_ARGS``) which will override any value set in the environment. + As an environment variable, this list of args must use the system's list separator (``NUNAVUT_PATH_LIST_SEP``) + to specify multiple arguments. As a cache variable, cmake's semicolon list separator must be used. #]==] -function(export_nunavut_manifest) - # +-[input]----------------------------------------------------------------+ - set(options) - set(singleValueArgs OUT_MANIFEST_PATH) - set(multiValueArgs) - set(usageLines " [OUT_MANIFEST_PATH ]") - _nunavut_config_args(ON options singleValueArgs multiValueArgs usageLines) - - # +-[body]-----------------------------------------------------------------+ - _nunavut_local_args() - # List all inputs to use as the dependencies for the custom command. - execute_process( - COMMAND - ${Python3_EXECUTABLE} -m nunavut - --target-language ${ARG_LANGUAGE} - --list-inputs - --list-outputs - --outdir ${ARG_OUTPUT_DIR} - ${LOCAL_LIST_CONFIGURATION} - --list-format ${LOCAL_JSON_FORMAT} - --dry-run - ${LOCAL_DYNAMIC_ARGS} - ${ARG_DSDL_FILES} - ${LOCAL_DEBUG_COMMAND_OPTIONS} - WORKING_DIRECTORY ${ARG_WORKING_DIRECTORY} - OUTPUT_VARIABLE LOCAL_LIB_INPUTS_AND_OUTPUTS - OUTPUT_STRIP_TRAILING_WHITESPACE - ENCODING UTF8 - ) +#[==[.rst: + .. cmake:variable:: NUNAVUT_PATH_LIST_SEP - set(LOCAL_MANIFEST_FILE "${ARG_OUTPUT_DIR}/${ARG_NAME}.json") - file(WRITE ${LOCAL_MANIFEST_FILE} ${LOCAL_LIB_INPUTS_AND_OUTPUTS}) + Platform-specific list separator determed by the Nunavut package and used to parse lists read from the + environment or form lists set in the environment. Users shouldn't need to use this variable but it can + be overridden if the Nunavut cmake package's automatic detection is incorrect. - # +-[output]---------------------------------------------------------------+ - if(ARG_OUT_MANIFEST_PATH) - set(${ARG_OUT_MANIFEST_PATH} ${LOCAL_MANIFEST_FILE} PARENT_SCOPE) - endif() -endfunction() +#]==] #[==[.rst: @@ -446,6 +386,13 @@ endfunction() The file extension to use for generated files. If omitted then the default for the language is used. + - **param** ``EXPORT_CONFIGURE_MANIFEST`` **optional path**: + + A folder under which a json file containing a list of all the inputs, outputs, and other information about + the configure-step execution of nunavut will be written to. This file will be named + ``${EXPORT_CONFIGURE_MANIFEST}/generate_commands.json`` and cannot contain generator expressions as this + file is created at configure-time. + - **option** ``ALLOW_EXPERIMENTAL_LANGUAGES``: If set then unsupported languages will be allowed. @@ -496,84 +443,90 @@ function(discover_inputs_and_outputs) # +-[input]----------------------------------------------------------------+ set(options) set(singleValueArgs + EXPORT_CONFIGURE_MANIFEST OUT_MANIFEST_DATA OUT_INPUTS_LIST OUT_OUTPUTS_LIST ) set(multiValueArgs) list(APPEND usageLines - " [OUT_INPUTS_LIST ] [OUT_OUTPUTS_LIST ] [OUT_MANIFEST_DATA ]" + " [EXPORT_CONFIGURE_MANIFEST ] [OUT_INPUTS_LIST ]" + " [OUT_OUTPUTS_LIST ] [OUT_MANIFEST_DATA ]" ) - _nunavut_config_args(OFF options singleValueArgs multiValueArgs usageLines) + _nunavut_config_args(options singleValueArgs multiValueArgs usageLines) # +-[body]-----------------------------------------------------------------+ - _nunavut_local_args() + + if(NUNV_ARG_EXPORT_CONFIGURE_MANIFEST) + _add_option_once(NUNV_LOCAL_DYNAMIC_ARGS "--list-configuration") + _add_single_value_once(NUNV_LOCAL_DYNAMIC_ARGS "--list-format" "json-pretty") + list(APPEND NUNV_LOCAL_DYNAMIC_ARGS "--list-to-file" ${NUNV_ARG_EXPORT_CONFIGURE_MANIFEST}/generate_commands.json) + else() + _add_single_value_once(NUNV_LOCAL_DYNAMIC_ARGS "--list-format" "json") + endif() # List all inputs to use as the dependencies for the custom command. execute_process( COMMAND ${Python3_EXECUTABLE} -m nunavut - --target-language ${ARG_LANGUAGE} --list-inputs --list-outputs - --outdir ${ARG_OUTPUT_DIR} - ${LOCAL_LIST_CONFIGURATION} - --list-format ${LOCAL_JSON_FORMAT} --dry-run - ${LOCAL_DYNAMIC_ARGS} - ${ARG_DSDL_FILES} - ${LOCAL_DEBUG_COMMAND_OPTIONS} - WORKING_DIRECTORY ${ARG_WORKING_DIRECTORY} - OUTPUT_VARIABLE LOCAL_LIB_INPUTS_AND_OUTPUTS + ${NUNV_LOCAL_DYNAMIC_ARGS} + ${NUNV_LOCAL_LOOKUP_DIRS} + ${NUNV_ARG_DSDL_FILES} + ${NUNV_LOCAL_DEBUG_COMMAND_OPTIONS} + WORKING_DIRECTORY ${NUNV_ARG_WORKING_DIRECTORY} + OUTPUT_VARIABLE NUNV_LOCAL_LIB_INPUTS_AND_OUTPUTS OUTPUT_STRIP_TRAILING_WHITESPACE ENCODING UTF8 ) - string(JSON LOCAL_LIB_INPUTS ERROR_VARIABLE LOCAL_LIB_READ_INPUTS_ERROR GET ${LOCAL_LIB_INPUTS_AND_OUTPUTS} "inputs") + string(JSON NUNV_LOCAL_LIB_INPUTS ERROR_VARIABLE NUNV_LOCAL_LIB_READ_INPUTS_ERROR GET ${NUNV_LOCAL_LIB_INPUTS_AND_OUTPUTS} "inputs") - if(LOCAL_LIB_READ_INPUTS_ERROR) - message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}: Failed to read inputs from nunavut: ${LOCAL_LIB_READ_INPUTS_ERROR}") + if(NUNV_LOCAL_LIB_READ_INPUTS_ERROR) + message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}: Failed to read inputs from nunavut: ${NUNV_LOCAL_LIB_READ_INPUTS_ERROR}") endif() - _nunavut_json_array_to_list(LOCAL_LIB_INPUTS LOCAL_LIB_INPUTS_LIST) - list(LENGTH LOCAL_LIB_INPUTS_LIST LOCAL_LIB_INPUTS_LENGTH) + _nunavut_json_array_to_list(NUNV_LOCAL_LIB_INPUTS NUNV_LOCAL_LIB_INPUTS_LIST) + list(LENGTH NUNV_LOCAL_LIB_INPUTS_LIST NUNV_LOCAL_LIB_INPUTS_LENGTH) - if(${LOCAL_LIB_INPUTS_LENGTH} EQUAL 0) - message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}: No input files found for ${LOCAL_TARGET_NAME} (${LOCAL_LIB_INPUTS_LIST}).") + if(${NUNV_LOCAL_LIB_INPUTS_LENGTH} EQUAL 0) + message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}: No input files found for ${NUNV_LOCAL_TARGET_NAME} (${NUNV_LOCAL_LIB_INPUTS_LIST}).") endif() - if(ARG_CONSOLE_DEBUG) - message(STATUS "\n${CMAKE_CURRENT_FUNCTION}: Found input files: ${LOCAL_LIB_INPUTS_LIST}") + if(NUNV_ARG_CONSOLE_DEBUG) + message(STATUS "\n${CMAKE_CURRENT_FUNCTION}: Found input files: ${NUNV_LOCAL_LIB_INPUTS_LIST}") endif() - string(JSON LOCAL_LIB_OUTPUTS ERROR_VARIABLE LOCAL_LIB_READ_OUTPUTS_ERROR GET ${LOCAL_LIB_INPUTS_AND_OUTPUTS} "outputs") + string(JSON NUNV_LOCAL_LIB_OUTPUTS ERROR_VARIABLE NUNV_LOCAL_LIB_READ_OUTPUTS_ERROR GET ${NUNV_LOCAL_LIB_INPUTS_AND_OUTPUTS} "outputs") - if(LOCAL_LIB_READ_OUTPUTS_ERROR) - message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}: Failed to read outputs from nunavut: ${LOCAL_LIB_READ_OUTPUTS_ERROR}") + if(NUNV_LOCAL_LIB_READ_OUTPUTS_ERROR) + message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}: Failed to read outputs from nunavut: ${NUNV_LOCAL_LIB_READ_OUTPUTS_ERROR}") endif() - _nunavut_json_array_to_list(LOCAL_LIB_OUTPUTS LOCAL_LIB_OUTPUTS_LIST) - list(LENGTH LOCAL_LIB_OUTPUTS_LIST LOCAL_LIB_OUTPUTS_LENGTH) + _nunavut_json_array_to_list(NUNV_LOCAL_LIB_OUTPUTS NUNV_LOCAL_LIB_OUTPUTS_LIST) + list(LENGTH NUNV_LOCAL_LIB_OUTPUTS_LIST NUNV_LOCAL_LIB_OUTPUTS_LENGTH) - if(${LOCAL_LIB_OUTPUTS_LENGTH} EQUAL 0) - message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}: No output files found for ${LOCAL_TARGET_NAME}.") + if(${NUNV_LOCAL_LIB_OUTPUTS_LENGTH} EQUAL 0) + message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}: No output files found for ${NUNV_LOCAL_TARGET_NAME}.") endif() - if(ARG_CONSOLE_DEBUG) - message(STATUS "\n${CMAKE_CURRENT_FUNCTION}: Found output files: ${LOCAL_LIB_OUTPUTS_LIST}") + if(NUNV_ARG_CONSOLE_DEBUG) + message(STATUS "\n${CMAKE_CURRENT_FUNCTION}: Found output files: ${NUNV_LOCAL_LIB_OUTPUTS_LIST}") endif() # +-[output]---------------------------------------------------------------+ - if(ARG_OUT_MANIFEST_DATA) - set(${ARG_OUT_MANIFEST_DATA} ${LOCAL_LIB_INPUTS_AND_OUTPUTS} PARENT_SCOPE) + if(NUNV_ARG_OUT_MANIFEST_DATA) + set(${NUNV_ARG_OUT_MANIFEST_DATA} ${NUNV_LOCAL_LIB_INPUTS_AND_OUTPUTS} PARENT_SCOPE) endif() - if(ARG_OUT_INPUTS_LIST) - set(${ARG_OUT_INPUTS_LIST} ${LOCAL_LIB_INPUTS_LIST} PARENT_SCOPE) + if(NUNV_ARG_OUT_INPUTS_LIST) + set(${NUNV_ARG_OUT_INPUTS_LIST} ${NUNV_LOCAL_LIB_INPUTS_LIST} PARENT_SCOPE) endif() - if(ARG_OUT_OUTPUTS_LIST) - set(${ARG_OUT_OUTPUTS_LIST} ${LOCAL_LIB_OUTPUTS_LIST} PARENT_SCOPE) + if(NUNV_ARG_OUT_OUTPUTS_LIST) + set(${NUNV_ARG_OUT_OUTPUTS_LIST} ${NUNV_LOCAL_LIB_OUTPUTS_LIST} PARENT_SCOPE) endif() endfunction() @@ -613,6 +566,20 @@ endfunction() The directory to write generated code to. If omitted then ``${CMAKE_CURRENT_BINARY_DIR}/generated`` is used. + - **param** ``EXPORT_CONFIGURE_MANIFEST`` **optional path**: + + A folder under which a json file containing a list of all the inputs, outputs, and other information about + the configure-step execution of nunavut will be written to. This file will be named + ``${EXPORT_CONFIGURE_MANIFEST}/${OUT_CODEGEN_TARGET}.json`` and cannot contain generator expressions as this + file is created at configure-time (see ``EXPORT_GENERATE_MANIFEST`` for the compile-time equivalent). + + - **param** ``EXPORT_GENERATE_MANIFEST`` **optional path**: + + A folder under which a json file containing a list of all the inputs, outputs, and other information about + the code-generation-step execution of nunavut will be written to. This file will be named + ``${EXPORT_GENERATE_MANIFEST}/${OUT_CODEGEN_TARGET}.json`` and may contain generator expressions as this + file is created only when executing the code gen build rule. + - **param** ``CONFIGURATION`` **optional list[path]**: A list of configuration files to pass into nunavut. See the nunavut documentation for more information about @@ -628,22 +595,18 @@ endfunction() The path to the PyDSDL tool. If omitted then this is set to ${NUNAVUT_SUBMODULES_DIR}/pydsdl which is the root of the pydsdl submodule in the Nunavut repo. - - **param** ``SERIALIZATION_ASSERT`` **optional string** - - Both enables generation of serialization assert logic and inserts the value of this parameter as the code - expanded for the ``NUNAVUT_ASSERT`` assert macro. For example ``SERIALIZATION_ASSERT assert`` will set - ``-DNUNAVUT_ASSERT=assert`` for the build which will then use ``assert`` as the call for serialization - asserts. - - **param** ``FILE_EXTENSION`` **optional str**: The file extension to use for generated files. If omitted then the default for the language is used. - - **param** ``EXTRA_GENERATOR_ARGS`` **optional list[str]** + - **param** ``EXTRA_GENERATOR_ARGS`` **optional list[str]**: Additional command-line arguments to pass to the Nunavut CLI when generating code. These args are not used for invoking the Nunavut CLI to discover dependencies. + These are combined with any arguments specified by a :cmake:variable:`NUNAVUT_EXTRA_GENERATOR_ARGS` + environment variable. + - **option** ``ALLOW_EXPERIMENTAL_LANGUAGES``: If set then unsupported languages will be allowed. @@ -668,18 +631,13 @@ endfunction() If set then the target name will be exactly as specified in ``NAME``. Otherwise, the target name will be prefixed with an internal default. - - **option** ``EXPORT_MANIFEST``: - - If set then a JSON file containing a list of all the inputs, outputs, and other information about the - custom command will be written to ``${CMAKE_CURRENT_BINARY_DIR}/${OUT_CODEGEN_TARGET}.json``. - - **option** ``OMIT_PUBLIC_REGULATED_NAMESPACE``: By default, ``${NUNAVUT_SUBMODULES_DIR}/public_regulated_data_types/uavcan`` is added to the list of ``DSDL_NAMESPACES`` even if this variable is not set. This option disables this behaviour so only explicitly listed ``DSDL_NAMESPACES`` values will be used. - - **option** mutually exclusive: one of ``ENDIAN_ANY``|``ENDIAN_LITTLE``|``ENDIAN_BIG``: + - **option, mutually exclusive** (one of ``ENDIAN_ANY`` | ``ENDIAN_LITTLE`` | ``ENDIAN_BIG``): If one of these is set then the endianness argument is passed into the nunavut CLI otherwise endianess is taken from language configuration. @@ -703,146 +661,162 @@ endfunction() function(add_cyphal_library) # +-[input]----------------------------------------------------------------+ set(options - EXPORT_MANIFEST EXACT_NAME ENDIAN_ANY ENDIAN_LITTLE ENDIAN_BIG ) set(singleValueArgs - SERIALIZATION_ASSERT + NAME + EXPORT_CONFIGURE_MANIFEST + EXPORT_GENERATE_MANIFEST OUT_LIBRARY_TARGET OUT_CODEGEN_TARGET ) set(multiValueArgs EXTRA_GENERATOR_ARGS) list(APPEND usageLines - " [EXPORT_MANIFEST] [EXACT_NAME] [EXTRA_GENERATOR_ARGS ]" + " NAME [EXACT_NAME] " + " [EXPORT_GENERATE_MANIFEST ] [EXTRA_GENERATOR_ARGS ]" " [ENDIAN_ANY|ENDIAN_LITTLE|ENDIAN_BIG]" - " [SERIALIZATION_ASSERT ]" " [OUT_LIBRARY_TARGET ] [OUT_CODEGEN_TARGET ]" ) - _nunavut_config_args(ON options singleValueArgs multiValueArgs usageLines) - - if(NOT ARG_NAME AND EXACT_NAME) - message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}: NAME is required if EXACT_NAME is set.") - endif() + _nunavut_config_args(options singleValueArgs multiValueArgs usageLines) - if(ARG_SERIALIZATION_ASSERT) - string(LENGTH ARG_SERIALIZATION_ASSERT LOCAL_SERIALIZATION_ASSERT_STRING_LENGTH) - if (${LOCAL_SERIALIZATION_ASSERT_STRING_LENGTH} GREATER 0) - list(APPEND LOCAL_DYNAMIC_ARGS "--enable-serialization-asserts") - endif() - else() - set(LOCAL_SERIALIZATION_ASSERT_STRING_LENGTH 0) - endif() - - if(ARG_ENDIAN_ANY) - if (ARG_ENDIAN_BIG OR ARG_ENDIAN_LITTLE) + # +-[body]-----------------------------------------------------------------+ + if(NUNV_ARG_ENDIAN_ANY) + if (NUNV_ARG_ENDIAN_BIG OR NUNV_ARG_ENDIAN_LITTLE) message(FATAL_ERROR "ENDIAN_ANY|ENDIAN_LITTLE|ENDIAN_BIG options are mutually exclusive. Provide only one or none.") endif() - list(APPEND LOCAL_DYNAMIC_ARGS "--target-endianness" "any") - elseif(ARG_ENDIAN_BIG) - if (ARG_ENDIAN_ANY OR ARG_ENDIAN_LITTLE) + list(APPEND NUNV_LOCAL_DYNAMIC_ARGS "--target-endianness" "any") + elseif(NUNV_ARG_ENDIAN_BIG) + if (NUNV_ARG_ENDIAN_ANY OR NUNV_ARG_ENDIAN_LITTLE) message(FATAL_ERROR "ENDIAN_ANY|ENDIAN_LITTLE|ENDIAN_BIG options are mutually exclusive. Provide only one or none.") endif() - list(APPEND LOCAL_DYNAMIC_ARGS "--target-endianness" "big") - elseif(ARG_ENDIAN_LITTLE) - if (ARG_ENDIAN_ANY OR ARG_ENDIAN_BIG) + list(APPEND NUNV_LOCAL_DYNAMIC_ARGS "--target-endianness" "big") + elseif(NUNV_ARG_ENDIAN_LITTLE) + if (NUNV_ARG_ENDIAN_ANY OR NUNV_ARG_ENDIAN_BIG) message(FATAL_ERROR "ENDIAN_ANY|ENDIAN_LITTLE|ENDIAN_BIG options are mutually exclusive. Provide only one or none.") endif() - list(APPEND LOCAL_DYNAMIC_ARGS "--target-endianness" "little") + list(APPEND NUNV_LOCAL_DYNAMIC_ARGS "--target-endianness" "little") endif() - if(NOT ARG_EXTRA_GENERATOR_ARGS) + if(NOT NUNV_ARG_NAME AND EXACT_NAME) + message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}: NAME is required if EXACT_NAME is set.") + endif() + + if(NOT NUNV_ARG_EXTRA_GENERATOR_ARGS) # silence use of undefined warning - set(ARG_EXTRA_GENERATOR_ARGS) + set(NUNV_ARG_EXTRA_GENERATOR_ARGS) endif() - # +-[body]-----------------------------------------------------------------+ - _nunavut_local_args() + if(NOT NUNV_ARG_NAME) + message(FATAL_ERROR "${CMAKE_CURRENT_FUNCTION}: NAME is required.") + endif() - if(ARG_EXACT_NAME) - set(LOCAL_TARGET_NAME "${ARG_NAME}") + if(NUNV_ARG_EXACT_NAME) + set(NUNV_LOCAL_TARGET_NAME "${NUNV_ARG_NAME}") else() - if(NOT ARG_NAME) - set(ARG_NAME "") + if(NOT NUNV_ARG_NAME) + set(NUNV_ARG_NAME "") else() - set(ARG_NAME "-${ARG_NAME}") + set(NUNV_ARG_NAME "-${NUNV_ARG_NAME}") endif() - if(ARG_SUPPORT_ONLY) - set(LOCAL_TARGET_NAME "cyphal-support${ARG_NAME}") - elseif(ARG_NO_SUPPORT) - set(LOCAL_TARGET_NAME "cyphal-types${ARG_NAME}") + if(NUNV_ARG_SUPPORT_ONLY) + set(NUNV_LOCAL_TARGET_NAME "cyphal-support${NUNV_ARG_NAME}") + elseif(NUNV_ARG_NO_SUPPORT) + set(NUNV_LOCAL_TARGET_NAME "cyphal-types${NUNV_ARG_NAME}") else() - set(LOCAL_TARGET_NAME "cyphal-types-and-support${ARG_NAME}") + set(NUNV_LOCAL_TARGET_NAME "cyphal-types-and-support${NUNV_ARG_NAME}") endif() endif() + _forward_single_value_arg(NUNV_LOCAL_FORWARDED_ARGS LANGUAGE) + _forward_single_value_arg(NUNV_LOCAL_FORWARDED_ARGS OUTPUT_DIR) + _forward_single_value_arg(NUNV_LOCAL_FORWARDED_ARGS LANGUAGE_STANDARD) + _forward_single_value_arg(NUNV_LOCAL_FORWARDED_ARGS PYDSDL_PATH) + _forward_single_value_arg(NUNV_LOCAL_FORWARDED_ARGS WORKING_DIRECTORY) + _forward_single_value_arg(NUNV_LOCAL_FORWARDED_ARGS FILE_EXTENSION) + _forward_single_value_arg(NUNV_LOCAL_FORWARDED_ARGS EXPORT_CONFIGURE_MANIFEST) + + _forward_single_value_arg(NUNV_LOCAL_FORWARDED_ARGS CONFIGURATION) + _forward_single_value_arg(NUNV_LOCAL_FORWARDED_ARGS DSDL_FILES) + _forward_single_value_arg(NUNV_LOCAL_FORWARDED_ARGS DSDL_NAMESPACES) + + _forward_option(NUNV_LOCAL_FORWARDED_ARGS ALLOW_EXPERIMENTAL_LANGUAGES) + _forward_option(NUNV_LOCAL_FORWARDED_ARGS CONSOLE_DEBUG) + _forward_option(NUNV_LOCAL_FORWARDED_ARGS SUPPORT_ONLY) + _forward_option(NUNV_LOCAL_FORWARDED_ARGS NO_SUPPORT) + _forward_option(NUNV_LOCAL_FORWARDED_ARGS OMIT_PUBLIC_REGULATED_NAMESPACE) + _forward_option(NUNV_LOCAL_FORWARDED_ARGS OMIT_DEPENDENCIES) + + list(APPEND NUNV_LOCAL_DYNAMIC_ARGS ${NUNV_ARG_EXTRA_GENERATOR_ARGS}) + + if (DEFINED NUNAVUT_EXTRA_GENERATOR_ARGS) + message(DEBUG "Got from cache: NUNAVUT_EXTRA_GENERATOR_ARGS=${NUNAVUT_EXTRA_GENERATOR_ARGS}") + list(APPEND NUNV_LOCAL_DYNAMIC_ARGS ${NUNAVUT_EXTRA_GENERATOR_ARGS}) + elseif (DEFINED ENV{NUNAVUT_EXTRA_GENERATOR_ARGS}) + if (NOT NUNAVUT_PATH_LIST_SEP STREQUAL ";") + string(REPLACE ${NUNAVUT_PATH_LIST_SEP} ";" LOCAL_NUNAVUT_EXTRA_GENERATOR_ARGS $ENV{NUNAVUT_EXTRA_GENERATOR_ARGS}) + else() + set(LOCAL_NUNAVUT_EXTRA_GENERATOR_ARGS $ENV{NUNAVUT_EXTRA_GENERATOR_ARGS}) + endif() + message(DEBUG "Got from environment: NUNAVUT_EXTRA_GENERATOR_ARGS=${LOCAL_NUNAVUT_EXTRA_GENERATOR_ARGS}") + list(APPEND NUNV_LOCAL_DYNAMIC_ARGS ${LOCAL_NUNAVUT_EXTRA_GENERATOR_ARGS}) + endif() + + set(NUNV_LOCAL_CODEGEN_TARGET "${NUNV_LOCAL_TARGET_NAME}-generate") + + set(NUNV_LOCAL_LIB_BYPRODUCTS_LIST) + if(NUNV_ARG_EXPORT_GENERATE_MANIFEST) + _add_option_once(NUNV_LOCAL_DYNAMIC_ARGS "--list-configuration") + _add_single_value_once(NUNV_LOCAL_DYNAMIC_ARGS "--list-format" "json-pretty") + set(LOCAL_GENERATE_MANIFEST_PATH ${NUNV_ARG_EXPORT_GENERATE_MANIFEST}/${NUNV_LOCAL_CODEGEN_TARGET}.json) + list(APPEND NUNV_LOCAL_LIB_BYPRODUCTS_LIST ${LOCAL_GENERATE_MANIFEST_PATH}) + list(APPEND NUNV_LOCAL_DYNAMIC_ARGS "--list-to-file" ${LOCAL_GENERATE_MANIFEST_PATH}) + endif() + discover_inputs_and_outputs( - LANGUAGE ${ARG_LANGUAGE} - DSDL_FILES ${ARG_DSDL_FILES} - DSDL_NAMESPACES ${ARG_DSDL_NAMESPACES} - LANGUAGE_STANDARD ${ARG_LANGUAGE_STANDARD} - OUTPUT_DIR ${ARG_OUTPUT_DIR} - CONFIGURATION ${ARG_CONFIGURATION} - WORKING_DIRECTORY ${ARG_WORKING_DIRECTORY} - PYDSDL_PATH ${ARG_PYDSDL_PATH} - FILE_EXTENSION ${ARG_FILE_EXTENSION} - ${LOCAL_ARG_ALLOW_EXPERIMENTAL_LANGUAGES} - ${LOCAL_ARG_CONSOLE_DEBUG} - ${LOCAL_ARG_SUPPORT_ONLY} - ${LOCAL_ARG_NO_SUPPORT} - OUT_MANIFEST_DATA LOCAL_MANIFEST_DATA - OUT_INPUTS_LIST LOCAL_LIB_INPUTS_LIST - OUT_OUTPUTS_LIST LOCAL_LIB_OUTPUTS_LIST + ${NUNV_LOCAL_FORWARDED_ARGS} + OUT_MANIFEST_DATA NUNV_LOCAL_MANIFEST_DATA + OUT_INPUTS_LIST NUNV_LOCAL_LIB_INPUTS_LIST + OUT_OUTPUTS_LIST NUNV_LOCAL_LIB_OUTPUTS_LIST ) # Create the custom command to generate source files. add_custom_command( - OUTPUT ${LOCAL_LIB_OUTPUTS_LIST} + OUTPUT ${NUNV_LOCAL_LIB_OUTPUTS_LIST} + BYPRODUCTS ${NUNV_LOCAL_LIB_BYPRODUCTS_LIST} COMMAND - export PYTHONPATH=${LOCAL_PYTHON_PATH} && ${Python3_EXECUTABLE} -m nunavut - --target-language ${ARG_LANGUAGE} - --outdir ${ARG_OUTPUT_DIR} - ${LOCAL_DYNAMIC_ARGS} - ${ARG_EXTRA_GENERATOR_ARGS} - ${ARG_DSDL_FILES} - WORKING_DIRECTORY ${ARG_WORKING_DIRECTORY} - DEPENDS ${LOCAL_LIB_INPUTS_LIST} + export PYTHONPATH=${NUNV_LOCAL_PYTHON_PATH} && ${Python3_EXECUTABLE} -m nunavut + ${NUNV_LOCAL_DYNAMIC_ARGS} + ${NUNV_LOCAL_LOOKUP_DIRS} + ${NUNV_ARG_DSDL_FILES} + WORKING_DIRECTORY ${NUNV_ARG_WORKING_DIRECTORY} + DEPENDS ${NUNV_LOCAL_LIB_INPUTS_LIST} ) - set(LOCAL_CODEGEN_TARGET "${LOCAL_TARGET_NAME}-generate") - add_custom_target(${LOCAL_CODEGEN_TARGET} - DEPENDS ${LOCAL_LIB_OUTPUTS_LIST} + add_custom_target(${NUNV_LOCAL_CODEGEN_TARGET} + DEPENDS ${NUNV_LOCAL_LIB_OUTPUTS_LIST} ) # finally, define the interface library for the generated headers. - add_library(${LOCAL_TARGET_NAME} INTERFACE ${LOCAL_LIB_OUTPUTS_LIST}) - - target_include_directories(${LOCAL_TARGET_NAME} INTERFACE ${ARG_OUTPUT_DIR}) + add_library(${NUNV_LOCAL_TARGET_NAME} INTERFACE ${NUNV_LOCAL_LIB_OUTPUTS_LIST}) - add_dependencies(${LOCAL_TARGET_NAME} ${LOCAL_CODEGEN_TARGET}) + target_include_directories(${NUNV_LOCAL_TARGET_NAME} INTERFACE ${NUNV_ARG_OUTPUT_DIR}) - if (${LOCAL_SERIALIZATION_ASSERT_STRING_LENGTH} GREATER 0) - target_compile_definitions(${LOCAL_TARGET_NAME} INTERFACE "-DNUNAVUT_ASSERT=${ARG_SERIALIZATION_ASSERT}") - endif() - - if(ARG_EXPORT_MANIFEST) - set(LOCAL_MANIFEST_FILE "${CMAKE_CURRENT_BINARY_DIR}/${LOCAL_CODEGEN_TARGET}.json") - file(WRITE ${LOCAL_MANIFEST_FILE} ${LOCAL_MANIFEST_DATA}) - endif() + add_dependencies(${NUNV_LOCAL_TARGET_NAME} ${NUNV_LOCAL_CODEGEN_TARGET}) - if(ARG_CONSOLE_DEBUG) - message(STATUS "${CMAKE_CURRENT_FUNCTION}: Done adding library ${LOCAL_TARGET_NAME}.") + if(NUNV_ARG_CONSOLE_DEBUG) + message(STATUS "${CMAKE_CURRENT_FUNCTION}: Done adding library ${NUNV_LOCAL_TARGET_NAME}.") endif() # +-[output]---------------------------------------------------------------+ - if(ARG_OUT_LIBRARY_TARGET) - set(${ARG_OUT_LIBRARY_TARGET} ${LOCAL_TARGET_NAME} PARENT_SCOPE) + if(NUNV_ARG_OUT_LIBRARY_TARGET) + set(${NUNV_ARG_OUT_LIBRARY_TARGET} ${NUNV_LOCAL_TARGET_NAME} PARENT_SCOPE) endif() - if(ARG_OUT_CODEGEN_TARGET) - set(${ARG_OUT_CODEGEN_TARGET} ${LOCAL_CODEGEN_TARGET} PARENT_SCOPE) + if(NUNV_ARG_OUT_CODEGEN_TARGET) + set(${NUNV_ARG_OUT_CODEGEN_TARGET} ${NUNV_LOCAL_CODEGEN_TARGET} PARENT_SCOPE) endif() endfunction() diff --git a/conf.py b/conf.py index c55e0472..9d26878f 100644 --- a/conf.py +++ b/conf.py @@ -62,7 +62,6 @@ "sphinxarg.ext", "sphinx.ext.intersphinx", "sphinxemoji.sphinxemoji", - "sphinx_rtd_theme", "sphinxcontrib.moderncmakedomain", ] @@ -100,13 +99,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # -html_theme = "sphinx_rtd_theme" - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -# -html_theme_options = {"logo_only": True} +html_theme = "furo" html_context = { "display_github": True, diff --git a/docs/cli.rst b/docs/cli.rst index 836f8967..e41d785c 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -11,4 +11,8 @@ Usage .. argparse:: :filename: src/nunavut/cli/__init__.py :func: make_argparse_parser + :nodefaultconst: :prog: nnvg + + post-processing options : @skip + skip these since they are going away. diff --git a/docs/cmake/CMakeLists.txt b/docs/cmake/CMakeLists.txt index 2d349f21..6ea11c8f 100644 --- a/docs/cmake/CMakeLists.txt +++ b/docs/cmake/CMakeLists.txt @@ -121,11 +121,16 @@ add_cyphal_library( OUT_LIBRARY_TARGET CYPHAL_GENERATED_HEADERS_ECORP # <-------- ${CYPHAL_GENERATED_HEADERS_ECORP} will resolve to the # name of the interface library defined after this # function exits (successfully). - EXPORT_MANIFEST # <-------- Optional. Writes a json file in the build output that - # dumps all configuration used by Nunavut, all template + EXPORT_CONFIGURE_MANIFEST ${CMAKE_CURRENT_BINARY_DIR} # <---- Optional. Writes a configure_commands.json file under + # this directory that dumps all configuration used by + # Nunavut for the cmake configure step. (think of this + # as the nnvg equivalent of compile_commands.json). + EXPORT_GENERATE_MANIFEST ${CMAKE_CURRENT_BINARY_DIR} # <----- Optional. Writes a ${OUT_CODEGEN_TARGET}.json file + # under this directory which includes all template # files, all dsdl files, and all output files that were - # or would be generated (think of this as the nnvg - # equivalent of compile_commands.json). + # generated when a given code generation rule was + # executed. Note that this value may use generator + # expressions which is useful for Ninja-Multi-Config. ) diff --git a/docs/cmake/cmake.rst b/docs/cmake/cmake.rst index 620eb524..0218347d 100644 --- a/docs/cmake/cmake.rst +++ b/docs/cmake/cmake.rst @@ -42,11 +42,11 @@ See the :ref:`fetch_content_cmake_lists` figure for more context. :download:`CMakePresets.json ` ************************************* -Helper Functions +NunavutConfig ************************************* Use either `CMake's FetchContent `__ (see :ref:`fetch_content`) or `find_package(nunavut) `__, -to load the Nunavut cmake functions documented here into your project. +to load the Nunavut cmake functions and variables documented here into your project. .. cmake-module:: ../../NunavutConfig.cmake diff --git a/requirements.txt b/requirements.txt index c15407f7..9962eb9b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ # This file provided for readthedocs.io only. Use tox.ini for all dependencies. . -sphinx_rtd_theme +furo sphinx-argparse sphinxemoji sphinxcontrib-moderncmakedomain diff --git a/src/nunavut/cli/__init__.py b/src/nunavut/cli/__init__.py index 3aa29e8f..7623ae80 100644 --- a/src/nunavut/cli/__init__.py +++ b/src/nunavut/cli/__init__.py @@ -522,30 +522,6 @@ def extension_type(raw_arg: str) -> str: run_mode_group.add_argument("--dry-run", "-d", action="store_true", help="If True then no files will be generated.") - run_mode_group.add_argument( - "--omit-deprecated", - action="store_true", - help=textwrap.dedent( - """ - Disables deprecated types. - - """ - ).lstrip(), - ) - - run_mode_group.add_argument( - "-M", - "--depfile", - action="store_true", - help=textwrap.dedent( - """ - Emits a makefile compatible dependency file for the generated files. Use with --dry-run to - generate a list of dependencies for a build system. - - """ - ).lstrip(), - ) - run_mode_group.add_argument( "--list-outputs", action="store_true", @@ -623,6 +599,21 @@ def extension_type(raw_arg: str) -> str: ).lstrip(), ) + run_mode_group.add_argument( + "--list-to-file", + type=Path, + help=textwrap.dedent( + """ + + If provided then the output of --list-outputs, --list-inputs, or --list-configuration + will also be written to the file specified. If the file exists it will be overwritten. + This utf-8-encoded file will be written in the format specified by --list-format even + if --dry-run is set. + + """ + ).lstrip(), + ) + # +-----------------------------------------------------------------------+ # | Post-Processing Options # +-----------------------------------------------------------------------+ @@ -632,8 +623,7 @@ def extension_type(raw_arg: str) -> str: description=textwrap.dedent( """ - Options that enable various post-generation steps because Pavel Kirienko doesn't - like writing jinja templates. + This options are all deprecated and will be removed in a future release. """ ).lstrip(), @@ -837,7 +827,24 @@ def extension_type(raw_arg: str) -> str: There is a set of built-in configuration for Nunavut that provides default values for known languages as documented `in the template guide `_. This - argument lets you specify override configuration json. + argument lets you specify an override configuration json file where any value provided will + override the built-in defaults. To see the built-in defaults you can use:: + + nnvg --list-configuration --list-format json-pretty > built-in.json + + This will generate a json file with all the built-in configuration values as the "configuration.sections" + value. If you have `jq` installed you can use this to filter the output to just the configuration:: + + nnvg --list-configuration --list-format json-pretty | jq '.configuration.sections' > my-config.json + + Then you can edit the my-config.json file to provide overrides for the built-in defaults and use + this option to provide the file to nnvg:: + + nnvg --configuration my-config.json ... + + + Also see ``--list-to-file`` which writes this configuration to disk if combined with ``--list-configuration``. + """ ).lstrip(), ) diff --git a/src/nunavut/cli/listers.py b/src/nunavut/cli/listers.py new file mode 100644 index 00000000..2fdb3a39 --- /dev/null +++ b/src/nunavut/cli/listers.py @@ -0,0 +1,184 @@ +# +# Copyright (C) OpenCyphal Development Team +# Copyright Amazon.com Inc. or its affiliates. +# SPDX-License-Identifier: MIT +# +""" + Objects that write debug/accounting information about code generation inputs, outputs, and configuration + to various outputs. +""" + +import abc +import csv +import json +import sys +from pathlib import Path +from typing import Any, Dict, Iterable, Optional, Type + +from nunavut._utilities import DefaultValue + + +class ConfigJSONEncoder(json.JSONEncoder): + """ + A JSON encoder that can handle Nunavut configuration and pydsdl objects. + """ + + def default(self, o: Any) -> Any: + if isinstance(o, Path): + return str(o) + if isinstance(o, DefaultValue): + return o.value + return super().default(o) + + +class Lister(abc.ABC): + """ + Abstract base class for listers + """ + + DefaultEncoding = "utf-8" + + @classmethod + def get_lister(cls, list_format: str, list_file: Optional[Path]) -> "Lister": + """ + Get a lister object based on the given format and file. + """ + if list_format in ("json", "json-pretty"): + return JsonLister(list_file=list_file, pretty=list_format == "json-pretty") + elif list_format in ("csv", "scsv"): + return ValueSeparatedLister(separator=";" if list_format == "scsv" else ",", list_file=list_file) + else: + raise ValueError(f"Unsupported list format: {list_format}") # pragma: no cover + + @abc.abstractmethod + def list(self, list_object: Dict[str, Any]) -> None: + """ + List the given object to the concrete lister's output(s). + """ + + +class JsonLister(Lister): + """ + Dumps the given list object as JSON to stdout and optionally to a file. + + .. invisible-code-block: python + + import pathlib + import json + from nunavut.cli.listers import JsonLister + + list_file_path = gen_paths_for_module.out_dir / pathlib.Path("lister-test.json") + list_data = { + "inputs": ["input1", "input2"], + "outputs": ["output1", "output2"] + } + + JsonLister(list_file=list_file_path).list(list_data) + + with list_file_path.open("r", encoding=Lister.DefaultEncoding) as list_file: + json_data = json.load(list_file) + assert "inputs" in json_data + assert "outputs" in json_data + found_data = set(json_data["inputs"] + json_data["outputs"]) + + assert "input1" in found_data + assert "input2" in found_data + assert "output1" in found_data + assert "output2" in found_data + + """ + + def __init__( + self, + json_encoder: Type[json.JSONEncoder] = ConfigJSONEncoder, + pretty: bool = False, + list_file: Optional[Path] = None, + ): + self._json_encoder = json_encoder + self._list_file = list_file + self._pretty = pretty + + def list(self, list_object: Dict[str, Any]) -> None: + """ + List the given object to the JSON file and optionally to a file. + """ + if self._pretty: + indent = 2 + else: + indent = None + json.dump(list_object, sys.stdout, ensure_ascii=False, indent=indent, cls=self._json_encoder) + if self._list_file is not None: + with self._list_file.open("w", encoding=self.DefaultEncoding) as list_file: + json.dump(list_object, list_file, ensure_ascii=False, indent=indent, cls=self._json_encoder) + + +class ValueSeparatedLister(Lister): + """ + Dumps the given list object to stdout and optionally to a file with a given separator. + + .. invisible-code-block: python + + import pathlib + import csv + from nunavut.cli.listers import ValueSeparatedLister + + list_file_path = gen_paths_for_module.out_dir / pathlib.Path("lister-test.csv") + list_data = { + "inputs": ["input1", "input2"], + "outputs": ["output1", "output2"] + } + + ValueSeparatedLister(separator=",", list_file=list_file_path).list(list_data) + + found_data = set() + + with list_file_path.open("r", encoding=Lister.DefaultEncoding) as list_file: + csv_reader = csv.reader(list_file, delimiter=',') + for row in csv_reader: + found_data.update(row) + + assert "input1" in found_data + assert "input2" in found_data + assert "output1" in found_data + assert "output2" in found_data + + """ + + def __init__(self, separator: str, list_file: Optional[Path] = None): + self._list_file = list_file + self._sep = separator + + def list(self, list_object: Dict[str, Any]) -> None: + self.stdout_lister(list_object) + + if self._list_file is not None: + with self._list_file.open("w", encoding=self.DefaultEncoding) as list_file: + csv_writer = csv.writer(list_file, delimiter=self._sep) + for key, value in list_object.items(): + csv_writer.writerow([key] + value) + + def stdout_lister(self, list_object: Dict[str, Any]) -> None: + """ + The output dialect to stdout is not supported by the CSV implementation in python. Use this instead. + """ + + def _write_row(row: Iterable[str], sep: str, end: str) -> None: + first = True + for cell in row: + if first: + first = False + else: + sys.stdout.write(sep) + sys.stdout.write(cell) + if not first: + sys.stdout.write(end) + + had_inputs = False + has_outputs = "outputs" in list_object and len(list_object["outputs"]) > 0 + if "inputs" in list_object and len(list_object["inputs"]) > 0: + had_inputs = True + _write_row(list_object["inputs"], sep=self._sep, end=(self._sep if has_outputs else "")) + if has_outputs: + if had_inputs: + sys.stdout.write(self._sep) + _write_row(list_object["outputs"], sep=self._sep, end="") diff --git a/src/nunavut/cli/runners.py b/src/nunavut/cli/runners.py index fcc19d81..8d9ac21b 100644 --- a/src/nunavut/cli/runners.py +++ b/src/nunavut/cli/runners.py @@ -9,29 +9,15 @@ import argparse import itertools -import json import logging import sys from pathlib import Path -from typing import Any, Dict, Iterable, Optional +from typing import Any, Dict, Optional from .._generators import basic_language_context_builder_from_args, generate_all -from .._utilities import DefaultValue from .._utilities import ResourceType from ..lang import LanguageContext - - -class ConfigJSONEncoder(json.JSONEncoder): - """ - A JSON encoder that can handle Nunavut configuration and pydsdl objects. - """ - - def default(self, o: Any) -> Any: - if isinstance(o, Path): - return str(o) - if isinstance(o, DefaultValue): - return o.value - return super().default(o) +from .listers import Lister class StandardArgparseRunner: @@ -82,28 +68,7 @@ def run(self) -> int: file_iterators.append(result.generated_files) lister_object["outputs"] = [str(p.resolve()) for p in itertools.chain(*file_iterators)] - if self._args.list_format == "json": - json.dump(lister_object, sys.stdout, ensure_ascii=False, cls=ConfigJSONEncoder) - elif self._args.list_format == "json-pretty": - json.dump(lister_object, sys.stdout, ensure_ascii=False, indent=2, cls=ConfigJSONEncoder) - else: - if self._args.list_format == "scsv": - sep = ";" - end = ";" - elif self._args.list_format == "csv": - sep = "," - end = "," - else: # pragma: no cover - raise ValueError(f"Unsupported list format: {self._args.list_format}") - had_inputs = False - has_outputs = "outputs" in lister_object and len(lister_object["outputs"]) > 0 - if "inputs" in lister_object and len(lister_object["inputs"]) > 0: - had_inputs = True - self.stdout_lister(lister_object["inputs"], sep=sep, end=(end if has_outputs else "")) - if has_outputs: - if had_inputs: - sys.stdout.write(sep) - self.stdout_lister(lister_object["outputs"], sep=sep, end="") + Lister.get_lister(self._args.list_format, self._args.list_to_file).list(lister_object) return 0 @@ -118,29 +83,6 @@ def list_configuration(self, lctx: LanguageContext) -> Dict[str, Any]: return config - def stdout_lister( - self, - things_to_list: Iterable[str], - sep: str, - end: str, - ) -> None: - """ - Write a list of things to stdout. - - :param Iterable[Any] things_to_list: The things to list. - :param str sep: The separator to use between things. - :param str end: The character to print at the end. - """ - first = True - for thing in things_to_list: - if first: - first = False - else: - sys.stdout.write(sep) - sys.stdout.write(thing) - if not first: - sys.stdout.write(end) - # --[ MAIN ]----------------------------------------------------------------------------------------------------------- def main(command_line_args: Optional[Any] = None) -> int: diff --git a/src/nunavut/lang/c/support/serialization.j2 b/src/nunavut/lang/c/support/serialization.j2 index 12f51c41..8414fad2 100644 --- a/src/nunavut/lang/c/support/serialization.j2 +++ b/src/nunavut/lang/c/support/serialization.j2 @@ -449,7 +449,11 @@ static inline int8_t nunavutGetI8(const uint8_t* const buf, const uint8_t sat = (uint8_t) nunavutChooseMin(len_bits, 8U); uint8_t val = nunavutGetU8(buf, buf_size_bytes, off_bits, sat); const bool neg = (sat > 0U) && ((val & (1ULL << (sat - 1U))) != 0U); - val = ((sat < 8U) && neg) ? (uint8_t)(val | ~((1U << sat) - 1U)) : val; // Sign extension + if ((sat < 8U) && neg) + { + val = (uint8_t)(val | ~((1U << sat) - 1U)); // Sign extension + } + return neg ? (int8_t)((-(int8_t)(uint8_t) ~val) - 1) : (int8_t) val; } @@ -461,7 +465,10 @@ static inline int16_t nunavutGetI16(const uint8_t* const buf, const uint8_t sat = (uint8_t) nunavutChooseMin(len_bits, 16U); uint16_t val = nunavutGetU16(buf, buf_size_bytes, off_bits, sat); const bool neg = (sat > 0U) && ((val & (1ULL << (sat - 1U))) != 0U); - val = ((sat < 16U) && neg) ? (uint16_t)(val | ~((1U << sat) - 1U)) : val; // Sign extension + if ((sat < 16U) && neg) + { + val = (uint16_t)(val | ~((1U << sat) - 1U)); // Sign extension + } return neg ? (int16_t)((-(int16_t)(uint16_t) ~val) - 1) : (int16_t) val; } @@ -473,7 +480,10 @@ static inline int32_t nunavutGetI32(const uint8_t* const buf, const uint8_t sat = (uint8_t) nunavutChooseMin(len_bits, 32U); uint32_t val = nunavutGetU32(buf, buf_size_bytes, off_bits, sat); const bool neg = (sat > 0U) && ((val & (1ULL << (sat - 1U))) != 0U); - val = ((sat < 32U) && neg) ? (uint32_t)(val | ~((1UL << sat) - 1U)) : val; // Sign extension + if ((sat < 32U) && neg) + { + val = (uint32_t)(val | ~((1UL << sat) - 1U)); // Sign extension + } return neg ? (int32_t)((-(int32_t) ~val) - 1) : (int32_t) val; } @@ -485,7 +495,10 @@ static inline int64_t nunavutGetI64(const uint8_t* const buf, const uint8_t sat = (uint8_t) nunavutChooseMin(len_bits, 64U); uint64_t val = nunavutGetU64(buf, buf_size_bytes, off_bits, sat); const bool neg = (sat > 0U) && ((val & (1ULL << (sat - 1U))) != 0U); - val = ((sat < 64U) && neg) ? (uint64_t)(val | ~((1ULL << sat) - 1U)) : val; // Sign extension + if ((sat < 64U) && neg) + { + val = (uint64_t)(val | ~((1ULL << sat) - 1U)); // Sign extension + } return neg ? (int64_t)((-(int64_t) ~val) - 1) : (int64_t) val; } diff --git a/submodules/CETL b/submodules/CETL index ccd53164..096f890c 160000 --- a/submodules/CETL +++ b/submodules/CETL @@ -1 +1 @@ -Subproject commit ccd5316400ace5475ce3f9b5c223f17d38f246e0 +Subproject commit 096f890cd64ad211d781b008b8e28f3efb98203d diff --git a/test/gentest_dsdl/test_dsdl.py b/test/gentest_dsdl/test_dsdl.py index 74d4cc79..5bb9c168 100644 --- a/test/gentest_dsdl/test_dsdl.py +++ b/test/gentest_dsdl/test_dsdl.py @@ -50,7 +50,6 @@ def test_realgen( resource_types=resource_types, language_options=language_options, include_experimental_languages=True, - depfile=True, ) # We only expect one root namespace directory in the public regulated data types. diff --git a/test/gentest_multiple/dsdl/family/esmeinc/DaughterType.0.1.dsdl b/test/gentest_multiple/dsdl/family/esmeinc/DaughterType.0.1.dsdl new file mode 100644 index 00000000..6945e338 --- /dev/null +++ b/test/gentest_multiple/dsdl/family/esmeinc/DaughterType.0.1.dsdl @@ -0,0 +1,4 @@ + +int32 age + +@sealed \ No newline at end of file diff --git a/test/gentest_multiple/test_multiple.py b/test/gentest_multiple/test_multiple.py index 9b540b54..8f5b6a71 100644 --- a/test/gentest_multiple/test_multiple.py +++ b/test/gentest_multiple/test_multiple.py @@ -59,6 +59,29 @@ def test_three_roots_using_nnvg(gen_paths, run_nnvg): # type: ignore '-I', str(gen_paths.dsdl_dir / Path("esmeinc")), '-O', str(gen_paths.out_dir), '-e', str('.json'), + "-Xlang", str(gen_paths.dsdl_dir / Path("scotec"))] run_nnvg(gen_paths, nnvg_cmd) + + +def test_same_type_different_paths(gen_paths, run_nnvg_main): # type: ignore + + expected_output = [gen_paths.out_dir / Path("esmeinc") / Path("DaughterType_0_1").with_suffix(".json")] + + nnvg_args = ['--templates', str(gen_paths.templates_dir), + '-I', (gen_paths.dsdl_dir / Path("esmeinc")).as_posix(), + '-I', (gen_paths.dsdl_dir / Path("family") / Path("esmeinc")).as_posix(), + '-O', (gen_paths.out_dir).as_posix(), + "--list-outputs", + "-l", "js", + "-Xlang", + "--omit-serialization-support", + '-e', str('.json'), + (Path("esmeinc") / Path("DaughterType.0.1.dsdl")).as_posix()] + + result = run_nnvg_main(gen_paths, nnvg_args) + assert 0 == result.returncode + completed = result.stdout.decode("utf-8").split(";") + completed_wo_empty = sorted([Path(i) for i in completed if len(i) > 0]) + assert expected_output == completed_wo_empty diff --git a/tox.ini b/tox.ini index 5f6131e0..90dcd545 100644 --- a/tox.ini +++ b/tox.ini @@ -22,6 +22,7 @@ deps = rope isort nox + jsonschema # +---------------------------------------------------------------------------+ # | CONFIGURATION @@ -170,7 +171,6 @@ commands = deps = -rrequirements.txt sphinx - readthedocs-sphinx-ext commands = sphinx-build -W -b html {toxinidir} {envtmpdir} @@ -222,7 +222,7 @@ deps = setuptools commands = - python pydsdl_version_check.py -vv + python version_check_pydsdl.py -vv python -m build \ -o {toxworkdir}/package/dist \ --sdist \ diff --git a/verification/.vscode/tasks.json b/verification/.vscode/tasks.json index 69b4f37b..9ebc7b5a 100644 --- a/verification/.vscode/tasks.json +++ b/verification/.vscode/tasks.json @@ -36,23 +36,6 @@ "kind": "build", }, "problemMatcher": [] - }, - { - "label": "verify c native32", - "type": "shell", - "command": "${workspaceFolder}/.github/verify.py", - "args": [ - "--verbose", - "--force", - "--language", "c", - "--endianness", "any", - "--platform", "native32", - "--build-type", "Debug" - ], - "group": "build", - "problemMatcher": [ - "$gcc" - ] } ] } diff --git a/verification/CMakeLists.txt b/verification/CMakeLists.txt index b3052c8e..83dcdfcb 100644 --- a/verification/CMakeLists.txt +++ b/verification/CMakeLists.txt @@ -3,124 +3,61 @@ # Copyright Amazon.com Inc. or its affiliates. # SPDX-License-Identifier: MIT # - cmake_minimum_required(VERSION 3.22.0) -project(nunavut_verification C CXX) - -include(${CMAKE_SOURCE_DIR}/cmake/utils.cmake) - - # +---------------------------------------------------------------------------+ -# | GLOBAL DEFINITIONS +# | PROJECT DEFINITION # +---------------------------------------------------------------------------+ -if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE Release) -endif() - -message(STATUS "Configuring with CMAKE_BUILD_TYPE=\"${CMAKE_BUILD_TYPE}\"") - -if (NOT DEFINED NUNAVUT_PROJECT_ROOT) - get_filename_component(NUNAVUT_PROJECT_ROOT - "${CMAKE_SOURCE_DIR}/../" - REALPATH BASE_DIR - "${CMAKE_BINARY_DIR}") - message(STATUS "Setting NUNAVUT_PROJECT_ROOT = ${NUNAVUT_PROJECT_ROOT}") -else() - message(STATUS "Using ${NUNAVUT_PROJECT_ROOT} for NUNAVUT_PROJECT_ROOT") -endif() - -if(DEFINED ENV{NUNAVUT_VERIFICATION_LANG}) - message(STATUS "Getting NUNAVUT_VERIFICATION_LANG from the environment ($ENV{NUNAVUT_VERIFICATION_LANG})") - set(NUNAVUT_VERIFICATION_LANG "$ENV{NUNAVUT_VERIFICATION_LANG}" CACHE STRING "The Nunavut output language to verify.") -else() - set(NUNAVUT_VERIFICATION_LANG "unspecified" CACHE STRING "The Nunavut output language to verify.") -endif() - -if (NUNAVUT_VERIFICATION_LANG STREQUAL "cpp") - set(NUNAVUT_VERIFICATION_ROOT "${CMAKE_SOURCE_DIR}/cpp") - message(STATUS "NUNAVUT_VERIFICATION_LANG is C++ (${NUNAVUT_VERIFICATION_LANG})") - message(STATUS "Setting NUNAVUT_VERIFICATION_ROOT = ${NUNAVUT_VERIFICATION_ROOT}") -elseif(NUNAVUT_VERIFICATION_LANG STREQUAL "c") - set(NUNAVUT_VERIFICATION_ROOT "${CMAKE_SOURCE_DIR}/c") - message(STATUS "NUNAVUT_VERIFICATION_LANG is C (${NUNAVUT_VERIFICATION_LANG})") - message(STATUS "Setting NUNAVUT_VERIFICATION_ROOT = ${NUNAVUT_VERIFICATION_ROOT}") -else() - message(FATAL_ERROR "Unknown or no verification language (${NUNAVUT_VERIFICATION_LANG}). Try cmake -DNUNAVUT_VERIFICATION_LANG:string=[cpp|c]") -endif() - -string(TOLOWER ${NUNAVUT_VERIFICATION_LANG} LOCAL_VERIFICATION_LANG) +project(nunavut_verification C CXX) -set(NUNAVUT_SUBMODULES_ROOT "${NUNAVUT_PROJECT_ROOT}/submodules" CACHE STRING "The path to git submodules for the project.") +include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/utils.cmake) -# Ensure NUNAVUT_VERIFICATION_LANG_STANDARD has a default -if (NUNAVUT_VERIFICATION_LANG STREQUAL "cpp" AND NUNAVUT_VERIFICATION_LANG_STANDARD) - set(NUNAVUT_VERIFICATION_LANG_CPP_STANDARD ${NUNAVUT_VERIFICATION_LANG_STANDARD}) -else() - set(NUNAVUT_VERIFICATION_LANG_CPP_STANDARD "c++20") -endif() +# +-[GLOBAL DEFINITIONS]------------------------------------------------------+ +handle_nunavut_verification_language_and_standard( + REQUIRED + PRINT_STATUS + OUT_LOCAL_VERIFICATION_TARGET_LANGUAGE LOCAL_NUNAVUT_VERIFICATION_TARGET_LANG + OUT_VERIFICATION_TARGET_PLATFORM LOCAL_VERIFICATION_TARGET_PLATFORM + OUT_VERIFICATION_LANGUAGE_STANDARD_C LOCAL_VERIFICATION_LANGUAGE_STANDARD_C + OUT_VERIFICATION_LANGUAGE_STANDARD_CPP LOCAL_VERIFICATION_LANGUAGE_STANDARD_CPP +) -if (NUNAVUT_VERIFICATION_LANG STREQUAL "c" AND NUNAVUT_VERIFICATION_LANG_STANDARD) - set(NUNAVUT_VERIFICATION_LANG_C_STANDARD ${NUNAVUT_VERIFICATION_LANG_STANDARD}) +if(NOT DEFINED LOCAL_PROJECT_ROOT) + get_filename_component(LOCAL_PROJECT_ROOT + "${CMAKE_CURRENT_SOURCE_DIR}/../" + REALPATH BASE_DIR + "${CMAKE_CURRENT_BINARY_DIR}") + message(STATUS "Setting LOCAL_PROJECT_ROOT = ${LOCAL_PROJECT_ROOT}") else() - set(NUNAVUT_VERIFICATION_LANG_C_STANDARD "c11") -endif() - -message(STATUS "NUNAVUT_VERIFICATION_LANG_C_STANDARD is ${NUNAVUT_VERIFICATION_LANG_C_STANDARD}") -message(STATUS "NUNAVUT_VERIFICATION_LANG_CPP_STANDARD is ${NUNAVUT_VERIFICATION_LANG_CPP_STANDARD}") - -if(NOT DEFINED NUNAVUT_VERIFICATION_TARGET_ENDIANNESS) - set(NUNAVUT_VERIFICATION_TARGET_ENDIANNESS "any" CACHE STRING "The endianess for the target verification architecture.") + message(STATUS "Using ${LOCAL_PROJECT_ROOT} for LOCAL_PROJECT_ROOT") endif() -if(NOT DEFINED NUNAVUT_VERIFICATION_SER_ASSERT) - set(NUNAVUT_VERIFICATION_SER_ASSERT ON CACHE BOOL "Enable or disable serialization asserts in generated code.") -endif() - -if(NOT DEFINED NUNAVUT_VERIFICATION_SER_FP_DISABLE) - set(NUNAVUT_VERIFICATION_SER_FP_DISABLE OFF CACHE BOOL "Enable or disable floating point support in generated support code.") -endif() - -if(NOT DEFINED NUNAVUT_VERIFICATION_OVR_VAR_ARRAY_ENABLE) - set(NUNAVUT_VERIFICATION_OVR_VAR_ARRAY_ENABLE OFF CACHE BOOL "Enable or disable override variable array capacity in generated support code.") -endif() - -if(DEFINED ENV{NUNAVUT_FLAGSET}) - set(NUNAVUT_FLAGSET "$ENV{NUNAVUT_FLAGSET}") - message(STATUS "Using ${NUNAVUT_FLAGSET} from environment for NUNAVUT_FLAGSET") -elseif (DEFINED NUNAVUT_FLAGSET) - message(STATUS "Using override NUNAVUT_FLAGSET = ${NUNAVUT_FLAGSET}") -else() - set(NUNAVUT_FLAGSET "${CMAKE_SOURCE_DIR}/cmake/compiler_flag_sets/native_w_cov.cmake") - message(STATUS "Setting (default) NUNAVUT_FLAGSET = ${NUNAVUT_FLAGSET}") -endif() -include("${NUNAVUT_FLAGSET}") +# +# For now we only compile for the native environment. In the future we may cross +# compile some verifications as well. +# +include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/compiler_flag_sets/native.cmake") # # Tell cmake where to find our custom scripts. # -set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules") +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules") # -# All generated source can be found under this directory. +# Tell cmake where to find the Nunavut configuration package. # -set(NUNAVUT_GENERATED_ROOT ${CMAKE_BINARY_DIR}/generated) +set(CMAKE_PREFIX_PATH ${LOCAL_PROJECT_ROOT}) # # All test binaries and reports will be created under this directory. # -set(NUNAVUT_VERIFICATIONS_BINARY_DIR ${CMAKE_BINARY_DIR}/suite) +set(NUNAVUT_VERIFICATIONS_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/$") +# +-[DEPENDENCIES]------------------------------------------------------------+ # -# Write a README to create the tests folder. +# Load the Nunavut cmake integration. # -file(WRITE ${NUNAVUT_VERIFICATIONS_BINARY_DIR}/README.txt - "All test binaries and output will appear under here.") - - -# +---------------------------------------------------------------------------+ -# | BUILD ENVIRONMENT -# +---------------------------------------------------------------------------+ +find_package(Nunavut 3.0 REQUIRED) # # We generate coverage reports. Please look at them (It wasn't easy to get this to work). @@ -128,10 +65,6 @@ file(WRITE ${NUNAVUT_VERIFICATIONS_BINARY_DIR}/README.txt find_package(lcov REQUIRED) find_package(genhtml REQUIRED) -# +---------------------------------------------------------------------------+ -# | SOURCE GENERATION -# +---------------------------------------------------------------------------+ - # # We require googletest to run the verification suite. # @@ -143,53 +76,15 @@ find_package(gtest REQUIRED) # find_package(unity REQUIRED) add_definitions(-DUNITY_SUPPORT_64=1 - -DUNITY_INCLUDE_FLOAT=1 - -DUNITY_INCLUDE_DOUBLE=1) - -# -# Make sure nnvg was installed correctly. -# -find_package(Nunavut 3.0 REQUIRED) + -DUNITY_INCLUDE_FLOAT=1 + -DUNITY_INCLUDE_DOUBLE=1) # # Pull in some stuff we need for testing C++ # find_package(o1heap REQUIRED) -# +-[ARGUMENT TRANSLATION]----------------------------------------------------+ -# -# Translate local arguments into the form needed by add_cyphal_library -# -if (NUNAVUT_VERIFICATION_SER_ASSERT) - if(NUNAVUT_VERIFICATION_LANG STREQUAL "cpp") - set(LOCAL_SERIALIZATION_ASSERT_VALUE "EXPECT_TRUE") - else() - set(LOCAL_SERIALIZATION_ASSERT_VALUE "TEST_ASSERT_TRUE") - endif() -else() - set(LOCAL_SERIALIZATION_ASSERT_VALUE) -endif() - -set(LOCAL_EXTRA_NUNAVUT_ARGS) -if (NUNAVUT_VERIFICATION_SER_FP_DISABLE) - list(APPEND LOCAL_EXTRA_NUNAVUT_ARGS "--omit-float-serialization-support") -endif() - -if (NUNAVUT_VERIFICATION_OVR_VAR_ARRAY_ENABLE) - list(APPEND LOCAL_EXTRA_NUNAVUT_ARGS "--enable-override-variable-array-capacity") -endif() - -if (${NUNAVUT_VERIFICATION_TARGET_ENDIANNESS} STREQUAL "any") - set(LOCAL_NUNAVUT_VERIFICATION_TARGET_ENDIANNESS_OPTION "ENDIAN_ANY") -elseif (${NUNAVUT_VERIFICATION_TARGET_ENDIANNESS} STREQUAL "little") - set(LOCAL_NUNAVUT_VERIFICATION_TARGET_ENDIANNESS_OPTION "ENDIAN_LITTLE") -elseif (${NUNAVUT_VERIFICATION_TARGET_ENDIANNESS} STREQUAL "big") - set(LOCAL_NUNAVUT_VERIFICATION_TARGET_ENDIANNESS_OPTION "ENDIAN_BIG") -else() - message(FATAL_ERROR "NUNAVUT_VERIFICATION_TARGET_ENDIANNESS value \"${NUNAVUT_VERIFICATION_TARGET_ENDIANNESS}\" is invalid.") -endif() - -# +-[DISCOVER DSDL FILES]-----------------------------------------------------+ +# +-[DSDL FILES]--------------------------------------------------------------+ # add_cyphal_library automatically adds the public_regulated_data_types/uavcan namespace # (unless OMIT_PUBLIC_REGULATED_NAMESPACE is set) so we can use relative paths for these. @@ -204,70 +99,74 @@ set(LOCAL_TEST_TYPES_PUBLIC_REGULATED uavcan/metatransport/can/ArbitrationID.0.1.dsdl ) -set(LOCAL_NAMESPACE_TEST0_REGULATED ${CMAKE_SOURCE_DIR}/nunavut_test_types/test0/regulated) +set(LOCAL_NAMESPACE_TEST0_REGULATED ${CMAKE_CURRENT_SOURCE_DIR}/nunavut_test_types/test0/regulated) file(GLOB_RECURSE LOCAL_TEST_TYPES_TEST0_REGULATED CONFIGURE_DEPENDS ${LOCAL_NAMESPACE_TEST0_REGULATED}/*.dsdl) -set(LOCAL_NAMESPACE_NESTED_ARRAY_TYPES ${CMAKE_SOURCE_DIR}/nunavut_test_types/nested_array_types/mymsgs) +set(LOCAL_NAMESPACE_NESTED_ARRAY_TYPES ${CMAKE_CURRENT_SOURCE_DIR}/nunavut_test_types/nested_array_types/mymsgs) file(GLOB_RECURSE LOCAL_NESTED_ARRAY_TYPES CONFIGURE_DEPENDS ${LOCAL_NAMESPACE_NESTED_ARRAY_TYPES}/*.dsdl) # +-[C-TYPES]-----------------------------------------------------------------+ # # Generate additional types for verification # + add_cyphal_library( NAME dsdl-test-c LANGUAGE c - LANGUAGE_STANDARD ${NUNAVUT_VERIFICATION_LANG_C_STANDARD} + LANGUAGE_STANDARD ${LOCAL_VERIFICATION_LANGUAGE_STANDARD_C} + OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/$/generated DSDL_FILES - ${LOCAL_TEST_TYPES_TEST0_REGULATED} - ${LOCAL_NESTED_ARRAY_TYPES} - ${LOCAL_TEST_TYPES_PUBLIC_REGULATED} + ${LOCAL_TEST_TYPES_TEST0_REGULATED} + ${LOCAL_NESTED_ARRAY_TYPES} + ${LOCAL_TEST_TYPES_PUBLIC_REGULATED} DSDL_NAMESPACES - ${LOCAL_NAMESPACE_TEST0_REGULATED} - ${LOCAL_NAMESPACE_NESTED_ARRAY_TYPES} - SERIALIZATION_ASSERT ${LOCAL_SERIALIZATION_ASSERT_VALUE} - EXTRA_GENERATOR_ARGS ${LOCAL_EXTRA_NUNAVUT_ARGS} - ${LOCAL_NUNAVUT_VERIFICATION_TARGET_ENDIANNESS_OPTION} - EXPORT_MANIFEST - ALLOW_EXPERIMENTAL_LANGUAGES + ${LOCAL_NAMESPACE_TEST0_REGULATED} + ${LOCAL_NAMESPACE_NESTED_ARRAY_TYPES} + EXPORT_CONFIGURE_MANIFEST ${CMAKE_CURRENT_BINARY_DIR} + EXPORT_GENERATE_MANIFEST ${NUNAVUT_VERIFICATIONS_BINARY_DIR} + EXTRA_GENERATOR_ARGS "$<$:--enable-serialization-asserts>" OUT_LIBRARY_TARGET LOCAL_TEST_TYPES_C_LIBRARY ) # +-[CPP-TYPES]---------------------------------------------------------------+ -if(NUNAVUT_VERIFICATION_LANG STREQUAL "cpp") +if(LOCAL_NUNAVUT_VERIFICATION_TARGET_LANG STREQUAL "cpp") add_cyphal_library( NAME dsdl-test-cpp - LANGUAGE ${NUNAVUT_VERIFICATION_LANG} - LANGUAGE_STANDARD ${NUNAVUT_VERIFICATION_LANG_CPP_STANDARD} + LANGUAGE ${LOCAL_NUNAVUT_VERIFICATION_TARGET_LANG} + LANGUAGE_STANDARD ${LOCAL_VERIFICATION_LANGUAGE_STANDARD_CPP} + OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/$/generated DSDL_FILES - ${LOCAL_TEST_TYPES_TEST0_REGULATED} - ${LOCAL_NESTED_ARRAY_TYPES} - ${LOCAL_TEST_TYPES_PUBLIC_REGULATED} + ${LOCAL_TEST_TYPES_TEST0_REGULATED} + ${LOCAL_NESTED_ARRAY_TYPES} + ${LOCAL_TEST_TYPES_PUBLIC_REGULATED} DSDL_NAMESPACES - ${LOCAL_NAMESPACE_TEST0_REGULATED} - ${LOCAL_NAMESPACE_NESTED_ARRAY_TYPES} - SERIALIZATION_ASSERT ${LOCAL_SERIALIZATION_ASSERT_VALUE} - EXTRA_GENERATOR_ARGS ${LOCAL_EXTRA_NUNAVUT_ARGS} - ${LOCAL_NUNAVUT_VERIFICATION_TARGET_ENDIANNESS_OPTION} - EXPORT_MANIFEST + ${LOCAL_NAMESPACE_TEST0_REGULATED} + ${LOCAL_NAMESPACE_NESTED_ARRAY_TYPES} + EXTRA_GENERATOR_ARGS "$<$:--enable-serialization-asserts>" + EXPORT_CONFIGURE_MANIFEST ${CMAKE_CURRENT_BINARY_DIR} + EXPORT_GENERATE_MANIFEST ${NUNAVUT_VERIFICATIONS_BINARY_DIR} ALLOW_EXPERIMENTAL_LANGUAGES OUT_LIBRARY_TARGET LOCAL_TEST_TYPES_CPP_LIBRARY ) + + target_compile_definitions(${LOCAL_TEST_TYPES_C_LIBRARY} INTERFACE "$<$:-DNUNAVUT_ASSERT=EXPECT_TRUE>") + target_compile_definitions(${LOCAL_TEST_TYPES_CPP_LIBRARY} INTERFACE "$<$:-DNUNAVUT_ASSERT=EXPECT_TRUE>") +else() + target_compile_definitions(${LOCAL_TEST_TYPES_C_LIBRARY} INTERFACE "$<$:-DNUNAVUT_ASSERT=TEST_ASSERT_TRUE>") endif() # +---------------------------------------------------------------------------+ # | VERIFICATION SUITE # +---------------------------------------------------------------------------+ -# We generate individual test binaires so we can record which test generated -# what coverage. We also allow test authors to generate coverage reports for -# just one test allowing for faster iteration. - +# We generate individual test binaires so we can record which test generated +# what coverage. We also allow test authors to generate coverage reports for +# just one test allowing for faster iteration. add_custom_target( lcov_zero ${LCOV} - ${NUNAVUT_GOV_TOOL_ARG} - --zerocounters - --directory ${CMAKE_CURRENT_BINARY_DIR} + ${NUNAVUT_GOV_TOOL_ARG} + --zerocounters + --directory ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Resetting coverage counters." ) @@ -276,55 +175,56 @@ set(ALL_TESTS_WITH_LCOV "") set(ALL_TEST_COVERAGE "") function(runTestCpp) - set(options "") - set(oneValueArgs TEST_FILE) - set(multiValueArgs LINK LANGUAGE_FLAVORS) - cmake_parse_arguments(runTestCpp "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - # Skip tests not relevant to the specified language standard - list(FIND runTestCpp_LANGUAGE_FLAVORS "${NUNAVUT_VERIFICATION_LANG_CPP_STANDARD}" FIND_INDEX) - if (${FIND_INDEX} EQUAL -1) - message(STATUS "Skipping ${runTestCpp_TEST_FILE}") - return() - endif() - - set(NATIVE_TEST "${NUNAVUT_VERIFICATION_LANG}/suite/${runTestCpp_TEST_FILE}") - get_filename_component(NATIVE_TEST_NAME ${NATIVE_TEST} NAME_WE) - - define_native_unit_test(FRAMEWORK "gtest" - TEST_NAME ${NATIVE_TEST_NAME} - TEST_SOURCE ${NATIVE_TEST} - OUTDIR ${NUNAVUT_VERIFICATIONS_BINARY_DIR} - DSDL_TARGETS - ${runTestCpp_LINK} - ) - if(NUNAVUT_VERIFICATION_LANG STREQUAL "c") - # - # If we are testing C headers with C++ tests we have to disable - # certain checks to allow the inline code to compile without - # warnings. - # - target_compile_options(${NATIVE_TEST_NAME} PRIVATE "-Wno-old-style-cast") - endif() - - # Some tests use deprecated DSDLs - target_compile_options(${NATIVE_TEST_NAME} PRIVATE "-Wno-deprecated-declarations") - target_link_libraries(${NATIVE_TEST_NAME} PUBLIC o1heap) - target_include_directories(${NATIVE_TEST_NAME} PUBLIC "${NUNAVUT_PROJECT_ROOT}/submodules/CETL/include") - define_native_test_run(TEST_NAME ${NATIVE_TEST_NAME} OUTDIR ${NUNAVUT_VERIFICATIONS_BINARY_DIR}) - define_native_test_run_with_lcov(${NATIVE_TEST_NAME} ${NUNAVUT_VERIFICATIONS_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/\\*) - define_native_test_coverage(${NATIVE_TEST_NAME} ${NUNAVUT_VERIFICATIONS_BINARY_DIR}) - list(APPEND ALL_TESTS "run_${NATIVE_TEST_NAME}") - list(APPEND ALL_TESTS_WITH_LCOV "run_${NATIVE_TEST_NAME}_with_lcov") - list(APPEND ALL_TEST_COVERAGE "--add-tracefile") - list(APPEND ALL_TEST_COVERAGE "${NUNAVUT_VERIFICATIONS_BINARY_DIR}/coverage.${NATIVE_TEST_NAME}.filtered.info") - set(ALL_TESTS ${ALL_TESTS} PARENT_SCOPE) - set(ALL_TESTS_WITH_LCOV ${ALL_TESTS_WITH_LCOV} PARENT_SCOPE) - set(ALL_TEST_COVERAGE ${ALL_TEST_COVERAGE} PARENT_SCOPE) -endfunction() + set(options "") + set(oneValueArgs TEST_FILE) + set(multiValueArgs LINK LANGUAGE_FLAVORS) + cmake_parse_arguments(runTestCpp "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + # Skip tests not relevant to the specified language standard + list(FIND runTestCpp_LANGUAGE_FLAVORS "${LOCAL_VERIFICATION_LANGUAGE_STANDARD_CPP}" FIND_INDEX) + + if(${FIND_INDEX} EQUAL -1) + message(STATUS "Skipping ${runTestCpp_TEST_FILE}") + return() + endif() + + set(NATIVE_TEST "${CMAKE_CURRENT_SOURCE_DIR}/cpp/suite/${runTestCpp_TEST_FILE}") + get_filename_component(NATIVE_TEST_NAME ${NATIVE_TEST} NAME_WE) + + define_native_unit_test(FRAMEWORK "gtest" + TEST_NAME ${NATIVE_TEST_NAME} + TEST_SOURCE ${NATIVE_TEST} + OUTDIR ${NUNAVUT_VERIFICATIONS_BINARY_DIR} + DSDL_TARGETS + ${runTestCpp_LINK} + ) -if (NUNAVUT_VERIFICATION_LANG STREQUAL "cpp") + if(LOCAL_NUNAVUT_VERIFICATION_TARGET_LANG STREQUAL "c") + # + # If we are testing C headers with C++ tests we have to disable + # certain checks to allow the inline code to compile without + # warnings. + # + target_compile_options(${NATIVE_TEST_NAME} PRIVATE "-Wno-old-style-cast") + endif() + # Some tests use deprecated DSDLs + target_compile_options(${NATIVE_TEST_NAME} PRIVATE "-Wno-deprecated-declarations") + target_link_libraries(${NATIVE_TEST_NAME} PUBLIC o1heap) + target_include_directories(${NATIVE_TEST_NAME} PUBLIC "${NUNAVUT_SUBMODULES_DIR}/CETL/include") + define_native_test_run(TEST_NAME ${NATIVE_TEST_NAME} OUTDIR ${NUNAVUT_VERIFICATIONS_BINARY_DIR}) + define_native_test_run_with_lcov(${NATIVE_TEST_NAME} ${NUNAVUT_VERIFICATIONS_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/\\*) + define_native_test_coverage(${NATIVE_TEST_NAME} ${NUNAVUT_VERIFICATIONS_BINARY_DIR}) + list(APPEND ALL_TESTS "run_${NATIVE_TEST_NAME}") + list(APPEND ALL_TESTS_WITH_LCOV "run_${NATIVE_TEST_NAME}_with_lcov") + list(APPEND ALL_TEST_COVERAGE "--add-tracefile") + list(APPEND ALL_TEST_COVERAGE "${NUNAVUT_VERIFICATIONS_BINARY_DIR}/coverage.${NATIVE_TEST_NAME}.filtered.info") + set(ALL_TESTS ${ALL_TESTS} PARENT_SCOPE) + set(ALL_TESTS_WITH_LCOV ${ALL_TESTS_WITH_LCOV} PARENT_SCOPE) + set(ALL_TEST_COVERAGE ${ALL_TEST_COVERAGE} PARENT_SCOPE) +endfunction() + +if(LOCAL_NUNAVUT_VERIFICATION_TARGET_LANG STREQUAL "cpp") runTestCpp(TEST_FILE test_serialization.cpp LINK ${LOCAL_TEST_TYPES_C_LIBRARY} ${LOCAL_TEST_TYPES_CPP_LIBRARY} LANGUAGE_FLAVORS c++14 c++17 c++17-pmr c++20 c++20-pmr) runTestCpp(TEST_FILE test_array_c++17-pmr.cpp LINK ${LOCAL_TEST_TYPES_C_LIBRARY} ${LOCAL_TEST_TYPES_CPP_LIBRARY} LANGUAGE_FLAVORS c++17-pmr c++20-pmr) runTestCpp(TEST_FILE test_array_cetl++14-17.cpp LINK ${LOCAL_TEST_TYPES_C_LIBRARY} ${LOCAL_TEST_TYPES_CPP_LIBRARY} LANGUAGE_FLAVORS cetl++14-17 ) @@ -336,41 +236,42 @@ if (NUNAVUT_VERIFICATION_LANG STREQUAL "cpp") endif() function(runTestC) - set(options "") - set(oneValueArgs TEST_FILE FRAMEWORK) - set(multiValueArgs LINK LANGUAGE_FLAVORS) - cmake_parse_arguments(runTestC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) - - # Skip tests not relevant to the specified language standard - list(FIND runTestC_LANGUAGE_FLAVORS "${NUNAVUT_VERIFICATION_LANG_C_STANDARD}" FIND_INDEX) - if (${FIND_INDEX} EQUAL -1) - message(STATUS "Skipping ${runTestC_TEST_FILE}") - return() - endif() - - set(NATIVE_TEST "${NUNAVUT_VERIFICATION_ROOT}/suite/${runTestC_TEST_FILE}") - get_filename_component(NATIVE_TEST_NAME ${NATIVE_TEST} NAME_WE) - - define_native_unit_test(FRAMEWORK ${runTestC_FRAMEWORK} - TEST_NAME ${NATIVE_TEST_NAME} - TEST_SOURCE ${NATIVE_TEST} - OUTDIR ${NUNAVUT_VERIFICATIONS_BINARY_DIR} - DSDL_TARGETS - ${runTestC_LINK} - ) - define_native_test_run(TEST_NAME ${NATIVE_TEST_NAME} OUTDIR ${NUNAVUT_VERIFICATIONS_BINARY_DIR}) - define_native_test_run_with_lcov(${NATIVE_TEST_NAME} ${NUNAVUT_VERIFICATIONS_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/\\*) - define_native_test_coverage(${NATIVE_TEST_NAME} ${NUNAVUT_VERIFICATIONS_BINARY_DIR}) - list(APPEND ALL_TESTS "run_${NATIVE_TEST_NAME}") - list(APPEND ALL_TESTS_WITH_LCOV "run_${NATIVE_TEST_NAME}_with_lcov") - list(APPEND ALL_TEST_COVERAGE "--add-tracefile") - list(APPEND ALL_TEST_COVERAGE "${NUNAVUT_VERIFICATIONS_BINARY_DIR}/coverage.${NATIVE_TEST_NAME}.filtered.info") - set(ALL_TESTS ${ALL_TESTS} PARENT_SCOPE) - set(ALL_TESTS_WITH_LCOV ${ALL_TESTS_WITH_LCOV} PARENT_SCOPE) - set(ALL_TEST_COVERAGE ${ALL_TEST_COVERAGE} PARENT_SCOPE) + set(options "") + set(oneValueArgs TEST_FILE FRAMEWORK) + set(multiValueArgs LINK LANGUAGE_FLAVORS) + cmake_parse_arguments(runTestC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + # Skip tests not relevant to the specified language standard + list(FIND runTestC_LANGUAGE_FLAVORS "${LOCAL_VERIFICATION_LANGUAGE_STANDARD_C}" FIND_INDEX) + + if(${FIND_INDEX} EQUAL -1) + message(STATUS "Skipping ${runTestC_TEST_FILE}") + return() + endif() + + set(NATIVE_TEST "${CMAKE_CURRENT_SOURCE_DIR}/c/suite/${runTestC_TEST_FILE}") + get_filename_component(NATIVE_TEST_NAME ${NATIVE_TEST} NAME_WE) + + define_native_unit_test(FRAMEWORK ${runTestC_FRAMEWORK} + TEST_NAME ${NATIVE_TEST_NAME} + TEST_SOURCE ${NATIVE_TEST} + OUTDIR ${NUNAVUT_VERIFICATIONS_BINARY_DIR} + DSDL_TARGETS + ${runTestC_LINK} + ) + define_native_test_run(TEST_NAME ${NATIVE_TEST_NAME} OUTDIR ${NUNAVUT_VERIFICATIONS_BINARY_DIR}) + define_native_test_run_with_lcov(${NATIVE_TEST_NAME} ${NUNAVUT_VERIFICATIONS_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/\\*) + define_native_test_coverage(${NATIVE_TEST_NAME} ${NUNAVUT_VERIFICATIONS_BINARY_DIR}) + list(APPEND ALL_TESTS "run_${NATIVE_TEST_NAME}") + list(APPEND ALL_TESTS_WITH_LCOV "run_${NATIVE_TEST_NAME}_with_lcov") + list(APPEND ALL_TEST_COVERAGE "--add-tracefile") + list(APPEND ALL_TEST_COVERAGE "${NUNAVUT_VERIFICATIONS_BINARY_DIR}/coverage.${NATIVE_TEST_NAME}.filtered.info") + set(ALL_TESTS ${ALL_TESTS} PARENT_SCOPE) + set(ALL_TESTS_WITH_LCOV ${ALL_TESTS_WITH_LCOV} PARENT_SCOPE) + set(ALL_TEST_COVERAGE ${ALL_TEST_COVERAGE} PARENT_SCOPE) endfunction() -if (NUNAVUT_VERIFICATION_LANG STREQUAL "c") +if(LOCAL_NUNAVUT_VERIFICATION_TARGET_LANG STREQUAL "c") runTestCpp(TEST_FILE test_canard.cpp LINK ${LOCAL_TEST_TYPES_C_LIBRARY} LANGUAGE_FLAVORS c11) runTestCpp(TEST_FILE test_support_assert.cpp LINK ${LOCAL_TEST_TYPES_C_LIBRARY} LANGUAGE_FLAVORS c11) runTestC( TEST_FILE test_constant.c LINK ${LOCAL_TEST_TYPES_C_LIBRARY} LANGUAGE_FLAVORS c11 FRAMEWORK "unity") @@ -381,29 +282,28 @@ if (NUNAVUT_VERIFICATION_LANG STREQUAL "c") endif() # +---------------------------------------------------------------------------+ -# Finally, we setup an overall report. the coverage.info should be uploaded -# to a coverage reporting service as part of the CI pipeline. - +# Finally, we setup an overall report. the coverage.info should be uploaded +# to a coverage reporting service as part of the CI pipeline. add_custom_command( OUTPUT ${NUNAVUT_VERIFICATIONS_BINARY_DIR}/coverage.all.info COMMAND - ${LCOV} - ${NUNAVUT_GOV_TOOL_ARG} - --rc lcov_branch_coverage=1 - ${ALL_TEST_COVERAGE} - --output-file ${NUNAVUT_VERIFICATIONS_BINARY_DIR}/coverage.all.info + ${LCOV} + ${NUNAVUT_GOV_TOOL_ARG} + --rc lcov_branch_coverage=1 + ${ALL_TEST_COVERAGE} + --output-file ${NUNAVUT_VERIFICATIONS_BINARY_DIR}/coverage.all.info DEPENDS ${ALL_TESTS_WITH_LCOV} ) add_custom_command( OUTPUT ${NUNAVUT_VERIFICATIONS_BINARY_DIR}/coverage.info COMMAND - ${LCOV} - ${NUNAVUT_GOV_TOOL_ARG} - --rc lcov_branch_coverage=1 - --extract ${NUNAVUT_VERIFICATIONS_BINARY_DIR}/coverage.all.info - ${NUNAVUT_PROJECT_ROOT}/\\* - --output-file ${NUNAVUT_VERIFICATIONS_BINARY_DIR}/coverage.info + ${LCOV} + ${NUNAVUT_GOV_TOOL_ARG} + --rc lcov_branch_coverage=1 + --extract ${NUNAVUT_VERIFICATIONS_BINARY_DIR}/coverage.all.info + ${LOCAL_PROJECT_ROOT}/\\* + --output-file ${NUNAVUT_VERIFICATIONS_BINARY_DIR}/coverage.info DEPENDS ${NUNAVUT_VERIFICATIONS_BINARY_DIR}/coverage.all.info ) @@ -415,16 +315,16 @@ add_custom_target( add_custom_target( cov_all ${GENHTML} --title "${PROJECT_NAME} native test coverage" - --output-directory ${NUNAVUT_VERIFICATIONS_BINARY_DIR}/coverage/all - --demangle-cpp - --sort - --num-spaces 4 - --function-coverage - --branch-coverage - --legend - --highlight - --show-details - ${NUNAVUT_VERIFICATIONS_BINARY_DIR}/coverage.info + --output-directory ${NUNAVUT_VERIFICATIONS_BINARY_DIR}/coverage/all + --demangle-cpp + --sort + --num-spaces 4 + --function-coverage + --branch-coverage + --legend + --highlight + --show-details + ${NUNAVUT_VERIFICATIONS_BINARY_DIR}/coverage.info DEPENDS ${NUNAVUT_VERIFICATIONS_BINARY_DIR}/coverage.info COMMENT "Build and run all tests and generate an overall html coverage report." ) @@ -432,22 +332,22 @@ add_custom_target( add_custom_target( test_all DEPENDS - ${ALL_TESTS} + ${ALL_TESTS} ) add_custom_target( cov_all_archive COMMAND ${CMAKE_COMMAND} - -E tar - "cfv" - "coverage_all.zip" - --format=zip - "${NUNAVUT_VERIFICATIONS_BINARY_DIR}/coverage/all" + -E tar + "cfv" + "coverage_all.zip" + --format=zip + "${NUNAVUT_VERIFICATIONS_BINARY_DIR}/coverage/all" DEPENDS - cov_all + cov_all BYPRODUCTS - "${NUNAVUT_VERIFICATIONS_BINARY_DIR}/coverage_all.zip" + "${NUNAVUT_VERIFICATIONS_BINARY_DIR}/coverage_all.zip" COMMENT - "Build and run all tests and generate an overall html coverage report as a zip archive." + "Build and run all tests and generate an overall html coverage report as a zip archive." ) diff --git a/verification/CMakePresets.json b/verification/CMakePresets.json index 27afd573..b8192499 100644 --- a/verification/CMakePresets.json +++ b/verification/CMakePresets.json @@ -9,7 +9,7 @@ { "name": "config-common", "hidden": true, - "generator": "Ninja", + "generator": "Ninja Multi-Config", "binaryDir": "${sourceDir}/build", "warnings": { "deprecated": true, @@ -18,282 +18,927 @@ "cacheVariables": { "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", "CMAKE_PREFIX_PATH": "${sourceDir}/..", - "NUNAVUT_VERIFICATION_EXTRA_COMPILE_CFLAGS":"", - "CMAKE_BUILD_TYPE":"Debug" + "CMAKE_CONFIGURATION_TYPES": "Release;Debug;DebugAsan;DebugCov", + "CMAKE_CROSS_CONFIGS": "all", + "CMAKE_DEFAULT_BUILD_TYPE": "Debug", + "CMAKE_DEFAULT_CONFIGS": "Debug" + }, + "environment": { + "NUNAVUT_EXTRA_GENERATOR_ARGS": "--embed-auditing-info" } }, - { - "name": "config-toolchain-gcc-native", - "hidden": true, - "toolchainFile": "${sourceDir}/cmake/toolchains/gcc-native.cmake" - }, { "name": "config-toolchain-clang-native", "hidden": true, "toolchainFile": "${sourceDir}/cmake/toolchains/clang-native.cmake" }, { - "name": "config-flags-asan", + "name": "config-toolchain-gcc-native", "hidden": true, - "cacheVariables": { - "NUNAVUT_FLAGSET": "${sourceDir}/cmake/compiler_flag_sets/native_w_asan.cmake" - } + "toolchainFile": "${sourceDir}/cmake/toolchains/gcc-native.cmake" }, { - "name": "config-flags-cov", + "name": "config-toolchain-gcc-native32", "hidden": true, + "toolchainFile": "${sourceDir}/cmake/toolchains/gcc-native.cmake", "cacheVariables": { - "NUNAVUT_FLAGSET": "${sourceDir}/cmake/compiler_flag_sets/native_w_cov.cmake" + "NUNAVUT_VERIFICATION_TARGET_PLATFORM": "native32" } }, { - "name": "config-flags-common", + "name": "config-language-c-11", "hidden": true, "cacheVariables": { - "NUNAVUT_FLAGSET": "${sourceDir}/cmake/compiler_flag_sets/native.cmake" + "NUNAVUT_VERIFICATION_LANG": "c", + "NUNAVUT_VERIFICATION_LANG_STANDARD": "c11" } }, { - "name": "config-platform-native32", + "name": "config-language-cpp-14", "hidden": true, "cacheVariables": { - "NUNAVUT_VERIFICATION_TARGET_PLATFORM": "native32" + "NUNAVUT_VERIFICATION_LANG": "cpp", + "NUNAVUT_VERIFICATION_LANG_STANDARD": "c++14" } }, { - "name": "config-platform-native64", + "name": "config-language-cetl-14-17", "hidden": true, "cacheVariables": { - "NUNAVUT_VERIFICATION_TARGET_PLATFORM": "native64" + "NUNAVUT_VERIFICATION_LANG": "cpp", + "NUNAVUT_VERIFICATION_LANG_STANDARD": "cetl++14-17" } }, { - "name": "config-endianness-little", + "name": "config-language-cpp-17", "hidden": true, "cacheVariables": { - "NUNAVUT_VERIFICATION_TARGET_ENDIANNESS": "little" + "NUNAVUT_VERIFICATION_LANG": "cpp", + "NUNAVUT_VERIFICATION_LANG_STANDARD": "c++17" } }, { - "name": "config-endianness-big", + "name": "config-language-cpp-17-pmr", "hidden": true, "cacheVariables": { - "NUNAVUT_VERIFICATION_TARGET_ENDIANNESS": "big" + "NUNAVUT_VERIFICATION_LANG": "cpp", + "NUNAVUT_VERIFICATION_LANG_STANDARD": "c++17-pmr" } }, { - "name": "config-lang-c", + "name": "config-language-cpp-20", "hidden": true, "cacheVariables": { - "NUNAVUT_VERIFICATION_LANG": "c", - "NUNAVUT_VERIFICATION_LANG_STANDARD": "" + "NUNAVUT_VERIFICATION_LANG": "cpp", + "NUNAVUT_VERIFICATION_LANG_STANDARD": "c++20" } }, { - "name": "config-lang-c11", + "name": "config-language-cpp-20-pmr", "hidden": true, "cacheVariables": { - "NUNAVUT_VERIFICATION_LANG": "c", - "NUNAVUT_VERIFICATION_LANG_STANDARD": "c11", - "CMAKE_C_STANDARD": "11" + "NUNAVUT_VERIFICATION_LANG": "cpp", + "NUNAVUT_VERIFICATION_LANG_STANDARD": "c++20-pmr" } }, { - "name": "config-lang-cpp-14", - "hidden": true, - "cacheVariables": { - "NUNAVUT_VERIFICATION_LANG": "cpp", - "NUNAVUT_VERIFICATION_LANG_STANDARD": "c++14", - "CMAKE_CXX_STANDARD": "14" - } + "name": "config-clang-native-c-11", + "inherits": [ + "config-common", + "config-toolchain-clang-native", + "config-language-c-11" + ] }, { - "name": "config-lang-cetl-14-17", - "hidden": true, - "cacheVariables": { - "NUNAVUT_VERIFICATION_LANG": "cpp", - "NUNAVUT_VERIFICATION_LANG_STANDARD": "cetl++14-17", - "CMAKE_CXX_STANDARD": "14" - } + "name": "config-clang-native-cpp-14", + "inherits": [ + "config-common", + "config-toolchain-clang-native", + "config-language-cpp-14" + ] }, { - "name": "config-lang-cpp-17", - "hidden": true, - "cacheVariables": { - "NUNAVUT_VERIFICATION_LANG": "cpp", - "NUNAVUT_VERIFICATION_LANG_STANDARD": "c++17", - "CMAKE_CXX_STANDARD": "17" - } + "name": "config-clang-native-cetl-14-17", + "inherits": [ + "config-common", + "config-toolchain-clang-native", + "config-language-cetl-14-17" + ] }, { - "name": "config-lang-cpp-17-pmr", - "hidden": true, - "cacheVariables": { - "NUNAVUT_VERIFICATION_LANG": "cpp", - "NUNAVUT_VERIFICATION_LANG_STANDARD": "c++17-pmr", - "CMAKE_CXX_STANDARD": "17" - } + "name": "config-clang-native-cpp-17", + "inherits": [ + "config-common", + "config-toolchain-clang-native", + "config-language-cpp-17" + ] }, { - "name": "config-lang-cpp-20", - "hidden": true, - "cacheVariables": { - "NUNAVUT_VERIFICATION_LANG": "cpp", - "NUNAVUT_VERIFICATION_LANG_STANDARD": "c++20", - "CMAKE_CXX_STANDARD": "20" - } + "name": "config-clang-native-cpp-17-pmr", + "inherits": [ + "config-common", + "config-toolchain-clang-native", + "config-language-cpp-17-pmr" + ] }, { - "name": "config-lang-cpp-20-pmr", - "hidden": true, - "cacheVariables": { - "NUNAVUT_VERIFICATION_LANG": "cpp", - "NUNAVUT_VERIFICATION_LANG_STANDARD": "c++20-pmr", - "CMAKE_CXX_STANDARD": "20" - } + "name": "config-clang-native-cpp-20", + "inherits": [ + "config-common", + "config-toolchain-clang-native", + "config-language-cpp-20" + ] }, { - "name": "config-clang-cetl-14-17", - "displayName": "Clang Native : CETL 14-17", + "name": "config-clang-native-cpp-20-pmr", "inherits": [ "config-common", "config-toolchain-clang-native", - "config-flags-common", - "config-lang-cetl-14-17" + "config-language-cpp-20-pmr" ] }, { - "name": "config-gcc-cetl-14-17", - "displayName": "GCC Native : CETL 14-17", + "name": "config-gcc-native-c-11", "inherits": [ "config-common", "config-toolchain-gcc-native", - "config-flags-common", - "config-lang-cetl-14-17" + "config-language-c-11" ] }, { - "name": "config-clang-cpp-14", - "displayName": "Clang Native : C++14", + "name": "config-gcc-native-cpp-14", "inherits": [ "config-common", - "config-toolchain-clang-native", - "config-flags-common", - "config-lang-cpp-14" + "config-toolchain-gcc-native", + "config-language-cpp-14" ] }, { - "name": "config-gcc-cpp-14", - "displayName": "GCC Native : C++14", + "name": "config-gcc-native-cetl-14-17", "inherits": [ "config-common", "config-toolchain-gcc-native", - "config-flags-common", - "config-lang-cpp-14" + "config-language-cetl-14-17" ] }, { - "name": "config-clang-cpp-17", - "displayName": "Clang Native : C++17", + "name": "config-gcc-native-cpp-17", "inherits": [ "config-common", - "config-toolchain-clang-native", - "config-flags-common", - "config-lang-cpp-17" + "config-toolchain-gcc-native", + "config-language-cpp-17" ] }, { - "name": "config-gcc-cpp-17", - "displayName": "GCC Native : C++17", + "name": "config-gcc-native-cpp-17-pmr", "inherits": [ "config-common", "config-toolchain-gcc-native", - "config-flags-common", - "config-lang-cpp-17" + "config-language-cpp-17-pmr" ] }, { - "name": "config-clang-c", - "displayName": "Clang Native : C", + "name": "config-gcc-native-cpp-20", "inherits": [ "config-common", - "config-toolchain-clang-native", - "config-flags-common", - "config-lang-c" + "config-toolchain-gcc-native", + "config-language-cpp-20" ] }, { - "name": "config-gcc-c", - "displayName": "GCC Native : C", + "name": "config-gcc-native-cpp-20-pmr", "inherits": [ "config-common", "config-toolchain-gcc-native", - "config-flags-common", - "config-lang-c" + "config-language-cpp-20-pmr" + ] + }, + { + "name": "config-gcc-native32-c-11", + "inherits": [ + "config-common", + "config-toolchain-gcc-native32", + "config-language-c-11" + ] + }, + { + "name": "config-gcc-native32-cpp-14", + "inherits": [ + "config-common", + "config-toolchain-gcc-native32", + "config-language-cpp-14" + ] + }, + { + "name": "config-gcc-native32-cetl-14-17", + "inherits": [ + "config-common", + "config-toolchain-gcc-native32", + "config-language-cetl-14-17" + ] + }, + { + "name": "config-gcc-native32-cpp-17", + "inherits": [ + "config-common", + "config-toolchain-gcc-native32", + "config-language-cpp-17" + ] + }, + { + "name": "config-gcc-native32-cpp-17-pmr", + "inherits": [ + "config-common", + "config-toolchain-gcc-native32", + "config-language-cpp-17-pmr" + ] + }, + { + "name": "config-gcc-native32-cpp-20", + "inherits": [ + "config-common", + "config-toolchain-gcc-native32", + "config-language-cpp-20" + ] + }, + { + "name": "config-gcc-native32-cpp-20-pmr", + "inherits": [ + "config-common", + "config-toolchain-gcc-native32", + "config-language-cpp-20-pmr" ] } ], "buildPresets": [ { - "name": "build-debug-clang-cetl-14-17", - "displayName": "Clang Native : CETL 14 - 17", - "configurePreset": "config-clang-cetl-14-17", + "name": "build-clang-native-c-11-debugasan", + "configurePreset": "config-clang-native-c-11", + "configuration": "DebugAsan", + "targets": [ + "test_all" + ] + }, + { + "name": "build-clang-native-cpp-14-debugasan", + "configurePreset": "config-clang-native-cpp-14", + "configuration": "DebugAsan", + "targets": [ + "test_all" + ] + }, + { + "name": "build-clang-native-cetl-14-17-debugasan", + "configurePreset": "config-clang-native-cetl-14-17", + "configuration": "DebugAsan", + "targets": [ + "test_all" + ] + }, + { + "name": "build-clang-native-cpp-17-debugasan", + "configurePreset": "config-clang-native-cpp-17", + "configuration": "DebugAsan", + "targets": [ + "test_all" + ] + }, + { + "name": "build-clang-native-cpp-17-pmr-debugasan", + "configurePreset": "config-clang-native-cpp-17-pmr", + "configuration": "DebugAsan", + "targets": [ + "test_all" + ] + }, + { + "name": "build-clang-native-cpp-20-debugasan", + "configurePreset": "config-clang-native-cpp-20", + "configuration": "DebugAsan", + "targets": [ + "test_all" + ] + }, + { + "name": "build-clang-native-cpp-20-pmr-debugasan", + "configurePreset": "config-clang-native-cpp-20-pmr", + "configuration": "DebugAsan", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native-c-11-debugasan", + "configurePreset": "config-gcc-native-c-11", + "configuration": "DebugAsan", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native-cpp-14-debugasan", + "configurePreset": "config-gcc-native-cpp-14", + "configuration": "DebugAsan", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native-cetl-14-17-debugasan", + "configurePreset": "config-gcc-native-cetl-14-17", + "configuration": "DebugAsan", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native-cpp-17-debugasan", + "configurePreset": "config-gcc-native-cpp-17", + "configuration": "DebugAsan", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native-cpp-17-pmr-debugasan", + "configurePreset": "config-gcc-native-cpp-17-pmr", + "configuration": "DebugAsan", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native-cpp-20-debugasan", + "configurePreset": "config-gcc-native-cpp-20", + "configuration": "DebugAsan", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native-cpp-20-pmr-debugasan", + "configurePreset": "config-gcc-native-cpp-20-pmr", + "configuration": "DebugAsan", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native32-c-11-debugasan", + "configurePreset": "config-gcc-native32-c-11", + "configuration": "DebugAsan", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native32-cpp-14-debugasan", + "configurePreset": "config-gcc-native32-cpp-14", + "configuration": "DebugAsan", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native32-cetl-14-17-debugasan", + "configurePreset": "config-gcc-native32-cetl-14-17", + "configuration": "DebugAsan", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native32-cpp-17-debugasan", + "configurePreset": "config-gcc-native32-cpp-17", + "configuration": "DebugAsan", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native32-cpp-17-pmr-debugasan", + "configurePreset": "config-gcc-native32-cpp-17-pmr", + "configuration": "DebugAsan", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native32-cpp-20-debugasan", + "configurePreset": "config-gcc-native32-cpp-20", + "configuration": "DebugAsan", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native32-cpp-20-pmr-debugasan", + "configurePreset": "config-gcc-native32-cpp-20-pmr", + "configuration": "DebugAsan", + "targets": [ + "test_all" + ] + }, + { + "name": "build-clang-native-c-11-release", + "configurePreset": "config-clang-native-c-11", + "configuration": "Release", + "targets": [ + "test_all" + ] + }, + { + "name": "build-clang-native-cpp-14-release", + "configurePreset": "config-clang-native-cpp-14", + "configuration": "Release", + "targets": [ + "test_all" + ] + }, + { + "name": "build-clang-native-cetl-14-17-release", + "configurePreset": "config-clang-native-cetl-14-17", + "configuration": "Release", + "targets": [ + "test_all" + ] + }, + { + "name": "build-clang-native-cpp-17-release", + "configurePreset": "config-clang-native-cpp-17", + "configuration": "Release", + "targets": [ + "test_all" + ] + }, + { + "name": "build-clang-native-cpp-17-pmr-release", + "configurePreset": "config-clang-native-cpp-17-pmr", + "configuration": "Release", + "targets": [ + "test_all" + ] + }, + { + "name": "build-clang-native-cpp-20-release", + "configurePreset": "config-clang-native-cpp-20", + "configuration": "Release", + "targets": [ + "test_all" + ] + }, + { + "name": "build-clang-native-cpp-20-pmr-release", + "configurePreset": "config-clang-native-cpp-20-pmr", + "configuration": "Release", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native-c-11-release", + "configurePreset": "config-gcc-native-c-11", + "configuration": "Release", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native-cpp-14-release", + "configurePreset": "config-gcc-native-cpp-14", + "configuration": "Release", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native-cetl-14-17-release", + "configurePreset": "config-gcc-native-cetl-14-17", + "configuration": "Release", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native-cpp-17-release", + "configurePreset": "config-gcc-native-cpp-17", + "configuration": "Release", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native-cpp-17-pmr-release", + "configurePreset": "config-gcc-native-cpp-17-pmr", + "configuration": "Release", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native-cpp-20-release", + "configurePreset": "config-gcc-native-cpp-20", + "configuration": "Release", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native-cpp-20-pmr-release", + "configurePreset": "config-gcc-native-cpp-20-pmr", + "configuration": "Release", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native32-c-11-release", + "configurePreset": "config-gcc-native32-c-11", + "configuration": "Release", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native32-cpp-14-release", + "configurePreset": "config-gcc-native32-cpp-14", + "configuration": "Release", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native32-cetl-14-17-release", + "configurePreset": "config-gcc-native32-cetl-14-17", + "configuration": "Release", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native32-cpp-17-release", + "configurePreset": "config-gcc-native32-cpp-17", + "configuration": "Release", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native32-cpp-17-pmr-release", + "configurePreset": "config-gcc-native32-cpp-17-pmr", + "configuration": "Release", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native32-cpp-20-release", + "configurePreset": "config-gcc-native32-cpp-20", + "configuration": "Release", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native32-cpp-20-pmr-release", + "configurePreset": "config-gcc-native32-cpp-20-pmr", + "configuration": "Release", + "targets": [ + "test_all" + ] + }, + { + "name": "build-clang-native-c-11-debugcov", + "configurePreset": "config-clang-native-c-11", + "configuration": "DebugCov", + "targets": [ + "test_all" + ] + }, + { + "name": "build-clang-native-cpp-14-debugcov", + "configurePreset": "config-clang-native-cpp-14", + "configuration": "DebugCov", + "targets": [ + "test_all" + ] + }, + { + "name": "build-clang-native-cetl-14-17-debugcov", + "configurePreset": "config-clang-native-cetl-14-17", + "configuration": "DebugCov", + "targets": [ + "test_all" + ] + }, + { + "name": "build-clang-native-cpp-17-debugcov", + "configurePreset": "config-clang-native-cpp-17", + "configuration": "DebugCov", + "targets": [ + "test_all" + ] + }, + { + "name": "build-clang-native-cpp-17-pmr-debugcov", + "configurePreset": "config-clang-native-cpp-17-pmr", + "configuration": "DebugCov", + "targets": [ + "test_all" + ] + }, + { + "name": "build-clang-native-cpp-20-debugcov", + "configurePreset": "config-clang-native-cpp-20", + "configuration": "DebugCov", + "targets": [ + "test_all" + ] + }, + { + "name": "build-clang-native-cpp-20-pmr-debugcov", + "configurePreset": "config-clang-native-cpp-20-pmr", + "configuration": "DebugCov", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native-c-11-debugcov", + "configurePreset": "config-gcc-native-c-11", + "configuration": "DebugCov", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native-cpp-14-debugcov", + "configurePreset": "config-gcc-native-cpp-14", + "configuration": "DebugCov", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native-cetl-14-17-debugcov", + "configurePreset": "config-gcc-native-cetl-14-17", + "configuration": "DebugCov", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native-cpp-17-debugcov", + "configurePreset": "config-gcc-native-cpp-17", + "configuration": "DebugCov", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native-cpp-17-pmr-debugcov", + "configurePreset": "config-gcc-native-cpp-17-pmr", + "configuration": "DebugCov", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native-cpp-20-debugcov", + "configurePreset": "config-gcc-native-cpp-20", + "configuration": "DebugCov", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native-cpp-20-pmr-debugcov", + "configurePreset": "config-gcc-native-cpp-20-pmr", + "configuration": "DebugCov", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native32-c-11-debugcov", + "configurePreset": "config-gcc-native32-c-11", + "configuration": "DebugCov", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native32-cpp-14-debugcov", + "configurePreset": "config-gcc-native32-cpp-14", + "configuration": "DebugCov", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native32-cetl-14-17-debugcov", + "configurePreset": "config-gcc-native32-cetl-14-17", + "configuration": "DebugCov", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native32-cpp-17-debugcov", + "configurePreset": "config-gcc-native32-cpp-17", + "configuration": "DebugCov", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native32-cpp-17-pmr-debugcov", + "configurePreset": "config-gcc-native32-cpp-17-pmr", + "configuration": "DebugCov", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native32-cpp-20-debugcov", + "configurePreset": "config-gcc-native32-cpp-20", + "configuration": "DebugCov", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native32-cpp-20-pmr-debugcov", + "configurePreset": "config-gcc-native32-cpp-20-pmr", + "configuration": "DebugCov", + "targets": [ + "test_all" + ] + }, + { + "name": "build-clang-native-c-11-debug", + "configurePreset": "config-clang-native-c-11", + "configuration": "Debug", + "targets": [ + "test_all" + ] + }, + { + "name": "build-clang-native-cpp-14-debug", + "configurePreset": "config-clang-native-cpp-14", + "configuration": "Debug", + "targets": [ + "test_all" + ] + }, + { + "name": "build-clang-native-cetl-14-17-debug", + "configurePreset": "config-clang-native-cetl-14-17", + "configuration": "Debug", + "targets": [ + "test_all" + ] + }, + { + "name": "build-clang-native-cpp-17-debug", + "configurePreset": "config-clang-native-cpp-17", + "configuration": "Debug", + "targets": [ + "test_all" + ] + }, + { + "name": "build-clang-native-cpp-17-pmr-debug", + "configurePreset": "config-clang-native-cpp-17-pmr", + "configuration": "Debug", + "targets": [ + "test_all" + ] + }, + { + "name": "build-clang-native-cpp-20-debug", + "configurePreset": "config-clang-native-cpp-20", + "configuration": "Debug", + "targets": [ + "test_all" + ] + }, + { + "name": "build-clang-native-cpp-20-pmr-debug", + "configurePreset": "config-clang-native-cpp-20-pmr", + "configuration": "Debug", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native-c-11-debug", + "configurePreset": "config-gcc-native-c-11", + "configuration": "Debug", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native-cpp-14-debug", + "configurePreset": "config-gcc-native-cpp-14", + "configuration": "Debug", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native-cetl-14-17-debug", + "configurePreset": "config-gcc-native-cetl-14-17", + "configuration": "Debug", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native-cpp-17-debug", + "configurePreset": "config-gcc-native-cpp-17", + "configuration": "Debug", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native-cpp-17-pmr-debug", + "configurePreset": "config-gcc-native-cpp-17-pmr", + "configuration": "Debug", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native-cpp-20-debug", + "configurePreset": "config-gcc-native-cpp-20", + "configuration": "Debug", + "targets": [ + "test_all" + ] + }, + { + "name": "build-gcc-native-cpp-20-pmr-debug", + "configurePreset": "config-gcc-native-cpp-20-pmr", + "configuration": "Debug", "targets": [ "test_all" ] }, { - "name": "build-debug-gcc-cetl-14-17", - "displayName": "GCC Native : CETL 14 - 17", - "configurePreset": "config-gcc-cetl-14-17", + "name": "build-gcc-native32-c-11-debug", + "configurePreset": "config-gcc-native32-c-11", + "configuration": "Debug", "targets": [ "test_all" ] }, { - "name": "build-debug-clang-cpp-14", - "displayName": "Clang Native : C++14", - "configurePreset": "config-clang-cpp-14", + "name": "build-gcc-native32-cpp-14-debug", + "configurePreset": "config-gcc-native32-cpp-14", + "configuration": "Debug", "targets": [ "test_all" ] }, { - "name": "build-debug-gcc-cpp-14", - "displayName": "GCC Native : C++14", - "configurePreset": "config-gcc-cpp-14", + "name": "build-gcc-native32-cetl-14-17-debug", + "configurePreset": "config-gcc-native32-cetl-14-17", + "configuration": "Debug", "targets": [ "test_all" ] }, { - "name": "build-debug-clang-cpp-17", - "displayName": "Clang Native : C++17", - "configurePreset": "config-clang-cpp-17", + "name": "build-gcc-native32-cpp-17-debug", + "configurePreset": "config-gcc-native32-cpp-17", + "configuration": "Debug", "targets": [ "test_all" ] }, { - "name": "build-debug-gcc-cpp-17", - "displayName": "GCC Native : C++17", - "configurePreset": "config-gcc-cpp-17", + "name": "build-gcc-native32-cpp-17-pmr-debug", + "configurePreset": "config-gcc-native32-cpp-17-pmr", + "configuration": "Debug", "targets": [ "test_all" ] }, { - "name": "build-debug-clang-c", - "displayName": "Clang Native : C", - "configurePreset": "config-clang-c", + "name": "build-gcc-native32-cpp-20-debug", + "configurePreset": "config-gcc-native32-cpp-20", + "configuration": "Debug", "targets": [ "test_all" ] }, { - "name": "build-debug-gcc-c", - "displayName": "GCC Native : C", - "configurePreset": "config-gcc-c", + "name": "build-gcc-native32-cpp-20-pmr-debug", + "configurePreset": "config-gcc-native32-cpp-20-pmr", + "configuration": "Debug", "targets": [ "test_all" ] diff --git a/verification/cmake/compiler_flag_sets/common.cmake b/verification/cmake/compiler_flag_sets/common.cmake index 0f8a94d9..d60ac361 100644 --- a/verification/cmake/compiler_flag_sets/common.cmake +++ b/verification/cmake/compiler_flag_sets/common.cmake @@ -10,44 +10,33 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON) -set(C_FLAG_SET ) -set(EXE_LINKER_FLAG_SET ) -set(DEFINITIONS_SET ) +set(C_FLAG_SET) +set(EXE_LINKER_FLAG_SET) # # Diagnostics for C and C++ # list(APPEND C_FLAG_SET - "-pedantic" - "-Wall" - "-Wextra" - "-Werror" - "-Wfloat-equal" - "-Wconversion" - "-Wunused-parameter" - "-Wunused-variable" - "-Wunused-value" - "-Wcast-align" - "-Wmissing-declarations" - "-Wmissing-field-initializers" - "-Wdouble-promotion" - "-Wswitch-enum" - "-Wtype-limits" - "-Wno-c++17-attribute-extensions" + "-pedantic" + "-Wall" + "-Wextra" + "-Werror" + "-Wfloat-equal" + "-Wconversion" + "-Wunused-parameter" + "-Wunused-variable" + "-Wunused-value" + "-Wcast-align" + "-Wmissing-declarations" + "-Wmissing-field-initializers" + "-Wdouble-promotion" + "-Wswitch-enum" + "-Wtype-limits" ) -if(DEFINED NUNAVUT_VERIFICATION_TARGET_PLATFORM) - if(${NUNAVUT_VERIFICATION_TARGET_PLATFORM} STREQUAL "native32") - list(APPEND C_FLAG_SET "-m32") - list(APPEND EXE_LINKER_FLAG_SET "-m32") - message(STATUS "Configuring for native32 platform.") - elseif(${NUNAVUT_VERIFICATION_TARGET_PLATFORM} STREQUAL "native64") - list(APPEND C_FLAG_SET "-m64") - list(APPEND EXE_LINKER_FLAG_SET "-m64") - message(STATUS "Configuring for native64 platform.") - else() - message(FATAL_ERROR "\"${NUNAVUT_VERIFICATION_TARGET_PLATFORM}\" is not a supported value.") - endif() +if(${LOCAL_VERIFICATION_TARGET_PLATFORM} STREQUAL "native32") + list(APPEND C_FLAG_SET "-m32") + list(APPEND EXE_LINKER_FLAG_SET "-m32") endif() set(CXX_FLAG_SET ${C_FLAG_SET}) @@ -57,45 +46,64 @@ set(ASM_FLAG_SET ${C_FLAG_SET}) # C++ only diagnostics # list(APPEND CXX_FLAG_SET - "-Wsign-conversion" - "-Wsign-promo" - "-Wold-style-cast" - "-Wzero-as-null-pointer-constant" - "-Wnon-virtual-dtor" - "-Woverloaded-virtual" + "-Wsign-conversion" + "-Wsign-promo" + "-Wold-style-cast" + "-Wzero-as-null-pointer-constant" + "-Wnon-virtual-dtor" + "-Woverloaded-virtual" + "-Wno-c++17-attribute-extensions" ) -if (CMAKE_BUILD_TYPE STREQUAL "Release") - message(STATUS "Release build. Setting optimization flags.") - list(APPEND C_FLAG_SET - "-O2" - # "-D_FORTIFY_SOURCE=3" # TODO: 3 if gcc12 or later otherwise 2 - ) -else() - - message(STATUS "Not a Release build. Setting debug flags.") - list(APPEND C_FLAG_SET - "-Og" - "-DDEBUG" - "-ggdb" - ) - -endif() - -if (CETLVAST_DISABLE_CPP_EXCEPTIONS) +if(CETLVAST_DISABLE_CPP_EXCEPTIONS) message(STATUS "CETLVAST_DISABLE_CPP_EXCEPTIONS is true. Adding -fno-exceptions to compiler flags.") - list(APPEND CXX_FLAG_SET - "-fno-exceptions") + list(APPEND CXX_FLAG_SET "-fno-exceptions") endif() list(APPEND CXX_FLAG_SET ${C_FLAG_SET}) list(APPEND ASM_FLAG_SET ${C_FLAG_SET}) +list(APPEND LOCAL_SANITIZER_OPTIONS + "-fsanitize=address" + "-fsanitize=pointer-compare" + "-fsanitize=pointer-subtract" + "-fsanitize=undefined" + "-fsanitize=alignment" + "-fsanitize=null" + "-fsanitize=pointer-compare" + "-fsanitize=pointer-subtract" + "-fsanitize=pointer-overflow" + "-fsanitize=bounds" + "-fsanitize=signed-integer-overflow" + "-fsanitize=shift" + "-fsanitize=shift-exponent" + "-fsanitize=shift-base" + "-fsanitize=float-divide-by-zero" + "-fsanitize=float-cast-overflow" + "-fsanitize=pointer-overflow" + "-fsanitize=builtin" +) + +add_compile_options( + "$<$:-O2>" + "$<$:-Og>" + "$<$:-ggdb>" +) add_compile_options("$<$:${C_FLAG_SET}>") add_compile_options("$<$:${CXX_FLAG_SET}>") add_compile_options("$<$:${ASM_FLAG_SET}>") add_compile_options("$<$:-Wno-stringop-overflow>") +add_compile_options("$<$:${LOCAL_SANITIZER_OPTIONS}>") +add_compile_options( + "$<$,$>:--coverage>" + "$<$,$>:-fno-elide-constructors>" + "$<$,$>:-fprofile-instr-generate>" + "$<$,$>:-ftest-coverage>" + "$<$,$>:-fprofile-arcs>" + "$<$,$>:-fcoverage-mapping>" +) add_link_options(${EXE_LINKER_FLAG_SET}) -add_definitions(${DEFINITIONS_SET}) +add_link_options("$<$:${LOCAL_SANITIZER_OPTIONS}>") +add_link_options("$<$:--coverage>") set(CMAKE_C_EXTENSIONS OFF) set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/verification/cmake/compiler_flag_sets/native_w_asan.cmake b/verification/cmake/compiler_flag_sets/native_w_asan.cmake deleted file mode 100644 index 9c27d55e..00000000 --- a/verification/cmake/compiler_flag_sets/native_w_asan.cmake +++ /dev/null @@ -1,41 +0,0 @@ -# -# Copyright (C) OpenCyphal Development Team -# Copyright Amazon.com Inc. or its affiliates. -# SPDX-License-Identifier: MIT -# - -# -# Enable undefined behaviour sanitizer -# - -include(${CMAKE_CURRENT_LIST_DIR}/common.cmake) - - -list(APPEND LOCAL_SANITIZER_OPTIONS - "-fsanitize=address" - "-fsanitize=pointer-compare" - "-fsanitize=pointer-subtract" - "-fsanitize=undefined" - "-fsanitize=alignment" - "-fsanitize=null" - "-fsanitize=pointer-compare" - "-fsanitize=pointer-subtract" - "-fsanitize=pointer-overflow" - "-fsanitize=bounds" - "-fsanitize=signed-integer-overflow" - "-fsanitize=shift" - "-fsanitize=shift-exponent" - "-fsanitize=shift-base" - "-fsanitize=float-divide-by-zero" - "-fsanitize=float-cast-overflow" - "-fsanitize=pointer-overflow" - "-fsanitize=builtin" -) - -add_compile_options( - ${LOCAL_SANITIZER_OPTIONS} -) - -add_link_options( - ${LOCAL_SANITIZER_OPTIONS} -) diff --git a/verification/cmake/compiler_flag_sets/native_w_cov.cmake b/verification/cmake/compiler_flag_sets/native_w_cov.cmake deleted file mode 100644 index 9d09df11..00000000 --- a/verification/cmake/compiler_flag_sets/native_w_cov.cmake +++ /dev/null @@ -1,21 +0,0 @@ -# -# Copyright (C) OpenCyphal Development Team -# Copyright Amazon.com Inc. or its affiliates. -# SPDX-License-Identifier: MIT -# - -# -# Enable code coverage instrumentation for C and C++ code -# - -include(${CMAKE_CURRENT_LIST_DIR}/common.cmake) - -add_compile_options( - "-fprofile-arcs" - "-ftest-coverage" -) - -add_link_options( - "-fprofile-arcs" - "-ftest-coverage" -) diff --git a/verification/cmake/modules/Findgtest.cmake b/verification/cmake/modules/Findgtest.cmake index 7ad88248..013af45a 100644 --- a/verification/cmake/modules/Findgtest.cmake +++ b/verification/cmake/modules/Findgtest.cmake @@ -3,7 +3,7 @@ # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. # -set(GOOGLETEST_SUBMODULE "${NUNAVUT_SUBMODULES_ROOT}/googletest") +set(GOOGLETEST_SUBMODULE "${NUNAVUT_SUBMODULES_DIR}/googletest") if(EXISTS "${GOOGLETEST_SUBMODULE}/googletest") set(GTEST_FOUND ON) @@ -37,7 +37,6 @@ target_compile_options(gmock_main PRIVATE "-Wno-float-equal" "-Wno-conversion" "-DGTEST_HAS_PTHREAD=0" - "${NUNAVUT_VERIFICATION_EXTRA_COMPILE_CFLAGS}" ) include(FindPackageHandleStandardArgs) diff --git a/verification/cmake/modules/Findlcov.cmake b/verification/cmake/modules/Findlcov.cmake index ef5da663..a7c3834e 100644 --- a/verification/cmake/modules/Findlcov.cmake +++ b/verification/cmake/modules/Findlcov.cmake @@ -1,5 +1,5 @@ # -# Find lcov and deal with clang weridness. +# Find lcov and deal with clang weirdness. # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. # @@ -34,7 +34,7 @@ if(LCOV) # Thanks to http://logan.tw/posts/2015/04/28/check-code-coverage-with-clang-and-lcov/ file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/gcov_tool.sh "#!/usr/bin/env bash\nexec ${LLVM_COV} gcov \"$@\"\n") file(COPY ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_FILES_DIRECTORY}/gcov_tool.sh - DESTINATION ${NUNAVUT_VERIFICATIONS_BINARY_DIR} + DESTINATION ${CMAKE_CURRENT_BINARY_DIR} NO_SOURCE_PERMISSIONS FILE_PERMISSIONS OWNER_READ OWNER_WRITE @@ -43,7 +43,7 @@ if(LCOV) GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) - set(NUNAVUT_GOV_TOOL_ARG "--gcov-tool" "${NUNAVUT_VERIFICATIONS_BINARY_DIR}/gcov_tool.sh") + set(NUNAVUT_GOV_TOOL_ARG "--gcov-tool" "${CMAKE_CURRENT_BINARY_DIR}/gcov_tool.sh") else() message(WARNING "llvm-cov was not found but we are compiling using clang. The coverage report build step may fail.") endif() diff --git a/verification/cmake/modules/Findo1heap.cmake b/verification/cmake/modules/Findo1heap.cmake index 9332e674..4225bc9c 100644 --- a/verification/cmake/modules/Findo1heap.cmake +++ b/verification/cmake/modules/Findo1heap.cmake @@ -4,7 +4,7 @@ # Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved. # -set(O1HEAP_SUBMODULE "${NUNAVUT_SUBMODULES_ROOT}/o1heap") +set(O1HEAP_SUBMODULE "${NUNAVUT_SUBMODULES_DIR}/o1heap") if(EXISTS "${O1HEAP_SUBMODULE}/README.md") set(O1HEAP_FOUND ON) @@ -20,10 +20,6 @@ add_library(o1heap STATIC EXCLUDE_FROM_ALL ${O1HEAP_SUBMODULE}/o1heap/o1heap.c ) -target_compile_options(o1heap PUBLIC - "${NUNAVUT_VERIFICATION_EXTRA_COMPILE_CFLAGS}" - ) - include(FindPackageHandleStandardArgs) find_package_handle_standard_args(o1heap diff --git a/verification/cmake/modules/Findunity.cmake b/verification/cmake/modules/Findunity.cmake index 10bcdf7b..e4ca8b64 100644 --- a/verification/cmake/modules/Findunity.cmake +++ b/verification/cmake/modules/Findunity.cmake @@ -4,7 +4,7 @@ # Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. # -set(UNITY_SUBMODULE "${NUNAVUT_SUBMODULES_ROOT}/unity") +set(UNITY_SUBMODULE "${NUNAVUT_SUBMODULES_DIR}/unity") if(EXISTS "${UNITY_SUBMODULE}/src") set(UNITY_FOUND ON) @@ -30,7 +30,6 @@ target_compile_options(unity_core PUBLIC "-Wno-switch-enum" "-Wno-conversion" "-DUNITY_INCLUDE_DOUBLE" - "${NUNAVUT_VERIFICATION_EXTRA_COMPILE_CFLAGS}" ) include(FindPackageHandleStandardArgs) diff --git a/verification/cmake/utils.cmake b/verification/cmake/utils.cmake index aa40d7b7..c673fed0 100644 --- a/verification/cmake/utils.cmake +++ b/verification/cmake/utils.cmake @@ -80,9 +80,9 @@ endfunction() # function: define_native_test_run - creates a makefile target that will build and # run individual unit tests. # -# param: ARG_TEST_NAME string - The name of the test to run. A target will be created +# param: TEST_NAME string - The name of the test to run. A target will be created # with the name run_${ARG_TEST_NAME} -# param: ARG_OUTDIR path - The path where the test binaries live. +# param: OUTDIR path - The path where the test binaries live. # function(define_native_test_run) # +--[ INPUTS ]-----------------------------------------------------------+ @@ -108,3 +108,144 @@ function(define_native_test_run) ) endfunction() + +# +# function: handle_nunavut_verification_language_and_standard - Parse NUNAVUT_VERIFICATION_LANG, +# NUNAVUT_VERIFICATION_LANG_STANDARD, and NUNAVUT_VERIFICATION_TARGET_PLATFORM applying defaults, +# error checking, and other transformations to produce a consistent result for these values. +# +# option: PRINT_STATUS - If set then STATUS messages will be emitted +# to make the results of this function visible in configuration logs. +# option: REQUIRED - If set then each language standard will have +# their coresponding CMAKE_[langaug]_STANDARD_REQUIRED variable set. +# param: OUT_LOCAL_VERIFICATION_TARGET_LANGUAGE - The name of a variable to set in the parent +# scope with the resolved verification target language. This shall be a valid, +# built-in --target-language value for Nunavut. +# param: OUT_VERIFICATION_LANGUAGE_STANDARD_CPP - The name of a variable to set in the parent +# scope with the resolved c++ standard to use when generating c++ headers. +# This shall be a valid built-in --language-standard value for Nunavut. +# param: OUT_VERIFICATION_LANGUAGE_STANDARD_C - The name of a variable to set in the parent +# scope with the resolved c standard to use when generating c headers. +# This shall be a valid built-in --language-standard value for Nunavut. +# param: OUT_VERIFICATION_TARGET_PLATFORM - The name of a variable to set in the parent +# scope with the resolved target platform. +# +function(handle_nunavut_verification_language_and_standard) + # +--[ INPUTS ]-----------------------------------------------------------+ + set(options PRINT_STATUS REQUIRED) + set(monoValues + OUT_LOCAL_VERIFICATION_TARGET_LANGUAGE + OUT_VERIFICATION_LANGUAGE_STANDARD_CPP + OUT_VERIFICATION_LANGUAGE_STANDARD_C + OUT_VERIFICATION_TARGET_PLATFORM + ) + set(multiValues "") + + cmake_parse_arguments( + ARG + "${options}" + "${monoValues}" + "${multiValues}" + ${ARGN} + ) + + # +--[ BODY ]------------------------------------------------------------+ + + if(DEFINED ENV{NUNAVUT_VERIFICATION_LANG}) + if (ARG_PRINT_STATUS) + message(STATUS "Getting NUNAVUT_VERIFICATION_LANG from the environment ($ENV{NUNAVUT_VERIFICATION_LANG})") + endif() + set(NUNAVUT_VERIFICATION_LANG "$ENV{NUNAVUT_VERIFICATION_LANG}" CACHE STRING "The Nunavut output language to verify.") + else() + set(NUNAVUT_VERIFICATION_LANG "c" CACHE STRING "The Nunavut output language to verify.") + endif() + + string(TOLOWER ${NUNAVUT_VERIFICATION_LANG} LOCAL_VERIFICATION_LANG) + + if(NOT (LOCAL_VERIFICATION_LANG STREQUAL "cpp" OR LOCAL_VERIFICATION_LANG STREQUAL "c")) + message(FATAL_ERROR "Unknown or no verification language (${NUNAVUT_VERIFICATION_LANG}). Try cmake -DNUNAVUT_VERIFICATION_LANG:string=[cpp|c]") + endif() + + if(DEFINED ENV{NUNAVUT_VERIFICATION_LANG_STANDARD}) + if (ARG_PRINT_STATUS) + message(STATUS "Getting NUNAVUT_VERIFICATION_LANG_STANDARD from the environment ($ENV{NUNAVUT_VERIFICATION_LANG_STANDARD})") + endif() + set(NUNAVUT_VERIFICATION_LANG_STANDARD "$ENV{NUNAVUT_VERIFICATION_LANG_STANDARD}" CACHE STRING "The language standard to use when generating source for verification.") + else() + set(NUNAVUT_VERIFICATION_LANG_STANDARD "c11" CACHE STRING "The language standard to use when generating source for verification.") + endif() + + if(DEFINED ENV{NUNAVUT_VERIFICATION_TARGET_PLATFORM}) + if (ARG_PRINT_STATUS) + message(STATUS "Getting NUNAVUT_VERIFICATION_TARGET_PLATFORM from the environment ($ENV{NUNAVUT_VERIFICATION_TARGET_PLATFORM})") + endif() + set(NUNAVUT_VERIFICATION_TARGET_PLATFORM "$ENV{NUNAVUT_VERIFICATION_TARGET_PLATFORM}" CACHE STRING "The platform to compile for when generating source for verification.") + else() + set(NUNAVUT_VERIFICATION_TARGET_PLATFORM "native" CACHE STRING "The platform to compile for when generating source for verification.") + endif() + + # C++ + if(LOCAL_VERIFICATION_LANG STREQUAL "cpp") + string(TOLOWER ${NUNAVUT_VERIFICATION_LANG_STANDARD} LOCAL_VERIFICATION_LANG_CPP_STANDARD) + else() + set(LOCAL_VERIFICATION_LANG_CPP_STANDARD "c++20") + endif() + + string(REGEX MATCH "c?e?t?l?[px\+]+-?([0-9]*).*" LOCAL_VERIFICATION_LANG_CPP_MATCH ${LOCAL_VERIFICATION_LANG_CPP_STANDARD}) + + if(NOT LOCAL_VERIFICATION_LANG_CPP_MATCH) + message(FATAL_ERROR "NUNAVUT_VERIFICATION_LANG_STANDARD (${LOCAL_VERIFICATION_LANG_CPP_STANDARD}) is in an unexpected format.") + endif() + + set(LOCAL_VERIFICATION_LANG_CPP_MATCH ${CMAKE_MATCH_1}) + + set(CMAKE_CXX_STANDARD ${LOCAL_VERIFICATION_LANG_CPP_MATCH} PARENT_SCOPE) + if (ARG_REQUIRED) + set(CMAKE_CXX_STANDARD_REQUIRED ON PARENT_SCOPE) + endif() + + # C + if(NUNAVUT_VERIFICATION_LANG STREQUAL "c") + string(TOLOWER ${NUNAVUT_VERIFICATION_LANG_STANDARD} LOCAL_VERIFICATION_LANG_C_STANDARD) + else() + set(LOCAL_VERIFICATION_LANG_C_STANDARD "c11") + endif() + + string(REGEX REPLACE "c-?([0-9]*).*" "\\1" LOCAL_VERIFICATION_LANG_C_MATCH ${LOCAL_VERIFICATION_LANG_C_STANDARD}) + + if(NOT LOCAL_VERIFICATION_LANG_C_MATCH) + message(FATAL_ERROR "NUNAVUT_VERIFICATION_LANG_STANDARD (${LOCAL_VERIFICATION_LANG_C_STANDARD}) is in an unexpected format.") + endif() + + set(CMAKE_C_STANDARD ${LOCAL_VERIFICATION_LANG_C_MATCH} PARENT_SCOPE) + if (ARG_REQUIRED) + set(CMAKE_C_STANDARD_REQUIRED ON PARENT_SCOPE) + endif() + + # PLATFORM + string(TOLOWER ${NUNAVUT_VERIFICATION_TARGET_PLATFORM} LOCAL_VERIFICATION_TARGET_PLATFORM) + if(NOT (${LOCAL_VERIFICATION_TARGET_PLATFORM} STREQUAL "native32" OR ${LOCAL_VERIFICATION_TARGET_PLATFORM} STREQUAL "native")) + message(FATAL_ERROR "\"NUNAVUT_VERIFICATION_TARGET_PLATFORM=${NUNAVUT_VERIFICATION_TARGET_PLATFORM}\" is not a supported value.") + endif() + + # STATUS + if (ARG_PRINT_STATUS) + message(STATUS "${ARG_OUT_LOCAL_VERIFICATION_TARGET_LANGUAGE} is ${LOCAL_VERIFICATION_LANG}") + message(STATUS "${ARG_OUT_VERIFICATION_TARGET_PLATFORM} is ${LOCAL_VERIFICATION_TARGET_PLATFORM}") + message(STATUS "${ARG_OUT_VERIFICATION_LANGUAGE_STANDARD_C} is ${LOCAL_VERIFICATION_LANG_C_STANDARD}") + message(STATUS "${ARG_OUT_VERIFICATION_LANGUAGE_STANDARD_CPP} is ${LOCAL_VERIFICATION_LANG_CPP_STANDARD}") + if (ARG_REQUIRED) + set(LOCAL_REQUIRED_STATUS " (required)") + else() + set(LOCAL_REQUIRED_STATUS "") + endif() + message(STATUS "CMAKE_C_STANDARD is ${LOCAL_VERIFICATION_LANG_C_MATCH}${LOCAL_REQUIRED_STATUS}") + message(STATUS "CMAKE_CXX_STANDARD is ${LOCAL_VERIFICATION_LANG_CPP_MATCH}${LOCAL_REQUIRED_STATUS}") + endif() + + # +--[ OUT ]-------------------------------------------------------------+ + set(${ARG_OUT_LOCAL_VERIFICATION_TARGET_LANGUAGE} ${LOCAL_VERIFICATION_LANG} PARENT_SCOPE) + set(${ARG_OUT_VERIFICATION_LANGUAGE_STANDARD_CPP} ${LOCAL_VERIFICATION_LANG_CPP_STANDARD} PARENT_SCOPE) + set(${ARG_OUT_VERIFICATION_LANGUAGE_STANDARD_C} ${LOCAL_VERIFICATION_LANG_C_STANDARD} PARENT_SCOPE) + set(${ARG_OUT_VERIFICATION_TARGET_PLATFORM} ${LOCAL_VERIFICATION_TARGET_PLATFORM} PARENT_SCOPE) +endfunction() diff --git a/verification/cpp/suite/test_constant.cpp b/verification/cpp/suite/test_constant.cpp index cde89ef4..fa06dfc9 100644 --- a/verification/cpp/suite/test_constant.cpp +++ b/verification/cpp/suite/test_constant.cpp @@ -11,6 +11,43 @@ #include "regulated/basics/Union_0_1.hpp" #include "regulated/basics/Service_0_1.hpp" +#if __cplusplus == 201402L +const std::uint16_t regulated::basics::Struct__0_1::_traits_::FixedPortId; +const bool regulated::basics::Struct__0_1::_traits_::HasFixedPortID; +const std::size_t regulated::basics::Union_0_1::_traits_::ExtentBytes; +const std::size_t regulated::basics::Union_0_1::VariantType::MAX_INDEX; +const double regulated::basics::Struct__0_1::CONSTANT_MINUS_THREE; +const std::int64_t regulated::basics::Struct__0_1::CONSTANT_MINUS_MAX_OFFSET; +const std::uint8_t regulated::basics::Struct__0_1::CONSTANT_ZEE; +const bool regulated::basics::Struct__0_1::CONSTANT_TRUTH; +const float regulated::basics::Service::Response_0_1::ONE_TENTH; +const double regulated::basics::Service::Request_0_1::HALF; +const bool regulated::basics::Service::Request_0_1::_traits_::IsRequest; +const bool regulated::basics::Service::Request_0_1::_traits_::IsResponse; +const bool regulated::basics::Service::Request_0_1::_traits_::IsService; +const bool regulated::basics::Service::Request_0_1::_traits_::IsServiceType; +const bool regulated::basics::Service::Response_0_1::_traits_::IsResponse; +const bool regulated::basics::Service::Response_0_1::_traits_::IsServiceType; +const bool regulated::basics::Service_0_1::_traits_::IsService; +const bool regulated::basics::Service_0_1::_traits_::IsServiceType; + +const std::size_t regulated::basics::Struct__0_1::_traits_::ArrayCapacity::i10_4; +const std::size_t regulated::basics::Struct__0_1::_traits_::ArrayCapacity::f16_le2; +const std::size_t regulated::basics::Struct__0_1::_traits_::ArrayCapacity::unaligned_bitpacked_3; +const std::size_t regulated::basics::Struct__0_1::_traits_::ArrayCapacity::bytes_lt3; +const std::size_t regulated::basics::Struct__0_1::_traits_::ArrayCapacity::bytes_3; +const std::size_t regulated::basics::Struct__0_1::_traits_::ArrayCapacity::u2_le4; +const std::size_t regulated::basics::Struct__0_1::_traits_::ArrayCapacity::delimited_fix_le2; +const std::size_t regulated::basics::Struct__0_1::_traits_::ArrayCapacity::u16_2; +const std::size_t regulated::basics::Struct__0_1::_traits_::ArrayCapacity::aligned_bitpacked_3; +const std::size_t regulated::basics::Struct__0_1::_traits_::ArrayCapacity::unaligned_bitpacked_lt3; +const std::size_t regulated::basics::Struct__0_1::_traits_::ArrayCapacity::delimited_var_2; +const std::size_t regulated::basics::Struct__0_1::_traits_::ArrayCapacity::aligned_bitpacked_le3; + +const std::size_t regulated::basics::Union_0_1::_traits_::ArrayCapacity::delimited_fix_le2; +const std::size_t regulated::basics::Union_0_1::_traits_::ArrayCapacity::delimited_var_le2; +#endif + using testing::Le; using testing::StrEq; using testing::FloatNear; diff --git a/verification/preset_generator.py b/verification/preset_generator.py new file mode 100755 index 00000000..3da851ef --- /dev/null +++ b/verification/preset_generator.py @@ -0,0 +1,351 @@ +#!/usr/bin/env python3 +# +# Copyright (C) OpenCyphal Development Team +# Copyright Amazon.com Inc. or its affiliates. +# SPDX-License-Identifier: MIT +# +""" + Cmake presets has a problem (see https://gitlab.kitware.com/cmake/cmake/-/issues/22538) where a matrix of options + causes a combinatorial explosion of presets that are tedious to generate and maintain. This script automates the + generation and modification of such large lists of presets for the Nunavut verification project. +""" + +import argparse +import functools +import itertools +import json +import sys +import textwrap +from pathlib import Path +from collections import OrderedDict + +dimensions: OrderedDict[str, dict] = OrderedDict( + [ + ( + "toolchain", + { + "short_name": "tc", + "help": "The toolchain to use. Optionally provide colon separated platform name.", + "split": ":", + "values": { + "toolchainFile": lambda tc: f"${{sourceDir}}/cmake/toolchains/{tc.split(':')[0]}.cmake", + "cacheVariables": lambda tc: ( + {"NUNAVUT_VERIFICATION_TARGET_PLATFORM": tc.split(":")[1]} if len(tc.split(":")) > 1 else {} + ), + }, + }, + ), + ( + "language", + { + "short_name": "ln", + "help": "A pair of language name and language standard to use separated by a dash. For example, 'c-11'.", + "split": "-", + "values": { + "cacheVariables": lambda las: { + "NUNAVUT_VERIFICATION_LANG": las.split("-")[0], + "NUNAVUT_VERIFICATION_LANG_STANDARD": las.split("-")[1], + } + }, + }, + ), + ] +) + + +def parse_arguments() -> argparse.Namespace: + """ + Define and parse the command line arguments. + """ + + parser = argparse.ArgumentParser( + formatter_class=argparse.RawTextHelpFormatter, + description=textwrap.dedent( + """ + Generate CMake presets based on given options. See + https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html for details on cmake presets. + + This script is most useful when given a pre-existing CMakePresets.json file that contains common + configurations that are used as a base for generating a large number of configurations. See the + `--common-configuration` option for the default common configuration or to specify your own. + + The script will look for CMAKE_CONFIGURATION_TYPES in the common configurations and generate + build presets for each of the configurations found. If CMAKE_CONFIGURATION_TYPES is not found, + it will look for CMAKE_DEFAULT_BUILD_TYPE. If neither are found, it will raise an error. + + An error will be raised in the preset version does not match the --preset-version value. + """ + ).lstrip(), + epilog=textwrap.dedent( + """ + + Copyright (C) OpenCyphal Development Team + Copyright Amazon.com Inc. or its affiliates. + Released under SPDX-License-Identifier: MIT + + **Example Usage** :: + + # a single --clean is reccomended which will remove all visible configurations and build presets before + # generating new ones from the common, hidden, and given options. + + ./preset_generator.py --clean -ln c-11 -tc gcc:linux -ln c++-17 -tc gcc:armv7m + + # Double clean will remove everything except the common configurations. + + ./preset_generator.py --clean --clean -ln c-11 -tc gcc:linux -ln c++-17 -tc gcc:armv7m + + # Triple clean will remove everything including the common configurations. + + ./preset_generator.py --clean --clean --clean -ln c-11 -tc gcc:linux -ln c++-17 -tc gcc:armv7m + + ᓄᓇᕗᑦ + """ + ), + ) + parser.add_argument( + "--preset-file", + type=Path, + default="CMakePresets.json", + help=textwrap.dedent( + """ + The file to read, modify, and/or write the presets to. The file should be in the format of a CMake presets + json file. + + """ + ).lstrip(), + ) + + parser.add_argument("--indent", type=int, default=4, help="The number of spaces to indent the output by.") + + parser.add_argument("--presets-version", type=int, default=7, help="The required version of the presets file.") + + parser.add_argument("--clean", action="count", help="Clean the presets file of all presets first.") + + parser.add_argument( + "--common-configuration", + action="append", + type=str, + default=["config-common"], + help="Common configuration all visible configurations inherit from.", + ) + + parser.add_argument( + "--build-targets", action="append", type=str, default=["test_all"], help="The build targets to use." + ) + + options_args = parser.add_argument_group(title="Options") + for option, config in dimensions.items(): + options_args.add_argument( + f"--{option}", f"-{config['short_name']}", type=str, action="append", help=config["help"] + ) + + return parser.parse_args() + + +def validate_json_schema(args: argparse.Namespace, presets: dict) -> bool: + """ + Validates the preset file against certain assumptions this script makes. If jsonschema and requests is available + the script will also validate the file against the CMake presets schema pulled from gihub. + """ + try: + import jsonschema # pylint: disable=import-outside-toplevel + import urllib.request # pylint: disable=import-outside-toplevel + + schema_url = "https://raw.githubusercontent.com/Kitware/CMake/master/Help/manual/presets/schema.json" + with urllib.request.urlopen(schema_url, timeout=10) as response: + schema = json.loads(response.read().decode()) + + try: + jsonschema.validate(instance=presets, schema=schema) + except jsonschema.ValidationError as e: + print(f"JSON schema validation error: {e.message}") + return False + + except ImportError: + if not getattr(args, "validate_json_schema_warn_once", False): + print("jsonschema is not available. Skipping schema validation.") + args.validate_json_schema_warn_once = True + + if "version" not in presets or presets["version"] != args.presets_version: + print("The version field is missing from the presets file.") + return False + if "configurePresets" not in presets: + print("The configurePresets field is missing from the presets file.") + return False + for common_config in args.common_configuration: + configure_presets = presets["configurePresets"] + if not any(preset["name"] == common_config for preset in configure_presets): + print(f"Common configuration '{common_config}' not found in the configure presets.") + return False + return True + + +def find_configuration_types(args: argparse.Namespace, hidden_presets: list[dict]) -> list[str]: + """ + Find the semi-colon delinated CMAKE_CONFIGURATION_TYPES types in the common configurations. + + @param args: The parsed command line arguments. + @param hidden_presets: The hidden configure presets. + @return: A list of the CMAKE_CONFIGURATION_TYPES found in the common configurations, if any. + @raises ValueError: If multiple common configurations have different CMAKE_CONFIGURATION_TYPES or if no + MAKE_CONFIGURATION_TYPES were found and CMAKE_DEFAULT_BUILD_TYPE is not set. + """ + configuration_types: set[str] = set() + default_build_types: set[str] = set() + + for preset in hidden_presets: + if preset["name"] in args.common_configuration: + cache_variables = preset.get("cacheVariables", {}) + config_types = cache_variables.get("CMAKE_CONFIGURATION_TYPES") + if config_types: + config_types_set = set(config_types.split(";")) + if configuration_types and configuration_types != config_types_set: + raise ValueError("Multiple common configurations have different CMAKE_CONFIGURATION_TYPES.") + configuration_types = config_types_set + default_build_type = cache_variables.get("CMAKE_DEFAULT_BUILD_TYPE") + if default_build_type: + default_build_types.add(default_build_type) + + if len(default_build_types) > 1: + raise ValueError("Multiple common configurations have different CMAKE_DEFAULT_BUILD_TYPE values.") + + if not configuration_types and not default_build_types: + raise ValueError( + "No CMAKE_CONFIGURATION_TYPES found in common configurations and CMAKE_DEFAULT_BUILD_TYPE is not set." + ) + + return list(configuration_types) if configuration_types else list(default_build_types) + + +def update_hidden_configure_presets(args: argparse.Namespace, configure_presets: dict) -> list[dict]: + """ + Update the hidden configure presets based on the arguments given to the script. + + @return: The updated hidden configure presets merged from the given presets and the arguments. + """ + configure_hidden_presets_index = { + d["name"].lower(): d for d in filter(lambda x: "hidden" in x and x["hidden"], configure_presets) + } + + for argument_name, aspect_template in dimensions.items(): + parameters = getattr(args, argument_name.replace("-", "_")) + if parameters: + for parameter in parameters: + unsplit_name = parameter.replace(aspect_template["split"], "-") + preset_name = f"config-{argument_name}-{unsplit_name}" + configure_preset = {"name": preset_name, "hidden": True} + for key, value_template in aspect_template["values"].items(): + if callable(value_template): + configure_preset[key] = value_template(parameter) + elif isinstance(value_template, dict): + configure_preset[key] = {k: v.format(parameter) for k, v in value_template.items()} + elif isinstance(value_template, str): + configure_preset[key] = value_template.format(parameter) # pylint: disable=no-member + else: + raise ValueError(f"Unsupported value template type: {type(value_template)}") + if preset_name in configure_hidden_presets_index: + configure_hidden_presets_index[preset_name].update(configure_preset) + else: + configure_hidden_presets_index[preset_name] = configure_preset + + return list(configure_hidden_presets_index.values()) + + +def generate_visible_configure_presets(args: argparse.Namespace, hidden_configure_presets: list[dict]) -> list[dict]: + """ + Generate visible configure presets based on the hidden configure presets. + + @return: The visible configure presets as generated by calculating the cartisian product of the hidden configure + presets. + """ + configurations_index: dict[str, list[tuple]] = {} + + # investigate the hidden presets to see what configurations are available + for dimension in dimensions: + prefix = f"config-{dimension}-" + configurations: list[tuple[str, str]] = configurations_index.get(dimension, []) + configurations += [ + (x["name"], x["name"].replace(prefix, "")) for x in hidden_configure_presets if x["name"].startswith(prefix) + ] + configurations_index[dimension] = configurations + + # generate all permutations of the hidden configurations as visible configurations + visible_configure_presets: list[dict] = [] + product = itertools.product(*configurations_index.values()) + for configuration in product: + config_name = functools.reduce(lambda x, y: f"{x}-{y[1]}", configuration, "config") + inherited = args.common_configuration + [x[0] for x in configuration] + visible_configure_presets.append({"name": config_name, "inherits": inherited}) + + return visible_configure_presets + + +def generate_build_presets( + args: argparse.Namespace, configurations: list[str], visible_configure_presets: list[dict] +) -> list[dict]: + """ + Generate build presets based on the visible configure presets. + + @return: A list of build presets, one for each visible configure preset. + """ + build_presets = [] + + for configuration in configurations: + for preset in visible_configure_presets: + build_presets.append( + { + "name": f"build-{preset['name'].replace('config-', '')}-{configuration.lower()}", + "configurePreset": preset["name"], + "configuration": configuration, + "targets": args.build_targets, + } + ) + return build_presets + + +def main() -> int: + """ + Idempotent (mostly) generation of CMake presets based on the given options and the contents of the given presets + file. + """ + args = parse_arguments() + + with args.preset_file.open("r", encoding="UTF-8") as f: + json_presets = json.loads(f.read()) + + if not validate_json_schema(args, json_presets): + return 1 + + if args.clean >= 1: + # clean level 1 deletes all existing buildPresets + json_presets["buildPresets"] = [] + + if args.clean == 2: + # clean level 2 deletes all existing configurePresets except for the common configurations + json_presets["configurePresets"] = [ + preset for preset in json_presets["configurePresets"] if preset["name"] in args.common_configuration + ] + elif args.clean >= 3: + # clean level 3 deletes all existing configurePresets + json_presets["configurePresets"] = [] + + hidden_presets = update_hidden_configure_presets(args, json_presets["configurePresets"]) + visible_presets = generate_visible_configure_presets(args, hidden_presets) + json_presets["configurePresets"] = hidden_presets + visible_presets + + json_presets["buildPresets"] = generate_build_presets( + args, find_configuration_types(args, hidden_presets), visible_presets + ) + + if not validate_json_schema(args, json_presets): + return 1 + + with args.preset_file.open("w", encoding="UTF-8") as f: + f.write(json.dumps(json_presets, indent=args.indent)) + f.write("\n") + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/version_check_nunavut.py b/version_check_nunavut.py new file mode 100755 index 00000000..4ea6c42c --- /dev/null +++ b/version_check_nunavut.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 +# +# Copyright (C) OpenCyphal Development Team +# Copyright Amazon.com Inc. or its affiliates. +# SPDX-License-Identifier: MIT +# +""" + Helper for getting and comparing the version of Nunavut. +""" + +import argparse +import functools +import logging +import pathlib +import sys +import textwrap +import typing + + +def _make_parser() -> argparse.ArgumentParser: + + script = pathlib.Path(__file__).relative_to(pathlib.Path.cwd()) + + epilog = textwrap.dedent( + f""" + + **Example Usage**:: + + {script} --version-only + + """ + ) + + parser = argparse.ArgumentParser( + description="CMake command-line helper for running verification builds.", + epilog=epilog, + formatter_class=argparse.RawTextHelpFormatter, + ) + + parser.add_argument( + "--version-only", + action="store_true", + help=textwrap.dedent( + f""" + Print out the version number (stored in src/nunavut/_version.py) only and exit. This number + will be the only output to stdout allowing build scripts to extract this string value for + use in the build environment. For example: + + export NUNAVUT_FULL_VERSION=$({script} --version-only) + + """[ + 1: + ] + ), + ) + + parser.add_argument( + "--major-minor-version-only", + action="store_true", + help=textwrap.dedent( + f""" + Print out the major and minor version number (stored in src/nunavut/_version.py) only and exit. + This number will be the only output to stdout allowing build scripts to extract this string + value for use in the build environment. For example: + + export NUNAVUT_MAJOR_MINOR_VERSION=$({script} --major-minor-version-only) + + """[ + 1: + ] + ), + ) + + parser.add_argument( + "--version-check", + help=textwrap.dedent( + f""" + Compares a given semantic version number with the current Nunavut version + (stored in src/nunavut/_version.py) and returns 0 if it matches else returns 1. + + if $({script} --version-check 1.0.2); then echo "match"; fi + + """[ + 1: + ] + ), + ) + + parser.add_argument("-v", "--verbose", action="count", default=0, help="Set output verbosity.") + + return parser + + +@functools.lru_cache(maxsize=None) +def _get_version_string() -> typing.Tuple[str, str, str, str]: + version: typing.Dict[str, str] = {} + nunavut_version_file = pathlib.Path("src/nunavut/_version.py") + + with nunavut_version_file.open("r", encoding="UTF-8") as version_py: + exec(version_py.read(), version) # pylint: disable=exec-used + + version_string = version["__version__"] + version_array = version_string.split(".") + if len(version_array) not in (3, 4): + raise RuntimeError(f"Invalid version string: {version_string}") + if len(version_array) == 3: + return (version_array[0], version_array[1], version_array[2], "") + else: + return (version_array[0], version_array[1], version_array[2], version_array[3]) + + +def main() -> int: + """ + Main method to execute when this package/script is invoked as a command. + """ + args = _make_parser().parse_args() + + if args.version_only: + sys.stdout.write(".".join(_get_version_string())) + sys.stdout.flush() + return 0 + + if args.major_minor_version_only: + version = _get_version_string() + sys.stdout.write(f"{version[0]}.{version[1]}") + sys.stdout.flush() + return 0 + + logging_level = logging.WARN + + if args.verbose == 1: + logging_level = logging.INFO + elif args.verbose > 1: + logging_level = logging.DEBUG + + logging.basicConfig(format="%(levelname)s: %(message)s", level=logging_level) + + version_as_string = ".".join(_get_version_string()) + + logging.debug( + "Comparing nunavut version %s to provided version %s (%s)", + version_as_string, + args.version_check, + "matches" if (version_as_string == args.version_check) else "no-match", + ) + + return 0 if (version_as_string == args.version_check) else 1 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/pydsdl_version_check.py b/version_check_pydsdl.py similarity index 100% rename from pydsdl_version_check.py rename to version_check_pydsdl.py