Examples

[English]

001 - Minimal Project

Every project has a top-level CMakeLists.txt file that contains build settings for the entire project. A minimal project looks like this:

cmake_minimum_required(VERSION 3.5)
include($ENV{ARMINO_PATH}/tools/build_tools/cmake/project.cmake)
project(myProject)

CMakeLists.txt Explanation

Every project must add these two lines in the above order:

  • cmake_minimum_required(VERSION 3.5) must be placed on the first line of the CMakeLists.txt file, it tells CMake the minimum version number required to build the project. ARMINO supports version 3.5 or higher.

  • include($ENV{ARMINO_PATH}/tools/build_tools/cmake/project.cmake) imports the rest of CMake’s functionality to complete tasks such as configuring the project, searching for components, etc.

  • project(myProject) creates the project and specifies the project name. This name will be used as the name of the final output binary files, i.e., myProject.elf and myProject.bin. Each CMakeLists.txt file can only define one project.

Default Project Variables

The following variables all have default values. Users can override these variable values to customize build behavior.

  • COMPONENT_DIRS: Component search directories, defaults to ARMINO_PATH/components, ARMINO_PATH/middleware, PROJECT_DIR/components, and EXTRA_COMPONENTS_DIRS. Override this variable if you don’t want to search for components in these locations.

  • EXTRA_COMPONENTS_DIRS: List of other optional directories to search for components. Paths can be relative to the project directory or absolute paths.

  • COMPONENTS: List of component names to build into the project, defaults to all components found in COMPONENT_DIRS directories. Use this variable to “slim down” the project to reduce build time. Note that if a component specifies another component it depends on through COMPONENT_REQUIRES, it will be automatically added to COMPONENTS.

Paths in the above variables can be absolute paths, or relative paths to the project directory.

Note

Please use the set command in cmake to set these variables, such as set(VARIABLE "VALUE"). Note that the set() command should be placed before include(...).

002 - Adding Component Search Directories

You can set EXTRA_COMPONENTS_DIRS in the project’s top-level CMakeLists.txt to import components outside the default component search directories:

cmake_minimum_required(VERSION 3.5)
set(EXTRA_COMPONENTS_DIRS my_component_dir)
include($ENV{ARMINO_PATH}/tools/build_tools/cmake/project.cmake)
project(myProject)

003 - Not Building Specific Components

You can set EXCLUDE_COMPONENTS in the project’s top-level CMakeLists.txt to exclude certain components from the build:

cmake_minimum_required(VERSION 3.5)
set(EXCLUDE_COMPONENTS c1_not_build)
include($ENV{ARMINO_PATH}/tools/build_tools/cmake/project.cmake)
project(myProject)

004 - Building Only Specific Components

You can set COMPONENTS in the project’s top-level CMakeLists.txt to build only specific components. In this example, only c3_contain_main and its dependent components are built:

cmake_minimum_required(VERSION 3.5)
set(COMPONENTS c3_contain_main)
include($ENV{ARMINO_PATH}/tools/build_tools/cmake/project.cmake)
project(myProject)

Note:

  • All components in the COMPONENTS list and their dependent components will be built.

  • Since all components depend on common components, common components will also be built.

  • The build components must include a main() function implementation.

005 - Overriding Default Compilation Options

After project() in the top-level CMakeLists.txt, use Wno-extra to override the default Wextra:

cmake_minimum_required(VERSION 3.5)
include($ENV{ARMINO_PATH}/tools/build_tools/cmake/project.cmake)
project(myProject)

armino_build_set_property(COMPILE_OPTIONS "-Wno-extra" APPEND)

101 - Minimal Component

The simplest component CMakeLists.txt looks like this, calling armino_component_register() to register the component into the build system:

armino_component_register(SRCS c1.c INCLUDE_DIRS include)

102 - Setting Component Compilation Options

When compiling source files for a specific component, you can use the target_compile_options command to pass compiler options:

target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-unused-variable)

To specify compiler flags for a single source file, you can use CMake’s set_source_files_properties command:

set_source_files_properties(compile_options.c
    PROPERTIES COMPILE_FLAGS
    -Wno-unused-variable
)

Note that both commands above can only be called after the armino_component_register command in the component’s CMakeLists file.

103 - Overriding Components

You can override default ARMINO components by defining a component with the same name. The example uses bk_log defined in the project components to override the armino component.

104 - Setting Component Dependencies

The example includes four components:

- components/
    - c1/
        - c1.c
        - c1.h
        - c1_internal
            - c1_internal.c1
            - c1_internal.h
        - include/
            - bk_api_c1.h
    - c2/
        - c2.c
        - include
            - bk_api_c2.h
    - c3/
        - c3.c
        - include
            - bk_api_c3.h
    - c4/
        - c4.c
        - include
            - bk_api_c4.h

