Is there something wrong with my CMakeLists? - c++

The following is my CMakeLists file. By default, I expect it to define the symbol ALLEGRO_STATICLINK in the agui_allegro5 library, but it does not, however when I check off WANT_SHARED it defines it, which it shouldnt, but does not define AGUI_BACKEND_BUILD as it should. Is my logic flawed or something?
cmake_minimum_required(VERSION 2.6)
project(agui)
OPTION(WANT_SHARED "Build agui and the backend as a shared library" OFF)
OPTION(WANT_ALLEGRO5_BACKEND "Build the Allegro 5 backend" ON)
set(AGUI_SOURCES
src/Agui/ActionEvent.cpp
src/Agui/ActionListener.cpp
src/Agui/BaseTypes.cpp
src/Agui/BlinkingEvent.cpp
src/Agui/BorderLayout.cpp
src/Agui/Color.cpp
src/Agui/Dimension.cpp
src/Agui/EmptyWidget.cpp
src/Agui/EventArgs.cpp
src/Agui/FlowLayout.cpp
src/Agui/FocusListener.cpp
src/Agui/FocusManager.cpp
src/Agui/Font.cpp
src/Agui/FontLoader.cpp
src/Agui/Graphics.cpp
src/Agui/GridLayout.cpp
src/Agui/Gui.cpp
src/Agui/Image.cpp
src/Agui/ImageLoader.cpp
src/Agui/Input.cpp
src/Agui/KeyboardListener.cpp
src/Agui/Layout.cpp
src/Agui/MouseListener.cpp
src/Agui/Point.cpp
src/Agui/Rectangle.cpp
src/Agui/ResizableText.cpp
src/Agui/ResizableBorderLayout.cpp
src/Agui/SelectionListener.cpp
src/Agui/TopContainer.cpp
src/Agui/Widget.cpp
src/Agui/WidgetListener.cpp
src/Agui/Widgets/Button/Button.cpp
src/Agui/Widgets/Button/ButtonListener.cpp
src/Agui/Widgets/CheckBox/CheckBox.cpp
src/Agui/Widgets/CheckBox/CheckBoxListener.cpp
src/Agui/Widgets/DropDown/DropDown.cpp
src/Agui/Widgets/DropDown/DropDownListener.cpp
src/Agui/Widgets/Frame/Frame.cpp
src/Agui/Widgets/Frame/FrameListener.cpp
src/Agui/Widgets/Label/Label.cpp
src/Agui/Widgets/Label/LabelListener.cpp
src/Agui/Widgets/ListBox/ListBox.cpp
src/Agui/Widgets/ListBox/ListBoxListener.cpp
src/Agui/Widgets/RadioButton/RadioButton.cpp
src/Agui/Widgets/RadioButton/RadioButtonListener.cpp
src/Agui/Widgets/RadioButton/RadioButtonGroup.cpp
src/Agui/Widgets/ScrollBar/HScrollBar.cpp
src/Agui/Widgets/ScrollBar/HScrollBarListener.cpp
src/Agui/Widgets/ScrollBar/VScrollBar.cpp
src/Agui/Widgets/ScrollBar/VScrollBarListener.cpp
src/Agui/Widgets/ScrollPane/ScrollPane.cpp
src/Agui/Widgets/Slider/Slider.cpp
src/Agui/Widgets/Slider/SliderListener.cpp
src/Agui/Widgets/Tab/Tab.cpp
src/Agui/Widgets/Tab/TabbedPane.cpp
src/Agui/Widgets/Tab/TabbedPaneListener.cpp
src/Agui/Widgets/TextBox/TextBox.cpp
src/Agui/Widgets/TextBox/TextBoxListener.cpp
src/Agui/Widgets/TextBox/ExtendedTextBox.cpp
src/Agui/Widgets/TextField/TextField.cpp
src/Agui/Widgets/TextField/TextFieldListener.cpp
)
set(ALLEGRO5_BACKEND_SOURCES
src/Agui/Backends/Allegro5/Allegro5Font.cpp
src/Agui/Backends/Allegro5/Allegro5FontLoader.cpp
src/Agui/Backends/Allegro5/Allegro5Graphics.cpp
src/Agui/Backends/Allegro5/Allegro5Image.cpp
src/Agui/Backends/Allegro5/Allegro5ImageLoader.cpp
src/Agui/Backends/Allegro5/Allegro5Input.cpp
)
include_directories (./include)
if(WANT_SHARED)
add_library(agui SHARED ${AGUI_SOURCES})
set_target_properties(agui PROPERTIES DEFINE_SYMBOL "AGUI_BUILD")
if(WANT_ALLEGRO5_BACKEND)
add_library(agui_allegro5 SHARED ${ALLEGRO5_BACKEND_SOURCES})
set_target_properties(agui_allegro5 PROPERTIES DEFINE_SYMBOL "AGUI_BACKEND_BUILD")
target_link_libraries (agui_allegro5 agui)
endif()
else()
add_library(agui STATIC ${AGUI_SOURCES})
if(WANT_ALLEGRO5_BACKEND)
add_library(agui_allegro5 STATIC ${ALLEGRO5_BACKEND_SOURCES})
set_target_properties(agui_allegro5 PROPERTIES DEFINE_SYMBOL "ALLEGRO_STATICLINK")
endif()
endif()
Thanks

