跳转到内容

cmake编译安装wxWidgets并使用

  • 从 wxWidgets 的 git 仓库克隆最新代码
  • 用 cmake 编译安装 wxWidgets 静态库
  • 在个人项目中使用 wxWidgets,使用 config 文件和 find_package() 调用 wxWidgets 依赖
  • 将 wxWidgets 库文件链接到个人项目的 目标(target)

从 wxWidgets 的 git 仓库克隆最新代码

我将源码放在用户家目录顶层。

git clone --recurse-submodules https://ghproxy.net/https://github.com/wxWidgets/wxWidgets.git

参考:wxWidgets/README-GIT.md at master · wxWidgets/wxWidgets (github.com)

用 cmake 编译安装 wxWidgets 静态库

安装至~/wx_install,也就是家目录下的 wx_install 文件夹。

cd C:\wxWidgets\wxWidgets_debug_static
cmake C:\Users\Aoyu\wxWidgets -DCMAKE_INSTALL_PREFIX=~/wx_install -DwxBUILD_SHARED=OFF
cmake --build . --target install

参考:源码目录下wxWidgets/docs/doxygen/overviews/cmake.md

在个人项目中使用 wxWidgets

在 wxWidgets 的安装目录内,~/wx_install/lib/cmake/wxWidgets下有自动生成的配置文件wxWidgetsConfig.cmakewxWidgetsConfigVersion.cmake,用于帮助 CMake 能够正确地配置个人项目以使用 wxWidgets 库。以及在 ~/wx_install/lib/cmake/wxWidgets/vc_x64_lib 内有配置文件 wxWidgetsTargets.cmakewxWidgetsTargets-debug.cmake,作用问gpt。

用于测试的小项目来自 wxWidgets 自带示例 wxWidgets/samples/minimal.

minimal.cpp

minimal.cpp
/////////////////////////////////////////////////////////////////////////////
// Purpose: Minimal wxWidgets sample
// Author: Julian Smart
// Modified by:
// Created: 04/01/98
// Copyright: (c) Julian Smart
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// ============================================================================
// declarations
// ============================================================================
// ----------------------------------------------------------------------------
// headers
// ----------------------------------------------------------------------------
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers)
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
// ----------------------------------------------------------------------------
// resources
// ----------------------------------------------------------------------------
// the application icon (under Windows it is in resources and even
// though we could still include the XPM here it would be unused)
#ifndef wxHAS_IMAGES_IN_RESOURCES
#include "../sample.xpm"
#endif
// ----------------------------------------------------------------------------
// private classes
// ----------------------------------------------------------------------------
// Define a new application type, each program should derive a class from wxApp
class MyApp : public wxApp
{
public:
// override base class virtuals
// ----------------------------
// this one is called on application startup and is a good place for the app
// initialization (doing it here and not in the ctor allows to have an error
// return: if OnInit() returns false, the application terminates)
virtual bool OnInit() override;
};
// Define a new frame type: this is going to be our main frame
class MyFrame : public wxFrame
{
public:
// ctor(s)
MyFrame(const wxString& title);
// event handlers (these functions should _not_ be virtual)
void OnQuit(wxCommandEvent& event);
void OnAbout(wxCommandEvent& event);
private:
// any class wishing to process wxWidgets events must use this macro
wxDECLARE_EVENT_TABLE();
};
// ----------------------------------------------------------------------------
// constants
// ----------------------------------------------------------------------------
// IDs for the controls and the menu commands
enum
{
// menu items
Minimal_Quit = wxID_EXIT,
// it is important for the id corresponding to the "About" command to have
// this standard value as otherwise it won't be handled properly under Mac
// (where it is special and put into the "Apple" menu)
Minimal_About = wxID_ABOUT
};
// ----------------------------------------------------------------------------
// event tables and other macros for wxWidgets
// ----------------------------------------------------------------------------
// the event tables connect the wxWidgets events with the functions (event
// handlers) which process them. It can be also done at run-time, but for the
// simple menu events like this the static method is much simpler.
wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
EVT_MENU(Minimal_Quit, MyFrame::OnQuit)
EVT_MENU(Minimal_About, MyFrame::OnAbout)
wxEND_EVENT_TABLE()
// Create a new application object: this macro will allow wxWidgets to create
// the application object during program execution (it's better than using a
// static object for many reasons) and also implements the accessor function
// wxGetApp() which will return the reference of the right type (i.e. MyApp and
// not wxApp)
wxIMPLEMENT_APP(MyApp);
// ============================================================================
// implementation
// ============================================================================
// ----------------------------------------------------------------------------
// the application class
// ----------------------------------------------------------------------------
// 'Main program' equivalent: the program execution "starts" here
bool MyApp::OnInit()
{
// call the base class initialization method, currently it only parses a
// few common command-line options but it could be do more in the future
if ( !wxApp::OnInit() )
return false;
// create the main application window
MyFrame *frame = new MyFrame("Minimal wxWidgets App");
// and show it (the frames, unlike simple controls, are not shown when
// created initially)
frame->Show(true);
// success: wxApp::OnRun() will be called which will enter the main message
// loop and the application will run. If we returned false here, the
// application would exit immediately.
return true;
}
// ----------------------------------------------------------------------------
// main frame
// ----------------------------------------------------------------------------
// frame constructor
MyFrame::MyFrame(const wxString& title)
: wxFrame(nullptr, wxID_ANY, title)
{
// set the frame icon
SetIcon(wxICON(sample));
#if wxUSE_MENUBAR
// create a menu bar
wxMenu *fileMenu = new wxMenu;
// the "About" item should be in the help menu
wxMenu *helpMenu = new wxMenu;
helpMenu->Append(Minimal_About, "&About\tF1", "Show about dialog");
fileMenu->Append(Minimal_Quit, "E&xit\tAlt-X", "Quit this program");
// now append the freshly created menu to the menu bar...
wxMenuBar *menuBar = new wxMenuBar();
menuBar->Append(fileMenu, "&File");
menuBar->Append(helpMenu, "&Help");
// ... and attach this menu bar to the frame
SetMenuBar(menuBar);
#else // !wxUSE_MENUBAR
// If menus are not available add a button to access the about box
wxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
wxButton* aboutBtn = new wxButton(this, wxID_ANY, "About...");
aboutBtn->Bind(wxEVT_BUTTON, &MyFrame::OnAbout, this);
sizer->Add(aboutBtn, wxSizerFlags().Center());
SetSizer(sizer);
#endif // wxUSE_MENUBAR/!wxUSE_MENUBAR
#if wxUSE_STATUSBAR
// create a status bar just for fun (by default with 1 pane only)
CreateStatusBar(2);
SetStatusText("Welcome to wxWidgets!");
#endif // wxUSE_STATUSBAR
}
// event handlers
void MyFrame::OnQuit(wxCommandEvent& WXUNUSED(event))
{
// true is to force the frame to close
Close(true);
}
void MyFrame::OnAbout(wxCommandEvent& WXUNUSED(event))
{
wxMessageBox(wxString::Format
(
"Welcome to %s!\n"
"\n"
"This is the minimal wxWidgets sample\n"
"running under %s.",
wxVERSION_STRING,
wxGetOsDescription()
),
"About wxWidgets minimal sample",
wxOK | wxICON_INFORMATION,
this);
}