105 - Linking Libraries in Components

You can import a library using add_prebuilt_library:

add_prebuilt_library(target_name lib_path [REQUIRES req1 req2 ...] [PRIV_REQUIRES req1 req2 ...])

Where:

  • target_name - Name used to reference the imported library, such as when linking to other targets

  • lib_path - Path to the prebuilt library, can be absolute path or relative to the component directory

Optional arguments REQUIRES and PRIV_REQUIRES specify dependencies on other components. These arguments have the same meaning as the arguments for armino_component_register.

Note

Note that the prebuilt library’s compilation target must be the same as the current project. The prebuilt library’s parameters must also match. If not carefully attended to, these two factors can lead to bugs in the application.

106 - Pure CMake ARMINO Component

Usually ARMINO components on the component search path should be registered according to ARMINO requirements, but if you want to turn an ARMINO component search path component into a pure CMake component, you can do so with the following code:

if (CMAKE_BUILD_EARLY_EXPANSION)
        return()
endif()

add_library(c1 STATIC c1.c)
target_include_directories(c1 PUBLIC include)

The first three lines of code tell the ARMINO build system not to include this component in the build, and the c1 component will be built in pure CMake fashion. In addition, you need to add this component to the build system in CMake’s way. In this example, the c1 component is added to the build tree in the project’s top-level CMakeLists.txt:

cmake_minimum_required(VERSION 3.5)
include($ENV{ARMINO_PATH}/tools/build_toos/cmake_project.cmake)
project(cmake_exam)

add_subdirectory(components/c1)

Note

Normally, components under the ARMINO search path should call armino_component_register() and be written according to ARMINO requirements. When you have a special reason to write your own pure CMake component, you can choose to import your pure CMake component according to _<Importing Third-Party CMake Components in ARMINO Components> or relative to the project directory. _<Importing Third-Party CMake Components Anywhere>.

107 - Importing Pure CMake Component 1

In the example, foo is built using pure CMake and placed in the main component, and can be imported using the following method:

armino_component_register(SRCS "main.c" INCLUDE_DIRS .)
add_subdirectory(foo)
target_link_libraries(${COMPONENT_LIB} PUBLIC foo)

108 - Importing Pure CMake Component 2

In the example, foo is built using pure CMake and placed in the c1 component, and can be imported using the following method:

armino_component_register(SRCS "c1.c" INCLUDE_DIRS include)
add_subdirectory(foo)
target_link_libraries(${COMPONENT_LIB} PUBLIC foo)

109 - Importing Pure CMake Component 3

In the example, anywhere is placed in the project root directory and built with pure CMake. You can import it by adding this line of code in the project’s top-level CMakeLists.txt:

add_subdirectory(anywhere)

In fact, you can use any method you like to import third-party pure CMake components.

110 - Using armino Components in Pure CMake Components

The way to reference armino components in pure CMake components is armino::component_name. In the example, the c1 component in the anywhere directory will use the armino component c component:

target_link_libraries(c1 armino::c)

111 - Importing GNU Makefile Project 1

If you have a component that is not written using cmake, for example, your component is written with GNU Makefile and you want to use this component in ARMINO, but you don’t want to rewrite the component build into CMake form. In this case, you need to use CMake’s ExternalProject feature.

The example imports a foo component written with Makefile into the c1 component:

# External build process for foo, runs in the source directory
# and generates libfoo.a
externalproject_add(foo_build
    PREFIX ${COMPONENT_DIR}
    SOURCE_DIR ${COMPONENT_DIR}/foo
    CONFIGURE_COMMAND ""
    BUILD_IN_SOURCE 1
    BUILD_COMMAND make CC=${CMAKE_C_COMPILER} libfoo.a
    INSTALL_COMMAND ""
    )

# Add libfoo.a to the build system
add_library(foo STATIC IMPORTED GLOBAL)
add_dependencies(foo foo_build)

set_target_properties(foo PROPERTIES IMPORTED_LOCATION
    ${COMPONENT_DIR}/foo/libfoo.a)
set_target_properties(foo PROPERTIES INTERFACE_INCLUDE_DIRECTORIES
    ${COMPONENT_DIR}/foo/include)

set_directory_properties( PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES
    "${COMPONENT_DIR}/foo/libfoo.a")

