nnvg

Usage

Generate code from UAVCAN DSDL using pydsdl and jinja2

usage: nnvg [-h] [--lookup-dir LOOKUP_DIR] [--verbose] [--version]
            [--outdir OUTDIR] --templates TEMPLATES --output-extension
            OUTPUT_EXTENSION [--dry-run] [--list-outputs] [--list-inputs]
            [--generate-namespace-types]
            [--namespace-output-stem NAMESPACE_OUTPUT_STEM] [--no-overwrite]
            [--file-mode FILE_MODE] [--trim-blocks] [--lstrip-blocks]
            [--pp-max-emptylines PP_MAX_EMPTYLINES]
            [--pp-trim-trailing-whitespace] [-pp-rp PP_RUN_PROGRAM]
            [-pp-rpa PP_RUN_PROGRAM_ARG]
            root_namespace

Positional Arguments

root_namespace A source directory with DSDL definitions.

Named Arguments

--lookup-dir, -I
 

List of other namespace directories containing data type definitions that are referred to from the target root namespace. For example, if you are reading a vendor-specific namespace, the list of lookup directories should always include a path to the standard root namespace “uavcan”, otherwise the types defined in the vendor-specific namespace won’t be able to use data types from the standard namespace.

Additional directories can also be specified through the environment variable UAVCAN_DSDL_INCLUDE_PATH, where the path entries are separated by colons “:”

Default: []

--verbose, -v verbosity level (-v, -vv)
--version show program’s version number and exit
--outdir, -O

output directory

Default: “nunavut_out”

--templates A path to a directory containing templates to use when generating code.
--output-extension, -e
 

The extension to use for generated files.

Default: “”

--dry-run, -d

If True then no files will be generated.

Default: False

--list-outputs

Emit a semicolon-separated list of files. (implies –dry-run) Emits files that would be generated if invoked without –dry-run. This command is useful for integrating with CMake and other build systems that need a list of targets to determine if a rebuild is necessary.

Default: False

--list-inputs

Emit a semicolon-separated list of files. (implies –dry-run) A list of files that are resolved given input arguments like templates. This command is useful for integrating with CMake and other build systems that need a list of inputs to determine if a rebuild is necessary.

Default: False

--generate-namespace-types
 

If enabled this script will generate source for namespaces. All namespaces including and under the root namespace will be treated as a pseudo-type and the appropriate template will be used. The generator will first look for a template with the stem “Namespace” and will then use the “Any” template if that is available. The name of the output file will be the default value for the –namespace-output-stem argument and can be changed using that argument.

Default: False

--namespace-output-stem
 

The name of the file generated when –generate-namespace-types is provided.

Default: “Namespace”

--no-overwrite

By default, generated files will be silently overwritten by subsequent invocations of the generator. If this argument is specified an error will be raised instead preventing overwrites.

Default: False

--file-mode

The filemode each generated file is set to after it is created. Note that this value is interpreted using python auto base detection. Because of this, to provide an octal value, you’ll need to prefix your literal with ‘0o’ (e.g. –file-mode 0o664).

Default: 292

--trim-blocks

If this is set to True the first newline after a block in a template is removed (block, not variable tag!).

Default: False

--lstrip-blocks
 

If this is set to True leading spaces and tabs are stripped from the start of a line to a block in templates.

Default: False

--pp-max-emptylines
 

If provided this will suppress generation of additional consecutive empty lines beyond the limit set by this argument.

Note that this will insert a line post-processor which may reduce performance. Consider using a code formatter on the generated output to enforce whitespace rules instead.

--pp-trim-trailing-whitespace
 

Enables a line post-processor that will elide all whitespace at the end of each line.

Note that this will insert a line post-processor which may reduce performance. Consider using a code formatter on the generated output to enforce whitespace rules instead.

Default: False

-pp-rp, --pp-run-program
 

Runs a program after each file is generated but before the file is set to read-only.

example

# invokes clang-format with the "in-place" argument on each file after it is
# generated.

nnvg --outdir include --templates c_jinja -e .h -pp-rp clang-format -pp-rpa=-i dsdl
-pp-rpa, --pp-run-program-arg
 Additional arguments to provide to the program specified by –pp-run-program. The last argument will always be the path to the generated file.

Example Usage:

# This would include j2 templates for a folder named 'c_jinja'
# and generate .h files into a directory named 'include' using
# dsdl root namespaces found under a folder named 'dsdl'.

nnvg --outdir include --templates c_jinja -e .h dsdl

CMake Integration

The following cmake function demonstrates how to integrate pydsdelgen with a cmake build system:

#
# :function: create_dsdl_target
# Creates a target that will generate source code from dsdl definitions.
#
# The source is generated to files with ${NNVG_EXTENSION} as the extension.
#
# :param str ARG_TARGET_NAME:               The name to give the target.
# :param Path ARG_OUTPUT_FOLDER:            The directory to generate all source under.
# :param Path ARG_TEMPLATES_DIR:            A directory containing the templates to use to generate the source.
# :param Path ARG_DSDL_ROOT_DIR:            A directory containing the root namespace dsdl.
# :param ...:                               A list of paths to use when looking up dependent DSDL types.
# :returns: Sets a variable "ARG_TARGET_NAME"-OUTPUT in the parent scope to the list of files the target
#           will generate. For example, if ARG_TARGET_NAME == 'foo-bar' then after calling this function
#           ${foo-bar-OUTPUT} will be set to the list of output files.
#
function (create_dsdl_target ARG_TARGET_NAME ARG_OUTPUT_FOLDER ARG_TEMPLATES_DIR ARG_DSDL_ROOT_DIR)

    set(LOOKUP_DIR_CMD_ARGS "")

    if (${ARGC} GREATER 5)
        foreach(ARG_N RANGE 5 ${ARGC}-1)
            list(APPEND LOOKUP_DIR_CMD_ARGS " -I ${ARGV${ARG_N}}")
        endforeach(ARG_N)
    endif()

    execute_process(COMMAND ${PYTHON} ${NNVG}
                                        --list-outputs
                                        --output-extension ${NNVG_EXTENSION}
                                        -O ${ARG_OUTPUT_FOLDER}
                                        ${LOOKUP_DIR_CMD_ARGS}
                                        ${ARG_DSDL_ROOT_DIR}
                    OUTPUT_VARIABLE OUTPUT_FILES
                    RESULT_VARIABLE LIST_OUTPUTS_RESULT)

    if(NOT LIST_OUTPUTS_RESULT EQUAL 0)
        message(FATAL_ERROR "Failed to retrieve a list of headers nnvg would "
                            "generate for the ${ARG_TARGET_NAME} target (${LIST_OUTPUTS_RESULT})"
                            " (${PYTHON} ${NNVG})")
    endif()

    execute_process(COMMAND ${PYTHON} ${NNVG}
                                        --list-inputs
                                        -O ${ARG_OUTPUT_FOLDER}
                                        --templates ${ARG_TEMPLATES_DIR}
                                        ${LOOKUP_DIR_CMD_ARGS}
                                        ${ARG_DSDL_ROOT_DIR}
                    OUTPUT_VARIABLE INPUT_FILES
                    RESULT_VARIABLE LIST_INPUTS_RESULT)

    if(NOT LIST_INPUTS_RESULT EQUAL 0)
        message(FATAL_ERROR "Failed to resolve inputs using nnvg for the ${ARG_TARGET_NAME} "
                            "target (${LIST_INPUTS_RESULT})"
                            " (${PYTHON} ${NNVG})")
    endif()

    add_custom_command(OUTPUT ${OUTPUT_FILES}
                    COMMAND ${PYTHON} ${NNVG}
                                        --templates ${ARG_TEMPLATES_DIR}
                                        --output-extension ${NNVG_EXTENSION}
                                        -O ${ARG_OUTPUT_FOLDER}
                                        ${LOOKUP_DIR_CMD_ARGS}
                                        ${ARG_DSDL_ROOT_DIR}
                    DEPENDS ${INPUT_FILES}
                    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
                    COMMENT "Running nnvg")

    add_custom_target(${ARG_TARGET_NAME} ALL DEPENDS ${OUTPUT_FILES})

    set(${ARG_TARGET_NAME}-OUTPUT ${OUTPUT_FILES} PARENT_SCOPE)

endfunction(create_dsdl_target)

This will setup a target that will trigger rebuilds of ${ARG_TARGET_NAME} if any of the templates or dsdl files are modified. Unfortunately, cmake only allows for this list to be generated when the build files are being generated so you’ll need to re-run cmake if adding or removing templates or dsdl types.