Skip to content

Commit ff044ef

Browse files
mkschulzeclaude
andcommitted
ci(windows): generate MSVC .lib import libraries for ffmpeg DLLs
CI run 26063971492 build-windows passed the openh264 link-test (gendef + binutils fix landed in de0e71f) but then JamWide's MSVC link failed with 24 unresolved externals: av_packet_unref, av_freep, avcodec_find_decoder, avcodec_send_packet, etc. — every ffmpeg API. Root cause is the same shape as the openh264 issue. ffmpeg's MinGW autotools install produces .dll.a (MinGW-format import libraries) that MSVC's link.exe rejects for non-trivial decoration. Apply the same gendef + dlltool fix pattern to every ffmpeg DLL after `make install`. Script changes (scripts/build_ffmpeg_lgpl.sh, Windows post-process): 1. Consolidate DLLs from $PREFIX/bin/ (MinGW autotools default) into $PREFIX/lib/ so cmake/ffmpeg.cmake finds DLLs + import libs in one place. 2. For each *.dll in $PREFIX/lib/: gendef → .def, dlltool --output-lib → .lib. dlltool emits COFF that MSVC accepts for ffmpeg's C symbols. 3. Strip MinGW .dll.a files — cmake's file(GLOB) would otherwise pick them up and MSVC fails on them anyway. The .lib files we generated supersede. cmake/ffmpeg.cmake WIN32 path: - _lib_glob now signals MSVC link target (.lib) instead of runtime binary (.dll). Two file(GLOB) variants per library — Windows ffmpeg DLLs lack the `lib` prefix (avcodec-61.lib) while openh264 keeps it (libopenh264.lib). Glob both patterns. - _win32_no_libprefix flag switches the existing glob block to the Windows naming convention; macOS/Linux paths untouched. Same one-shot bundling pattern applies once this lane goes green: capture libs/ffmpeg/windows-x86_64/ as artifact, commit, strip MSYS2 setup + Vendor LGPL ffmpeg from Windows CI. macOS already done. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 16de377 commit ff044ef

2 files changed

Lines changed: 78 additions & 9 deletions

File tree

cmake/ffmpeg.cmake

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,15 @@ elseif(UNIX AND NOT APPLE)
131131
set(_have_symlinks TRUE)
132132
elseif(WIN32)
133133
set(_ffmpeg_dir "${CMAKE_CURRENT_LIST_DIR}/../libs/ffmpeg/windows-x86_64")
134-
set(_lib_glob "*.dll")
134+
# MSVC links against .lib import libraries, not .dll directly.
135+
# build_ffmpeg_lgpl.sh's Windows post-process generates these via
136+
# gendef + dlltool so $PREFIX/lib has both .dll (runtime) and .lib
137+
# (link-time). ffmpeg's MinGW autotools convention drops the `lib`
138+
# prefix on Windows (e.g. avcodec-61.dll, avcodec-61.lib) — handle
139+
# that below by globbing both `lib${name}.*` and `${name}-*.lib`.
140+
set(_lib_glob "lib")
135141
set(_have_symlinks FALSE)
142+
set(_win32_no_libprefix TRUE)
136143
else()
137144
message(FATAL_ERROR "ffmpeg::lgpl: unsupported platform")
138145
endif()
@@ -147,11 +154,22 @@ endif()
147154

148155
# Resolve library paths. The version suffixes track the ffmpeg release vendored
149156
# by scripts/build_ffmpeg_lgpl.sh (currently ffmpeg 7.1.2 + openh264 2.1.1).
150-
file(GLOB _avcodec_lib "${_ffmpeg_dir}/lib/libavcodec.${_lib_glob}")
151-
file(GLOB _avformat_lib "${_ffmpeg_dir}/lib/libavformat.${_lib_glob}")
152-
file(GLOB _swscale_lib "${_ffmpeg_dir}/lib/libswscale.${_lib_glob}")
153-
file(GLOB _avutil_lib "${_ffmpeg_dir}/lib/libavutil.${_lib_glob}")
154-
file(GLOB _openh264_lib "${_ffmpeg_dir}/lib/libopenh264.${_lib_glob}")
157+
if(_win32_no_libprefix)
158+
# Windows: ffmpeg DLLs/libs lack the `lib` prefix (avcodec-61.lib) and
159+
# are versioned (-61, -7, -8 etc.). Glob both `lib${name}*.lib`
160+
# (openh264 keeps the prefix) and `${name}-*.lib` (ffmpeg drops it).
161+
file(GLOB _avcodec_lib "${_ffmpeg_dir}/lib/avcodec-*.lib" "${_ffmpeg_dir}/lib/libavcodec.lib")
162+
file(GLOB _avformat_lib "${_ffmpeg_dir}/lib/avformat-*.lib" "${_ffmpeg_dir}/lib/libavformat.lib")
163+
file(GLOB _swscale_lib "${_ffmpeg_dir}/lib/swscale-*.lib" "${_ffmpeg_dir}/lib/libswscale.lib")
164+
file(GLOB _avutil_lib "${_ffmpeg_dir}/lib/avutil-*.lib" "${_ffmpeg_dir}/lib/libavutil.lib")
165+
file(GLOB _openh264_lib "${_ffmpeg_dir}/lib/libopenh264*.lib")
166+
else()
167+
file(GLOB _avcodec_lib "${_ffmpeg_dir}/lib/libavcodec.${_lib_glob}")
168+
file(GLOB _avformat_lib "${_ffmpeg_dir}/lib/libavformat.${_lib_glob}")
169+
file(GLOB _swscale_lib "${_ffmpeg_dir}/lib/libswscale.${_lib_glob}")
170+
file(GLOB _avutil_lib "${_ffmpeg_dir}/lib/libavutil.${_lib_glob}")
171+
file(GLOB _openh264_lib "${_ffmpeg_dir}/lib/libopenh264.${_lib_glob}")
172+
endif()
155173

