Search code examples
c++cmakegtkmm

How to create a CMakeLists.txt for a gtkmm application?


My Application tree is as follows:

├── build
├── CMakeLists.txt
├── example
│   ├── applicationwindow.cpp
│   ├── applicationwindow.h
│   └── Application.glade
└── main.cpp

2 directories, 5 files

And the CMakeLists.txt file I created is as follows:

cmake_minimum_required(VERSION 3.1.0)
project(Example_App)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")

find_package(PkgConfig)
pkg_check_modules(GTKMM gtkmm-3.0)

include_directories(${GTKMM_INCLUDE_DIRS})
link_directories(${GTKMM_LIBRARY_DIRS})

set(SOURCE_FILES main.cpp)
add_executable(${CMAKE_PROJECT_NAME} ${SOURCE_FILES} example/applicationwindow.cpp)
target_link_libraries(${CMAKE_PROJECT_NAME} ${GTKMM_LIBRARIES})

After a successful cmake .. and make when I execute the application I get the following error:

terminate called after throwing an instance of 'Glib::FileError'
Aborted (core dumped)

I have tried building this with a Makefile but that doesn't seem to give any issues. I have also tried to look into Glib::FileError however, I am unable to extract the exact cause of the problem. On trying the similar CMakeLists on a simpler instance like this example as denoted here: Gtkmm Example of a Application Window

It however seems to work. What might be the exact problem here?


Solution

  • Since you seem to be new to CMake, I will add some code around the answer provided by mohamadp91.

    Your problem is that when you are running cmake .., the Application.glade file is never copied into the build directory, when you run make and execute your software. At this point, the application tries to load a build/example/Application.glade file, but does not find any and you get your error.

    This does not happen when using plain Makefiles because you do not build you directory in build, but directly alongside the example directory. The application can then find the Glade file.

    I made a small example to reproduce your problem. It has the same directory structure:

    ├── build
    ├── CMakeLists.txt
    ├── example
    │   ├── CMakeLists.txt
    │   └── Application.glade
    └── main.cpp
    

    It only has a main.cpp file, which tries to load a ressource (Glade file in this case, like you), under example. Here is the C++ code:

    #include <iostream>
    #include <gtkmm.h>
    
    constexpr int FILE_ERROR    = 1;
    constexpr int MARKUP_ERROR  = 2;
    constexpr int BUILDER_ERROR = 3;
    
    Gtk::ApplicationWindow* window = nullptr;
    
    int main(int argc, char **argv)
    {
        auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example");
        auto builder = Gtk::Builder::create();
    
        try
        {
            // Try to use Application.glade:
            builder->add_from_file("example/Application.glade");
        }
        catch(const Glib::FileError& ex)
        {
          // This was your case, because the Application.glade file could
          // not be located:
          std::cout << "File error :" << ex.what() << std::endl;
          return FILE_ERROR;
        }
        catch(const Glib::MarkupError& ex)
        {
          std::cout << "Markup error :" << ex.what() << std::endl;
          return MARKUP_ERROR;
        }
        catch(const Gtk::BuilderError& ex)
        {
          std::cout << "Builder error :" << ex.what() << std::endl;
          return BUILDER_ERROR;
        }
    
        //Get the GtkBuilder-instantiated Dialog:
        builder->get_widget("MainWindow", window);
    
        if(window)
        {
            app->run(*window);
        }
    
        delete window;
    
        return 0;
    }
    

    The example shows how you could catch the Glib::FileError exception and avoid a segfault. The Glade file is simple and looks like this:

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- Generated with glade 3.22.2 -->
    <interface>
      <requires lib="gtk+" version="3.20"/>
      <object class="GtkApplicationWindow" id="MainWindow">
        <property name="can_focus">False</property>
        <child type="titlebar">
          <placeholder/>
        </child>
        <child>
          <object class="GtkButton" id="MainButton">
            <property name="label" translatable="yes">Hello World</property>
            <property name="visible">True</property>
            <property name="can_focus">True</property>
            <property name="receives_default">True</property>
          </object>
        </child>
      </object>
    </interface>
    

    In the top level directory, I use the add_subdirectory command to tell CMake to go inside the example subdirectory and process the CMakeLists.txt file located there:

    cmake_minimum_required(VERSION 3.1.0)
    project(Example_App)
    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
    
    find_package(PkgConfig)
    pkg_check_modules(GTKMM gtkmm-3.0)
    
    include_directories(${GTKMM_INCLUDE_DIRS})
    link_directories(${GTKMM_LIBRARY_DIRS})
    
    set(SOURCE_FILES main.cpp)
    add_executable(${CMAKE_PROJECT_NAME} ${SOURCE_FILES} main.cpp)
    target_link_libraries(${CMAKE_PROJECT_NAME} ${GTKMM_LIBRARIES})
    
    # CMake is gonna read the 'CMakeLists.txt' file located under 'example':
    add_subdirectory(example)
    

    In in that file, I simply copy the Application.glade file under example in the build directory:

    file(COPY Application.glade DESTINATION ${CMAKE_CURRENT_BINARY_DIR})