(The above CMakeLists.txt can be used to create a component named foo that uses its own Makefile to build libfoo.a.)

  • externalproject_add defines an external build system.

    • Set SOURCE_DIR, CONFIGURE_COMMAND, BUILD_COMMAND and INSTALL_COMMAND. If the external build system doesn’t have a configuration step, you can set CONFIGURE_COMMAND to an empty string. In Armino’s build system, INSTALL_COMMAND is generally set to empty.

    • Set BUILD_IN_SOURCE, i.e., the build directory is the same as the source directory. Otherwise, you can also set the BUILD_DIR variable.

    • For details about the externalproject_add() command, please refer to ExternalProject_Add.

  • The second set of commands adds a target library pointing to the library file generated by the external build system. To add include directories and tell CMake where the file is located, you need to set some more properties.

  • Finally, the generated library is added to ADDITIONAL_MAKE_CLEAN_FILES. This means that executing make clean will delete the library. Note that other target files in the build system will not be deleted.

112 - Importing GNU Makefile Project 2

Another way to import GNU projects is to use the add_custom_command approach to import projects written with GNU Makefile.

The example imports a foo component written with Makefile into the c1 component:

armino_component_register(SRCS c1.c INCLUDE_DIRS include)

add_custom_command(OUTPUT ${COMPONENT_DIR}/foo/libfoo.a
    COMMAND ${COMPONENT_DIR}/foo/build.sh ${COMPONENT_DIR}/foo ${CMAKE_C_COMPILER}
    VERBATIM
    COMMENT "Build external project"
    )
add_custom_target(foo_build DEPENDS ${COMPONENT_DIR}/foo/libfoo.a)

add_library(foo STATIC IMPORTED GLOBAL)
add_dependencies(foo foo_build)
set_target_properties(foo PROPERTIES IMPORTED_LOCATION ${COMPONENT_DIR}/foo/libfoo.a)
set_target_properties(foo PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${COMPONENT_DIR}/foo/include)

target_link_libraries(${COMPONENT_LIB} PUBLIC foo)

The above use case first calls armino_component_register to register a standard armino component c1, then adds a command to generate libfoo.a via add_custom_command(), and then calls add_custom_target() to add the target foo_build.

The next four commands create the foo target and set the location of the library corresponding to foo and the header file directory. Finally, link the target foo to the armino standard component c1.

Note

When using add_custom_command(), you should note that the file generated after OUTPUT must be used directly in the CMakeLists.txt corresponding to the armino component, so that it will trigger the Makefile dependency rule to call the added COMMAND. Otherwise, since the generated file is not used in CMakeLists.txt, Makefile will think the build system doesn’t need this file and thus won’t trigger the command call!

113 - Using armino in Custom cmake Projects

When you need to port armino to open source platforms like zephyr/rtt/alios, one approach is to compile armino as a library and put it in there.

201 - Minimal Component Kconfig

Each component can contain a Kconfig file that includes some configuration settings to be added to the component’s configuration menu.

When running menuconfig, you can find these settings under the Component Settings menu.

To create a component’s Kconfig file, the simplest method is to use an existing Kconfig file from ARMINO as a template and modify it.

Minimal component Kconfig:

config C1
    bool "Enable component c1"
    default y

The build system will add the following configuration item to the generated sdkconfig:

CONFIG_C1=y

The build system will add the following configuration item to sdkconfig.h under the root directory (usually the build directory):

#define CONFIG_C1 1

202 - Adding Global Configuration

You can define a KConfig file for a component to implement global component configuration. If you want to add configuration options at the top level of menuconfig, instead of in the “Component Configuration” submenu, you can define these options in the KConfig.projbuild file in the same directory as the CMakeLists.txt file.

Typically a project-related Kconfig.projbuild is added for the main component. But be careful when adding configuration to this file, as the configuration will be included in the entire project configuration. Where possible, please create KConfig files for component configuration.

203 - Configuration-Only Component

A component can contain no source files or header files, only Kconfig configuration files, called a configuration-only component:

armino_component_register()

204 - Custom Project Configuration

armino loads Kconfig in the following order. For the same configuration item, later loaded values will override earlier loaded values:

  • Component Kconfig default configuration

  • Target-specific default configuration in :middleware:: <arch/bkxxx/bkxxx.defconfig>

  • Project-related, target-common configuration defined in project root directory/config/common.config

  • Project-related, target-specific configuration defined in project root directory/config/bkxxx.config

Applications can set project/target-related configuration items by configuring “project root directory/config/common.config” and “project root directory/config/bkxxx.config”, where bkxxx is the specific SoC, such as bk7239, etc.

207 - Disabling Components via Kconfig

There are multiple ways to disable a component. One method is to disable it through the component enable configuration in Kconfig:

set(src)
set(inc)

if (CONFIG_C1)
    list(APPEND src c1.c)
    list(APPEND inc include)
endif()

armino_component_register(SRCS ${src} INCLUDE_DIRS ${inc})

Other methods to disable components are:

  • Disable via ARMINO_SOC

  • Disable via EXCLUDE_COMPONENTS