嵌入式系统使用Cmake控制编译

之前曾自己写makefile控制嵌入式项目的编译,由于对makefile不精通,rule写得不好,导致效率不高,文件多时也比较慢。最近接触cmake后,就使用cmake控制编译,还比较合适,这里介绍一下配置方式。

安装cmake

Ubuntu下安装cmake很方便:

apt-get install cmake

项目配置

目录树结构

nuc123                                #顶层目录
├── build                           #编译目录
├── burn.bat
├── cmake                        #编译选项文件目录
│   └── build.cmake
├── CMakeLists.txt             #[1]顶层目录CMake
└── src                                #源文件目录
    ├── CMakeLists.txt         #[2]源文件目录CMake
    ├── cmsis_boot            
    │   ├── NUC123.h
    │   ├── startup
    │   │   └── startup_NUC123.c
    │   ├── system_NUC123.c
    │   └── system_NUC123.h
    ├── cmsis_core
    │   ├── core_cm0.h
    │   ├── core_cmFunc.h
    │   └── core_cmInstr.h
    ├── cmsis_lib                    #芯片驱动目录
    │   ├── CMakeLists.txt      #[3]芯片驱动目录CMake
    │   ├── include
    │   │   ├── adc.h
    │   │   ├── clk.h
    │   │   ├── crc.h
    │   │   ├── fmc.h
    │   │   ├── gpio.h
    │   │   ├── i2c.h
    │   │   ├── i2s.h
    │   │   ├── pdma.h
    │   │   ├── ps2.h
    │   │   ├── pwm.h
    │   │   ├── spi.h
    │   │   ├── sys.h
    │   │   ├── timer.h
    │   │   ├── uart.h
    │   │   ├── usbd.h
    │   │   ├── wdt.h
    │   │   └── wwdt.h
    │   └── source
    │       ├── adc.c
    │       ├── clk.c
    │       ├── crc.c
    │       ├── ctimer.c
    │       ├── fmc.c
    │       ├── gpio.c
    │       ├── i2c.c
    │       ├── i2s.c
    │       ├── pdma.c
    │       ├── ps2.c
    │       ├── pwm.c
    │       ├── retarget.c
    │       ├── spi.c
    │       ├── sys.c
    │       ├── uart.c
    │       ├── usbd.c
    │       ├── wdt.c
    │       └── wwdt.c
    ├── CoOS                        #操作系统目录
    │   ├── CMakeLists.txt    #[4]操作系统CMake
    │   ├── Document
    │   │   └── readme.txt
    │   ├── kernel
    │   │   ├── coocox.h
    │   │   ├── CoOS.h
    │   │   ├── core.c
    │   │   ├── event.c
    │   │   ├── flag.c
    │   │   ├── hook.c
    │   │   ├── kernelHeap.c
    │   │   ├── mbox.c
    │   │   ├── mm.c
    │   │   ├── mutex.c
    │   │   ├── OsConfig.h
    │   │   ├── OsCore.h
    │   │   ├── OsError.h
    │   │   ├── OsEvent.h
    │   │   ├── OsFlag.h
    │   │   ├── OsKernelHeap.h
    │   │   ├── OsMM.h
    │   │   ├── OsMutex.h
    │   │   ├── OsQueue.h
    │   │   ├── OsServiceReq.h
    │   │   ├── OsTask.h
    │   │   ├── OsTime.h
    │   │   ├── OsTimer.h
    │   │   ├── queue.c
    │   │   ├── sem.c
    │   │   ├── serviceReq.c
    │   │   ├── task.c
    │   │   ├── time.c
    │   │   ├── timer.c
    │   │   ├── utility.c
    │   │   └── utility.h
    │   └── portable
    │       ├── arch.c
    │       ├── GCC
    │       │   └── port.c
    │       └── OsArch.h
    ├── device                        #外设驱动
    │   ├── CMakeLists.txt      #[5]外设驱动CMake
    │   ├── include
    │   │   └── blast.h
    │   ├── portable
    │   │   └── ir.c
    │   └── source
    │       └── blast.c
    ├── gnu.ld                        #链接加载文件
    ├── main.c
    └── syscalls
        └── syscalls.c

