Multiple undefined reference during linking of a CMake project using ARM toolchain - cmake

I'm developing a build system using CMake to build applications using the arm-none-eabi toolchain.
This is my folder structure:
project/
├── apps/
│ ├── test_app
│ │ ├── inc/
│ │ ├── src/
│ │ ├── CMakeLists.txt
├── arch/
│ ├── CMSIS/
│ ├── include/
│ ├── startup/
│ ├── CMakeLists.txt
├── cmake/
│ ├── toolchain-samd51.cmake
├── CMakeLists.txt
This is my top level CMakeLists.txt:
cmake_minimum_required(VERSION 3.17)
project(SMALL-FW LANGUAGES C)
add_subdirectory(arch)
add_subdirectory(apps/test_app)
This is the toolchain cmake file:
# Set target architecture
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR arm)
# Set compiler to use
set(CMAKE_C_COMPILER "arm-none-eabi-gcc")
#set(CMAKE_LINKER "arm-none-eabi-ld")
# Clear default compiler and linker flags.
set(CMAKE_C_FLAGS "")
set(CMAKE_C_LINK_FLAGS "")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
# FIX - Bypass compiler check
set(CMAKE_C_COMPILER_FORCED TRUE)
# Define common compiler and linker flags
set(ARM_OPTIONS
-mthumb
-mabi=aapcs-linux
-mcpu=cortex-m4
-mfpu=fpv4-sp-d16
-mfloat-abi=softfp
--specs=nano.specs
-mlong-calls
-DSAMD51
)
# Define compiler specific flags
add_compile_options(
${ARM_OPTIONS}
-D__SAMD51J19A__
-ffunction-sections
-Wall
)
# Define linker specific flags
add_link_options(
${ARM_OPTIONS}
#--specs=nano.specs
LINKER:--gc-sections
)
This is the CMakeList.txt inside the arch folder:
add_library(asf OBJECT
startup/startup_samd51.c
startup/system_samd51.c
)
# Every target that links against asf needs to know where the ASF headers are.
target_include_directories(asf PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/CMSIS/Include
${CMAKE_CURRENT_SOURCE_DIR}/include
)
# Use the custom linker script provided with ASF.
target_link_options(asf PUBLIC
-T${CMAKE_CURRENT_SOURCE_DIR}/startup/samd51j19a_flash.ld
)
And this is the app CMakeLists.txt:
add_executable(APP)
target_sources(APP PRIVATE src/main.c src/module.c)
target_include_directories(APP PRIVATE inc/)
target_link_libraries(APP asf)
CMake is running fine when the CMAKE_C_COMPILER_FORCED options is set to true, but when I try to make the project it fails with multiple undefined references errors like the next one:
/build/arm-none-eabi-newlib/src/build-nano/arm-none-eabi/thumb/v7e-m+fp/softfp/newlib/libc/reent/../../../../../../../../newlib-4.2.0.20211231/newlib/libc/reent/sbrkr.c:51: undefined reference to _sbrk'`
I have tried using nosys.specs flag but similar errors occurs.

Try this, it looks like typo
# Define linker specific flags
add_link_options(
${ARM_OPTIONS}
--specs=nano.specs
--gc-sections
)

Related

Make error with CppUTest - libCppUTest.a error adding symbols

