# # Copyright (c) 2020 Nordic Semiconductor # # SPDX-License-Identifier: LicenseRef-BSD-5-Clause-Nordic # # The Gadgets Profile relies on the nanopb generator tool, which must be # available at the configuration stage. # This tool is used to compile the alexa .proto files at build time. # Output from the .proto compilation is a set of .c and .h files. # Note: as there doesn't seem to be an easy way of checking the version of the nanopb generator (protoc-gen-nanopb binary) # there is the possibility of a version mismatch between the generator and the nanopb source files that is used in the firmware sample. # If there is a mismatch, there will be a compilation error in the build step. # The easiest way to fix such an error is to download the appropriate nanopb generator .zip and extract it to this sample folder. # (https://jpa.kapsi.fi/nanopb/download/) # Check system for protoc and nanopb binaries presence. # Prefer local variable to make version matching easier find_program(PROTOC_BIN protoc HINTS "${PROJECT_SOURCE_DIR}/*/generator-bin/") find_program(NANOPB_BIN protoc-gen-nanopb HINTS "${PROJECT_SOURCE_DIR}/*/generator-bin/") if ((NOT NANOPB_BIN) OR (NOT PROTOC_BIN)) message(STATUS "nanopb not found.") message(STATUS "Please download platform-specific nanopb from here https://jpa.kapsi.fi/nanopb/download/") message(STATUS "Extract the files to `${PROJECT_SOURCE_DIR}`, or add `/nanopb-x.y.z-/generator-bin` to system PATH.") message(STATUS "Alternatively, specify install location using: `-DCMAKE_PROGRAM_PATH=/nanopb-x.y.z-/generator-bin` when running CMake.") message(FATAL_ERROR "nanopb required by this project.") endif() # Locate alexa-embedded and nanopb in local ncs tree if(ALEXA_EMBEDDED_PATH) # Do nothing, just use the provided path elseif(WEST) ## Use Python and west to find the alexa-embedded path execute_process( COMMAND ${PYTHON_EXECUTABLE} -c "from west.manifest import Manifest; manifest = Manifest.from_file(); print(manifest.get_projects(['Alexa-Gadgets-Embedded-Sample-Code'])[0].posixpath)" OUTPUT_VARIABLE west_project_output RESULT_VARIABLE result ) string(REGEX REPLACE "[\r\n]+" "" ALEXA_EMBEDDED_PATH "${west_project_output}") if(${result}) message(FATAL_ERROR "Failed to find alexa-embedded, cannot build sample") endif() else() message(FATAL_ERROR "west not installed, please provide ALEXA_EMBEDDED_PATH to CMake to build this sample") endif() if(NANOPB_PATH) # Do nothing, just use the provided path elseif(WEST) ## Use Python and west to find the nanopb path execute_process( COMMAND ${PYTHON_EXECUTABLE} -c "from west.manifest import Manifest; manifest = Manifest.from_file(); print(manifest.get_projects(['nanopb'])[0].posixpath)" OUTPUT_VARIABLE west_project_output RESULT_VARIABLE result ) string(REGEX REPLACE "[\r\n]+" "" NANOPB_PATH "${west_project_output}") if(${result}) message(FATAL_ERROR "Failed to find nanopb source files, cannot build sample") endif() else() message(FATAL_ERROR "west not installed, please provide NANOPB_PATH to CMake to build this sample") endif() # Protobuf generation setup set(PROTO_COMPILATION_DIR ${CMAKE_BINARY_DIR}/generated/alexa/protobuf) # Populate lists of .proto files that should be compiled at build time set(BASE_PROTO_FILES ${ALEXA_EMBEDDED_PATH}/ConnectionHelpers/BLE/Proto/accessories.proto ${ALEXA_EMBEDDED_PATH}/ConnectionHelpers/BLE/Proto/common.proto ${ALEXA_EMBEDDED_PATH}/ConnectionHelpers/BLE/Proto/device.proto ${ALEXA_EMBEDDED_PATH}/ConnectionHelpers/BLE/Proto/firmware.proto ${ALEXA_EMBEDDED_PATH}/AlexaGadgetsProtobuf/parser/directiveParser.proto ${ALEXA_EMBEDDED_PATH}/AlexaGadgetsProtobuf/common/directiveHeader.proto ${ALEXA_EMBEDDED_PATH}/AlexaGadgetsProtobuf/parser/eventParser.proto ${ALEXA_EMBEDDED_PATH}/AlexaGadgetsProtobuf/common/eventHeader.proto ${ALEXA_EMBEDDED_PATH}/AlexaGadgetsProtobuf/Alexa.Discovery/Discover/alexaDiscoveryDiscoverDirective.proto ${ALEXA_EMBEDDED_PATH}/AlexaGadgetsProtobuf/Alexa.Discovery/Discover/alexaDiscoveryDiscoverDirectivePayload.proto ${ALEXA_EMBEDDED_PATH}/AlexaGadgetsProtobuf/Alexa.Discovery/Discover.Response/alexaDiscoveryDiscoverResponseEvent.proto ${ALEXA_EMBEDDED_PATH}/AlexaGadgetsProtobuf/Alexa.Discovery/Discover.Response/alexaDiscoveryDiscoverResponseEventPayload.proto ) set(NOTIFICATIONS_PROTO_FILES ${ALEXA_EMBEDDED_PATH}/AlexaGadgetsProtobuf/Notifications/SetIndicator/notificationsSetIndicatorDirective.proto ${ALEXA_EMBEDDED_PATH}/AlexaGadgetsProtobuf/Notifications/SetIndicator/notificationsSetIndicatorDirectivePayload.proto ${ALEXA_EMBEDDED_PATH}/AlexaGadgetsProtobuf/Notifications/ClearIndicator/notificationsClearIndicatorDirective.proto ${ALEXA_EMBEDDED_PATH}/AlexaGadgetsProtobuf/Notifications/ClearIndicator/notificationsClearIndicatorDirectivePayload.proto ) set(ALERTS_PROTO_FILES ${ALEXA_EMBEDDED_PATH}/AlexaGadgetsProtobuf/Alerts/SetAlert/alertsSetAlertDirective.proto ${ALEXA_EMBEDDED_PATH}/AlexaGadgetsProtobuf/Alerts/SetAlert/alertsSetAlertDirectivePayload.proto ${ALEXA_EMBEDDED_PATH}/AlexaGadgetsProtobuf/Alerts/DeleteAlert/alertsDeleteAlertDirective.proto ${ALEXA_EMBEDDED_PATH}/AlexaGadgetsProtobuf/Alerts/DeleteAlert/alertsDeleteAlertDirectivePayload.proto ) set(STATELISTENER_PROTO_FILES ${ALEXA_EMBEDDED_PATH}/AlexaGadgetsProtobuf/Alexa.Gadget.StateListener/StateUpdate/alexaGadgetStateListenerStateUpdateDirective.proto ${ALEXA_EMBEDDED_PATH}/AlexaGadgetsProtobuf/Alexa.Gadget.StateListener/StateUpdate/alexaGadgetStateListenerStateUpdateDirectivePayload.proto ) set(SPEECHDATA_PROTO_FILES ${ALEXA_EMBEDDED_PATH}/AlexaGadgetsProtobuf/Alexa.Gadget.SpeechData/Speechmarks/alexaGadgetSpeechDataSpeechmarksDirective.proto ${ALEXA_EMBEDDED_PATH}/AlexaGadgetsProtobuf/Alexa.Gadget.SpeechData/Speechmarks/alexaGadgetSpeechDataSpeechmarksDirectivePayload.proto ) set(MUSICDATA_PROTO_FILES ${ALEXA_EMBEDDED_PATH}/AlexaGadgetsProtobuf/Alexa.Gadget.MusicData/Tempo/alexaGadgetMusicDataTempoDirective.proto ${ALEXA_EMBEDDED_PATH}/AlexaGadgetsProtobuf/Alexa.Gadget.MusicData/Tempo/alexaGadgetMusicDataTempoDirectivePayload.proto ) set(CUSTOM_PROTO_FILES ${CMAKE_CURRENT_SOURCE_DIR}/proto/custom_event.proto ) foreach(GROUP BASE NOTIFICATIONS ALERTS STATELISTENER SPEECHDATA MUSICDATA CUSTOM) # Populate list of expected .c files output from protobuf generation foreach(file ${${GROUP}_PROTO_FILES}) get_filename_component(FILE_DIR ${file} DIRECTORY) get_filename_component(FILE_BASE_NAME ${file} NAME_WE) list(APPEND PROTOC_INC_DIRS "-I.") list(APPEND PROTOC_INC_DIRS "-I${FILE_DIR}") list(APPEND ${GROUP}_PROTO_OUTPUTS "${PROTO_COMPILATION_DIR}/${FILE_BASE_NAME}.pb.c") list(APPEND ${GROUP}_PROTO_DEPENDENCIES "${file}") endforeach() # Each .pb.c file to be compiled by protoc gets its own CMake custom command, # with dependencies to all other .proto files in its group and the base files. foreach(file ${${GROUP}_PROTO_FILES}) get_filename_component(FILE_DIR ${file} DIRECTORY) get_filename_component(FILE_NAME ${file} NAME) get_filename_component(FILE_BASE_NAME ${file} NAME_WE) set(PROTOC_ARGS "-I." "${PROTOC_INC_DIRS}" "--nanopb_out=${PROTO_COMPILATION_DIR}" "${FILE_NAME}" ) add_custom_command( OUTPUT "${PROTO_COMPILATION_DIR}/${FILE_BASE_NAME}.pb.c" COMMAND ${PROTOC_BIN} ${PROTOC_ARGS} WORKING_DIRECTORY ${FILE_DIR} MAIN_DEPENDENCY ${file} DEPENDS ${${GROUP}_PROTO_DEPENDENCIES} ${BASE_PROTO_DEPENDENCIES} ) endforeach() endforeach() zephyr_library_named(ALEXA_GADGETS_PROFILE) zephyr_include_directories(${CMAKE_CURRENT_SOURCE_DIR}) zephyr_include_directories(${PROTO_COMPILATION_DIR}) zephyr_include_directories(${NANOPB_PATH}) zephyr_library_sources_ifdef(CONFIG_GADGETS_PROFILE_ENABLE ${CMAKE_CURRENT_SOURCE_DIR}/gadgets_profile.c ${NANOPB_PATH}/pb_common.c ${NANOPB_PATH}/pb_encode.c ${NANOPB_PATH}/pb_decode.c ${BASE_PROTO_OUTPUTS} ) zephyr_library_sources_ifdef(CONFIG_GADGETS_CAPABILITY_NOTIFICATIONS_ENABLE ${NOTIFICATIONS_PROTO_OUTPUTS} ) zephyr_library_sources_ifdef(CONFIG_GADGETS_CAPABILITY_ALERTS_ENABLE ${ALERTS_PROTO_OUTPUTS} ) zephyr_library_sources_ifdef(CONFIG_GADGETS_CAPABILITY_STATELISTENER_ENABLE ${STATELISTENER_PROTO_OUTPUTS} ) zephyr_library_sources_ifdef(CONFIG_GADGETS_CAPABILITY_SPEECHDATA_ENABLE ${SPEECHDATA_PROTO_OUTPUTS} ) zephyr_library_sources_ifdef(CONFIG_GADGETS_CAPABILITY_MUSICDATA_ENABLE ${MUSICDATA_PROTO_OUTPUTS} ) zephyr_library_sources_ifdef(CONFIG_GADGETS_CAPABILITY_CUSTOM_ENABLE ${CUSTOM_PROTO_OUTPUTS} )