之前曾自己写makefile控制嵌入式项目的编译,由于对makefile不精通,rule写得不好,导致效率不高,文件多时也比较慢。最近接触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默认去找名为CMakeLists.txt
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") #设置链接选项
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文件夹
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
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
这两个文件夹下的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).