c++cmakegtk3pkg-config

Steps to configure, generate and build correctly a simple project which includes a system library, using CMake and pkg-config?


I have some old experience in writing C/C++ code, but now I want to learn how to build an application in a modern environment, also linking some system libraries.

I am building my code just using the terminal and CMake (i.e. not using an IDE). I am in Linux Mint, but I think that the main answer to this question will apply to any system.

In my case the library I want is GTK, to have a GUI, and I am working on a simple "hello world" example of an application found here:

https://gnome.pages.gitlab.gnome.org/gtkmm-documentation/sec-helloworld.html.

But I am writing the question with a general scenario in mind.

So, my question is: what are the steps to follow, from the time when I realize that the code "includes" some header file from some library, continuing with the installation of the ('dev' package of the) said library, including maybe the inspection of the right path to the given library (headers) (to check that pkg-config does its job), and finally, the correct way to write the CMakeLists.txt, so that the code can be correctly 'configured' and 'built'?

The steps I have followed so far are as follows.

  1. Create a project folder with the main.cpp and helloworld.h files.

  2. Notice that the header file has the #include <gtkmm/button.h> line in it, requiring GTK library.

  3. Making sure that the "dev" package of the needed library is present in the system (with its header files), and if needed, install it using the system package manager. Take note of the installation location.

  4. Write the CMakeLists.txt file, including the find_package(PkgConfig REQUIRED) line to use pkg-config, and then pkg_check_modules(GTK REQUIRED gtkmm-3.0) to read the information about the library paths and dependencies.

  5. Run cmake .. to configure and generate the CMake files in the build folder of the project

  6. Inspect the CMakeCache.txt to check that the correct paths to the library have been read from pkg-config, and possibly amend the CMakeLists.txt file.

  7. Build the exacutable with 'cmake - - build .' in CMake.

This question may seem similar to other questions, such as this one, however, I would like to stress the general nature of my question. Once again I would like to know how to start from scratch, and know the names of the libraries/modules to write in the CMakeLists.txt. In the example I propose, how to go from knowin that GTK is needed, to knowing that the right package name to look for pkg-config is qtkmm-3.0 ? Other similar questions do not have this general approach in mind. Although the accepted answer solves the problem with GTK, I would like to know the steps for e.g. a similar situation but with Qt instead of GTK.

I have used the CMakeLists.txt file I found in here as a starting point: https://gist.github.com/fracek/3323924?permalink_comment_id=4047507. Using this I successfully configure and generate, with the following output on the terminal:

$ cmake ..
-- The C compiler identification is GNU 11.4.0
-- The CXX compiler identification is GNU 11.4.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found PkgConfig: /usr/bin/pkg-config (found version "0.29.2")
-- Checking for module 'gtkmm-3.0'
--   Found gtkmm-3.0, version 3.24.5
-- Configuring done
-- Generating done
-- Build files have been written to: /home/fabio/Documents/GTK_experiment/build


but I fail to build, receiving the following error:

$ cmake --build .
[ 50%] Building CXX object CMakeFiles/hello.dir/main.cpp.o
In file included from /home/fabio/Documents/GTK_experiment/main.cpp:14:
/home/fabio/Documents/GTK_experiment/helloworld.h:17:10: fatal error: gtkmm/button.h: No such file or directory
   17 | #include <gtkmm/button.h>
      |          ^~~~~~~~~~~~~~~~
compilation terminated.
gmake[2]: *** [CMakeFiles/hello.dir/build.make:76: CMakeFiles/hello.dir/main.cpp.o] Error 1
gmake[1]: *** [CMakeFiles/Makefile2:83: CMakeFiles/hello.dir/all] Error 2
gmake: *** [Makefile:91: all] Error 2

Since the error is gtkmm/button.h: no such file or directory while I know for sure that the file is correctly installed, I understand that the problem is in the CMakeLists.txt file, failing to point the linker to the correct path to the library headers folder. So, here is again the question: how to correctly learn the path to the library, and check that CMakeLists.txt points to it?

