I am making a project in C with GTK and Cairo using CMake to generate the Makefile and its compilation, I have certain problems with CMake because it is giving me an error when trying to compile and I cannot identify where it is, I think I know what the problem is (I don't gtk + links) as it complains that it can't find gtk/gtk.h which is in my gui.h header. My directory tree is as follows:
Proyecto
|--CMakeLists.txt
|--includes
| |--GUI
| |--gui.h
| |--Principal
| |--window-principal.h
|--src
|--main.c
|--CMakeLists.txt
|--GUI
|--CMakeLists.txt
|--gui.c
|--Principal
|--CMakeLists.txt
|--windows-principal.c
My Project has two directories: includes and src , which, as their names indicate, one is for the headers and the other for the sources.
Inside src so far there is the GUI directory , I have separated my project in this way because the GUI design is going to be quite extensive, inside it for now there is only the Main directory , I want to separate each part of the GUI for a future maintenance, leaving one directory for the Main GUI structure, open other directories for the Menu layout , Preferences , Settings , Cairo etc, and everything will be a different section within the GUI directory .
In the src directory there will be other directories for everything corresponding to the Project Core to separate my project from the GUI for the future if I want to implement a GUI in QT or bring it to Windows I can do it without much work, and not only with the GUI Also if I want to switch to Cairo for OpenGL or Vulkan I can do it without breaking anything.
My CMake files are as follows:
Project/CMakeLists.txt
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
project(Proyecto)
include_directories(includes)
add_subdirectory(src)
src/CMakeLists.txt
cmake_minimum_required(VERSION 3.8 FATAL_ERROR)
project(Proyecto)
INCLUDE_DIRECTORIES(../includes)
add_subdirectory(GUI)
add_executable(${PROJECT_NAME} "main.c" GUI)
GUI/CMakeLists.txt
set(INCLUDE_GUI ../../includes/GUI)
INCLUDE_DIRECTORIES(../../includes/GUI)
set(GUI_SOURCES
gui.c
${INCLUDE_GUI}/gui.h
)
add_library(GUI ${GUI_SOURCES})
find_package(PkgConfig REQUIRED)
pkg_search_module(GTK REQUIRED gtk+-3.0)
target_link_libraries(GUI PUBLIC ${GTK_LIBRARIES})
target_include_directories(GUI PUBLIC ${GTK_INCLUDE_DIRS})
target_compile_options(GUI PUBLIC ${GTK_CFLAGS_OTHERS})
add_subdirectory(Principal)
In this CMakeLists.txt I think is the problem, but according to the CMake documentation , the PkgConfig package should link to it as it works fine for me.
Main/CMakeLists.txt
set(WINDOW_PRINCIPAL_INCLUDE ../../../includes/GUI/Principal)
set(WINDOW_PRINCIPAL_SOURCE
window-principal.c
${WINDOW_PRINCIPAL_INCLUDE}/window-principal.h)
add_library(WINDOW_PRINCIPAL ${WINDOW_PRINCIPAL_SOURCE})
target_link_libraries(WINDOW_PRINCIPAL PUBLIC GUI)
Before I had the CMakes well done because I created the Makefile and compiled it correctly, but I "broke" it trying to add the latter, because as you can see, within the GUI directory all its sections must have GTK+ linked, which in turn is linked a library that is created in the GUI directory .
Keep in mind that GTK+ only needs it linked to the library that comes out of the GUI directory because I don't need it in the rest of the project and its respective sections.
So I am not able to link the GTK+ library correctly nor have I been able to achieve that striped directory that I want.
I sincerely appreciate your responses, corrections, concerns, and advice.
EDITED 1
CMake generates the Makefile without any problems, but when trying to compile it complains that it cannot find the gtk/gtk.h header that is inside the gui.h header, the error is as follows:
GUI/gui.h:7: error: gtk/gtk.h: No such file or directory #include
<gtk/gtk.h>
EDITED 2
In the CMake cache the two variables GTK_INCLUDEDIR:INTERNAL and GTK_INCLUDE_DIRS have the following settings:
GTK_INCLUDEDIR:INTERNAL=/usr/include
GTK_INCLUDE_DIRS:INTERNAL=/usr/include/gtk-3.0;/usr/include/at-spi2-atk/2.0;/usr
/include/at-spi-2.0;/usr/include/dbus-1.0;/usr/lib64/dbus-1.0/include;/usr/inclu
de/gtk-3.0;/usr/include/gio-unix-2.0/;/usr/include/cairo;/usr/include/pango-1.0;
/usr/include/harfbuzz;/usr/include/pango-1.0;/usr/include/atk-1.0;/usr/include/c
airo;/usr/include/pixman-1;/usr/include/freetype2;/usr/include/libdrm;/usr/inclu
de/libpng16;/usr/include/gdk-pixbuf-2.0;/usr/include/libpng16;/usr/include/glib-
2.0;/usr/lib64/glib-2.0/include
As dependencies shows:
GUI_LIB_DEPENDS:STATIC=general;gtk-3;general;gdk-3;general;
pangocairo-1.0;general;pango-1.0;general;atk-1.0;general;cairo-gobject;
general;cairo;general;gdk_pixbuf-2.0;general;gio-2.0;general;gobject-
2.0;general;glib-2.0;
The C compiler tries to compile with the following line:
/usr/bin/cc -I/home/usuario/Documentos/Programacion/GTK/Proyecto/includes -o CMakeFiles/Proyecto.dir/main.c.o -c /home/usuario/Documentos/Programacion/GTK/Proyecto/src/main.c
After a while standing on this problem I found the cause, and it is more than all a lack of general knowledge about cmake, so I am going to answer the question in parts, the first part is what the title of the question says, How generate a project in cmake , so based on a series of questions in the StackOverflow community which are more than a couple of years old, but being theory and logical operation, they are still perfectly functional.
The following is a translation, summary and own conclusions based on the answer to the question how is cmake used? , and the cmake documentation .
What is CMake?
Its official page says:CMake does not compile, it generates the configuration and the compilation files, it creates the Makefile(s) and make proceeds to compile (in linux) depending on the configuration that was generated, the main script used by CMake is always called CMakeLists.txt, in This contains EVERYTHING needed for the compilation, which compiler will be used, which libraries will be linked to the project, which libraries will be created, how (static, shared or modules), how the project directory tree is sectioned, external configurations such as that said project can be compiled using Qt or GTK, etc.
In CMake you have a single general configuration without the need to have several separate configurations depending on which compiler you work with, it is also supported by several IDEs such as Visual Studio , Qt Creator , Eclipse , Netbeans , Code::Blocks etc. being able to pass projects from IDE to IDE without the need to change anything, since the configuration goes in the CMake files.
How does CMake work?
I will use as an example the initial directory tree of my projectYour CMake project is configured according to the first CMakeLists.txt and executes all the instructions and configurations that are in it, a project can have many sub-directories and as you can see in the directory tree there are several CMakeLists.txt in the different sub-directories, the first CMakeLists.txt is executed and inside this there is an instruction called add_subdirectory(directory) which specifies a sub-directory in which another CMakeLists.txt script is located, executing it, in this way you can have a very sectionalized and well-organized directory structure.
CMake performs the configuration and compilation from a folder that is not the root of the project, this is called Compiling out of source , which has the benefit of maintaining the order of the project tree, because the generated files are outside of it, or being able to compile several versions of the same project, a Release version and a Debug version, as other benefits to parts. The tree generated by CMake of the project would look like this (removing several generated files that I won't explain now):
and when performing a
make
to the project the following tree is generated following the assigned configuration:The project is built and the executable is ready. You can change the place of the executable or have multiple executables, it all varies depending on your project design.
How to build a project with CMake
The first CMakeLists.txt of the project is as follows:Project/CMakeLists.txt
In the first line the minimum required version of CMake is established, this must always be established, in this case it is version 3.8 in which it is sure that it works. The name of the project is established with
project
and it is advisable to add it, it is not uncommon in cases where several projects are managed from common files, SET establishes a normal, cache or environment variable, in this case an own one is created using the variable CMAKE_SOURCE_DIR to save the place where the headers are, include_directories adds the directories where the compiler will look for the headers and finallyadd_subdirectory
it would execute the CMakeLists.txt inside the src directory.Inside the src directory there is another script, it is the following:
src/CMakeLists.txt
In this script the script is called in GUI , and the executable is created with add_executable from some source files (main.c) and finally target_link_libraries is used to link a library or indicator with a specific object or dependencies , in summary it is indicated that the executable object has a dependency on the A_GUI library, which is created in the GUI directory.
In the GUI directory is the following script:
GUI/CMakeLists.txt
In this script the static library A_GUI is created with add_library , this library is the one that the executable depends on, the line
include
adds the FindPkgConfig package which is PkgConfig that its module is added withfind_package
,pkg_search_module
searches the system for GTK+,target_link_libraries
links the GTK+ library to the project, putting it as a dependency on the A_GUI library along with the WINDOW_PRINCIPAL library , which is created in the Main sub-directory .This is a very small overview of what CMake can do and how it does it, being able to generate really complex projects, and that can be built on different compilers using the same cmake files.
Problem solution
That was a brief explanation of how CMake works, giving the example of the project I work on, now answering the question for which I created this question in the first place, why my initial configuration did not work for me, the GTK+ library did not link to me as a dependency of A_GUI and the project.
As user Trauma had commented, I had a path problem, I wasn't adding the GTK+ headers path, but at the same time I wasn't putting the A_GUI library in the executable as a dependency.
Added the following lines in the script's:
src/CMakeLists.txt
GUI/CMakeLists.txt
I made some changes to the scripts (if you're an observer you'll already notice), which was to make them more "elegant", although the reason it didn't work was basically those two lines.
Take a good look at the error message:
It complains that it can't find an include file . And now, in the relevant part of your
CMakeLists.txt
:The problem is clear. You are already putting the main path of the GTK headers in the list of directories to search for includes .
Therefore, your
#include <gtk/gtk.h>
is incorrect. As you have it, the directorygtk
is already in the list of directories to search for headers.Simply remove the directory part: