blob: e74edc2d30e8d9e239e153f5a888d334a4d72541 [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.
Andrew Beggs97a00d42021-06-15 15:45:46 +000061
62# Pass extra include paths to nanopb build uning CFLAGS if needed
63if (NOT "${NANOPB_EXTERNAL_INCLUDE_PATHS}" STREQUAL "")
64 string(REPLACE ";" "-I " NANOPB_EXTERNAL_INCLUDE_PATHS "${NANOPB_EXTERNAL_INCLUDE_PATHS}")
65 set(_SAVED_CFLAGS $ENV{CFLAGS})
66 set(ENV{CFLAGS} "$ENV{CFLAGS} -I ${NANOPB_EXTERNAL_INCLUDE_PATHS}")
67endif()
68
Julian Halldf86fce2020-11-23 18:09:55 +010069if( NOT CMAKE_CROSSCOMPILING)
70 execute_process(COMMAND
71 ${CMAKE_COMMAND}
72 -DBUILD_SHARED_LIBS=Off
73 -DBUILD_STATIC_LIBS=On
74 -Dnanopb_BUILD_RUNTIME=On
75 -Dnanopb_BUILD_GENERATOR=On
76 -Dnanopb_PROTOC_PATH=${nanopb_SOURCE_DIR}/generator/protoc
77 -Dnanopb_MSVC_STATIC_RUNTIME=Off
78 -DCMAKE_INSTALL_PREFIX=${NANOPB_INSTALL_PATH}
79 -DCMAKE_TOOLCHAIN_FILE=${TS_EXTERNAL_LIB_TOOLCHAIN_FILE}
80 -GUnix\ Makefiles
81 ${nanopb_SOURCE_DIR}
82 WORKING_DIRECTORY
83 ${nanopb_BINARY_DIR}
84 RESULT_VARIABLE _exec_error
85 )
86else()
87 execute_process(COMMAND
88 ${CMAKE_COMMAND}
89 -DBUILD_SHARED_LIBS=Off
90 -DBUILD_STATIC_LIBS=On
91 -Dnanopb_BUILD_RUNTIME=On
92 -Dnanopb_BUILD_GENERATOR=On
93 -Dnanopb_PROTOC_PATH=${nanopb_SOURCE_DIR}/generator/protoc
94 -Dnanopb_MSVC_STATIC_RUNTIME=Off
95 -DCMAKE_INSTALL_PREFIX=${NANOPB_INSTALL_PATH}
96 -DCMAKE_TOOLCHAIN_FILE=${TS_EXTERNAL_LIB_TOOLCHAIN_FILE}
97 -DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY
98 -GUnix\ Makefiles
99 ${nanopb_SOURCE_DIR}
100 WORKING_DIRECTORY
101 ${nanopb_BINARY_DIR}
102 RESULT_VARIABLE _exec_error
103 )
104endif()
105
Andrew Beggs97a00d42021-06-15 15:45:46 +0000106if (NOT "${NANOPB_EXTERNAL_INCLUDE_PATHS}" STREQUAL "")
107 set($ENV{CFLAGS} ${_SAVED_CFLAGS})
108 unset(_SAVED_CFLAGS)
109 unset(NANOPB_EXTERNAL_INCLUDE_PATHS)
110endif()
111
Julian Halldf86fce2020-11-23 18:09:55 +0100112if (_exec_error)
113 message(FATAL_ERROR "Configuration step of nanopb runtime failed with ${_exec_error}.")
114endif()
115
116execute_process(COMMAND
117 ${CMAKE_COMMAND} --build ${nanopb_BINARY_DIR} -- install -j8
118 RESULT_VARIABLE _exec_error
119 )
120if (_exec_error)
121 message(FATAL_ERROR "Build step of nanopb runtime failed with ${_exec_error}.")
122endif()
123
124#### Include Nanopb runtime in the build.
125find_package(Nanopb
126 PATHS "${NANOPB_INSTALL_PATH}"
127 NO_DEFAULT_PATH
128 )
129
130#### Build access to the protobuf compiler
131#TODO: verify protoc dependencies: python3-protobuf
132find_package(Python3 COMPONENTS Interpreter)
133
134if (NOT Python3_Interpreter_FOUND)
135 message(FATAL_ERROR "Failed to find python3 interpreter.")
136endif()
137
138find_file(NANOPB_GENERATOR_PATH
139 NAMES nanopb_generator.py
140 PATHS ${nanopb_SOURCE_DIR}/generator
141 DOC "nanopb protobuf compiler"
142 NO_DEFAULT_PATH
143 )
144
145if (NOT NANOPB_GENERATOR_PATH)
146 message(FATAL_ERROR "Nanopb generator was not found!")
147endif()
148
149#[===[.rst:
150.. cmake:command:: protobuf_generate
151
152 .. code-block:: cmake
153
154 protobuf_generate(SRC file.proto
155 TGT foo
156 NAMESPACE bar
157 BASE_DIR "proto/definitions")
158
159 Run the ``nanopb_generator`` to compile a protobuf definition file into C source.
160 Generated source file will be added to the source list of ``TGT``. Protobuf
161 compilation will take part before TGT+NAMESPACE is built.
162
163 Protobuf file names added to the same TGT must not collide.
164
165 Inputs:
166
167 ``SRC``
168 Path to of the protobuf file to process. Either absoluto or relative to the
169 callers location.
170
171 ``TGT``
172 Name of target to compile generated source files.
173
174 ``NAMESPACE``
175 Namespace to put generated files under. Specifies include path and allows
176 separating colliding protobuf files.
177
178 ``BASE_DIR``
179 Base directory. Generated files are located reletive to this base.
180
181#]===]
182function(protobuf_generate)
183 set(_options )
184 set(_oneValueArgs SRC TGT NAMESPACE BASE_DIR)
185 set(_multiValueArgs )
186
187 cmake_parse_arguments(PARAMS "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN})
188
189 #Verify mandatory parameters
190 if (NOT DEFINED PARAMS_SRC)
191 message(FATAL_ERROR "nanopb_generate(): mandatory parameter SRC missing.")
192 endif()
193 if (NOT DEFINED PARAMS_TGT)
194 message(FATAL_ERROR "nanopb_generate(): mandatory parameter TGT missing.")
195 endif()
196 if (NOT DEFINED PARAMS_NAMESPACE)
197 message(FATAL_ERROR "nanopb_generate(): mandatory parameter NAMESPACE missing.")
198 endif()
199 if (NOT DEFINED PARAMS_BASE_DIR)
200 message(FATAL_ERROR "nanopb_generate(): mandatory parameter BASE_DIR missing.")
201 endif()
202
203 #If SRC is not abolute make it relative to the callers location.
204 if (NOT IS_ABSOLUTE ${PARAMS_SRC})
205 set(PARAMS_SRC "${CMAKE_CURRENT_LIST_DIR}/${PARAMS_SRC}")
206 endif()
207
208 #Calculate the output directory
209 set(_OUT_DIR_BASE ${CMAKE_BINARY_DIR}/src/${PARAMS_NAMESPACE})
210 #Calculate output file names
211 get_filename_component(_BASENAME ${PARAMS_SRC} NAME_WE)
212
213 #Get relative path or SRC to BASE_DIR
214 file(RELATIVE_PATH _SRC_REL ${PARAMS_BASE_DIR} ${PARAMS_SRC})
215 get_filename_component(_OUT_DIR_REL ${_SRC_REL} DIRECTORY )
216
217 #Calculate output file paths
218 set(_OUT_C "${_OUT_DIR_BASE}/${_OUT_DIR_REL}/${_BASENAME}.pb.c")
219 set(_OUT_H "${_OUT_DIR_BASE}/${_OUT_DIR_REL}/${_BASENAME}.pb.h")
220
221 #some helper variables for the purpose of readability
222 set(_nanopb_target "nanopb_generate_${PARAMS_TGT}_${PARAMS_NAMESPACE}")
223 set(_nanopb_fake_file "nanopb_generate_ff_${PARAMS_TGT}")
224
225 if (NOT TARGET "${_nanopb_target}")
Gyorgy Szingcd3241a2021-06-30 22:41:11 +0000226 #Tell cmake the dependency (source) file is fake.
227 set_source_files_properties("${_nanopb_fake_file}" PROPERTIES SYMBOLIC "true")
Julian Halldf86fce2020-11-23 18:09:55 +0100228 #Create a custom target which depends on a "fake" file.
229 add_custom_target("${_nanopb_target}"
230 DEPENDS "${_nanopb_fake_file}")
Julian Halldf86fce2020-11-23 18:09:55 +0100231 #Add a cutom command to the target to create output directory.
232 add_custom_command(OUTPUT "${_nanopb_fake_file}"
233 COMMAND ${CMAKE_COMMAND} -E make_directory ${_OUT_DIR_BASE}
234 COMMENT "Generating source from protobuf definitions for target ${PARAMS_TGT}")
235 #Ensure protobuf build happens before test target.
236 add_dependencies(${PARAMS_TGT} ${_nanopb_target})
237 #Add include path to protobuf output.
238 target_include_directories(${PARAMS_TGT} PRIVATE ${_OUT_DIR_BASE})
239 endif()
240
241 #Append a protobuf generator command to the nanopb_generate target.
Gyorgy Szingcd3241a2021-06-30 22:41:11 +0000242 add_custom_command(OUTPUT "${_OUT_C}" "${_OUT_H}"
Julian Halldf86fce2020-11-23 18:09:55 +0100243 COMMAND ${Python3_EXECUTABLE} ${NANOPB_GENERATOR_PATH}
244 -I ${PARAMS_BASE_DIR}
245 -D ${_OUT_DIR_BASE}
246 ${_SRC_REL}
247 DEPENDS "${PARAMS_SRC}")
248
249 #Add generated file to the target
250 set_property(SOURCE "${_OUT_C}" PROPERTY GENERATED TRUE)
251 target_sources(${PARAMS_TGT} PRIVATE "${_OUT_C}")
252endfunction()
253
254#[===[.rst:
255.. cmake:command:: protobuf_generate_all
256
257 .. code-block:: cmake
258
259 protobuf_generate_all(TGT foo
260 NAMESPACE bar
261 BASE_DIR "proto/definitions")
262
263 Generates C code from all .proto files listed in the target
264 property PROTOBUF_FILES.
265
266 Inputs:
267
268 ``TGT``
269 Name of target to compile generated source files.
270
271 ``NAMESPACE``
272 Namespace to put generated files under. Specifies include path and allows
273 separating colliding protobuf files.
274
275 ``BASE_DIR``
276 Base directory. Generated files are located reletive to this base.
277
278#]===]
279function(protobuf_generate_all)
280 set(_options )
281 set(_oneValueArgs TGT NAMESPACE BASE_DIR)
282 set(_multiValueArgs )
283
284 cmake_parse_arguments(PARAMS "${_options}" "${_oneValueArgs}" "${_multiValueArgs}" ${ARGN})
285
286 #Verify mandatory parameters
287 if (NOT DEFINED PARAMS_TGT)
288 message(FATAL_ERROR "nanopb_generate_all(): mandatory parameter TGT missing.")
289 endif()
290 if (NOT DEFINED PARAMS_NAMESPACE)
291 message(FATAL_ERROR "nanopb_generate_all(): mandatory parameter NAMESPACE missing.")
292 endif()
293 if (NOT DEFINED PARAMS_BASE_DIR)
294 message(FATAL_ERROR "nanopb_generate_all(): mandatory parameter BASE_DIR missing.")
295 endif()
296
297 get_property(_protolist TARGET ${PARAMS_TGT} PROPERTY PROTOBUF_FILES)
298
299 #Build of each .proto file
300 foreach(_file IN LISTS _protolist)
301 protobuf_generate(
302 TGT ${PARAMS_TGT}
303 SRC "${_file}"
304 NAMESPACE ${PARAMS_NAMESPACE}
305 BASE_DIR ${PARAMS_BASE_DIR})
306 endforeach()
307endfunction()