I copy here the three files in my project:

main.cpp


#include "helloworld.h"
#include <iostream>

HelloWorld::HelloWorld()
: m_button("Hello World")   // creates a new button with label "Hello World".
{
  // Sets the border width of the window.
  set_border_width(10);

  // When the button receives the "clicked" signal, it will call the
  // on_button_clicked() method defined below.
  m_button.signal_clicked().connect(sigc::mem_fun(*this,
              &HelloWorld::on_button_clicked));

  // This packs the button into the Window (a container).
  add(m_button);

  // The final step is to display this newly created widget...
  m_button.show();
}

HelloWorld::~HelloWorld()
{
}

void HelloWorld::on_button_clicked()
{
  std::cout << "Hello World" << std::endl;
}
helloworld.h

#ifndef GTKMM_EXAMPLE_HELLOWORLD_H
#define GTKMM_EXAMPLE_HELLOWORLD_H

#include <gtkmm/button.h>
#include <gtkmm/window.h>

class HelloWorld : public Gtk::Window
{

public:
  HelloWorld();
  virtual ~HelloWorld();

protected:
  //Signal handlers:
  void on_button_clicked();

  //Member widgets:
  Gtk::Button m_button;
};

#endif
CMakeLists.txt
# Set the name and the supported language of the project
project(hello-world C CXX)

# Set the minimum version of cmake required to build this project
cmake_minimum_required(VERSION 3.10)

# Use the package PkgConfig to detect GTK+ headers/library files
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED gtkmm-3.0)


add_executable(hello main.cpp)
target_link_libraries(hello PRIVATE ${GTKMM_LIBRARIES})


# Add other flags to the compiler
target_compile_definitions(hello PRIVATE ${GTKMM_CFLAGS_OTHER})


# Setup CMake to use GTK+, tell the compiler where to look for headers
# and to the linker where to look for libraries
target_include_directories(hello PRIVATE ${GTKMM_INCLUDE_DIRS})
target_link_directories(hello PRIVATE ${GTKMM_LIBRARY_DIRS})

The CMakeLists.txt file I found online and I am using as a starter has several variables such as GTKMM_LIBRARIES, GTKMM_INCLUDE_DIRS and GTKMM_LIBRARY_DIRS, which I believe should be learned from steps 4, 5 and 6 above. But which one, and where to put them in the CMakeLists.txt file?

I know that in my system the library is in the path

/usr/include/gtkmm-3.0

and

/usr/lib/x86_64-linux-gnu/gtkmm-3.0

and indeed in the CMakeCache.txt file I find:

GTK_STATIC_INCLUDE_DIRS:INTERNAL=/usr/include/gtkmm-3.0;/usr/lib/x86_64-linux-gnu/gtkmm-3.0
GTK_INCLUDE_DIRS:INTERNAL=/usr/include/gtkmm-3.0;/usr/lib/x86_64-linux-gnu/gtkmm-3.0/include
GTK_STATIC_CFLAGS:INTERNAL=-pthread;-I/usr/include/gtkmm-3.0;-I/usr/lib/x86_64-linux-gnu/gtkmm-3.0/include

so I edited the CMakeLists.txt, and changed e.g.

GTKMM_INCLUDE_DIRS   ->   GTK_STATIC_INCLUDE_DIRS:INTERNAL
GTKMM_LIBRARY_DIRS   ->   GTK_INCLUDE_DIRS:INTERNAL

and similar combinaitons, but I still get the same error.


Solution

  • You're making things more complex than they need to be.
    The FindPkgConfig module supports imported targets for quite a while now, so you can just do:

    pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtkmm-3.0)
    target_link_libraries(hello PUBLIC PkgConfig::GTK)
    

    The imported target will take care of all header and linking flags.