静态编译的好处是发布的时候不用带上一堆动态链接库。Msys2工具链理论上是支持静态编译的,但实际使用的时候即使直接加上-static开关,链接生成的可执行文件最后运行时还是会提示缺失动态链接库,尤其是Mingw工具链依赖的libwinpthread-1.dll、libstdc++-6.dll和libgcc_s_seh-1.dll这"三大件"。而对于clang编译器,运行时依赖又变成了libc++.dll。本文探讨一种简便的办法,方便针对不同编译器的静态链接编译管理。
1. FLTK项目静态编译
这里用FLTK实现一个简单的窗口应用,FLTK的LICENSE支持发布静态链接版本的程序,而且生成的程序体积非常小,用来编写和发布小工具十分方便。
// --- mian.cpp ---
#include <FL/Fl.H>
#include <FL/Fl_Window.H>
#include <FL/Fl_Button.H>
#include <FL/fl_ask.H>
class SimpleWindow : public Fl_Window
{
private:
Fl_Button *button;
public:
SimpleWindow(int w, int h, const char *title) : Fl_Window(w, h, title)
{
color(FL_WHITE);
button = new Fl_Button(w / 2 - 50, h / 2 - 20, 100, 40, "Click Me!");
button->color(FL_BLUE);
button->labelcolor(FL_WHITE);
button->callback([](Fl_Widget *w, void *data)
{
fl_message_title("Click Me!");
fl_message("You clicked the button!"); }, NULL);
end();
}
};
int main(int argc, char **argv)
{
SimpleWindow window(400, 300, "FLTK Simple Window");
window.show(argc, argv);
return Fl::run();
}
msys2安装的FLTK提供了fltk-config工具用于配置和编译FLTK项目源码,使用该工具可以很方便地将FLTK项目的cpp源码编译成可执行文件。
fltk-config --compile main.cpp --link -static
--link -static表示链接静态库,运行效果如下图所示。
2. CMake管理的FLTK项目
fltk-config工具虽然好用,但是只支持bash,只能在msys2和Linux终端中使用。如果有跨平台编译需求,或者项目依赖稍微复杂一点,fltk-config明显就不太够用了。推荐使用CMake管理FLTK项目,CMake配置文件CMakeLists.txt编写如下。
# --- CMakeLists.txt ---
cmake_minimum_required(VERSION 3.21)
set(PROJECT_NAME fltk_sample)
project(${PROJECT_NAME} LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(NOT "$ENV{VCPKG_TARGET_TRIPLET}" STREQUAL "")
set(VCPKG_TARGET_TRIPLET $ENV{VCPKG_TARGET_TRIPLET})
message(STATUS "VCPKG_TARGET_TRIPLET: ${VCPKG_TARGET_TRIPLET}")
endif()
# find fltk
find_package(FLTK REQUIRED)
if(FLTK_FOUND)
message(STATUS "Found FLTK: ${FLTK_VERSION}")
message(STATUS "FLTK_INCLUDE_DIR: ${FLTK_INCLUDE_DIR}")
include_directories(${FLTK_INCLUDE_DIR})
message(STATUS "FLTK_LIBRARIES: ${FLTK_LIBRARIES}")
else()
message(FATAL_ERROR "FLTK not found")
endif()
add_executable(${PROJECT_NAME} main.cpp)
if(MSVC)
target_link_options(${PROJECT_NAME} PRIVATE "/SUBSYSTEM:WINDOWS" "/ENTRY:mainCRTStartup")
message(STATUS "Set msvc link flags: /SUBSYSTEM:WINDOWS /ENTRY:mainCRTStartup")
if(${VCPKG_TARGET_TRIPLET} MATCHES "static")
set_property(TARGET ${PROJECT_NAME} PROPERTY
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
message(STATUS "Set msvc runtime library: MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()
target_link_libraries(${PROJECT_NAME} ${FLTK_LIBRARIES})
elseif(MINGW)
target_link_options(${PROJECT_NAME} PRIVATE "-mwindows")
message(STATUS "Set mingw link flags: -mwindows")
include(${CMAKE_CURRENT_SOURCE_DIR}/static.cmake)
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
message(STATUS "Using clang, add -static c++")
target_link_libraries(${PROJECT_NAME}
-static c++ ${FLTK_LIBRARIES})
else()
message(STATUS "Using gcc, add -static gcc stdc++ winpthread")
target_link_libraries(${PROJECT_NAME}
-static gcc stdc++ winpthread ${FLTK_LIBRARIES})
endif()
endif()
# set installation directories
install(TARGETS ${PROJECT_NAME}
BUNDLE DESTINATION .
RUNTIME DESTINATION bin
)
这里对编译器进行了区分,针对gcc用到了-static gcc stdc++ winpthread链接三大件的静态库1,针对clang使用-static c++链接libc++的静态库。
CMake在msys2中默认链接的FLTK库的是动态库,需要自己定义如何链接到静态库。这里通过链接自定义脚本来处理。
# --- static.cmake ---
if(NOT MINGW)
message(FATAL_ERROR "This script is only for static linking with MinGW")
elseif(NOT FLTK_LIBRARIES)
message(WARNING "FLTK_LIBRARIES is empty")
else()
string(REPLACE ".dll.a" ".a" FLTK_LIBRARIES "${FLTK_LIBRARIES}")
message(STATUS "Static FLTK_LIBRARIES: ${FLTK_LIBRARIES}")
endif()
3. CMake管理的wxWidgets项目
wxWidgets也提供的自己的工具wx-config用来处理编译构建问题,和fltk-config一样只支持bash终端,还是推荐用CMake来管理wxWidgets项目。这里还是用官方的示例代码2来做演示,CMake配置文件CMakeLists.txt编写如下。
# --- CMakeLists.txt ---
cmake_minimum_required(VERSION 3.21)
set(PROJECT_NAME wx_sample)
project(${PROJECT_NAME} LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if(NOT "$ENV{VCPKG_TARGET_TRIPLET}" STREQUAL "")
set(VCPKG_TARGET_TRIPLET $ENV{VCPKG_TARGET_TRIPLET})
message(STATUS "VCPKG_TARGET_TRIPLET: ${VCPKG_TARGET_TRIPLET}")
else()
set(wxWidgets_USE_STATIC ON)
message(STATUS "Set wxWidgets_USE_STATIC: ON")
endif()
# find wxWidgets
find_package(wxWidgets REQUIRED COMPONENTS core base)
if(wxWidgets_USE_FILE) # not defined in CONFIG mode
include(${wxWidgets_USE_FILE})
endif()
message(STATUS "wxWidgets_LIBRARIES: ${wxWidgets_LIBRARIES}")
add_executable(${PROJECT_NAME} main.cpp)
if(MSVC)
set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS "/SUBSYSTEM:WINDOWS")
message(STATUS "Set msvc link flags: /SUBSYSTEM:WINDOWS")
if(${VCPKG_TARGET_TRIPLET} MATCHES "static")
set_property(TARGET ${PROJECT_NAME} PROPERTY
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
message(STATUS "Set msvc runtime library: MultiThreaded$<$<CONFIG:Debug>:Debug>")
endif()
elseif(MINGW)
target_link_options(${PROJECT_NAME} PRIVATE "-mwindows")
message(STATUS "Set mingw link flags: -mwindows")
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
message(STATUS "Using clang, add -static c++")
set(MINGW_LINK_LIBS -static c++)
else()
message(STATUS "Using gcc, add -static gcc stdc++ winpthread")
set(MINGW_LINK_LIBS -static gcc stdc++ winpthread)
endif()
endif()
target_link_libraries(${PROJECT_NAME} ${MINGW_LINK_LIBS} ${wxWidgets_LIBRARIES})
# set installation directories
install(TARGETS ${PROJECT_NAME}
BUNDLE DESTINATION .
RUNTIME DESTINATION bin
)
wxWidgets通过设置wxWidgets_USE_STATIC开关来指定是否链接到静态库,不过目前只有msys2安装的wxWidgets库有效,或者自己编译
wxWidgets库。
4. 其他
其他ui库,像gtk本身不支持静态编译,Qt则因为许可证问题不推荐静态编译。其他程序也可以参照上面的办法使用CMake编译发布静态链接版本。