将 Makefile 转换为 Tiva C 系列的 CMakeLists.txt

Converting a Makefile to CMakeLists.txt for Tiva C Series

我想使用 CMake 构建过程在 C++ 中对我的 Tiva C 系列 LaunchPad 板进行编程。我下载了一个简单的示例来闪烁我使用 make 构建的 RGB LED,我希望能够使用 cmake 来启动一个更大的项目。

这里是示例中提供的 Makefile :

# Tiva Makefile
# #####################################
#
# Part of the uCtools project
# uctools.github.com
#
#######################################
# user configuration:
#######################################
# TARGET: name of the output file
TARGET = firmware
# MCU: part number to build for
MCU = TM4C123GH6PM
# SOURCES: list of input source sources
SOURCES = main.c startup_gcc.c
# INCLUDES: list of includes, by default, use Includes directory
INCLUDES = -IInclude
# OUTDIR: directory to use for output
OUTDIR = build
# TIVAWARE_PATH: path to tivaware folder
TIVAWARE_PATH = ../tivaware

# LD_SCRIPT: linker script
LD_SCRIPT = $(MCU).ld

# define flags
CFLAGS = -g -mthumb -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=softfp
CFLAGS +=-Os -ffunction-sections -fdata-sections -MD -std=c99 -Wall
CFLAGS += -pedantic -DPART_$(MCU) -c -I$(TIVAWARE_PATH)
CFLAGS += -DTARGET_IS_BLIZZARD_RA1
LDFLAGS = -T $(LD_SCRIPT) --entry ResetISR --gc-sections

#######################################
# end of user configuration
#######################################
#
#######################################
# binaries
#######################################
CC = arm-none-eabi-gcc
LD = arm-none-eabi-ld
OBJCOPY = arm-none-eabi-objcopy
RM      = rm -f
MKDIR   = mkdir -p
#######################################

# list of object files, placed in the build directory regardless of source path
OBJECTS = $(addprefix $(OUTDIR)/,$(notdir $(SOURCES:.c=.o)))

# default: build bin
all: $(OUTDIR)/$(TARGET).bin

$(OUTDIR)/%.o: src/%.c | $(OUTDIR)
    $(CC) -o $@ $^ $(CFLAGS)

$(OUTDIR)/a.out: $(OBJECTS)
    $(LD) -o $@ $^ $(LDFLAGS)

$(OUTDIR)/$(TARGET).bin: $(OUTDIR)/a.out
    $(OBJCOPY) -O binary $< $@

# create the output directory
$(OUTDIR):
    $(MKDIR) $(OUTDIR)

