feat: Portable, fully-static release executables on Linux (#1351)
This adds the option FULLY_STATIC to create fully-static executables. To create portable, fully-static release executables on Linux, we need to use musl instead of glibc. Static executables from glibc are not portable. The popular musl-gcc wrapper does not support C++, so instead we use a full musl cross-compiler toolchain in the build workflow. To build FULLY_STATIC, the user must point to the appropriate cross-compiler, as we do in the workflow. On systems where musl is the native libc (such as Alpine Linux), this is not necessary. I have also read that musl's allocator is not very fast in multi-threaded applications. So when FULLY_STATIC is enabled, we will also enable mimalloc, a replacement allocator that is very fast. I tested a very basic packaging command to compare speeds of dynamic glibc, static musl, and static musl+mimalloc: dynamic glibc: runs: 2.527, 2.798, 2.703, 2.756, 2.959 avg = 2.749, std dev = 0.156s static musl: runs: 2.813, 2.920, 3.129, 3.003, 2.738 avg = 2.921s, std dev = 0.154s static musl+mimalloc: runs: 2.291, 2.034, 2.415, 2.303, 2.265 avg = 2.262s, std dev = 0.140s The mimalloc build is 82% faster than musl default allocator, 77% faster than glibc, and has more consistent runtime characteristics (lower standard deviation).
This commit is contained in:
parent
615720e7dd
commit
9be7c2b1ac
|
@ -108,7 +108,7 @@ jobs:
|
||||||
kitware_key_path="/usr/share/keyrings/kitware-archive-keyring.gpg"
|
kitware_key_path="/usr/share/keyrings/kitware-archive-keyring.gpg"
|
||||||
kitware_sources_path="/etc/apt/sources.list.d/kitware.list"
|
kitware_sources_path="/etc/apt/sources.list.d/kitware.list"
|
||||||
|
|
||||||
wget -O - "$kitware_key_url" 2>/dev/null | gpg --dearmor - \
|
curl -sL "$kitware_key_url" | gpg --dearmor - \
|
||||||
| sudo tee "$kitware_key_path" >/dev/null
|
| sudo tee "$kitware_key_path" >/dev/null
|
||||||
|
|
||||||
. /etc/lsb-release # Defines $DISTRIB_CODENAME (jammy, focal, etc)
|
. /etc/lsb-release # Defines $DISTRIB_CODENAME (jammy, focal, etc)
|
||||||
|
@ -149,9 +149,32 @@ jobs:
|
||||||
export PACKAGER_LOW_MEMORY_BUILD=yes
|
export PACKAGER_LOW_MEMORY_BUILD=yes
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Do fully static release builds on Linux.
|
||||||
|
BUILD_CONFIG="${{ matrix.build_type }}-${{ matrix.lib_type }}"
|
||||||
|
if [[ "${{ runner.os }}" == "Linux" && \
|
||||||
|
"$BUILD_CONFIG" == "Release-static" ]]; then
|
||||||
|
# Enable build settings for fully-static.
|
||||||
|
FULLY_STATIC="ON"
|
||||||
|
|
||||||
|
# Use a musl toolchain, since glibc static executables are not
|
||||||
|
# portable.
|
||||||
|
if [[ "${{matrix.target_arch}}" == "arm64" ]]; then
|
||||||
|
MUSL_ARCH="aarch64"
|
||||||
|
else
|
||||||
|
MUSL_ARCH="x86_64"
|
||||||
|
fi
|
||||||
|
curl -LO https://musl.cc/"$MUSL_ARCH"-linux-musl-native.tgz
|
||||||
|
tar xf "$MUSL_ARCH"-linux-musl-native.tgz
|
||||||
|
export CC=`pwd`/"$MUSL_ARCH"-linux-musl-native/bin/"$MUSL_ARCH"-linux-musl-gcc
|
||||||
|
export CXX=`pwd`/"$MUSL_ARCH"-linux-musl-native/bin/"$MUSL_ARCH"-linux-musl-g++
|
||||||
|
else
|
||||||
|
FULLY_STATIC="OFF"
|
||||||
|
fi
|
||||||
|
|
||||||
cmake \
|
cmake \
|
||||||
-DCMAKE_BUILD_TYPE="${{ matrix.build_type }}" \
|
-DCMAKE_BUILD_TYPE="${{ matrix.build_type }}" \
|
||||||
-DBUILD_SHARED_LIBS="$BUILD_SHARED_LIBS" \
|
-DBUILD_SHARED_LIBS="$BUILD_SHARED_LIBS" \
|
||||||
|
-DFULLY_STATIC="$FULLY_STATIC" \
|
||||||
-S . \
|
-S . \
|
||||||
-B build/
|
-B build/
|
||||||
|
|
||||||
|
@ -179,7 +202,21 @@ jobs:
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# TODO: Check static executables?
|
# Check static executables
|
||||||
|
if [[ "${{ runner.os }}" == "Linux" ]]; then
|
||||||
|
echo "::group::Check static executables"
|
||||||
|
for exe in build/packager/{packager,mpd_generator}; do
|
||||||
|
# Capture information about the executables, but also let it be
|
||||||
|
# logged to stdout.
|
||||||
|
ldd "$exe" | tee static.log
|
||||||
|
# The phrase "statically linked" means we got it right. Fail if
|
||||||
|
# we don't find it.
|
||||||
|
if ! cat static.log | grep -q statically; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo "::endgroup::"
|
||||||
|
fi
|
||||||
|
|
||||||
echo "::group::Prepare artifacts folder"
|
echo "::group::Prepare artifacts folder"
|
||||||
mkdir artifacts
|
mkdir artifacts
|
||||||
|
|
|
@ -34,3 +34,6 @@
|
||||||
[submodule "packager/third_party/c-ares/source"]
|
[submodule "packager/third_party/c-ares/source"]
|
||||||
path = packager/third_party/c-ares/source
|
path = packager/third_party/c-ares/source
|
||||||
url = https://github.com/c-ares/c-ares
|
url = https://github.com/c-ares/c-ares
|
||||||
|
[submodule "packager/third_party/mimalloc/source"]
|
||||||
|
path = packager/third_party/mimalloc/source
|
||||||
|
url = https://github.com/microsoft/mimalloc
|
||||||
|
|
|
@ -15,10 +15,14 @@ include("packager/policies.cmake")
|
||||||
# Project name. May not contain spaces. Versioning is managed elsewhere.
|
# Project name. May not contain spaces. Versioning is managed elsewhere.
|
||||||
project(shaka-packager VERSION "")
|
project(shaka-packager VERSION "")
|
||||||
|
|
||||||
# The only build option for Shaka Packager is whether to build a shared
|
# Whether to build a shared libpackager library. By default, don't.
|
||||||
# libpackager library. By default, don't.
|
|
||||||
option(BUILD_SHARED_LIBS "Build libpackager as a shared library" OFF)
|
option(BUILD_SHARED_LIBS "Build libpackager as a shared library" OFF)
|
||||||
|
|
||||||
|
# Whether to attempt a static linking of the command line front-end.
|
||||||
|
# Only supported on Linux and with musl. This will also cause us to link
|
||||||
|
# against mimalloc to replace the standard allocator in musl, which is slow.
|
||||||
|
option(FULLY_STATIC "Attempt fully static linking of all CLI apps" OFF)
|
||||||
|
|
||||||
# Enable CMake's test infrastructure.
|
# Enable CMake's test infrastructure.
|
||||||
enable_testing()
|
enable_testing()
|
||||||
|
|
||||||
|
|
|
@ -106,6 +106,18 @@ After configuring CMake you can run the build with
|
||||||
cmake --build build --parallel
|
cmake --build build --parallel
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To build portable, fully-static executables on Linux, you will need either musl
|
||||||
|
as your system libc, or a musl toolchain. (See [musl.cc](https://musl.cc).
|
||||||
|
To create a portable, fully-static build for Linux, configure CMake with:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
-DCMAKE_BUILD_TYPE=Release \
|
||||||
|
-DBUILD_SHARED_LIBS="OFF" \
|
||||||
|
-DFULLY_STATIC="ON" \
|
||||||
|
-DCMAKE_C_COMPILER=/path/to/x86_64-linux-musl-gcc \
|
||||||
|
-DCMAKE_CXX_COMPILER=/path/to/x86_64-linux-musl-g++
|
||||||
|
```
|
||||||
|
|
||||||
#### Windows
|
#### Windows
|
||||||
|
|
||||||
Windows build instructions are similar. Using Tools > Command Line >
|
Windows build instructions are similar. Using Tools > Command Line >
|
||||||
|
|
|
@ -57,6 +57,9 @@ include_directories(..)
|
||||||
# Public include folder, to reference public headers as packager/foo.h
|
# Public include folder, to reference public headers as packager/foo.h
|
||||||
include_directories(../include)
|
include_directories(../include)
|
||||||
|
|
||||||
|
# Include settings for optional fully-static binaries.
|
||||||
|
include("fully-static.cmake")
|
||||||
|
|
||||||
# Include our module for gtest-based testing.
|
# Include our module for gtest-based testing.
|
||||||
include("gtest.cmake")
|
include("gtest.cmake")
|
||||||
|
|
||||||
|
@ -171,6 +174,7 @@ target_link_libraries(packager
|
||||||
libpackager
|
libpackager
|
||||||
license_notice
|
license_notice
|
||||||
string_utils
|
string_utils
|
||||||
|
${EXTRA_EXE_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(mpd_generator
|
add_executable(mpd_generator
|
||||||
|
@ -187,6 +191,7 @@ target_link_libraries(mpd_generator
|
||||||
license_notice
|
license_notice
|
||||||
mpd_builder
|
mpd_builder
|
||||||
mpd_util
|
mpd_util
|
||||||
|
${EXTRA_EXE_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
add_executable(packager_test
|
add_executable(packager_test
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
# Copyright 2024 Google LLC. All rights reserved.
|
||||||
|
#
|
||||||
|
# Use of this source code is governed by a BSD-style
|
||||||
|
# license that can be found in the LICENSE file or at
|
||||||
|
# https://developers.google.com/open-source/licenses/bsd
|
||||||
|
|
||||||
|
# Fully-static build settings.
|
||||||
|
if(FULLY_STATIC)
|
||||||
|
# This is the "object" version of mimalloc, as opposed to the library
|
||||||
|
# version. This is important for a static override of malloc and friends.
|
||||||
|
set(EXTRA_EXE_LIBRARIES $<TARGET_OBJECTS:mimalloc-obj>)
|
||||||
|
|
||||||
|
# Keep the linker from searching for dynamic libraries.
|
||||||
|
set(CMAKE_LINK_SEARCH_START_STATIC OFF)
|
||||||
|
set(CMAKE_LINK_SEARCH_END_STATIC OFF)
|
||||||
|
|
||||||
|
# Tell CMake not to plan to relink the executables, which wouldn't make sense
|
||||||
|
# in this context and causes CMake to fail at configure time when using a
|
||||||
|
# musl toolchain for static builds.
|
||||||
|
set(CMAKE_SKIP_BUILD_RPATH ON)
|
||||||
|
|
||||||
|
# Set extra linker options necessary for fully static linking. These apply
|
||||||
|
# to all executables, which is critical when using a musl toolchain. Without
|
||||||
|
# applying these to all executables, we could create dynamic musl executables
|
||||||
|
# as intermediate outputs, which then could not run on a glibc host system.
|
||||||
|
add_link_options(-static-libgcc -static-libstdc++ -static)
|
||||||
|
endif()
|
|
@ -46,6 +46,7 @@ add_subdirectory(libpng EXCLUDE_FROM_ALL)
|
||||||
add_subdirectory(libwebm EXCLUDE_FROM_ALL)
|
add_subdirectory(libwebm EXCLUDE_FROM_ALL)
|
||||||
add_subdirectory(libxml2 EXCLUDE_FROM_ALL)
|
add_subdirectory(libxml2 EXCLUDE_FROM_ALL)
|
||||||
add_subdirectory(mbedtls EXCLUDE_FROM_ALL)
|
add_subdirectory(mbedtls EXCLUDE_FROM_ALL)
|
||||||
|
add_subdirectory(mimalloc EXCLUDE_FROM_ALL)
|
||||||
add_subdirectory(mongoose EXCLUDE_FROM_ALL)
|
add_subdirectory(mongoose EXCLUDE_FROM_ALL)
|
||||||
add_subdirectory(protobuf EXCLUDE_FROM_ALL)
|
add_subdirectory(protobuf EXCLUDE_FROM_ALL)
|
||||||
add_subdirectory(zlib EXCLUDE_FROM_ALL)
|
add_subdirectory(zlib EXCLUDE_FROM_ALL)
|
||||||
|
|
|
@ -14,6 +14,7 @@ set(USE_LIBIDN2 OFF)
|
||||||
set(USE_LIBRTMP OFF)
|
set(USE_LIBRTMP OFF)
|
||||||
set(CURL_USE_LIBSSH2 OFF)
|
set(CURL_USE_LIBSSH2 OFF)
|
||||||
set(CURL_ZLIB OFF CACHE STRING "Force curl not to search for system zlib")
|
set(CURL_ZLIB OFF CACHE STRING "Force curl not to search for system zlib")
|
||||||
|
set(BUILD_CURL_EXE OFF)
|
||||||
|
|
||||||
if(UNIX AND NOT APPLE)
|
if(UNIX AND NOT APPLE)
|
||||||
# Use c-ares to fix static linking on Linux. Set USE_ARES directly, not the
|
# Use c-ares to fix static linking on Linux. Set USE_ARES directly, not the
|
||||||
|
|
|
@ -25,6 +25,8 @@ set(PNG_BUILD_ZLIB ON)
|
||||||
set(ZLIB_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../zlib/source/")
|
set(ZLIB_INCLUDE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../zlib/source/")
|
||||||
# Tell libpng where to find zlib library to link to.
|
# Tell libpng where to find zlib library to link to.
|
||||||
set(ZLIB_LIBRARY zlibstatic)
|
set(ZLIB_LIBRARY zlibstatic)
|
||||||
|
# Tell libpng where to find libm on Linux (-lm).
|
||||||
|
set(M_LIBRARY m)
|
||||||
|
|
||||||
# With these set in scope of this folder, load the library's own CMakeLists.txt.
|
# With these set in scope of this folder, load the library's own CMakeLists.txt.
|
||||||
add_subdirectory(source)
|
add_subdirectory(source)
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Copyright 2024 Google LLC. All rights reserved.
|
||||||
|
#
|
||||||
|
# Use of this source code is governed by a BSD-style
|
||||||
|
# license that can be found in the LICENSE file or at
|
||||||
|
# https://developers.google.com/open-source/licenses/bsd
|
||||||
|
|
||||||
|
# CMake build file to host mimalloc configuration.
|
||||||
|
# This is only used to produce static binaries on Linux, as a replacement for
|
||||||
|
# the default allocator in musl, which is slower.
|
||||||
|
|
||||||
|
# Turn these off to save time.
|
||||||
|
set(MI_BUILD_SHARED OFF)
|
||||||
|
set(MI_BUILD_STATIC OFF)
|
||||||
|
set(MI_BUILD_TESTS OFF)
|
||||||
|
|
||||||
|
# Turn these on. They are already on by default as of the date we wrote this
|
||||||
|
# file, but in case the defaults ever change, these settings are critical.
|
||||||
|
set(MI_OVERRIDE ON)
|
||||||
|
set(MI_BUILD_OBJECT ON)
|
||||||
|
|
||||||
|
# With these set in scope of this folder, load the library's own CMakeLists.txt.
|
||||||
|
add_subdirectory(source)
|
|
@ -0,0 +1 @@
|
||||||
|
Subproject commit 4e50d6714d471b72b2285e25a3df6c92db944593
|
Loading…
Reference in New Issue