As the title explains, I am trying to bundle FFMPEG with my executable application.
However, I can't seem to figure out the runtime loading of the FFMPEG shared libraries. I'm not sure what is incorrect:
RPATH/RUNPATH
of main project and FFMPEG libraries.- The CMake install steps.
- Or just my entire approach (most likely).
This is a CMake project and in hope of finally finding a "standard solution", I am going to include everything needed to reproduce this issue.
CMakeLists.txt:
cmake_minimum_required(VERSION 3.21) # Required for the install(RUNTIME_DEPENDENCY_SET...) subcommand
project(cmake-demo LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED YES)
set(CMAKE_CXX_EXTENSIONS NO)
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_VISIBILITY_INLINES_HIDDEN YES)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
# Setup CMAKE_PREFIX_PATH for `find_package`
list(APPEND CMAKE_PREFIX_PATH ${CMAKE_CURRENT_LIST_DIR}/vendor/install)
find_package(PkgConfig REQUIRED)
pkg_check_modules(ffmpeg REQUIRED IMPORTED_TARGET libavdevice libavfilter libavformat libavcodec
libswresample libswscale libavutil)
# Setup variable representing the vendor install directory
set(VENDOR_PATH ${CMAKE_CURRENT_LIST_DIR}/vendor/install)
# Set RPATH to the dependency install tree
set(CMAKE_INSTALL_RPATH $ORIGIN/../deps)
# Add our executable target
add_executable(demo main.cpp)
target_include_directories(demo PRIVATE ${VENDOR_PATH}/include)
target_link_libraries(demo PRIVATE PkgConfig::ffmpeg)
include(GNUInstallDirs)
install(TARGETS demo RUNTIME_DEPENDENCY_SET runtime_deps)
install(RUNTIME_DEPENDENCY_SET runtime_deps
DESTINATION ${CMAKE_INSTALL_PREFIX}/deps
# DIRECTORIES ${VENDOR_PATH}/lib
POST_EXCLUDE_REGEXES "^/lib" "^/usr" "^/bin")
main.cpp:
extern "C"{
#include <libavcodec/avcodec.h>
}
#include <iostream>
int main(int argc, char* argv[])
{
std::cout << "FFMPEG AVCODEC VERSION: " << avcodec_version() << std::endl;
return 0;
}
Instructions:
Setup FFMPEG (this can take awhile...):
git clone https://github.com/FFmpeg/FFmpeg.git --recurse-submodules --shallow-submodules vendor/src/ffmpeg
git checkout release/5.0
export INSTALL_PATH=$PWD/vendor/install
cd vendor/src/ffmpeg
./configure --prefix=$INSTALL_PATH --enable-shared --disable-static --enable-pic --enable-lto --extra-cflags=-fPIC --extra-ldexeflags=-pie
# WITH RPATH (HARDCODED TO VENDOR install directory)
# ./configure --prefix=$INSTALL_PATH --enable-shared --disable-static --enable-pic --enable-lto --extra-cflags=-fPIC --extra-ldflags=-Wl,-rpath,$INSTALL_PATH/lib --extra-ldexeflags=-pie
make -j16 V=1
make install
cd ../../..
CMake Commands:
cmake -S . -B build -DCMAKE_INSTALL_PREFIX=install
cmake --build build -j16 #-v
cmake --install build
Cleanup between steps:
# Run from top-level dir of project
rm -rf build install
rm -rf vendor/install # ONLY if you need to modify the FFMPEG step.
Investigation:
- Following the direction of this post I set the
RPATH/RUNPATH
of the executable to$ORIGIN/../deps
and no RPATH on the FFMPEG libraries.
- When I do this, the cmake install step fails complaining that
libavutil.so
cannot be resolved in theinstall(RUNTIME_DEPENDENCY_SET...)
function. This does make sense because the generatedcmake_install.cmake
script in thebuild
directory is moving the demo executable to the install directory and performingfile(RPATH_CHANGE...)
on the executable prior to doing the runtime dependency set analysis. According to the CMake documentation here and usingreadelf -d demo
theRUNPATH
of the executable is$ORIGIN/../deps
and only case #1 from the docs apply here so the library isn't found at all.
- From the CMake documentation referenced in 1, there is a
DIRECTORIES
argument for theinstall(RUNTIME_DEPENDENCY_SET...)
function that can be used as a search path. When combining the setup in 1, uncommenting this inCMakeLists.txt
and rerunning all the CMake commands, the installation is successful (although as documented, CMake does emit warnings for all the dependencies found usingDIRECTORIES
). However, running the executable (i.e../install/bin/demo
) is unsuccessful because the runtime loader cannot locatelibswresample.so
. Debugging into this a little further, I ranexport LD_DEBUG=libs
and tried to relaunch the executable. This shows that the runtime loader is findinglibavcodec.so
using the$ORIGIN/../deps
RUNPATH fromdemo
. However, when searching for the transitive dependency (i.e.libavcodec.so
's dep. onlibswresample.so
), theRUNPATH
ofdemo
is NOT searched, instead the loader falls back to the system path and the library is not found. - I was able to satisfy CMake and the loader by setting the RPATH on the FFMPEG libraries and not using the
DIRECTORIES
argument with the CMakeinstall(RUNTIME_DEPENDENCY_SET...)
sub-command. CMake runs without any warnings and the executable loads/runs as expected. However, this is NOT a viable solution because when inspecting the libraries in theinstall/deps
folder withldd
, thelibswresample.so
and other libraries are pointing back to thevendor/install
directory which is not going to be present in the bundle when deployed.
Does anyone know what the standard approach is or what I am missing above? I am open to any and all suggestions/tools but I am doing this in an attempt to learn what is "standard practice" and am hoping to extend this to a cross-platform solution (i.e Windows/macOS). As a result, I would like to avoid messing with the system level loader configuration (ldconfig
).
Thanks!