I am trying to create a project folder structure based on CMake, using the gcc-arm-none-eabi toolchain, and that can use QEMU (arm) to run CppUTest.
Right now, my folder structure look like this:
project/
├── app/
│ ├── src/
│ │ └── main.cpp
│ ├── tests/
│ │ ├── CMakeLists.txt
│ │ ├── FooTest.cpp
│ │ └── main.cpp
│ ├── CMakeLists.txt
│ └── runCMake.sh
├── libraries/
│ ├── errortype/
│ │ ├── include/
│ │ ├── src/
│ │ └── CMakeLists.txt
│ ├── math/
│ │ ├── include/
│ │ ├── src/
│ │ └── CMakeLists.txt
│ └── prediction/
│ ├── include/
│ ├── src/
│ └── CMakeLists.txt
└── scripts/
└── cmake/
├── PreTargetDef.cmake
├── PostTargetDef.cmake
└── toolchain.cmake
The app/CMakeLists.txt:
### PROJECT INITIALIZATION
cmake_minimum_required(VERSION 3.19)
project(hello-arm)
### SET TOOLCHAIN
set(CMAKE_TOOLCHAIN_FILE ${CMAKE_SOURCE_DIR}/../scripts/cmake/toolchain.cmake)
### CUSTOM CMAKE SCRIPTS
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../scripts/cmake")
include(PreTargetDef)
### APPLICATION SOURCES
file(GLOB ${PROJECT_NAME}_SRC
"src/main.cpp"
)
### CREATE EXECUTABLE
add_executable(${PROJECT_NAME} ${${PROJECT_NAME}_SRC})
### LIBRARIES TARGET INCLUDE DIRECTORIES
target_include_directories(${PROJECT_NAME} PUBLIC ${LIBERRORTYPE_INCLUDE_DIRS})
### CUSTOM CMAKE SCRIPT
include(PostTargetDef)
### OPTIONALLY COMPILE TESTS
option(COMPILE_TESTS "Compile the tests" OFF)
if(COMPILE_TESTS)
add_subdirectory(tests)
endif(COMPILE_TESTS)
Toolchain.cmake:
cmake_minimum_required (VERSION 3.6)
#set(CMAKE_CROSSCOMPILING 1)
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(TRIPLE "arm-none-eabi")
unset(TOOLCHAIN_GCC_PROGRAM CACHE)
find_program(TOOLCHAIN_GCC_PROGRAM "${TRIPLE}-gcc" NO_DEFAULT_PATH HINTS ${ARM_CORTEX_COMPILER_PATH} $ENV{ARM_CORTEX_COMPILER_PATH})
get_filename_component(TOOLCHAIN_ROOT ${TOOLCHAIN_GCC_PROGRAM} DIRECTORY)
get_filename_component(EXE_EXT ${TOOLCHAIN_GCC_PROGRAM} EXT)
# To create static libraries suitable for link-time optimization (LTO),
# use gcc-ar and gcc-ranlib instead of ar and ranlib. See gcc -flto option
set(CMAKE_C_COMPILER "${TOOLCHAIN_ROOT}/${TRIPLE}-gcc${EXE_EXT}" CACHE FILEPATH "gcc" FORCE)
set(CMAKE_CXX_COMPILER "${TOOLCHAIN_ROOT}/${TRIPLE}-g++${EXE_EXT}" CACHE FILEPATH "g++" FORCE)
set(CMAKE_OBJCOPY "${TOOLCHAIN_ROOT}/${TRIPLE}-objcopy${EXE_EXT}" CACHE FILEPATH "objcopy" FORCE)
set(CMAKE_OBJDUMP "${TOOLCHAIN_ROOT}/${TRIPLE}-objdump${EXE_EXT}" CACHE FILEPATH "objdump" FORCE)
set(CMAKE_RANLIB "${TOOLCHAIN_ROOT}/${TRIPLE}-gcc-ranlib${EXE_EXT}" CACHE FILEPATH "ranlib" FORCE)
set(CMAKE_STRIP "${TOOLCHAIN_ROOT}/${TRIPLE}-strip${EXE_EXT}" CACHE FILEPATH "strip" FORCE)
set(CMAKE_AR "${TOOLCHAIN_ROOT}/${TRIPLE}-gcc-ar${EXE_EXT}" CACHE FILEPATH "ar" FORCE)
set(CMAKE_AS "${TOOLCHAIN_ROOT}/${TRIPLE}-as${EXE_EXT}" CACHE FILEPATH "as" FORCE)
#SET(CMAKE_EXE_LINKER_FLAGS_INIT "--specs=nosys.specs -Wl,-gc-sections -nostdlib -static-libgcc -static-libstdc++ -nostartfiles")
set(CMAKE_EXE_LINKER_FLAGS_INIT " --specs=nosys.specs ")
string(APPEND CMAKE_C_FLAGS_INIT " ")
string(APPEND CMAKE_C_FLAGS_INIT_DEBUG " ")
string(APPEND CMAKE_CXX_FLAGS_INIT " ")
string(APPEND CMAKE_CXX_FLAGS_INIT_DEBUG " ")
set(SHARED_LIBS OFF)
set(STATIC_LIBS ON)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY BOTH )
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE BOTH )
PreTargetDef.cmake:
### ENABLE LANGUAGES
enable_language(C)
enable_language(CXX)
enable_language(ASM)
### COMPILE TARGET TYPE
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
### CREATE BUILD DIRECTORY IF NOT ALREADY DONE
file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../build)
### OPTIONS
set(APP_ROOT ${CMAKE_SOURCE_DIR}/src)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_C_STANDARD 99)
set(CMAKE_EXE_LINKER_FLAGS --specs=rdimon.specs)
### COMPILE OPTIONS
list(APPEND COMPILE_OPTIONS
-mthumb
-mcpu=cortex-a9
-mfloat-abi=softfp
-mfpu=fpv4-sp-d16
-flto
-lm
-lc
-lrdimon
-Wall
$<$<CONFIG:RELEASE>:-g3>
$<$<CONFIG:RELEASE>:-Os>
)
add_compile_options(
${COMPILE_OPTIONS}
)
### ADD SUBDIRECTORIES FOR LIBRARIES
get_filename_component(COMMON_LIBRARIES_ROOT "${CMAKE_SOURCE_DIR}/../libraries/" ABSOLUTE)
get_filename_component(COMMON_LIBRARIES_BUILD_ROOT "${CMAKE_BINARY_DIR}/common_libraries/" ABSOLUTE)
add_subdirectory(${COMMON_LIBRARIES_ROOT}/errortype/src ${COMMON_LIBRARIES_BUILD_ROOT}/errortype)
add_subdirectory(${COMMON_LIBRARIES_ROOT}/math/src ${COMMON_LIBRARIES_BUILD_ROOT}/math)
add_subdirectory(${COMMON_LIBRARIES_ROOT}/prediction/src ${COMMON_LIBRARIES_BUILD_ROOT}/prediction)
set( LIBS
prediction
errortype
)
PostTargetDef.cmake:
### TARGET LINK LIBRARY
target_link_libraries(
${PROJECT_NAME}
${COMPILE_OPTIONS}
-Wl,-static
-Wl,--start-group
${LIBS}
-Wl,--end-group
)
And, tests/ CMakeLists.txt:
# (1) Look for installed version of CppUTest
if(DEFINED ENV{CPPUTEST_HOME})
message(STATUS "Using CppUTest home: $ENV{CPPUTEST_HOME}")
set(CPPUTEST_INCLUDE_DIRS $ENV{CPPUTEST_HOME}/include)
set(CPPUTEST_LIBRARIES $ENV{CPPUTEST_HOME}/lib)
set(CPPUTEST_LDFLAGS CppUTest CppUTestExt)
else()
find_package(PkgConfig REQUIRED)
pkg_search_module(CPPUTEST REQUIRED cpputest>=3.8)
message(STATUS "Found CppUTest version ${CPPUTEST_VERSION}")
endif()
# (2) Our unit tests sources
set(TEST_APP_NAME ${PROJECT_NAME}_tests)
set(TEST_SOURCES
mocks/IFooMock.cpp
FooTest.cpp
main.cpp
)
# (3) Take care of include directories
include_directories(${CPPUTEST_INCLUDE_DIRS} ../src/)
link_directories(${CPPUTEST_LIBRARIES})
# (4) Build the unit tests objects and link then with the app library
add_executable(${TEST_APP_NAME} ${TEST_SOURCES})
target_link_libraries(${TEST_APP_NAME}
${CPPUTEST_LDFLAGS}
${COMPILE_OPTIONS}
-Wl,-static
-Wl,--start-group
${LIBS}
-Wl,--end-group
)
Doing cmake -DARM_CORTEX_COMPILER_PATH="/path/to/gcc-arm-bin/" -DCOMPILE_TESTS=ON $PROJECT_PATH creates a Makefile in the build folder for the main application and a second one under the build/tests folder.
Running make produces a hello-arm file for the main app that runs under qemu-arm hello-arm just fine. But come time to make the tests/ part, I get the following error (make VERBOSE=1):
[ 91%] Building CXX object tests/CMakeFiles/hello-arm_tests.dir/main.cpp.obj
cd /home/tester/Documents/hello-qemu-arm/build/release/tests && /home/tester/miniconda3/envs/hello-qemu/bin/arm-none-eabi-g++ -I/home/tester/miniconda3/envs/hello-qemu/include -I/home/tester/Documents/hello-qemu-arm/application/tests/../src -I/home/tester/Documents/hello-qemu-arm/libraries/prediction/src/include -I/home/tester/Documents/hello-qemu-arm/build/release/common_libraries/prediction/src-config/include -I/home/tester/Documents/hello-qemu-arm/libraries/types/src/include -I/home/tester/Documents/hello-qemu-arm/build/release/common_libraries/types/src-config/include -I/home/tester/Documents/hello-qemu-arm/libraries/math/src/include -I/home/tester/Documents/hello-qemu-arm/build/release/common_libraries/math/src-config/include -I/home/tester/Documents/hello-qemu-arm/libraries/errortype/src/include -I/home/tester/Documents/hello-qemu-arm/build/release/common_libraries/errortype/src-config/include -I/home/tester/Documents/hello-qemu-arm/build/include -I/home/tester/Documents/hello-qemu-arm/libraries/etl/src/include -I/home/tester/Documents/hello-qemu-arm/libraries/etl/src/../config -O3 -DNDEBUG -mthumb -mcpu=cortex-a9 -mfloat-abi=softfp -mfpu=fpv4-sp-d16 -flto -lm -lc -lrdimon -Wall -g3 -Os -std=gnu++17 -MD -MT tests/CMakeFiles/hello-arm_tests.dir/main.cpp.obj -MF CMakeFiles/hello-arm_tests.dir/main.cpp.obj.d -o CMakeFiles/hello-arm_tests.dir/main.cpp.obj -c /home/tester/Documents/hello-qemu-arm/application/tests/main.cpp
[100%] Linking CXX executable hello-arm_tests
cd /home/tester/Documents/hello-qemu-arm/build/release/tests && /home/tester/miniconda3/envs/hello-qemu/bin/cmake -E cmake_link_script CMakeFiles/hello-arm_tests.dir/link.txt --verbose=1
/home/tester/miniconda3/envs/hello-qemu/bin/arm-none-eabi-g++ -O3 -DNDEBUG --specs=rdimon.specs "CMakeFiles/hello-arm_tests.dir/mocks/IFooMock.cpp.obj" "CMakeFiles/hello-arm_tests.dir/FooTest.cpp.obj" "CMakeFiles/hello-arm_tests.dir/main.cpp.obj" -o hello-arm_tests -L/home/tester/miniconda3/envs/hello-qemu/lib -lCppUTest -lCppUTestExt -mthumb -mcpu=cortex-a9 -mfloat-abi=softfp -mfpu=fpv4-sp-d16 -flto -lm -lc -lrdimon -Wall -g3 -Os -Wl,-static -Wl,--start-group ../common_libraries/prediction/libprediction.a ../common_libraries/errortype/liberrortype.a -Wl,--end-group
/home/tester/miniconda3/envs/hello-qemu/arm-none-eabi/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/bin/ld: /home/tester/miniconda3/envs/hello-qemu/lib/libCppUTest.a: error adding symbols: file format not recognized
collect2: error: ld returned 1 exit status
make[2]: *** [tests/CMakeFiles/hello-arm_tests.dir/build.make:131: tests/hello-arm_tests] Error 1
make[2]: Leaving directory '/home/tester/Documents/hello-qemu-arm/build/release'
make[1]: *** [CMakeFiles/Makefile2:296: tests/CMakeFiles/hello-arm_tests.dir/all] Error 2
make[1]: Leaving directory '/home/tester/Documents/hello-qemu-arm/build/release'
make: *** [Makefile:91: all] Error 2

