blob: 195e741dbb137a432690415d6f818097662eaa61 [file] [log] [blame]
Julian Halldf86fce2020-11-23 18:09:55 +01001#-------------------------------------------------------------------------------
Gyorgy Szingcd3241a2021-06-30 22:41:11 +00002# Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved.
Julian Halldf86fce2020-11-23 18:09:55 +01003#
4# SPDX-License-Identifier: BSD-3-Clause
5#
6#-------------------------------------------------------------------------------
7
8#[===[.rst:
9NonoPB integration for cmake
10----------------------------
11
12This module will:
13 - download nanopb if not available locally
14 - build the runtime static library and the generator
15 - import the static library to the build
16 - define a function to provide access to the generator
17
18Note: the python module created by the generator build will be installed under
19Python_SITELIB ("Third-party platform independent installation directory.")
20This means the build may alter the state of your system. Please use virtualenv.
21
22Note: see requirements.txt for dependnecies which need to be installed before
23running this module.
24
25#]===]
26
27#### Get the dependency
28
29set(NANOPB_URL "https://github.com/nanopb/nanopb.git" CACHE STRING "nanopb repository URL")
30set(NANOPB_REFSPEC "nanopb-0.4.2" CACHE STRING "nanopb git refspec")
31set(NANOPB_INSTALL_PATH "${CMAKE_CURRENT_BINARY_DIR}/nanopb_install" CACHE PATH "nanopb installation directory")
32set(NANOPB_PACKAGE_PATH "${NANOPB_INSTALL_PATH}/libnanopb/cmake" CACHE PATH "nanopb CMake package directory")
33
34include(FetchContent)
35
36# Checking git
37find_program(GIT_COMMAND "git")
38if (NOT GIT_COMMAND)
39 message(FATAL_ERROR "Please install git")
40endif()
41
42# Fetching nanopb
43FetchContent_Declare(
44 nanopb
45 GIT_REPOSITORY ${NANOPB_URL}
46 GIT_TAG ${NANOPB_REFSPEC}
47 GIT_SHALLOW TRUE
48 #See the .patch file for details on why it is needed.
49 PATCH_COMMAND git stash
50 COMMAND git apply ${CMAKE_CURRENT_LIST_DIR}/fix-pyhon-name.patch
51)
52
53# FetchContent_GetProperties exports nanopb_SOURCE_DIR and nanopb_BINARY_DIR variables
54FetchContent_GetProperties(nanopb)
55if(NOT nanopb_POPULATED)
56 message(STATUS "Fetching nanopb")
57 FetchContent_Populate(nanopb)
58endif()
59
60#### Build the runtime and the generator.
61if( NOT CMAKE_CROSSCOMPILING)
62 execute_process(COMMAND
63 ${CMAKE_COMMAND}
64 -DBUILD_SHARED_LIBS=Off
65 -DBUILD_STATIC_LIBS=On
66 -Dnanopb_BUILD_RUNTIME=On
67 -Dnanopb_BUILD_GENERATOR=On
68 -Dnanopb_PROTOC_PATH=${nanopb_SOURCE_DIR}/generator/protoc
69 -Dnanopb_MSVC_STATIC_RUNTIME=Off
70 -DCMAKE_INSTALL_PREFIX=${NANOPB_INSTALL_PATH}
71 -DCMAKE_TOOLCHAIN_FILE=${TS_EXTERNAL_LIB_TOOLCHAIN_FILE}
72 -GUnix\ Makefiles
73 ${nanopb_SOURCE_DIR}
74 WORKING_DIRECTORY
75 ${nanopb_BINARY_DIR}
76 RESULT_VARIABLE _exec_error
77 )
78else()
79 execute_process(COMMAND
80 ${CMAKE_COMMAND}
81 -DBUILD_SHARED_LIBS=Off
82 -DBUILD_STATIC_LIBS=On
83 -Dnanopb_BUILD_RUNTIME=On
84 -Dnanopb_BUILD_GENERATOR=On
85 -Dnanopb_PROTOC_PATH=${nanopb_SOURCE_DIR}/generator/protoc
86 -Dnanopb_MSVC_STATIC_RUNTIME=Off
87 -DCMAKE_INSTALL_PREFIX=${NANOPB_INSTALL_PATH}
88 -DCMAKE_TOOLCHAIN_FILE=${TS_EXTERNAL_LIB_TOOLCHAIN_FILE}
89 -DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY
90 -GUnix\ Makefiles
91 ${nanopb_SOURCE_DIR}
92 WORKING_DIRECTORY
93 ${nanopb_BINARY_DIR}
94 RESULT_VARIABLE _exec_error
95 )
96endif()
97
98if (_exec_error)
99 message(FATAL_ERROR "Configuration step of nanopb runtime failed with ${_exec_error}.")
100endif()
101
102execute_process(COMMAND
103 ${CMAKE_COMMAND} --build ${nanopb_BINARY_DIR} -- install -j8
104 RESULT_VARIABLE _exec_error
105 )
106if (_exec_error)
107 message(FATAL_ERROR "Build step of nanopb runtime failed with ${_exec_error}.")
108endif()
109
110#### Include Nanopb runtime in the build.
111find_package(Nanopb
112 PATHS "${NANOPB_INSTALL_PATH}"
113 NO_DEFAULT_PATH
114 )
115
116#### Build access to the protobuf compiler
117#TODO: verify protoc dependencies: python3-protobuf
118find_package(Python3 COMPONENTS Interpreter)
119
120if (NOT Python3_Interpreter_FOUND)
121 message(FATAL_ERROR "Failed to find python3 interpreter.")
122endif()
123
124find_file(NANOPB_GENERATOR_PATH
125 NAMES nanopb_generator.py
126 PATHS ${nanopb_SOURCE_DIR}/generator
127 DOC "nanopb protobuf compiler"
128 NO_DEFAULT_PATH
129 )
130
131if (NOT NANOPB_GENERATOR_PATH)
132 message(FATAL_ERROR "Nanopb generator was not found!")
133endif()
134
135#[===[.rst:
136.. cmake:command:: protobuf_generate
137
138 .. code-block:: cmake
139
140 protobuf_generate(SRC file.proto
141 TGT foo
142 NAMESPACE bar
143 BASE_DIR "proto/definitions")
144
145 Run the ``nanopb_generator`` to compile a protobuf definition file into C source.
146 Generated source file will be added to the source list of ``TGT``. Protobuf
147 compilation will take part before TGT+NAMESPACE is built.
148
149 Protobuf file names added to the same TGT must not collide.
150
151 Inputs:
152
153 ``SRC``
154 Path to of the protobuf file to process. Either absoluto or relative to the
155 callers location.
156
157 ``TGT``
158 Name of target to compile generated source files.
159
160 ``NAMESPACE``
161 Namespace to put generated files under. Specifies include path and allows
162 separating colliding protobuf files.
163
164 ``BASE_DIR``
165 Base directory. Generated files are located reletive to this base.
166
167#]===]
168function(protobuf_generate)
169 set(_options )
170 set(_oneValueArgs SRC TGT NAMESPACE BASE_DIR)
171 set(_multiValueArgs )
172
173 cmake_parse_arguments(PARAMS "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN})
174
175 #Verify mandatory parameters
176 if (NOT DEFINED PARAMS_SRC)
177 message(FATAL_ERROR "nanopb_generate(): mandatory parameter SRC missing.")
178 endif()
179 if (NOT DEFINED PARAMS_TGT)
180 message(FATAL_ERROR "nanopb_generate(): mandatory parameter TGT missing.")
181 endif()
182 if (NOT DEFINED PARAMS_NAMESPACE)
183 message(FATAL_ERROR "nanopb_generate(): mandatory parameter NAMESPACE missing.")
184 endif()
185 if (NOT DEFINED PARAMS_BASE_DIR)
186 message(FATAL_ERROR "nanopb_generate(): mandatory parameter BASE_DIR missing.")
187 endif()
188
189 #If SRC is not abolute make it relative to the callers location.
190 if (NOT IS_ABSOLUTE ${PARAMS_SRC})
191 set(PARAMS_SRC "${CMAKE_CURRENT_LIST_DIR}/${PARAMS_SRC}")
192 endif()
193
194 #Calculate the output directory
195 set(_OUT_DIR_BASE ${CMAKE_BINARY_DIR}/src/${PARAMS_NAMESPACE})
196 #Calculate output file names
197 get_filename_component(_BASENAME ${PARAMS_SRC} NAME_WE)
198
199 #Get relative path or SRC to BASE_DIR
200 file(RELATIVE_PATH _SRC_REL ${PARAMS_BASE_DIR} ${PARAMS_SRC})
201 get_filename_component(_OUT_DIR_REL ${_SRC_REL} DIRECTORY )
202
203 #Calculate output file paths
204 set(_OUT_C "${_OUT_DIR_BASE}/${_OUT_DIR_REL}/${_BASENAME}.pb.c")
205 set(_OUT_H "${_OUT_DIR_BASE}/${_OUT_DIR_REL}/${_BASENAME}.pb.h")
206
207 #some helper variables for the purpose of readability
208 set(_nanopb_target "nanopb_generate_${PARAMS_TGT}_${PARAMS_NAMESPACE}")
209 set(_nanopb_fake_file "nanopb_generate_ff_${PARAMS_TGT}")
210
211 if (NOT TARGET "${_nanopb_target}")
Gyorgy Szingcd3241a2021-06-30 22:41:11 +0000212 #Tell cmake the dependency (source) file is fake.
213 set_source_files_properties("${_nanopb_fake_file}" PROPERTIES SYMBOLIC "true")
Julian Halldf86fce2020-11-23 18:09:55 +0100214 #Create a custom target which depends on a "fake" file.
215 add_custom_target("${_nanopb_target}"
216 DEPENDS "${_nanopb_fake_file}")
Julian Halldf86fce2020-11-23 18:09:55 +0100217 #Add a cutom command to the target to create output directory.
218 add_custom_command(OUTPUT "${_nanopb_fake_file}"
219 COMMAND ${CMAKE_COMMAND} -E make_directory ${_OUT_DIR_BASE}
220 COMMENT "Generating source from protobuf definitions for target ${PARAMS_TGT}")
221 #Ensure protobuf build happens before test target.
222 add_dependencies(${PARAMS_TGT} ${_nanopb_target})
223 #Add include path to protobuf output.
224 target_include_directories(${PARAMS_TGT} PRIVATE ${_OUT_DIR_BASE})
225 endif()
226
227 #Append a protobuf generator command to the nanopb_generate target.
Gyorgy Szingcd3241a2021-06-30 22:41:11 +0000228 add_custom_command(OUTPUT "${_OUT_C}" "${_OUT_H}"
Julian Halldf86fce2020-11-23 18:09:55 +0100229 COMMAND ${Python3_EXECUTABLE} ${NANOPB_GENERATOR_PATH}
230 -I ${PARAMS_BASE_DIR}
231 -D ${_OUT_DIR_BASE}
232 ${_SRC_REL}
233 DEPENDS "${PARAMS_SRC}")
234
235 #Add generated file to the target
236 set_property(SOURCE "${_OUT_C}" PROPERTY GENERATED TRUE)
237 target_sources(${PARAMS_TGT} PRIVATE "${_OUT_C}")
238endfunction()
239
240#[===[.rst:
241.. cmake:command:: protobuf_generate_all
242
243 .. code-block:: cmake
244
245 protobuf_generate_all(TGT foo
246 NAMESPACE bar
247 BASE_DIR "proto/definitions")
248
249 Generates C code from all .proto files listed in the target
250 property PROTOBUF_FILES.
251
252 Inputs:
253
254 ``TGT``
255 Name of target to compile generated source files.
256
257 ``NAMESPACE``
258 Namespace to put generated files under. Specifies include path and allows
259 separating colliding protobuf files.
260
261 ``BASE_DIR``
262 Base directory. Generated files are located reletive to this base.
263
264#]===]
265function(protobuf_generate_all)
266 set(_options )
267 set(_oneValueArgs TGT NAMESPACE BASE_DIR)
268 set(_multiValueArgs )
269
270 cmake_parse_arguments(PARAMS "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN})
271
272 #Verify mandatory parameters
273 if (NOT DEFINED PARAMS_TGT)
274 message(FATAL_ERROR "nanopb_generate_all(): mandatory parameter TGT missing.")
275 endif()
276 if (NOT DEFINED PARAMS_NAMESPACE)
277 message(FATAL_ERROR "nanopb_generate_all(): mandatory parameter NAMESPACE missing.")
278 endif()
279 if (NOT DEFINED PARAMS_BASE_DIR)
280 message(FATAL_ERROR "nanopb_generate_all(): mandatory parameter BASE_DIR missing.")
281 endif()
282
283 get_property(_protolist TARGET ${PARAMS_TGT} PROPERTY PROTOBUF_FILES)
284
285 #Build of each .proto file
286 foreach(_file IN LISTS _protolist)
287 protobuf_generate(
288 TGT ${PARAMS_TGT}
289 SRC "${_file}"
290 NAMESPACE ${PARAMS_NAMESPACE}
291 BASE_DIR ${PARAMS_BASE_DIR})
292 endforeach()
293endfunction()