编译项目需要文件CMakeLists.txt

导入包 wxWidgets

我认为这类似于 python 代码的 import 语句。让编译器能找到 wxWidgets 的库代码。

要让项目包含 wxWidgets 的代码需使用 find_package() 函数,像这样:

# Request the required wxWidgets libs
find_package(wxWidgets 3.3 COMPONENTS core base REQUIRED CONFIG)

这里指定了 wxWidgets 名称、版本号,以及各个组件的名称,以及指定了 CONFIG,表示 CMake 将使用配置模式(Config mode),也就是 CMake 会尝试调用 wxWidgetsConfig.cmake 文件,来配置 wxWidgets 库。

但是只有上面那一句还不够,因为 CMake 不知道要去哪里寻找wxWidgetsConfig.cmake。所以,在 CMakeLists.txt 文件中,应该将下面一句代码放在 find_package() 前面:

set(CMAKE_PREFIX_PATH "C:/Users/Aoyu/wx_install" ${CMAKE_PREFIX_PATH})

有关于此的更详细解释参见:使用依赖项指南 Using Dependencies Guide — CMake 3.29.0-rc3 Documentation

Packages will not be found automatically without help if they are in locations not known to CMake, such as /opt/mylib or $HOME/dev/prefix. This is a normal situation, and CMake provides several ways for users to specify where to find such libraries.
如果包位于 CMake 未知的位置(例如 /opt/mylib 或 $HOME/dev/prefix ),则在没有帮助的情况下不会自动找到包。这是正常情况,CMake 提供了多种方法供用户指定在哪里找到此类库。

The CMAKE_PREFIX_PATH variable may be set when invoking CMake. It is treated as a list of base paths in which to search for config files. A package installed in /opt/somepackage will typically install config files such as /opt/somepackage/lib/cmake/somePackage/SomePackageConfig.cmake. In that case, /opt/somepackage should be added to CMAKE_PREFIX_PATH.
调用 CMake 时可以设置 CMAKE_PREFIX_PATH 变量。它被视为在其中搜索配置文件的基本路径列表。安装在 /opt/somepackage 中的包通常会安装配置文件,例如 /opt/somepackage/lib/cmake/somePackage/SomePackageConfig.cmake 。在这种情况下,应将 /opt/somepackage 添加到 CMAKE_PREFIX_PATH 中。

