# ##############################################################################
# cmake/nuttx_add_application.cmake
#
# SPDX-License-Identifier: Apache-2.0
#
# Licensed to the Apache Software Foundation (ASF) under one or more contributor
# license agreements. See the NOTICE file distributed with this work for
# additional information regarding copyright ownership. The ASF licenses this
# file to you under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
#
# ##############################################################################
include(nuttx_parse_function_args)
define_property(
GLOBAL
PROPERTY NUTTX_APPS_LIBRARIES
BRIEF_DOCS "NuttX application libs"
FULL_DOCS "List of all NuttX application libraries")
# Create a directories for the application binaries `bin` for stripped binaries
# `bin_debug` for debug binaries if(NOT EXISTS ${CMAKE_BINARY_DIR}/bin) Make
# sure the binary directory always rebuild
add_custom_target(
gen_apps_bin_dir ALL
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/bin
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/bin_debug
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/bin
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/bin_debug
COMMENT "Generating bin output directories")
add_dependencies(apps_context gen_apps_bin_dir)
# ~~~
# Allow external toolchain to provide custom nuttx_add_elf_app implementation
#
# External toolchains can define their own nuttx_add_elf_app by:
# 1. Defining the function before this file is included
# 2. The custom function MUST set RESULT_TARGET variable in parent scope
#
# Example in toolchain file:
# function(nuttx_add_elf_app)
# # ... parse arguments
# # ... custom implementation
# set(RESULT_TARGET "custom_target_name" PARENT_SCOPE)
# endfunction()
# include(nuttx_add_application. cmake)
# ~~~
if(NOT COMMAND nuttx_add_elf_app)
# ~~~
# nuttx_add_elf_app
#
# Description:
# Creates a standalone executable application (loadable module, kernel ELF,
# or dynamic library) as opposed to a static library.
#
# This function can be overridden by external toolchains to provide custom
# ELF building behavior.
#
# Usage:
# nuttx_add_elf_app(
# NAME <string>
# MODULE <string>
# DYNLIB <string>
# SRCS <list>
# [ PRIORITY <string> ]
# [ STACKSIZE <string> ]
# [ HEAPSIZE <string> ]
# [ UID <string> ]
# [ GID <string> ]
# [ MODE <string> ]
# [ COMPILE_FLAGS <list> ]
# [ LINK_FLAGS <list> ]
# [ INCLUDE_DIRECTORIES <list> ]
# [ DEPENDS <list> ]
# [ DEFINITIONS <list> ])
#
# Parameters:
# NAME : name of the application
# MODULE : module configuration ("m" for loadable)
# DYNLIB : dynamic library flag ("y" for dynlib)
# SRCS : source files
# Other parameters: same as nuttx_add_application
#
# Returns:
# Sets RESULT_TARGET variable in parent scope
# ~~~
function(nuttx_add_elf_app)
nuttx_parse_function_args(
FUNC
nuttx_add_elf_app
ONE_VALUE
NAME
PRIORITY
STACKSIZE
UID
GID
MODE
HEAPSIZE
MODULE
DYNLIB
MULTI_VALUE
COMPILE_FLAGS
LINK_FLAGS
INCLUDE_DIRECTORIES
SRCS
DEPENDS
DEFINITIONS
OPTIONS
ARGN
${ARGN})
# create as standalone executable (loadable application or "module")
set(ELF_TARGET "${NAME}")
# determine the compiled elf mode
if(CONFIG_BUILD_KERNEL)
set(KERNEL_ELF_MODE True) # kernel elf will link all user libs
elseif("${MODULE}" STREQUAL "m")
set(LOADABLE_ELF_MODE True) # loadable elf only link extra libs
elseif("${DYNLIB}" STREQUAL "y")
set(DYNLIB_ELF_MODE True) # dynlib elf dont need start obj and other lib
endif()
# Use ELF capable toolchain, by building manually and overwriting the
# non-elf output
if(NOT CMAKE_C_ELF_COMPILER)
set(ELF_NAME "${NAME}")
set(ELF_TARGET "ELF_${NAME}")
add_library(${ELF_TARGET} ${SRCS})
add_dependencies(${ELF_TARGET} apps_post)
if(TARGET STARTUP_OBJS)
add_dependencies(${ELF_TARGET} STARTUP_OBJS)
endif()
if(NOT "${CMAKE_C_COMPILER}" MATCHES "gcc$|clang$")
set(USE_LINKER True)
endif()
if(STACKSIZE)
set(SYMBOL_STACKSIZE --add-symbol nx_stacksize=${STACKSIZE})
endif()
if(PRIORITY)
if(PRIORITY STREQUAL "SCHED_PRIORITY_DEFAULT")
set(PRIORITY "0")
endif()
set(SYMBOL_PRIORITY --add-symbol nx_priority=${PRIORITY})
endif()
if(CONFIG_SCHED_USER_IDENTITY)
if(UID)
set(SYMBOL_UID --add-symbol nx_uid=${UID})
endif()
if(GID)
set(SYMBOL_GID --add-symbol nx_gid=${GID})
endif()
if(MODE)
set(SYMBOL_MODE --add-symbol nx_mode=${MODE})
endif()
endif()
if(CONFIG_MM_TASK_HEAP)
if(HEAPSIZE)
set(SYMBOL_HEAPSIZE --add-symbol nx_heapsize=${HEAPSIZE})
else()
set(SYMBOL_HEAPSIZE --add-symbol
nx_heapsize=${CONFIG_MM_TASK_HEAP_DEFAULT_SIZE})
endif()
endif()
get_property(NUTTX_EXTRA_FLAGS GLOBAL PROPERTY NUTTX_EXTRA_FLAGS)
add_custom_command(
TARGET ${ELF_TARGET}
POST_BUILD
COMMAND
# add default link option
${CMAKE_C_COMPILER} ${NUTTX_EXTRA_FLAGS} -T
${CMAKE_BINARY_DIR}/gnu-elf.ld
# add global MOD link option if dynlib link
$<$<BOOL:${DYNLIB_ELF_MODE}>:$<TARGET_PROPERTY:nuttx_global,NUTTX_MOD_APP_LINK_OPTIONS>>
# add global ELF link option if m&kernel link
$<$<OR:$<BOOL:${KERNEL_ELF_MODE}>,$<BOOL:${LOADABLE_ELF_MODE}>>:$<TARGET_PROPERTY:nuttx_global,NUTTX_ELF_APP_LINK_OPTIONS>>
# add local link option lastest
${LINK_FLAGS}
# link startup obj if m&kernel link
$<$<AND:$<TARGET_EXISTS:STARTUP_OBJS>,$<NOT:$<BOOL:${DYNLIB_ELF_MODE}>>>:$<TARGET_OBJECTS:STARTUP_OBJS>>
$<$<NOT:$<BOOL:${USE_LINKER}>>:-Wl,>--start-group
# link user lib if kernel link
$<$<BOOL:${KERNEL_ELF_MODE}>:$<GENEX_EVAL:$<TARGET_PROPERTY:nuttx_global,NUTTX_ELF_LINK_LIBRARIES>>>
# always link extra libs
$<GENEX_EVAL:$<TARGET_PROPERTY:nuttx_global,NUTTX_ELF_LINK_EXTRA_LIBRARIES>>
$<$<NOT:$<BOOL:${USE_LINKER}>>:-Wl,>--whole-archive
$<TARGET_FILE:${ELF_TARGET}>
$<$<NOT:$<BOOL:${USE_LINKER}>>:-Wl,>--no-whole-archive
$<$<NOT:$<BOOL:${USE_LINKER}>>:-Wl,>--end-group -o
${CMAKE_BINARY_DIR}/bin_debug/${ELF_NAME}
COMMAND
${CMAKE_OBJCOPY} ${SYMBOL_STACKSIZE} ${SYMBOL_PRIORITY} ${SYMBOL_UID}
${SYMBOL_GID} ${SYMBOL_MODE} ${SYMBOL_HEAPSIZE}
${CMAKE_BINARY_DIR}/bin_debug/${ELF_NAME}
COMMAND
${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/bin_debug/${ELF_NAME}
${CMAKE_BINARY_DIR}/bin/${ELF_NAME}
COMMAND ${CMAKE_STRIP} ${CMAKE_BINARY_DIR}/bin/${ELF_NAME}
COMMENT "Building ELF:${ELF_NAME}"
COMMAND_EXPAND_LISTS)
else()
add_executable(${ELF_TARGET} ${SRCS})
target_link_options(
${ELF_TARGET} PRIVATE
$<GENEX_EVAL:$<TARGET_PROPERTY:nuttx_global,NUTTX_ELF_APP_LINK_OPTIONS>>
)
endif()
# easy access to final ELF, regardless of how it was created
set_property(TARGET ${ELF_TARGET}
PROPERTY ELF_BINARY ${CMAKE_CURRENT_BINARY_DIR}/${ELF_TARGET})
nuttx_add_library_internal(${ELF_TARGET})
# loadable build requires applying ELF flags to all applications
if(CONFIG_MODULES)
add_dependencies(nuttx_apps_mksymtab ${ELF_TARGET})
target_compile_options(
${ELF_TARGET}
PRIVATE
$<GENEX_EVAL:$<TARGET_PROPERTY:nuttx_global,NUTTX_ELF_APP_COMPILE_OPTIONS>>
)
endif()
if(DYNLIB_ELF_MODE)
add_dependencies(nuttx_apps_mksymtab ${ELF_TARGET})
target_compile_options(
${ELF_TARGET}
PRIVATE
$<GENEX_EVAL:$<TARGET_PROPERTY:nuttx_global,NUTTX_MOD_APP_COMPILE_OPTIONS>>
)
endif()
install(TARGETS ${ELF_TARGET})
set_property(
TARGET nuttx
APPEND
PROPERTY NUTTX_LOADABLE_APPS ${ELF_TARGET})
# Return target name to parent scope
set(RESULT_TARGET
${ELF_TARGET}
PARENT_SCOPE)
endfunction()
# Mark that the default implementation has been defined
set(NUTTX_DEFAULT_ELF_BUILD_DEFINED
TRUE
CACHE INTERNAL "Default nuttx_add_elf_app is defined")
else()
message(STATUS "Using custom nuttx_add_elf_app implementation")
endif()
# ~~~
# nuttx_add_application
#
# Description:
# Declares a NuttX application as a static library. The corresponding target
# will be named apps_<NAME>. The first entry into the source list is assumed
# to be the one containing main() and will thus receive a -Dmain=app_main
# definition during build.
#
# Usage:
# nuttx_add_application( NAME <string> [ PRIORITY <string> ]
# [ STACKSIZE <string> ] [ COMPILE_FLAGS <list> ]
# [ INCLUDE_DIRECTORIES <list> ] [ DEPENDS <string> ]
# [ DEFINITIONS <string> ] [ MODULE <string> ] [ SRCS <list> ] )
#
# Parameters:
# NAME : unique name of application
# PRIORITY : priority
# STACKSIZE : stack size
# HEAPSIZE : heap size
# COMPILE_FLAGS : compile flags
# INCLUDE_DIRECTORIES : include directories
# DEPENDS : targets which this module depends on
# DEFINITIONS : optional compile definitions
# MODULE : if "m", build module (designed to received
# CONFIG_<app> value)
# SRCS : source files
# NO_MAIN_ALIAS : do not add a main=<app>_main alias(*)
# DYNLIB : if "y", build as dynamic loadable library
# LINK_FLAGS : link flags only for elf or loadable link
#
# (*) This is only really needed in convoluted cases where a single .c file
# contains differently named <app>_main() entries for different <app>. This
# situation should really be changed into a separate main file per actual app
# using a shared user library.
#
# Example:
# nuttx_add_application(
# NAME test
# SRCS file.cpp
# STACKSIZE 1024
# HEAPSIZE 65536
# DEPENDS nshlib
# MODULE ${CONFIG_EXAMPLES_TEST})
# ~~~
function(nuttx_add_application)
# parse arguments into variables
nuttx_parse_function_args(
FUNC
nuttx_add_application
ONE_VALUE
NAME
PRIORITY
STACKSIZE
UID
GID
MODE
HEAPSIZE
MODULE
DYNLIB
MULTI_VALUE
COMPILE_FLAGS
LINK_FLAGS
INCLUDE_DIRECTORIES
SRCS
DEPENDS
DEFINITIONS
OPTIONS
NO_MAIN_ALIAS
ARGN
${ARGN})
if(NOT NAME)
return()
endif()
# check if SRCS exist
if(SRCS)
file(GLOB SRCS_EXIST ${SRCS})
endif()
# Initialize TARGET variable
set(TARGET "")
set(NO_COMPILABLE_TARGET FALSE)
# create target
if(SRCS_EXIST)
if(MODULE
AND ("${MODULE}" STREQUAL "m")
OR CONFIG_BUILD_KERNEL
OR "${DYNLIB}" STREQUAL "y")
# Build as elf using the extracted function
nuttx_add_elf_app(
NAME
${NAME}
MODULE
${MODULE}
DYNLIB
${DYNLIB}
SRCS
${SRCS}
PRIORITY
${PRIORITY}
STACKSIZE
${STACKSIZE}
UID
${UID}
GID
${GID}
MODE
${MODE}
HEAPSIZE
${HEAPSIZE}
COMPILE_FLAGS
${COMPILE_FLAGS}
LINK_FLAGS
${LINK_FLAGS}
INCLUDE_DIRECTORIES
${INCLUDE_DIRECTORIES}
DEPENDS
${DEPENDS}
DEFINITIONS
${DEFINITIONS})
# Get the target name returned by nuttx_add_elf_app
set(TARGET ${RESULT_TARGET})
else()
# create as library to be archived into libapps.a
set(TARGET "apps_${NAME}")
add_library(${TARGET} ${SRCS})
nuttx_add_library_internal(${TARGET})
# add to list of application libraries
set_property(GLOBAL APPEND PROPERTY NUTTX_APPS_LIBRARIES ${TARGET})
if(NOT NO_MAIN_ALIAS)
# provide main() alias
list(GET SRCS 0 MAIN_SRC)
set_property(
SOURCE ${MAIN_SRC}
APPEND
PROPERTY COMPILE_DEFINITIONS main=${NAME}_main)
endif()
endif()
else()
set(TARGET "apps_${NAME}")
add_custom_target(${TARGET})
set_property(GLOBAL APPEND PROPERTY NUTTX_APPS_ONLY_REGISTER ${TARGET})
set_target_properties(${TARGET} PROPERTIES NO_COMPILABLE_TARGET True)
set(NO_COMPILABLE_TARGET True)
endif()
# apps applications need to depends on apps_context by default
add_dependencies(${TARGET} apps_context)
# store parameters into properties (used during builtin list generation)
set_target_properties(${TARGET} PROPERTIES APP_MAIN ${NAME}_main)
set_target_properties(${TARGET} PROPERTIES APP_NAME ${NAME})
if(PRIORITY)
set_target_properties(${TARGET} PROPERTIES APP_PRIORITY ${PRIORITY})
else()
set_target_properties(${TARGET} PROPERTIES APP_PRIORITY
SCHED_PRIORITY_DEFAULT)
endif()
if(STACKSIZE)
set_target_properties(${TARGET} PROPERTIES APP_STACK ${STACKSIZE})
else()
set_target_properties(${TARGET} PROPERTIES APP_STACK
${CONFIG_DEFAULT_TASK_STACKSIZE})
endif()
if(CONFIG_SCHED_USER_IDENTITY)
if(UID)
set_target_properties(${TARGET} PROPERTIES APP_UID ${UID})
else()
set_target_properties(${TARGET} PROPERTIES APP_UID 0)
endif()
if(GID)
set_target_properties(${TARGET} PROPERTIES APP_GID ${GID})
else()
set_target_properties(${TARGET} PROPERTIES APP_GID 0)
endif()
if(MODE)
set_target_properties(${TARGET} PROPERTIES APP_MODE ${MODE})
else()
set_target_properties(${TARGET} PROPERTIES APP_MODE 0)
endif()
endif()
if(CONFIG_MM_TASK_HEAP)
if(HEAPSIZE)
set_target_properties(${TARGET} PROPERTIES APP_HEAP ${HEAPSIZE})
else()
set_target_properties(${TARGET} PROPERTIES APP_HEAP 0)
endif()
endif()
# call target_ options only target is compilable
if(NOT NO_COMPILABLE_TARGET)
# compile options
if(COMPILE_FLAGS)
target_compile_options(${TARGET} PRIVATE ${COMPILE_FLAGS})
endif()
# compile definitions
if(DEFINITIONS)
target_compile_definitions(${TARGET} PRIVATE ${DEFINITIONS})
endif()
if(INCLUDE_DIRECTORIES)
target_include_directories(${TARGET} BEFORE
PRIVATE ${INCLUDE_DIRECTORIES})
endif()
# Set apps global compile options & definitions hold by nuttx_apps_interface
target_compile_options(
${TARGET}
PRIVATE
$<GENEX_EVAL:$<TARGET_PROPERTY:nuttx_apps_interface,APPS_COMPILE_OPTIONS>>
)
target_compile_definitions(
${TARGET}
PRIVATE
$<GENEX_EVAL:$<TARGET_PROPERTY:nuttx_apps_interface,APPS_COMPILE_DEFINITIONS>>
)
target_include_directories(
${TARGET}
PRIVATE
$<GENEX_EVAL:$<TARGET_PROPERTY:nuttx_apps_interface,APPS_INCLUDE_DIRECTORIES>>
)
endif()
# add supplied dependencies
if(DEPENDS)
# using target_link_libraries for dependencies provides linking as well as
# interface include and libraries
foreach(dep ${DEPENDS})
nuttx_add_dependencies(TARGET ${TARGET} DEPENDS ${dep})
endforeach()
endif()
endfunction()