Can I make a CMake target dependent upon a target in another CMake project?

I have two separate projects, but one of them must now incorporate aspects of the other, including the generation of some code, which done by a Python script which is called by CMake.
Here is my project structure:
repo/
├── project_top/
│ ├── stuff_and_things.cpp
│ └── CMakeLists.txt
│
└── submods/
└── project_bottom/
├── CMakeLists.txt
└── tools/
├── build_scripts
│ └── cmake_bits.cmake
└── generator
└── gen_code.py
In repo/submods/project_bottom/tools/build_scripts/cmake_bits.cmake there is a macro set_up_additional_targets(), which includes a custom target which runs repo/submods/project_bottom/tools/generator/gen_code.py in that directory. This is based on project_bottom being its own project.
add_custom_target(gen_code
COMMAND echo "Generating code"
COMMAND python3 gen_code.py args
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/tools/generator
)
Now, I need to make a new target in project_top dependent upon the gen_code target in project_bottom. How do I do this? The gen_code target needs to be run as part of the project_top build, but within the context of project_bottom, because for that target, ${CMAKE_CURRENT_SOURCE_DIR} needs to be repo/submods/project_bottom, not repo/project_top.

Cmake add include_dirs to a target, then link target to a executable

How do I set the include dirs of a target, then include that target in my executable the current project folder looks like this:
├── CMakeLists.txt
├── libs
│ ├── CMakeLists.txt
│ └── libs.h
├── src
│ ├── CMakeLists.txt
│ └── main.cpp
└── tests
└── tests.cpp
So I would like to puts the .h files of the libs on the libs folder and make that a target which I can link to, I currently have something like this:
CMakeLists.txt:
cmake_minimum_required(VERSION 3.20.0)
project("Project")
add_subdirectory(libs)
add_subdirectory(src)
src/CMakeLists.txt:
add_executable(main main.cpp)
target_link_libraries(main libs)
libs/CMakeLists.txt:
add_custom_target(libs PUBLIC)
target_include_directories(libs ${CMAKE_CURRENT_SOURCE_DIR})