The environment variable CMAKE_PREFIX_PATH may also be populated with prefixes to search for packages. Like the PATH environment variable, this is a list, but it needs to use the platform-specific environment variable list item separator (: on Unix and ; on Windows).
环境变量 CMAKE_PREFIX_PATH 也可以填充前缀以搜索包。与 PATH 环境变量一样,这是一个列表,但它需要使用特定于平台的环境变量列表项分隔符(Unix 上使用: 以及 Windows 上使用 ; )。

The CMAKE_PREFIX_PATH variable provides convenience in cases where multiple prefixes need to be specified, or when multiple packages are available under the same prefix. Paths to packages may also be specified by setting variables matching <PackageName>_DIR, such as SomePackage_DIR. Note that this is not a prefix, but should be a full path to a directory containing a config-style package file, such as /opt/somepackage/lib/cmake/SomePackage in the above example. See the find_package() documentation for other CMake variables and environment variables that can affect the search.
在需要指定多个前缀或同一前缀下有多个包时, CMAKE_PREFIX_PATH 变量提供了便利。包的路径也可以通过设置与 <PackageName>_DIR 匹配的变量来指定,例如 SomePackage_DIR 。请注意,这不是前缀,而应该是包含配置样式包文件的目录的完整路径,例如上例中的 /opt/somepackage/lib/cmake/SomePackage 。有关可能影响搜索的其他 CMake 变量和环境变量,请参阅 find_package() 文档。

将 wxWidgets 库文件链接到 目标(target)

现在,编译器能够找到 wxWidgets 的库代码了,接下来就是使用 wxWidgets 库代码了。需要让项目的目标(target,可能是可执行文件,也可能是库,前者更有可能)和 wxWidgets 库文件链接在一起。如下:

# Link executable to the wxWidgets target
target_link_libraries(${PROJECT_NAME} wxWidgets::wxWidgets)

全 CMakeLists.txt

关键点就是上面的几个语句,但一个完整的 CMakeLists.txt 需要考虑的地方更多。如软件图标在各个平台上的差异,以及其他跨平台产生的问题。一个完整的 CMakeLists.txt 如下:

# Declare the minimum required CMake version
cmake_minimum_required(VERSION 3.5)
if(APPLE AND NOT CMAKE_OSX_DEPLOYMENT_TARGET)
# If no deployment target has been set default to the minimum supported
# OS version (this has to be set before the first project() call)
if(CMAKE_SYSTEM_NAME STREQUAL "iOS")
set(CMAKE_OSX_DEPLOYMENT_TARGET 12.0 CACHE STRING "iOS Deployment Target")
else()
set(CMAKE_OSX_DEPLOYMENT_TARGET 10.10 CACHE STRING "macOS Deployment Target")
endif()
endif()
# Name the project
project(minimal)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_PREFIX_PATH "C:/Users/Aoyu/wx_install" ${CMAKE_PREFIX_PATH})
# Request the required wxWidgets libs
find_package(wxWidgets 3.3 COMPONENTS core base REQUIRED CONFIG)
# Define a variable containing a list of source files for the project
set(SRC_FILES
minimal.cpp
)
if(WIN32)
# Include a RC file for windows
list(APPEND SRC_FILES src/sample.rc)
elseif(APPLE)
# Add an icon for the apple .app file
list(APPEND SRC_FILES src/osx/carbon/wxmac.icns)
endif()
# Define the build target for the executable
add_executable(${PROJECT_NAME} WIN32 MACOSX_BUNDLE ${SRC_FILES})
# Link executable to the wxWidgets target
target_link_libraries(${PROJECT_NAME} wxWidgets::wxWidgets)
if(MSVC)
# Use manifest from wx.rc
set_target_properties(${PROJECT_NAME} PROPERTIES
COMPILE_FLAGS "/DwxUSE_RC_MANIFEST"
LINK_FLAGS "/MANIFEST:NO"
)
elseif(APPLE)
set_target_properties(${PROJECT_NAME} PROPERTIES
RESOURCE "src/osx/carbon/wxmac.icns"
MACOSX_BUNDLE_ICON_FILE wxmac.icns
MACOSX_BUNDLE_COPYRIGHT "Copyright wxWidgets"
MACOSX_BUNDLE_GUI_IDENTIFIER "org.wxwidgets.minimal"
)
endif()

上述文件基本来自 wxWidgets 自带示例 wxWidgets/samples/minimal

编译

cmake -B build
cmake --build build

编译 wxWidgets 最小示例

上面的代码基本都来自 wxWidgets 自带示例 wxWidgets/samples/minimal。如果想不加修改地编译原版的minimal(可能是想要比较原版和个人项目),可以使用下面的命令:

cd C:\wxWidgets\minimal
# C:\Users\Aoyu\wxWidgets 是 wxWidgets 源码所在文件夹
cmake C:\Users\Aoyu\wxWidgets -DwxBUILD_SAMPLES=SOME
cmake --build . --target minimal

来自:源码目录下wxWidgets/docs/doxygen/overviews/cmake.md

END

以上。