I would like to add custom command so that intermediate files are generated and will be passed on to add_library(). I tried below but it seems to be not working as no moc cpp files are created.
set( MOCKFILES
a.h
b.h
c.h)
set(QTMOCEXE ../../../../../../../XXXX/external/Qt/5.5.0/compilers/moc.exe)
set(MOCOPTION -DUNICODE -DWIN32 -DQT_THREAD_SUPPORT
-DQT_CORE_LIB -DQT_GUI_LIB -DQT_XML_LIB
-DADMATERIALUI_LIB -D_WINDLL)
foreach(_file ${MOCKFILES})
string(LENGTH ${_file} length)
string(FIND ${_file} "/" FileNameTemp REVERSE)
string(SUBSTRING ${_file} ${FileNameTemp} ${length} FileName)
#get_filename_component(${_file} FileName)
add_custom_command(
OUTPUT moc_${FileName}.cpp
COMMAND QTMOCEXE MOCOPTION ${_file} -o moc_${FileName}.cpp
COMMENT here we go!
)
endforeach()
My idea is to create intermediate moc files (generated from *.h) using qt MOC. I do not want to use the set(CMAKE_AUTOMOC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON).
I'm newbie to CMake and could not get away with this issue. Could somebody shed some lights on this part. Thanks in advance.
Related
I am trying to write a proper CMake script for building an embedded project(STM32). Our project structure looks like this:
App/
....Module1/
....Module2/
Core/
....OwnFramework/
........CoreModule1/
........CoreModule2/
....CubeMXGeneratedCode/
........Core/ (there are more files and dirs, but they are not relevant)
...........Src/main.c (only important info is that main is here)
........Drivers/
....Libs/
........Lib1/ (for example ETL)
Right now there is one CMakeLists.txt file in root project dir, but this seems like a bad solution. I've tried adding CMakeLists into App/ and App/Module but then I would get linking errors (while looking at compile_commands I've seen that they were compiled with almost no flags).
./CMakeLists.txt (removed some non-important parts, like set variables that are obvious, so it's little bit shorter)
set(APP_INCLUDE_DIRECTORIES
${APP_DIR}/Module1
${APP_DIR}/Module2
)
set(CORE_INCLUDE_DIRECTORIES
${Core_DIR}/CoreModule1
${Core_DIR}/CoreModule2
)
set(FLAGS
"-Wl,--gc-sections")
set(CPP_FLAGS
"-fno-rtti -fno-exceptions -fno-threadsafe-statics")
set(CPU_PARAMETERS
-mcpu=cortex-m4
-mthumb
-mfpu=fpv4-sp-d16
-mfloat-abi=hard)
set(STARTUP_SCRIPT ${CUBEMX_DIR}/startup_stm32fxxxxx.s)
set(MCU_LINKER_SCRIPT ${CUBEMX_DIR}/STM32Fxxxxxxx_FLASH.ld)
set(CUBEMX_INCLUDE_DIRECTORIES
${CUBEMX_DIR}/Core/Inc
... and some more Inc dirs
set(PROJECT_INCLUDE_DIRECTORIES
${CMAKE_SOURCE_DIR}
${APP_INCLUDE_DIRECTORIES}
${CORE_INCLUDE_DIRECTORIES})
file(GLOB_RECURSE STM32CUBEMX_SOURCES
${CUBEMX_DIR}/Core/*.c
${CUBEMX_DIR}/Drivers/*.c
${CUBEMX_DIR}/Middlewares/Third_Party/FreeRTOS/Source/*.c)
file(GLOB_RECURSE PROJECT_SOURCES FOLLOW_SYMLINKS
${APP_DIR}/*.cpp
${APP_DIR}/*.c
${CORE_DIR}/*.cpp
${CORE_DIR}/*.c)
add_executable(${EXECUTABLE}
${STM32CUBEMX_SOURCES}
${PROJECT_SOURCES}
${STARTUP_SCRIPT})
target_compile_definitions(${EXECUTABLE} PRIVATE
${MCU_MODEL}
USE_HAL_DRIVER)
target_include_directories(${EXECUTABLE} PRIVATE
${CUBEMX_INCLUDE_DIRECTORIES}
${PROJECT_INCLUDE_DIRECTORIES})
target_compile_options(${EXECUTABLE} PRIVATE
${CPU_PARAMETERS}
${FLAGS}
-Wall
-Wextra
-Wpedantic
-Wno-unused-parameter
$<$<COMPILE_LANGUAGE:CXX>:
${CPP_FLAGS}
-Wno-volatile
-Wold-style-cast
-Wuseless-cast
-Wsuggest-override>
$<$<CONFIG:Debug>:-Og -g3 -ggdb>
$<$<CONFIG:Release>:-Og -g0>)
target_link_options(${EXECUTABLE} PRIVATE
-T${MCU_LINKER_SCRIPT}
${CPU_PARAMETERS}
-Wl,-Map=${CMAKE_PROJECT_NAME}.map
--specs=nosys.specs
-Wl,--start-group
-lc
-lm
-lstdc++
-lsupc++
-Wl,--end-group
-Wl,--print-memory-usage)
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD
COMMAND ${CMAKE_SIZE} $<TARGET_FILE:${EXECUTABLE}>)
add_custom_command(TARGET ${EXECUTABLE} POST_BUILD
COMMAND ${CMAKE_OBJCOPY} -O ihex $<TARGET_FILE:${EXECUTABLE}>
${EXECUTABLE}.hex
COMMAND ${CMAKE_OBJCOPY} -O binary $<TARGET_FILE:${EXECUTABLE}>
${EXECUTABLE}.bin)
There is also toolchain file:
set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(TOOLCHAIN_PREFIX arm-none-eabi-)
set(FLAGS
"-fdata-sections -ffunction-sections \
--specs=nano.specs -Wl,--gc-sections")
set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc ${FLAGS})
set(CMAKE_ASM_COMPILER ${CMAKE_C_COMPILER})
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++ ${FLAGS})
set(CMAKE_OBJCOPY ${TOOLCHAIN_PREFIX}objcopy)
set(CMAKE_SIZE ${TOOLCHAIN_PREFIX}size)
set(CMAKE_EXECUTABLE_SUFFIX_ASM ".elf")
set(CMAKE_EXECUTABLE_SUFFIX_C ".elf")
set(CMAKE_EXECUTABLE_SUFFIX_CXX ".elf")
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
I am building everything with this command:
cmake -B${workspaceFolder}/build -DCMAKE_TOOLCHAIN_FILE=gcc-arm-none-eabi.cmake -DCMAKE_BUILD_TYPE=Debug
And this setup works, but I have a feeling that this is a vary bad way of doing it. When I tried adding subdirectories in App and each module, I've got linking erros due to problems with compile flags (arm none eabi elf uses VFP register arguments linking error).
I am also not sure how well will it work when I start adding libs like Embedded Template Library.
So my question is, how do I add subdirectories so they compile the same way my root CMakeLists does? I've tried doing this following multiple guides and examples like this one: https://github.com/rgujju/STM32_Base_Project/blob/master/modules/simple_module/CMakeLists.txt.
But when I've put this in my App/Module/CMakeLists.txt
set(MODULE_NAME simple_module)
file(GLOB SOURCES ./*.c)
add_library(${MODULE_NAME} STATIC
${SOURCES}
)
target_include_directories(${MODULE_NAME}
PUBLIC
${CMAKE_CURRENT_LIST_DIR}
${PROJECT_SOURCE_DIR}/include
)
And added add_subdirectory(App) to main, my generated commands for modules where without most of my flags and didn't link.
If anyone has experience with dividing CMake project into modules, libs and subdirectories, I would gladly accept any help.
I have a folder with only headers.
In my config project I need some interfaces of the param project
src
CMakeList.txt
- config
- CMakeList.txt
- param
- CMakeList.txt
- parameter.h
This is inside my config CMakeList.txt
project(config)
file(GLOB_RECURSE SOURCES "${config_SOURCE_DIR}/*.cpp")
add_library(config ${SOURCES})
target_include_directories(config PUBLIC "${param_SOURCE_DIR}")
target_include_directories(config PUBLIC "${Utils_SOURCE_DIR}/include")
qt5_use_modules(config Core)
target_link_libraries(config)
My param library CMakeList.txt is (it needs Utils too)
project(param)
file(GLOB_RECURSE HEADERS "${param_SOURCE_DIR}/*.h")
add_library(param INTERFACE)
Then in my config project I have a class that include one of the interface and when building it doesn't find it. "parameter.h" file not found.
/usr/local/bin/clang++ -DQT_CORE_LIB -DQT_NO_DEBUG -I/builds/modules/project/src/config -I~/modules/project/src/config -I/usr/include/eigen3 -I~/modules/libs/utils/include -isystem ~/devel/qt5/qtbase/include -isystem ~/devel/qt5/qtbase/include/QtCore -isystem ~/devel/qt5/qtbase/./mkspecs/linux-g++ -fPIC -std=gnu++11 -o CMakeFiles/config.dir/configuration.cpp.o -c ~/modules/project/src/config/configuration.cpp
One of the source file in config cannot compile because of :
fatal error: 'parameter.h' file not found
#include "parameter.h"
In src/CMakeLists.txt you obviously need
include_directories(param)
line. Alternatively, you can use include_directories(../param) in src/config/CMakeLists.txt.
The include_directories() command adds specified directories to compiler search paths for all targets declared below it in current CMakeLists.txt and every add_subdirectoryed one.
I'd like to program my Tiva C Series LaunchPad board in C++ using CMake build process. I downloaded a simple examples to blink the RGB LED I built using make and I'd like to be able to use cmake to start a bigger project.
Here is the Makefile provided in the example :
# 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
My first CMakeLists.txt file based on it :
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})
It passes the CMake step but when I try to compile using make, I get
arm-none-eabi-g++: error: unrecognized command line option '--gc-sections'
I guess the linkers flags should be used with arm-none-eabi-ld instead. How do I do this ?
Edit1:
I still have no idea how to set correct linker exe and flags but I found that CMake generates a file in firmware.dir/link.txt. Its content is
~/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
And I edited it to what I want to fix temporary this issue
~/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
But it seems that LD doesn't like .o files generated by G++ because a make says
toolchain/bin/arm-none-eabi-ld: warning: cannot find entry symbol ResetISR; defaulting to 00000000
Turning my comment into an answer
Edit: As mentioned by in Marc Glisse in his comment 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'
Then you won't need to change the linker command to ld.
For the comparibility with your makefile you can use the CMAKE_LINKER and CMAKE_CXX_LINK_EXECUTABLE variables to change the linker command like call to ld.
Regarding your problems:
if the linker flags are not taken, just put them directly into the linker command line itself.
when getting "cannot find entry symbol"
you should check the map file if ResetISR is there
you could try to replace --gc-sections with --discard-none
if you get "cannot find entry symbol" during CMake's try compile step, then activate the ..._COMPILER_WORKS flags (see below)
I've taken your code and moved it to a 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)
Add call it with
cmake -DCMAKE_TOOLCHAIN_FILE:string=TM4C123Toolchain.cmake ...
Background
I added a map file with -Map to CMAKE_CXX_LINK_EXECUTABLE command line for debugging. And --start-group/--end-group because my distribution's standard libraries have cyclic dependencies.
I prefer CMAKE_PREFIX_PATH and find_program() because the paths and names may vary between toolchain vendors or versions (and both accept lists of paths/names).
I prefer to FORCE the variable settings, because I don't use the feature that a user can changed cached compiler settings and I prefer changes in the toolchain file to get active without having to run CMake from scratch again.
The _DEBUG and _RELEASE flags are appended by CMake to the standard flags.
References
cmake - Global linker flag setting (for all targets in directory)
What's the CMake syntax to set and use variables?
I found a solution by using the -specs flag.
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})
with tiva.specs:
*link:
--entry ResetISR --gc-sections
*lib:
-lm -lc
and tm4c123g.ld as the linker script:
/******************************************************************************
*
* 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
}
I have a simple bouncing ball program that I wrote in QtCreator on Windows a couple years ago, and now I'm trying to rebuild it on Linux (Ubuntu 14.04) using CMakeLists instead of the Qt project files. My CMakeLists.txt file (loosely based off the cmake file in this question is below.
cmake_minimum_required ( VERSION 2.6 )
project ( BouncyBall )
find_package ( Qt4 COMPONENTS QtGui QtOpenGL REQUIRED )
set (QT_USE_QTOPENGL TRUE)
include(${QT_USE_FILE})
add_definitions(${QT_DEFINITIONS})
include_directories( ${QT_QTOPENGL_INCLUDE_DIR} ${OPENGL_INCLUDE_DIR} )
find_package ( Bullet REQUIRED )
add_executable ( BouncyBall main.cpp BouncyBall.cpp BulletWidget.cpp )
target_link_libraries( BouncyBall ${QT_LIBRARIES} ${BULLET_LIBRARIES} )
Cmake configures and generates the build files without any errors, but fails to compile with the following error:
[ 33%] Building CXX object CMakeFiles/BouncyBall.dir/main.cpp.o
/usr/bin/c++ -DQT_GUI_LIB -DQT_NO_DEBUG -DQT_OPENGL_LIB -isystem /usr/include/qt4 -isystem /usr/include/qt4/QtOpenGL -isystem /usr/include/qt4/QtGui -I/home/user/Programs/BuildArea/Games/BouncyBall -o CMakeFiles/BouncyBall.dir/main.cpp.o -c /home/user/Programs/Games/BouncyBall/main.cpp
In file included from /home/user/Programs/Games/BouncyBall/main.cpp:2:0:
/home/user/Programs/Games/BouncyBall/BulletWidget.h:4:30: fatal error: QtOpenGl/QGLWidget: No such file or directory
#include <QtOpenGl/QGLWidget>
I've verified that the file is indeed in /usr/include/qt4/QtOpenGL. I've looked online, but all the solutions I've found refer to adding QT += opengl to a project file (but I'm using CMake) or to not having the dev-tools or qt-sdk packages installed (which I do have).
What am I missing?
Isn't there a typo in your code 'QtOpenGl/QGLWidget'? QtOpenGl should be changed to QtOpenGL. If you do it on Linux the case matters.
The buildtime of my cuda library is increasing and so I thought that separate compilation introduced in CUDA 5.0 might help me. I couldn't figure out how to achieve separate compilation with cmake. I looked into the NVCC documentation and found how to compile device object (using the -dc option) and how to link them (using the -dlink). My attempts to get it running using cmake failed. I'm using cmake 2.8.10.2 and the head of the trunk of the FindCUDA.cmake. I couldn't however find out how to specify which files should be compiled and how to link them into a library.
Especially the syntax of the function(CUDA_LINK_SEPARABLE_COMPILATION_OBJECTS output_file_var cuda_target options object_files source_files)
is unclear to me because I don't know what the output_file_var and the cuda_target are.
Here the not working results of my attemps:
cuda_compile(DEVICEMANAGER_O devicemanager.cu OPTIONS -dc)
cuda_compile(BLUB_O blub.cu OPTIONS -dc)
CUDA_LINK_SEPARABLE_COMPILATION_OBJECTS(TEST_O gpuacceleration
"" DEVICEMANGER_O BLUB_O)
set(LIB_TYPE SHARED)
#cuda_add_library(gpuacceleration ${LIB_TYPE}
#${gpuacc_SRCS}
#devicemanager.cu
# blub.cu
#DEVICEMANAGER_O
# TEST_O
#)
Does anyone know how to compile and link a cuda library using cmake?
Thanks in advance.
EDIT:
After a friend consulted the developer of the FindCUDA.cmake, a bug got fixed in the example provided with FindCUDA.cmake (https://gforge.sci.utah.edu/gf/project/findcuda/scmsvn/?action=browse&path=%2Fcheckout%2Ftrunk%2FFindCuda.html).
I'm now able to build the example.
In my project I can build the library as needed using the following (cmake 2.8.10 required):
set(LIB_TYPE SHARED)
set(CUDA_SEPARABLE_COMPILATION ON)
cuda_add_library(gpuacceleration ${LIB_TYPE}
blub.cu
blab.cu
)
BUT:
I cannot link against this library. When I builded the lib without separate compilation i was able to link against it.
Now getting the following error:
undefined reference to `__cudaRegisterLinkedBinary_53_tmpxft_00005ab4_00000000_6_blub_cpp1_ii_d07d5695'
for every file with a function used in the interface. Seems strange since it builds without any warning etc.
Any ideas how to get this working?
EDIT:
I finally figured out how to do this. See #PHD's and my answer for details.
I finally got it running ;)
In Addition to the answer of #PHD and my comment on it I modified: set(BUILD_SHARED_LIBS OFF) in my CMakeLists.txt since shared libs are not supported for separate compilation according to the nvcc manually v5.0 page 40.
In addition to that use the latest rev (1223) from the repository instead of rev 1221. I contacted the developer and he fixed some issue blocking this.
This revision doesn't set the nvcc -arch=sm_xx flag correctly, so I added this manually for my project and informed the developer of FindCUDA.cmake. So this might get fixed in the future.
Don't forget to get cmake > 2.8.10 for this to work.
Hope this helps anyone but me ;)
Here is my CMakeLists.txt:
#Required for CUDA-Check
cmake_minimum_required(VERSION 2.8.10)
project(gpulib)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/CMake/cuda" ${CMAKE_MODULE_PATH})
# ============================================
# === Target
# ============================================
file(GLOB_RECURSE gpuacc_SRCS "*.cu")
include_directories(.)
# ---------------------------------------
# Find Cuda
find_package(CUDA REQUIRED)
set(CUDA_ATTACH_VS_BUILD_RULE_TO_CUDA_FILE ON)
set(BUILD_SHARED_LIBS OFF)
set(CUDA_SEPARABLE_COMPILATION ON)
#list(APPEND CUDA_NVCC_FLAGS -arch=sm_20)
set(LIB_NAME "gpuacceleration")
cuda_add_library(${LIB_NAME}
${gpuacc_SRCS}
OPTIONS -DSTUFF="blah blah"
RELEASE -DNDEBUG
DEBUG -g -DDEBUG
)
set(PUBLIC_HEADERS "myheader1.h;myheader2.h")
INSTALL(FILES ${PUBLIC_HEADERS} DESTINATION include)
INSTALL(FILES "${CMAKE_BINARY_DIR}/src/lib${LIB_NAME}.a" DESTINATION lib)
EDIT: this is not working!
The problem is that there are undefined references to all cuda functions (eg. cudaMalloc) when linking the generated library when building a executable in the main project.
Still working on it
EDIT (2016-03-15): Yes, it is confirmed as a bug in FindCUDA: https://cmake.org/Bug/view.php?id=15157
TL;DR: This seems to be a bug in FindCUDA, which makes objects lose info on external definitions before the final linking.
The problem is that, even if separable compilation is enabled, a linking step is still performed for all the targets individually before the final linking.
For instance, I have module.cu with:
#include "module.h"
#include <cstdio>
double arr[10] = {1,2,3,4,5,6,7,8,9,10};
__constant__ double carr[10];
void init_carr() {
cudaMemcpyToSymbol(carr,arr,10*sizeof(double));
}
__global__ void pkernel() {
printf("(pkernel) carr[%d]=%g\n",threadIdx.x,carr[threadIdx.x]);
}
void print_carr() {
printf("in print_carr\n");
pkernel<<<1,10>>>();
}
and module.h with:
extern __constant__ double carr[10];
extern double arr[10];
void print_carr();
void init_carr();
and finally main.cu with:
#include "module.h"
#include <cstdio>
__global__ void kernel() {
printf("(kernel) carr[%d]=%g\n",threadIdx.x,carr[threadIdx.x]);
}
int main(int argc, char *argv[]) {
printf("arr: %g %g %g ..\n",arr[0],arr[1],arr[2]);
kernel<<<1,10>>>();
cudaDeviceSynchronize();
print_carr();
cudaDeviceSynchronize();
init_carr();
cudaDeviceSynchronize();
kernel<<<1,10>>>();
cudaDeviceSynchronize();
print_carr();
cudaDeviceSynchronize();
return 0;
}
This then works fine with the following Makefile:
NVCC=nvcc
NVCCFLAGS=-arch=sm_20
LIB=libmodule.a
OBJS=module.o main.o
PROG=extern
$(PROG): main.o libmodule.a
$(NVCC) $(NVCCFLAGS) -o $# $^
%.o: %.cu
$(NVCC) $(NVCCFLAGS) -dc -c -o $# $^
$(LIB): module.o
ar cr $# $^
clean:
$(RM) $(PROG) $(OBJS) $(LIB)
But then I try to use the following CMakeLists.txt:
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.8)
PROJECT(extern)
FIND_PACKAGE(CUDA REQUIRED)
SET(CUDA_SEPARABLE_COMPILATION ON)
SITE_NAME(HOSTNAME)
SET(CUDA_NVCC_FLAGS ${CUDA_NVCC_FLAGS} -arch=sm_20)
cuda_add_library(module module.cu)
CUDA_ADD_EXECUTABLE(extern main.cu)
TARGET_LINK_LIBRARIES(extern module)
When then compiling, what then happens is that the following:
$ cmake ..
-- The C compiler identification is GNU 4.9.2
...
$ make VERBOSE=1
...
[ 25%] Building NVCC (Device) object CMakeFiles/module.dir//./module_generated_module.cu.o
...
-- Generating <...>/build/CMakeFiles/module.dir//./module_generated_module.cu.o
/usr/local/cuda/bin/nvcc <...>/module.cu -dc -o <...>/build/CMakeFiles/module.dir//./module_generated_module.cu.o -ccbin /usr/bin/cc -m64 -Xcompiler ,\"-g\" -arch=sm_20 -DNVCC -I/usr/local/cuda/include
[ 50%] Building NVCC intermediate link file CMakeFiles/module.dir/./module_intermediate_link.o
/usr/local/cuda/bin/nvcc -arch=sm_20 -m64 -ccbin "/usr/bin/cc" -dlink <...>/build/CMakeFiles/module.dir//./module_generated_module.cu.o -o <...>/build/CMakeFiles/module.dir/./module_intermediate_link.o
...
/usr/bin/ar cr libmodule.a CMakeFiles/module.dir/./module_generated_module.cu.o CMakeFiles/module.dir/./module_intermediate_link.o
/usr/bin/ranlib libmodule.a
...
[ 50%] Built target module
[ 75%] Building NVCC (Device) object CMakeFiles/extern.dir//./extern_generated_main.cu.o
...
-- Generating <...>/build/CMakeFiles/extern.dir//./extern_generated_main.cu.o
/usr/local/cuda/bin/nvcc <...>/main.cu -dc -o <...>/build/CMakeFiles/extern.dir//./extern_generated_main.cu.o -ccbin /usr/bin/cc -m64 -Xcompiler ,\"-g\" -arch=sm_20 -DNVCC -I/usr/local/cuda/include -I/usr/local/cuda/include
...
[100%] Building NVCC intermediate link file CMakeFiles/extern.dir/./extern_intermediate_link.o
/usr/local/cuda/bin/nvcc -arch=sm_20 -m64 -ccbin "/usr/bin/cc" -dlink <...>/build/CMakeFiles/extern.dir//./extern_generated_main.cu.o -o <...>/build/CMakeFiles/extern.dir/./extern_intermediate_link.o
nvlink error : Undefined reference to 'carr' in '<...>/build/CMakeFiles/extern.dir//./extern_generated_main.cu.o'
Clearly, the problem are the nvcc -dlink obj.o -o obj_intermediate_link.o lines. Then, I guess, the info on external definitions are lost. So, the question is, it is possible to make CMake/FindCUDA not do this extra linking step?
Otherwise, I would argue that this is a bug. Do you agree? I can file a bug report with CMake.
Tested it with nvcc version:
nvcc: NVIDIA (R) Cuda compiler driver Copyright (c) 2005-2012 NVIDIA
Corporation Built on Fri_Sep_21_17:28:58_PDT_2012 Cuda compilation
tools, release 5.0, V0.2.1221
and svn revision:
URL: https://gforge.sci.utah.edu/svn/findcuda/trunk
Repository Root: https://gforge.sci.utah.edu/svn/findcuda
Repository UUID: 81322f20-870f-0410-845c-a4cd4664c908
Revision: 1221
Node Kind: directory
Schedule: normal
Last Changed Rev: 1221
Last Changed Date: 2013-01-28 22:31:07 +0100 (Mon, 28 Jan 2013)
In this example includes following classes:
lib.h / lib.cu
kernel.h / kernel.cu
kernel.cu contains a simple CUDA kernel and a class with a public method to call the CUDA kernel. The class lib contains an instance of the class kernel and a method calling the public method of class kernel.
Following CMakeLists.txt works with this configuration:
cmake_minimum_required(VERSION 2.6.2)
project(Cuda-project)
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/CMake/cuda" ${CMAKE_MODULE_PATH})
find_package(CUDA QUIET REQUIRED)
set(CUDA_ATTACH_VS_BUILD_RULE_TO_CUDA_FILE OFF)
set(BUILD_SHARED_LIBS ON)
list(APPEND CUDA_NVCC_FLAGS -DBLAH="he he" -DTEST1="this is a test")
CUDA_ADD_LIBRARY(test_lib
kernel.cu
lib.cu
# SHARED
# STATIC
OPTIONS -DSTUFF="blah blah"
RELEASE --use_fast_math -DNDEBUG
DEBUG -g -DDEBUG
)
INSTALL(FILES lib.h kernel.h
DESTINATION include)
INSTALL(FILES "${CMAKE_BINARY_DIR}/libtest_lib.so"
DESTINATION lib)
I couldn't make it works using CUDA_ADD_EXECUTABLE so I created a function that makes a custom target to do so.
function(add_cuda_exe_lib name files libraries is_lib)
set (obj_list)
foreach(file ${files})
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${file}.o
DEPENDS ${file}
COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --blue "Building NVCC Device object ${CMAKE_CURRENT_SOURCE_DIR}/${file}"
COMMAND ${CUDA_NVCC_EXECUTABLE} ${CUDA_NVCC_FLAGS} -dc "${CMAKE_CURRENT_SOURCE_DIR}/${file}" -o "${CMAKE_CURRENT_BINARY_DIR}/${file}.o"
COMMENT "Building ${CMAKE_CURRENT_SOURCE_DIR}/${file}"
VERBATIM
)
LIST(APPEND obj_list ${CMAKE_CURRENT_BINARY_DIR}/${file}.o)
endforeach()
set (lib_list)
LIST(APPEND lib_list "-lcudadevrt")
foreach(library_name ${libraries})
LIST(APPEND lib_list "-l${library_name}")
endforeach()
set (flags ${CUDA_NVCC_FLAGS})
if (is_lib)
LIST(APPEND flags "-dlink")
set (obj_name "${CMAKE_CURRENT_BINARY_DIR}/${name}.so")
else()
set (obj_name "${CMAKE_CURRENT_BINARY_DIR}/${name}")
endif()
add_custom_target(${name} ALL
COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --red "Linking CXX executable ${name}"
COMMAND ${CUDA_NVCC_EXECUTABLE} ${flags} ${obj_list} ${lib_list} -o ${obj_name}
DEPENDS ${obj_list}
COMMENT "Linking ${name}"
)
endfunction()
function(add_cuda_exe name files libraries)
add_cuda_exe_lib(${name} "${files}" "${libraries}" OFF)
endfunction()
function(add_cuda_lib name files libraries)
add_cuda_exe_lib(${name} "${files}" "${libraries}" ON)
endfunction()
Now, to generate a lib, just use:
add_cuda_lib(testar "devicemanager.cu;blub.cu" "")
Or this to generate an executable:
add_cuda_exe(testar "devicemanager.cu;blub.cu" "")
The last param is a list of libs to be attached.
I hope it helps.