CLion without PlatformIO?

Old thread, but I finally had to remove platformio. Posting this here for anyone else that comes by.

This has only been used on Linux. Not sure what happens on Windows or macOS.

### Arduino
The Arduino IDE is used for the compiler, Teensy cores and libraries. All updates are handled through the Arduino IDE and its board and library manager.
1. Download and install the Arduino IDE 2.x
- https://www.arduino.cc/en/software
2. Install Teensy into Arduino
- https://www.pjrc.com/teensy/td_download.html
- In `File > Preferences`
- `Show verbose output`: `compile` and `upload` (useful for toolchain development)
- `Additional boards manager URLs`: `https://www.pjrc.com/teensy/package_teensy_index.json`

### CLion
#### Toolchain
Create a new toolchain in Clion Settings:
1. In Clion `Settings > Build, Execution, Deployment > Toolchains`
2. Remove exciting
3. Create new toolchain:
- Name: `arduino_teensy`
- CMake `Bundled` (default)
- Build Tool: `Ninja` (default)
- C Compiler: `/home/{{user}}/.arduino15/packages/teensy/tools/teensy-compile/11.3.1/arm/bin/arm-none-eabi-gcc` (replace user)
- C++ Compiler: `/home/{{craiuserg}}/.arduino15/packages/teensy/tools/teensy-compile/11.3.1/arm/bin/arm-none-eabi-g++` (replace user)
### CMake
Crate a cmake profile
1. In Clion `Settings > Build, Execution, Deployment > CMake`
2. Delete any existing profiles
3. Create a new profile:
-Name: `Release`
-Build Type: `Release`
-Toolchain: `arduino_teensy` (default)
-Generator: `Ninja` (default)
-CMake Options: (leave empty to use defaults in grey)
-Build Options: (leave empty to use defaults in grey)
### Configurations
The following configurations are created:
- `{{project}}_elf`: compiles project to elf
- `{{project}}_hex`: compiles and converts elf to hex
- `{{project}}_upload`: compiles and uploads

Code:
cmake_minimum_required(VERSION 3.27)

# arduino home
# must install arduino ide
# https://www.arduino.cc/en/software
set(ARDUINO_PATH "$ENV{HOME}/.arduino15")

# add teensy 4 (4.0 and 4.1) toolchain
# path changes for each project
include("${CMAKE_SOURCE_DIR}/../../cmake/teensy4.toolchain.cmake")

# project
project(examples C CXX)

# remove clion -MD so can use -MMD
# https://discourse.cmake.org/t/how-to-force-cmake-use-mmd-flag-instead-of-md/9120/8
string(REPLACE "-MD" "" CMAKE_DEPFILE_FLAGS_C "${CMAKE_DEPFILE_FLAGS_C}")
string(REPLACE "-MD" "" CMAKE_DEPFILE_FLAGS_CXX "${CMAKE_DEPFILE_FLAGS_CXX}")

## set vars
set(TEENSY_VERSION 40 "[40, 41]" FORCE)

# arduino libs
teensy_add_arduino_library(EEPROM)
teensy_add_arduino_library(SPI)
teensy_add_arduino_library(Wire)

# executable: blank
teensy_add_executable(blink blink.cpp)

# executable: libs
teensy_add_executable(libs libs.cpp)
teensy_target_link_libraries(libs EEPROM SPI Wire)

# executable: serial
teensy_add_executable(serial serial.cpp)

Code:
# GoToTags

# heavily based on code from these projects:
# https://github.com/newdigate/teensy-cmake-macros/blob/main/cmake/teensy.cmake
# https://github.com/ronj/teensy-cmake-template/blob/master/toolchains/teensy.cmake
# https://github.com/platformio/platform-teensy
# https://github.com/PaulStoffregen/cores/blob/master/teensy4/Makefile

cmake_minimum_required(VERSION 3.25)

# cmake
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(CMAKE_TRY_COMPILE_TARGET_TYPE "STATIC_LIBRARY")
set(CMAKE_CXX_STANDARD 17)

set(TEENSY_CORE_PATH "${ARDUINO_PATH}/packages/teensy/hardware/avr/1.59.0/cores/teensy4/" CACHE INTERNAL "TEENSY_CORE_PATH")
message(STATUS "TEENSY_CORE_PATH: ${TEENSY_CORE_PATH}")

set(ARDUINO_LIBS_PATH "${ARDUINO_PATH}/packages/teensy/hardware/avr/1.59.0/libraries/" CACHE INTERNAL "ARDUINO_LIBS_PATH")
message(STATUS "ARDUINO_LIBS_PATH: ${ARDUINO_LIBS_PATH}")