CMake error : "include could not find load file GoogleTest"

I have a problem with adding googletests to CMakeLists.txt.
The problem is "Error:include could not find load file:
GoogleTest".
I have installed google tests using the commands:
sudo apt-get install libgtest-dev
sudo apt-get install cmake # install cmake
cd /usr/src/gtest
sudo cmake CMakeLists.txt
sudo make
sudo cp *.a /usr/lib
These commands I took from here.
There is my CMakeLists.txt:
cmake_minimum_required(VERSION 3.6.2)
set(CMAKE_CXX_COMPILER clang++)
set(CMAKE_CXX_STANDARD 17)
project(compiler)
include_directories(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
find_package(FLEX REQUIRED)
FLEX_TARGET(lexer src/lexer.fl ${CMAKE_CURRENT_BINARY_DIR}/lexer.cpp)
find_package(GTest REQUIRED)
include(GoogleTest) <- There is a problem!
add_executable(run_tests src/tests/test.cpp src/main.h ${FLEX_lexer_OUTPUTS})
target_include_directories(run_tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_link_libraries(run_tests ${GTEST_LIBRARIES} stdc++fs)
#gtest_add_tests(TARGET run_tests)
add_executable(compiler src/main.h src/main.cpp src/common.h ${FLEX_lexer_OUTPUTS})
I will be really appreciate if you give me an advice!
If you already got libgtest installed, just add following in your CMakeLists.txt:
target_link_libraries($YOUR_TARGTET gtest)
But! I think you made it too complicated.
It's better to include the googletest source code directlly into your project as a third party library. I will go here this way here and after.
As you involved 'apt-get' in your code example, I assume you are on Debian or Debian decendants. Well, of caurse actually it does not matter which OS you are on.
Clone the googletest source code(check previous for link) into your project, add following into your main CMakeLists.txt:
add_subdirectory(googletest)
add following into sub-cmake projects where you are going to need gtest:
include_directories(${PARENT_PATH_OF_GTEST}/googltest/googltest/include)
# ...
target_link_libraries($YOUR_TARGET gtest)
e.g.
Let's say you have a project with following structure:
.
├── assets
│   └── ...
├── bin
│   ├── CMakeLists.txt
│   └── ...
├── CMakeLists.txt <== Your main CmakeLists.txt
├── src
│   ├── CMakeLists.txt
│   └── ...
├── tests <== Test cases
│   ├── CMakeLists.txt
│   └── ...
└── third_party
├── CMakeLists.txt
├── googletest <=== gtest
└── ...
In your main CMakeLists.txt, you should have:
add_subdirectory(tests)
add_subdirectory(third_party)
# ...
In the third_party CMakeLists.txt:
add_subdirectory(googletest)
# ...
In the tests CMakeLists.txt:
include_directories(${CMAKE_SOURCE_DIR}/third_party/googltest/googltest/include)
# ...
add_executable(my_test...)
target_link_libraries(my_test gtest)
# ...
A better example
Here is a video example for Gtest in CLion from Jetbrains. As CLion actually bases on CMake, so it also clearlly shows you how to integrate GTest into CMake.
Google Test support in CLion

cmake can't find the depending library

I have a demo project which rely on a logger library, The file structure of the relevant files are listed as below:
.
├── FancyLogger
│   ├── CMakeLists.txt
│   ├── FancyLogger.cpp
│   ├── FancyLogger.h
│   ├── bin
│   │   └── libFancyLogger.a
└── HelloClion
├── CMakeLists.txt
├── helloclion.cpp
I have build a static library named as libFancyLogger.a in the ./FancyLogger/bin directory.
My CMakeFiles of the project HelloClion is listed as below:
cmake_minimum_required(VERSION 3.3)
project(HelloClion)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
get_filename_component(CODE_ROOT ${CMAKE_CURRENT_SOURCE_DIR} DIRECTORY)
set(SOURCE_FILES helloclion.cpp)
add_executable(HelloClion ${SOURCE_FILES})
include_directories(${CODE_ROOT}/FancyLogger)
link_directories(${CODE_ROOT}/FancyLogger/bin)
target_link_libraries(HelloClion FancyLogger)
But the linker complains that library not found for -lFancyLogger, I don't know how to fix this, please help.
I seems to find out why. ;)
In the cmake documentation:https://cmake.org/cmake/help/v3.0/command/link_directories.html
link_directories(directory1 directory2 ...)
Specify the paths in which
the linker should search for libraries. The command will apply only to
targets created after it is called.
The doc says, the command only applys to targets created after it is called. So I just need to move link_directories(${CODE_ROOT}/FancyLogger/bin) to some position in front of the add_executable(HelloClion ${SOURCE_FILES}) command.
Problems solved!