如何编译

cmake采用外部构建(out-of-source build), 编译的过程文件和源文件不在同一个目录。
单独建立build文件夹,并在其内执行

frank@frank-virtual-machine:/mnt/hgfs/D/project/nuc123$ mkdir build
frank@frank-virtual-machine:/mnt/hgfs/D/project/nuc123$ cd build
frank@frank-virtual-machine:/mnt/hgfs/D/project/nuc123/build$ cmake ..

会看到生成Makefile的过程显示:
-- The C compiler identification is GNU
-- The CXX compiler identification is GNU
-- Check for working C compiler: /usr/bin/gcc
-- Check for working C compiler: /usr/bin/gcc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
Frank DEBUG: PROJECT_SOURCE_DIR /mnt/hgfs/D/project/nuc123
Frank DEBUG: CMAKE_C_COMPILER /opt/gcc-arm-none-eabi-4_9-2014q4/bin/arm-none-eabi-gcc
-- Configuring done
-- Generating done
-- Build files have been written to: /mnt/hgfs/D/project/nuc123/build    

然后执行make,就开始编译了,编译完成后执行文件会放在build/bin
 frank@frank-virtual-machine:/mnt/hgfs/D/project/nuc123/build$ make

CMake文件

CMake默认去找名为CMakeLists.txt

build.cmake

SET(CMAKE_SYSTEM_NAME Generic)            #指定系统名,既不是linux也不是windows
SET(CMAKE_SYSTEM_PROCESSOR arm)        #指定处理器名
SET(CMAKE_CROSSCOMPILING TRUE)           #指定是交叉编译

set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")        #不使用动态链接[note1]
set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "")    #不使用动态链接
set(TOOLCHAIN_PATH "/opt/gcc-arm-none-eabi-4_9-2014q4/")
set(TOOLCHAIN_FOLDER "arm-none-eabi")
set(CMAKE_C_COMPILER "${TOOLCHAIN_PATH}bin/${TOOLCHAIN_FOLDER}-gcc")            #指定C编译器
set(CMAKE_CXX_COMPILER "${TOOLCHAIN_PATH}bin/${TOOLCHAIN_FOLDER}-g++")        #指定C++编译器 
set(CMAKE_STRIP "${TOOLCHAIN_PATH}bin/${TOOLCHAIN_FOLDER}-strip")                    #指定strip工具

SET(CMAKE_FIND_ROOT_PATH ${TOOLCHAIN_PATH})                #设置交叉编译工具root路径
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

set(CMAKE_C_FLAGS "-mthumb -mcpu=cortex-m0  -Wall -fsigned-char -fno-builtin  -ffunction-sections -O0 -g")    #设置编译选项
set(CMAKE_EXE_LINKER_FLAGS "-static -Wl,-T -Xlinker ${PROJECT_SOURCE_DIR}/src/gnu.ld -u Default_Handler -nostartfiles -Wl,-Map -Xlinker -Wl,--gc-sections")        #设置链接选项

[1]顶层目录CMake

cmake_minimum_required(VERSION 2.8)        #需要CMake的最小版本
PROJECT (nuc123)                                            #定义项目名称                                    [note2]
include(cmake/build.cmake)                            #将编译选项包含进入当前的cmake文件
SET(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)   #设置生成可执行文件的目录build/bin
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib)          #设置生成库文件的目录build/lib
ADD_SUBDIRECTORY(src)                                                             #指定要进入编译的子目录src

说明
EXECUTABLE_OUTPUT_PATH 设置这个变量指定可执行文件的存放位置
LIBRARY_OUTPUT_PATH 设置这个变量指定生成库文件的存放位置
PROJECT_BINARY_DIR build文件夹

