1#
2# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
3#
4# SPDX-License-Identifier: BSD-2-Clause
5#
6
7include_guard(GLOBAL)
8
9# set up some paths
10set(GENERATOR_PATH ${CMAKE_BINARY_DIR}/nanopb/generator)
11set(NANOPB_GENERATOR_EXECUTABLE ${GENERATOR_PATH}/nanopb_generator.py)
12set(GENERATOR_CORE_DIR ${GENERATOR_PATH}/proto)
13set(GENERATOR_CORE_SRC ${GENERATOR_CORE_DIR}/nanopb.proto)
14
15# figure out where nanopb is
16if(NOT DEFINED NANOPB_SRC_ROOT_FOLDER)
17    find_file(NANOPB_SRC_ROOT_FOLDER nanopb PATHS ${CMAKE_SOURCE_DIR} NO_CMAKE_FIND_ROOT_PATH)
18endif()
19mark_as_advanced(FORCE NANOPB_SRC_ROOT_FOLDER)
20if("${NANOPB_SRC_ROOT_FOLDER}" STREQUAL "NANOPB_SRC_ROOT_FOLDER-NOTFOUND")
21    message(
22        FATAL_ERROR "Failed to find nanopb. Consider cmake -DNANOPB_SRC_ROOT_FOLDER=/path/to/nanopb"
23    )
24endif()
25
26# include the nanopb cmake stuff - portions of it have been forked to this file,
27# but much of it is still useful.
28list(APPEND CMAKE_MODULE_PATH "${NANOPB_SRC_ROOT_FOLDER}/extra/")
29find_package(Nanopb REQUIRED)
30
31# generate nanopb runtime library
32file(GLOB nanopb_src ${NANOPB_SRC_ROOT_FOLDER}/*.h ${NANOPB_SRC_ROOT_FOLDER}/*.c)
33
34add_library(nanopb STATIC EXCLUDE_FROM_ALL ${nanopb_src})
35target_include_directories(nanopb PUBLIC ${NANOPB_SRC_ROOT_FOLDER})
36target_link_libraries(nanopb muslc)
37
38# Treat the source diretory as immutable.
39#
40# Copy the generator directory to the build directory before
41# compiling python and proto files.
42add_custom_command(
43    TARGET nanopb
44    COMMAND
45        ${CMAKE_COMMAND} -E copy_directory
46        ARGS
47            ${NANOPB_GENERATOR_SOURCE_DIR}
48            ${GENERATOR_PATH}
49            BYPRODUCTS
50            ${NANOPB_GENERATOR_EXECUTABLE}
51            ${GENERATOR_CORE_SRC}
52    VERBATIM
53)
54
55# This is a fork of nanopb's NANOPB_GENERATE_CPP function
56# for seL4
57function(SEL4_GENERATE_PROTOBUF SRCS HDRS)
58    cmake_parse_arguments(NANOPB_GENERATE_CPP "" "RELPATH" "" ${ARGN})
59    if(NOT NANOPB_GENERATE_CPP_UNPARSED_ARGUMENTS)
60        return()
61    endif()
62
63    set(GENERATOR_PATH ${CMAKE_BINARY_DIR}/nanopb/generator)
64    set(NANOPB_GENERATOR_EXECUTABLE ${GENERATOR_PATH}/nanopb_generator.py)
65
66    set(GENERATOR_CORE_DIR ${GENERATOR_PATH}/proto)
67    set(GENERATOR_CORE_SRC ${GENERATOR_CORE_DIR}/nanopb.proto)
68
69    set(_nanopb_include_path "-I${NANOPB_SRC_ROOT_FOLDER}/src")
70    if(DEFINED NANOPB_IMPORT_DIRS)
71        foreach(DIR ${NANOPB_IMPORT_DIRS})
72            get_filename_component(ABS_PATH ${DIR} ABSOLUTE)
73            list(APPEND _nanopb_include_path "-I${ABS_PATH}")
74        endforeach()
75    endif()
76    list(REMOVE_DUPLICATES _nanopb_include_path)
77
78    if(NANOPB_GENERATE_CPP_APPEND_PATH)
79        # Create an include path for each file specified
80        foreach(FIL ${NANOPB_GENERATE_CPP_UNPARSED_ARGUMENTS})
81            get_filename_component(ABS_FIL ${FIL} ABSOLUTE)
82            get_filename_component(ABS_PATH ${ABS_FIL} PATH)
83            list(APPEND _nanopb_include_path "-I${ABS_PATH}")
84        endforeach()
85    else()
86        set(_nanopb_include_path "-I${CMAKE_CURRENT_SOURCE_DIR}")
87    endif()
88
89    if(NANOPB_GENERATE_CPP_RELPATH)
90        list(APPEND _nanopb_include_path "-I${NANOPB_GENERATE_CPP_RELPATH}")
91    endif()
92
93    if(DEFINED NANOPB_IMPORT_DIRS)
94        foreach(DIR ${NANOPB_IMPORT_DIRS})
95            get_filename_component(ABS_PATH ${DIR} ABSOLUTE)
96            list(APPEND _nanopb_include_path "-I${ABS_PATH}")
97        endforeach()
98    endif()
99
100    list(REMOVE_DUPLICATES _nanopb_include_path)
101
102    set(NANOPB_GENERATOR_PLUGIN ${GENERATOR_PATH}/protoc-gen-nanopb)
103
104    if(NANOPB_GENERATE_CPP_RELPATH)
105        get_filename_component(ABS_ROOT ${NANOPB_GENERATE_CPP_RELPATH} ABSOLUTE)
106    endif()
107    foreach(FIL ${NANOPB_GENERATE_CPP_UNPARSED_ARGUMENTS})
108        get_filename_component(ABS_FIL ${FIL} ABSOLUTE)
109        get_filename_component(FIL_WE ${FIL} NAME_WE)
110        get_filename_component(FIL_DIR ${FIL} PATH)
111        set(FIL_PATH_REL)
112        if(ABS_ROOT)
113            # Check that the file is under the given "RELPATH"
114            string(FIND ${ABS_FIL} ${ABS_ROOT} LOC)
115            if(${LOC} EQUAL 0)
116                string(
117                    REPLACE
118                        "${ABS_ROOT}/"
119                        ""
120                        FIL_REL
121                        ${ABS_FIL}
122                )
123                get_filename_component(FIL_PATH_REL ${FIL_REL} PATH)
124                file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${FIL_PATH_REL})
125            endif()
126        endif()
127        if(NOT FIL_PATH_REL)
128            set(FIL_PATH_REL ".")
129        endif()
130
131        list(APPEND ${SRCS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_PATH_REL}/${FIL_WE}.pb.c")
132        list(APPEND ${HDRS} "${CMAKE_CURRENT_BINARY_DIR}/${FIL_PATH_REL}/${FIL_WE}.pb.h")
133
134        set(NANOPB_PLUGIN_OPTIONS)
135        set(NANOPB_OPTIONS_DIRS)
136
137        # If there an options file in the same working directory, set it as a dependency
138        set(NANOPB_OPTIONS_FILE ${FIL_DIR}/${FIL_WE}.options)
139        if(EXISTS ${NANOPB_OPTIONS_FILE})
140            # Get directory as lookups for dependency options fail if an options
141            # file is used. The options is still set as a dependency of the
142            # generated source and header.
143            get_filename_component(options_dir ${NANOPB_OPTIONS_FILE} DIRECTORY)
144            list(APPEND NANOPB_OPTIONS_DIRS ${options_dir})
145        else()
146            set(NANOPB_OPTIONS_FILE)
147        endif()
148
149        # If the dependencies are options files, we need to pass the directories
150        # as arguments to nanopb
151        foreach(depends_file ${NANOPB_DEPENDS})
152            get_filename_component(ext ${depends_file} EXT)
153            if(ext STREQUAL ".options")
154                get_filename_component(depends_dir ${depends_file} DIRECTORY)
155                list(APPEND NANOPB_OPTIONS_DIRS ${depends_dir})
156            endif()
157        endforeach()
158
159        if(NANOPB_OPTIONS_DIRS)
160            list(REMOVE_DUPLICATES NANOPB_OPTIONS_DIRS)
161        endif()
162
163        foreach(options_path ${NANOPB_OPTIONS_DIRS})
164            set(NANOPB_PLUGIN_OPTIONS "${NANOPB_PLUGIN_OPTIONS} -I${options_path}")
165        endforeach()
166
167        if(NANOPB_OPTIONS)
168            set(NANOPB_PLUGIN_OPTIONS "${NANOPB_PLUGIN_OPTIONS} ${NANOPB_OPTIONS}")
169        endif()
170
171        add_custom_command(
172            OUTPUT
173                "${CMAKE_CURRENT_BINARY_DIR}/${FIL_PATH_REL}/${FIL_WE}.pb.c"
174                "${CMAKE_CURRENT_BINARY_DIR}/${FIL_PATH_REL}/${FIL_WE}.pb.h"
175            COMMAND
176                ${PROTOBUF_PROTOC_EXECUTABLE}
177                ARGS
178                    -I${GENERATOR_PATH}
179                    -I${GENERATOR_CORE_DIR}
180                    -I${CMAKE_CURRENT_BINARY_DIR}
181                    ${_nanopb_include_path}
182                --plugin=protoc-gen-nanopb=${NANOPB_GENERATOR_PLUGIN}
183                    "--nanopb_out=${NANOPB_PLUGIN_OPTIONS}:${CMAKE_CURRENT_BINARY_DIR}" ${ABS_FIL}
184            DEPENDS
185                ${ABS_FIL}
186                ${GENERATOR_CORE_SRC}
187                ${GENERATOR_CORE_PYTHON_SRC}
188                ${NANOPB_OPTIONS_FILE}
189                ${NANOPB_DEPENDS}
190            COMMENT "Running C++ protocol buffer compiler using nanopb plugin on ${FIL}"
191            VERBATIM
192        )
193
194    endforeach()
195
196    set_source_files_properties(${${SRCS}} ${${HDRS}} PROPERTIES GENERATED TRUE)
197    set(${SRCS} ${${SRCS}} ${NANOPB_SRCS} PARENT_SCOPE)
198    set(${HDRS} ${${HDRS}} ${NANOPB_HDRS} PARENT_SCOPE)
199endfunction()
200