The advantage of static compilation is that you don’t need to include a bunch of dynamic link libraries when publishing. In theory, the Msys2 toolchain supports static compilation, but in practice, even if you directly add the -static flag, the generated executable still reports missing dynamic link libraries at runtime, especially the three libraries required by the Mingw toolchain: libwinpthread-1.dll, libstdc++-6.dll, and libgcc_s_seh-1.dll. For the clang compiler, the runtime dependency becomes libc++.dll. This article explores a simple method to facilitate static link compilation management for different compilers.

1. Static Compiling of FLTK Project

Here, a simple window application is implemented using FLTK. The FLTK license supports the release of statically linked programs, and the generated program is very small in size, making it very convenient for writing and distributing small tools.

// --- 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();
}

The FLTK installed via msys2 provides the fltk-config tool for configuring and compiling FLTK project source code. Using this tool, you can easily compile the C++ source files of an FLTK project into an executable.

fltk-config --compile main.cpp --link -static

--link -static means linking a static library, and the runtime effect is shown in the figure below.

5f13ec9d82726c88e3d8f6fbc4cf90c6.png

2. FLTK Project Managed by CMake

Although the fltk-config tool is convenient, it only supports the bash shell and can only be used in msys2 and Linux terminals. If there is a need for cross-platform compilation, or if the project dependencies are slightly more complex, fltk-config is clearly not enough. It is recommended to use CMake to manage FLTK projects, with the CMake configuration file CMakeLists.txt written as follows.

# --- 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
)

Here, a distinction is made for the compiler. For gcc, the three major static libraries -static gcc stdc++ winpthread are used for linking1, while for clang, the static library libc++ is linked using -static c++.

In msys2, CMake defaults to linking the dynamic version of the FLTK library, so you need to define how to link to the static library yourself. Here, this is handled by linking through a custom script.

# --- 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-managed wxWidgets project

wxWidgets also provides its own tool wx-config for handling compilation and build issues. Like fltk-config, it only supports the bash terminal, so it is still recommended to use CMake to manage wxWidgets projects. Here, we’ll use the official sample code2 for demonstration, and the CMake configuration file CMakeLists.txt is written as follows.

# --- 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 specifies whether to link to the static library by setting the wxWidgets_USE_STATIC switch, but currently it only works with wxWidgets libraries installed via msys2, or those you compile yourself.

4. Others

Other UI libraries, such as GTK, do not support static compilation, and Qt is not recommended for static compilation due to licensing issues. Other programs can also follow the above method to use CMake to compile and release a statically linked version.