1

I'm new to CMake and I'm trying to convert an existing multi-target multi-arch make project to it. Basically I'm building and flashing a firmware image for AVR onto a target device, but I want to build and run unit tests on the host prior to flashing the firmware image, in particular I want building the firmware to depend on the unit tests succeeding if possible.

Note that this is different from compiling the same code in 32/64 configurations which seem to be solved by doing out of tree builds using environment variables to tell cmake which target to build from. As I need to work with both archs at the same time without twiddling with re-running cmake in between.

The principal source structure is like so:

├── CMakeLists.txt
├── src
│   └── Bar.cpp
├── lib
│   └── external/arch/foo.cpp
└── test
    └── BarTest.cpp

I need all files in "src" and some files from "lib/external/avr/" to build my AVR target using avr-g++.

To build the tests that are ran on the host (x86(-64)) I need all files in "test" and some files from "src" (the code under test) and some files from "lib/external/x86/" and I need to build on the host compiler (don't care if it's gcc or clang) with slightly different flags. This is complicated in my make file above by the fact that the same file needs multiple object files built for different architectures and thus they need different output dirs.

In the end I want to be able to do make test to run the host tests and make upload to compile and program the target device (only if tests pass).

What I currently have in my CMakeLists.txt:

cmake_minimum_required(VERSION 2.8)

set(AVRCPP   avr-g++)
set(AVRSTRIP avr-strip)
set(OBJCOPY  avr-objcopy)
set(OBJDUMP  avr-objdump)
set(AVRSIZE  avr-size)
set(AVRDUDE  avrdude)

set(CXXSTANDARD "-std=c++14")
set(COPT      "-O3")
set(CWARN     "-Werror -Wall -Wextra -pedantic -Wno-attributes")
set(CTUNING   "-fno-threadsafe-statics -fpack-struct -fshort-enums -fno-stack-protector")

set(BASE_AVR_CXXFLAGS "${CXXSTANDARD} ${COPT} ${CWARN} ${CTUNING}")

set(CMAKE_SYSTEM_NAME  Generic)
set(CMAKE_CXX_COMPILER ${AVRCPP})

#
# Probe project
#
project(probe CXX)

set(MCU "atmega328p")
set(CMCU "-mmcu=${MCU} -DF_CPU=16000000UL")
set(CDEFS "-DUART_BAUD=9600 -DUART_DATA_BITS=8 -DUART_PARITY_BITS=0 -DUART_STOP_BITS=1")
set(CMAKE_CXX_FLAGS "${BASE_AVR_CXXFLAGS} ${CMCU} ${DEFS}")

set(BASE_PATH    "${${PROJECT_NAME}_SOURCE_DIR}")
set(INC_PATH     "${BASE_PATH}/include")
set(SRC_PATH     "${BASE_PATH}/src")
set(LIB_DIR_PATH "${BASE_PATH}/lib")
set(XTD_SRC_PATH "${BASE_PATH}/xtd_uc/src")

include_directories(${INC_PATH} "xtd_uc/include")
file(GLOB SRC_FILES "${SRC_PATH}/*.cpp")
set(XTD_SRC_FILES 
  ${XTD_SRC_PATH}/delay.cpp
  ${XTD_SRC_PATH}/atmega/adc.cpp
  ${XTD_SRC_PATH}/atmega/bootstrap.cpp
  ${XTD_SRC_PATH}/atmega/chrono.cpp
  ${XTD_SRC_PATH}/atmega/gpio.cpp
  ${XTD_SRC_PATH}/atmega/sleep.cpp
  ${XTD_SRC_PATH}/atmega/uart.cpp
  ${XTD_SRC_PATH}/atmega/wdt.cpp
  )
add_executable(${PROJECT_NAME} ${SRC_FILES} ${XTD_SRC_FILES})
set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "${PROJECT_NAME}.elf")

# Compiling targets
add_custom_target(strip ALL     ${AVRSTRIP} "${PROJECT_NAME}.elf" DEPENDS ${PROJECT_NAME})
add_custom_target(hex   ALL     ${OBJCOPY} -R .eeprom -O ihex "${PROJECT_NAME}.elf" "${PROJECT_NAME}.hex" DEPENDS strip)
add_custom_target(eeprom        ${OBJCOPY} -j .eeprom --change-section-lma .eeprom=0 -O ihex "${PROJECT_NAME}.elf" "${PROJECT_NAME}.eeprom" DEPENDS strip)
add_custom_target(disassemble   ${OBJDUMP} -S "${PROJECT_NAME}.elf" > "${PROJECT_NAME}.lst" DEPENDS strip)

# Flashing targets
set(PROG_TYPE_ARDUINO "-v -c arduino -b 115200 -P /dev/ttyUSB0")
set(PROG_TYPE_AVRISP "-v -c avrisp -b 19200 -P /dev/ttyACM0")
set(PROG_TYPE ${PROG_TYPE_ARDUINO})

add_custom_target(flash_program ${AVRDUDE} ${PROG_TYPE} -p ${MCU} -U flash:w:${PROJECT_NAME}.hex DEPENDS hex)
add_custom_target(flash_eeprom ${AVRDUDE} ${PROG_TYPE} -p ${MCU} -U eeprom:w:${PROJECT_NAME}.hex DEPENDS eeprom)

set_directory_properties(PROPERTIES ADDITIONAL_MAKE_CLEAN_FILES "${PROJECT_NAME}.hex;${PROJECT_NAME}.eeprom;${PROJECT_NAME}.lst")

message("* ")
message("* Project Name:\t${PROJECT_NAME}")
message("* Project Source:\t${SRC_PATH}")
message("* Project Include:\t${INC_PATH}")
message("* Library Include:\t${LIB_INC_PATH}")
message("* ")
message("* Project Source Files:\t${SRC_FILES}")
message("* XTD Source Files:\t${XTD_SRC_FILES}")
message("* ")
message("* CXX Flags:\t${CMAKE_CXX_FLAGS}")
message("* ")

And it works for building the AVR target but I'm at a loss for how to integrate the tests into this with a different compiler/target-arch.

Any help with how I would achieve the above with cmake would be greatly appreciated.

Emily L.
  • 5,673
  • 2
  • 40
  • 60
  • 1
    CMake **cannot** configure the project for **several architectures** at once. Some CMake generators (e.g. Visual Studio) supports several configurations to be processed by a single CMake run, but those configurations usually differ slightly, like 32/64 flavours of a single architecture. You may, however, configure the project for one architecture, and include via `ExternalProject_Add` other projects, configured for other architectures. – Tsyvarev Jan 18 '19 at 15:55

0 Answers0