I want to use the Qt5 library in my subdirectories without adding the all components to each subdirectory. In the parent CMakeLists I use find_package(Qt5) to make sure that the library exists and the Qt5_DIR variable is set. Example:
CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
find_package(Qt5 COMPONENTS Core REQUIRED)
add_subdirectory(exec1)
add_subdirectory(exec2)
In the subdirectory exec1 I want to use the Qt5::Xml component. As far as I have understood CMake, the Qt5_DIR variable is passed to the subdirectories. Therefore I do not have to use find_package again. Is this assumption correct? Example:
exec1/CMakeLists.txt
project(exec1)
set(SRC exec1.cpp)
set(HDR exec1.h)
add_executable(exec1 ${SRC} ${HDR})
target_link_libraries(exec1 Qt5::Core Qt5::Xml)
In a second subdirectory exec2 I want to add other components of Qt5. Example:
exec2/CMakeLists.txt
project(exec2)
set(SRC exec2.cpp)
set(HDR exec2.h)
add_executable(exec2 ${SRC} ${HDR})
target_link_libraries(exec2 Qt5::Core Qt5::Websockets)
Does it make any difference if I add all components in the parent CMakeLists.txt instead of choosing for each subproject only some components?
How do I handle this case so that I can exchange Qt5 components in the subdirectories without affecting other subdirectories?
Or is it more convenient to add the find_package to each subdirectory instead of using it once in the parent CMakeLists.txt?
Any suggestions and tipps are appreciated.
If you want to use Qt5 components in subdirectories while using find_package only once in the root CMakeLists.txt -- which is IMHO the right thing to do -- you will have to list all the wanted components when finding Qt.
So following your example, root CMakeLists.txt should be something like that:
cmake_minimum_required(VERSION 3.0)
find_package(QT5 COMPONENTS Xml Websocket REQUIRED)
add_subdirectory(exec1) # will use Xml
add_subdirectory(exec2) # will use Websocket
Note that I removed Core since it is implicitly added as a dependency of both Xml and Websocket. Same goes for the subdirectories:
add_executable(exec1 ${SRC} ${HDR})
target_link_libraries(exec1 QT5::Xml)
Here exec1 implicitly links against Core component because it is a dependency of Xml.
Related
How to create a cmake header-only library that depends on external header files? is close but different.
I have a single-header library, MyHeaderLib. In MyHeaderLib/MyHeader.h I have #include <QString>, so anyone doing #include "MyHeaderLib/MyHeader.h" had better have QString in their path (i.e., Qt5Core to CMake, I think(?)) and it they'll need to link to Qt5Core.
What belongs in my CMakeLists.txt for MyHeaderLib? I have
cmake_minimum_required(VERSION 3.12)
add_library(MyHeaderLib INTERFACE)
target_include_directories(MyHeaderLib include/)
# (^ Where include/ contains MyHeaderLib/MyHeader.h)
Anything I try with target_link_libraries(MyHeaderLib requires INTERFACE and if I do target_link_libraries(MyHeaderLib INTERFACE Qt5Core) that doesn't suffice.
Ultimately I got it to work as follows, but I don't understand what is going on:
cmake_minimum_required(VERSION 3.12)
find_package(Qt5Core REQUIRED) # <- Can't be Qt5::Core
add_library(MyHeaderLib INTERFACE)
target_include_directories(MyHeaderLib include/)
# (^ Where include/ contains MyHeaderLib/MyHeader.h)
target_link_libraries(MyHeaderLibrary
INTERFACE
Qt5::Core # <- Can't be Qt5Core
)
I gather the targets with :: in them are aliases, but I'm perplexed why it needs to be exactly like this. Furthermore, I can't find add_library(Qt5::Core ALIAS Qt5Core) anywhere. What is going on? Why do I have to find_package(Qt5Core REQUIRED) and not find_package(Qt5::Core REQUIRED) and why can't target_link_libraries take Qt5Core?
Packages are responsible for defining targets. The Qt maintainers chose to name the package Qt5Core while deciding to define the Qt5::Core target.
Usually the convention with CMake packages is that a package named package-name will define package-name::package-name with maybe other optional targets or subcomponents of package-name::package-name.
As to answer why Qt don't act like this, look inside Qt5CoreConfig.cmake, you'll see this line:
add_library(Qt5::Core SHARED IMPORTED)
Here you go. The file is named Qt5CoreConfig so it needs find_package(Qt5Core), but the target is under the Qt5 namespace as they choose to define it.
This is maybe because Qt5 Also has a general package which you can use components:
find_package(Qt5 REQUIRED COMPONENTS Core)
# Here Qt5::Core kinda make sense.
I'm still very new to CMake so feedback is definitely welcome. So, I'm trying to build a simple application that should eventually create a pdf using the library libharu.
I think i figured it out how to link the library. But I still receive build errors for the findpng module (I suppose libharu depends on it)
CMakeLists.txt:
cmake_minimum_required(VERSION 3.2.0 FATAL_ERROR) # current latest stable version (if lower give FATAL_ERROR)
project(pdf_generator VERSION 0.1.0) # name of the project, version.
file(GLOB TARGET_SRC "./src/*.cpp") # Creates variable, using globbing.
include_directories(${PROJECT_SOURCE_DIR}/include) # list of directories to be used as header search paths.
add_executable(main ${TARGET_SRC}) # Create an executable of set of source files [exe name files to bundle].
find_library(libhpdf_location NAMES libhpdf.a) # find the location of libhpdf.a and save the value in the variable libhpdf_location.
message(STATUS ${libhpdf_location}) # print status of variable.
add_library(libhpdf STATIC IMPORTED) # Add library via a static import.
set_target_properties(
libhpdf PROPERTIES
IMPORTED_LOCATION ${libhpdf_location}
)
target_link_libraries(main libhpdf)
I've never worked with that particular library before, but skimming their CMakeLists.txt on GitHub it seems like libharu has optional dependencies on libpdf and zlib. Without knowing how you built your version of libharu I'm going to assume that both are needed.
Luckily, CMake comes with find-modules for both libpng and zlib, so adding the following should work:
find_package(PNG REQUIRED)
find_package(ZLIB REQUIRED)
set_target_properties(libhpdf
PROPERTIES
INTERFACE_LINK_LIBRARIES "ZLIB::ZLIB;PNG::PNG"
)
Looks like all you need to do is tell cmake to link libpng.
I'm trying to learn how to make a shared library. And the following seems to work (please comment if you have some feedback to this method, I basically have no idea what I'm doing).
In my library project, I've put the header files into a folder named "include", and the source files into "src".
My library's CMakeLists.txt:
cmake_minimum_required(VERSION 2.4.0)
project(mycustomlib)
# Find source files
file(GLOB SOURCES src/*.cpp)
# Include header files
include_directories(include)
# Create shared library
add_library(${PROJECT_NAME} SHARED ${SOURCES})
# Install library
install(TARGETS ${PROJECT_NAME} DESTINATION lib)
# Install library headers
file(GLOB HEADERS include/*.h)
install(FILES ${HEADERS} DESTINATION include)
My application's CMakeLists.txt:
cmake_minimum_required(VERSION 2.4.0)
project(myprogram)
# Find source files
file(GLOB SOURCES src/*.cpp)
# Create executable
add_executable(${PROJECT_NAME} ${SOURCES})
# Find and link library
find_library(MYCUSTOMLIB mycustomlib)
target_link_libraries(${PROJECT_NAME} ${MYCUSTOMLIB})
And this is working. The problem is that I want to put both the headers and the library into subfolders (specifically: /usr/local/include/mycustomlib/ for the headers, and /usr/local/lib/mycustomlib/ for the library).
So this is my attempt:
My library's new CMakeLists.txt:
cmake_minimum_required(VERSION 2.4.0)
project(mycustomlib)
# Find source files
file(GLOB SOURCES src/*.cpp)
# Include header files
include_directories(include)
# Create shared library
add_library(${PROJECT_NAME} SHARED ${SOURCES})
# Install library
install(TARGETS ${PROJECT_NAME} DESTINATION lib/${PROJECT_NAME})
# Install library headers
file(GLOB HEADERS include/*.h)
install(FILES ${HEADERS} DESTINATION include/${PROJECT_NAME})
My application's new CMakeLists.txt:
cmake_minimum_required(VERSION 2.4.0)
project(myprogram)
# Find source files
file(GLOB SOURCES src/*.cpp)
# Create executable
add_executable(${PROJECT_NAME} ${SOURCES})
# Find and link library
find_library(MYCUSTOMLIB mycustomlib/mycustomlib)
target_link_libraries(${PROJECT_NAME} ${MYCUSTOMLIB})
And this is not working. Now, I'm forced to specify the .so file of the library like this:
find_library(MYCUSTOMLIB mycustomlib/libmycustomlib.so)
How come?
I'll deal with your actual problem first and offer additional comments after that. Technically speaking, you are asking CMake to find a library named mycustomlib/mycustomlib, but what you really want to say is you want find mycustomlib and it can be found in a subdirectory called mycustomlib. A couple of alternative ways to call find_library() to achieve this for your second case would be:
find_library(MYCUSTOMLIB mycustomlib PATH_SUFFIXES mycustomlib)
find_library(MYCUSTOMLIB mycustomlib PATHS /usr/local/lib/mycustomlib)
The latter is making more assumptions than it should about where you have the library installed, so I'd favour the first option. The first option assumes CMake would already find libraries in /usr/local/lib, which it seems it is from your question. You can influence where CMake looks for libraries by modifying CMAKE_PREFIX_PATH and CMAKE_LIBRARY_PATH. I'd expect either of the above options to make your second case work.
Now to other observations. You've requested a very old minimum CMake version in the first line of each of your CMakeLists.txt files. You probably want to consider at the very least making this 2.8 (personally, I'd suggest more like 3.2 or later, but it depends on what your project needs to support).
You have used file globbing to obtain your list of sources and headers. This is not robust and should generally be avoided (see a discussion of this here). You will see plenty of example code use method this for simplicity, but it is not recommended for real world projects (the CMake documentation even says not to use it). Explicitly list out your source and header files individually if you want robust builds.
If you are happy to require CMake 2.8.11 or later (and you should be these days), rather than calling include_directories() which makes everything pick up the header search path you specified, you should prefer to attach the search path requirement to the target that needs it. You do this with target_include_directories(). The equivalent of your code above would be:
target_include_directories(${PROJECT_NAME} PUBLIC include)
This gives much better control of your inter-target dependencies as your project grows in size and complexity. For a more in-depth discussion of this topic, see this article and perhaps also this one (disclosure: I wrote both articles).
Are your library and program totally separate source code repositories? Can they be built in the same project? You can build multiple targets in one CMakeLists.txt file. The project name doesn't have to have any relationship to the names of any of the targets (you often see the PROJECT_NAME variable re-used for the target name in simple examples, which is unfortunate since it suggests a relationship between the two, but for all but simple projects this won't be the case). If they are in the same repository, building them together would be a much simpler build since you wouldn't have to install the library for the executable to find it and link to it.
If they must be built in separate projects, then something like the following for the application's project should get you close:
cmake_minimum_required(VERSION 2.8.11)
project(myprogram)
# List your program's sources here explicitly
add_executable(myprogram src/foo.cpp src/bar.cpp)
# Find and link library
find_library(MYCUSTOMLIB mycustomlib PATH_SUFFIXES mycustomlib)
target_link_libraries(myprogram PUBLIC ${MYCUSTOMLIB})
# Find library's headers and add it as a search path.
# Provide the name of one header file you know should
# be present in mycustomlib's include dir.
find_path(MCL_HEADER_PATH mycustomlib.h PATH_SUFFIXES mycustomlib)
target_include_directories(myprogram PUBLIC ${MCL_HEADER_PATH})
For extra points, you could try to confirm that the header path is in the same area as the library by checking the common path prefix, or you could just derive
the MCL_HEADER_PATH from the MYCUSTOMLIB path by assuming a directory structure. Both approaches have advantages and drawbacks. If you want to explore the latter, the get_filename_component() command will be your friend.
Hopefully that points you in the right direction.
I'm working on a project that consists of 3 server executables and one library for shared code. I want it to be cross-platform, so I'm using CMake (since Xcode is being a pain anyway) to handle the build process. I'm having trouble with setting up the CMakeLists so that I can include the library from a directory at the same level when I'm building the executable.
Here's the directory structure (and the CMake files):
tethealla2.0/
CMakeLists.txt
libtethealla/
CMakeLists.txt
encryption/
utils/
patch_server/
CMakeLists.txt
login_server/
CMakeLists.txt
ship_server/
CMakeLists.txt
My top-level CMake (tethealla2.0/CMakeLists.txt, only includes the sub-project that should compile):
project(tethealla CXX)
cmake_minimum_required(VERSION 2.6)
add_subdirectory(libtethealla)
add_subdirectory(patch_server)
tethealla2.0/libtethealla/CMakeLists.txt, which generates a static library:
project(Libtethealla C)
cmake_minimum_required(VERSION 2.6)
include_directories(encryption)
set(ENC_DR encryption/)
set(ENCRYPTION_SOURCES
${ENC_DR}/psobb-crypt.c
${ENC_DR}/psogc-crypt.c
${ENC_DR}/psobb-crypt.c
${ENC_DR}/encryption.c
)
add_library(tethealla STATIC ${ENCRYPTION_SOURCES})
tethealla2.0/patch_server/CMakeLists.txt thus far:
project(patch_server CXX)
cmake_minimum_required(VERSION 2.6)
add_executable(server main.cc)
target_link_libraries(server tethealla)
So it makes more sense if I build it from the top level since tethealla2.0/CMakeLists.txt will inherit the targets from each of the subdirectories and the one in patch_server will have access to the tethealla library. However what I want is to be able to build from within these subdirectories to generate Xcode projects so that I can work on/recompile them individually. To do so I need to be able to get to the libtethealla/build directory (where CMake outputs) to access the libtethealla.a library from patch_server. Is this possible?
On kind of another note, even in building from the top-level directory my source in patch_server can't include "encryption.h", the header file for the library in encryption. Which seems to be building fine. Any thoughts on that are also greatly appreciated!
My solution is to use add_subdirectory with relative patch to shared_lib directory. I don't think that this is a perfect solution it has its caveats:
Logic very similar to a header guard must be added to library CMakeLists.txt to prevent from defining targets multiple times.
Each CMakeList.txt file must know the relative path to the library, if one want to move library all CMakeLists must be updated.
Let's assume that the directory structure looks like this:
root/
CMakeLists.txt
shared_lib/
CMakeLists.txt
inc/
foo.h
src/
foo.c
exec1/
CMakeLists.txt
main.c
exec2/
CMakeLists.txt
main.c
root/CMakeList.txt
cmake_minimum_required(VERSION 2.6)
add_subdirectory(shared_lib)
add_subdirectory(exec1)
add_subdirectory(exec2)
I have decided that shared_lib/CMakeLists.txt will export a variable named SHARED_DIR_INCLUDE_DIR. This approach helps to decouple things a little bit.
root/exec1/CMakeLists.txt
cmake_minimum_required(VERSION 2.6)
add_subdirectory(./../shared_lib shared_lib)
include_directories(${SHARED_LIB_INCLUDE_DIR})
set(SRCS main.c)
add_executable(exec1 ${SRCS})
target_link_libraries(exec1 shared_lib)
if() in the fourth line solves the issue with target's multiple definition in case the CMakeLists file is added multiple times. The second and the third lines exports the include directory for library in SHARED_LIB_INCLUDE_DIR
root/shared_lib/CMakeLists.txt
cmake_minimum_required(VERSION 2.6)
set(SHARED_LIB_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/inc)
set(SHARED_LIB_INCLUDE_DIR ${SHARED_LIB_INCLUDE_DIR} PARENT_SCOPE)
if(TARGET shared_lib)
message("shared_lib is already defined")
else()
include_directories(${SHARED_LIB_INCLUDE_DIR})
set(LIB_SRCS ./src/foo.c)
add_library(shared_lib STATIC ${LIB_SRCS})
endif()
I'm attempting to build a Qt-based application using cmake (It's what Kdevelop gave me). I tried to use a QWebView;
QWebView *webView = new QWebView( this );
webView->load(QUrl("http://google.ca"));
But it failed with Undefined Reference errors...
undefined reference to `QWebView::QWebView(QWidget*)'
undefined reference to `QWebView::load(QUrl const&)'
I looked it up and I needed to add QTWEBKIT to my project, but all the solutions said to add it to my .pro file... And I'm not using .pro. In the QT documentation it said to add "set(QT_USE_QTWEBKIT TRUE)" to my CMAKE file, this is my CMakeLists.txt file now:
#-------------------------------------------------------------------------------
# Corrections Tool CMAKE list
#-------------------------------------------------------------------------------
project(corrections)
# Versioning Requirements
#-------------------------------------------------------------------------------
cmake_minimum_required(VERSION 2.6)
find_package(Qt4 REQUIRED)
# Include QT Librtaries
#-------------------------------------------------------------------------------
set(QT_USE_QTWEBKIT TRUE)
# Set Sources
#-------------------------------------------------------------------------------
set(corrections_SRCS corrections.cpp main.cpp utilities.cpp prettySplash.cpp)
#The Rest
#-------------------------------------------------------------------------------
include_directories(${QT_INCLUDES} ${CMAKE_CURRENT_BINARY_DIR})
qt4_automoc(${corrections_SRCS})
add_executable(corrections ${corrections_SRCS})
target_link_libraries(corrections ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY})
install(TARGETS corrections RUNTIME DESTINATION bin)
But I'm still getting the errors, so either I did it wrong, in the wrong place, etc. I've also cleaned out and reconfigured my project several times making sure I wasn't using a bad generated makefile.
How would I either fix my cmake config to actually work, or convert my project to using .pro (with minimum stress & heartache)?
Thank you.
The canonical way to select Qt components in CMake is to specify the them in the find_package call and then to include ${QT_USE_FILE}
FIND_PACKAGE( Qt4 COMPONENTS QtWebKit REQUIRED )
INCLUDE( ${QT_USE_FILE} )
...
TARGET_LINK_LIBRARIES( corrections ${QT_LIBRARIES} )
This already configures the include directories and sets ${QT_LIBRARIES} to contain all relevant Qt libraries (i.e. your selected component and all Qt libraries it depends on).
So you don't need to manually add the libraries by listing them individually as you did in your example.
Edit:
Additional explaination:
The COMPONENTparameter to FIND_PACKAGE actually does the same as your manual call to set QT_USE_WEBKIT. But this variable is only evaluated/used in UseQt4.cmake which is included (and "executed") by the INCLUDEcommand. See CMake documentation of FindQt4 for details.
You do not appear to be linking against all of the Qt libraries. Use ${QT_LIBRARIES} instead of ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY} in your target_link_libraries
target_link_libraries(corrections ${QT_LIBRARIES} )