Make error with CppUTest - libCppUTest.a error adding symbols - cmake

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

Related

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

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
)

Cython, CMake and out-of-source build results in double compilation

I have a big project containing multiple projects that create library/executables and some even a python module using a library.
To keep it simple, I have an MWE included called myprogram that creates an executable and a shared library for use by Cython code to create a python package for that same code. I use CMake to compile the code but due to my setup, as detailed here, the build process of the python routine is executed twice (during build and install).
During build the output is:
Scanning dependencies of target pymyprogram
[ 80%] Generating build/timestamp
Compiling /builds/myprogram/src/myprogram/pymyprogram.pyx because it changed.
[1/1] Cythonizing /builds/myprogram/src/myprogram/pymyprogram.pyx
running build_ext
building 'pymyprogram' extension
creating build
creating build/temp.linux-x86_64-3.8
creating build/temp.linux-x86_64-3.8/builds
creating build/temp.linux-x86_64-3.8/builds/myprogram
creating build/temp.linux-x86_64-3.8/builds/myprogram/src
creating build/temp.linux-x86_64-3.8/builds/myprogram/src/myprogram
gcc -Wno-unused-result -Wsign-compare -DNDEBUG -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -I/opt/rh/rh-python38/root/usr/include -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -I/opt/rh/rh-python38/root/usr/include -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -fPIC -I/builds/myprogram/src/myprogram -I/builds/myprogram/include -I/builds/myprogram/buildRelease -I/opt/rh/rh-python38/root/usr/include/python3.8 -c /builds/myprogram/src/myprogram/pymyprogram.c -o build/temp.linux-x86_64-3.8/builds/myprogram/src/myprogram/pymyprogram.o
creating build/lib.linux-x86_64-3.8
gcc -pthread -shared -L/opt/rh/rh-python38/root/usr/lib64-Wl,-z,relro -Wl,-rpath,/opt/rh/rh-python38/root/usr/lib64 -Wl,--enable-new-dtags -g -L/opt/rh/rh-python38/root/usr/lib64-Wl,-z,relro -Wl,-rpath,/opt/rh/rh-python38/root/usr/lib64 -Wl,--enable-new-dtags -g build/temp.linux-x86_64-3.8/builds/myprogram/src/myprogram/pymyprogram.o -L/builds/myprogram/buildRelease/src/myprogram -L/opt/rh/rh-python38/root/usr/lib64 -lmyprogram -o build/lib.linux-x86_64-3.8/pymyprogram.cpython-38-x86_64-linux-gnu.so -Wl,-rpath=/builds/myprogram/lib/
[ 80%] Built target pymyprogram
And during install:
-- Installing: /builds/myprogram/bin/myprogram.x
running install
running build
running build_ext
building 'pymyprogram' extension
creating build
creating build/temp.linux-x86_64-3.8
creating build/temp.linux-x86_64-3.8/builds
creating build/temp.linux-x86_64-3.8/builds/myprogram
creating build/temp.linux-x86_64-3.8/builds/myprogram/src
creating build/temp.linux-x86_64-3.8/builds/myprogram/src/myprogram
gcc -Wno-unused-result -Wsign-compare -DNDEBUG -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -I/opt/rh/rh-python38/root/usr/include -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -I/opt/rh/rh-python38/root/usr/include -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -D_GNU_SOURCE -fPIC -fwrapv -fPIC -I/builds/myprogram/src/myprogram -I/builds/myprogram/include -I/builds/myprogram/buildRelease -I/opt/rh/rh-python38/root/usr/include/python3.8 -c /builds/myprogram/src/myprogram/pymyprogram.c -o build/temp.linux-x86_64-3.8/builds/myprogram/src/myprogram/pymyprogram.o
creating build/lib.linux-x86_64-3.8
gcc -pthread -shared -L/opt/rh/rh-python38/root/usr/lib64-Wl,-z,relro -Wl,-rpath,/opt/rh/rh-python38/root/usr/lib64 -Wl,--enable-new-dtags -g -L/opt/rh/rh-python38/root/usr/lib64-Wl,-z,relro -Wl,-rpath,/opt/rh/rh-python38/root/usr/lib64 -Wl,--enable-new-dtags -g build/temp.linux-x86_64-3.8/builds/myprogram/src/myprogram/pymyprogram.o -L/builds/myprogram/buildRelease/src/myprogram -L/opt/rh/rh-python38/root/usr/lib64 -lmyprogram -o build/lib.linux-x86_64-3.8/pymyprogram.cpython-38-x86_64-linux-gnu.so -Wl,-rpath=/builds/myprogram/lib/
running install_lib
creating /builds/myprogram/lib64
creating /builds/myprogram/lib64/python3.8
creating /builds/myprogram/lib64/python3.8/site-packages
copying build/lib.linux-x86_64-3.8/pymyprogram.cpython-38-x86_64-linux-gnu.so -> /builds/program/lib/python3.8/site-packages
running install_egg_info
Writing /builds/myprogram/lib/python3.8/site-packages/pymyprogram-1.1A-py3.8.egg-info
Also when I make changes to otherprogram.c but no changes to myprogram in either the library of pyx code, at install stage the library is build anyway which increases compilation time.
I therefore searched for a solution and found the method from PJ_Finnegan but have a few issues so wondered whether others have resolved that or could help out.
My working tree for the MWE is:
.
├── README.TXT
├── README.md
├── bin
├── CMakeLists.txt
├── buildDebug
│   ├── CMakeCache.txt
│   ├── CMakeFiles
│   ├── Makefile
│   ├── cmake_install.cmake
│   ├── install_manifest.txt
│   ├── src
│   └── utils
├── include
│   ├── version.h
│   └── version.h.txt
├── lib
│   └── libmyprogram.dylib
├─── src
│   ├── some other myprogram
│   │   ├── CMakeLists.txt
│   │   ├── othermyprogram.h
│   │   ├── othermyprogram.c
│   ├── myprogram
│   │   ├── CMakeLists.txt
│   │   ├── myprogram.h
│   │   ├── myprogram.c
│   │   ├── myprogram.pyx
│   │   ├── setup.py.in
│   └── versioning.cmake
The top level CMakeLists.txt contains add_subdirectory(src/myprogram) and the content of the CMakeLists.txt in src/myprograms is:
cmake_minimum_required(VERSION 2.8.8...3.20.5 FATAL_ERROR)
project (myprogram)
message ("-- Configuring: *** myprogram **")
if(APPLE)
set(CMAKE_MACOSX_RPATH 1)
endif()
set(CMAKE_C_FLAGS "${CFLAGS} -O0 -ggdb -fPIC")
set(CMAKE_C_FLAGS_DEBUG "${CFLAGS} -O0 -ggdb -fPIC")
set(CMAKE_C_FLAGS_RELEASE "${CFLAGS} -O3 -fPIC")
set(CMAKE_CXX_FLAGS "${CFLAGS} -O0 -ggdb")
set(CMAKE_CXX_FLAGS_DEBUG "${CFLAGS} -O0 -ggdb")
set(CMAKE_CXX_FLAGS_RELEASE "${CFLAGS} -O3")
# *** myprogram.so ***
SET(libmyprogram_SRCS
myprogram.c
)
ADD_LIBRARY(myprogram SHARED
${libmyprogram_SRCS}
)
ADD_DEPENDENCIES(myprogram versioning)
TARGET_LINK_LIBRARIES(myprogram
m
)
install(TARGETS myprogram
DESTINATION "lib")
if (APPLE)
install(CODE "execute_process(COMMAND ln -sf ${PROJECT_HOME}/lib/libmyprogram.dylib /usr/local/lib/libmyprogram.dylib)")
endif()
# *** myprogram.x ***
SET(myprogram_SRCS
myprogram.c
)
ADD_EXECUTABLE(myprogram.x
${myprogram_SRCS}
)
ADD_DEPENDENCIES(myprogram.x myprogram versioning)
TARGET_LINK_LIBRARIES(myprogram.x
m
)
set (EXECUTABLES "${EXECUTABLES}" myprogram.x)
# install executables and scripts
install (TARGETS ${EXECUTABLES}
RUNTIME DESTINATION "bin")
# *** pymyprogram.pyx ***
find_package(PythonInterp 3 REQUIRED)
if (CMAKE_BUILD_TYPE MATCHES "Debug")
message(STATUS "Python: version=${PYTHON_VERSION_STRING} interpreter=${PYTHON_EXECUTABLE}")
set(PYTHON_BUILD_FLAGS "--debug")
endif()
# Get location of user site-packages to install libs to
execute_process(COMMAND python3 -m site --user-site OUTPUT_VARIABLE PYTHON_INSTALL_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
if (PYTHONINTERP_FOUND)
set(SETUP_PY_IN "${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in")
set(SETUP_PY "${CMAKE_CURRENT_BINARY_DIR}/setup.py")
set(DEPS "${CMAKE_CURRENT_SOURCE_DIR}/pymyprogram.pyx")
set(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/build/timestamp")
message ("-- Building Python Extension using: " ${PYTHON_EXECUTABLE})
configure_file(${SETUP_PY_IN} ${SETUP_PY})
add_custom_command(OUTPUT ${OUTPUT}
COMMAND ${PYTHON_EXECUTABLE} ${SETUP_PY} build ${PYTHON_BUILD_FLAGS} --build-lib ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}
COMMAND ${CMAKE_COMMAND} -E touch ${OUTPUT}
DEPENDS ${DEPS})
add_custom_target(pymyprogram ALL DEPENDS ${OUTPUT})
add_dependencies(pymyprogram myprogram versioning)
install(CODE "execute_process(COMMAND ${PYTHON_EXECUTABLE} ${SETUP_PY} install_lib --skip-build
--build-dir ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}
--install-dir=${PYTHON_INSTALL_DIR} --force
COMMAND ${PYTHON_EXECUTABLE} ${SETUP_PY} install_egg_info
--install-dir=${PYTHON_INSTALL_DIR})" DEPENDS ${DEPS})
else()
message (WARNING "-- Could not build *** pymyprogram **, could not locate PYTHON: ${PYTHON_EXECUTABLE} or improper version")
endif()
And the setup.py.in that is used:
import os, sys
from distutils.core import setup, Extension
from Cython.Build import cythonize
link_arguments = []
if (sys.platform == 'darwin'):
link_arguments.append("-Wl,-rpath")
link_arguments.append("-Wl,#loader_path/")
os.environ["CC"] = "gcc"
else:
link_arguments.append("-Wl,-rpath=${CMAKE_SOURCE_DIR}/lib/")
myprogram_extension = Extension(
name="pymyprogram",
sources=["${CMAKE_CURRENT_SOURCE_DIR}/pymyprogram.pyx"],
libraries=["myprogram"],
extra_link_args = link_arguments,
library_dirs=["${PROJECT_BINARY_DIR}"],
include_dirs=["${CMAKE_SOURCE_DIR}/include", "${CMAKE_SOURCE_DIR}/build${CMAKE_BUILD_TYPE}"],
)
setup(name="pymyprogram",
author="My Name",
author_email="my-email",
version="1.1A",
description="Python wrapper for myprogram",
package_dir={ "": "${CMAKE_SOURCE_DIR}/lib/" },
license="MIT License",
ext_modules=cythonize([myprogram_extension], compiler_directives={'language_level' : "3"})
)
The code runs on different machines (Linux or macOS) and my module was previously build and installed using this method and either the --user or --prefix of the install command based on whether the code ran on Linux or macOS.
To avoid the double build step of the python module, I implemented a new method from PJ_Finnegan as mentioned above and shown in the code sample of my CMakeLists.txt. This method uses install_lib, to avoid the second build which allows the use of --build-dir which is great but no longer works with --user or --prefix which is unwanted as it will require me to define the path manually and that the path no longer depends on the python version automatically.
I also wonder what the difference between using install-lib vs install entails. I noticed no egg-info file being created, is this a problem? I therefore added the install_egg_info step as well.
Also, the the mypogram code is build twice, (the executable and library), and a third time for the python package. Is there some optimisation possible to reduce compile time?
Any other ways to build and install a python/Cython extension and separate standalone executable and help the install step find the build code? Perhaps using sdist and pip install?
EDIT
I've changed the INSTALL command in my CMAKE files to:
install(CODE "execute_process(COMMAND ${PYTHON_EXECUTABLE} ${SETUP_PY} install ${PYTHON_INSTALL_PREFIX} --skip-build --force WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})")
This reduces the install step to only installation and no more build as the build directory from the previous build step is now found.
Now the question remains, how to make the CMAKE install command not run every time and only upon changes in the *.pyx file. I know add_custom_target is always considered out-of-date so is there perhaps a workaround?