[2]源文件目录CMake

INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/src/cmsis_boot/ ${PROJECT_SOURCE_DIR}/src/cmsis_core/
                    ${PROJECT_SOURCE_DIR}/src/cmsis_lib/include/
                    ${PROJECT_SOURCE_DIR}/src/CoOS/kernel/
                    ${PROJECT_SOURCE_DIR}/src/CoOS/portable/)    #设置头文件路径
SET(SRC_LIST main.c cmsis_boot/system_NUC123.c
        cmsis_boot/startup/startup_NUC123.c
        syscalls/syscalls.c)    #将要参加编译的文件设置到变量SRC_LIST内
ADD_SUBDIRECTORY(cmsis_lib)      #指定要进入编译的子目录cmsis_lib
ADD_SUBDIRECTORY(CoOS)           #指定要进入编译的子目录CoOS
ADD_SUBDIRECTORY(device)         #指定要进入编译的子目录device
message("Frank DEBUG: PROJECT_SOURCE_DIR ${PROJECT_SOURCE_DIR}") #打印要显示的信息
message("Frank DEBUG: CMAKE_C_COMPILER ${CMAKE_C_COMPILER}")
ADD_EXECUTABLE(nuc123.elf ${SRC_LIST})    #将SRC_LIST指定的文件加入编译链接到nuc123.elf
target_link_libraries(nuc123.elf -Wl,--start-group device -Wl,--end-group) #将device产生的库链接进入nuc123.elf
target_link_libraries(nuc123.elf -Wl,--start-group CoOS -Wl,--end-group)     #将CoOS产生的库链接进入nuc123.elf
target_link_libraries(nuc123.elf -Wl,--start-group cmsis_lib -Wl,--end-group)    #将cmsis_lib产生的库链接进入nuc123.elf

[3]芯片驱动目录CMake

   INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/src/cmsis/include/)                           
   INCLUDE_DIRECTORIES(${PROJECT_SOURCE_DIR}/src/cmsis_lib/include/)    #设置头文件路径
   SET(SRC_LIST source/adc.c source/clk.c
        source/crc.c
        source/ctimer.c
        source/fmc.c
        source/gpio.c
        source/i2c.c
        source/i2s.c
        source/pdma.c
        source/ps2.c
        source/pwm.c
        source/spi.c
        source/sys.c
        source/uart.c
        source/usbd.c
        source/wdt.c
        source/wwdt.c)     #将要参加编译的文件设置到变量SRC_LIST内
ADD_LIBRARY(cmsis_lib STATIC ${SRC_LIST})  #将SRC_LIST指定的文件加入编译生成库cmsis_lib

[4]操作系统CMake

[5]外设驱动CMake

这两个文件夹下的CMakelists.txt和cmisis_lib大同小异,可以比较添加

其它信息

[note1]
Cmake在生成链接文件时,会自动加上-rdyamic, 由于arm-none-eabi不支援该参数,因此加入以下两句清空动态链接的选项
set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS ““)
set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS ““)
以上的方案来源:http://stackoverflow.com/questions/10599038/can-i-skip-cmake-compiler-tests-or-avoid-error-unrecognized-option-rdynamic

[note2]
设置编译器放在定义project之前,Cmake会自动先检查设置后的编译器,由于是使用的交叉编译器。cmake的检查方案无法通过。所以将定义projec放到最开始,cmake默认会检查系统的gcc和g++,如何跳过检查目前还没找到解决方案
以上信息来源http://www.cmake.org/Wiki/CMake_FAQ#How_do_I_use_a_different_compiler.3F
Method 3 (avoid): use set()
Set the appropriate CMAKE_FOO_COMPILER variable(s) to a valid compiler name or full path in a list file using set(). This must be done before any language is set (ie before any project() or enable_language() command).

你可能感兴趣的:(嵌入式系统使用Cmake控制编译)