set(TEENSY_LOADER_CLI "${CMAKE_CURRENT_LIST_DIR}/../teensy_loader_cli/linux/teensy_loader_cli" CACHE INTERNAL "TEENSY_LOADER_CLI")
message(STATUS "TEENSY_LOADER_CLI: ${TEENSY_LOADER_CLI}")

# remove clion defaults
string(REPLACE "-O3" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")

# teensy init
function(teensy_init)
    #message(STATUS "teensy_init()")

    get_property(teensy_init_run GLOBAL PROPERTY TEENSY_INIT_RUN)

    if (NOT teensy_init_run)
        message(STATUS "teensy_init_run()")
        set_property(GLOBAL PROPERTY TEENSY_INIT_RUN TRUE)

        teensy_vars()

        teensy_lib()
    endif()
endfunction()

# teensy vars
function(teensy_vars)
    #message(STATUS "teensy_vars()")

    get_property(teensy_vars_run GLOBAL PROPERTY TEENSY_VARS_RUN)

    if (NOT teensy_vars_run)
        message(STATUS "teensy_vars_run()")
        set_property(GLOBAL PROPERTY TEENSY_VARS_RUN TRUE)

        # teensy version
        if (NOT DEFINED TEENSY_VERSION)
            message(FATAL_ERROR "TEENSY_VERSION not set!")
        else()
            message(STATUS "TEENSY_VERSION: ${TEENSY_VERSION}")
        endif()

        # vars different for teensy versions
        if(TEENSY_VERSION EQUAL 40)
            set(MCU "ARDUINO_TEENSY40")
            set(build_flags_optimize "-Os")
            set(LINKER_FILE ${TEENSY_CORE_PATH}imxrt1062.ld)

        elseif (TEENSY_VERSION EQUAL 41)
            set(MCU "ARDUINO_TEENSY41")
            set(build_flags_optimize "-O2")
            set(LINKER_FILE ${TEENSY_CORE_PATH}imxrt1062_t41.ld)
        else()
            message(FATAL_ERROR "Unknown TEENSY_VERSION: ${TEENSY_VERSION}")
        endif()

        # common vars
        set(build_flags_cpu "-mthumb -mcpu=cortex-m7 -mfloat-abi=hard -mfpu=fpv5-d16")
        set(build_flags_libs "-lm")
        set(build_flags_ld "-Wl,--gc-sections,--relax,-v")

        # compile flags
        set(COMPILE_FLAGS "-v -D${MCU} -D__IMXRT1062__ -DARDUINO=10607 -DTEENSYDUINO=159 -DF_CPU=600000000 -DUSB_SERIAL -DLAYOUT_US_ENGLISH ${build_flags_cpu} -Wall -g ${build_flags_optimize} -ffunction-sections -fdata-sections -MMD")

        # these are cached for use outside scope of this function

        # compile c++
        set(CPP_COMPILE_FLAGS "${COMPILE_FLAGS} -fno-exceptions -fpermissive -fno-rtti -fno-threadsafe-statics -felide-constructors -Wno-error=narrowing" CACHE INTERNAL "CPP_COMPILE_FLAGS")
        #message(STATUS "CPP_COMPILE_FLAGS: ${CPP_COMPILE_FLAGS}")

        # compile c
        set(C_COMPILE_FLAGS "${COMPILE_FLAGS}" CACHE INTERNAL "C_COMPILE_FLAGS")
        #message(STATUS "C_COMPILE_FLAGS: ${C_COMPILE_FLAGS}")

        # link flags
        set(LINK_FLAGS "${build_flags_optimize} ${build_flags_ld} ${build_flags_cpu} -T${LINKER_FILE} ${build_flags_libs}" CACHE INTERNAL "C_COMPILE_FLAGS")
        #message(STATUS "LINK_FLAGS: ${LINK_FLAGS}")
    endif()
endfunction()

function(teensy_lib)
    message(STATUS "teensy_lib()")

    set(TEENSY_LIB Teensy CACHE INTERNAL "TEENSY_LIB")

    teensy_add_library(${TEENSY_LIB} ${TEENSY_CORE_PATH})
endfunction()

# teensy_add_executable
function(teensy_add_executable TARGET)
    message(STATUS "teensy_add_executable(${TARGET} ${ARGN})")

    teensy_init()

    # find all .cpp files and add to TEENSY_EXE_CPP_SOURCES
    foreach(arg IN LISTS ARGN)
        #message(STATUS "teensy_add_executable: arg=${arg}")
        file(GLOB TEST_SOURCE RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${arg})
        list(FILTER TEST_SOURCE INCLUDE REGEX ".cpp$")
        set(TEENSY_EXE_CPP_SOURCES ${TEENSY_EXE_CPP_SOURCES} ${TEST_SOURCE})
    endforeach()
    #message(STATUS "teensy_add_executable: TEENSY_EXE_CPP_SOURCES=${TEENSY_EXE_CPP_SOURCES}")

    # set_source_files_properties on app .cpp files
    foreach(SOURCE_CPP ${TEENSY_EXE_CPP_SOURCES})
        #message(STATUS "teensy_add_executable: SOURCE_CPP=${SOURCE_CPP}")
        set_source_files_properties(${SOURCE_CPP} PROPERTIES COMPILE_FLAGS "${CPP_COMPILE_FLAGS}")
    endforeach()

    # elf
    set(ELF_TARGET "${TARGET}_elf")
    set(ELF_FILE "${TARGET}.elf")

    # executable
    add_executable(${ELF_TARGET} ${TEENSY_EXE_CPP_SOURCES})
    set_target_properties(${ELF_TARGET} PROPERTIES OUTPUT_NAME ${ELF_FILE})

    # link flags
    set_target_properties(${ELF_TARGET} PROPERTIES LINK_FLAGS "${LINK_FLAGS}")
#    target_link_options(${ELF_TARGET} PUBLIC ${LINK_FLAGS})

    # link to Teensy lib
    target_link_libraries(${ELF_TARGET} PRIVATE ${TEENSY_LIB})

    # elf
    set(HEX_TARGET "${TARGET}_hex")
    set(HEX_FILE "${TARGET}.hex")

    # convert elf to hex
    add_custom_command(OUTPUT ${HEX_FILE}
        COMMAND ${COMPILERPATH}arm-none-eabi-size ${ELF_FILE}
        COMMAND ${COMPILERPATH}arm-none-eabi-objcopy -O ihex -R .eeprom ${ELF_FILE} ${HEX_FILE}
        DEPENDS ${ELF_TARGET}
        COMMENT "Converting ${ELF_FILE} to ${HEX_FILE}")

    add_custom_target(${HEX_TARGET} ALL DEPENDS ${HEX_FILE})

    # build elf before hex
    add_dependencies(${HEX_TARGET} ${ELF_TARGET})

    # upload target
    add_custom_target("${TARGET}_upload"
        COMMAND ${TEENSY_LOADER_CLI} -w -s -v -mmcu=IMXRT1062 ${HEX_FILE}
        DEPENDS ${HEX_FILE}
        COMMENT "Uploading ${HEX_FILE} to Teensy 4"
    )
endfunction()

function(teensy_add_arduino_library LIB_NAME)
    message(STATUS "teensy_add_arduino_library(${LIB_NAME})")

    teensy_init()

    set(LIB_ROOT "${ARDUINO_LIBS_PATH}${LIB_NAME}/")

    teensy_add_library(${LIB_NAME} ${LIB_ROOT})
endfunction()

function(teensy_add_library LIB_NAME LIB_ROOT)
    message(STATUS "teensy_add_library(${LIB_NAME} ${LIB_ROOT})")

    teensy_init()

    # lib .c files
    file(GLOB_RECURSE C_FILES ABSOLUTE ${LIB_ROOT}**.c)
    #message(STATUS "C_FILES: ${C_FILES}")
    foreach(SOURCE_C ${C_FILES})
        set_source_files_properties(${SOURCE_C} PROPERTIES COMPILE_FLAGS "${C_COMPILE_FLAGS}")
    endforeach()

    # lib .cpp files
    file(GLOB_RECURSE CPP_FILES ${LIB_ROOT}**.cpp)
    #message(STATUS "CPP_FILES: ${CPP_FILES}")
    foreach(SOURCE_CPP ${CPP_FILES})
        set_source_files_properties(${SOURCE_CPP} PROPERTIES COMPILE_FLAGS "${CPP_COMPILE_FLAGS}")
    endforeach()

    set(LIB_SOURCES ${C_FILES} ${CPP_FILES})
    #message(STATUS "LIB_SOURCES: ${LIB_SOURCES}")

    set(LIB_TARGET ${LIB_NAME})

    # add library
    add_library(${LIB_TARGET} STATIC ${LIB_SOURCES})

    # include headers
    target_include_directories(${LIB_TARGET} PUBLIC ${TEENSY_CORE_PATH} ${LIB_ROOT})

    # link options
    target_link_options(${LIB_TARGET} PRIVATE ${LINK_FLAGS})
endfunction()

# teensy_target_link_libraries
macro(teensy_target_link_libraries TARGET)
    message(STATUS "teensy_target_link_libraries(${TARGET} ${ARGN})")

    set(ELF_TARGET "${TARGET}_elf")

    set(libs "${ARGN}")
    foreach(lib IN LISTS libs)
        target_link_libraries(${ELF_TARGET} PRIVATE ${lib})
    endforeach()
endmacro()
 
Back
Top