我在编写 C/C++ 代码方面有一些旧的经验,但现在我想学习如何在现代环境中构建应用程序,并链接一些系统库。
我仅使用终端和 CMake(即不使用 IDE)构建代码。 我在 Linux Mint,但我认为这个问题的主要答案适用于任何系统。
就我而言,我想要的库是 GTK,有一个 GUI,我正在开发一个简单的“hello world”应用程序示例,可以在此处找到:
https://gnome.pages.gitlab.gnome.org/gtkmm-documentation/sec-helloworld.html.
但是我在写这个问题时考虑的是一般情况。
所以,我的问题是:从我意识到代码“包含”某个库中的某些头文件开始,要遵循哪些步骤,继续安装所述库(的“dev”包),可能包括检查给定库(标头)的正确路径(以检查 pkg-config 是否完成其工作),最后是编写 CMakeLists.txt 的正确方法,以便可以正确“配置”代码和“建造”?
到目前为止我所遵循的步骤如下。
使用
main.cpp
和 helloworld.h
文件创建项目文件夹。
注意头文件中有
#include <gtkmm/button.h>
行,需要GTK库。
确保系统中存在所需库的“dev”包(及其头文件),如果需要,请使用系统包管理器安装它。记下安装位置。
编写 CMakeLists.txt 文件,包括
find_package(PkgConfig REQUIRED)
行以使用 pkg-config,然后 pkg_check_modules(GTK REQUIRED gtkmm-3.0)
读取有关库路径和依赖项的信息。
运行
cmake ..
在项目的build文件夹中配置并生成CMake文件
检查 CMakeCache.txt 以检查是否已从 pkg-config 读取到库的正确路径,并可能修改 CMakeLists.txt 文件。
使用“cmake - - build”构建可执行文件。在 CMake 中。
我使用了在这里找到的 CMakeLists.txt 文件作为起点: https://gist.github.com/fracek/3323924?permalink_comment_id=4047507。 使用它,我成功配置和生成,终端上有以下输出:
$ 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
但是我无法构建,收到以下错误:
$ 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
由于错误是
gtkmm/button.h: no such file or directory
,虽然我确信该文件已正确安装,但我知道问题出在 CMakeLists.txt 文件中,未能将链接器指向库头文件夹的正确路径。
那么,问题又来了:如何正确学习库的路径,并检查 CMakeLists.txt 是否指向它?
我将项目中的三个文件复制到此处:
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})
我在网上找到并用作初学者的 CMakeLists.txt 文件有几个变量,例如
GTKMM_LIBRARIES
、GTKMM_INCLUDE_DIRS
和 GTKMM_LIBRARY_DIRS
,我认为应该从上面的步骤 4、5 和 6 中学习。
但是是哪一个,以及将它们放在 CMakeLists.txt 文件中的什么位置?
我知道在我的系统中,库位于路径中
/usr/include/gtkmm-3.0
和
/usr/lib/x86_64-linux-gnu/gtkmm-3.0
确实在
CMakeCache.txt
文件中我发现:
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
所以我编辑了
CMakeLists.txt
,并更改了例如
GTKMM_INCLUDE_DIRS -> GTK_STATIC_INCLUDE_DIRS:INTERNAL
GTKMM_LIBRARY_DIRS -> GTK_INCLUDE_DIRS:INTERNAL
和类似的组合,但我仍然遇到相同的错误。
你让事情变得比他们需要的更复杂。
FindPkgConfig
模块现在已经支持导入目标很长一段时间了,所以你可以这样做:
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtkmm-3.0)
target_link_libraries(hello PUBLIC PkgConfig::GTK)
导入的目标将处理所有标头和链接标志。