clean:
    -$(RM) $(OUTDIR)/*

.PHONY: all clean

我的第一个 CMakeLists.txt 文件基于它:

project(firmware)
cmake_minimum_required(VERSION 2.8)

# this one is important
set(CMAKE_SYSTEM_NAME Generic)
#this one not so much
#set(CMAKE_SYSTEM_VERSION 1)

# specify the toolchain
set(TOOLCHAIN_PREFIX ${PROJECT_SOURCE_DIR}/../toolchain/bin/arm-none-eabi-)
set(CMAKE_C_COMPILER   ${TOOLCHAIN_PREFIX}gcc)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++)
set(CMAKE_OBJCOPY ${TOOLCHAIN_PREFIX}objcopy)
set(CMAKE_AR ${TOOLCHAIN_PREFIX}ar)

# set compiler flags
set(MCU TM4C123GH6PM)
set(COMMON_FLAGS "-mthumb -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=softfp \
    -ffunction-sections -fdata-sections -pedantic \
    -MD -DPART_${MCU} -DTARGET_IS_BLIZZARD_RA1")

set(CMAKE_C_FLAGS_DEBUG "-g -Wall ${COMMON_FLAGS}")
set(CMAKE_CXX_FLAGS_DEBUG "-g -Wall -std=c++11 ${COMMON_FLAGS}")
set(CMAKE_C_FLAGS_RELEASE "-O2 -DNOTEST ${COMMON_FLAGS}")
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -std=c++11 -DNOTEST ${COMMON_FLAGS}")

# search for programs in the build host directories
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# for libraries and headers in the target directories
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

# add TivaWare header files to the project
include_directories(${PROJECT_SOURCE_DIR}/../tivaware)

# add source files to the project
aux_source_directory(. SRC_LIST)
add_executable(${PROJECT_NAME} ${SRC_LIST})

# set linker flags
set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS)
set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS)
set_target_properties(${PROJECT_NAME}
    PROPERTIES
    LINK_FLAGS "-T ${MCU}.ld --entry ResetISR --gc-sections"
)

# define objcopy macro
macro(OBJCOPY_FILE EXE_NAME)
    set(FO ${CMAKE_CURRENT_BINARY_DIR}/${EXE_NAME}.bin)
    set(FI ${CMAKE_CURRENT_BINARY_DIR}/${EXE_NAME})
    message(STATUS ${FO})
    add_custom_command(
        OUTPUT ${FO}
        COMMAND ${CMAKE_OBJCOPY}
        ARGS -O binary -I elf32-little ${FI} ${FO}
        DEPENDS ${FI}
    )
    get_filename_component(TGT "${EXE_NAME}" NAME)
    add_custom_target("target-objcopy_${TGT}" ALL DEPENDS ${FO} VERBATIM)
    get_directory_property(extra_clean_files ADDITIONAL_MAKE_CLEAN_FILES)
    set_directory_properties(
        PROPERTIES
        ADDITIONAL_MAKE_CLEAN_FILES "${extra_clean_files};${FO}"
    )
    set_source_files_properties("${FO}" PROPERTIES GENERATED TRUE)
endmacro(OBJCOPY_FILE)

# set the objcopy for binary file
objcopy_file(${PROJECT_NAME})

它通过了 CMake 步骤,但是当我尝试使用 make 进行编译时,我得到

arm-none-eabi-g++: error: unrecognized command line option '--gc-sections'

我想链接器标志应该与 arm-none-eabi-ld 一起使用。我该怎么做?

编辑1:

我仍然不知道如何设置正确的链接器 exe 和标志,但我发现 CMake 在固件中生成了一个文件。dir/link.txt。它的内容是

~/Documents/crh-2016/src/tiva/firmware/../toolchain/bin/arm-none-eabi-g++   -O2 -std=c++11 -fno-exceptions -DNOTEST -mthumb -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=softfp     -ffunction-sections -fdata-sections -pedantic     -MD -DPART_TM4C123GH6PM -DTARGET_IS_BLIZZARD_RA1    -T ~/Documents/crh-2016/src/tiva/firmware/TM4C123GH6PM.ld --entry ResetISR --gc-sections CMakeFiles/firmware.dir/main.cpp.o CMakeFiles/firmware.dir/startup_gcc.cpp.o  -o firmware  

然后我将其编辑为我想要临时解决此问题的内容

~/Documents/crh-2016/src/tiva/firmware/../toolchain/bin/arm-none-eabi-ld -T ~/Documents/crh-2016/src/tiva/firmware/TM4C123GH6PM.ld --entry ResetISR --gc-sections CMakeFiles/firmware.dir/main.cpp.o CMakeFiles/firmware.dir/startup_gcc.cpp.o  -o firmware

但 LD 似乎不喜欢 G++ 生成的 .o 文件,因为 make

toolchain/bin/arm-none-eabi-ld: warning: cannot find entry symbol ResetISR; defaulting to 00000000

将我的评论变成答案

编辑: 正如 Marc Glisse 在 you could pass linker flags in CMAKE_EXE_LINKER_FLAGS with -Wl,XXX, see e.g. cflags '-Wl,-export-dynamic' vs linker flags '-export-dynamic'

中提到的

那么您就不需要将链接器命令更改为 ld


为了与您的 makefile 兼容,您可以使用 CMAKE_LINKERCMAKE_CXX_LINK_EXECUTABLE 变量来更改链接器命令,例如调用 ld

关于您的问题:

  • 如果没有使用链接器标志,只需将它们直接放入链接器命令行本身即可。
  • 当得到 "cannot find entry symbol"
    • 如果 ResetISR 存在,你应该检查 map 文件
    • 您可以尝试将 --gc-sections 替换为 --discard-none
    • 如果在 CMake 的尝试编译步骤中得到 "cannot find entry symbol",则激活 ..._COMPILER_WORKS 标志(见下文)

我已将您的代码移至 toolchain file for readability and to demonstrate what have worked for me on other "bare-metal" cross-compiling:

TM4C123Toolchain.cmake

# this one is important
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(MCU TM4C123GH6PM)

# Optional for testing
#set(CMAKE_C_COMPILER_WORKS 1 CACHE INTERNAL "")
#set(CMAKE_CXX_COMPILER_WORKS 1 CACHE INTERNAL "")

# specify the toolchain
set(CMAKE_PREFIX_PATH "${PROJECT_SOURCE_DIR}/../toolchain/bin")
set(TOOLCHAIN_PREFIX "arm-none-eabi-")

# add processor specific definitions
add_definitions(
    -DPART_TM4C123GH6PM
    -DTARGET_IS_TM4C123_RA1
    -Dgcc
)

# add TivaWare header files to the project
set(TIVAWARE_PATH "${PROJECT_SOURCE_DIR}/../tivaware")
include_directories(${TIVAWARE_PATH})

#list(
#    APPEND _cxx_standard_libraries_list 
#        "-l${TIVAWARE_PATH}/usblib/gcc/libusb.a" 
#        "-l${TIVAWARE_PATH}/driverlib/gcc/libdriver.a"
#)
#unset(CMAKE_CXX_STANDARD_LIBRARIES CACHE)
#string(REPLACE ";" " " CMAKE_CXX_STANDARD_LIBRARIES_INIT "${_cxx_standard_libraries_list}")

set(CMAKE_CXX_COMPILER_ENV_VAR "")

unset(CMAKE_C_COMPILER CACHE)
find_program(CMAKE_C_COMPILER NAMES ${TOOLCHAIN_PREFIX}gcc)
unset(CMAKE_CXX_COMPILER CACHE)
find_program(CMAKE_CXX_COMPILER NAMES ${TOOLCHAIN_PREFIX}g++)

unset(CMAKE_ASM_COMPILER CACHE)
find_program(CMAKE_ASM_COMPILER NAMES ${TOOLCHAIN_PREFIX}as)
unset(CMAKE_OBJCOPY CACHE)
find_program(CMAKE_OBJCOPY NAMES ${TOOLCHAIN_PREFIX}objcopy)
unset(CMAKE_LINKER CACHE)
find_program(CMAKE_LINKER NAMES ${TOOLCHAIN_PREFIX}ld)

# set compiler flags
# NOTE: The following variables are cached by default (CMake<Lang>Information.cmake), 
#       so we have to prefill the cache with our values. They won't be overwritten.
set(CMAKE_C_FLAGS "-mthumb -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=softfp \
    -ffunction-sections -fdata-sections -pedantic \
    -MD -DPART_${MCU} -DTARGET_IS_BLIZZARD_RA1" CACHE INTERNAL "" FORCE)
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -std=c++11" CACHE INTERNAL "" FORCE)
set(CMAKE_C_FLAGS_DEBUG "-g -Wall" CACHE INTERNAL "" FORCE)
set(CMAKE_CXX_FLAGS_DEBUG "-g -Wall" CACHE INTERNAL "" FORCE)
set(CMAKE_C_FLAGS_RELEASE "-O2 -DNOTEST" CACHE INTERNAL "" FORCE)
set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNOTEST" CACHE INTERNAL "" FORCE)
set(CMAKE_ASM_FLAGS "-mthumb -mcpu=cortex-m4" CACHE INTERNAL "" FORCE)

set(CMAKE_EXECUTABLE_SUFFIX_CXX ".elf" CACHE INTERNAL "" FORCE)          

set(CMAKE_CXX_LINK_EXECUTABLE "<CMAKE_LINKER> <CMAKE_CXX_LINK_FLAGS> -EL -n -Map=<TARGET_NAME>.map -T ${MCU}.ld --entry ResetISR --gc-sections -o <TARGET> --start-group <OBJECTS> <LINK_LIBRARIES> --end-group --cref")

set(CMAKE_C_USE_RESPONSE_FILE_FOR_OBJECTS 0)
set(CMAKE_CXX_USE_RESPONSE_FILE_FOR_OBJECTS 0)

set(CMAKE_C_RESPONSE_FILE_LINK_FLAG "@")
set(CMAKE_CXX_RESPONSE_FILE_LINK_FLAG "@")

# search for programs in the build host directories
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# for libraries and headers in the target directories
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

添加调用

cmake -DCMAKE_TOOLCHAIN_FILE:string=TM4C123Toolchain.cmake ...

背景

  • 我在 CMAKE_CXX_LINK_EXECUTABLE 命令行中添加了带有 -Map 的映射文件以进行调试。并且 --start-group/--end-group 因为我的发行版的标准库具有循环依赖性。
  • 我更喜欢 CMAKE_PREFIX_PATH and find_program(),因为工具链供应商或版本之间的路径和名称可能会有所不同(并且都接受 paths/names 的列表)。
  • 我更喜欢 FORCE 变量设置,因为我不使用用户可以更改缓存的编译器设置的功能,我更喜欢工具链文件中的更改以激活而无需 运行 再次从头开始制作。
  • CMake 将 _DEBUG_RELEASE 标志附加到标准标志。

参考资料

我找到了使用 -specs 标志的解决方案。

project(firmware)
cmake_minimum_required(VERSION 2.8)

# set cross compilation information
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR arm)

# specify the toolchain
set(TOOLCHAIN_PREFIX ${PROJECT_SOURCE_DIR}/../toolchain/bin/arm-none-eabi-)
set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++)
set(CMAKE_ASM_COMPILER ${TOOLCHAIN_PREFIX}as)
set(CMAKE_AR ${TOOLCHAIN_PREFIX}ar)
set(CMAKE_OBJCOPY ${TOOLCHAIN_PREFIX}objcopy)
set(CMAKE_OBJDUMP ${TOOLCHAIN_PREFIX}objdump)

enable_language(ASM)

# set compiler flags
set(CPU "-mcpu=cortex-m4")
set(FPU "-mfpu=fpv4-sp-d16 -mfloat-abi=softfp")
set(CMAKE_ASM_FLAGS "-mthumb ${CPU} ${FPU} -MD")
set(CMAKE_C_FLAGS "-mthumb ${CPU} ${FPU} -std=gnu99 -Os -ffunction-sections -fdata-sections -MD -Wall -pedantic")
set(CMAKE_CXX_FLAGS "-mthumb ${CPU} ${FPU} -Os -ffunction-sections -fdata-sections -MD -Wall -pedantic -std=c++11 -fno-exceptions -fno-rtti")

# set linker flags
set(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "")
set(CMAKE_SHARED_LIBRARY_LINK_CXX_FLAGS "")
set(CMAKE_EXE_LINKER_FLAGS "-T${PROJECT_SOURCE_DIR}/tm4c123g.ld -specs=${PROJECT_SOURCE_DIR}/tiva.specs")

# add processor specific definitions
add_definitions(-DPART_TM4C123GH6PM)
add_definitions(-DTARGET_IS_TM4C123_RA1)
add_definitions(-Dgcc)

# add TivaWare header files to the project
set(TIVAWARE_PATH "${PROJECT_SOURCE_DIR}/../tivaware")
include_directories(${TIVAWARE_PATH})

# add source files to the project
aux_source_directory(. SRC_LIST)
add_executable(${PROJECT_NAME} ${SRC_LIST})

# add linked library to the project
target_link_libraries(${PROJECT_NAME}
    ${TIVAWARE_PATH}/usblib/gcc/libusb.a
    ${TIVAWARE_PATH}/driverlib/gcc/libdriver.a
)

# define objcopy macro
macro(OBJCOPY_FILE EXE_NAME)
    set(FO ${CMAKE_CURRENT_BINARY_DIR}/${EXE_NAME}.bin)
    set(FI ${CMAKE_CURRENT_BINARY_DIR}/${EXE_NAME})
    message(STATUS ${FO})
    add_custom_command(
        OUTPUT ${FO}
        COMMAND ${CMAKE_OBJCOPY}
        ARGS -O binary ${FI} ${FO}
        DEPENDS ${FI}
    )
    get_filename_component(TGT "${EXE_NAME}" NAME)
    add_custom_target("target-objcopy_${TGT}" ALL DEPENDS ${FO} VERBATIM)
    get_directory_property(extra_clean_files ADDITIONAL_MAKE_CLEAN_FILES)
    set_directory_properties(
        PROPERTIES
        ADDITIONAL_MAKE_CLEAN_FILES "${extra_clean_files};${FO}"
    )
    set_source_files_properties("${FO}" PROPERTIES GENERATED TRUE)
endmacro(OBJCOPY_FILE)

# set the objcopy for binary file
objcopy_file(${PROJECT_NAME})

与 tiva.specs:

*link:
--entry ResetISR --gc-sections 

*lib:
-lm -lc

和 tm4c123g.ld 作为链接描述文件:

/******************************************************************************
 *
 * Linker configuration file.
 *
 * Copyright (c) 2012-2014 Texas Instruments Incorporated.  All rights reserved.
 * Software License Agreement
 * 
 * Texas Instruments (TI) is supplying this software for use solely and
 * exclusively on TI's microcontroller products. The software is owned by
 * TI and/or its suppliers, and is protected under applicable copyright
 * laws. You may not combine this software with "viral" open-source
 * software in order to form a larger program.
 * 
 * THIS SOFTWARE IS PROVIDED "AS IS" AND WITH ALL FAULTS.
 * NO WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT
 * NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. TI SHALL NOT, UNDER ANY
 * CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL, OR CONSEQUENTIAL
 * DAMAGES, FOR ANY REASON WHATSOEVER.
 * 
 * This is part of revision 2.1.0.12573 of the EK-TM4C123GXL Firmware Package.
 *
 *****************************************************************************/

MEMORY
{
    FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x00040000
    SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00008000
}

SECTIONS
{
    .text :
    {
        _text = .;
        KEEP(*(.isr_vector))
        *(.text*)
        *(.rodata*)
        _etext = .;
    } > FLASH

    .data : AT(ADDR(.text) + SIZEOF(.text))
    {
        _data = .;
        *(vtable)
        *(.data*)
        _edata = .;
    } > SRAM

    .bss :
    {
        _bss = .;
        *(.bss*)
        *(COMMON)
        _ebss = .;
    } > SRAM
}