I have a folder structure with the following
mainfolder/
folderA/a.dll
folderB/subFolderB/subsubFolderB/b.dll
folderC/c.dll
I want to get all DLLs which are in mainfolder, including sub folders.
I don't get any result by doing a GLOB_RECURSE on the mainfolder. The DLLs are definitely there before the code reaches the GLOB_RECURSE call.
Here is what I am doing:
set(THIRDPARTY_INSTALLFOLDER ${CMAKE_BINARY_DIR}/3rdparty/${CMAKE_BUILD_TYPE})
file(GLOB_RECURSE THEDLLS ${THIRDPARTY_INSTALLFOLDER} "*.dll")
message(STATUS ${THEDLLS})
so the ${THIRDPARTY_INSTALLFOLDER} is the mainfolder I mentioned above. The output of the message with ${THEDLLS} is just nothing.
NOTE: After clean/rebuild/run cmake again it finds the DLLs which are in folderA but nothing more(nothing in folderB or folderC). And again, the DLLs are definitely created before the GLOB_RECURSE call but NOT before the CMakeLists.txt is called.
EDIT: Here is a working example. Be aware that it downloads the rather large library OpenCV. I used this because my main concern is that the DLLs are created in different folder structures depending on which system you built it, which is why copying the DLLs seems so difficult to me. To run this example, just create a CMakeLists.txt and a main.cpp with the following content:
CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
project(DownloadAndLinkOpenCV)
###First I define where to output .exe and DLLs if there were any
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/build/${CMAKE_BUILD_TYPE}/bin)
###This defines the INSTALL dir of OpenCV(Where to put DLLs etc.)
set(THIRDPARTY_INSTALLFOLDER ${CMAKE_BINARY_DIR}/3rdparty/${CMAKE_BUILD_TYPE})
####function to download and build external project from https://stackoverflow.com/questions/17446981/cmake-externalproject-add-and-findpackage
function (build_external_project target git_repo git_tag cmake_custom_args)
message(STATUS "building ${target} this might take a while...")
set(trigger_build_dir ${CMAKE_BINARY_DIR}/3rdparty_${target})
file(MAKE_DIRECTORY ${trigger_build_dir} ${trigger_build_dir}/build)
set(CMAKE_LIST_CONTENT "
cmake_minimum_required(VERSION 3.13)
include(ExternalProject)
ExternalProject_Add(${target}
GIT_REPOSITORY ${git_repo}
GIT_TAG ${git_tag}
PREFIX ${target}
CMAKE_ARGS ${cmake_custom_args}
)
ExternalProject_Add_StepTargets(${target} build install)
add_custom_target(trigger_${target})
add_dependencies(trigger_${target} ${target})
")
file(WRITE ${trigger_build_dir}/CMakeLists.txt "${CMAKE_LIST_CONTENT}")
execute_process(COMMAND ${CMAKE_COMMAND} .. -G${CMAKE_GENERATOR}
WORKING_DIRECTORY ${trigger_build_dir}/build
)
execute_process(COMMAND ${CMAKE_COMMAND} --build .
WORKING_DIRECTORY ${trigger_build_dir}/build
)
message(STATUS "finished building ${target}!")
endfunction()
####build OpenCV
set(OPENCV_GIT_REPO https://github.com/opencv/opencv.git)
set(OPENCV_TAG 3.4.6)
set(OPENCV_CMAKE_ARGS "-DCMAKE_INSTALL_PREFIX=${THIRDPARTY_INSTALLFOLDER} -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")
build_external_project(opencv ${OPENCV_GIT_REPO} ${OPENCV_TAG} ${OPENCV_CMAKE_ARGS})
####finished building OpenCV now that it is built we can use find_package
set(OpenCV_DIR ${THIRDPARTY_INSTALLFOLDER})
find_package(OpenCV REQUIRED)
####finished 3rdparty building
add_executable(TestApplication main.cpp)
target_link_libraries(TestApplication ${OpenCV_LIBS})
####Now I want to copy the DLLs which were built but GLOB_RECURSE will not give me the name of all DLLs
####I specifically used OpenCV since the main problem is that it creates a more complex folder structure
####in the INSTALL dir. In my case it creates /x64/vc15/...the dlls (x64 is architecture and vc15 the compiler)
####I don't want to hard code the x64/vc15 string because anyone should be able to built it with any compiler
file(GLOB_RECURSE THEDLLS ${THIRDPARTY_INSTALLFOLDER} "*.dll")
message(STATUS ${THEDLLS})
main.cpp
#include <iostream>
#include <opencv2/core.hpp>
int main()
{
cv::Mat Mat;
std::cout<<"Hello World"<<std::endl;
}
Note that I use Qt Creator as IDE and the Visual Studio 2017 compiler
After some additional thoughts I found the very simple solution to it. I did a wrong call to GLOB_RECURSE. Here is the snippet which is working and does what I want: Copying all DLLs inside a folder to a specific directory
####get all dlls in a folder including sub folders
####The wrong call was this one:
####file(GLOB_RECURSE THEDLLS ${THIRDPARTY_INSTALLFOLDER} "*.dll")
file(GLOB_RECURSE THEDLLS "${THIRDPARTY_INSTALLFOLDER}/*.dll")
#####This will copy the DLLs to a folder
foreach(currentDLL ${THEDLLS})
add_custom_command(TARGET TestApplication POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${currentDLL}
${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
endforeach(currentDLL)
The thing is GLOB_RECURSE does a complete path pattern matching.
Related
I'm trying to use CLion to create a SDL2 project.
The problem is that the SDL headers can't be found when using #include's.
My CMakeLists.txt file:
cmake_minimum_required(VERSION 2.8.4)
project(ChickenShooter)
set(SDL2_INCLUDE_DIR C:/SDL/SDL2-2.0.3/include)
set(SDL2_LIBRARY C:/SDL/SDL2-2.0.3/lib/x64)
include_directories(${SDL2_INCLUDE_DIR})
set(SOURCE_FILES main.cpp)
add_executable(ChickenShooter ${SOURCE_FILES})
target_link_libraries(ChickenShooter ${SDL2_LIBRARY})
My test main.cpp:
#include <iostream>
#include "SDL.h" /* This one can't be found */
int main(){
if (SDL_Init(SDL_INIT_VIDEO) != 0){
std::cout << "SDL_Init Error: " << SDL_GetError() << std::endl;
return 1;
}
SDL_Quit();
return 0;
}
Thank you for any help you could give me.
Edit:
I'm using Windows and CLion is configured to use cygwin64.
This blog post shows how you can do it: Using SDL2 with CMake
On Linux you can use a recent CMake (e.g. version 3.7) and using SDL2 works out of the box.
cmake_minimum_required(VERSION 3.7)
project(SDL2Test)
find_package(SDL2 REQUIRED)
include_directories(SDL2Test ${SDL2_INCLUDE_DIRS})
add_executable(SDL2Test Main.cpp)
target_link_libraries(SDL2Test ${SDL2_LIBRARIES})
Under Windows you can download the SDL2 development package, extract it somewhere and then create a sdl-config.cmake file in the extracted location with the following content:
set(SDL2_INCLUDE_DIRS "${CMAKE_CURRENT_LIST_DIR}/include")
# Support both 32 and 64 bit builds
if (${CMAKE_SIZEOF_VOID_P} MATCHES 8)
set(SDL2_LIBRARIES "${CMAKE_CURRENT_LIST_DIR}/lib/x64/SDL2.lib;${CMAKE_CURRENT_LIST_DIR}/lib/x64/SDL2main.lib")
else ()
set(SDL2_LIBRARIES "${CMAKE_CURRENT_LIST_DIR}/lib/x86/SDL2.lib;${CMAKE_CURRENT_LIST_DIR}/lib/x86/SDL2main.lib")
endif ()
string(STRIP "${SDL2_LIBRARIES}" SDL2_LIBRARIES)
When you now configure inside the CMake-GUI application there will be a SDL2_DIR variable. You have to point it to the SDL2 directory where you extracted the dev package and reconfigure then everything should work.
You can then include SDL2 headers by just writing #include "SDL.h".
Don't set the path to SDL2 by hand. Use the proper find command which uses FindSDL. Should look like:
find_file(SDL2_INCLUDE_DIR NAME SDL.h HINTS SDL2)
find_library(SDL2_LIBRARY NAME SDL2)
add_executable(ChickenShooter main.cpp)
target_include_directories(ChickenShooter ${SDL2_INCLUDE_DIR})
target_link_libraries(ChickenShooter ${SDL2_LIBRARY})
If SDL2 is not found, you have to add the path to SDL2 to CMAKE_PREFIX_PATH, that's the place where CMake looks for installed software.
If you can use Pkg-config, its use might be easier, see How to use SDL2 and SDL_image with cmake
If you feel more comfortable to use a FindSDL2.cmake file similar to FindSDL.cmake provided by CMake, see https://brendanwhitfield.wordpress.com/2015/02/26/using-cmake-with-sdl2/
You can also pull in the SDL source repository as a submodule and build/link it statically along with your main program via add_subdirectory() and target_link_libraries():
cmake_minimum_required( VERSION 3.18.0 )
project( sdl2-demo )
set( SDL_STATIC ON CACHE BOOL "" FORCE )
set( SDL_SHARED OFF CACHE BOOL "" FORCE )
# 'external/sdl' should point at a SDL
# repo clone or extracted release tarball
add_subdirectory( external/sdl )
add_executable(
${CMAKE_PROJECT_NAME}
"src/main.cpp"
)
target_link_libraries( ${CMAKE_PROJECT_NAME} SDL2main SDL2-static )
(At least as of the release-2.0.9 tag, possibly earlier.)
I recently discovered the latest version of SDL2 (version 2.0.12) now comes with all the required CMake config/install scripts, so there's no need to use FindSDL anymore.
I downloaded the SDL source from https://www.libsdl.org/download-2.0.php then from the root folder ran...
cmake -S . -B build/debug -G Ninja -DCMAKE_INSTALL_PREFIX=./install -DCMAKE_BUILD_TYPE=Debug
cmake --build build/debug --target install
This will build and install the debug version of the library, you can then also run...
cmake -S . -B build/release -G Ninja -DCMAKE_INSTALL_PREFIX=./install -DCMAKE_BUILD_TYPE=Release
cmake --build build/release --target install
Which will build and install the release version of the library (and because the SDL CMake script uses DEBUG_POSTFIX the release version of the library won't overwrite the debug one as the debug versions all have 'd' appended to their name).
In your CMakeLists.txt file you can then simply do this:
find_package(SDL2 REQUIRED)
add_executable(${PROJECT_NAME} ...)
target_link_libraries(
${PROJECT_NAME} PRIVATE
SDL2::SDL2
SDL2::SDL2main
You'll need to tell your application where to find the SDL install folder if you used a custom location as I've done in the example. To do this from the root folder of your app run:
cmake -S . -B build/debug -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_PREFIX_PATH=</absolute/path/to/install/dir>
cmake --build build/debug
Note: You can use $(pwd) (*nix/macOS) or %cd% (Windows) to create a hybrid relative path which can be very useful.
You can omit both DCMAKE_INSTALL_PREFIX and DCMAKE_PREFIX_PATH if you want to install SDL to the default system location.
In the examples I've opted to use the Ninja generator as it is consistent across macOS/Windows - it can be used with MSVC/Visual Studio, just make sure you run this (path may differ slightly depending on year/version) to add Ninja to your path.
C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat
Update:
One other thing I remembered which is useful on Windows is the ability to copy the SDL .dll file into the application binary directory, this can be achieved like so:
if (WIN32)
# copy the .dll file to the same folder as the executable
add_custom_command(
TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:SDL2::SDL2>
$<TARGET_FILE_DIR:${PROJECT_NAME}>
VERBATIM)
endif()
Using the SDL2 CMake module that I developed, you can integrate the SDL2 library easily in a modern and portable approach.
You should just copy the module in cmake/sdl2 (Or just clone the modules repo) in your project:
git clone https://github.com/aminosbh/sdl2-cmake-modules cmake/sdl2
Then add the following lines in your CMakeLists.txt:
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/sdl2)
find_package(SDL2 REQUIRED)
target_link_libraries(${PROJECT_NAME} SDL2::Main)
Note: If CMake didn't find the SDL2 library (in Windows), we can specify the CMake option SDL2_PATH as follows:
cmake .. -DSDL2_PATH="/path/to/sdl2"
For more details, please read the README.md file.
The SDL2 CMake modules support other related libraries : SDL2_image, SDL2_ttf, SDL2_mixer, SDL2_net and SDL2_gfx.
You can find a list of examples/samples and projects that uses these modules here : https://github.com/aminosbh/sdl-samples-and-projects
With the compiled version of SDL2-2.0.9 with MinGW-w64 in Windows, the following configuration works for me:
find_package(SDL2 REQUIRED)
add_executable(sdl-test ${SOURCES})
target_link_libraries(sdl-test
mingw32
SDL2::SDL2main
SDL2::SDL2
)
A longer explanation
By reading SDL2Targets.cmake file, I've learned that SDL2 is providing several targets:
SDL2::SDL2main (lib/libSDL2main.a)
SDL2::SDL2 (lib/libSDL2.dll.a)
SDL2::SDL2-static (lib/libSDL2-static.a)
Each of them has INTERFACE_INCLUDE_DIRECTORIES defined, which means we don't need to manually specify include_directories for SDL2.
But by only adding SDL2::SDL2main and SDL2::SDL2 as target_link_libraries is not enough. The g++ compiler might be complaining about "undefined reference to `WinMain'".
By inspecting the compiler options, I found that the SDL2 libraries are added before -lmingw32 option. In order to make the -lmingw32 option comes before SDL2 libraries, we have to also specify mingw32 as the first target_link_libraries. Which will make this configuration working.
The command that I have used for building it is:
$ mkdir build && cd build && cmake .. -G"MinGW Makefiles" && cmake --build .
The only small problem here is in the finally generated compiler options, the -lmingw32 option is duplicated. But since it doesn't affect the linking process, I've ignored it for now.
On Linux, in Clion, this works:
cmake_minimum_required(VERSION 3.20)
project(first_game)
set(CMAKE_CXX_STANDARD 14)
find_package(SDL2 REQUIRED)
include_directories(${SDL2_INCLUDE_DIRS})
add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} ${SDL2_LIBRARIES})
You don't seems to have a CMake error whike generating your make file. But I think your problem is, the SDL Header are located in a subfolder named "SDL2".
Change your CMakeLists.txt to include
C:/SDL/SDL2-2.0.3/include/SDL2
Instead of
C:/SDL/SDL2-2.0.3/include
I had the same problem and none of the other solutions worked.
But I finally got it working by following this solution : How to properly link libraries with cmake?
In a nutshell, the problem was that the SDL2 library was not linked properly in my CMakeLists.txt. And by writing this into the file, it worked (more explainations in the other thread) :
project (MyProgramExecBlaBla) #not sure whether this should be the same name of the executable, but I always see that "convention"
cmake_minimum_required(VERSION 2.8)
ADD_LIBRARY(LibsModule
file1.cpp
file2.cpp
)
target_link_libraries(LibsModule -lpthread)
target_link_libraries(LibsModule liblapack.a)
target_link_libraries(LibsModule -L/home/user/libs/somelibpath/)
ADD_EXECUTABLE(MyProgramExecBlaBla main.cpp)
target_link_libraries(MyProgramExecBlaBla LibsModule)
Highlighting the steps of how I was able to eventually accomplish this using the FindSDL2.cmake module:
Download SDL2-devel-2.0.9-VC.zip (or whatever version is out after this answer is posted) under the Development Libraries section of the downloads page.
Extract the zip folder and you should see a folder similar to "SDL2-2.0.9". Paste this folder in your C:\Program Files(x86)\ directory.
Copy the FindSDL2.cmake module and place it in a new "cmake" directory within your project. I found a FindSDL2.cmake file in the answer referenced in the Accepted Answer: https://brendanwhitfield.wordpress.com/2015/02/26/using-cmake-with-sdl2/
Find the SET(SDL2_SEARCH_PATHS line in the FindSDL2.cmake and add your copied development directory for SDL2 as a new line: "/Program Files (x86)/SDL2-2.0.9" # Windows
Within my CMakeLists.txt, add this line: set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
After this, running CMake worked for me. I'm including the rest of my CMakeLists just in case it further clarifies anything I may have left out:
cmake_minimum_required(VERSION 2.8.4)
project(Test_Project)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
# includes cmake/FindSDL2.cmake
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake)
set(SOURCE_FILES src/main.cpp src/test.cpp)
add_executable(test ${SOURCE_FILES})
# The two lines below have been removed to run on my Windows machine
#INCLUDE(FindPkgConfig)
#PKG_SEARCH_MODULE(SDL2 REQUIRED sdl2)
find_package(SDL2 REQUIRED)
INCLUDE_DIRECTORIES(${SDL2_INCLUDE_DIR})
TARGET_LINK_LIBRARIES(chip8 ${SDL2_LIBRARY})
Hope this helps somebody in the near future.
by the time of my answer, SDL2 is provided with sdl2-config executable (as I understand, developers call him "experimental").
After "make install" of SDL2 you can try calling it from terminal with
sdl2-config --cflags --libs to see what it outputs.
And then you can add call to it in your makefile:
set(PROJECT_NAME SomeProject)
project(${PROJECT_NAME})
execute_process(COMMAND /usr/local/bin/sdl2-config --libs RESULT_VARIABLE CMD_RES OUTPUT_VARIABLE SDL2_CFLAGS_LIBS ERROR_VARIABLE ERR_VAR OUTPUT_STRIP_TRAILING_WHITESPACE)
message("SDL2_CFLAGS_LIBS=${SDL2_CFLAGS_LIBS}; CMD_RES=${CMD_RES}; ERR_VAR=${ERR_VAR}")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 ${SDL2_CFLAGS_LIBS}")
set(SOURCE_FILES main.cpp)
add_executable(${PROJECT_NAME} ${SOURCE_FILES})
Here I have a problem - if I only put an executable name without path like
execute_process(COMMAND sdl2-config --libs <...>
I get error "No such file", i.e. cmake does not search in current path and I don't know how to write it properly by now.
One more notice: in my makefile I do not user --cflags option, because cmake finds includes correctly and I do not need to specify them explicitly.
For your information, I was able to successfully cmake and compile SDL2_ttf while linking to SDL2 source code.
At first I was getting errors due to cmake not being able to locate SDL2, even though it was specified in cmake using the SLD2_DIR variable in cmake.
It seems that for some reason cmaking SDL2 fails to create the SDL2Targets.cmake file which is searched for by SDL2_ttf
If this is the case for you, get the SDL2Targets.cmake file from https://bugs.archlinux.org/task/57972 and modify the file like so:
You can remove the following lines:
get_filename_component(_IMPORT_PREFIX "${CMAKE_CURRENT_LIST_FILE}" PATH)
get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
get_filename_component(_IMPORT_PREFIX "${_IMPORT_PREFIX}" PATH)
if(_IMPORT_PREFIX STREQUAL "/")
set(_IMPORT_PREFIX "")
endif()
and add this one:
set(_IMPORT_PREFIX "C:/SDL2-2.0.12")
Obviously change the filepath to the place you unpacked the SDL2 source code
I'm not sure if this is exactly your issue, but there it is.
I have the following simple CMake project. It's basically an executable which links dynamically to Qt Widgets (I'm using Qt just as an example). What I'm trying to figure out is whether it is possible to copy all the linked libraries (not only the ones built by the current project) to the executable output directory using CMake.
cmake_minimum_required(VERSION 3.12)
project(MyProject)
set(CMAKE_CXX_STANDARD 14)
set(QT_CMAKE_DIR "/Users/huser/Qt/5.11.1/clang_64/lib/cmake")
set(CMAKE_PREFIX_PATH ${CMAKE_MODULE_PATH} ${QT_CMAKE_DIR})
find_package(Qt5 REQUIRED COMPONENTS Widgets)
add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} PUBLIC Qt5::Widgets)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_LIST_DIR}/build)
The issue is that the output directory only contains the MyProject executable (which is the expected behaviour). However, if I were to distribute that executable to someone who doesn't have Qt installed, they would not be able to open it. Hence, I would like to bundle only the necessary libraries/ frameworks with the executable.
Running otool -L MyProject lists the dependencies:
MyProject:
#rpath/QtWidgets.framework/Versions/5/QtWidgets
#rpath/QtGui.framework/Versions/5/QtGui
#rpath/QtCore.framework/Versions/5/QtCore
/usr/lib/libc++.1.dylib
/usr/lib/libSystem.B.dylib
What I'm looking for is a common way through CMake to get these 3 frameworks copied in the output directory right after the build step. That would result in the following directory structure:
build/
MyProject
QtWidgets.framework
QtGui.framework
QtCore.framework
Any help would be greatly appreciated!
There are two aspects to consider:
the build tree
the install tree
The build tree is what you work with as a developer, the install tree is what is "created" after executing the install target or after extracting the content of a package.
To redistribute your Qt5 based project, I suggest you leverage two tools:
CPack: This allow to create generate packages or archives that can be distributed to users. These includes windows installers, .tar,gz, .dmg, ...
macdeployqt: Tools provided by Qt allowing to copy all libraries, plugins, ... required by your application.
Using the BundleUtilities would still require you to explicitly identify and install all Qt plugins. For more complex application, with dependencies other than Qt, is is indeed helpful but for a simple application, I would suggest to use the approach described below.
You will find below a modified version of your example including some suggestions regarding the best practices as well as the integration of CPack and macdeployqt.
After configuring and building project, building the Package target will create a MyProject-0.1.1-Darwin.dmg package.
Note that more would need to be done but that should give a good starting point.
Reading the following may also be helpful: https://gitlab.kitware.com/cmake/community/wikis/doc/cmake/RPATH-handling
To configure the project, consider passing the variable -DQt5_DIR:PATH=/path/to/lib/cmake/Qt5 instead of hardcoding the path.
Assuming the sources or the project are in a directory named src, you would configure the project with:
mkdir build
cd build
cmake -DQt5_DIR:PATH=/Volumes/Dashboards/Support/Qt5.9.1/5.9.1/clang_64/lib/cmake/Qt5 ../src/
src/CMakeLists.txt:
cmake_minimum_required(VERSION 3.12)
project(MyProject)
set(CMAKE_CXX_STANDARD 14)
# Suggestions:
# (1) EXECUTABLE_OUTPUT_PATH is deprecated, consider
# setting the CMAKE_*_OUTPUT_DIRECTORY variables
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/build)
set(CMAKE_MACOSX_BUNDLE 1)
set(CMAKE_INSTALL_RPATH "#executable_path/../Frameworks")
# Suggestions:
# (1) Do not hardcode path to Qt installation
# (2) Configure the project specifying -DQt5_DIR
# See https://blog.kitware.com/cmake-finding-qt5-the-right-way/
# (3) By convention, "REQUIRED" is added at the end
find_package(Qt5 COMPONENTS Widgets REQUIRED)
add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} PUBLIC Qt5::Widgets)
install(TARGETS ${PROJECT_NAME} DESTINATION . COMPONENT Runtime)
# Get reference to deployqt
get_target_property(uic_location Qt5::uic IMPORTED_LOCATION)
get_filename_component( _dir ${uic_location} DIRECTORY)
set(deployqt "${_dir}/macdeployqt")
if(NOT EXISTS ${deployqt})
message(FATAL_ERROR "Failed to locate deployqt executable: [${deployqt}]")
endif()
# Execute deployqt during package creation
# See https://doc.qt.io/qt-5/osx-deployment.html#macdeploy
install(CODE "set(deployqt \"${deployqt}\")" COMPONENT Runtime)
install(CODE [===[
execute_process(COMMAND "${deployqt}" "${CMAKE_INSTALL_PREFIX}/MyProject.app")
]===] COMPONENT Runtime)
set(CPACK_GENERATOR "DragNDrop")
include(CPack)
I have been working on a project that uses cmake as the build system and working in QtCreator just fine. There are sub-projects (some Qt related and others just plain c++) and up until now all of them were able to find header files and hence provide auto-complete functionality.
At some point, I added a Qt project and noticed that my headers for Qt proejcts were not being found by the editor. The really odd thing is they compile and run just fine via QtCreator 'Build->Build All'. It is almost like QtCreator just can't find them for auto-complete.
I looked around and users had problems in earlier versions of QtCreator finding Qt classes, but did not seem to have a problem finding their defined header files.
I put together a simplified "project" and added two fresh Qt Widget Application projects with the main window being a QDialog instead of QMainWindow. I added the following CMakeLists.txt file to each project:
My Standard CMake File For Qt
cmake_minimum_required(VERSION 3.0.2)
project("FooProj")
set(EXECUTABLE_NAME "Foo")
# Find Qt packages.
find_package(Qt5Core)
find_package(Qt5Widgets)
find_package(Qt5Gui)
set(CMAKE_AUTOMOC ON)
set(SOURCES
"src/main.cpp"
"src/Dialog.cpp"
)
set(HEADERS
"includes/Dialog.h"
)
set(UI_FORMS
"forms/Dialog.ui"
)
set(RESOURCES_FILES
"resources/resources.qrc"
)
# Convert Qt UI and resource files to C/C++ files.
qt5_wrap_ui(UI_HEADERS ${UI_FORMS})
qt5_add_resources(RESOURCES ${RESOURCES_FILES})
# Define the executable to be built.
add_executable(${EXECUTABLE_NAME}
${SOURCES}
${HEADERS}
${UI_HEADERS}
${RESOURCES}
)
# Include paths for this project.
target_include_directories(${EXECUTABLE_NAME} PRIVATE
"${CMAKE_CURRENT_SOURCE_DIR}/includes"
"${CMAKE_CURRENT_BINARY_DIR}"
)
# Link to appropriate libraries.
target_link_libraries(${EXECUTABLE_NAME}
Qt5::Core
Qt5::Widgets
Qt5::Gui
)
# Set distribution location and project structure.
set(DIST_DIR "${CMAKE_BINARY_DIR}/dist/${EXECUTABLE_NAME}")
set_property(TARGET ${EXECUTABLE_NAME} PROPERTY FOLDER "Guis")
# Setup distribution environment and copy final executable.
add_custom_command(TARGET ${EXECUTABLE_NAME}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory ${DIST_DIR}
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:${EXECUTABLE_NAME}> ${DIST_DIR}
)
# OS specific deployment setup for sytems without Qt installed.
if(WIN32)
add_custom_command(TARGET ${EXECUTABLE_NAME}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/QtDeploymentFiles/run.bat" ${DIST_DIR}/${EXECUTABLE_NAME}.bat
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/QtDeploymentFiles/qt.conf" ${DIST_DIR}
)
elseif(UNIX AND NOT APPLE)
add_custom_command(TARGET ${EXECUTABLE_NAME}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/QtDeploymentFiles/run.sh" ${DIST_DIR}/${EXECUTABLE_NAME}.sh
)
endif()
Along with this top level CMakeLists.txt:
Top Level CMakeLists.txt
cmake_minimum_required(VERSION 3.0.2)
project("Test")
add_subdirectory("FooProj")
add_subdirectory("BarProj")
Everything makes, compiles, and runs just fine. Auto-complete also works.
I add a Qt library project I need and everything still works fine even when I use the library files. Here is the cmake file for my library:
Qt Library CMakeLists.txt
cmake_minimum_required(VERSION 3.0.2)
project("QtLib")
set(LIB_NAME "QtLib")
# Find Qt packages.
find_package(Qt5Core)
find_package(Qt5Widgets)
set(CMAKE_AUTOMOC ON)
# Set source files to be built.
set(SOURCES
"src/LibFile1.cpp"
"src/LibFile2.cpp"
.
.
.
)
set(HEADERS
"includes/LibFile1Header.h"
"includes/LibFile2Header.h"
"includes/OtherHeaders.h"
.
.
.
)
add_library(${LIB_NAME} ${SOURCES} ${HEADERS})
set_property(TARGET ${LIB_NAME} PROPERTY FOLDER "Libs")
target_include_directories(${LIB_NAME} PRIVATE
"${Qt5Core_INCLUDE_DIRS}"
"${Qt5Widgets_INCLUDE_DIRS}"
"${CMAKE_CURRENT_BINARY_DIR}"
)
target_include_directories(${LIB_NAME} PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}/includes"
)
target_link_libraries(${LIB_NAME}
Qt5::Core
Qt5::Widgets
)
Lastly I add one of my real projects (one of the ones that does not auto-complete) The structure is as follows:
-forms
-MainForm.ui
-resources
-resources.qrc
-resource files and dirs
-src
-main.cpp
-MainWindow.cpp
-includes
-MainWindow.h
The CMake file is exactly the same as the ones above. Everything makes, compiles, and runs just fine, but auto-complete stops working. I also noticed that ui_MainWindow.h (the one being auto-generated in my project binary directory) is not found.
The really odd thing is, if I just the exact same files with no changes and just open it as a stand alone project, auto-complete works.
Is there something off about my cmake files or is this a Qt related issue? The same thing happens in VS2013 which makes me think it is not QtCreator related, but I am not sure.
After reading around a little more, it seemed like this might be a QtCreator issue (although I don't know why I had problems in VS2013 when I first tested it). I ended up upgrading my QtCreator to 3.6 and my auto-complete problem went away.
I have the following situation: a Project A depends on a Project B, but both are built at the same time. Project A has to include the includes of project B and it needs also to link its libraries. Up to now I've tried this way:
ADD_SUBDIRECTORY(${CMAKE_SOURCE_DIR}/other_project other_project)
and then:
INCLUDE_DIRECTORIES(includ ${CMAKE_SOURCE_DIR}/other_project/include})
LIST(APPEND LINK_LIBS other_project)
in the CMakeLists.txt of Project A
but it doesn't seem to work, the compiler also gives me error when including the headers of Project B saying that they do not exist.
What is the right way to add dependencies in A? How should the CMakeLists.txt look like?
EDIT:
as suggested in the comments, this question was addressed in this, however I'd like to see an example of how to use it in a CMakeList.txt file.
The following is a simple example which builds zlib and then builds libxml2 which depends on the zlib we build. One thing to note, I quickly pulled this example from stuff I've done before. The libxml2 example uses make, thus will only actually build on a system which has it, e.g. Linux, Mac ...
Here is the proposed directory structure for this example ...
src/
-- CMakeLists.txt
CMake/
---- External_ZLib.cmake
---- External_libxml2.cmake
Dowloads/ ( no need to create this directory, CMake will do it for you )
build/ ( Out of source build to prevent littering source tree )
Files:
CMakeLists.txt
# Any version that supports ExternalProject should do
cmake_minimum_required( VERSION 3.1)
project(test_ext_proj)
set(test_ext_proj_CMAKE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/CMake")
set(CMAKE_MODULE_PATH ${test_ext_proj_CMAKE_DIR} ${CMAKE_MODULE_PATH})
set(test_ext_proj_BUILD_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/install)
set(test_ext_proj_DOWNLOAD_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Downloads CACHE PATH "Directory to store downloaded tarballs.")
include(ExternalProject)
include(External_ZLib)
include(External_libxml2)
External_ZLib.cmake:
set(ZLib_version 1.2.8)
set(ZLib_url "http://zlib.net/zlib-${ZLib_version}.tar.gz")
set(ZLib_md5 "44d667c142d7cda120332623eab69f40")
ExternalProject_Add(ZLib
URL ${ZLib_url}
URL_MD5 ${ZLib_md5}
PREFIX ${vision-tpl_BUILD_PREFIX}
DOWNLOAD_DIR ${test_ext_proj_DOWNLOAD_DIR}
INSTALL_DIR ${test_ext_proj_BUILD_INSTALL_PREFIX}
CMAKE_GENERATOR ${gen}
CMAKE_ARGS
-DCMAKE_INSTALL_PREFIX:PATH=${test_ext_proj_BUILD_INSTALL_PREFIX}
-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
-DCMAKE_C_FLAGS=${CMAKE_C_FLAGS}
)
#This variable is required so other packages can find it.
set(ZLIB_ROOT ${test_ext_proj_BUILD_INSTALL_PREFIX} CACHE PATH "" FORCE)
External_libxml2.cmake:
set(libxml2_release "2.9")
set(libxml2_patch_version 0)
set(libxml2_url "ftp://xmlsoft.org/libxml2/libxml2-sources-${libxml2_release}.${libxml2_patch_version}.tar.gz")
set(libxml2_md5 "7da7af8f62e111497d5a2b61d01bd811")
#We need to state that we're dependent on ZLib so build order is correct
set(_XML2_DEPENDS ZLib)
#This build requires make, ensure we have it, or error out.
if(CMAKE_GENERATOR MATCHES ".*Makefiles")
set(MAKE_EXECUTABLE "$(MAKE)")
else()
find_program(MAKE_EXECUTABLE make)
if(NOT MAKE_EXECUTABLE)
message(FATAL_ERROR "Could not find 'make', required to build libxml2.")
endif()
endif()
ExternalProject_Add(libxml2
DEPENDS ${_XML2_DEPENDS}
URL ${libxml2_url}
URL_MD5 ${libxml2_md5}
PREFIX ${test_ext_proj_BUILD_PREFIX}
DOWNLOAD_DIR ${test_ext_proj_DOWNLOAD_DIR}
INSTALL_DIR ${test_ext_proj_BUILD_INSTALL_PREFIX}
BUILD_IN_SOURCE 1
CONFIGURE_COMMAND ./configure
--prefix=${test_ext_proj_BUILD_INSTALL_PREFIX}
--with-zlib=${ZLIB_ROOT}
BUILD_COMMAND ${MAKE_EXECUTABLE}
INSTALL_COMMAND ${MAKE_EXECUTABLE} install
)
I am working with gcc(cygwin), gnu make, windows 7 and cmake.
my cmake testprojekt has the following structure
rootdir
|-- App
| |-- app.cpp
| +-- CMakeLists.txt
|-- Lib
| |-- lib.cpp
| |-- CMakeLists.txt
|-- MakeFileProject
+ CMakeLists.txt
rootdir/App/app.cpp:
#include<string>
void printThemMessageToScreen(std::string input);//prototype
int main(int argc,char **argv){
printThemMessageToScreen("this will be displayed by our lib");
return 0;
}
rootdir/Lib/lib.cpp:
#include<iostream>
#include<string>
void printThemMessageToScreen(std::string input){
std::cout<<input;
}
rootdir/CMakeLists.txt:
cmake_minimum_required(VERSION 2.6)
project(TestProject)
add_subdirectory(App)
add_subdirectory(Lib)
rootdir/Lib/CMakeLists.txt:
add_library(Lib SHARED lib.cpp)
rootdir/App/CMakeLists.txt:
# Make sure the compiler can find include files from our Lib library.
include_directories (${LIB_SOURCE_DIR}/Lib)
# Make sure the linker can find the Lib library once it is built.
link_directories (${LIB_BINARY_DIR}/Lib)
# Add executable called "TestProjectExecutable" that is built from the source files
add_executable (TestProjectExecutable app.cpp)
# Link the executable to the lib library.
target_link_libraries (TestProjectExecutable Lib)
Now, when i run cmake and make, everything will get generated & built with no errors, but when i try to execute the binary, it will fail because the library which was generated could not be found.
BUT: when i copy the lib dll into the same directory like the app exe, it will get executed!
also: if i configure the library to be static, it will also execute.
how to tell the runtime linker where to look for my dll?
UPDATE:
Solution according to the Method proposed by User Vorren:
I opened up the registry editor, and navigated to the following Key:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths
, here i created a new key with the name of my Applikation:
in this case : TestProjectExecutable.exe
after that, the (default) value was set to the full path of TestProjectExecutable.exe including the filename and extension. Then i created another String Value called "Path" and set the value to the folder where the dll was located:
Your problem lies not with linker or compiler, but with the way Windows searches for DLL's.
The OS will use the following algorithm to locate the required DLL's:
Look in:
The directories listed in the Application-specific Path registry key;
The directory where the executable module for the current process is located;
The current directory;
The Windows system directory;
The Windows directory;
The directories listed in the PATH environment variable;
Thus you have two reasonable options if you don't want to clutter the OS directories with your app-specific dll:
Create an app-specific Path registry entry (I would go with this option);
Put your DLL in the same folder as your EXE;
Modify the PATH variable (but why would you do that, if you can go with option 1?);
A solution I prefer that hasn't really been mentioned, is build your shared-libs into the same directory as your executables. This tends to be a much simpler solution.
One way to do this with cmake is
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
Or you can also set output directories based on build flavours.
See how do I make cmake output into a 'bin' dir?
I discovered (what I believe to be) quite a nice way of handling this. It follows the approach of adding the .dll to the same directory as the .exe. You can do it in CMake like so:
if (WIN32)
# copy the .dll file to the same folder as the executable
add_custom_command(
TARGET <app-target> POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
$<TARGET_FILE_DIR:<lib-target>>
$<TARGET_FILE_DIR:<app-target>)
endif()
where app-target is the name of the application or library you're building (created through add_executable or add_library) and lib-target is the imported library brought in with find_package.
# full example
cmake_minimum_required(VERSION 3.14)
project(my-app-project VERSION 0.0.1 LANGUAGES CXX)
find_package(useful-library REQUIRED)
add_executable(my-application main.cpp)
target_link_libraries(my-application PUBLIC useful-library::useful-library)
if (WIN32)
# copy the .dll file to the same folder as the executable
add_custom_command(
TARGET my-application POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
$<TARGET_FILE_DIR:useful-library::useful-library>
$<TARGET_FILE_DIR:my-application>)
endif()
I tried the option 1 from accepted answer (by pdeschain).
I even created a cmake hook to register paths of linked libraries automatically
function (xtarget_link_libraries target libs) # same as target_link_libraries but with additional improvements to allow windows find the library at runtime
LIST(REMOVE_AT ARGV 0)
SET(LIBS ${ARGV}) # this is to pass list into this function
target_link_libraries(${target} ${LIBS}) # call standard routine
if(WIN32)
set(TFILE ".")
get_property(slibs TARGET ${target} PROPERTY all_libs) # recall libs linked before
set(LIBS ${slibs};${LIBS})
set_property(TARGET ${target} PROPERTY all_libs ${LIBS}) # save all libs
FOREACH(lib ${LIBS}) # compose a list of paths
set(TFILE "${TFILE};$<TARGET_LINKER_FILE_DIR:${lib}>")
ENDFOREACH()
#add reg key
add_custom_command(TARGET ${target} POST_BUILD COMMAND reg add "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\${target}.exe" /v "Path" /d "${TFILE}" /f )
endif()
endfunction()
Can be used as xtarget_link_libraries(test lib1 lib2). The application will be able to find dynamic libraries at their absolute paths.
BUT, there is a big problem with this, that the App Paths mechanism https://msdn.microsoft.com/en-us/library/windows/desktop/ee872121(v=vs.85).aspx#appPaths
does not allow to have different entries for say 'Debug/test.exe' and 'Release/test.exe'. So to me this is a poor option.
You may add the following line to fill the Default key as path to the program as suggested in the post.
add_custom_command(TARGET ${target} POST_BUILD COMMAND reg add "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\${target}.exe" /ve /d "$<TARGET_FILE:${target}>" /f )
Now you can enjoy running test.exe from anywhere in the system... I guess my next try will be option
Create symbolic links to dlls with cmake.