how to link cmake created library with main file?

Here i am creating a project . i am using cmake . i am not able to link cmake created shared library with my main application ??? My CMakeLists.txt file are as follows :
//tree -stucture of my project
├── CMakeLists.txt
├── include
│   ├── Account.h
│   ├── bankingSystem.h
│   ├── bankStruct.h
│   └── stdheader.h
├── main.cpp
└── src
├── Account.cpp
├── bankingSystem.cpp
└── main.cpp
//CMakeLists.txt
cmake_minimum_required(VERSION 3.17.1)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -std=c++11 -DPC_BUILD")
include_directories(
./include
)
link_directories(
/usr/local/lib)
#create a shared library to be linked with test application
add_library(
BANK SHARED
src/bankingSystem.cpp
src/Account.cpp
)
add_executable(
main main.cpp
)
message("BANK")
But i am getting Undefined Reference :
[ 60%] Built target BANK
[ 80%] Linking CXX executable main
CMakeFiles/main.dir/main.cpp.o: In function `main':
/home/pankaj/BANK/main.cpp:8: undefined reference to `BankingSystem::BankingSystem()'
/home/pankaj/BANK/main.cpp:19: undefined reference to `BankingSystem::Create_new_Account()'
...
collect2: error: ld returned 1 exit status
CMakeFiles/main.dir/build.make:103: recipe for target 'main' failed
make[2]: *** [main] Error 1
CMakeFiles/Makefile2:124: recipe for target 'CMakeFiles/main.dir/all' failed
make[1]: *** [CMakeFiles/main.dir/all] Error 2
Makefile:103: recipe for target 'all' failed
make: *** [all] Error 2

CMake: How to handle multiple versions of same libraries?

in my project I am using the header only library rapidjson v1.1.0.
└── my_project
├── CMakeLists.txt
├── src
│
├── 3rdParty/tiny_dnn (header only)
│ ├── CMakeLists.txt
│ ├── src
│ └── rapidjson_v0.2
│
└── rapidjson_v1.1.0
The problem is now that tiny-dnn has also included rapidjson (but an older version), so while i try to include tiny_dnn in the main CMakeLists.txt like include_directories(${PROJECT_SOURCE_DIR}/3rdParty/tiny_dnn) some conflicts arise from either tiny-dnn searches mine rapidjson or my project searches in tiny-dnn's rapidjson.
my_project CMakeLists.txt
cmake_minimum_required(VERSION 3.9)
project(my_project)
# check the build type and set compiler and linker flags
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
string(TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE)
IF(CMAKE_BUILD_TYPE MATCHES DEBUG)
message("Debug build")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-unknown-pragmas -g -O0 -std=c++17 -Ddeveloper_build")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fPIC -g -O0")
ELSEIF(CMAKE_BUILD_TYPE MATCHES RELEASE)
message("Release build")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-unknown-pragmas -O3 -std=c++17")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -O3")
ELSE()
message(FATAL_ERROR "No build type specified")
ENDIF()
find_package(Boost COMPONENTS system filesystem REQUIRED)
include_directories(${Boost_INCLUDE_DIRS} ${PROJECT_SOURCE_DIR}/3rdParty/tiny-dnn)
file(GLOB_RECURSE SOURCE_FILES src/*.cpp src/*.c)
set(SOURCE_FILES
${SOURCE_FILES})
add_executable(my_project ${SOURCE_FILES})
target_link_libraries(my_project ${Boost_LIBRARIES} pthread)
set_target_properties(my_project PROPERTIES SUFFIX ${CMAKE_BUILD_TYPE})
my_project.cpp
#include <tiny_dnn/tiny_dnn.h>
#include <rapidjson/rapidjson.h> // <- usr/local/include/rapidjson
int main(int argc, char **argv)
{
rapidjson::Document d; // <- uses rapidjson (v0.2) of tiny_dnn/cereal/external/rapidjson but in my project i would use /usr/local/include/rapidjson (v1.0.1)
return 0;
}
In general - don't. Force one client to use higher / better. Even if you walk around those problems, you are likely to violate one definition rule and get segfault later.

CMake imported target found when configuring but generated build.make says target-NOTFOUND

I have a simple shared library libfool2.so with installed header fool2.h which are not from a CMake project. My project my_temp1 depends on fool2 so I write a FindFool2.cmake to make an imported target:
find_path(Fool2_INCLUDE_DIR fool2.h PATH_SUFFIXES fool2)
find_library(Fool2_LIB fool2)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Fool2
REQUIRED_VARS Fool2_INCLUDE_DIR Fool2_LIB
)
if(Fool2_FOUND AND NOT TARGET Fool2::Fool2)
add_library(Fool2::Fool2 SHARED IMPORTED)
set_target_properties(Fool2::Fool2 PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${Fool2_INCLUDE_DIR}"
INTERFACE_LINK_LIBRARIES "${Fool2_LIB}"
)
endif()
The CMakeLists.txt for my_temp1 project is:
cmake_minimum_required(VERSION 3.3)
project(my_temp1)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake/cmake_modules)
# FindFool2.cmake is in ${CMAKE_CURRENT_LIST_DIR}/cmake/cmake_modules
find_package(Fool2 REQUIRED)
if (TARGET Fool2::Fool2)
message(STATUS "target found")
endif()
add_executable(my_temp1 main.cpp)
target_link_libraries(my_temp1 Fool2::Fool2)
Now
$ tree ../__install
../__install/
├── include
│   └── fool2
│   ├── fool2.h
│   └── version.h
└── lib
└── libfool2.so
$ tree .
.
├── cmake
│   └── cmake_modules
│   └── FindFool2.cmake
├── CMakeLists.txt
└── main.cpp
$ cmake -H. -B_builds -DCMAKE_INSTALL_PREFIX=../__install
# some output omitted
-- target found
-- Configuring done
-- Generating done
-- Build files have been written to: /OMITTED/my_temp1/_builds
$ cmake --build _builds
CMakeFiles/my_temp1.dir/build.make:82: *** target pattern contains no '%'. Stop.
CMakeFiles/Makefile2:67: recipe for target 'CMakeFiles/my_temp1.dir/all' failed
make[1]: *** [CMakeFiles/my_temp1.dir/all] Error 2
Makefile:83: recipe for target 'all' failed
make: *** [all] Error 2
$ head -n 85 _builds/CMakeFiles/my_temp1.dir/build.make | tail -n 10
# External object files for target my_temp1
my_temp1_EXTERNAL_OBJECTS =
my_temp1: CMakeFiles/my_temp1.dir/main.cpp.o
my_temp1: CMakeFiles/my_temp1.dir/build.make
my_temp1: Fool2::Fool2-NOTFOUND
my_temp1: CMakeFiles/my_temp1.dir/link.txt
#$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --green --bold --progress-dir=/OMITTED/my_temp1/_builds/CMakeFiles --progress-num=$(CMAKE_PROGRESS_2) "Linking CXX executable my_temp1"
$(CMAKE_COMMAND) -E cmake_link_script CMakeFiles/my_temp1.dir/link.txt --verbose=$(VERBOSE)
The command $ cmake -H. -B_builds -DCMAKE_INSTALL_PREFIX=../__install finds fool2 because find_* commands searches in the CMAKE_INSTALL_PREFIX as well.
But why is there weird output my_temp1: Fool2::Fool2-NOTFOUND in build.make?
CMake version is 3.11.3
For IMPORTED library target value -NOTFOUND corresponds to absent IMPORTED_LOCATION property, corresponded to the library's path. You need to set that property for correctly work with IMPORTED target.
If you want CMake target to be a placeholder just for link with other libraries, use INTERFACE library target instead: such library target doesn't have library location.