#------------------------------------------------------------------------------- # Copyright (c) 2020-2023, Arm Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # #------------------------------------------------------------------------------- #[===[.rst: NanoPB integration for cmake ---------------------------- This module will: - download nanopb if not available locally - build the runtime static library and the generator - import the static library to the build - define a function to provide access to the generator Note: the python module created by the generator build will be installed under Python_SITELIB ("Third-party platform independent installation directory.") This means the build may alter the state of your system. Please use virtualenv. Note: see requirements.txt for dependencies which need to be installed before running this module. #]===] #### Get the dependency set(NANOPB_URL "https://github.com/nanopb/nanopb.git" CACHE STRING "nanopb repository URL") set(NANOPB_REFSPEC "nanopb-0.4.2" CACHE STRING "nanopb git refspec") set(NANOPB_SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/_deps/nanopb-src" CACHE PATH "nanopb source-code") set(NANOPB_INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/nanopb_install" CACHE PATH "nanopb installation directory") set(NANOPB_BUILD_TYPE "Release" CACHE STRING "nanopb build type") # Checking git find_program(GIT_COMMAND "git") if (NOT GIT_COMMAND) message(FATAL_ERROR "Please install git") endif() set(GIT_OPTIONS GIT_REPOSITORY ${NANOPB_URL} GIT_TAG ${NANOPB_REFSPEC} GIT_SHALLOW FALSE #See the .patch file for details on why it is needed. PATCH_COMMAND git stash COMMAND git apply ${CMAKE_CURRENT_LIST_DIR}/fix-pyhon-name.patch ) # Only pass libc settings to nanopb if needed. For environments where the standard # library is not overridden, this is not needed. if(TARGET stdlib::c) include(${TS_ROOT}/tools/cmake/common/PropertyCopy.cmake) # Save libc settings save_interface_target_properties(TGT stdlib::c PREFIX LIBC) # Translate libc settings to cmake code fragment. Will be inserted into # nanopb-init-cache.cmake.in when LazyFetch configures the file. translate_interface_target_properties(PREFIX LIBC RES _cmake_fragment) unset_saved_properties(LIBC) endif() include(${TS_ROOT}/tools/cmake/common/LazyFetch.cmake REQUIRED) LazyFetch_MakeAvailable(DEP_NAME nanopb FETCH_OPTIONS ${GIT_OPTIONS} INSTALL_DIR ${NANOPB_INSTALL_DIR} PACKAGE_DIR ${NANOPB_INSTALL_DIR} CACHE_FILE "${TS_ROOT}/external/nanopb/nanopb-init-cache.cmake.in" SOURCE_DIR "${NANOPB_SOURCE_DIR}" ) unset(_cmake_fragment) if(TARGET stdlib::c) target_link_libraries(nanopb::protobuf-nanopb-static INTERFACE stdlib::c) endif() #### Build access to the protobuf compiler #TODO: verify protoc dependencies: python3-protobuf find_package(Python3 REQUIRED COMPONENTS Interpreter) find_file(NANOPB_GENERATOR_PATH NAMES nanopb_generator.py PATHS ${nanopb_SOURCE_DIR}/generator DOC "nanopb protobuf compiler" NO_DEFAULT_PATH ) if (NOT NANOPB_GENERATOR_PATH) message(FATAL_ERROR "Nanopb generator was not found!") endif() #[===[.rst: .. cmake:command:: protobuf_generate .. code-block:: cmake protobuf_generate(SRC file.proto TGT foo NAMESPACE bar BASE_DIR "proto/definitions") Run the ``nanopb_generator`` to compile a protobuf definition file into C source. Generated source file will be added to the source list of ``TGT``. Protobuf compilation will take part before TGT+NAMESPACE is built. Protobuf file names added to the same TGT must not collide. Inputs: ``SRC`` Path to of the protobuf file to process. Either absolute or relative to the callers location. ``TGT`` Name of target to compile generated source files. ``NAMESPACE`` Namespace to put generated files under. Specifies include path and allows separating colliding protobuf files. ``BASE_DIR`` Base directory. Generated files are located relative to this base. #]===] function(protobuf_generate) set(_options ) set(_oneValueArgs SRC TGT NAMESPACE BASE_DIR) set(_multiValueArgs ) cmake_parse_arguments(PARAMS "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) #Verify mandatory parameters if (NOT DEFINED PARAMS_SRC) message(FATAL_ERROR "nanopb_generate(): mandatory parameter SRC missing.") endif() if (NOT DEFINED PARAMS_TGT) message(FATAL_ERROR "nanopb_generate(): mandatory parameter TGT missing.") endif() if (NOT DEFINED PARAMS_NAMESPACE) message(FATAL_ERROR "nanopb_generate(): mandatory parameter NAMESPACE missing.") endif() if (NOT DEFINED PARAMS_BASE_DIR) message(FATAL_ERROR "nanopb_generate(): mandatory parameter BASE_DIR missing.") endif() #If SRC is not absolute make it relative to the callers location. if (NOT IS_ABSOLUTE ${PARAMS_SRC}) set(PARAMS_SRC "${CMAKE_CURRENT_LIST_DIR}/${PARAMS_SRC}") endif() #Calculate the output directory set(_OUT_DIR_BASE ${CMAKE_BINARY_DIR}/src/${PARAMS_NAMESPACE}) #Calculate output file names get_filename_component(_BASENAME ${PARAMS_SRC} NAME_WE) #Get relative path or SRC to BASE_DIR file(RELATIVE_PATH _SRC_REL ${PARAMS_BASE_DIR} ${PARAMS_SRC}) get_filename_component(_OUT_DIR_REL ${_SRC_REL} DIRECTORY ) #Calculate output file paths set(_OUT_C "${_OUT_DIR_BASE}/${_OUT_DIR_REL}/${_BASENAME}.pb.c") set(_OUT_H "${_OUT_DIR_BASE}/${_OUT_DIR_REL}/${_BASENAME}.pb.h") #some helper variables for the purpose of readability set(_nanopb_target "nanopb_generate_${PARAMS_TGT}_${PARAMS_NAMESPACE}") set(_nanopb_fake_file "nanopb_generate_ff_${PARAMS_TGT}") if (NOT TARGET "${_nanopb_target}") #Tell cmake the dependency (source) file is fake. set_source_files_properties("${_nanopb_fake_file}" PROPERTIES SYMBOLIC "true") #Create a custom target which depends on a "fake" file. add_custom_target("${_nanopb_target}" DEPENDS "${_nanopb_fake_file}") #Add a custom command to the target to create output directory. add_custom_command(OUTPUT "${_nanopb_fake_file}" COMMAND ${CMAKE_COMMAND} -E make_directory ${_OUT_DIR_BASE} COMMENT "Generating source from protobuf definitions for target ${PARAMS_TGT}") #Ensure protobuf build happens before test target. add_dependencies(${PARAMS_TGT} ${_nanopb_target}) #Add include path to protobuf output. target_include_directories(${PARAMS_TGT} PRIVATE ${_OUT_DIR_BASE}) endif() get_filename_component(NANOPB_GENERATOR_DIR "${NANOPB_GENERATOR_PATH}" DIRECTORY CACHE "Location of nanopb generator.") #Append a protobuf generator command to the nanopb_generate target. add_custom_command(OUTPUT "${_OUT_C}" "${_OUT_H}" COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${NANOPB_GENERATOR_DIR} ${Python3_EXECUTABLE} ${NANOPB_GENERATOR_PATH} -I ${PARAMS_BASE_DIR} -D ${_OUT_DIR_BASE} ${_SRC_REL} DEPENDS "${PARAMS_SRC}") #Add generated file to the target set_property(SOURCE "${_OUT_C}" PROPERTY GENERATED TRUE) target_sources(${PARAMS_TGT} PRIVATE "${_OUT_C}") endfunction() #[===[.rst: .. cmake:command:: protobuf_generate_all .. code-block:: cmake protobuf_generate_all(TGT foo NAMESPACE bar BASE_DIR "proto/definitions") Generates C code from all .proto files listed in the target property PROTOBUF_FILES. Inputs: ``TGT`` Name of target to compile generated source files. ``NAMESPACE`` Namespace to put generated files under. Specifies include path and allows separating colliding protobuf files. ``BASE_DIR`` Base directory. Generated files are located relative to this base. #]===] function(protobuf_generate_all) set(_options ) set(_oneValueArgs TGT NAMESPACE BASE_DIR) set(_multiValueArgs ) cmake_parse_arguments(PARAMS "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN}) #Verify mandatory parameters if (NOT DEFINED PARAMS_TGT) message(FATAL_ERROR "nanopb_generate_all(): mandatory parameter TGT missing.") endif() if (NOT DEFINED PARAMS_NAMESPACE) message(FATAL_ERROR "nanopb_generate_all(): mandatory parameter NAMESPACE missing.") endif() if (NOT DEFINED PARAMS_BASE_DIR) message(FATAL_ERROR "nanopb_generate_all(): mandatory parameter BASE_DIR missing.") endif() get_property(_protolist TARGET ${PARAMS_TGT} PROPERTY PROTOBUF_FILES) #Build of each .proto file foreach(_file IN LISTS _protolist) protobuf_generate( TGT ${PARAMS_TGT} SRC "${_file}" NAMESPACE ${PARAMS_NAMESPACE} BASE_DIR ${PARAMS_BASE_DIR}) endforeach() endfunction()