According to CMake documentation "DEFINE_SYMBOL sets the name of the preprocessor symbol defined when compiling sources in a shared library" while you are trying to use it on a static library, hence no effect. You may also find this discussion useful.

Cmake documentation says that property DEFINE_SYMBOL attended only for shared libraries, try to use COMPILE_DEFINITIONS property.

Related

How to create a cmake header-only library that depends on external non-header-only target?

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.

CMake BUILD undefined reference (findpng)

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.

Does the header would be included explicitly when use find_package in CMake?

When I use PyTorch for C++, it's pretty easy to just use find_package to set up the dependency. And here is the CMakeLists.txt:
cmake_minimum_required(VERSION 3.0 FATAL_ERROR)
project(dcgan)
set(CMAKE_PREFIX_PATH /User/root/libtorch) # I added this line, does it effect?
find_package(Torch REQUIRED)
add_executable(dcgan dcgan.cpp)
target_link_libraries(dcgan "${TORCH_LIBRARIES}")
set_property(TARGET dcgan PROPERTY CXX_STANDARD 14)
There isn't any explicit command to include the header, but the header could be found if target_link_libraries(dcgan "${TORCH_LIBRARIES}") exists. I am curious why the header file could be found even there is no target_include_directories(dcgan PUBLIC ${TORCH_INCLUDE_DIRS}).
The code is on the official website of PyTorch and it works on MacOS and Linux. What happened.
ADD:
The package is in a directory where the compiler knows nothing about it.
The include path can be set as propagated setting in the dependency:
target_link_libraries
Specify libraries or flags to use when linking a given target and/or
its dependents. Usage requirements from linked library targets will be
propagated. Usage requirements of a target’s dependencies affect
compilation of its own sources.
https://cmake.org/cmake/help/latest/command/target_link_libraries.html
That means that target_link_libraries will configure the target . It will set target_compile_features, target_compile_options, target_compile_directories, if they're set as INTERFACE or PUBLIC in the dependency.
E.g.
add_library(Lib ${SRCS_LIB})
target_include_directories(Lib INTERFACE ${DIRECTORY})
add_exectuable(Exe ${SRCS_EXE})
target_link_libraries(Exe PRIVATE Lib)
In this example Exe will inherit the include directories from Lib. You don't need to set them explicitly.
That's also how Conan works, e.g. Getting started
cmake_minimum_required(VERSION 2.8.12)
project(MD5Encrypter)
add_definitions("-std=c++11")
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
add_executable(md5 md5.cpp)
target_link_libraries(md5 ${CONAN_LIBS})
and how it's described in Effective Modern CMake
Use exported targets of external packages.
Don’t fall back to the old
CMake style of using variables defined by external packages. Use the
exported targets via target_link_libraries instead.
Best practice is to not use target_include_directories for your dependencies.

How to include external (shared) library with several dlls/dylibs