156174
# Filter out symlinks — keep only the versioned canonical files. On Windows
157175
# (.dll) there are no symlinks, so this is a no-op; on macOS/Linux it

scripts/build_ffmpeg_lgpl.sh

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -371,9 +371,60 @@ case "$OS" in
371371
;;
372372

373373
MINGW64_NT-*|MSYS_NT-*|CYGWIN_NT-*)
374-
# Windows: .dll discovery is via consumer-adjacent path / %PATH%.
375-
# No install-name rewriting needed. Just verify the .dll files are present.
376-
echo " (Windows: .dll files in $PREFIX/lib — consumers discover via PATH)"
374+
# Windows: two-step post-process.
375+
#
376+
# 1. ffmpeg's MinGW autotools install puts DLLs in $PREFIX/bin/ (no `lib`
377+
# prefix on Windows by convention — e.g. avcodec-61.dll). Consolidate
378+
# into $PREFIX/lib/ so cmake/ffmpeg.cmake finds DLLs + import libs in
379+
# one place.
380+
#
381+
# 2. JamWide is built with MSVC, but ffmpeg's MinGW build produces
382+
# .dll.a MinGW-format import libraries — MSVC's link.exe rejects
383+
# those for any non-trivial decoration. Generate MSVC-consumable
384+
# .lib import libraries from each DLL via gendef + dlltool. dlltool
385+
# --output-lib emits COFF that MSVC accepts for the C symbols
386+
# ffmpeg exposes.
387+
# Same fix pattern as the openh264 .lib generation we already do
388+
# further up in this script.
389+
390+
# Step 1: consolidate DLLs from bin/ into lib/.
391+
if [ -d "$PREFIX/bin" ]; then
392+
find "$PREFIX/bin" -maxdepth 1 -name "*.dll" -exec mv {} "$PREFIX/lib/" \;
393+
# Empty bin/ left over — remove if no other content.
394+
rmdir "$PREFIX/bin" 2>/dev/null || true
395+
fi
396+
397+
# Step 2: generate MSVC .lib for every ffmpeg DLL.
398+
if ! command -v gendef >/dev/null 2>&1 || ! command -v dlltool >/dev/null 2>&1; then
399+
echo "ERROR: gendef + dlltool required for Windows MSVC .lib generation" >&2
400+
echo " install via MSYS2 mingw-w64-x86_64-tools-git + mingw-w64-x86_64-binutils" >&2
401+
exit 1
402+
fi
403+
404+
( cd "$PREFIX/lib" && \
405+
for dll in *.dll; do
406+
[ -f "$dll" ] || continue
407+
# Already converted? skip.
408+
stem="${dll%.dll}"
409+
if [ -f "${stem}.lib" ]; then continue; fi
410+
gendef "$dll" >/dev/null 2>&1
411+
if [ -f "${stem}.def" ]; then
412+
dlltool --dllname "$dll" --def "${stem}.def" \
413+
--output-lib "${stem}.lib"
414+
echo " generated ${stem}.lib from $dll"
415+
rm -f "${stem}.def"
416+
else
417+
echo "WARNING: gendef did not produce ${stem}.def — MSVC link may fail" >&2
418+
fi
419+
done
420+
)
421+
422+
# Strip MinGW-format .dll.a files — they confuse cmake/ffmpeg.cmake's
423+
# file(GLOB) (it'd pick the wrong one) and MSVC doesn't consume them
424+
# anyway. The .lib files we just generated supersede.
425+
find "$PREFIX/lib" -maxdepth 1 -name "*.dll.a" -delete
426+
427+
echo " Windows: $PREFIX/lib has DLLs + MSVC .lib import libraries"
377428
;;
378429
esac
379430

0 commit comments

Comments
 (0)