Add support for static linking with CMake
I saw this line in the cpp example:
# On Windows, copy the Slint DLL next to the application binary so that it's found.
Isn't it possible to link to the library statically so that I can make it to a single binary for the convenience of distribution?
At the moment, static linking is not supported for C++ builds. I think that this would be a desirable feature of the build system to support in the future. It's a little tricky because cargo can't tell us the names/paths of all the dependencies when creating a static library - transitive dependencies. But we could work around this by trying to solve this by hand on the CMake side, i.e. manually keeping track.
I'm actually a bit confused about why it's not so easy to implement. I'm assuming that you are using cabi to do the ffi, thus, making it into a .lib file shouldn't be hard
wdym by dependencies?
Yes, creating the .a file is easy, cargo does that for us. But there are system libraries that libslint_cpp.so links against, say libfreetype.so.2 when using Skia. When creating libslint_cpp.so, cargo encodes the need for libfreetype.so.2 into libslint_cpp.so and an application linking against libslint_cpp.so is good and doesn't need to know about libfreetype.so.2.
However, static libraries that cargo creates are just archives (.a), they don't provide a way to encode system library dependencies. So there needs to be another way of conveying to the build system of the application that includes libslint_cpp.a to also link against libfreetype.so.2 at application link time.
CMake itself can solve that when it creates the static library, it calls ar to create the archive and encodes the dependencies needed in the generated cmake targets. Libtool archives are another (older way) of solving this. Cargo however doesn't provide a mechanism of finding out what the external library dependencies are of any dependent crates when creating a staticlib.
got that. In the CI, we can compile the dll first. Then, read IAT of the dll (or whatever thing like that in executables other than PE formats) to get the system dlls it used.
it's weird, though.
Interesting idea to compile twice. CMake even has a function for it : https://cmake.org/cmake/help/latest/command/file.html#get-runtime-dependencies
wow, I haven't heard that until now then, with this and LIBRARIES, we should achieve that easily. I'll try it now
No, this is not for build time, it's for only install stage
damn, I eventually got it working!
Also found that here's a --print=native-static-libs arg in rustc, this can provide essential information for us.
CMakeLists.txt for that:
cmake_minimum_required(VERSION 3.28)
project(test_slint_static_link)
set(CMAKE_CXX_STANDARD 23)
set(BUILD_SHARED_LIBS OFF)
add_subdirectory(../slint slint)
add_executable(test_slint_static_link main.cpp)
set(CMAKE_VERBOSE_MAKEFILE ON)
set_property(TARGET test_slint_static_link PROPERTY
MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
set_property(TARGET test_slint_static_link PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
slint_target_sources(test_slint_static_link ui.slint)
target_link_directories(test_slint_static_link PUBLIC ./slint)
target_link_libraries(test_slint_static_link PUBLIC Ws2_32 wsock32 opengl32 imm32 dwmapi UxTheme Uiautomationcore slint_cpp2 Slint::Slint)
The slint_cpp.lib is renamed to slint_cpp2.lib (to avoid being shadowed by the slint_cpp target) and placed in ./slint
Also a small patch is applied to build.ninja:
Remove all
\\(\S+):msvcrt
idk why but as I linked to Slint::Slint, the part is added to the link target as a "library" by something evil, causing the error ninja: error: '/defaultlib:msvcrt', needed by 'test_slint_static_link.exe', missing and no known rule to make it to appear. I have no choice but to remove it manually.
btw, even with LTO enabled and relwithdebuginfo profile, the binary size of a simple helloworld goes to ~10MiB. Can this be reduced somehow?
As per https://github.com/slint-ui/slint/discussions/3376#discussioncomment-6860995 at least with Rust and static linking I see sizes around 2.6-3-6MiB for a hello world. I'm not sure why you see a ~10MiB size.
Also found that here's a --print=native-static-libs arg in rustc, this can provide essential information for us.
I don't know of a way to invoke this and get all transitive library dependencies for a crate :(
I support it. Static linking is very necessary.
I tried to poke around with it myself. Everything started with "BUILD_SHARED_LIBS=false", but there is a problem. The linker really does remove some symbols, marking them as "unused". So I added "/OPT:NOREF" and everything worked, but the binary size leaves much to be desired.