I need to integrate an externally built (shared) library as a logical
build target into my CMake configuration which consists of several
shared library files (dlls/dylibs).
Usually I'd do the following:
find_path(MyLib_INCLUDE_DIR mylib.h HINTS ${MyLib_PATH}/include)
find_library(MyLib_LIBRARY NAMES MyLib HINTS ${MyLib_PATH}/bin)
find_library(MyLib_LIBRARY_DEBUG NAMES MyLib_d ${MyLib_PATH}/bin)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(MyLib DEFAULT_MSG
MyLib_LIBRARY MyLib_LIBRARY_DEBUG MyLib_INCLUDE_DIR)
if(MyLib_FOUND AND NOT TARGET MyLib::MyLib)
set(MyLib_LIBRARIES ${MyLib_LIBRARY})
set(MyLib_INCLUDE_DIRS ${MyLib_INCLUDE_DIR})
add_library(MyLib::MyLib UNKNOWN IMPORTED)
set_target_properties(MyLib::MyLib PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES ${MyLib_INCLUDE_DIR}
IMPORTED_LOCATION ${MyLib_LIBRARY}
IMPORTED_LOCATION_DEBUG ${MyLib_LIBRARY_DEBUG})
mark_as_advanced(MyLib_INCLUDE_DIR MyLib_LIBRARY MyLib_LIBRARY_DEBUG)
endif()
My understanding is that add_library(... UNKOWN IMPORTED) will CMake
figure out shared/static libraries by itself (and based on the option
BUILD_SHARED_LIBS probably, but this shouldn't be necessary). However,
CMake doesn't allow to add a list of libraries to the property
IMPORTED_LOCATION. For Instance, in case I want to import MyLib,
MyLibFoo and MyLibBar both as debug and release builds, this is not
possible using the listed approach.
I'm aware of add_library(MyLib::MyLib INTERFACE IMPORTED) and set a
list of libraries to the property INTERFACE_LINK_LIBRARIES, but this
is problematic in the case of creating relocatable packages (as
mentioned in the CMake documentation). Furthermore, there's no such
property as INTERFACE_LINK_LIBRARIES_DEBUG, thus it would only be
possible to refer to either the release or debug builds of my
externally imported library.
So, how am I supposed to import my externally built library as an
relocatable-ready logical CMake target which actually includes several
either debug or release build shared library files?
Disclaimer: I've already asked this on the CMake mailing list, but haven't received any answer so far.
However, CMake doesn't allow to add a list of libraries to the property IMPORTED_LOCATION.
This is logical, because a single library (even IMPORTED) should have a single file.
Just create a library IMPORTED target per actual library:
add_library(MyLib::MyLibFoo UNKNOWN IMPORTED)
set_target_properties(MyLib::MyLibFoo PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES ${MyLibFoo_INCLUDE_DIR}
IMPORTED_LOCATION ${MyLibFoo_LIBRARY}
IMPORTED_LOCATION_DEBUG ${MyLibFoo_LIBRARY_DEBUG})
add_library(MyLib::MyLibBar UNKNOWN IMPORTED)
set_target_properties(MyLib::MyLibBar PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES ${MyLibBar_INCLUDE_DIR}
IMPORTED_LOCATION ${MyLibBar_LIBRARY}
IMPORTED_LOCATION_DEBUG ${MyLibBar_LIBRARY_DEBUG})
And create a single INTERFACE IMPORTED library target, which links above targets:
add_library(MyLib::MyLib INTERFACE IMPORTED)
set_property(TARGET MyLib::MyLib PROPERTY
INTERFACE_LINK_LIBRARIES MyLib::MyLibFoo MyLib::MyLibBar)
So, when user links MyLib::MyLib, he actually links all of your libraries.

Possible to add an imported library to target_link_libraries that takes care of include directories too?

somehow I am struggling with finding out whether it is possible to define an imported library in CMake, specifying target properties (include_directories and library path) and hoping that CMake will append the include directories once I add that project to target_link_libraries in another project.
Let's say I have an imported library in a file called Module-Conf.cmake:
add_library(mymodule STATIC IMPORTED)
set_target_properties(mymodule PROPERTIES IMPORTED_LOCATION "${OUTPUT_DIR}/lib")
set_target_properties(mymodule PROPERTIES INCLUDE_DIRECTORIES "${OUTPUT_DIR}/include")
And in a project I add the dependency:
include(Module-Conf)
target_link_libraries(${PROJECT_NAME} mymodule)
Will CMake append the include_directories property to the include path? Right now I cannot see the path so it seems that I have to do it by myself by using get_target_property?
Question: Can I do some CMake magic to automatically append the include to the include directories of another project?
Thanks a lot.
Martin
The difference between the INCLUDE_DIRECTORIES property and the INTERFACE_INCLUDE_DIRECTORIES property is transitivity.
Set INTERFACE_INCLUDE_DIRECTORIES instead.
http://www.cmake.org/cmake/help/latest/manual/cmake-buildsystem.7.html#transitive-usage-requirements
Starting from CMake 3.11, it's possible to use target_include_directories() with IMPORTED targets.
add_library(mymodule SHARED IMPORTED)
target_include_directories(mymodule INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
Another way is set_property(), which also allows using generator expressions.
set_property(TARGET mymodule PROPERTY INTERFACE_INCLUDE_DIRECTORIES
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)